OSDN Git Service

Backported from mainline
[pf3gnuchains/gcc-fork.git] / gcc / cselib.c
index 9934abb..56f2b7f 100644 (file)
@@ -52,6 +52,7 @@ struct elt_list {
 
 static bool cselib_record_memory;
 static bool cselib_preserve_constants;
+static bool cselib_any_perm_equivs;
 static int entry_and_rtx_equal_p (const void *, const void *);
 static hashval_t get_value_hash (const void *);
 static struct elt_list *new_elt_list (struct elt_list *, cselib_val *);
@@ -277,12 +278,27 @@ new_elt_loc_list (cselib_val *val, rtx loc)
            }
          el->next = val->locs;
          next = val->locs = CSELIB_VAL_PTR (loc)->locs;
-         if (CSELIB_VAL_PTR (loc)->next_containing_mem != NULL
-             && val->next_containing_mem == NULL)
-           {
-             val->next_containing_mem = first_containing_mem;
-             first_containing_mem = val;
-           }
+       }
+
+      if (CSELIB_VAL_PTR (loc)->addr_list)
+       {
+         /* Bring in addr_list into canonical node.  */
+         struct elt_list *last = CSELIB_VAL_PTR (loc)->addr_list;
+         while (last->next)
+           last = last->next;
+         last->next = val->addr_list;
+         val->addr_list = CSELIB_VAL_PTR (loc)->addr_list;
+         CSELIB_VAL_PTR (loc)->addr_list = NULL;
+       }
+
+      if (CSELIB_VAL_PTR (loc)->next_containing_mem != NULL
+         && val->next_containing_mem == NULL)
+       {
+         /* Add VAL to the containing_mem list after LOC.  LOC will
+            be removed when we notice it doesn't contain any
+            MEMs.  */
+         val->next_containing_mem = CSELIB_VAL_PTR (loc)->next_containing_mem;
+         CSELIB_VAL_PTR (loc)->next_containing_mem = val;
        }
 
       /* Chain LOC back to VAL.  */
@@ -368,22 +384,29 @@ cselib_clear_table (void)
   cselib_reset_table (1);
 }
 
-/* Remove from hash table all VALUEs except constants
-   and function invariants.  */
+/* Return TRUE if V is a constant, a function invariant or a VALUE
+   equivalence; FALSE otherwise.  */
 
-static int
-preserve_only_constants (void **x, void *info ATTRIBUTE_UNUSED)
+static bool
+invariant_or_equiv_p (cselib_val *v)
 {
-  cselib_val *v = (cselib_val *)*x;
   struct elt_loc_list *l;
 
+  if (v == cfa_base_preserved_val)
+    return true;
+
+  /* Keep VALUE equivalences around.  */
+  for (l = v->locs; l; l = l->next)
+    if (GET_CODE (l->loc) == VALUE)
+      return true;
+
   if (v->locs != NULL
       && v->locs->next == NULL)
     {
       if (CONSTANT_P (v->locs->loc)
          && (GET_CODE (v->locs->loc) != CONST
              || !references_value_p (v->locs->loc, 0)))
-       return 1;
+       return true;
       /* Although a debug expr may be bound to different expressions,
         we can preserve it as if it was constant, to get unification
         and proper merging within var-tracking.  */
@@ -391,24 +414,29 @@ preserve_only_constants (void **x, void *info ATTRIBUTE_UNUSED)
          || GET_CODE (v->locs->loc) == DEBUG_IMPLICIT_PTR
          || GET_CODE (v->locs->loc) == ENTRY_VALUE
          || GET_CODE (v->locs->loc) == DEBUG_PARAMETER_REF)
-       return 1;
-      if (cfa_base_preserved_val)
-       {
-         if (v == cfa_base_preserved_val)
-           return 1;
-         if (GET_CODE (v->locs->loc) == PLUS
-             && CONST_INT_P (XEXP (v->locs->loc, 1))
-             && XEXP (v->locs->loc, 0) == cfa_base_preserved_val->val_rtx)
-           return 1;
-       }
+       return true;
+
+      /* (plus (value V) (const_int C)) is invariant iff V is invariant.  */
+      if (GET_CODE (v->locs->loc) == PLUS
+         && CONST_INT_P (XEXP (v->locs->loc, 1))
+         && GET_CODE (XEXP (v->locs->loc, 0)) == VALUE
+         && invariant_or_equiv_p (CSELIB_VAL_PTR (XEXP (v->locs->loc, 0))))
+       return true;
     }
 
