OSDN Git Service

PR middle-end/33699
authornemet <nemet@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 28 May 2009 07:42:52 +0000 (07:42 +0000)
committernemet <nemet@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 28 May 2009 07:42:52 +0000 (07:42 +0000)
* target.h (struct gcc_target): Fix indentation.  Add
const_anchor.
* target-def.h (TARGET_CONST_ANCHOR): New macro.
(TARGET_INITIALIZER): Use it.
* cse.c (CHEAPER): Move it up to the other macros.
(insert): Rename this ...
(insert_with_costs): ... to this.  Add cost parameters.  Update
function comment.
(insert): New function.  Call insert_with_costs.
(compute_const_anchors, insert_const_anchor, insert_const_anchors,
find_reg_offset_for_const, try_const_anchors): New functions.
(cse_insn): Call try_const_anchors.  Adjust cost of src_related
when using a const-anchor.  Call insert_const_anchors.
* config/mips/mips.c (mips_set_mips16_mode): Set
targetm.const_anchor.
* doc/tm.texi (Misc): Document TARGET_CONST_ANCHOR.

testsuite/
* gcc.target/mips/const-anchor-1.c: New test.
* gcc.target/mips/const-anchor-2.c: New test.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@147944 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/mips/mips.c
gcc/cse.c
gcc/doc/tm.texi
gcc/target-def.h
gcc/target.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/mips/const-anchor-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/mips/const-anchor-2.c [new file with mode: 0644]

index c2e6e76..19324c7 100644 (file)
@@ -1,3 +1,23 @@
+2009-05-28  Adam Nemet  <anemet@caviumnetworks.com>
+
+       PR middle-end/33699
+       * target.h (struct gcc_target): Fix indentation.  Add
+       const_anchor.
+       * target-def.h (TARGET_CONST_ANCHOR): New macro.
+       (TARGET_INITIALIZER): Use it.
+       * cse.c (CHEAPER): Move it up to the other macros.
+       (insert): Rename this ...
+       (insert_with_costs): ... to this.  Add cost parameters.  Update
+       function comment.
+       (insert): New function.  Call insert_with_costs.
+       (compute_const_anchors, insert_const_anchor, insert_const_anchors,
+       find_reg_offset_for_const, try_const_anchors): New functions.
+       (cse_insn): Call try_const_anchors.  Adjust cost of src_related
+       when using a const-anchor.  Call insert_const_anchors.
+       * config/mips/mips.c (mips_set_mips16_mode): Set
+       targetm.const_anchor.
+       * doc/tm.texi (Misc): Document TARGET_CONST_ANCHOR.
+
 2009-05-28  Alexandre Oliva  <aoliva@redhat.com>
 
        * tree-inline.c (remap_decls): Enable nonlocalized variables
index ed10c39..50a47e9 100644 (file)
@@ -13928,6 +13928,8 @@ mips_set_mips16_mode (int mips16_p)
       targetm.min_anchor_offset = 0;
       targetm.max_anchor_offset = 127;
 
+      targetm.const_anchor = 0;
+
       if (flag_pic && !TARGET_OLDABI)
        sorry ("MIPS16 PIC for ABIs other than o32 and o64");
 
@@ -13955,6 +13957,8 @@ mips_set_mips16_mode (int mips16_p)
 
       targetm.min_anchor_offset = -32768;
       targetm.max_anchor_offset = 32767;
+
+      targetm.const_anchor = 0x8000;
     }
 
   /* (Re)initialize MIPS target internals for new ISA.  */
index a697ed4..8e37b64 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -502,6 +502,11 @@ struct table_elt
 
 #define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0)
 
