📄 sparc.c
字号:
intmove_pic_label (op, mode) rtx op; enum machine_mode mode;{ /* Special case for PIC. */ if (flag_pic && GET_CODE (op) == LABEL_REF) return 1; return 0;}intsplittable_symbolic_memory_operand (op, mode) rtx op; enum machine_mode mode;{ if (GET_CODE (op) != MEM) return 0; if (! symbolic_operand (XEXP (op, 0), Pmode)) return 0; return 1;}intsplittable_immediate_memory_operand (op, mode) rtx op; enum machine_mode mode;{ if (GET_CODE (op) != MEM) return 0; if (! immediate_operand (XEXP (op, 0), Pmode)) return 0; return 1;}/* Return truth value of whether OP is EQ or NE. */inteq_or_neq (op, mode) rtx op; enum machine_mode mode;{ return (GET_CODE (op) == EQ || GET_CODE (op) == NE);}/* Return 1 if this is a comparison operator, but not an EQ, NE, GEU, or LTU for non-floating-point. We handle those specially. */intnormal_comp_operator (op, mode) rtx op; enum machine_mode mode;{ enum rtx_code code = GET_CODE (op); if (GET_RTX_CLASS (code) != '<') return 0; if (GET_MODE (XEXP (op, 0)) == CCFPmode || GET_MODE (XEXP (op, 0)) == CCFPEmode) return 1; return (code != NE && code != EQ && code != GEU && code != LTU);}/* Return 1 if this is a comparison operator. This allows the use of MATCH_OPERATOR to recognize all the branch insns. */intnoov_compare_op (op, mode) register rtx op; enum machine_mode mode;{ enum rtx_code code = GET_CODE (op); if (GET_RTX_CLASS (code) != '<') return 0; if (GET_MODE (XEXP (op, 0)) == CC_NOOVmode) /* These are the only branches which work with CC_NOOVmode. */ return (code == EQ || code == NE || code == GE || code == LT); return 1;}/* Nonzero if OP is a comparison operator suitable for use in v9 conditional move or branch on register contents instructions. */intv9_regcmp_op (op, mode) register rtx op; enum machine_mode mode;{ enum rtx_code code = GET_CODE (op); if (GET_RTX_CLASS (code) != '<') return 0; return v9_regcmp_p (code);}/* Return 1 if this is a SIGN_EXTEND or ZERO_EXTEND operation. */intextend_op (op, mode) rtx op; enum machine_mode mode;{ return GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;}/* Return nonzero if OP is an operator of mode MODE which can set the condition codes explicitly. We do not include PLUS and MINUS because these require CC_NOOVmode, which we handle explicitly. */intcc_arithop (op, mode) rtx op; enum machine_mode mode;{ if (GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR) return 1; return 0;}/* Return nonzero if OP is an operator of mode MODE which can bitwise complement its second operand and set the condition codes explicitly. */intcc_arithopn (op, mode) rtx op; enum machine_mode mode;{ /* XOR is not here because combine canonicalizes (xor (not ...) ...) and (xor ... (not ...)) to (not (xor ...)). */ return (GET_CODE (op) == AND || GET_CODE (op) == IOR);}/* Return true if OP is a register, or is a CONST_INT that can fit in a 13 bit immediate field. This is an acceptable SImode operand for most 3 address instructions. */intarith_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && SMALL_INT (op)));}/* Return true if OP is a register, or is a CONST_INT that can fit in an 11 bit immediate field. This is an acceptable SImode operand for the movcc instructions. */intarith11_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && ((unsigned) (INTVAL (op) + 0x400) < 0x800)));}/* Return true if OP is a register, or is a CONST_INT that can fit in an 10 bit immediate field. This is an acceptable SImode operand for the movrcc instructions. */intarith10_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && ((unsigned) (INTVAL (op) + 0x200) < 0x400)));}/* Return true if OP is a register, is a CONST_INT that fits in a 13 bit immediate field, or is a CONST_DOUBLE whose both parts fit in a 13 bit immediate field. v9: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that can fit in a 13 bit immediate field. This is an acceptable DImode operand for most 3 address instructions. */intarith_double_operand (op, mode) rtx op; enum machine_mode mode;{ return (register_operand (op, mode) || (GET_CODE (op) == CONST_INT && SMALL_INT (op)) || (! TARGET_V9 && GET_CODE (op) == CONST_DOUBLE && (unsigned) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 && (unsigned) (CONST_DOUBLE_HIGH (op) + 0x1000) < 0x2000) || (TARGET_V9 && GET_CODE (op) == CONST_DOUBLE && (unsigned) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 && ((CONST_DOUBLE_HIGH (op) == -1 && (CONST_DOUBLE_LOW (op) & 0x1000) == 0x1000) || (CONST_DOUBLE_HIGH (op) == 0 && (CONST_DOUBLE_LOW (op) & 0x1000) == 0))));}/* 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) (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) (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) (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;{ return (GET_CODE (op) == CONST_INT && SMALL_INT (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;{#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) < 0x100000000L)));#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;{ return (GET_CODE (op) == REG && call_used_regs[REGNO (op)]);}/* X and Y are two things to compare using CODE. Emit the compare insn and return the rtx for the cc reg in the proper mode. */rtxgen_compare_reg (code, x, y) enum rtx_code code; rtx x, y;{ enum machine_mode mode = SELECT_CC_MODE (code, x, y); rtx cc_reg; /* ??? We don't have movcc patterns so we cannot generate pseudo regs for the fpcc regs (cse can't tell they're really call clobbered regs and will remove a duplicate comparison even if there is an intervening function call - it will then try to reload the cc reg via an int reg which is why we need the movcc patterns). It is possible to provide the movcc patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two registers (say %g1,%g5) and it takes about 6 insns. A better fix would be to tell cse that CCFPE mode registers (even pseudos) are call clobbered. */ /* ??? This is an experiment. Rather than making changes to cse which may or may not be easy/clean, we do our own cse. This is possible because we will generate hard registers. Cse knows they're call clobbered (it doesn't know the same thing about pseudos). If we guess wrong, no big deal, but if we win, great! */ if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)#if 1 /* experiment */ { int reg; /* We cycle through the registers to ensure they're all exercised. */ static int next_fpcc_reg = 0; /* Previous x,y for each fpcc reg. */ static rtx prev_args[4][2]; /* Scan prev_args for x,y. */ for (reg = 0; reg < 4; reg++) if (prev_args[reg][0] == x && prev_args[reg][1] == y) break; if (reg == 4) { reg = next_fpcc_reg; prev_args[reg][0] = x; prev_args[reg][1] = y; next_fpcc_reg = (next_fpcc_reg + 1) & 3; } cc_reg = gen_rtx (REG, mode, reg + 96); }#else cc_reg = gen_reg_rtx (mode);#endif /* ! experiment */ else cc_reg = gen_rtx (REG, mode, 0); emit_insn (gen_rtx (SET, VOIDmode, cc_reg, gen_rtx (COMPARE, mode, x, y))); return cc_reg;}/* This function is used for v9 only. CODE is the code for an Scc's comparison. OPERANDS[0] is the target of the Scc insn. OPERANDS[1] is the value we compare against const0_rtx (which hasn't been generated yet). This function is needed to turn (set (reg:SI 110) (gt (reg:CCX 0 %g0) (const_int 0))) into (set (reg:SI 110) (gt:DI (reg:CCX 0 %g0) (const_int 0))) IE: The instruction recognizer needs to see the mode of the comparison to find the right instruction. We could use "gt:DI" right in the define_expand, but leaving it out allows us to handle DI, SI, etc. We refer to the global sparc compare operands sparc_compare_op0 and sparc_compare_op1. ??? Some of this is outdated as the scc insns set the mode of the comparison now. ??? We optimize for the case where op1 is 0 and the comparison allows us to use the "movrCC" insns. This reduces the generated code from three to two insns. This way seems too brute force though. Is there a more elegant way to achieve the same effect? Currently, this function always returns 1. ??? Can it ever fail? */intgen_v9_scc (compare_code, operands) enum rtx_code compare_code; register rtx *operands;{ rtx temp; if (GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) == MODE_INT && sparc_compare_op1 == const0_rtx && (compare_code == EQ || compare_code == NE || compare_code == LT || compare_code == LE || compare_code == GT || compare_code == GE)) { /* Special case for op0 != 0. This can be done with one instruction if op0 can be clobbered. We store to a temp, and then clobber the temp, but the combiner will remove the first insn. */ if (compare_code == NE && GET_MODE (operands[0]) == DImode && GET_MODE (sparc_compare_op0) == DImode) { emit_insn (gen_rtx (SET, VOIDmode, operands[0], sparc_compare_op0)); emit_insn (gen_rtx (SET, VOIDmode, operands[0], gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (compare_code, DImode, sparc_compare_op0, const0_rtx), const1_rtx, operands[0]))); return 1; } emit_insn (gen_rtx (SET, VOIDmode, operands[0], const0_rtx)); if (GET_MODE (sparc_compare_op0) != DImode) { temp = gen_reg_rtx (DImode); convert_move (temp, sparc_compare_op0, 0); } else { temp = sparc_compare_op0; } emit_insn (gen_rtx (SET, VOIDmode, operands[0], gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (compare_code, DImode, temp, const0_rtx), const1_rtx, operands[0]))); return 1; } else { operands[1] = gen_compare_reg (compare_code, sparc_compare_op0, sparc_compare_op1); switch (GET_MODE (operands[1])) { case CCmode :
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -