re PR tree-optimization/9814 (gcc fails to optimise if (l&2) l|=2 away)
authorRoger Sayle <roger@eyesopen.com>
Fri, 27 May 2005 02:46:01 +0000 (02:46 +0000)
committerRoger Sayle <sayle@gcc.gnu.org>
Fri, 27 May 2005 02:46:01 +0000 (02:46 +0000)
PR tree-optimization/9814
* ifcvt.c (noce_emit_move_insn): If we fail to recognize the move
instruction, add the necessary clobbers by re-expanding the RTL
for arithmetic operations via optab.c's expand_unop/expand_binop.
(noce_try_bitop): New function to optimize bit manipulation idioms
of the form "if (x & C) x = x op C" and "if (!(x & C) x = x op C".
(noce_process_if_block): Call noce_try_bitop.

* gcc.dg/pr9814-1.c: New test case.

From-SVN: r100240

gcc/ChangeLog
gcc/ifcvt.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/pr9814-1.c [new file with mode: 0644]

index 433b0f0b5f3e3dacf52e4ccb61762a8c3fdd52bf..190a25ad6ce87e99b1d5741bf05ed18bb362758d 100644 (file)
@@ -1,3 +1,13 @@
+2005-05-26  Roger Sayle  <roger@eyesopen.com>
+
+       PR tree-optimization/9814
+       * ifcvt.c (noce_emit_move_insn): If we fail to recognize the move
+       instruction, add the necessary clobbers by re-expanding the RTL
+       for arithmetic operations via optab.c's expand_unop/expand_binop.
+       (noce_try_bitop): New function to optimize bit manipulation idioms
+       of the form "if (x & C) x = x op C" and "if (!(x & C) x = x op C".
+       (noce_process_if_block): Call noce_try_bitop.
+
 2005-05-26  Roger Sayle  <roger@eyesopen.com>
 
        * reg-stack.c (convert_regs_entry, convert_regs_exit,
index be02aaa7d24f12991b13fd39c3de5ac523c79236..9575e62b2f1c2bd8a07ebb33b378852a9d28dbdc 100644 (file)
@@ -687,7 +687,57 @@ noce_emit_move_insn (rtx x, rtx y)
 
   if (GET_CODE (x) != STRICT_LOW_PART)
     {
-      emit_move_insn (x, y);
+      rtx seq, insn, target;
+      optab ot;
+
+      start_sequence ();
+      insn = emit_move_insn (x, y);
+      seq = get_insns ();
+      end_sequence();
+
+      if (recog_memoized (insn) <= 0)
+       switch (GET_RTX_CLASS (GET_CODE (y)))
+         {
+         case RTX_UNARY:
+           ot = code_to_optab[GET_CODE (y)];
+           if (ot)
+             {
+               start_sequence ();
+               target = expand_unop (GET_MODE (y), ot, XEXP (y, 0), x, 0);
+               if (target != NULL_RTX)
+                 {
+                   if (target != x)
+                     emit_move_insn (x, target);
+                   seq = get_insns ();
+                 }
+               end_sequence ();
+             }
+           break;
+
+         case RTX_BIN_ARITH:
+         case RTX_COMM_ARITH:
+           ot = code_to_optab[GET_CODE (y)];
+           if (ot)
+             {
+               start_sequence ();
+               target = expand_binop (GET_MODE (y), ot,
+                                      XEXP (y, 0), XEXP (y, 1),
+                                      x, 0, OPTAB_DIRECT);
+               if (target != NULL_RTX)
+                 {
+                   if (target != x)
+                     emit_move_insn (x, target);
+                   seq = get_insns ();
+                 }
+               end_sequence ();
+             }
+           break;
+
+         default:
+           break;
+         }
+
+      emit_insn (seq);
       return;
     }
 
@@ -1815,6 +1865,105 @@ noce_try_sign_mask (struct noce_if_info *if_info)
 }
 
 
+/* Optimize away "if (x & C) x |= C" and similar bit manipulation
+   transformations.  */
+
+static int
+noce_try_bitop (struct noce_if_info *if_info)
+{
+  rtx cond, x, a, result, seq;
+  enum machine_mode mode;
+  enum rtx_code code;
+  int bitnum;
+
+  x = if_info->x;
+  cond = if_info->cond;
+  code = GET_CODE (cond);
+
+  /* Check for no else condition.  */
+  if (! rtx_equal_p (x, if_info->b))
+    return FALSE;
+
+  /* Check for a suitable condition.  */
+  if (code != NE && code != EQ)
+    return FALSE;
+  if (XEXP (cond, 1) != const0_rtx)
+    return FALSE;
+  cond = XEXP (cond, 0);
+
+  /* ??? We could also handle AND here.  */
+  if (GET_CODE (cond) == ZERO_EXTRACT)
+    {
+      if (XEXP (cond, 1) != const1_rtx
+         || GET_CODE (XEXP (cond, 2)) != CONST_INT
+         || ! rtx_equal_p (x, XEXP (cond, 0)))
+       return FALSE;
+      bitnum = INTVAL (XEXP (cond, 2));
+      mode = GET_MODE (x);
+      if (bitnum >= HOST_BITS_PER_WIDE_INT)
+       return FALSE;
+    }
+  else
+    return FALSE;
+
+  a = if_info->a;
+  if (GET_CODE (a) == IOR || GET_CODE (a) == XOR)
+    {
+      /* Check for "if (X & C) x = x op C".  */
+      if (! rtx_equal_p (x, XEXP (a, 0))
+          || GET_CODE (XEXP (a, 1)) != CONST_INT
+         || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
+            != (unsigned HOST_WIDE_INT) 1 << bitnum)
+        return FALSE;
+
+      /* if ((x & C) == 0) x |= C; is transformed to x |= C.   */
+      /* if ((x & C) != 0) x |= C; is transformed to nothing.  */
+      if (GET_CODE (a) == IOR)
+       result = (code == NE) ? a : NULL_RTX;
+      else if (code == NE)
+       {
+         /* if ((x & C) == 0) x ^= C; is transformed to x |= C.   */
+         result = gen_int_mode ((HOST_WIDE_INT) 1 << bitnum, mode);
+         result = simplify_gen_binary (IOR, mode, x, result);
+       }
+      else
+       {
+         /* if ((x & C) != 0) x ^= C; is transformed to x &= ~C.  */
+         result = gen_int_mode (~((HOST_WIDE_INT) 1 << bitnum), mode);
+         result = simplify_gen_binary (AND, mode, x, result);
+       }
+    }
+  else if (GET_CODE (a) == AND)
+    {
+      /* Check for "if (X & C) x &= ~C".  */
+      if (! rtx_equal_p (x, XEXP (a, 0))
+         || GET_CODE (XEXP (a, 1)) != CONST_INT
+         || (INTVAL (XEXP (a, 1)) & GET_MODE_MASK (mode))
+            != (~((HOST_WIDE_INT) 1 << bitnum) & GET_MODE_MASK (mode)))
+        return FALSE;
+
+      /* if ((x & C) == 0) x &= ~C; is transformed to nothing.  */
+      /* if ((x & C) != 0) x &= ~C; is transformed to x &= ~C.  */
+      result = (code == EQ) ? a : NULL_RTX;
+    }
+  else
+    return FALSE;
+
+  if (result)
+    {
+      start_sequence ();
+      noce_emit_move_insn (x, result);
+      seq = end_ifcvt_sequence (if_info);
+      if (!seq)
+       return FALSE;
+
+      emit_insn_before_setloc (seq, if_info->jump,
+                              INSN_LOCATOR (if_info->insn_a));
+    }
+  return TRUE;
+}
+
+
 /* Similar to get_condition, only the resulting condition must be
    valid at JUMP, instead of at EARLIEST.  */
 
@@ -2078,6 +2227,8 @@ noce_process_if_block (struct ce_if_block * ce_info)
     goto success;
   if (noce_try_store_flag (&if_info))
     goto success;
+  if (noce_try_bitop (&if_info))
+    goto success;
   if (noce_try_minmax (&if_info))
     goto success;
   if (noce_try_abs (&if_info))
index d460ab34dde74871ead5834c5b7bd747b7ea0f23..00185e8011a985476ce99bdef4b6185f4d596d67 100644 (file)
@@ -1,3 +1,8 @@
+2005-05-26  Roger Sayle  <roger@eyesopen.com>
+
+       PR tree-optimization/9814
+       * gcc.dg/pr9814-1.c: New test case.
+
 2005-05-26  Ziemowit Laski  <zlaski@apple.com>
 
        * objc.dg/comp-types-8.m, objc.dg/encode-6.m,
diff --git a/gcc/testsuite/gcc.dg/pr9814-1.c b/gcc/testsuite/gcc.dg/pr9814-1.c
new file mode 100644 (file)
index 0000000..51b79f7
--- /dev/null
@@ -0,0 +1,107 @@
+/* PR tree-optimization/9814  */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+extern void abort(void);
+
+int test1(int x)
+{
+  if (x & 2)
+    x |= 2;
+  return x;
+}
+
+int test2(int x)
+{
+  if (!(x & 2))
+    x |= 2;
+  return x;
+}
+
+int test3(int x)
+{
+  if (x & 2)
+    x ^= 2;
+  return x;
+}
+
+int test4(int x)
+{
+  if (!(x & 2))
+    x ^= 2;
+  return x;
+}
+
+int test5(int x)
+{
+  if (x & 2)
+    x &= ~2;
+  return x;
+}
+
+int test6(int x)
+{
+  if (!(x & 2))
+    x &= ~2;
+  return x;
+}
+
+int main()
+{
+  if (test1(0) != 0)
+    abort();
+  if (test1(2) != 2)
+    abort();
+  if (test1(5) != 5)
+    abort();
+  if (test1(7) != 7)
+    abort();
+
+  if (test2(0) != 2)
+    abort();
+  if (test2(2) != 2)
+    abort();
+  if (test2(5) != 7)
+    abort();
+  if (test2(7) != 7)
+    abort();
+
+  if (test3(0) != 0)
+    abort();
+  if (test3(2) != 0)
+    abort();
+  if (test3(5) != 5)
+    abort();
+  if (test3(7) != 5)
+    abort();
+
+  if (test4(0) != 2)
+    abort();
+  if (test4(2) != 2)
+    abort();
+  if (test4(5) != 7)
+    abort();
+  if (test4(7) != 7)
+    abort();
+
+  if (test5(0) != 0)
+    abort();
+  if (test5(2) != 0)
+    abort();
+  if (test5(5) != 5)
+    abort();
+  if (test5(7) != 5)
+    abort();
+
+  if (test6(0) != 0)
+    abort();
+  if (test6(2) != 2)
+    abort();
+  if (test6(5) != 5)
+    abort();
+  if (test6(7) != 7)
+    abort();
+
+  return 0;
+}
+