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.
---
gcc/ifcvt.cc | 75 ++++++++++++++++++-
gcc/testsuite/gcc.target/riscv/pr122769.c | 3 +-
.../riscv/zicond-conditional-add-extend.c | 23 ++++++
3 files changed, 97 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..f28fb38dfc2 100644
--- a/gcc/ifcvt.cc
+++ b/gcc/ifcvt.cc
@@ -3370,6 +3370,47 @@ 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);
+ return GET_CODE (src) == ext_code
+ && GET_MODE (src) == GET_MODE (reg)
+ && GET_MODE (XEXP (src, 0)) == inner_mode;
+ }
+
+ return false;
+}
+
/* Helper function to return REG itself,
otherwise NULL_RTX for other RTX_CODE. */
@@ -3412,7 +3453,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 +3472,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 +3486,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 +3526,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 +3570,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