expand_mult (enum machine_mode mode, rtx op0, rtx op1, rtx target,
int unsignedp)
{
- rtx const_op1 = op1;
enum mult_variant variant;
struct algorithm algorithm;
+ int max_cost;
- /* synth_mult does an `unsigned int' multiply. As long as the mode is
- less than or equal in size to `unsigned int' this doesn't matter.
- If the mode is larger than `unsigned int', then synth_mult works only
- if the constant value exactly fits in an `unsigned int' without any
- truncation. This means that multiplying by negative values does
- not work; results are off by 2^32 on a 32 bit machine. */
-
- /* If we are multiplying in DImode, it may still be a win
- to try to work with shifts and adds. */
- if (GET_CODE (op1) == CONST_DOUBLE
- && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT
- && HOST_BITS_PER_INT >= BITS_PER_WORD
- && CONST_DOUBLE_HIGH (op1) == 0)
- const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
- else if (HOST_BITS_PER_INT < GET_MODE_BITSIZE (mode)
- && GET_CODE (op1) == CONST_INT
- && INTVAL (op1) < 0)
- const_op1 = 0;
-
- /* We used to test optimize here, on the grounds that it's better to
- produce a smaller program when -O is not used.
- But this causes such a terrible slowdown sometimes
- that it seems better to use synth_mult always. */
-
- if (const_op1 && GET_CODE (const_op1) == CONST_INT
+ /* Handling const0_rtx here allows us to use zero as a rogue value for
+ coeff below. */
+ if (op1 == const0_rtx)
+ return const0_rtx;
+ if (op1 == const1_rtx)
+ return op0;
+ if (op1 == constm1_rtx)
+ return expand_unop (mode,
+ GET_MODE_CLASS (mode) == MODE_INT
+ && !unsignedp && flag_trapv
+ ? negv_optab : neg_optab,
+ op0, target, 0);
+
+ /* These are the operations that are potentially turned into a sequence
+ of shifts and additions. */
+ if (GET_MODE_CLASS (mode) == MODE_INT
&& (unsignedp || !flag_trapv))
{
- HOST_WIDE_INT coeff = INTVAL (const_op1);
- int mult_cost;
+ HOST_WIDE_INT coeff = 0;
- /* Special case powers of two. */
- if (EXACT_POWER_OF_2_OR_ZERO_P (coeff))
+ /* synth_mult does an `unsigned int' multiply. As long as the mode is
+ less than or equal in size to `unsigned int' this doesn't matter.
+ If the mode is larger than `unsigned int', then synth_mult works
+ only if the constant value exactly fits in an `unsigned int' without
+ any truncation. This means that multiplying by negative values does
+ not work; results are off by 2^32 on a 32 bit machine. */
+
+ if (GET_CODE (op1) == CONST_INT)
{
- if (coeff == 0)
- return const0_rtx;
- if (coeff == 1)
- return op0;
- return expand_shift (LSHIFT_EXPR, mode, op0,
- build_int_cst (NULL_TREE, floor_log2 (coeff)),
- target, unsignedp);
+ /* Attempt to handle multiplication of DImode values by negative
+ coefficients, by performing the multiplication by a positive
+ multiplier and then inverting the result. */
+ if (INTVAL (op1) < 0
+ && GET_MODE_BITSIZE (mode) > HOST_BITS_PER_WIDE_INT)
+ {
+ /* Its safe to use -INTVAL (op1) even for INT_MIN, as the
+ result is interpreted as an unsigned coefficient. */
+ max_cost = rtx_cost (gen_rtx_MULT (mode, op0, op1), SET)
+ - neg_cost[mode];
+ if (max_cost > 0
+ && choose_mult_variant (mode, -INTVAL (op1), &algorithm,
+ &variant, max_cost))
+ {
+ rtx temp = expand_mult_const (mode, op0, -INTVAL (op1),
+ NULL_RTX, &algorithm,
+ variant);
+ return expand_unop (mode, neg_optab, temp, target, 0);
+ }
+ }
+ else coeff = INTVAL (op1);
+ }
+ else if (GET_CODE (op1) == CONST_DOUBLE)
+ {
+ /* If we are multiplying in DImode, it may still be a win
+ to try to work with shifts and adds. */
+ if (CONST_DOUBLE_HIGH (op1) == 0)
+ coeff = CONST_DOUBLE_LOW (op1);
+ else if (CONST_DOUBLE_LOW (op1) == 0
+ && EXACT_POWER_OF_2_OR_ZERO_P (CONST_DOUBLE_HIGH (op1)))
+ {
+ int shift = floor_log2 (CONST_DOUBLE_HIGH (op1))
+ + HOST_BITS_PER_WIDE_INT;
+ return expand_shift (LSHIFT_EXPR, mode, op0,
+ build_int_cst (NULL_TREE, shift),
+ target, unsignedp);
+ }
+ }
+
+ /* We used to test optimize here, on the grounds that it's better to
+ produce a smaller program when -O is not used. But this causes
+ such a terrible slowdown sometimes that it seems better to always
+ use synth_mult. */
+ if (coeff != 0)
+ {
+ /* Special case powers of two. */
+ if (EXACT_POWER_OF_2_OR_ZERO_P (coeff))
+ return expand_shift (LSHIFT_EXPR, mode, op0,
+ build_int_cst (NULL_TREE, floor_log2 (coeff)),
+ target, unsignedp);
+
+ max_cost = rtx_cost (gen_rtx_MULT (mode, op0, op1), SET);
+ if (choose_mult_variant (mode, coeff, &algorithm, &variant,
+ max_cost))
+ return expand_mult_const (mode, op0, coeff, target,
+ &algorithm, variant);
}
-
- mult_cost = rtx_cost (gen_rtx_MULT (mode, op0, op1), SET);
- if (choose_mult_variant (mode, coeff, &algorithm, &variant,
- mult_cost))
- return expand_mult_const (mode, op0, coeff, target,
- &algorithm, variant);
}
if (GET_CODE (op0) == CONST_DOUBLE)