Handle more shapes in ifcvt that occur in code on rv64 + zicond:
For example:
int foo (int cond, int val) {
if (cond) val *= 64;
return val;
}
the desired 3-insn output:
li a5,6
czero.eqz a0,a5,a0
sllw a0,a1,a0
in place of the previous 4-insn slli/czero.eqz/czero.nez/add sequence
that noce_try_cmove_arith was producing.
This patch improves cases for:
* int_ShiftLeft_const
* int_Mul_pow2_const
* int_Add\Sub_const
As well as improving tests from PR123322 with
(sign_extend:DI (subreg:SI (reg:DI 138) 0)))
cases.
The peel of the outer SIGN_EXTEND/ZERO_EXTEND is gated on the SUBREG
inside the binop being SUBREG_PROMOTED_VAR_P with a promotion sign
matching the outer extension code. This is the property that
makes (EXT (SUBREG b)) bit-equal to b.
The existing profitability gate (targetm.noce_conversion_profitable_p
at end_ifcvt_sequence) already covers this transform, so no separate
cost check is added.
AND is excluded from the peel path because its identity op is -1,
not 0, and noce_try_cond_arith already handles it through a separate
code path.
XOR is a different case which succeeds through noce_convert_multiple_sets
And thus still produces suboptimal code.
2026-05-27 Dusan Stojkovic <[email protected]>
PR target/124956
gcc/ChangeLog:
* ifcvt.cc (noce_peel_extend_around_binop): New helper.
(noce_try_cond_arith): Peel an optional SIGN/ZERO_EXTEND
wrapping of a supported binary op re-apply at success label.
Accept CONST_INT as the second operand of the binary op.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr123322.c: Reduce the number of czeros expected.
* gcc.target/riscv/pr124956.c: New test.
CONFIDENTIALITY: The contents of this e-mail are confidential and intended only
for the above addressee(s). If you are not the intended recipient, or the
person responsible for delivering it to the intended recipient, copying or
delivering it to anyone else or using it in any unauthorized manner is
prohibited and may be unlawful. If you receive this e-mail by mistake, please
notify the sender and the systems administrator at [email protected]
immediately.
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index c151146c1b2..1443525e061 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -3130,6 +3130,62 @@ get_base_reg (rtx exp)
return NULL_RTX;
}
+/* Helper for noce_try_cond_arith. Try to recognize that A is
+ (SIGN_EXTEND/ZERO_EXTEND (binop X K)) where the inner binop is one of
+ the identity-0 ops we already handle and X is a SUBREG_PROMOTED
+ lowpart subreg of B with promotion direction matching the outer
+ extension.
+
+ When the pattern matches we can peel the extension and run the rest
+ of noce_try_cond_arith on the inner binop, because the !cond branch
+ of the if-then-else writes B unchanged, which is bit-equal to
+ (EXT (SUBREG B)) precisely when SUBREG_PROMOTED_VAR_P with matching
+ signedness is set on the subreg.
+
+ On success, returns the inner binop and writes the extension code
+ into *EXT_OUT. Returns NULL_RTX otherwise. */
+
+static rtx
+noce_peel_extend_around_binop (rtx a, rtx b, rtx_code *ext_out)
+{
+ if (!REG_P (b))
+ return NULL_RTX;
+ if (GET_CODE (a) != SIGN_EXTEND && GET_CODE (a) != ZERO_EXTEND)
+ return NULL_RTX;
+
+ rtx inner = XEXP (a, 0);
+ if (!noce_cond_zero_binary_op_supported (inner))
+ return NULL_RTX;
+
+ if (GET_CODE (inner) == AND)
+ return NULL_RTX;
+
+ rtx_code ext = GET_CODE (a);
+
+ /* The inner op must reference a lowpart subreg of B that is in
+ promoted form matching the outer extension. Allow either operand
+ position for commutative ops; the canonicalization in
+ noce_try_cond_arith will swap it to the left if needed. */
+ for (int i = 0; i < 2; i++)
+ {
+ rtx s = XEXP (inner, i);
+ if (!SUBREG_P (s) || !subreg_lowpart_p (s))
+ continue;
+ if (!rtx_equal_p (SUBREG_REG (s), b))
+ continue;
+ if (!SUBREG_PROMOTED_VAR_P (s))
+ continue;
+ if (ext == SIGN_EXTEND ? !SUBREG_PROMOTED_SIGNED_P (s)
+ : !SUBREG_PROMOTED_UNSIGNED_P (s))
+ continue;
+ if (i == 1 && !COMMUTATIVE_ARITH_P (inner))
+ continue;
+ *ext_out = ext;
+ return inner;
+ }
+ return NULL_RTX;
+}
+
/* Try to covert if-then-else with conditional zero,
returning TURE on success or FALSE on failure.
IF_INFO describes the if-conversion scenario under consideration.
@@ -3143,6 +3199,14 @@ get_base_reg (rtx exp)
else |
x = y |
+ The operand z may be a register or a CONST_INT.
+
+ For narrow integer ops kept in wider registers, the THEN-side value
+ may be wrapped in a SIGN_EXTEND/ZERO_EXTEND around the binop, with
+ one operand being a promoted lowpart subreg of y. In that case the
+ wrapper is peeled, the transformation proceeds in the inner mode,
+ and the wrapper is re-applied to the final result.
+
AND is special as it needs to be handled differently.
tmp = !cond ? y : 0
@@ -3163,6 +3227,12 @@ noce_try_cond_arith (struct noce_if_info *if_info)
rtx_insn *seq;
rtx_code op;
machine_mode mode = GET_MODE (if_info->x);
+ /* If non-UNKNOWN, the THEN-side value `a` was wrapped in this extension
+ and the final result must be re-wrapped before being stored into
+ if_info->x. OUTER_MODE is the mode the extension produces. See
+ noce_peel_extend_around_binop for the pattern. */
+ rtx_code outer_extend = UNKNOWN;
+ machine_mode outer_mode = mode;
/* Scalar integral modes are only supported here.
Could support scalar floating point but that
@@ -3177,6 +3247,18 @@ noce_try_cond_arith (struct noce_if_info *if_info)
a = copy_rtx (if_info->a);
b = copy_rtx (if_info->b);
+ /* Peel a SIGN_EXTEND/ZERO_EXTEND around a supported binary op so we can
+ handle narrow-integer ops kept in wider registers. */
+ {
+ rtx peeled;
+ if ((peeled = noce_peel_extend_around_binop (a, b, &outer_extend)))
+ {
+ outer_mode = GET_MODE (a);
+ a = peeled;
+ mode = GET_MODE (a);
+ }
+ }
+
/* Canonicalize x = y : (y op z) to x = (y op z) : y. */
if (REG_P (a) && noce_cond_zero_binary_op_supported (b))
{
@@ -3207,7 +3289,9 @@ noce_try_cond_arith (struct noce_if_info *if_info)
a_op1 = get_base_reg (XEXP (a, 1));
}
- if (a_op1 == NULL_RTX)
+ /* The "z" operand may be a register/lowpart-subreg (a_op1 non-NULL) or
+ a CONST_INT. */
+ if (a_op1 == NULL_RTX && !CONST_INT_P (XEXP (a, 1)))
goto fail;
/* Ensure the cond is of form: x = (y op z) : y */
@@ -3217,7 +3301,16 @@ noce_try_cond_arith (struct noce_if_info *if_info)
start_sequence ();
- target = gen_reg_rtx (GET_MODE (XEXP (a, op != AND)));
+ /* The cmove output should live in the mode of the operand we're
+ conditionalising (XEXP (a, 1) for non-AND, XEXP (a, 0) for AND).
+ For CONST_INT operands GET_MODE returns VOIDmode, so fall back to
+ the binop's mode in that case. */
+ {
+ machine_mode tgt_mode = GET_MODE (XEXP (a, op != AND));
+ if (tgt_mode == VOIDmode)
+ tgt_mode = mode;
+ target = gen_reg_rtx (tgt_mode);
+ }
/* AND requires !cond, instead we swap ops around. */
target = noce_emit_cmove (if_info, target, code,
@@ -3306,14 +3404,30 @@ noce_try_cond_arith (struct noce_if_info *if_info)
if (!target)
goto end_seq_n_fail;
- target = expand_simple_binop (mode, op, a_op0, target, if_info->x, 0,
- OPTAB_WIDEN);
+ /* Use XEXP (a, 0) directly rather than the base register a_op0, so that
+ when we peeled an outer extension and the binop's first operand was a
+ SUBREG of B, the binop is emitted in the inner (narrower) mode using
+ that subreg. Pass NULL_RTX as the target hint in the peeled case
+ because if_info->x is in the wider outer mode and would cause
+ expand_simple_binop to reject it; we'll wrap the result and assign
+ into if_info->x at the success label. */
+ target = expand_simple_binop (mode, op, XEXP (a, 0), target,
+ outer_extend == UNKNOWN ? if_info->x : NULL_RTX,
+ 0, OPTAB_WIDEN);
if (!target)
goto end_seq_n_fail;
success:
- if (target != if_info->x)
+ if (outer_extend != UNKNOWN)
+ {
+ /* Re-apply the extension we peeled at the top of the function so
+ the result has the correct width. */
+ rtx wrapped = simplify_gen_unary (outer_extend, outer_mode, target,
+ GET_MODE (target));
+ noce_emit_move_insn (if_info->x, wrapped);
+ }
+ else if (target != if_info->x)
noce_emit_move_insn (if_info->x, target);
seq = end_ifcvt_sequence (if_info);
diff --git a/gcc/testsuite/gcc.target/riscv/pr123322.c
b/gcc/testsuite/gcc.target/riscv/pr123322.c
index da6759602db..13022b36596 100644
--- a/gcc/testsuite/gcc.target/riscv/pr123322.c
+++ b/gcc/testsuite/gcc.target/riscv/pr123322.c
@@ -17,7 +17,6 @@ TEST(lshift, <<=)
/* AND and MULT can be handled too, but need a different neutral element that
we aren't handling yet. */
-/* Each test should have precisely one czero. But for rv64, the int tests
- generate 2 for int cases where the op is widened. */
+/* Each test should have precisely one czero. */
/* { dg-final { scan-assembler-times "czero" 14 { target rv32 } } } */
-/* { dg-final { scan-assembler-times "czero" 18 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "czero" 14 { target rv64 } } } */
diff --git a/gcc/testsuite/gcc.target/riscv/pr124956.c
b/gcc/testsuite/gcc.target/riscv/pr124956.c
new file mode 100644
index 00000000000..2c0d66b4da8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr124956.c
@@ -0,0 +1,72 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcb_zicond -mabi=lp64d -mbranch-cost=4" { target {
rv64 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" "-O1" "-Os" "-Oz" } } */
+
+int
+test_int_ShiftLeft_const_eqz (int cond, int val)
+{
+ if (cond)
+ val <<= 6;
+ return val;
+}
+
+int
+test_int_Mul_pow2_eqz (int cond, int val)
+{
+ if (cond)
+ val *= 64; /* should canonicalize to val <<= 6. */
+ return val;
+}
+
+int
+test_int_Add_const_eqz (int cond, int val)
+{
+ if (cond)
+ val += 7;
+ return val;
+}
+
+/* Each test should have precisely one czero. But for rv64, the int_Xor test
+ generates 2 still for int cases where the op is widened. */
+
+int
+test_int_Xor_const_eqz (int cond, int val)
+{
+ if (cond)
+ val ^= 0xff;
+ return val;
+}
+
+long
+test_long_ShiftLeft_const_eqz (long cond, long val)
+{
+ if (cond)
+ val <<= 6;
+ return val;
+}
+
+long
+test_long_Mul_pow2_eqz (long cond, long val)
+{
+ if (cond)
+ val *= 64;
+ return val;
+}
+
+long
+test_long_Add_const_eqz (long cond, long val)
+{
+ if (cond)
+ val += 7;
+ return val;
+}
+
+long
+test_long_Xor_const_eqz (long cond, long val)
+{
+ if (cond)
+ val ^= 0xff;
+ return val;
+}
+
+/* { dg-final { scan-assembler-times "czero" 9 { target rv64 } } } */
--
2.43.0