aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Sayle <roger@eyesopen.com>2004-06-28 20:49:37 +0000
committerRoger Sayle <sayle@gcc.gnu.org>2004-06-28 20:49:37 +0000
commit0b55e9321430150f742bc7be3f7da16b1a3872d5 (patch)
tree20aed07aae396652217e2491603ed7111c8fbe05
parent3b50d22e799b48d8ae4d6cc5ed7bbc9d4c0dc39f (diff)
downloadgcc-0b55e9321430150f742bc7be3f7da16b1a3872d5.zip
gcc-0b55e9321430150f742bc7be3f7da16b1a3872d5.tar.gz
gcc-0b55e9321430150f742bc7be3f7da16b1a3872d5.tar.bz2
expmed.c (expand_smod_pow2): New function to expand signed remainder by a constant power of 2, such as "x % 16".
* expmed.c (expand_smod_pow2): New function to expand signed remainder by a constant power of 2, such as "x % 16". (expand_divmod): Call new expand_smod_pow2 when appropriate. Minor corrections to comments, e.g. wrapping long lines. From-SVN: r83815
-rw-r--r--gcc/ChangeLog7
-rw-r--r--gcc/expmed.c83
2 files changed, 84 insertions, 6 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5690035..f86f11a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2004-06-28 Roger Sayle <roger@eyesopen.com>
+
+ * expmed.c (expand_smod_pow2): New function to expand signed
+ remainder by a constant power of 2, such as "x % 16".
+ (expand_divmod): Call new expand_smod_pow2 when appropriate.
+ Minor corrections to comments, e.g. wrapping long lines.
+
2004-06-28 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.in (vec.o): Fix dependencies.
diff --git a/gcc/expmed.c b/gcc/expmed.c
index c862503..6316f99 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -51,6 +51,7 @@ static rtx lshift_value (enum machine_mode, rtx, int, int);
static rtx extract_split_bit_field (rtx, unsigned HOST_WIDE_INT,
unsigned HOST_WIDE_INT, int);
static void do_cmp_and_jump (rtx, rtx, enum rtx_code, enum machine_mode, rtx);
+static rtx expand_smod_pow2 (enum machine_mode, rtx, HOST_WIDE_INT);
/* Nonzero means divides or modulus operations are relatively cheap for
powers of two, so don't use branches; emit the operation instead.
@@ -3055,6 +3056,72 @@ expand_mult_highpart (enum machine_mode mode, rtx op0,
return expand_mult_highpart_optab (mode, op0, op1, target,
unsignedp, max_cost);
}
+
+
+/* Expand signed modulus of OP0 by a power of two D in mode MODE. */
+
+static rtx
+expand_smod_pow2 (enum machine_mode mode, rtx op0, HOST_WIDE_INT d)
+{
+ unsigned HOST_WIDE_INT mask;
+ rtx result, temp, label;
+ int logd;
+
+ logd = floor_log2 (d);
+ result = gen_reg_rtx (mode);
+
+ /* Avoid conditional branches when they're expensive. */
+ if (BRANCH_COST >= 2
+ && !optimize_size)
+ {
+ rtx signmask = emit_store_flag (result, LT, op0, const0_rtx,
+ mode, 0, -1);
+ if (signmask)
+ {
+ signmask = force_reg (mode, signmask);
+ temp = expand_binop (mode, xor_optab, op0, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, sub_optab, temp, signmask,
+ NULL_RTX, 0, OPTAB_LIB_WIDEN);
+ mask = ((HOST_WIDE_INT) 1 << logd) - 1;
+ temp = expand_binop (mode, and_optab, temp, GEN_INT (mask),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, xor_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, sub_optab, temp, signmask,
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ return temp;
+ }
+ }
+
+ /* Mask contains the mode's signbit and the significant bits of the
+ modulus. By including the signbit in the operation, many targets
+ can avoid an explicit compare operation in the following comparison
+ against zero. */
+
+ mask = (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)
+ | (((HOST_WIDE_INT) 1 << logd) - 1);
+
+ temp = expand_binop (mode, and_optab, op0, GEN_INT (mask), result,
+ 1, OPTAB_LIB_WIDEN);
+ if (temp != result)
+ emit_move_insn (result, temp);
+
+ label = gen_label_rtx ();
+ do_cmp_and_jump (result, const0_rtx, GE, mode, label);
+
+ temp = expand_binop (mode, sub_optab, result, const1_rtx, result,
+ 0, OPTAB_LIB_WIDEN);
+ mask = (HOST_WIDE_INT) -1 << logd;
+ temp = expand_binop (mode, ior_optab, temp, GEN_INT (mask), result,
+ 1, OPTAB_LIB_WIDEN);
+ temp = expand_binop (mode, add_optab, temp, const1_rtx, result,
+ 0, OPTAB_LIB_WIDEN);
+ if (temp != result)
+ emit_move_insn (result, temp);
+ emit_label (label);
+ return result;
+}
/* Emit the code to divide OP0 by OP1, putting the result in TARGET
if that is convenient, and returning where the result is.
@@ -3451,10 +3518,8 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode,
else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
&& (rem_flag ? smod_pow2_cheap[compute_mode]
: sdiv_pow2_cheap[compute_mode])
- /* ??? The cheap metric is computed only for
- word_mode. If this operation is wider, this may
- not be so. Assume true if the optab has an
- expander for this mode. */
+ /* We assume that cheap metric is true if the
+ optab has an expander for this mode. */
&& (((rem_flag ? smod_optab : sdiv_optab)
->handlers[compute_mode].insn_code
!= CODE_FOR_nothing)
@@ -3463,6 +3528,12 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode,
;
else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
{
+ if (rem_flag)
+ {
+ remainder = expand_smod_pow2 (compute_mode, op0, d);
+ if (remainder)
+ return gen_lowpart (mode, remainder);
+ }
lgup = floor_log2 (abs_d);
if (BRANCH_COST < 1 || (abs_d != 2 && BRANCH_COST < 3))
{
@@ -3496,8 +3567,8 @@ expand_divmod (int rem_flag, enum tree_code code, enum machine_mode mode,
tquotient, 0);
}
- /* We have computed OP0 / abs(OP1). If OP1 is negative, negate
- the quotient. */
+ /* We have computed OP0 / abs(OP1). If OP1 is negative,
+ negate the quotient. */
if (d < 0)
{
insn = get_last_insn ();