[3/3][RTL ifcvt] PR middle-end/37780: Conditional expression with __builtin_clz(...
authorKyrylo Tkachov <kyrylo.tkachov@arm.com>
Mon, 6 Jun 2016 16:06:05 +0000 (16:06 +0000)
committerKyrylo Tkachov <ktkachov@gcc.gnu.org>
Mon, 6 Jun 2016 16:06:05 +0000 (16:06 +0000)
PR middle-end/37780
* ifcvt.c (noce_try_ifelse_collapse): New function.
Declare prototype.
(noce_process_if_block): Call noce_try_ifelse_collapse.
* simplify-rtx.c (simplify_cond_clz_ctz): New function.
(simplify_ternary_operation): Use the above to simplify
conditional CLZ/CTZ expressions.

* gcc.c-torture/execute/pr37780.c: New test.
* gcc.target/aarch64/pr37780_1.c: Likewise.
* gcc.target/arm/pr37780_1.c: Likewise.

From-SVN: r237141

gcc/ChangeLog
gcc/ifcvt.c
gcc/simplify-rtx.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/pr37780.c [new file with mode: 0644]
gcc/testsuite/gcc.target/aarch64/pr37780_1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/pr37780_1.c [new file with mode: 0644]

index 8a1a2a6d52fb13a4761e9da53320b4e57b057c06..3e6879849f26679d7617ae3f85bbd6356bf2906c 100644 (file)
@@ -1,3 +1,13 @@
+2016-06-06  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       PR middle-end/37780
+       * ifcvt.c (noce_try_ifelse_collapse): New function.
+       Declare prototype.
+       (noce_process_if_block): Call noce_try_ifelse_collapse.
+       * simplify-rtx.c (simplify_cond_clz_ctz): New function.
+       (simplify_ternary_operation): Use the above to simplify
+       conditional CLZ/CTZ expressions.
+
 2016-06-06  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
 
        PR middle-end/37780
index 44ae020db2477597f5f9c5dd031e75139d2a5058..05fac71409d401a08d01b7dc7cf164613f8477c4 100644 (file)
@@ -817,6 +817,7 @@ struct noce_if_info
 
 static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
 static int noce_try_move (struct noce_if_info *);
+static int noce_try_ifelse_collapse (struct noce_if_info *);
 static int noce_try_store_flag (struct noce_if_info *);
 static int noce_try_addcc (struct noce_if_info *);
 static int noce_try_store_flag_constants (struct noce_if_info *);
@@ -1120,6 +1121,37 @@ noce_try_move (struct noce_if_info *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.  */
+
+static int
+noce_try_ifelse_collapse (struct noce_if_info * if_info)
+{
+  if (!noce_simple_bbs (if_info))
+    return FALSE;
+
+  machine_mode mode = GET_MODE (if_info->x);
+  rtx if_then_else = simplify_gen_ternary (IF_THEN_ELSE, mode, mode,
+                                           if_info->cond, if_info->b,
+                                           if_info->a);
+
+  if (GET_CODE (if_then_else) == IF_THEN_ELSE)
+    return FALSE;
+
+  rtx_insn *seq;
+  start_sequence ();
+  noce_emit_move_insn (if_info->x, if_then_else);
+  seq = end_ifcvt_sequence (if_info);
+  if (!seq)
+    return FALSE;
+
+  emit_insn_before_setloc (seq, if_info->jump,
+                         INSN_LOCATION (if_info->insn_a));
+  return TRUE;
+}
+
+
 /* Convert "if (test) x = 1; else x = 0".
 
    Only try 0 and STORE_FLAG_VALUE here.  Other combinations will be
@@ -3497,6 +3529,8 @@ noce_process_if_block (struct noce_if_info *if_info)
 
   if (noce_try_move (if_info))
     goto success;
+  if (noce_try_ifelse_collapse (if_info))
+    goto success;
   if (noce_try_store_flag (if_info))
     goto success;
   if (noce_try_bitop (if_info))
index fdc4b366c74d9ff61f00c2d5b640c727d54535a2..2b649fa405b9c7b0869667ba688073abd4daa93b 100644 (file)
@@ -5267,6 +5267,49 @@ simplify_const_relational_operation (enum rtx_code code,
 
   return 0;
 }
+
+/* Recognize expressions of the form (X CMP 0) ? VAL : OP (X)
+   where OP is CLZ or CTZ and VAL is the value from CLZ_DEFINED_VALUE_AT_ZERO
+   or CTZ_DEFINED_VALUE_AT_ZERO respectively and return OP (X) if the expression
+   can be simplified to that or NULL_RTX if not.
+   Assume X is compared against zero with CMP_CODE and the true
+   arm is TRUE_VAL and the false arm is FALSE_VAL.  */
+
+static rtx
+simplify_cond_clz_ctz (rtx x, rtx_code cmp_code, rtx true_val, rtx false_val)
+{
+  if (cmp_code != EQ && cmp_code != NE)
+    return NULL_RTX;
+
+  /* Result on X == 0 and X !=0 respectively.  */
+  rtx on_zero, on_nonzero;
+  if (cmp_code == EQ)
+    {
+      on_zero = true_val;
+      on_nonzero = false_val;
+    }
+  else
+    {
+      on_zero = false_val;
+      on_nonzero = true_val;
+    }
+
+  rtx_code op_code = GET_CODE (on_nonzero);
+  if ((op_code != CLZ && op_code != CTZ)
+      || !rtx_equal_p (XEXP (on_nonzero, 0), x)
+      || !CONST_INT_P (on_zero))
+    return NULL_RTX;
+
+  HOST_WIDE_INT op_val;
+  machine_mode mode = GET_MODE (on_nonzero);
+  if (((op_code == CLZ && CLZ_DEFINED_VALUE_AT_ZERO (mode, op_val))
+       || (op_code == CTZ && CTZ_DEFINED_VALUE_AT_ZERO (mode, op_val)))
+      && op_val == INTVAL (on_zero))
+    return on_nonzero;
+
+  return NULL_RTX;
+}
+
 \f
 /* Simplify CODE, an operation with result mode MODE and three operands,
    OP0, OP1, and OP2.  OP0_MODE was the mode of OP0 before it became
@@ -5400,6 +5443,19 @@ simplify_ternary_operation (enum rtx_code code, machine_mode mode,
            }
        }
 
+      /* Convert x == 0 ? N : clz (x) into clz (x) when
+        CLZ_DEFINED_VALUE_AT_ZERO is defined to N for the mode of x.
+        Similarly for ctz (x).  */
+      if (COMPARISON_P (op0) && !side_effects_p (op0)
+         && XEXP (op0, 1) == const0_rtx)
+       {
+         rtx simplified
+           = simplify_cond_clz_ctz (XEXP (op0, 0), GET_CODE (op0),
+                                    op1, op2);
+         if (simplified)
+           return simplified;
+       }
+
       if (COMPARISON_P (op0) && ! side_effects_p (op0))
        {
          machine_mode cmp_mode = (GET_MODE (XEXP (op0, 0)) == VOIDmode
index 1a7e9eef59bd731bc974e91269b99986d86bae94..b68744b92f3f924a1077efc03fb822d26ac7f5a3 100644 (file)
@@ -1,3 +1,10 @@
+2016-06-06  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
+
+       PR middle-end/37780
+       * gcc.c-torture/execute/pr37780.c: New test.
+       * gcc.target/aarch64/pr37780_1.c: Likewise.
+       * gcc.target/arm/pr37780_1.c: Likewise.
+
 2016-06-06  Marek Polacek  <polacek@redhat.com>
 
        * c-c++-common/attr-may-alias-1.c: New test.
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr37780.c b/gcc/testsuite/gcc.c-torture/execute/pr37780.c
new file mode 100644 (file)
index 0000000..a9eca68
--- /dev/null
@@ -0,0 +1,49 @@
+/* PR middle-end/37780.  */
+
+#define VAL (8 * sizeof (int))
+
+int __attribute__ ((noinline, noclone))
+fooctz (int i)
+{
+  return (i == 0) ? VAL : __builtin_ctz (i);
+}
+
+int __attribute__ ((noinline, noclone))
+fooctz2 (int i)
+{
+  return (i != 0) ? __builtin_ctz (i) : VAL;
+}
+
+unsigned int __attribute__ ((noinline, noclone))
+fooctz3 (unsigned int i)
+{
+  return (i > 0) ?  __builtin_ctz (i) : VAL;
+}
+
+int __attribute__ ((noinline, noclone))
+fooclz (int i)
+{
+  return (i == 0) ? VAL : __builtin_clz (i);
+}
+
+int __attribute__ ((noinline, noclone))
+fooclz2 (int i)
+{
+  return (i != 0) ? __builtin_clz (i) : VAL;
+}
+
+unsigned int __attribute__ ((noinline, noclone))
+fooclz3 (unsigned int i)
+{
+  return (i > 0) ? __builtin_clz (i) : VAL;
+}
+
+int
+main (void)
+{
+  if (fooctz (0) != VAL || fooctz2 (0) != VAL || fooctz3 (0) != VAL
+      || fooclz (0) != VAL || fooclz2 (0) != VAL || fooclz3 (0) != VAL)
+    __builtin_abort ();
+
+  return 0;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/aarch64/pr37780_1.c b/gcc/testsuite/gcc.target/aarch64/pr37780_1.c
new file mode 100644 (file)
index 0000000..97027e7
--- /dev/null
@@ -0,0 +1,46 @@
+/* Test that we can remove the conditional move due to CLZ
+   and CTZ being defined at zero.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+int
+fooctz (int i)
+{
+  return (i == 0) ? 32 : __builtin_ctz (i);
+}
+
+int
+fooctz2 (int i)
+{
+  return (i != 0) ? __builtin_ctz (i) : 32;
+}
+
+unsigned int
+fooctz3 (unsigned int i)
+{
+  return (i > 0) ?  __builtin_ctz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */
+
+int
+fooclz (int i)
+{
+  return (i == 0) ? 32 : __builtin_clz (i);
+}
+
+int
+fooclz2 (int i)
+{
+  return (i != 0) ? __builtin_clz (i) : 32;
+}
+
+unsigned int
+fooclz3 (unsigned int i)
+{
+  return (i > 0) ? __builtin_clz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "clz\t" 6 } } */
+/* { dg-final { scan-assembler-not "cmp\t.*0" } } */
diff --git a/gcc/testsuite/gcc.target/arm/pr37780_1.c b/gcc/testsuite/gcc.target/arm/pr37780_1.c
new file mode 100644 (file)
index 0000000..b954fe5
--- /dev/null
@@ -0,0 +1,47 @@
+/* Test that we can remove the conditional move due to CLZ
+   being defined at zero.  */
+
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v5_ok } */
+/* { dg-options "-O2" } */
+
+int
+fooctz (int i)
+{
+  return (i == 0) ? 32 : __builtin_ctz (i);
+}
+
+int
+fooctz2 (int i)
+{
+  return (i != 0) ? __builtin_ctz (i) : 32;
+}
+
+unsigned int
+fooctz3 (unsigned int i)
+{
+  return (i > 0) ?  __builtin_ctz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "rbit\t*" 3 } } */
+
+int
+fooclz (int i)
+{
+  return (i == 0) ? 32 : __builtin_clz (i);
+}
+
+int
+fooclz2 (int i)
+{
+  return (i != 0) ? __builtin_clz (i) : 32;
+}
+
+unsigned int
+fooclz3 (unsigned int i)
+{
+  return (i > 0) ? __builtin_clz (i) : 32;
+}
+
+/* { dg-final { scan-assembler-times "clz\t" 6 } } */
+/* { dg-final { scan-assembler-not "cmp\t.*0" } } */