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

Reply via email to