aboutsummaryrefslogtreecommitdiff
path: root/gcc/ifcvt.cc
diff options
context:
space:
mode:
authorJerry DeLisle <jvdelisle@gcc.gnu.org>2025-09-02 15:58:26 -0700
committerJerry DeLisle <jvdelisle@gcc.gnu.org>2025-09-02 15:58:26 -0700
commit071b4126c613881f4cb25b4e5c39032964827f88 (patch)
tree7ed805786566918630d1d617b1ed8f7310f5fd8e /gcc/ifcvt.cc
parent845d23f3ea08ba873197c275a8857eee7edad996 (diff)
parentcaa1c2f42691d68af4d894a5c3e700ecd2dba080 (diff)
downloadgcc-devel/gfortran-test.zip
gcc-devel/gfortran-test.tar.gz
gcc-devel/gfortran-test.tar.bz2
Merge branch 'master' into gfortran-testdevel/gfortran-test
Diffstat (limited to 'gcc/ifcvt.cc')
-rw-r--r--gcc/ifcvt.cc207
1 files changed, 207 insertions, 0 deletions
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index a0c6575..4579148 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -1179,6 +1179,211 @@ noce_try_move (struct noce_if_info *if_info)
return false;
}
+/* If a sign bit test is selecting across constants, we may be able
+ to generate efficient code utilizing the -1/0 result of a sign
+ bit splat idiom.
+
+ Do this before trying the generalized conditional move as these
+ (when applicable) are hopefully faster than a conditional move. */
+
+static bool
+noce_try_sign_bit_splat (struct noce_if_info *if_info)
+{
+ rtx cond = if_info->cond;
+ enum rtx_code code = GET_CODE (cond);
+
+ /* We're looking for sign bit tests, so only a few cases are
+ interesting. LT/GE 0 LE/GT -1. */
+ if (((code == LT || code == GE)
+ && XEXP (cond, 1) == CONST0_RTX (GET_MODE (cond)))
+ || ((code == LE || code == GT)
+ && XEXP (cond, 1) == CONSTM1_RTX (GET_MODE (cond))))
+ ;
+ else
+ return false;
+
+ /* It would be good if this could be extended since constant synthesis
+ on some platforms will result in blocks which fail this test. */
+ if (!noce_simple_bbs (if_info))
+ return false;
+
+ /* Only try this for constants in the true/false arms and a REG
+ destination. We could select between 0 and a REG pretty
+ easily with a logical AND. */
+ if (!CONST_INT_P (if_info->a)
+ || !CONST_INT_P (if_info->b)
+ || !REG_P (if_info->x))
+ return false;
+
+ machine_mode mode = GET_MODE (if_info->x);
+
+ /* If the mode of the destination does not match the mode of
+ the value we're testing, then this optimization is not valid. */
+ if (mode != GET_MODE (XEXP (cond, 0)))
+ return false;
+
+ HOST_WIDE_INT val_a = INTVAL (if_info->a);
+ HOST_WIDE_INT val_b = INTVAL (if_info->b);
+
+ rtx_insn *seq;
+ start_sequence ();
+
+ /* We're testing the sign bit of this operand. */
+ rtx condop = XEXP (cond, 0);
+
+ /* To splat the sign bit we arithmetically shift the
+ input value right by the size of the object - 1 bits.
+
+ Note some targets do not have strong shifters, but do have
+ alternative ways to generate the sign bit splat. The
+ profitability test when we end the sequence should reject
+ cases when the branchy sequence is better. */
+ int splat_count = GET_MODE_BITSIZE (GET_MODE (condop)).to_constant () - 1;
+ rtx splat = GEN_INT (splat_count);
+
+ rtx temp;
+ temp = expand_simple_binop (GET_MODE (XEXP (cond, 0)), ASHIFTRT,
+ XEXP (cond, 0), splat, NULL_RTX,
+ false, OPTAB_WIDEN);
+ if (!temp)
+ goto fail;
+
+ /* IOR of anything with -1 still results in -1. So we can
+ IOR the other operand to generate a select between -1 and
+ an arbitrary constant. */
+ if (val_a == -1)
+ {
+ if (code == LT || code == LE)
+ {
+ temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true);
+ if (!temp)
+ goto fail;
+ }
+
+ temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_b),
+ if_info->x, false, OPTAB_WIDEN);
+ }
+ /* AND of anything with 0 is still zero. So we can AND
+ with the -1 operand with the a constant to select
+ between the constant and zero. */
+ else if (val_b == 0)
+ {
+ if (code == LT || code == LE)
+ {
+ temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true);
+ if (!temp)
+ goto fail;
+ }
+
+ /* Since we know the value is currenly -1 or 0, some constants may
+ be more easily handled by shifting the value again. A right
+ logical shift constructs 2^n-1 constants a left shift constructs
+ ~(2^n-1) constants. Given some targets don't have efficient
+ shifts, generate the obvious RTL for both forms and select the
+ one with smaller cost. */
+ rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_a));
+ rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_a)));
+ HOST_WIDE_INT rshift_count
+ = (clz_hwi (val_a) & (GET_MODE_PRECISION (mode).to_constant() - 1));
+ rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (rshift_count));
+ bool speed_p = optimize_insn_for_speed_p ();
+ if (exact_log2 (val_a + 1) >= 0
+ && (rtx_cost (shift_right, mode, SET, 1, speed_p)
+ <= rtx_cost (and_form, mode, SET, 1, speed_p)))
+ temp = expand_simple_binop (mode, LSHIFTRT, temp,
+ GEN_INT (rshift_count),
+ if_info->x, false, OPTAB_WIDEN);
+ else if (exact_log2 (~val_a + 1) >= 0
+ && (rtx_cost (shift_left, mode, SET, 1, speed_p)
+ <= rtx_cost (and_form, mode, SET, 1, speed_p)))
+ temp = expand_simple_binop (mode, ASHIFT, temp,
+ GEN_INT (ctz_hwi (val_a)),
+ if_info->x, false, OPTAB_WIDEN);
+ else
+ temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_a),
+ if_info->x, false, OPTAB_WIDEN);
+ }
+ /* Same cases, but with the test or arms swapped. These
+ can be realized as well, though it typically costs
+ an extra instruction. */
+ else if (val_b == -1)
+ {
+ if (code != LT && code != LE)
+ {
+ temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true);
+ if (!temp)
+ goto fail;
+ }
+
+ temp = expand_simple_binop (mode, IOR, temp, GEN_INT (val_a),
+ if_info->x, false, OPTAB_WIDEN);
+ }
+ else if (val_a == 0)
+ {
+ if (code != LT && code != LE)
+ {
+ temp = expand_simple_unop (mode, NOT, temp, NULL_RTX, true);
+ if (!temp)
+ goto fail;
+ }
+
+ /* Since we know the value is currenly -1 or 0, some constants may
+ be more easily handled by shifting the value again. A right
+ logical shift constructs 2^n-1 constants a left shift constructs
+ ~(2^n-1) constants. Given some targets don't have efficient
+ shifts, generate the obvious RTL for both forms and select the
+ one with smaller cost. */
+ rtx and_form = gen_rtx_AND (mode, temp, GEN_INT (val_b));
+ rtx shift_left = gen_rtx_ASHIFT (mode, temp, GEN_INT (ctz_hwi (val_b)));
+ HOST_WIDE_INT rshift_count
+ = (clz_hwi (val_b) & (GET_MODE_PRECISION (mode).to_constant() - 1));
+ rtx shift_right = gen_rtx_LSHIFTRT (mode, temp, GEN_INT (rshift_count));
+ bool speed_p = optimize_insn_for_speed_p ();
+ if (exact_log2 (val_b + 1) >= 0
+ && (rtx_cost (shift_right, mode, SET, 1, speed_p)
+ <= rtx_cost (and_form, mode, SET, 1, speed_p)))
+ temp = expand_simple_binop (mode, LSHIFTRT, temp,
+ GEN_INT (rshift_count),
+ if_info->x, false, OPTAB_WIDEN);
+ else if (exact_log2 (~val_b + 1) >= 0
+ && (rtx_cost (shift_left, mode, SET, 1, speed_p)
+ <= rtx_cost (and_form, mode, SET, 1, speed_p)))
+ temp = expand_simple_binop (mode, ASHIFT, temp,
+ GEN_INT (ctz_hwi (val_b)),
+ if_info->x, false, OPTAB_WIDEN);
+ else
+ temp = expand_simple_binop (mode, AND, temp, GEN_INT (val_b),
+ if_info->x, false, OPTAB_WIDEN);
+ }
+ /* Nothing worked. */
+ else
+ temp = NULL_RTX;
+
+ if (!temp)
+ goto fail;
+
+ /* Move into the final destination if the value wasn't
+ constructed there. */
+ if (if_info->x != temp)
+ emit_move_insn (if_info->x, temp);
+
+ /* This ends the sequence and tests the cost model. */
+ seq = end_ifcvt_sequence (if_info);
+ if (!seq || !targetm.noce_conversion_profitable_p (seq, if_info))
+ return false;
+
+ /* Everything looks good. Install the if-converted sequence. */
+ emit_insn_before_setloc (seq, if_info->jump,
+ INSN_LOCATION (if_info->insn_a));
+ if_info->transform_name = "splat_sign_bit_trivial";
+ return true;
+
+ fail:
+ end_ifcvt_sequence (if_info);
+ return false;
+}
+
+
/* Try forming an IF_THEN_ELSE (cond, b, a) and collapsing that
through simplify_rtx. Sometimes that can eliminate the IF_THEN_ELSE.
If that is the case, emit the result into x. */
@@ -4238,6 +4443,8 @@ noce_process_if_block (struct noce_if_info *if_info)
if (!targetm.have_conditional_execution ()
&& noce_try_store_flag_constants (if_info))
goto success;
+ if (noce_try_sign_bit_splat (if_info))
+ goto success;
if (HAVE_conditional_move
&& noce_try_cmove (if_info))
goto success;