📄 expmed.c
字号:
if (temp != 0) return temp; } if (unsignedp) { /* No logical shift insn in either direction => try a bit-field extract instruction if we have one. */#ifdef HAVE_extzv#ifndef BITS_BIG_ENDIAN if (HAVE_extzv && !left && ((methods == OPTAB_DIRECT && mode == SImode) || (methods == OPTAB_WIDEN && GET_MODE_SIZE (mode) < GET_MODE_SIZE (SImode)))) { rtx shifted1 = convert_to_mode (SImode, shifted, 1); rtx target1 = target; /* If -fforce-mem, don't let the operand be in memory. */ if (flag_force_mem && GET_CODE (shifted1) == MEM) shifted1 = force_not_mem (shifted1); /* If this machine's extzv insists on a register for operand 1, arrange for that. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) (shifted1, SImode))) shifted1 = force_reg (SImode, shifted1); /* If we don't have or cannot use a suggested target, make a place for the result, in the proper mode. */ if (methods == OPTAB_WIDEN || target1 == 0 || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) (target1, SImode))) target1 = gen_reg_rtx (SImode); op1 = convert_to_mode (SImode, op1, 0); /* If this machine's extzv insists on a register for operand 3, arrange for that. */ if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3]) (op1, SImode))) op1 = force_reg (SImode, op1); op1 = protect_from_queue (op1, 1); /* TEMP gets the width of the bit field to extract: wordsize minus # bits to shift by. */ if (GET_CODE (op1) == CONST_INT) temp = gen_rtx (CONST_INT, VOIDmode, (GET_MODE_BITSIZE (mode) - INTVAL (op1))); else temp = expand_binop (SImode, sub_optab, gen_rtx (CONST_INT, VOIDmode, GET_MODE_BITSIZE (mode)), op1, gen_reg_rtx (SImode), 0, OPTAB_LIB_WIDEN); /* Now extract with width TEMP, omitting OP1 least sig bits. */ emit_insn (gen_extzv (protect_from_queue (target1, 1), protect_from_queue (shifted1, 0), temp, op1)); return convert_to_mode (mode, target1, 1); } /* Can also do logical shift with signed bit-field extract followed by inserting the bit-field at a different position. That strategy is not yet implemented. */#endif /* not BITS_BIG_ENDIAN */#endif /* HAVE_extzv */ /* We have failed to generate the logical shift and will abort. */ } } if (temp == 0) abort (); return temp;}/* Output an instruction or two to bitwise-and OP0 with OP1 in mode MODE, with output to TARGET if convenient and TARGET is not zero. Returns where the result is. *//* This function used to do more; now it could be eliminated. */rtxexpand_bit_and (mode, op0, op1, target) enum machine_mode mode; rtx op0, op1, target;{ register rtx temp; /* First try to open-code it directly. */ temp = expand_binop (mode, and_optab, op0, op1, target, 1, OPTAB_LIB_WIDEN); if (temp == 0) abort (); return temp;}/* Perform a multiplication and return an rtx for the result. MODE is mode of value; OP0 and OP1 are what to multiply (rtx's); TARGET is a suggestion for where to store the result (an rtx). We check specially for a constant integer as OP1. If you want this check for OP0 as well, then before calling you should swap the two operands if OP0 would be constant. */rtxexpand_mult (mode, op0, op1, target, unsignedp) enum machine_mode mode; register rtx op0, op1, target; int unsignedp;{ /* Don't use the function value register as a target since we have to read it as well as write it, and function-inlining gets confused by this. */ if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) target = 0; if (GET_CODE (op1) == CONST_INT) { register int foo; int bar; int negate = INTVAL (op1) < 0; int absval = INTVAL (op1) * (negate ? -1 : 1); /* Is multiplier a power of 2, or minus that? */ foo = exact_log2 (absval); if (foo >= 0) { rtx tem; if (foo == 0) tem = op0; else tem = expand_shift (LSHIFT_EXPR, mode, op0, build_int_2 (foo, 0), target, 0); return (negate ? expand_unop (mode, neg_optab, tem, target, 0) : tem); } /* Is multiplier a sum of two powers of 2, or minus that? */ bar = floor_log2 (absval); foo = exact_log2 (absval - (1 << bar)); if (bar >= 0 && foo >= 0) { rtx tem; /* Prevent multiple refs to a volatile location. */ if (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)) op0 = force_reg (mode, op0); tem = force_operand (gen_rtx (PLUS, mode, expand_shift (LSHIFT_EXPR, mode, op0, build_int_2 (bar - foo, 0), 0, 0), op0), ((foo == 0 && ! negate) ? target : 0)); if (foo != 0) tem = expand_shift (LSHIFT_EXPR, mode, tem, build_int_2 (foo, 0), negate ? 0 : target, 0); return negate ? expand_unop (mode, neg_optab, tem, target, 0) : tem; } } /* This used to use umul_optab if unsigned, but I think that for non-widening multiply there is no difference between signed and unsigned. */ op0 = expand_binop (mode, smul_optab, op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); if (op0 == 0) abort (); return op0;}/* Emit the code to divide OP0 by OP1, putting the result in TARGET if that is convenient, and returning where the result is. You may request either the quotient or the remainder as the result; specify REM_FLAG nonzero to get the remainder. CODE is the expression code for which kind of division this is; it controls how rounding is done. MODE is the machine mode to use. UNSIGNEDP nonzero means do unsigned division. *//* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI and then correct it by or'ing in missing high bits if result of ANDI is nonzero. For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result. This could optimize to a bfexts instruction. But C doesn't use these operations, so their optimizations are left for later. */rtxexpand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp) int rem_flag; enum tree_code code; enum machine_mode mode; register rtx op0, op1, target; int unsignedp;{ register rtx temp; int log = -1; int can_clobber_op0; int mod_insn_no_good = 0; rtx adjusted_op0 = op0; /* Don't use a non-register target. It could cause a crash in copy_to_suggested_reg when the value being copied has VOIDmode. */ if (target && GET_CODE (target) != REG) target = 0; /* Don't use the function value register as a target since we have to read it as well as write it, and function-inlining gets confused by this. */ if (target && REG_P (target) && REG_FUNCTION_VALUE_P (target)) target = 0; /* Don't clobber an operand while doing a multi-step calculation. */ if (target) if ((rem_flag && (reg_mentioned_p (target, op0) || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM))) || reg_mentioned_p (target, op1) || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)) target = 0; if (target == 0) target = gen_reg_rtx (mode); can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target); if (GET_CODE (op1) == CONST_INT) log = exact_log2 (INTVAL (op1)); /* If log is >= 0, we are dividing by 2**log, and will do it by shifting, which is really floor-division. Otherwise we will really do a divide, and we assume that is trunc-division. We must correct the dividend by adding or subtracting something based on the divisor, in order to do the kind of rounding specified by CODE. The correction depends on what kind of rounding is actually available, and that depends on whether we will shift or divide. */ switch (code) { case TRUNC_MOD_EXPR: case TRUNC_DIV_EXPR: if (log >= 0 && ! unsignedp) { rtx label = gen_label_rtx (); if (! can_clobber_op0) { adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); /* Copy op0 to a reg, since emit_cmp_insn will call emit_queue which will screw up mem refs for autoincrements. */ op0 = force_reg (mode, op0); } emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); emit_jump_insn (gen_bge (label)); expand_inc (adjusted_op0, plus_constant (op1, -1)); emit_label (label); mod_insn_no_good = 1; } break; case FLOOR_DIV_EXPR: case FLOOR_MOD_EXPR: if (log < 0 && ! unsignedp) { rtx label = gen_label_rtx (); if (! can_clobber_op0) { adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); op0 = force_reg (mode, op0); } emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); emit_jump_insn (gen_bge (label)); expand_dec (adjusted_op0, op1); expand_inc (adjusted_op0, const1_rtx); emit_label (label); mod_insn_no_good = 1; } break; case CEIL_DIV_EXPR: case CEIL_MOD_EXPR: if (! can_clobber_op0) { adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); op0 = force_reg (mode, op0); } if (log < 0) { rtx label = 0; if (! unsignedp) { label = gen_label_rtx (); emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); emit_jump_insn (gen_ble (label)); } expand_inc (adjusted_op0, op1); expand_dec (adjusted_op0, const1_rtx); if (! unsignedp) emit_label (label); } else { adjusted_op0 = expand_binop (GET_MODE (target), add_optab, adjusted_op0, plus_constant (op1, -1), 0, 0, OPTAB_LIB_WIDEN); } mod_insn_no_good = 1; break; case ROUND_DIV_EXPR: case ROUND_MOD_EXPR: if (! can_clobber_op0) { adjusted_op0 = copy_to_suggested_reg (adjusted_op0, target); op0 = force_reg (mode, op0); } if (log < 0) { op1 = expand_shift (RSHIFT_EXPR, mode, op1, integer_one_node, 0, 0); if (! unsignedp) { rtx label = gen_label_rtx (); emit_cmp_insn (adjusted_op0, const0_rtx, 0, 0, 0); emit_jump_insn (gen_bge (label)); expand_unop (mode, neg_optab, op1, op1, 0); emit_label (label); } expand_inc (adjusted_op0, op1); } else { op1 = gen_rtx (CONST_INT, VOIDmode, INTVAL (op1) / 2); expand_inc (adjusted_op0, op1); } mod_insn_no_good = 1; break; } if (rem_flag && !mod_insn_no_good) { /* Try to produce the remainder directly */ if (log >= 0) { return expand_bit_and (mode, adjusted_op0, gen_rtx (CONST_INT, VOIDmode, INTVAL (op1) - 1), target); } else { /* See if we can do remainder without a library call. */ temp = sign_expand_binop (mode, umod_optab, smod_optab, adjusted_op0, op1, target, unsignedp, OPTAB_WIDEN); if (temp != 0) return temp; /* No luck there. Can we do remainder and divide at once without a library call? */ temp = gen_reg_rtx (mode); if (expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, adjusted_op0, op1, 0, temp, unsignedp)) return temp; temp = 0; } } /* Produce the quotient. */ if (log >= 0) temp = expand_shift (RSHIFT_EXPR, mode, adjusted_op0, build_int_2 (exact_log2 (INTVAL (op1)), 0), target, unsignedp); else if (rem_flag && !mod_insn_no_good) /* If producing quotient in order to subtract for remainder, and a remainder subroutine would be ok, don't use a divide subroutine. */ temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, adjusted_op0, op1, target, unsignedp, OPTAB_WIDEN); else { /* Try a quotient insn, but not a library call. */ temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, adjusted_op0, op1, target, unsignedp, OPTAB_WIDEN); if (temp == 0) { /* No luck there. Try a quotient-and-remainder insn, keeping the quotient alone. */ temp = gen_reg_rtx (mode); if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, adjusted_op0, op1, temp, 0, unsignedp)) temp = 0; } /* If still no luck, use a library call. */ if (temp == 0) temp = sign_expand_binop (mode, udiv_optab, sdiv_optab, adjusted_op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); } /* If we really want the remainder, get it by subtraction. */ if (rem_flag) { if (temp == 0) { /* No divide instruction either. Use library for remainder. */ temp = sign_expand_binop (mode, umod_optab, smod_optab, op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); } else { /* We divided. Now finish doing X - Y * (X / Y). */ temp = expand_mult (mode, temp, op1, temp, unsignedp); if (! temp) abort (); temp = expand_binop (mode, sub_optab, op0, temp, target, unsignedp, OPTAB_LIB_WIDEN); } } if (temp == 0) abort (); return temp;}/* Return a tree node with data type TYPE, describing the value of X. Usually this is an RTL_EXPR, if there is no obvious better choice. */static treemake_tree (type, x) tree type; rtx x;{ tree t; switch (GET_CODE (x)) { case CONST_INT: t = build_int_2 (INTVAL (x), 0); TREE_TYPE (t) = type; return fold (t); default: t = make_node (RTL_EXPR); TREE_TYPE (t) = type; RTL_EXPR_RTL (t) = x; /* There are no insns to be output when this rtl_expr is used. */ RTL_EXPR_SEQUENCE (t) = 0; return t; }}/* Return an rtx representing the value of X * MULT + ADD. MODE is the machine mode for the computation. UNSIGNEDP is non-zero to do unsigned multiplication. This may emit insns. */rtxexpand_mult_add (x, mult, add, mode, unsignedp) rtx x, mult, add; enum machine_mode mode; int unsignedp;{ tree type = type_for_size (GET_MODE_BITSIZE (mode), unsignedp); tree prod = fold (build (MULT_EXPR, type, make_tree (type, x), make_tree (type, mult))); tree sum = fold (build (PLUS_EXPR, type, prod, make_tree (type, add))); return expand_expr (sum, 0, VOIDmode, 0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -