sparc.c
来自「GCC编译器源代码」· C语言 代码 · 共 2,284 行 · 第 1/5 页
C
2,284 行
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1))) return 1; return 0;}/* Legitimize PIC addresses. If the address is already position-independent, we return ORIG. Newly generated position-independent addresses go into a reg. This is REG if non zero, otherwise we allocate register(s) as necessary. */rtxlegitimize_pic_address (orig, mode, reg) rtx orig; enum machine_mode mode; rtx reg;{ if (GET_CODE (orig) == SYMBOL_REF) { rtx pic_ref, address; rtx insn; if (reg == 0) { if (reload_in_progress || reload_completed) abort (); else reg = gen_reg_rtx (Pmode); } if (flag_pic == 2) { /* If not during reload, allocate another temp reg here for loading in the address, so that these instructions can be optimized properly. */ rtx temp_reg = ((reload_in_progress || reload_completed) ? reg : gen_reg_rtx (Pmode)); /* Must put the SYMBOL_REF inside an UNSPEC here so that cse won't get confused into thinking that these two instructions are loading in the true address of the symbol. If in the future a PIC rtx exists, that should be used instead. */ emit_insn (gen_pic_sethi_si (temp_reg, orig)); emit_insn (gen_pic_lo_sum_si (temp_reg, temp_reg, orig)); address = temp_reg; } else address = orig; pic_ref = gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode, pic_offset_table_rtx, address)); current_function_uses_pic_offset_table = 1; RTX_UNCHANGING_P (pic_ref) = 1; insn = emit_move_insn (reg, pic_ref); /* Put a REG_EQUAL note on this insn, so that it can be optimized by loop. */ REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, orig, REG_NOTES (insn)); return reg; } else if (GET_CODE (orig) == CONST) { rtx base, offset; if (GET_CODE (XEXP (orig, 0)) == PLUS && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) return orig; if (reg == 0) { if (reload_in_progress || reload_completed) abort (); else reg = gen_reg_rtx (Pmode); } if (GET_CODE (XEXP (orig, 0)) == PLUS) { base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, base == reg ? 0 : reg); } else abort (); if (GET_CODE (offset) == CONST_INT) { if (SMALL_INT (offset)) return plus_constant_for_output (base, INTVAL (offset)); else if (! reload_in_progress && ! reload_completed) offset = force_reg (Pmode, offset); else /* If we reach here, then something is seriously wrong. */ abort (); } return gen_rtx (PLUS, Pmode, base, offset); } else if (GET_CODE (orig) == LABEL_REF) /* ??? Why do we do this? */ current_function_uses_pic_offset_table = 1; return orig;}/* Set up PIC-specific rtl. This should not cause any insns to be emitted. */voidinitialize_pic (){}/* Return the RTX for insns to set the PIC register. */static rtxpic_setup_code (){ rtx pic_pc_rtx; rtx l1, l2; rtx seq; start_sequence (); l1 = gen_label_rtx (); pic_pc_rtx = gen_rtx (CONST, Pmode, gen_rtx (MINUS, Pmode, global_offset_table, gen_rtx (CONST, Pmode, gen_rtx (MINUS, Pmode, gen_rtx (LABEL_REF, VOIDmode, l1), pc_rtx)))); /* sparc64: the RDPC instruction doesn't pair, and puts 4 bubbles in the pipe to boot. So don't use it here, especially when we're doing a save anyway because of %l7. */ l2 = gen_label_rtx (); emit_label (l1); /* Iff we are doing delay branch optimization, slot the sethi up here so that it will fill the delay slot of the call. */ if (flag_delayed_branch) emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, gen_rtx (HIGH, Pmode, pic_pc_rtx))); /* Note that we pun calls and jumps here! */ emit_jump_insn (gen_get_pc_via_call (l2, l1)); emit_label (l2); if (!flag_delayed_branch) emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, gen_rtx (HIGH, Pmode, pic_pc_rtx))); emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, gen_rtx (LO_SUM, Pmode, pic_offset_table_rtx, pic_pc_rtx))); emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, gen_rtx (PLUS, Pmode, pic_offset_table_rtx, gen_rtx (REG, Pmode, 15)))); /* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */ LABEL_PRESERVE_P (l1) = 1; LABEL_PRESERVE_P (l2) = 1; seq = gen_sequence (); end_sequence (); return seq;}/* Emit special PIC prologues and epilogues. */voidfinalize_pic (){ /* Labels to get the PC in the prologue of this function. */ int orig_flag_pic = flag_pic; rtx insn; if (current_function_uses_pic_offset_table == 0) return; if (! flag_pic) abort (); /* Initialize every time through, since we can't easily know this to be permanent. */ global_offset_table = gen_rtx (SYMBOL_REF, Pmode, "_GLOBAL_OFFSET_TABLE_"); flag_pic = 0; emit_insn_after (pic_setup_code (), get_insns ()); /* Insert the code in each nonlocal goto receiver. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE && XINT (PATTERN (insn), 1) == 4) emit_insn_after (pic_setup_code (), insn); flag_pic = orig_flag_pic; /* Need to emit this whether or not we obey regdecls, since setjmp/longjmp can cause life info to screw up. ??? In the case where we don't obey regdecls, this is not sufficient since we may not fall out the bottom. */ emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx));}/* Emit insns to move operands[1] into operands[0]. Return 1 if we have written out everything that needs to be done to do the move. Otherwise, return 0 and the caller will emit the move normally. */intemit_move_sequence (operands, mode) rtx *operands; enum machine_mode mode;{ register rtx operand0 = operands[0]; register rtx operand1 = operands[1]; if (CONSTANT_P (operand1) && flag_pic && pic_address_needs_scratch (operand1)) operands[1] = operand1 = legitimize_pic_address (operand1, mode, 0); /* Handle most common case first: storing into a register. */ if (register_operand (operand0, mode)) { if (register_operand (operand1, mode) || (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1)) || (GET_CODE (operand1) == CONST_DOUBLE && arith_double_operand (operand1, DImode)) || (GET_CODE (operand1) == HIGH && GET_MODE (operand1) != DImode) /* Only `general_operands' can come here, so MEM is ok. */ || GET_CODE (operand1) == MEM) { /* Run this case quickly. */ emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); return 1; } } else if (GET_CODE (operand0) == MEM) { if (register_operand (operand1, mode) || (operand1 == const0_rtx && ! TARGET_LIVE_G0)) { /* Run this case quickly. */ emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); return 1; } if (! reload_in_progress) { operands[0] = validize_mem (operand0); operands[1] = operand1 = force_reg (mode, operand1); } } if (GET_CODE (operand1) == LABEL_REF && mode == SImode && flag_pic) { if (TARGET_ARCH64) abort (); emit_insn (gen_move_pic_label_si (operand0, operand1)); return 1; } /* Non-pic LABEL_REF's in sparc64 are expensive to do the normal way, so always use special code. */ else if (GET_CODE (operand1) == LABEL_REF && mode == DImode) { if (! TARGET_ARCH64) abort (); emit_insn (gen_move_label_di (operand0, operand1)); return 1; } /* DImode HIGH values in sparc64 need a clobber added. */ else if (TARGET_ARCH64 && GET_CODE (operand1) == HIGH && GET_MODE (operand1) == DImode) { emit_insn (gen_sethi_di_sp64 (operand0, XEXP (operand1, 0))); return 1; } /* Simplify the source if we need to. */ else if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) { if (flag_pic && symbolic_operand (operand1, mode)) { rtx temp_reg = reload_in_progress ? operand0 : 0; operands[1] = legitimize_pic_address (operand1, mode, temp_reg); } else if (GET_CODE (operand1) == CONST_INT ? (! SMALL_INT (operand1) && ! SPARC_SETHI_P (INTVAL (operand1))) : GET_CODE (operand1) == CONST_DOUBLE ? ! arith_double_operand (operand1, DImode) : 1) { /* For DImode values, temp must be operand0 because of the way HI and LO_SUM work. The LO_SUM operator only copies half of the LSW from the dest of the HI operator. If the LO_SUM dest is 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_ARCH64 && mode == DImode) emit_insn (gen_sethi_di_sp64 (temp, operand1)); else emit_insn (gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1))); if (GET_CODE (operand1) == CONST_INT) operand1 = GEN_INT (INTVAL (operand1) & 0xffffffff); else if (GET_CODE (operand1) == CONST_DOUBLE) operand1 = GEN_INT (CONST_DOUBLE_LOW (operand1) & 0xffffffff); operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); } } /* 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 4 byte quantity. This isn't intended to be very smart. It is up to the caller to choose the best way to do things. Note that OPERANDS may be modified to suit the returned string. */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_INT (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')) { HOST_WIDE_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. */ /* FIXME: Use SETHI_P. */ 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 the best assembler insn template for moving operands[1] into operands[0] as an 8 byte quantity. This isn't intended to be very smart. It is up to the caller to choose the best way to do things. Note that OPERANDS may be modified to suit the returned string. */char *doublemove_string (operands) rtx *operands;{ rtx op0 = operands[0], op1 = operands[1]; if (GET_CODE (op0) == MEM) { if (GET_CODE (op1) == REG) { if (FP_REG_P (op1)) return "std %1,%0"; return TARGET_ARCH64 ? "stx %1,%0" : "std %1,%0"; } if (TARGET_ARCH64 && (op1 == const0_rtx || (GET_MODE (op1) != VOIDmode && op1 == CONST0_RTX (GET_MODE (op1))))) return "stx %r1,%0"; abort (); } else if (GET_CODE (op1) == MEM) { if (GET_CODE (op0) != REG) abort (); if (FP_REG_P (op0)) return "ldd %1,%0"; return TARGET_ARCH64 ? "ldx %1,%0" : "ldd %1,%0"; } else if (GET_CODE (operands[1]) == CONST_DOUBLE) { /* ??? Unfinished, and maybe not needed. */ abort (); } else if (GET_CODE (operands[1]) == CONST_INT && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'I')) { /* ??? Unfinished, and maybe not needed. */ abort (); } /* 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?