📄 sparc.c
字号:
not the same as the HI dest, then the MSW of the LO_SUM dest will never be set. ??? The real problem here is that the ...(HI:DImode pattern emits multiple instructions, and the ...(LO_SUM:DImode pattern emits one instruction. This fails, because the compiler assumes that LO_SUM copies all bits of the first operand to its dest. Better would be to have the HI pattern emit one instruction and the LO_SUM pattern multiple instructions. Even better would be to use four rtl insns. */ rtx temp = ((reload_in_progress || mode == DImode) ? operand0 : gen_reg_rtx (mode)); if (TARGET_V9 && mode == DImode) { int high_operand = 0; /* If the operand is already a HIGH, then remove the HIGH so that we won't get duplicate HIGH operators in this insn. Also, we must store the result into the original dest, because that is where the following LO_SUM expects it. */ if (GET_CODE (operand1) == HIGH) { operand1 = XEXP (operand1, 0); high_operand = 1; } emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1)), gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, DImode, 1))))); /* If this was a high operand, then we are now finished. */ if (high_operand) return 1; } else emit_insn (gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1))); operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); } } if (GET_CODE (operand1) == LABEL_REF && flag_pic) { /* The procedure for doing this involves using a call instruction to get the pc into o7. We need to indicate this explicitly because the tablejump pattern assumes that it can use this value also. */ emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2, gen_rtx (SET, VOIDmode, operand0, operand1), gen_rtx (SET, VOIDmode, gen_rtx (REG, mode, 15), pc_rtx)))); return 1; } /* Now have insn-emit do whatever it normally does. */ return 0;}/* Return the best assembler insn template for moving operands[1] into operands[0] as a fullword. */char *singlemove_string (operands) rtx *operands;{ if (GET_CODE (operands[0]) == MEM) { if (GET_CODE (operands[1]) != MEM) return "st %r1,%0"; else abort (); } else if (GET_CODE (operands[1]) == MEM) return "ld %1,%0"; else if (GET_CODE (operands[1]) == CONST_DOUBLE) { REAL_VALUE_TYPE r; long i; /* Must be SFmode, otherwise this doesn't make sense. */ if (GET_MODE (operands[1]) != SFmode) abort (); REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); REAL_VALUE_TO_TARGET_SINGLE (r, i); operands[1] = gen_rtx (CONST_INT, VOIDmode, i); if (CONST_OK_FOR_LETTER_P (i, 'I')) return "mov %1,%0"; else if ((i & 0x000003FF) != 0) return "sethi %%hi(%a1),%0\n\tor %0,%%lo(%a1),%0"; else return "sethi %%hi(%a1),%0"; } else if (GET_CODE (operands[1]) == CONST_INT && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I')) { int i = INTVAL (operands[1]); /* If all low order 10 bits are clear, then we only need a single sethi insn to load the constant. */ if ((i & 0x000003FF) != 0) return "sethi %%hi(%a1),%0\n\tor %0,%%lo(%a1),%0"; else return "sethi %%hi(%a1),%0"; } /* Operand 1 must be a register, or a 'I' type CONST_INT. */ return "mov %1,%0";}/* Return non-zero if it is OK to assume that the given memory operand is aligned at least to a 8-byte boundary. This should only be called for memory accesses whose size is 8 bytes or larger. */intmem_aligned_8 (mem) register rtx mem;{ register rtx addr; register rtx base; register rtx offset; if (GET_CODE (mem) != MEM) return 0; /* It's gotta be a MEM! */ addr = XEXP (mem, 0); /* Now that all misaligned double parms are copied on function entry, we can assume any 64-bit object is 64-bit aligned except those which are at unaligned offsets from the stack or frame pointer. If the TARGET_UNALIGNED_DOUBLES switch is given, we do not make this assumption. */ /* See what register we use in the address. */ base = 0; if (GET_CODE (addr) == PLUS) { if (GET_CODE (XEXP (addr, 0)) == REG && GET_CODE (XEXP (addr, 1)) == CONST_INT) { base = XEXP (addr, 0); offset = XEXP (addr, 1); } } else if (GET_CODE (addr) == REG) { base = addr; offset = const0_rtx; } /* If it's the stack or frame pointer, check offset alignment. We can have improper alignment in the function entry code. */ if (base && (REGNO (base) == FRAME_POINTER_REGNUM || REGNO (base) == STACK_POINTER_REGNUM)) { if (((INTVAL (offset) - SPARC_STACK_BIAS) & 0x7) == 0) return 1; } /* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES is true, in which case we can only assume that an access is aligned if it is to a constant address, or the address involves a LO_SUM. We used to assume an address was aligned if MEM_IN_STRUCT_P was true. That assumption was deleted so that gcc generated code can be used with memory allocators that only guarantee 4 byte alignment. */ else if (! TARGET_UNALIGNED_DOUBLES || CONSTANT_P (addr) || GET_CODE (addr) == LO_SUM) return 1; /* An obviously unaligned address. */ return 0;}enum optype { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP };/* Output assembler code to perform a doubleword move insn with operands OPERANDS. This is very similar to the following output_move_quad function. */char *output_move_double (operands) rtx *operands;{ register rtx op0 = operands[0]; register rtx op1 = operands[1]; register enum optype optype0; register enum optype optype1; rtx latehalf[2]; rtx addreg0 = 0; rtx addreg1 = 0; int highest_first = 0; int no_addreg1_decrement = 0; /* First classify both operands. */ if (REG_P (op0)) optype0 = REGOP; else if (offsettable_memref_p (op0)) optype0 = OFFSOP; else if (GET_CODE (op0) == MEM) optype0 = MEMOP; else optype0 = RNDOP; if (REG_P (op1)) optype1 = REGOP; else if (CONSTANT_P (op1)) optype1 = CNSTOP; else if (offsettable_memref_p (op1)) optype1 = OFFSOP; else if (GET_CODE (op1) == MEM) optype1 = MEMOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP || (optype0 == MEM && optype1 == MEM)) abort (); /* If an operand is an unoffsettable memory ref, find a register we can increment temporarily to make it refer to the second word. */ if (optype0 == MEMOP) addreg0 = find_addr_reg (XEXP (op0, 0)); if (optype1 == MEMOP) addreg1 = find_addr_reg (XEXP (op1, 0)); /* Ok, we can do one word at a time. Set up in LATEHALF the operands to use for the high-numbered (least significant) word and in some cases alter the operands in OPERANDS to be suitable for the low-numbered word. */ if (optype0 == REGOP) latehalf[0] = gen_rtx (REG, SImode, REGNO (op0) + 1); else if (optype0 == OFFSOP) latehalf[0] = adj_offsettable_operand (op0, 4); else latehalf[0] = op0; if (optype1 == REGOP) latehalf[1] = gen_rtx (REG, SImode, REGNO (op1) + 1); else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (op1, 4); else if (optype1 == CNSTOP) { if (TARGET_V9) { if (arith_double_operand (op1, DImode)) { operands[1] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (op1)); return "mov %1,%0"; } else { /* The only way to handle CONST_DOUBLEs or other 64 bit constants here is to use a temporary, such as is done for the V9 DImode sethi insn pattern. This is not a practical solution, so abort if we reach here. The md file should always force such constants to memory. */ abort (); } } else split_double (op1, &operands[1], &latehalf[1]); } else latehalf[1] = op1; /* Easy case: try moving both words at once. Check for moving between an even/odd register pair and a memory location. */ if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP && (TARGET_V9 || (REGNO (op0) & 1) == 0)) || (optype0 != REGOP && optype0 != CNSTOP && optype1 == REGOP && (TARGET_V9 || (REGNO (op1) & 1) == 0))) { register rtx mem,reg; if (optype0 == REGOP) mem = op1, reg = op0; else mem = op0, reg = op1; /* In v9, ldd can be used for word aligned addresses, so technically some of this logic is unneeded. We still avoid ldd if the address is obviously unaligned though. */ if (mem_aligned_8 (mem) /* If this is a floating point register higher than %f31, then we *must* use an aligned load, since `ld' will not accept the register number. */ || (TARGET_V9 && REGNO (reg) >= 64)) { if (FP_REG_P (reg) || ! TARGET_V9) return (mem == op1 ? "ldd %1,%0" : "std %1,%0"); else return (mem == op1 ? "ldx %1,%0" : "stx %1,%0"); } } if (TARGET_V9) { if (optype0 == REGOP && optype1 == REGOP) { if (FP_REG_P (op0)) return "fmovd %1,%0"; else return "mov %1,%0"; } } /* If the first move would clobber the source of the second one, do them in the other order. */ /* Overlapping registers. */ if (optype0 == REGOP && optype1 == REGOP && REGNO (op0) == REGNO (latehalf[1])) { /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Do low-numbered word. */ return singlemove_string (operands); } /* Loading into a register which overlaps a register used in the address. */ else if (optype0 == REGOP && optype1 != REGOP && reg_overlap_mentioned_p (op0, op1)) { /* If both halves of dest are used in the src memory address, add the two regs and put them in the low reg (op0). Then it works to load latehalf first. */ if (reg_mentioned_p (op0, XEXP (op1, 0)) && reg_mentioned_p (latehalf[0], XEXP (op1, 0))) { rtx xops[2]; xops[0] = latehalf[0]; xops[1] = op0; output_asm_insn ("add %1,%0,%1", xops); operands[1] = gen_rtx (MEM, DImode, op0); latehalf[1] = adj_offsettable_operand (operands[1], 4); addreg1 = 0; highest_first = 1; } /* Only one register in the dest is used in the src memory address, and this is the first register of the dest, so we want to do the late half first here also. */ else if (! reg_mentioned_p (latehalf[0], XEXP (op1, 0))) highest_first = 1; /* Only one register in the dest is used in the src memory address, and this is the second register of the dest, so we want to do the late half last. If addreg1 is set, and addreg1 is the same register as latehalf, then we must suppress the trailing decrement, because it would clobber the value just loaded. */ else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0])) no_addreg1_decrement = 1; } /* Normal case: do the two words, low-numbered first. Overlap case (highest_first set): do high-numbered word first. */ if (! highest_first) output_asm_insn (singlemove_string (operands), operands); /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) output_asm_insn ("add %0,0x4,%0", &addreg0); if (addreg1) output_asm_insn ("add %0,0x4,%0", &addreg1); /* Do that word. */ output_asm_insn (singlemove_string (latehalf), latehalf); /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("add %0,-0x4,%0", &addreg0); if (addreg1 && ! no_addreg1_decrement) output_asm_insn ("add %0,-0x4,%0", &addreg1); if (highest_first) output_asm_insn (singlemove_string (operands), operands); return "";}/* Output assembler code to perform a quadword move insn with operands OPERANDS. This is very similar to the preceding output_move_double function. */char *output_move_quad (operands) rtx *operands;{ register rtx op0 = operands[0]; register rtx op1 = operands[1]; register enum optype optype0; register enum optype optype1; rtx wordpart[4][2]; rtx addreg0 = 0; rtx addreg1 = 0; /* First classify both operands. */ if (REG_P (op0)) optype0 = REGOP; else if (offsettable_memref_p (op0)) optype0 = OFFSOP; else if (GET_CODE (op0) == MEM) optype0 = MEMOP; else optype0 = RNDOP; if (REG_P (op1)) optype1 = REGOP; else if (CONSTANT_P (op1)) optype1 = CNSTOP; else if (offsettable_memref_p (op1)) optype1 = OFFSOP; else if (GET_CODE (op1) == MEM) optype1 = MEMOP; else optype1 = RNDOP; /* Check for the cases that the operand constraints are not supposed to allow to happen. Abort if we get one, because generating code for these cases is painful. */ if (optype0 == RNDOP || optype1 == RNDOP || (optype0 == MEM && optype1 == MEM)) abort (); /* If an operand is an unoffsettable memory ref, find a register we can increment temporarily to make it refer to the later words. */ if (optype0 == MEMOP) addreg0 = find_addr_reg (XEXP (op0, 0)); if (optype1 == MEMOP) addreg1 = find_addr_reg (XEXP (op1, 0)); /* Ok, we can do one word at a time. Set up in wordpart the operands to use for each word of the arguments. */ if (optype0 == REGOP) { wordpart[0][0] = gen_rtx (REG, SImode, REGNO (op0) + 0); wordpart[1][0] = gen_rtx (REG, SImode, REGNO (op0) + 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -