re PR target/49903 ([avr] Redundant comparisons in binary-search switch/case expansion)
authorGeorg-Johann Lay <avr@gjlay.de>
Sun, 14 Aug 2011 09:10:13 +0000 (09:10 +0000)
committerGeorg-Johann Lay <gjl@gcc.gnu.org>
Sun, 14 Aug 2011 09:10:13 +0000 (09:10 +0000)
* PR target/49903
* config/avr/avr.md (UNSPEC_IDENTITY): New c_enum.
(branch_unspec): New insn.
(branch): Beauty farm.
* config/avr/avr.c (compare_condition): Use JUMP_P.  Test SET_SRC
to be IF_THEN_ELSE.
(avr_compare_pattern, avr_reorg_remove_redundant_compare):
New static functions.
(avr_reorg): Use them.  Use next_real_insn instead of NEXT_INSN.
Use CONST_INT_P.  Beauty.

From-SVN: r177744

gcc/ChangeLog
gcc/config/avr/avr.c
gcc/config/avr/avr.md

index b600d256a6eef9e82a4f4584b3a68d9b771095a0..6e8c5adb2e9082996450b5a8b19f927262dcba56 100644 (file)
@@ -1,3 +1,16 @@
+2011-08-14  Georg-Johann Lay  <avr@gjlay.de>
+       
+       * PR target/49903
+       * config/avr/avr.md (UNSPEC_IDENTITY): New c_enum.
+       (branch_unspec): New insn.
+       (branch): Beauty farm.
+       * config/avr/avr.c (compare_condition): Use JUMP_P.  Test SET_SRC
+       to be IF_THEN_ELSE.
+       (avr_compare_pattern, avr_reorg_remove_redundant_compare):
+       New static functions.
+       (avr_reorg): Use them.  Use next_real_insn instead of NEXT_INSN.
+       Use CONST_INT_P.  Beauty.
+
 2011-08-12  David Li  <davidxl@google.com>
 
        * cp/class.c (update_vtable_entry_for_fn): Set
index 76542a87dc047a196174c5b03fc7bfbd0929e1fb..6bb236cb58423dafe96b263e0dbc4d123beca8d5 100644 (file)
@@ -2947,15 +2947,17 @@ static RTX_CODE
 compare_condition (rtx insn)
 {
   rtx next = next_real_insn (insn);
-  RTX_CODE cond = UNKNOWN;
-  if (next && GET_CODE (next) == JUMP_INSN)
+
+  if (next && JUMP_P (next))
     {
       rtx pat = PATTERN (next);
       rtx src = SET_SRC (pat);
-      rtx t = XEXP (src, 0);
-      cond = GET_CODE (t);
+      
+      if (IF_THEN_ELSE == GET_CODE (src))
+        return GET_CODE (XEXP (src, 0));
     }
-  return cond;
+  
+  return UNKNOWN;
 }
 
 /* Returns nonzero if INSN is a tst insn that only tests the sign.  */
@@ -6046,82 +6048,265 @@ avr_normalize_condition (RTX_CODE condition)
     }
 }
 
-/* This function optimizes conditional jumps.  */
+/* Helper function for `avr_reorg'.  */
+
+static rtx
+avr_compare_pattern (rtx insn)
+{
+  rtx pattern = single_set (insn);
+
+  if (pattern
+      && NONJUMP_INSN_P (insn)
+      && SET_DEST (pattern) == cc0_rtx
+      && GET_CODE (SET_SRC (pattern)) == COMPARE)
+    {
+      return pattern;
+    }
+
+  return NULL_RTX;
+}
+
+/* Helper function for `avr_reorg'.  */
+
+/* Expansion of switch/case decision trees leads to code like
+
+       cc0 = compare (Reg, Num)
+       if (cc0 == 0)
+         goto L1
+         
+       cc0 = compare (Reg, Num)
+       if (cc0 > 0)
+         goto L2
+
+   The second comparison is superfluous and can be deleted.
+   The second jump condition can be transformed from a
+   "difficult" one to a "simple" one because "cc0 > 0" and
+   "cc0 >= 0" will have the same effect here.
+
+   This function relies on the way switch/case is being expaned
+   as binary decision tree.  For example code see PR 49903.
+         
+   Return TRUE if optimization performed.
+   Return FALSE if nothing changed.
+
+   INSN1 is a comparison, i.e. avr_compare_pattern != 0.
+
+   We don't want to do this in text peephole because it is
+   tedious to work out jump offsets there and the second comparison
+   might have been transormed by `avr_reorg'.
+
+   RTL peephole won't do because peephole2 does not scan across
+   basic blocks.  */                
+                        
+static bool
+avr_reorg_remove_redundant_compare (rtx insn1)
+{
+  rtx comp1, ifelse1, xcond1, branch1;
+  rtx comp2, ifelse2, xcond2, branch2, insn2;
+  enum rtx_code code;
+  rtx jump, target, cond;
+  
+  /* Look out for:  compare1 - branch1 - compare2 - branch2  */
+
+  branch1 = next_nonnote_nondebug_insn (insn1);
+  if (!branch1 || !JUMP_P (branch1))
+    return false;
+
+  insn2 = next_nonnote_nondebug_insn (branch1);
+  if (!insn2 || !avr_compare_pattern (insn2))
+    return false;
+
+  branch2 = next_nonnote_nondebug_insn (insn2);
+  if (!branch2 || !JUMP_P (branch2))
+    return false;
+
+  comp1 = avr_compare_pattern (insn1);
+  comp2 = avr_compare_pattern (insn2);
+  xcond1 = single_set (branch1);
+  xcond2 = single_set (branch2);
+  
+  if (!comp1 || !comp2
+      || !rtx_equal_p (comp1, comp2)
+      || !xcond1 || SET_DEST (xcond1) != pc_rtx
+      || !xcond2 || SET_DEST (xcond2) != pc_rtx
+      || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond1))
+      || IF_THEN_ELSE != GET_CODE (SET_SRC (xcond2)))
+    {
+      return false;
+    }
+
+  comp1 = SET_SRC (comp1);
+  ifelse1 = SET_SRC (xcond1);
+  ifelse2 = SET_SRC (xcond2);
+
+  /* comp<n> is COMPARE now and ifelse<n> is IF_THEN_ELSE.  */
+
+  if (EQ != GET_CODE (XEXP (ifelse1, 0))
+      || !REG_P (XEXP (comp1, 0))
+      || !CONST_INT_P (XEXP (comp1, 1))
+      || XEXP (ifelse1, 2) != pc_rtx
+      || XEXP (ifelse2, 2) != pc_rtx
+      || LABEL_REF != GET_CODE (XEXP (ifelse1, 1))
+      || LABEL_REF != GET_CODE (XEXP (ifelse2, 1))
+      || !COMPARISON_P (XEXP (ifelse2, 0))
+      || cc0_rtx != XEXP (XEXP (ifelse1, 0), 0)
+      || cc0_rtx != XEXP (XEXP (ifelse2, 0), 0)
+      || const0_rtx != XEXP (XEXP (ifelse1, 0), 1)
+      || const0_rtx != XEXP (XEXP (ifelse2, 0), 1))
+    {
+      return false;
+    }
+
+  /* We filtered the insn sequence to look like
+
+        (set (cc0)
+             (compare (reg:M N)
+                      (const_int VAL)))
+        (set (pc)
+             (if_then_else (eq (cc0)
+                               (const_int 0))
+                           (label_ref L1)
+                           (pc)))
+                           
+        (set (cc0)
+             (compare (reg:M N)
+                      (const_int VAL)))
+        (set (pc)
+             (if_then_else (CODE (cc0)
+                                 (const_int 0))
+                           (label_ref L2)
+                           (pc)))
+  */
+
+  code = GET_CODE (XEXP (ifelse2, 0));
+
+  /* Map GT/GTU to GE/GEU which is easier for AVR.
+     The first two instructions compare/branch on EQ
+     so we may replace the difficult
+        
+        if (x == VAL)   goto L1;
+        if (x > VAL)    goto L2;
+
+     with easy
+         
+         if (x == VAL)   goto L1;
+         if (x >= VAL)   goto L2;
+
+     Similarly, replace LE/LEU by LT/LTU.  */
+  
+  switch (code)
+    {
+    case EQ:
+    case LT:  case LTU:
+    case GE:  case GEU:
+      break;
+
+    case LE:  case LEU:
+    case GT:  case GTU:
+      code = avr_normalize_condition (code);
+      break;
+      
+    default:
+      return false;
+    }
+
+  /* Wrap the branches into UNSPECs so they won't be changed or
+     optimized in the remainder.  */
+
+  target = XEXP (XEXP (ifelse1, 1), 0);
+  cond = XEXP (ifelse1, 0);
+  jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn1);
+
+  JUMP_LABEL (jump) = JUMP_LABEL (branch1);
+
+  target = XEXP (XEXP (ifelse2, 1), 0);
+  cond = gen_rtx_fmt_ee (code, VOIDmode, cc0_rtx, const0_rtx);
+  jump = emit_jump_insn_after (gen_branch_unspec (target, cond), insn2);
+
+  JUMP_LABEL (jump) = JUMP_LABEL (branch2);
+
+  /* The comparisons in insn1 and insn2 are exactly the same;
+     insn2 is superfluous so delete it.  */
+     
+  delete_insn (insn2);
+  delete_insn (branch1);
+  delete_insn (branch2);
+
+  return true;
+}
+
+
+/* Implement `TARGET_MACHINE_DEPENDENT_REORG'.  */
+/* Optimize conditional jumps.  */
 
 static void
 avr_reorg (void)
 {
-  rtx insn, pattern;
+  rtx insn = get_insns();
   
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  for (insn = next_real_insn (insn); insn; insn = next_real_insn (insn))
     {
-      if (! (GET_CODE (insn) == INSN
-            || GET_CODE (insn) == CALL_INSN
-            || GET_CODE (insn) == JUMP_INSN)
-         || !single_set (insn))
-       continue;
+      rtx pattern = avr_compare_pattern (insn);
+      
+      if (!pattern)
+        continue;
 
-      pattern = PATTERN (insn);
+      if (optimize
+          && avr_reorg_remove_redundant_compare (insn))
+        {
+          continue;
+        }
 
-      if (GET_CODE (pattern) == PARALLEL)
-       pattern = XVECEXP (pattern, 0, 0);
-      if (GET_CODE (pattern) == SET
-         && SET_DEST (pattern) == cc0_rtx
-         && compare_diff_p (insn))
+      if (compare_diff_p (insn))
        {
-         if (GET_CODE (SET_SRC (pattern)) == COMPARE)
-           {
-             /* Now we work under compare insn.  */
-             
-             pattern = SET_SRC (pattern);
-             if (true_regnum (XEXP (pattern,0)) >= 0
-                 && true_regnum (XEXP (pattern,1)) >= 0 )
-               {
-                 rtx x = XEXP (pattern,0);
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
-                 PUT_CODE (t, swap_condition (GET_CODE (t)));
-                 XEXP (pattern,0) = XEXP (pattern,1);
-                 XEXP (pattern,1) = x;
-                 INSN_CODE (next) = -1;
-               }
-             else if (true_regnum (XEXP (pattern, 0)) >= 0
-                      && XEXP (pattern, 1) == const0_rtx)
-               {
-                 /* This is a tst insn, we can reverse it.  */
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
+          /* Now we work under compare insn with difficult branch.  */
+          
+          rtx next = next_real_insn (insn);
+          rtx pat = PATTERN (next);
+
+          pattern = SET_SRC (pattern);
+          
+          if (true_regnum (XEXP (pattern, 0)) >= 0
+              && true_regnum (XEXP (pattern, 1)) >= 0)
+            {
+              rtx x = XEXP (pattern, 0);
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
+              PUT_CODE (t, swap_condition (GET_CODE (t)));
+              XEXP (pattern, 0) = XEXP (pattern, 1);
+              XEXP (pattern, 1) = x;
+              INSN_CODE (next) = -1;
+            }
+          else if (true_regnum (XEXP (pattern, 0)) >= 0
+                   && XEXP (pattern, 1) == const0_rtx)
+            {
+              /* This is a tst insn, we can reverse it.  */
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
     
-                 PUT_CODE (t, swap_condition (GET_CODE (t)));
-                 XEXP (pattern, 1) = XEXP (pattern, 0);
-                 XEXP (pattern, 0) = const0_rtx;
-                 INSN_CODE (next) = -1;
-                 INSN_CODE (insn) = -1;
-               }
-             else if (true_regnum (XEXP (pattern,0)) >= 0
-                      && GET_CODE (XEXP (pattern,1)) == CONST_INT)
-               {
-                 rtx x = XEXP (pattern,1);
-                 rtx next = next_real_insn (insn);
-                 rtx pat = PATTERN (next);
-                 rtx src = SET_SRC (pat);
-                 rtx t = XEXP (src,0);
-                 enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
-
-                 if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
-                   {
-                     XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
-                     PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
-                     INSN_CODE (next) = -1;
-                     INSN_CODE (insn) = -1;
-                   }
-               }
-           }
-       }
+              PUT_CODE (t, swap_condition (GET_CODE (t)));
+              XEXP (pattern, 1) = XEXP (pattern, 0);
+              XEXP (pattern, 0) = const0_rtx;
+              INSN_CODE (next) = -1;
+              INSN_CODE (insn) = -1;
+            }
+          else if (true_regnum (XEXP (pattern, 0)) >= 0
+                   && CONST_INT_P (XEXP (pattern, 1)))
+            {
+              rtx x = XEXP (pattern, 1);
+              rtx src = SET_SRC (pat);
+              rtx t = XEXP (src,0);
+              enum machine_mode mode = GET_MODE (XEXP (pattern, 0));
+              
+              if (avr_simplify_comparison_p (mode, GET_CODE (t), x))
+                {
+                  XEXP (pattern, 1) = gen_int_mode (INTVAL (x) + 1, mode);
+                  PUT_CODE (t, avr_normalize_condition (GET_CODE (t)));
+                  INSN_CODE (next) = -1;
+                  INSN_CODE (insn) = -1;
+                }
+            }
+        }
     }
 }
 
index 356b5095438043cabef60180e8efd8cec4cca853..ad0febc333e7405cc3ecca6e7c110efb64940e8c 100644 (file)
@@ -56,6 +56,7 @@
    UNSPEC_FMULS
    UNSPEC_FMULSU
    UNSPEC_COPYSIGN
+   UNSPEC_IDENTITY
    ])
 
 (define_c_enum "unspecv"
 (define_insn "branch"
   [(set (pc)
         (if_then_else (match_operator 1 "simple_comparison_operator"
-                        [(cc0)
-                         (const_int 0)])
+                                      [(cc0)
+                                       (const_int 0)])
                       (label_ref (match_operand 0 "" ""))
                       (pc)))]
   ""
-  "*
-   return ret_cond_branch (operands[1], avr_jump_mode (operands[0],insn), 0);"
+  {
+    return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0);
+  }
   [(set_attr "type" "branch")
    (set_attr "cc" "clobber")])
 
+
+;; Same as above but wrap SET_SRC so that this branch won't be transformed
+;; or optimized in the remainder.
+
+(define_insn "branch_unspec"
+  [(set (pc)
+        (unspec [(if_then_else (match_operator 1 "simple_comparison_operator"
+                                               [(cc0)
+                                                (const_int 0)])
+                               (label_ref (match_operand 0 "" ""))
+                               (pc))
+                 ] UNSPEC_IDENTITY))]
+  ""
+  {
+    return ret_cond_branch (operands[1], avr_jump_mode (operands[0], insn), 0);
+  }
+  [(set_attr "type" "branch")
+   (set_attr "cc" "none")])
+
 ;; ****************************************************************
 ;; AVR does not have following conditional jumps: LE,LEU,GT,GTU.
 ;; Convert them all to proper jumps.