From: juewang <[email protected]>
Handle conditional arithmetic wrapped in a sign or zero extension when
the unchanged value is known to have the same extension. Use the RTL bit
property queries so equivalent forms such as masked values are recognized
as well as explicit extensions.
gcc/ChangeLog:
* ifcvt.cc (noce_cond_arith_operation): New function.
(noce_reg_known_extended_p): New function.
(noce_try_cond_arith): Handle arithmetic operations wrapped in an
extension.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr122769.c: Add the branch cost and restrict the
test to compatible optimization options.
* gcc.target/riscv/zicond-conditional-add-extend.c: New test.
---
gcc/ifcvt.cc | 86 ++++++++++++++++++-
gcc/testsuite/gcc.target/riscv/pr122769.c | 3 +-
.../riscv/zicond-conditional-add-extend.c | 23 +++++
3 files changed, 108 insertions(+), 4 deletions(-)
create mode 100644
gcc/testsuite/gcc.target/riscv/zicond-conditional-add-extend.c
diff --git a/gcc/ifcvt.cc b/gcc/ifcvt.cc
index add276ceb9b..a9b5e57019a 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -3370,6 +3370,58 @@ noce_try_sign_mask (struct noce_if_info *if_info)
return true;
}
+/* Return the arithmetic operation in X, looking through an extension. */
+
+static rtx
+noce_cond_arith_operation (rtx x)
+{
+ enum rtx_code code = GET_CODE (x);
+ if (code == SIGN_EXTEND || code == ZERO_EXTEND)
+ x = XEXP (x, 0);
+
+ return noce_cond_zero_binary_op_supported (x) ? x : NULL_RTX;
+}
+
+/* Return true if REG contains an EXT_CODE extension from INNER_MODE before
+ BEFORE. */
+
+static bool
+noce_reg_known_extended_p (rtx reg, rtx_insn *before,
+ enum rtx_code ext_code, machine_mode inner_mode)
+{
+ if (!REG_P (reg) || !before)
+ return false;
+
+ basic_block bb = BLOCK_FOR_INSN (before);
+ for (rtx_insn *insn = prev_nonnote_nondebug_insn (before);
+ insn && BLOCK_FOR_INSN (insn) == bb;
+ insn = prev_nonnote_nondebug_insn (insn))
+ if (modified_in_p (reg, insn))
+ {
+ rtx set = single_set (insn);
+ if (!set || !rtx_equal_p (SET_DEST (set), reg))
+ return false;
+
+ rtx src = SET_SRC (set);
+ machine_mode mode = GET_MODE (reg);
+ unsigned int precision = GET_MODE_PRECISION (mode).to_constant ();
+ unsigned int inner_precision
+ = GET_MODE_PRECISION (inner_mode).to_constant ();
+ if (ext_code == SIGN_EXTEND)
+ return (num_sign_bit_copies (src, mode)
+ > precision - inner_precision);
+
+ if (ext_code == ZERO_EXTEND)
+ return (HWI_COMPUTABLE_MODE_P (mode)
+ && (nonzero_bits (src, mode)
+ & ~GET_MODE_MASK (inner_mode)) == 0);
+
+ return false;
+ }
+
+ return false;
+}
+
/* Helper function to return REG itself,
otherwise NULL_RTX for other RTX_CODE. */
@@ -3412,7 +3464,7 @@ get_base_reg_or_constant (rtx exp)
static int
noce_try_cond_arith (struct noce_if_info *if_info)
{
- rtx target, a, b, a_op0, a_op1;
+ rtx target, a, b, a_op0, a_op1, outer_a;
rtx cond = if_info->cond;
rtx_code code = GET_CODE (cond);
rtx_insn *seq;
@@ -3431,9 +3483,11 @@ noce_try_cond_arith (struct noce_if_info *if_info)
a = copy_rtx (if_info->a);
b = copy_rtx (if_info->b);
+ rtx a_arith = noce_cond_arith_operation (a);
+ rtx b_arith = noce_cond_arith_operation (b);
/* Canonicalize x = y : (y op z) to x = (y op z) : y. */
- if (REG_P (a) && noce_cond_zero_binary_op_supported (b))
+ if (REG_P (a) && b_arith)
{
if (if_info->rev_cond)
{
@@ -3443,17 +3497,30 @@ noce_try_cond_arith (struct noce_if_info *if_info)
else
code = reversed_comparison_code (cond, if_info->jump);
std::swap (a, b);
+ a_arith = b_arith;
}
/* Check if x = (y op z) : y is supported by czero based ifcvt. */
- else if (!(noce_cond_zero_binary_op_supported (a) && REG_P (b)))
+ else if (!(a_arith && REG_P (b)))
goto fail;
if (code == UNKNOWN)
goto fail;
+ outer_a = a == a_arith ? NULL_RTX : a;
+ a = a_arith;
op = GET_CODE (a);
+ /* EXTEND (y op z) : y is valid only when Y already contains the same
+ extension of its lowpart. AND uses a different transformation. */
+ if (outer_a
+ && (op == AND
+ || GET_MODE (outer_a) != mode
+ || !SCALAR_INT_MODE_P (GET_MODE (a))
+ || !noce_reg_known_extended_p (b, if_info->jump,
+ GET_CODE (outer_a), GET_MODE (a))))
+ goto fail;
+
/* Canonicalize x = (z op y) : y to x = (y op z) : y */
a_op1 = get_base_reg_or_constant (XEXP (a, 1));
if (a_op1 && rtx_equal_p (a_op1, b) && COMMUTATIVE_ARITH_P (a))
@@ -3470,6 +3537,11 @@ noce_try_cond_arith (struct noce_if_info *if_info)
if (!(a_op0 && rtx_equal_p (a_op0, b)))
goto fail;
+ if (outer_a
+ && (!SUBREG_P (XEXP (a, 0))
+ || !subreg_lowpart_p (XEXP (a, 0))))
+ goto fail;
+
start_sequence ();
if (CONST_INT_P (XEXP (a, 1)))
@@ -3509,6 +3581,14 @@ noce_try_cond_arith (struct noce_if_info *if_info)
gcc_assert (target);
}
+ if (outer_a)
+ {
+ XEXP (a, 1) = target;
+ noce_emit_move_insn (if_info->x, outer_a);
+ target = if_info->x;
+ goto success;
+ }
+
/* For AND, try `cond ? z : -1` to see if that is cheaper or the same cost.
In some cases it will be cheaper to produce the -1 rather than the 0
case. */
if (op == AND)
diff --git a/gcc/testsuite/gcc.target/riscv/pr122769.c
b/gcc/testsuite/gcc.target/riscv/pr122769.c
index 0f7e19ca504..6e3d0d37e4e 100644
--- a/gcc/testsuite/gcc.target/riscv/pr122769.c
+++ b/gcc/testsuite/gcc.target/riscv/pr122769.c
@@ -1,5 +1,6 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -march=rv64gc_zbs_zicond -mabi=lp64d" { target rv64} } */
+/* { dg-options "-O2 -march=rv64gc_zbs_zicond -mabi=lp64d -mbranch-cost=4" {
target rv64} } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-O3" "-Os" "-Oz" "-Og" "-flto" } }
*/
/* Elide a short forward branch and generate a czero instead. */
diff --git a/gcc/testsuite/gcc.target/riscv/zicond-conditional-add-extend.c
b/gcc/testsuite/gcc.target/riscv/zicond-conditional-add-extend.c
new file mode 100644
index 00000000000..dd74a40e99a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zicond-conditional-add-extend.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-options "-O2 -march=rv64gc_zicond -mabi=lp64d -mbranch-cost=4" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-Oz" "-Og" "-funroll-loops"
} } */
+
+unsigned int
+xz_loop (unsigned int pos, unsigned int cur_match, unsigned int depth,
+ unsigned int cyclic_pos, unsigned int cyclic_size,
+ const unsigned int *son, const unsigned char *cur)
+{
+ unsigned int sum = 0;
+ while (depth-- != 0)
+ {
+ unsigned int delta = pos - cur_match;
+ const unsigned char *pb = cur - delta;
+ cur_match = son[cyclic_pos - delta
+ + (delta > cyclic_pos ? cyclic_size : 0)];
+ sum += cur_match + pb[0] + cur[0];
+ }
+ return sum;
+}
+
+/* { dg-final { scan-assembler-times {\tczero\.(eqz|nez)\t} 1 } } */
+/* { dg-final { scan-assembler-not {\tbgeu\t} } } */
--
2.34.1