-  /* Keep VALUE equivalences around.  */
-  for (l = v->locs; l; l = l->next)
-    if (GET_CODE (l->loc) == VALUE)
-      return 1;
+  return false;
+}
+
+/* Remove from hash table all VALUEs except constants, function
+   invariants and VALUE equivalences.  */
 
-  htab_clear_slot (cselib_hash_table, x);
+static int
+preserve_constants_and_equivs (void **x, void *info ATTRIBUTE_UNUSED)
+{
+  cselib_val *v = (cselib_val *)*x;
+
+  if (!invariant_or_equiv_p (v))
+    htab_clear_slot (cselib_hash_table, x);
   return 1;
 }
 
@@ -448,9 +476,12 @@ cselib_reset_table (unsigned int num)
     }
 
   if (cselib_preserve_constants)
-    htab_traverse (cselib_hash_table, preserve_only_constants, NULL);
+    htab_traverse (cselib_hash_table, preserve_constants_and_equivs, NULL);
   else
-    htab_empty (cselib_hash_table);
+    {
+      htab_empty (cselib_hash_table);
+      gcc_checking_assert (!cselib_any_perm_equivs);
+    }
 
   n_useless_values = 0;
   n_useless_debug_values = 0;
@@ -641,7 +672,7 @@ remove_useless_values (void)
 
   p = &first_containing_mem;
   for (v = *p; v != &dummy_val; v = v->next_containing_mem)
-    if (v->locs)
+    if (v->locs && v == canonical_cselib_val (v))
       {
        *p = v;
        p = &(*p)->next_containing_mem;
@@ -1020,6 +1051,10 @@ cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
 
   switch (code)
     {
+    case VALUE:
+      e = CSELIB_VAL_PTR (x);
+      return e->hash;
+
     case MEM:
     case REG:
       e = cselib_lookup (x, GET_MODE (x), create, memmode);
@@ -1270,6 +1305,7 @@ add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
 {
   struct elt_loc_list *l;
 
+  addr_elt = canonical_cselib_val (addr_elt);
   mem_elt = canonical_cselib_val (mem_elt);
 
   /* Avoid duplicates.  */
@@ -1318,6 +1354,7 @@ cselib_lookup_mem (rtx x, int create)
   if (! addr)
     return 0;
 
+  addr = canonical_cselib_val (addr);
   /* Find a value that describes a value of our mode at that address.  */
   for (l = addr->addr_list; l; l = l->next)
     if (GET_MODE (l->elt->val_rtx) == mode)
@@ -1351,8 +1388,18 @@ expand_loc (struct elt_loc_list *p, struct expand_value_data *evd,
   unsigned int regno = UINT_MAX;
   struct elt_loc_list *p_in = p;
 
-  for (; p; p = p -> next)
+  for (; p; p = p->next)
     {
+      /* Return these right away to avoid returning stack pointer based
+        expressions for frame pointer and vice versa, which is something
+        that would confuse DSE.  See the comment in cselib_expand_value_rtx_1
+        for more details.  */
+      if (REG_P (p->loc)
+         && (REGNO (p->loc) == STACK_POINTER_REGNUM
+             || REGNO (p->loc) == FRAME_POINTER_REGNUM
+             || REGNO (p->loc) == HARD_FRAME_POINTER_REGNUM
+             || REGNO (p->loc) == cfa_base_preserved_regno))
+       return p->loc;
       /* Avoid infinite recursion trying to expand a reg into a
         the same reg.  */
       if ((REG_P (p->loc))
@@ -1874,6 +1921,19 @@ cselib_subst_to_values (rtx x, enum machine_mode memmode)
   return copy;
 }
 
+/* Wrapper for cselib_subst_to_values, that indicates X is in INSN.  */
+
+rtx
+cselib_subst_to_values_from_insn (rtx x, enum machine_mode memmode, rtx insn)
+{
+  rtx ret;
+  gcc_assert (!cselib_current_insn);
+  cselib_current_insn = insn;
+  ret = cselib_subst_to_values (x, memmode);
+  cselib_current_insn = NULL;
+  return ret;
+}
+
 /* Look up the rtl expression X in our tables and return the value it
    has.  If CREATE is zero, we return NULL if we don't know the value.
    Otherwise, we create a new one if possible, using mode MODE if X
@@ -2151,20 +2211,6 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
     }
 }
 \f
-/* Return 1 if X has a value that can vary even between two
-   executions of the program.  0 means X can be compared reliably
-   against certain constants or near-constants.  */
-
-static bool
-cselib_rtx_varies_p (const_rtx x ATTRIBUTE_UNUSED, bool from_alias ATTRIBUTE_UNUSED)
-{
-  /* We actually don't need to verify very hard.  This is because
-     if X has actually changed, we invalidate the memory anyway,
-     so assume that all common memory addresses are
-     invariant.  */
-  return 0;
-}
-
 /* Invalidate any locations in the table which are changed because of a
    store to MEM_RTX.  If this is called because of a non-const call
    instruction, MEM_RTX is (mem:BLK const0_rtx).  */
@@ -2201,8 +2247,8 @@ cselib_invalidate_mem (rtx mem_rtx)
              continue;
            }
          if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
-             && ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx), mem_addr,
-                                         x, NULL_RTX, cselib_rtx_varies_p))
+             && ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx),
+                                         mem_addr, x, NULL_RTX))
            {
              has_mem = true;
              num_mems++;
@@ -2214,15 +2260,22 @@ cselib_invalidate_mem (rtx mem_rtx)
          /* We must have a mapping from this MEM's address to the
             value (E).  Remove that, too.  */
          addr = cselib_lookup (XEXP (x, 0), VOIDmode, 0, GET_MODE (x));
+         addr = canonical_cselib_val (addr);
+         gcc_checking_assert (v == canonical_cselib_val (v));
          mem_chain = &addr->addr_list;
          for (;;)
            {
-             if (canonical_cselib_val ((*mem_chain)->elt) == v)
+             cselib_val *canon = canonical_cselib_val ((*mem_chain)->elt);
+
+             if (canon == v)
                {
                  unchain_one_elt_list (mem_chain);
                  break;
                }
 
+             /* Record canonicalized elt.  */
+             (*mem_chain)->elt = canon;
+
              mem_chain = &(*mem_chain)->next;
            }
 
@@ -2339,6 +2392,8 @@ cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx insn)
 
   if (nelt != elt)
     {
+      cselib_any_perm_equivs = true;
+
       if (!PRESERVED_VALUE_P (nelt->val_rtx))
        cselib_preserve_value (nelt);
 
@@ -2348,6 +2403,14 @@ cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx insn)
   cselib_current_insn = save_cselib_current_insn;
 }
 
