📄 xtensa.c
字号:
if (GET_CODE (offset) != CONST_INT) return FALSE; val = INTVAL (offset); return (val & 3) == 0 && (val >= 0 && val <= 60); } } return FALSE;}intconstantpool_address_p (rtx addr){ rtx sym = addr; if (GET_CODE (addr) == CONST) { rtx offset; /* Only handle (PLUS (SYM, OFFSET)) form. */ addr = XEXP (addr, 0); if (GET_CODE (addr) != PLUS) return FALSE; /* Make sure the address is word aligned. */ offset = XEXP (addr, 1); if ((GET_CODE (offset) != CONST_INT) || ((INTVAL (offset) & 3) != 0)) return FALSE; sym = XEXP (addr, 0); } if ((GET_CODE (sym) == SYMBOL_REF) && CONSTANT_POOL_ADDRESS_P (sym)) return TRUE; return FALSE;}intconstantpool_mem_p (rtx op){ if (GET_CODE (op) == MEM) return constantpool_address_p (XEXP (op, 0)); return FALSE;}voidxtensa_extend_reg (rtx dst, rtx src){ rtx temp = gen_reg_rtx (SImode); rtx shift = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (GET_MODE (src))); /* Generate paradoxical subregs as needed so that the modes match. */ src = simplify_gen_subreg (SImode, src, GET_MODE (src), 0); dst = simplify_gen_subreg (SImode, dst, GET_MODE (dst), 0); emit_insn (gen_ashlsi3 (temp, src, shift)); emit_insn (gen_ashrsi3 (dst, temp, shift));}boolxtensa_mem_offset (unsigned v, enum machine_mode mode){ switch (mode) { case BLKmode: /* Handle the worst case for block moves. See xtensa_expand_block_move where we emit an optimized block move operation if the block can be moved in < "move_ratio" pieces. The worst case is when the block is aligned but has a size of (3 mod 4) (does this happen?) so that the last piece requires a byte load/store. */ return (xtensa_uimm8 (v) && xtensa_uimm8 (v + MOVE_MAX * LARGEST_MOVE_RATIO)); case QImode: return xtensa_uimm8 (v); case HImode: return xtensa_uimm8x2 (v); case DFmode: return (xtensa_uimm8x4 (v) && xtensa_uimm8x4 (v + 4)); default: break; } return xtensa_uimm8x4 (v);}boolxtensa_extra_constraint (rtx op, int c){ /* Allow pseudo registers during reload. */ if (GET_CODE (op) != MEM) return (c >= 'R' && c <= 'U' && reload_in_progress && GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER); switch (c) { case 'R': return smalloffset_mem_p (op); case 'T': return !TARGET_CONST16 && constantpool_mem_p (op); case 'U': return !constantpool_mem_p (op); default: break; } return false;}/* Make normal rtx_code into something we can index from an array. */static enum internal_testmap_test_to_internal_test (enum rtx_code test_code){ enum internal_test test = ITEST_MAX; switch (test_code) { default: break; case EQ: test = ITEST_EQ; break; case NE: test = ITEST_NE; break; case GT: test = ITEST_GT; break; case GE: test = ITEST_GE; break; case LT: test = ITEST_LT; break; case LE: test = ITEST_LE; break; case GTU: test = ITEST_GTU; break; case GEU: test = ITEST_GEU; break; case LTU: test = ITEST_LTU; break; case LEU: test = ITEST_LEU; break; } return test;}/* Generate the code to compare two integer values. The return value is the comparison expression. */static rtxgen_int_relational (enum rtx_code test_code, /* relational test (EQ, etc) */ rtx cmp0, /* first operand to compare */ rtx cmp1, /* second operand to compare */ int *p_invert /* whether branch needs to reverse test */){ struct cmp_info { enum rtx_code test_code; /* test code to use in insn */ bool (*const_range_p) (HOST_WIDE_INT); /* range check function */ int const_add; /* constant to add (convert LE -> LT) */ int reverse_regs; /* reverse registers in test */ int invert_const; /* != 0 if invert value if cmp1 is constant */ int invert_reg; /* != 0 if invert value if cmp1 is register */ int unsignedp; /* != 0 for unsigned comparisons. */ }; static struct cmp_info info[ (int)ITEST_MAX ] = { { EQ, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* EQ */ { NE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* NE */ { LT, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 }, /* GT */ { GE, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* GE */ { LT, xtensa_b4const_or_zero, 0, 0, 0, 0, 0 }, /* LT */ { GE, xtensa_b4const_or_zero, 1, 1, 1, 0, 0 }, /* LE */ { LTU, xtensa_b4constu, 1, 1, 1, 0, 1 }, /* GTU */ { GEU, xtensa_b4constu, 0, 0, 0, 0, 1 }, /* GEU */ { LTU, xtensa_b4constu, 0, 0, 0, 0, 1 }, /* LTU */ { GEU, xtensa_b4constu, 1, 1, 1, 0, 1 }, /* LEU */ }; enum internal_test test; enum machine_mode mode; struct cmp_info *p_info; test = map_test_to_internal_test (test_code); gcc_assert (test != ITEST_MAX); p_info = &info[ (int)test ]; mode = GET_MODE (cmp0); if (mode == VOIDmode) mode = GET_MODE (cmp1); /* Make sure we can handle any constants given to us. */ if (GET_CODE (cmp1) == CONST_INT) { HOST_WIDE_INT value = INTVAL (cmp1); unsigned HOST_WIDE_INT uvalue = (unsigned HOST_WIDE_INT)value; /* if the immediate overflows or does not fit in the immediate field, spill it to a register */ if ((p_info->unsignedp ? (uvalue + p_info->const_add > uvalue) : (value + p_info->const_add > value)) != (p_info->const_add > 0)) { cmp1 = force_reg (mode, cmp1); } else if (!(p_info->const_range_p) (value + p_info->const_add)) { cmp1 = force_reg (mode, cmp1); } } else if ((GET_CODE (cmp1) != REG) && (GET_CODE (cmp1) != SUBREG)) { cmp1 = force_reg (mode, cmp1); } /* See if we need to invert the result. */ *p_invert = ((GET_CODE (cmp1) == CONST_INT) ? p_info->invert_const : p_info->invert_reg); /* Comparison to constants, may involve adding 1 to change a LT into LE. Comparison between two registers, may involve switching operands. */ if (GET_CODE (cmp1) == CONST_INT) { if (p_info->const_add != 0) cmp1 = GEN_INT (INTVAL (cmp1) + p_info->const_add); } else if (p_info->reverse_regs) { rtx temp = cmp0; cmp0 = cmp1; cmp1 = temp; } return gen_rtx_fmt_ee (p_info->test_code, VOIDmode, cmp0, cmp1);}/* Generate the code to compare two float values. The return value is the comparison expression. */static rtxgen_float_relational (enum rtx_code test_code, /* relational test (EQ, etc) */ rtx cmp0, /* first operand to compare */ rtx cmp1 /* second operand to compare */){ rtx (*gen_fn) (rtx, rtx, rtx); rtx brtmp; int reverse_regs, invert; switch (test_code) { case EQ: reverse_regs = 0; invert = 0; gen_fn = gen_seq_sf; break; case NE: reverse_regs = 0; invert = 1; gen_fn = gen_seq_sf; break; case LE: reverse_regs = 0; invert = 0; gen_fn = gen_sle_sf; break; case GT: reverse_regs = 1; invert = 0; gen_fn = gen_slt_sf; break; case LT: reverse_regs = 0; invert = 0; gen_fn = gen_slt_sf; break; case GE: reverse_regs = 1; invert = 0; gen_fn = gen_sle_sf; break; default: fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1)); reverse_regs = 0; invert = 0; gen_fn = 0; /* avoid compiler warnings */ } if (reverse_regs) { rtx temp = cmp0; cmp0 = cmp1; cmp1 = temp; } brtmp = gen_rtx_REG (CCmode, FPCC_REGNUM); emit_insn (gen_fn (brtmp, cmp0, cmp1)); return gen_rtx_fmt_ee (invert ? EQ : NE, VOIDmode, brtmp, const0_rtx);}voidxtensa_expand_conditional_branch (rtx *operands, enum rtx_code test_code){ enum cmp_type type = branch_type; rtx cmp0 = branch_cmp[0]; rtx cmp1 = branch_cmp[1]; rtx cmp; int invert; rtx label1, label2; switch (type) { case CMP_DF: default: fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1)); case CMP_SI: invert = FALSE; cmp = gen_int_relational (test_code, cmp0, cmp1, &invert); break; case CMP_SF: if (!TARGET_HARD_FLOAT) fatal_insn ("bad test", gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1)); invert = FALSE; cmp = gen_float_relational (test_code, cmp0, cmp1); break; } /* Generate the branch. */ label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]); label2 = pc_rtx; if (invert) { label2 = label1; label1 = pc_rtx; } emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, gen_rtx_IF_THEN_ELSE (VOIDmode, cmp, label1, label2)));}static rtxgen_conditional_move (rtx cmp){ enum rtx_code code = GET_CODE (cmp); rtx op0 = branch_cmp[0]; rtx op1 = branch_cmp[1]; if (branch_type == CMP_SI) { /* Jump optimization calls get_condition() which canonicalizes comparisons like (GE x <const>) to (GT x <const-1>). Transform those comparisons back to GE, since that is the comparison supported in Xtensa. We shouldn't have to transform <LE x const> comparisons, because neither xtensa_expand_conditional_branch() nor get_condition() will produce them. */ if ((code == GT) && (op1 == constm1_rtx)) { code = GE; op1 = const0_rtx; } cmp = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx); if (boolean_operator (cmp, VOIDmode)) { /* Swap the operands to make const0 second. */ if (op0 == const0_rtx) { op0 = op1; op1 = const0_rtx; } /* If not comparing against zero, emit a comparison (subtract). */ if (op1 != const0_rtx) { op0 = expand_binop (SImode, sub_optab, op0, op1, 0, 0, OPTAB_LIB_WIDEN); op1 = const0_rtx; } } else if (branch_operator (cmp, VOIDmode)) { /* Swap the operands to make const0 second. */ if (op0 == const0_rtx) { op0 = op1; op1 = const0_rtx; switch (code) { case LT: code = GE; break; case GE: code = LT; break; default: gcc_unreachable (); } } if (op1 != const0_rtx) return 0; } else return 0; return gen_rtx_fmt_ee (code, VOIDmode, op0, op1); } if (TARGET_HARD_FLOAT && (branch_type == CMP_SF)) return gen_float_relational (code, op0, op1); return 0;}intxtensa_expand_conditional_move (rtx *operands, int isflt){ rtx cmp; rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx); if (!(cmp = gen_conditional_move (operands[1]))) return 0; if (isflt) gen_fn = (branch_type == CMP_SI ? gen_movsfcc_internal0 : gen_movsfcc_internal1); else gen_fn = (branch_type == CMP_SI ? gen_movsicc_internal0 : gen_movsicc_internal1); emit_insn (gen_fn (operands[0], XEXP (cmp, 0), operands[2], operands[3], cmp)); return 1;}intxtensa_expand_scc (rtx *operands){ rtx dest = operands[0]; rtx cmp = operands[1]; rtx one_tmp, zero_tmp; rtx (*gen_fn) (rtx, rtx, rtx, rtx, rtx); if (!(cmp = gen_conditional_move (cmp))) return 0; one_tmp = gen_reg_rtx (SImode); zero_tmp = gen_reg_rtx (SImode); emit_insn (gen_movsi (one_tmp, const_true_rtx)); emit_insn (gen_movsi (zero_tmp, const0_rtx)); gen_fn = (branch_type == CMP_SI ? gen_movsicc_internal0 : gen_movsicc_internal1); emit_insn (gen_fn (dest, XEXP (cmp, 0), one_tmp, zero_tmp, cmp)); return 1;}/* Split OP[1] into OP[2,3] and likewise for OP[0] into OP[0,1]. MODE is for the output, i.e., the input operands are twice as big as MODE. */voidxtensa_split_operand_pair (rtx operands[4], enum machine_mode mode){ switch (GET_CODE (operands[1])) { case REG: operands[3] = gen_rtx_REG (mode, REGNO (operands[1]) + 1); operands[2] = gen_rtx_REG (mode, REGNO (operands[1])); break; case MEM: operands[3] = adjust_address (operands[1], mode, GET_MODE_SIZE (mode)); operands[2] = adjust_address (operands[1], mode, 0); break; case CONST_INT: case CONST_DOUBLE: split_double (operands[1], &operands[2], &operands[3]); break; default: gcc_unreachable (); } switch (GET_CODE (operands[0])) { case REG: operands[1] = gen_rtx_REG (mode, REGNO (operands[0]) + 1); operands[0] = gen_rtx_REG (mode, REGNO (operands[0])); break; case MEM: operands[1] = adjust_address (operands[0], mode, GET_MODE_SIZE (mode)); operands[0] = adjust_address (operands[0], mode, 0); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -