sparc.c
来自「GCC编译器源代码」· C语言 代码 · 共 2,284 行 · 第 1/5 页
C
2,284 行
{ 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_ARCH64) { 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_ARCH64 || (REGNO (op0) & 1) == 0)) || (optype0 != REGOP && optype0 != CNSTOP && optype1 == REGOP && (TARGET_ARCH64 || (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_ARCH64) return (mem == op1 ? "ldd %1,%0" : "std %1,%0"); else return (mem == op1 ? "ldx %1,%0" : "stx %1,%0"); } } if (TARGET_ARCH64) { 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, word_mode, REGNO (op0) + 0); wordpart[1][0] = gen_rtx (REG, word_mode, REGNO (op0) + 1); if (TARGET_ARCH32) { wordpart[2][0] = gen_rtx (REG, word_mode, REGNO (op0) + 2); wordpart[3][0] = gen_rtx (REG, word_mode, REGNO (op0) + 3); } } else if (optype0 == OFFSOP) { wordpart[0][0] = adj_offsettable_operand (op0, 0); if (TARGET_ARCH32) { wordpart[1][0] = adj_offsettable_operand (op0, 4); wordpart[2][0] = adj_offsettable_operand (op0, 8); wordpart[3][0] = adj_offsettable_operand (op0, 12); } else wordpart[1][0] = adj_offsettable_operand (op0, 8); } else { wordpart[0][0] = op0; wordpart[1][0] = op0; wordpart[2][0] = op0; wordpart[3][0] = op0; } if (optype1 == REGOP) { wordpart[0][1] = gen_rtx (REG, word_mode, REGNO (op1) + 0); wordpart[1][1] = gen_rtx (REG, word_mode, REGNO (op1) + 1); if (TARGET_ARCH32) { wordpart[2][1] = gen_rtx (REG, word_mode, REGNO (op1) + 2); wordpart[3][1] = gen_rtx (REG, word_mode, REGNO (op1) + 3); } } else if (optype1 == OFFSOP) { wordpart[0][1] = adj_offsettable_operand (op1, 0); if (TARGET_ARCH32) { wordpart[1][1] = adj_offsettable_operand (op1, 4); wordpart[2][1] = adj_offsettable_operand (op1, 8); wordpart[3][1] = adj_offsettable_operand (op1, 12); } else wordpart[1][1] = adj_offsettable_operand (op1, 8); } else if (optype1 == CNSTOP) { REAL_VALUE_TYPE r; long l[4]; /* This only works for TFmode floating point constants. */ if (GET_CODE (op1) != CONST_DOUBLE || GET_MODE (op1) != TFmode) abort (); REAL_VALUE_FROM_CONST_DOUBLE (r, op1); REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l); wordpart[0][1] = GEN_INT (l[0]); wordpart[1][1] = GEN_INT (l[1]); wordpart[2][1] = GEN_INT (l[2]); wordpart[3][1] = GEN_INT (l[3]); } else { wordpart[0][1] = op1; wordpart[1][1] = op1; wordpart[2][1] = op1; wordpart[3][1] = op1; } /* Easy case: try moving the quad as two pairs. Check for moving between an even/odd register pair and a memory location. Also handle new v9 fp regs here. */ /* ??? Should also handle the case of non-offsettable addresses here. We can at least do the first pair as a ldd/std, and then do the third and fourth words individually. */ if ((optype0 == REGOP && optype1 == OFFSOP && (REGNO (op0) & 1) == 0) || (optype0 == OFFSOP && optype1 == REGOP && (REGNO (op1) & 1) == 0)) { rtx mem, reg; if (optype0 == REGOP) mem = op1, reg = op0; else mem = op0, reg = op1; 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) >= SPARC_FIRST_V9_FP_REG)) { if (TARGET_V9 && FP_REG_P (reg) && TARGET_HARD_QUAD) { if ((REGNO (reg) & 3) != 0) abort (); /* ??? Can `mem' have an inappropriate alignment here? */ return (mem == op1 ? "ldq %1,%0" : "stq %1,%0"); } operands[2] = adj_offsettable_operand (mem, 8); /* ??? In arch64 case, shouldn't we use ldd/std for fp regs. */ if (mem == op1) return TARGET_ARCH64 ? "ldx %1,%0;ldx %2,%R0" : "ldd %1,%0;ldd %2,%S0"; else return TARGET_ARCH64 ? "stx %1,%0;stx %R1,%2" : "std %1,%0;std %S1,%2"; } } /* If the first move would clobber the source of the second one, do them in the other order. */ /* Overlapping registers? */ if (TARGET_ARCH32) { if (optype0 == REGOP && optype1 == REGOP && (REGNO (op0) == REGNO (wordpart[1][3]) || REGNO (op0) == REGNO (wordpart[1][2]) || REGNO (op0)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?