+/* Return TRUE if any permanent equivalences have been recorded since
+   the table was last initialized.  */
+bool
+cselib_have_permanent_equivalences (void)
+{
+  return cselib_any_perm_equivs;
+}
+
 /* There is no good way to determine how many elements there can be
    in a PARALLEL.  Since it's fairly cheap, use a really large number.  */
 #define MAX_SETS (FIRST_PSEUDO_REGISTER * 2)
@@ -2602,6 +2665,7 @@ cselib_init (int record_what)
   value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100);
   cselib_record_memory = record_what & CSELIB_RECORD_MEMORY;
   cselib_preserve_constants = record_what & CSELIB_PRESERVE_CONSTANTS;
+  cselib_any_perm_equivs = false;
 
   /* (mem:BLK (scratch)) is a special mechanism to conflict with everything,
      see canon_true_dependence.  This is only created once.  */
@@ -2635,6 +2699,7 @@ cselib_finish (void)
 {
   cselib_discard_hook = NULL;
   cselib_preserve_constants = false;
+  cselib_any_perm_equivs = false;
   cfa_base_preserved_val = NULL;
   cfa_base_preserved_regno = INVALID_REGNUM;
   free_alloc_pool (elt_list_pool);
@@ -2674,8 +2739,11 @@ dump_cselib_val (void **x, void *info)
       fputs (" locs:", out);
       do
        {
-         fprintf (out, "\n  from insn %i ",
-                  INSN_UID (l->setting_insn));
+         if (l->setting_insn)
+           fprintf (out, "\n  from insn %i ",
+                    INSN_UID (l->setting_insn));
+         else
+           fprintf (out, "\n   ");
          print_inline_rtx (out, l->loc, 4);
        }
       while ((l = l->next));