+/* Compare table_elt X and Y and return true iff X is cheaper than Y.  */
+
+#define CHEAPER(X, Y) \
+ (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
+
 static struct table_elt *table[HASH_SIZE];
 
 /* Chain of `struct table_elt's made so far for this function
@@ -563,6 +568,8 @@ static void remove_pseudo_from_table (rtx, unsigned);
 static struct table_elt *lookup (rtx, unsigned, enum machine_mode);
 static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode);
 static rtx lookup_as_function (rtx, enum rtx_code);
+static struct table_elt *insert_with_costs (rtx, struct table_elt *, unsigned,
+                                           enum machine_mode, int, int);
 static struct table_elt *insert (rtx, struct table_elt *, unsigned,
                                 enum machine_mode);
 static void merge_equiv_classes (struct table_elt *, struct table_elt *);
@@ -1213,6 +1220,174 @@ insert_regs (rtx x, struct table_elt *classp, int modified)
     return mention_regs (x);
 }
 \f
+
+/* Compute upper and lower anchors for CST.  Also compute the offset of CST
+   from these anchors/bases such that *_BASE + *_OFFS = CST.  Return false iff
+   CST is equal to an anchor.  */
+
+static bool
+compute_const_anchors (rtx cst,
+                      HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs,
+                      HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs)
+{
+  HOST_WIDE_INT n = INTVAL (cst);
+
+  *lower_base = n & ~(targetm.const_anchor - 1);
+  if (*lower_base == n)
+    return false;
+
+  *upper_base =
+    (n + (targetm.const_anchor - 1)) & ~(targetm.const_anchor - 1);
+  *upper_offs = n - *upper_base;
+  *lower_offs = n - *lower_base;
+  return true;
+}
+
+/* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE.  */
+
+static void
+insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs,
+                    enum machine_mode mode)
+{
+  struct table_elt *elt;
+  unsigned hash;
+  rtx anchor_exp;
+  rtx exp;
+
+  anchor_exp = GEN_INT (anchor);
+  hash = HASH (anchor_exp, mode);
+  elt = lookup (anchor_exp, hash, mode);
+  if (!elt)
+    elt = insert (anchor_exp, NULL, hash, mode);
+
+  exp = plus_constant (reg, offs);
+  /* REG has just been inserted and the hash codes recomputed.  */
+  mention_regs (exp);
+  hash = HASH (exp, mode);
+
+  /* Use the cost of the register rather than the whole expression.  When
+     looking up constant anchors we will further offset the corresponding
+     expression therefore it does not make sense to prefer REGs over
+     reg-immediate additions.  Prefer instead the oldest expression.  Also
+     don't prefer pseudos over hard regs so that we derive constants in
+     argument registers from other argument registers rather than from the
+     original pseudo that was used to synthesize the constant.  */
+  insert_with_costs (exp, elt, hash, mode, COST (reg), 1);
+}
+
+/* The constant CST is equivalent to the register REG.  Create
+   equivalences between the two anchors of CST and the corresponding
+   register-offset expressions using REG.  */
+
+static void
+insert_const_anchors (rtx reg, rtx cst, enum machine_mode mode)
+{
+  HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs;
+
+  if (!compute_const_anchors (cst, &lower_base, &lower_offs,
+                             &upper_base, &upper_offs))
+      return;
+
+  /* Ignore anchors of value 0.  Constants accessible from zero are
+     simple.  */
+  if (lower_base != 0)
+    insert_const_anchor (lower_base, reg, -lower_offs, mode);
+
+  if (upper_base != 0)
+    insert_const_anchor (upper_base, reg, -upper_offs, mode);
+}
+
+/* We need to express ANCHOR_ELT->exp + OFFS.  Walk the equivalence list of
+   ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a
+   valid expression.  Return the cheapest and oldest of such expressions.  In
+   *OLD, return how old the resulting expression is compared to the other
+   equivalent expressions.  */
+
+static rtx
+find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs,
+                          unsigned *old)
+{
+  struct table_elt *elt;
+  unsigned idx;
+  struct table_elt *match_elt;
+  rtx match;
+
+  /* Find the cheapest and *oldest* expression to maximize the chance of
+     reusing the same pseudo.  */
+
+  match_elt = NULL;
+  match = NULL_RTX;
+  for (elt = anchor_elt->first_same_value, idx = 0;
+       elt;
+       elt = elt->next_same_value, idx++)
+    {
+      if (match_elt && CHEAPER (match_elt, elt))
+       return match;
+
+      if (REG_P (elt->exp)
+         || (GET_CODE (elt->exp) == PLUS
+             && REG_P (XEXP (elt->exp, 0))
+             && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT))
+       {
+         rtx x;
+
+         /* Ignore expressions that are no longer valid.  */
+         if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false))
+           continue;
+
+         x = plus_constant (elt->exp, offs);
+         if (REG_P (x)
+             || (GET_CODE (x) == PLUS
+                 && IN_RANGE (INTVAL (XEXP (x, 1)),
+                              -targetm.const_anchor,
+                              targetm.const_anchor - 1)))
+           {
+             match = x;
+             match_elt = elt;
+             *old = idx;
+           }
+       }
+    }
+
+  return match;
+}
+
+/* Try to express the constant SRC_CONST using a register+offset expression
+   derived from a constant anchor.  Return it if successful or NULL_RTX,
+   otherwise.  */
+
+static rtx
+try_const_anchors (rtx src_const, enum machine_mode mode)
+{
+  struct table_elt *lower_elt, *upper_elt;
+  HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs;
+  rtx lower_anchor_rtx, upper_anchor_rtx;
+  rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX;
+  unsigned lower_old, upper_old;
+
+  if (!compute_const_anchors (src_const, &lower_base, &lower_offs,
+                             &upper_base, &upper_offs))
+    return NULL_RTX;
+
+  lower_anchor_rtx = GEN_INT (lower_base);
+  upper_anchor_rtx = GEN_INT (upper_base);
+  lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode);
+  upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode);
+
+  if (lower_elt)
+    lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old);
+  if (upper_elt)
+    upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old);
+
+  if (!lower_exp)
+    return upper_exp;
+  if (!upper_exp)
+    return lower_exp;
+
+  /* Return the older expression.  */
+  return (upper_old > lower_old ? upper_exp : lower_exp);
+}
+\f
 /* Look in or update the hash table.  */
 
 /* Remove table element ELT from use in the table.
@@ -1380,11 +1555,11 @@ lookup_as_function (rtx x, enum rtx_code code)
   return 0;
 }
 
-/* Insert X in the hash table, assuming HASH is its hash code
-   and CLASSP is an element of the class it should go in
-   (or 0 if a new class should be made).
-   It is inserted at the proper position to keep the class in
-   the order cheapest first.
+/* Insert X in the hash table, assuming HASH is its hash code and
+   CLASSP is an element of the class it should go in (or 0 if a new
+   class should be made).  COST is the code of X and reg_cost is the
+   cost of registers in X.  It is inserted at the proper position to
+   keep the class in the order cheapest first.
 
    MODE is the machine-mode of X, or if X is an integer constant
    with VOIDmode then MODE is the mode with which X will be used.
@@ -1404,11 +1579,9 @@ lookup_as_function (rtx x, enum rtx_code code)
 
    If necessary, update table showing constant values of quantities.  */
 
