📄 sparc.c
字号:
/* Return true if OP is suitable as second operand for add/sub in DImode */intarith_double_add_operand (op, mode) rtx op; enum machine_mode mode;{ return arith_double_operand (op, mode) || arith_double_4096_operand (op, mode);}/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that can fit in an 11 bit immediate field. This is an acceptable DImode operand for the movcc instructions. *//* ??? Replace with arith11_operand? */intarith11_double_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_DOUBLE && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) && (unsigned HOST_WIDE_INT) (CONST_DOUBLE_LOW (op) + 0x400) < 0x800 && ((CONST_DOUBLE_HIGH (op) == -1 && (CONST_DOUBLE_LOW (op) & 0x400) == 0x400) || (CONST_DOUBLE_HIGH (op) == 0 && (CONST_DOUBLE_LOW (op) & 0x400) == 0))) || (GET_CODE (op) == CONST_INT && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x400) < 0x800));}/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that can fit in an 10 bit immediate field. This is an acceptable DImode operand for the movrcc instructions. *//* ??? Replace with arith10_operand? */intarith10_double_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_DOUBLE && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) && (unsigned) (CONST_DOUBLE_LOW (op) + 0x200) < 0x400 && ((CONST_DOUBLE_HIGH (op) == -1 && (CONST_DOUBLE_LOW (op) & 0x200) == 0x200) || (CONST_DOUBLE_HIGH (op) == 0 && (CONST_DOUBLE_LOW (op) & 0x200) == 0))) || (GET_CODE (op) == CONST_INT && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) && (unsigned HOST_WIDE_INT) (INTVAL (op) + 0x200) < 0x400));}/* Return truth value of whether OP is a integer which fits the range constraining immediate operands in most three-address insns, which have a 13 bit immediate field. */intsmall_int (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == CONST_INT && SMALL_INT (op));}intsmall_int_or_double (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return ((GET_CODE (op) == CONST_INT && SMALL_INT (op)) || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0 && SPARC_SIMM13_P (CONST_DOUBLE_LOW (op))));}/* Recognize operand values for the umul instruction. That instruction sign extends immediate values just like all other sparc instructions, but interprets the extended result as an unsigned number. */intuns_small_int (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{#if HOST_BITS_PER_WIDE_INT > 32 /* All allowed constants will fit a CONST_INT. */ return (GET_CODE (op) == CONST_INT && ((INTVAL (op) >= 0 && INTVAL (op) < 0x1000) || (INTVAL (op) >= 0xFFFFF000 && INTVAL (op) < 0x100000000)));#else return ((GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x1000) || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0 && (unsigned) CONST_DOUBLE_LOW (op) - 0xFFFFF000 < 0x1000));#endif}intuns_arith_operand (op, mode) rtx op; enum machine_mode mode;{ return register_operand (op, mode) || uns_small_int (op, mode);}/* Return truth value of statement that OP is a call-clobbered register. */intclobbered_register (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]);}/* Return 1 if OP is const0_rtx, used for TARGET_LIVE_G0 insns. */intzero_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return op == const0_rtx;}/* Return 1 if OP is a valid operand for the source of a move insn. */intinput_operand (op, mode) rtx op; enum machine_mode mode;{ /* If both modes are non-void they must be the same. */ if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; /* Only a tiny bit of handling for CONSTANT_P_RTX is necessary. */ if (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == CONSTANT_P_RTX) return 1; /* Allow any one instruction integer constant, and all CONST_INT variants when we are working in DImode and !arch64. */ if (GET_MODE_CLASS (mode) == MODE_INT && ((GET_CODE (op) == CONST_INT && ((SPARC_SETHI_P (INTVAL (op)) && (! TARGET_ARCH64 || (INTVAL (op) >= 0) || mode == SImode)) || SPARC_SIMM13_P (INTVAL (op)) || (mode == DImode && ! TARGET_ARCH64))) || (TARGET_ARCH64 && GET_CODE (op) == CONST_DOUBLE && ((CONST_DOUBLE_HIGH (op) == 0 && SPARC_SETHI_P (CONST_DOUBLE_LOW (op))) ||#if HOST_BITS_PER_WIDE_INT == 64 (CONST_DOUBLE_HIGH (op) == 0 && SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)))#else (SPARC_SIMM13_P (CONST_DOUBLE_LOW (op)) && (((CONST_DOUBLE_LOW (op) & 0x80000000) == 0 && CONST_DOUBLE_HIGH (op) == 0) || (CONST_DOUBLE_HIGH (op) == -1)))#endif )))) return 1; /* If !arch64 and this is a DImode const, allow it so that the splits can be generated. */ if (! TARGET_ARCH64 && mode == DImode && GET_CODE (op) == CONST_DOUBLE) return 1; if (register_operand (op, mode)) return 1; /* If this is a SUBREG, look inside so that we handle paradoxical ones. */ if (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); /* Check for valid MEM forms. */ if (GET_CODE (op) == MEM) { rtx inside = XEXP (op, 0); if (GET_CODE (inside) == LO_SUM) { /* We can't allow these because all of the splits (eventually as they trickle down into DFmode splits) require offsettable memory references. */ if (! TARGET_V9 && GET_MODE (op) == TFmode) return 0; return (register_operand (XEXP (inside, 0), Pmode) && CONSTANT_P (XEXP (inside, 1))); } return memory_address_p (mode, inside); } return 0;}/* We know it can't be done in one insn when we get here, the movsi expander guarentees this. */voidsparc_emit_set_const32 (op0, op1) rtx op0; rtx op1;{ enum machine_mode mode = GET_MODE (op0); rtx temp; if (GET_CODE (op1) == CONST_INT) { HOST_WIDE_INT value = INTVAL (op1); if (SPARC_SETHI_P (value) || SPARC_SIMM13_P (value)) abort (); } /* Full 2-insn decomposition is needed. */ if (reload_in_progress || reload_completed) temp = op0; else temp = gen_reg_rtx (mode); if (GET_CODE (op1) == CONST_INT) { /* Emit them as real moves instead of a HIGH/LO_SUM, this way CSE can see everything and reuse intermediate values if it wants. */ if (TARGET_ARCH64 && HOST_BITS_PER_WIDE_INT != 64 && (INTVAL (op1) & 0x80000000) != 0) { emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, INTVAL (op1) & 0xfffffc00, 0))); } else { emit_insn (gen_rtx_SET (VOIDmode, temp, GEN_INT (INTVAL (op1) & 0xfffffc00))); } emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_IOR (mode, temp, GEN_INT (INTVAL (op1) & 0x3ff)))); } else { /* A symbol, emit in the traditional way. */ emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_HIGH (mode, op1))); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (mode, temp, op1))); }}/* Sparc-v9 code-model support. */voidsparc_emit_set_symbolic_const64 (op0, op1, temp1) rtx op0; rtx op1; rtx temp1;{ switch (sparc_cmodel) { case CM_MEDLOW: /* The range spanned by all instructions in the object is less than 2^31 bytes (2GB) and the distance from any instruction to the location of the label _GLOBAL_OFFSET_TABLE_ is less than 2^31 bytes (2GB). The executable must be in the low 4TB of the virtual address space. sethi %hi(symbol), %temp or %temp, %lo(symbol), %reg */ emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1))); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1))); break; case CM_MEDMID: /* The range spanned by all instructions in the object is less than 2^31 bytes (2GB) and the distance from any instruction to the location of the label _GLOBAL_OFFSET_TABLE_ is less than 2^31 bytes (2GB). The executable must be in the low 16TB of the virtual address space. sethi %h44(symbol), %temp1 or %temp1, %m44(symbol), %temp2 sllx %temp2, 12, %temp3 or %temp3, %l44(symbol), %reg */ emit_insn (gen_seth44 (op0, op1)); emit_insn (gen_setm44 (op0, op0, op1)); emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_ASHIFT (DImode, op0, GEN_INT (12)))); emit_insn (gen_setl44 (op0, temp1, op1)); break; case CM_MEDANY: /* The range spanned by all instructions in the object is less than 2^31 bytes (2GB) and the distance from any instruction to the location of the label _GLOBAL_OFFSET_TABLE_ is less than 2^31 bytes (2GB). The executable can be placed anywhere in the virtual address space. sethi %hh(symbol), %temp1 sethi %lm(symbol), %temp2 or %temp1, %hm(symbol), %temp3 or %temp2, %lo(symbol), %temp4 sllx %temp3, 32, %temp5 or %temp4, %temp5, %reg */ /* Getting this right wrt. reloading is really tricky. We _MUST_ have a seperate temporary at this point, if we don't barf immediately instead of generating incorrect code. */ if (temp1 == op0) abort (); emit_insn (gen_sethh (op0, op1)); emit_insn (gen_setlm (temp1, op1)); emit_insn (gen_sethm (op0, op0, op1)); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_ASHIFT (DImode, op0, GEN_INT (32)))); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_PLUS (DImode, op0, temp1))); emit_insn (gen_setlo (op0, op0, op1)); break; case CM_EMBMEDANY: /* Old old old backwards compatibility kruft here. Essentially it is MEDLOW with a fixed 64-bit virtual base added to all data segment addresses. Text-segment stuff is computed like MEDANY, we can't reuse the code above because the relocation knobs look different. Data segment: sethi %hi(symbol), %temp1 or %temp1, %lo(symbol), %temp2 add %temp2, EMBMEDANY_BASE_REG, %reg Text segment: sethi %uhi(symbol), %temp1 sethi %hi(symbol), %temp2 or %temp1, %ulo(symbol), %temp3 or %temp2, %lo(symbol), %temp4 sllx %temp3, 32, %temp5 or %temp4, %temp5, %reg */ if (data_segment_operand (op1, GET_MODE (op1))) { emit_insn (gen_embmedany_sethi (temp1, op1)); emit_insn (gen_embmedany_brsum (op0, temp1)); emit_insn (gen_embmedany_losum (op0, op0, op1)); } else { /* Getting this right wrt. reloading is really tricky. We _MUST_ have a seperate temporary at this point, so we barf immediately instead of generating incorrect code. */ if (temp1 == op0) abort (); emit_insn (gen_embmedany_textuhi (op0, op1)); emit_insn (gen_embmedany_texthi (temp1, op1)); emit_insn (gen_embmedany_textulo (op0, op0, op1)); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_ASHIFT (DImode, op0, GEN_INT (32)))); emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_PLUS (DImode, op0, temp1))); emit_insn (gen_embmedany_textlo (op0, op0, op1)); } break; default: abort(); }}/* These avoid problems when cross compiling. If we do not go through all this hair then the optimizer will see invalid REG_EQUAL notes or in some cases none at all. */static void sparc_emit_set_safe_HIGH64 PROTO ((rtx, HOST_WIDE_INT));static rtx gen_safe_SET64 PROTO ((rtx, HOST_WIDE_INT));static rtx gen_safe_OR64 PROTO ((rtx, HOST_WIDE_INT));static rtx gen_safe_XOR64 PROTO ((rtx, HOST_WIDE_INT));#if HOST_BITS_PER_WIDE_INT == 64#define GEN_HIGHINT64(__x) GEN_INT ((__x) & 0xfffffc00)#define GEN_INT64(__x) GEN_INT (__x)#else#define GEN_HIGHINT64(__x) \ gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \ (__x) & 0xfffffc00, 0)#define GEN_INT64(__x) \ gen_rtx_CONST_DOUBLE (VOIDmode, const0_rtx, \ (__x) & 0xffffffff, \ ((__x) & 0x80000000 \ ? 0xffffffff : 0))#endif/* The optimizer is not to assume anything about exactly which bits are set for a HIGH, they are unspecified. Unfortunately this leads to many missed optimizations during CSE. We mask out the non-HIGH bits, and matches a plain movdi, to alleviate this problem. */static voidsparc_emit_set_safe_HIGH64 (dest, val) rtx dest; HOST_WIDE_INT val;{ emit_insn (gen_rtx_SET (VOIDmode, dest, GEN_HIGHINT64 (val)));}static rtxgen_safe_SET64 (dest, val) rtx dest; HOST_WIDE_INT val;{ return gen_rtx_SET (VOIDmode, dest, GEN_INT64 (val));}static rtxgen_safe_OR64 (src, val) rtx src; HOST_WIDE_INT val;{ return gen_rtx_IOR (DImode, src, GEN_INT64 (val));}static rtxgen_safe_XOR64 (src, val) rtx src; HOST_WIDE_INT val;{ return gen_rtx_XOR (DImode, src, GEN_INT64 (val));}/* Worker routines for 64-bit constant formation on arch64. One of the key things to be doing in these emissions is to create as many temp REGs as possible. This makes it possible for half-built constants to be used later when such values are similar to something required later on. Without doing this, the optimizer cannot see such opportunities. */static void sparc_emit_set_const64_quick1 PROTO((rtx, rtx, unsigned HOST_WIDE_INT, int));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -