OSDN Git Service

* Makefile.in (dojump.o): Depend on $(GGC_H) and dojump.h.
[pf3gnuchains/gcc-fork.git] / gcc / dojump.c
index d0cc13c..83af19b 100644 (file)
@@ -33,7 +33,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "expr.h"
 #include "optabs.h"
 #include "langhooks.h"
+#include "ggc.h"
 
+static bool prefer_and_bit_test (enum machine_mode, int);
 static void do_jump_by_parts_greater (tree, int, rtx, rtx);
 static void do_jump_by_parts_equality (tree, rtx, rtx);
 static void do_compare_and_jump        (tree, enum rtx_code, enum rtx_code, rtx,
@@ -101,6 +103,45 @@ jumpif (tree exp, rtx label)
   do_jump (exp, NULL_RTX, label);
 }
 
+/* Used internally by prefer_and_bit_test.  */
+
+static GTY(()) rtx and_reg;
+static GTY(()) rtx and_test;
+static GTY(()) rtx shift_test;
+
+/* Compare the relative costs of "(X & (1 << BITNUM))" and "(X >> BITNUM) & 1",
+   where X is an arbitrary register of mode MODE.  Return true if the former
+   is preferred.  */
+
+static bool
+prefer_and_bit_test (enum machine_mode mode, int bitnum)
+{
+  if (and_test == 0)
+    {
+      /* Set up rtxes for the two variations.  Use NULL as a placeholder
+        for the BITNUM-based constants.  */
+      and_reg = gen_rtx_REG (mode, FIRST_PSEUDO_REGISTER);
+      and_test = gen_rtx_AND (mode, and_reg, NULL);
+      shift_test = gen_rtx_AND (mode, gen_rtx_ASHIFTRT (mode, and_reg, NULL),
+                               const1_rtx);
+    }
+  else
+    {
+      /* Change the mode of the previously-created rtxes.  */
+      PUT_MODE (and_reg, mode);
+      PUT_MODE (and_test, mode);
+      PUT_MODE (shift_test, mode);
+      PUT_MODE (XEXP (shift_test, 0), mode);
+    }
+
+  /* Fill in the integers.  */
+  XEXP (and_test, 0) = GEN_INT ((unsigned HOST_WIDE_INT) 1 << bitnum);
+  XEXP (XEXP (shift_test, 0), 1) = GEN_INT (bitnum);
+
+  return (rtx_cost (and_test, IF_THEN_ELSE)
+         <= rtx_cost (shift_test, IF_THEN_ELSE));
+}
+
 /* Generate code to evaluate EXP and jump to IF_FALSE_LABEL if
    the result is zero, or IF_TRUE_LABEL if the result is one.
    Either of IF_FALSE_LABEL and IF_TRUE_LABEL may be zero,
@@ -206,6 +247,29 @@ do_jump (tree exp, rtx if_false_label, rtx if_true_label)
       break;
 
     case BIT_AND_EXPR:
+      /* fold_single_bit_test() converts (X & (1 << C)) into (X >> C) & 1.
+        See if the former is preferred for jump tests and restore it
+        if so.  */
+      if (TREE_CODE (TREE_OPERAND (exp, 0)) == RSHIFT_EXPR
+         && integer_onep (TREE_OPERAND (exp, 1)))
+       {
+         tree arg = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+         tree shift = TREE_OPERAND (TREE_OPERAND (exp, 0), 1);
+         tree one = TREE_OPERAND (exp, 1);
+         tree argtype = TREE_TYPE (arg);
+         if (TREE_CODE (shift) == INTEGER_CST
+             && compare_tree_int (shift, 0) > 0
+             && compare_tree_int (shift, HOST_BITS_PER_WIDE_INT) < 0
+             && prefer_and_bit_test (TYPE_MODE (argtype),
+                                     TREE_INT_CST_LOW (shift)))
+           {
+             do_jump (build (BIT_AND_EXPR, argtype, arg,
+                             fold (build (LSHIFT_EXPR, argtype, one, shift))),
+                      if_false_label, if_true_label);
+             break;
+           }
+       }
+
       /* If we are AND'ing with a small constant, do this comparison in the
          smallest type that fits.  If the machine doesn't have comparisons
          that small, it will be converted back to the wider comparison.
@@ -999,3 +1063,5 @@ do_compare_and_jump (tree exp, enum rtx_code signed_code,
                             ? expr_size (TREE_OPERAND (exp, 0)) : NULL_RTX),
                            if_false_label, if_true_label);
 }
+
+#include "gt-dojump.h"