-#define CHEAPER(X, Y) \
- (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
-
 static struct table_elt *
-insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode)
+insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash,
+                  enum machine_mode mode, int cost, int reg_cost)
 {
   struct table_elt *elt;
 
@@ -1430,8 +1603,8 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo
 
   elt->exp = x;
   elt->canon_exp = NULL_RTX;
-  elt->cost = COST (x);
-  elt->regcost = approx_reg_cost (x);
+  elt->cost = cost;
+  elt->regcost = reg_cost;
   elt->next_same_value = 0;
   elt->prev_same_value = 0;
   elt->next_same_hash = table[hash];
@@ -1566,6 +1739,17 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo
 
   return elt;
 }
+
+/* Wrap insert_with_costs by passing the default costs.  */
+
+static struct table_elt *
+insert (rtx x, struct table_elt *classp, unsigned int hash,
+       enum machine_mode mode)
+{
+  return
+    insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x));
+}
+
 \f
 /* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from
    CLASS2 into CLASS1.  This is done when we have reached an insn which makes
@@ -4253,6 +4437,7 @@ cse_insn (rtx insn)
       rtx src_eqv_here;
       rtx src_const = 0;
       rtx src_related = 0;
+      bool src_related_is_const_anchor = false;
       struct table_elt *src_const_elt = 0;
       int src_cost = MAX_COST;
       int src_eqv_cost = MAX_COST;
@@ -4602,6 +4787,19 @@ cse_insn (rtx insn)
        }
 #endif /* LOAD_EXTEND_OP */
 
+      /* Try to express the constant using a register+offset expression
+        derived from a constant anchor.  */
+
+      if (targetm.const_anchor
+         && !src_related
+         && src_const
+         && GET_CODE (src_const) == CONST_INT)
+       {
+         src_related = try_const_anchors (src_const, mode);
+         src_related_is_const_anchor = src_related != NULL_RTX;
+       }
+
+
       if (src == src_folded)
        src_folded = 0;
 
@@ -4706,6 +4904,18 @@ cse_insn (rtx insn)
            {
              src_related_cost = COST (src_related);
              src_related_regcost = approx_reg_cost (src_related);
+
+             /* If a const-anchor is used to synthesize a constant that
+                normally requires multiple instructions then slightly prefer
+                it over the original sequence.  These instructions are likely
+                to become redundant now.  We can't compare against the cost
+                of src_eqv_here because, on MIPS for example, multi-insn
+                constants have zero cost; they are assumed to be hoisted from
+                loops.  */
+             if (src_related_is_const_anchor
+                 && src_related_cost == src_cost
+                 && src_eqv_here)
+               src_related_cost--;
            }
        }
 
@@ -5440,6 +5650,14 @@ cse_insn (rtx insn)
        elt = insert (dest, sets[i].src_elt,
                      sets[i].dest_hash, GET_MODE (dest));
 
+       /* If this is a constant, insert the constant anchors with the
+          equivalent register-offset expressions using register DEST.  */
+       if (targetm.const_anchor
+           && REG_P (dest)
+           && SCALAR_INT_MODE_P (GET_MODE (dest))
+           && GET_CODE (sets[i].src_elt->exp) == CONST_INT)
+         insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest));
+
        elt->in_memory = (MEM_P (sets[i].inner_dest)
                          && !MEM_READONLY_P (sets[i].inner_dest));
 
index 67e1ca6..552d5c9 100644 (file)
@@ -10852,3 +10852,21 @@ cannot safely move arguments from the registers in which they are passed
 to the stack.  Therefore, this hook should return true in general, but
 false for naked functions.  The default implementation always returns true.
 @end deftypefn
+
+
+@deftypevr {Target Hook} {unsigned HOST_WIDE_INT} TARGET_CONST_ANCHOR
+On some architectures it can take multiple instructions to synthesize
+a constant.  If there is another constant already in a register that
+is close enough in value then it is preferable that the new constant
+is computed from this register using immediate addition or
+substraction.  We accomplish this through CSE.  Besides the value of
+the constant we also add a lower and an upper constant anchor to the
+available expressions.  These are then queried when encountering new
+constants.  The anchors are computed by rounding the constant up and
+down to a multiple of the value of @code{TARGET_CONST_ANCHOR}.
+@code{TARGET_CONST_ANCHOR} should be the maximum positive value
+accepted by immediate-add plus one.  We currently assume that the
+value of @code{TARGET_CONST_ANCHOR} is a power of 2.  For example, on
+MIPS, where add-immediate takes a 16-bit signed value,
+@code{TARGET_CONST_ANCHOR} is set to @samp{0x8000}.  The default value
+is zero, which disables this optimization.  @end deftypevr
index 2de89a0..8aeebeb 100644 (file)
 
 /* In cse.c.  */
 #define TARGET_ADDRESS_COST default_address_cost
+#define TARGET_CONST_ANCHOR 0
 
 /* In builtins.c.  */
 #define TARGET_INIT_BUILTINS hook_void_void
   TARGET_STACK_PROTECT_FAIL,                   \
   TARGET_INVALID_WITHIN_DOLOOP,                        \
   TARGET_VALID_DLLIMPORT_ATTRIBUTE_P,          \
+  TARGET_CONST_ANCHOR,                         \
   TARGET_CALLS,                                        \
   TARGET_INVALID_CONVERSION,                   \
   TARGET_INVALID_UNARY_OP,                     \
index d851425..0d60b11 100644 (file)
@@ -481,7 +481,7 @@ struct gcc_target
 
     /* Target builtin that implements vector permute.  */
     tree (* builtin_vec_perm) (tree, tree*);
-} vectorize;
+  } vectorize;
 
   /* The initial value of target_flags.  */
   int default_target_flags;
@@ -825,6 +825,10 @@ struct gcc_target
      checks to  handle_dll_attribute ().  */
   bool (* valid_dllimport_attribute_p) (const_tree decl);
 
+  /* If non-zero, align constant anchors in CSE to a multiple of this
+     value.  */
+  unsigned HOST_WIDE_INT const_anchor;
+
   /* Functions relating to calls - argument passing, returns, etc.  */
   struct calls {
     bool (*promote_function_args) (const_tree fntype);
index e56f57c..54fa22c 100644 (file)
@@ -1,3 +1,9 @@
+2009-05-28  Adam Nemet  <anemet@caviumnetworks.com>
+
+       PR middle-end/33699
+       * gcc.target/mips/const-anchor-1.c: New test.
+       * gcc.target/mips/const-anchor-2.c: New test.
+
 2009-05-27  Jason Merrill  <jason@redhat.com>
 
        * g++.dg/cpp0x/initlist15.C: New.
diff --git a/gcc/testsuite/gcc.target/mips/const-anchor-1.c b/gcc/testsuite/gcc.target/mips/const-anchor-1.c
new file mode 100644 (file)
index 0000000..6698167
--- /dev/null
@@ -0,0 +1,10 @@
+/* Derive a constant (0x1233ffff) from an intermediate value
+   (0x1234000) used to build another constant.  */
+/* { dg-options "-O" } */
+/* { dg-final { scan-assembler-not "0x12330000|305332224" } } */
+/* { dg-final { scan-assembler "addiu\t\\\$5,\\\$\[0-9\]*,-1" } } */
+
+NOMIPS16 void f ()
+{
+  g (0x12340001, 0x1233ffff);
+}
diff --git a/gcc/testsuite/gcc.target/mips/const-anchor-2.c b/gcc/testsuite/gcc.target/mips/const-anchor-2.c
new file mode 100644 (file)
index 0000000..ccb89bb
--- /dev/null
@@ -0,0 +1,9 @@
+/* Derive a constant (0x30001) from another constant.  */
+/* { dg-options "-O" } */
+/* { dg-final { scan-assembler-not "0x300000|196608" } } */
+/* { dg-final { scan-assembler "addiu\t\\\$5,\\\$\[0-9\]*,32763" } } */
+
+NOMIPS16 void f ()
+{
+  g (0x28006, 0x30001);
+}