OSDN Git Service

* config/arm/arm.c (arm_promote_prototypes): Use TARGET_AAPCS_BASED.
[pf3gnuchains/gcc-fork.git] / gcc / cselib.c
index c3a6872..9d738df 100644 (file)
@@ -1,6 +1,6 @@
 /* Common subexpression elimination library for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -40,7 +40,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "hashtab.h"
 #include "cselib.h"
 #include "params.h"
+#include "alloc-pool.h"
 
+static bool cselib_record_memory;
 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 *);
@@ -73,7 +75,7 @@ static void cselib_record_sets (rtx);
      the locations of the entries with the rtx we are looking up.  */
 
 /* A table that enables us to look up elts by their value.  */
-static GTY((param_is (cselib_val))) htab_t hash_table;
+static htab_t hash_table;
 
 /* This is a global so we don't have to pass this through every function.
    It is used in new_elt_loc_list to set SETTING_INSN.  */
@@ -100,9 +102,9 @@ static int n_useless_values;
    which the register was set; if the mode is unknown or the value is
    no longer valid in that mode, ELT will be NULL for the first
    element.  */
-static GTY(()) varray_type reg_values;
-static GTY((deletable (""))) varray_type reg_values_old;
-#define REG_VALUES(I) VARRAY_ELT_LIST (reg_values, (I))
+struct elt_list **reg_values;
+unsigned int reg_values_size;
+#define REG_VALUES(i) reg_values[i]
 
 /* The largest number of hard regs used by any entry added to the
    REG_VALUES table.  Cleared on each clear_table() invocation.  */
@@ -110,18 +112,13 @@ static unsigned int max_value_regs;
 
 /* Here the set of indices I with REG_VALUES(I) != 0 is saved.  This is used
    in clear_table() for fast emptying.  */
-static GTY(()) varray_type used_regs;
-static GTY((deletable (""))) varray_type used_regs_old;
+static unsigned int *used_regs;
+static unsigned int n_used_regs;
 
 /* We pass this to cselib_invalidate_mem to invalidate all of
    memory for a non-const call instruction.  */
 static GTY(()) rtx callmem;
 
-/* Caches for unused structures.  */
-static GTY((deletable (""))) cselib_val *empty_vals;
-static GTY((deletable (""))) struct elt_list *empty_elt_lists;
-static GTY((deletable (""))) struct elt_loc_list *empty_elt_loc_lists;
-
 /* Set by discard_useless_locs if it deleted the last location of any
    value.  */
 static int values_became_useless;
@@ -134,20 +131,17 @@ static cselib_val dummy_val;
    May or may not contain the useless values - the list is compacted
    each time memory is invalidated.  */
 static cselib_val *first_containing_mem = &dummy_val;
+static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
 \f
 
 /* Allocate a struct elt_list and fill in its two elements with the
    arguments.  */
 
-static struct elt_list *
+static inline struct elt_list *
 new_elt_list (struct elt_list *next, cselib_val *elt)
 {
-  struct elt_list *el = empty_elt_lists;
-
-  if (el)
-    empty_elt_lists = el->next;
-  else
-    el = ggc_alloc (sizeof (struct elt_list));
+  struct elt_list *el;
+  el = pool_alloc (elt_list_pool);
   el->next = next;
   el->elt = elt;
   return el;
@@ -156,18 +150,13 @@ new_elt_list (struct elt_list *next, cselib_val *elt)
 /* Allocate a struct elt_loc_list and fill in its two elements with the
    arguments.  */
 
-static struct elt_loc_list *
+static inline struct elt_loc_list *
 new_elt_loc_list (struct elt_loc_list *next, rtx loc)
 {
-  struct elt_loc_list *el = empty_elt_loc_lists;
-
-  if (el)
-    empty_elt_loc_lists = el->next;
-  else
-    el = ggc_alloc (sizeof (struct elt_loc_list));
+  struct elt_loc_list *el;
+  el = pool_alloc (elt_loc_list_pool);
   el->next = next;
   el->loc = loc;
-  el->canon_loc = NULL;
   el->setting_insn = cselib_current_insn;
   el->in_libcall = cselib_current_insn_in_libcall;
   return el;
@@ -176,14 +165,13 @@ new_elt_loc_list (struct elt_loc_list *next, rtx loc)
 /* The elt_list at *PL is no longer needed.  Unchain it and free its
    storage.  */
 
-static void
+static inline void
 unchain_one_elt_list (struct elt_list **pl)
 {
   struct elt_list *l = *pl;
 
   *pl = l->next;
-  l->next = empty_elt_lists;
-  empty_elt_lists = l;
+  pool_free (elt_list_pool, l);
 }
 
 /* Likewise for elt_loc_lists.  */
@@ -194,8 +182,7 @@ unchain_one_elt_loc_list (struct elt_loc_list **pl)
   struct elt_loc_list *l = *pl;
 
   *pl = l->next;
-  l->next = empty_elt_loc_lists;
-  empty_elt_loc_lists = l;
+  pool_free (elt_loc_list_pool, l);
 }
 
 /* Likewise for cselib_vals.  This also frees the addr_list associated with
@@ -207,8 +194,7 @@ unchain_one_value (cselib_val *v)
   while (v->addr_list)
     unchain_one_elt_list (&v->addr_list);
 
-  v->u.next_free = empty_vals;
-  empty_vals = v;
+  pool_free (cselib_val_pool, v);
 }
 
 /* Remove all entries from the hash table.  Also used during
@@ -220,12 +206,12 @@ clear_table (void)
 {
   unsigned int i;
 
-  for (i = 0; i < VARRAY_ACTIVE_SIZE (used_regs); i++)
-    REG_VALUES (VARRAY_UINT (used_regs, i)) = 0;
+  for (i = 0; i < n_used_regs; i++)
+    REG_VALUES (used_regs[i]) = 0;
 
   max_value_regs = 0;
 
-  VARRAY_POP_ALL (used_regs);
+  n_used_regs = 0;
 
   htab_empty (hash_table);
 
@@ -346,6 +332,7 @@ discard_useless_values (void **x, void *info ATTRIBUTE_UNUSED)
 
   if (v->locs == 0)
     {
+      CSELIB_VAL_PTR (v->u.val_rtx) = NULL;
       htab_clear_slot (hash_table, x);
       unchain_one_value (v);
       n_useless_values--;
@@ -371,7 +358,6 @@ remove_useless_values (void)
   while (values_became_useless);
 
   /* Second pass: actually remove the values.  */
-  htab_traverse (hash_table, discard_useless_values, 0);
 
   p = &first_containing_mem;
   for (v = *p; v != &dummy_val; v = v->next_containing_mem)
@@ -382,6 +368,8 @@ remove_useless_values (void)
       }
   *p = &dummy_val;
 
+  htab_traverse (hash_table, discard_useless_values, 0);
+
   if (n_useless_values != 0)
     abort ();
 }
@@ -697,21 +685,23 @@ hash_rtx (rtx x, enum machine_mode mode, int create)
 /* Create a new value structure for VALUE and initialize it.  The mode of the
    value is MODE.  */
 
-static cselib_val *
+static inline cselib_val *
 new_cselib_val (unsigned int value, enum machine_mode mode)
 {
-  cselib_val *e = empty_vals;
-
-  if (e)
-    empty_vals = e->u.next_free;
-  else
-    e = ggc_alloc (sizeof (cselib_val));
+  cselib_val *e = pool_alloc (cselib_val_pool);
 
+#ifdef ENABLE_CHECKING
   if (value == 0)
     abort ();
+#endif
 
   e->value = value;
-  e->u.val_rtx = gen_rtx_VALUE (mode);
+  /* We use custom method to allocate this RTL construct because it accounts
+     about 8% of overall memory usage.  */
+  e->u.val_rtx = pool_alloc (value_pool);
+  memset (e->u.val_rtx, 0, RTX_HDR_SIZE);
+  PUT_CODE (e->u.val_rtx, VALUE);
+  PUT_MODE (e->u.val_rtx, mode);
   CSELIB_VAL_PTR (e->u.val_rtx) = e;
   e->addr_list = 0;
   e->locs = 0;
@@ -758,6 +748,7 @@ cselib_lookup_mem (rtx x, int create)
   struct elt_list *l;
 
   if (MEM_VOLATILE_P (x) || mode == BLKmode
+      || !cselib_record_memory
       || (FLOAT_MODE_P (mode) && flag_float_store))
     return 0;
 
@@ -910,7 +901,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
 
       if (i < FIRST_PSEUDO_REGISTER)
        {
-         unsigned int n = HARD_REGNO_NREGS (i, mode);
+         unsigned int n = hard_regno_nregs[i][mode];
 
          if (n > max_value_regs)
            max_value_regs = n;
@@ -923,7 +914,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
          /* Maintain the invariant that the first entry of
             REG_VALUES, if present, must be the value used to set the
             register, or NULL.  */
-         VARRAY_PUSH_UINT (used_regs, i);
+         used_regs[n_used_regs++] = i;
          REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
        }
       REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
@@ -990,7 +981,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
       else
        i = regno - max_value_regs;
 
-      endregno = regno + HARD_REGNO_NREGS (regno, mode);
+      endregno = regno + hard_regno_nregs[regno][mode];
     }
   else
     {
@@ -1011,7 +1002,7 @@ cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
          unsigned int this_last = i;
 
          if (i < FIRST_PSEUDO_REGISTER && v != NULL)
-           this_last += HARD_REGNO_NREGS (i, GET_MODE (v->u.val_rtx)) - 1;
+           this_last += hard_regno_nregs[i][GET_MODE (v->u.val_rtx)] - 1;
 
          if (this_last < regno || v == NULL)
            {
@@ -1089,7 +1080,6 @@ cselib_invalidate_mem (rtx mem_rtx)
       while (*p)
        {
          rtx x = (*p)->loc;
-         rtx canon_x = (*p)->canon_loc;
          cselib_val *addr;
          struct elt_list **mem_chain;
 
@@ -1100,8 +1090,6 @@ cselib_invalidate_mem (rtx mem_rtx)
              p = &(*p)->next;
              continue;
            }
-         if (!canon_x)
-           canon_x = (*p)->canon_loc = canon_rtx (x);
          if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
              && ! canon_true_dependence (mem_rtx, GET_MODE (mem_rtx), mem_addr,
                                          x, cselib_rtx_varies_p))
@@ -1187,7 +1175,7 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
     {
       if (dreg < FIRST_PSEUDO_REGISTER)
        {
-         unsigned int n = HARD_REGNO_NREGS (dreg, GET_MODE (dest));
+         unsigned int n = hard_regno_nregs[dreg][GET_MODE (dest)];
 
          if (n > max_value_regs)
            max_value_regs = n;
@@ -1195,7 +1183,7 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
 
       if (REG_VALUES (dreg) == 0)
        {
-         VARRAY_PUSH_UINT (used_regs, dreg);
+         used_regs[n_used_regs++] = dreg;
          REG_VALUES (dreg) = new_elt_list (REG_VALUES (dreg), src_elt);
        }
       else
@@ -1211,7 +1199,8 @@ cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
        n_useless_values--;
       src_elt->locs = new_elt_loc_list (src_elt->locs, dest);
     }
-  else if (GET_CODE (dest) == MEM && dest_addr_elt != 0)
+  else if (GET_CODE (dest) == MEM && dest_addr_elt != 0
+          && cselib_record_memory)
     {
       if (src_elt->locs == 0)
        n_useless_values--;
@@ -1285,7 +1274,8 @@ cselib_record_sets (rtx insn)
        sets[i].dest = dest = XEXP (dest, 0);
 
       /* We don't know how to record anything but REG or MEM.  */
-      if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
+      if (GET_CODE (dest) == REG
+         || (GET_CODE (dest) == MEM && cselib_record_memory))
         {
          rtx src = sets[i].src;
          if (cond)
@@ -1303,11 +1293,35 @@ cselib_record_sets (rtx insn)
      locations may go away.  */
   note_stores (body, cselib_invalidate_rtx, NULL);
 
+  /* If this is an asm, look for duplicate sets.  This can happen when the
+     user uses the same value as an output multiple times.  This is valid
+     if the outputs are not actually used thereafter.  Treat this case as
+     if the value isn't actually set.  We do this by smashing the destination
+     to pc_rtx, so that we won't record the value later.  */
+  if (n_sets >= 2 && asm_noperands (body) >= 0)
+    {
+      for (i = 0; i < n_sets; i++)
+       {
+         rtx dest = sets[i].dest;
+         if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
+           {
+             int j;
+             for (j = i + 1; j < n_sets; j++)
+               if (rtx_equal_p (dest, sets[j].dest))
+                 {
+                   sets[i].dest = pc_rtx;
+                   sets[j].dest = pc_rtx;
+                 }
+           }
+       }
+    }
+
   /* Now enter the equivalences in our tables.  */
   for (i = 0; i < n_sets; i++)
     {
       rtx dest = sets[i].dest;
-      if (GET_CODE (dest) == REG || GET_CODE (dest) == MEM)
+      if (GET_CODE (dest) == REG
+         || (GET_CODE (dest) == MEM && cselib_record_memory))
        cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
     }
 }
@@ -1381,45 +1395,42 @@ cselib_process_insn (rtx insn)
     remove_useless_values ();
 }
 
-/* Make sure our varrays are big enough.  Not called from any cselib routines;
-   it must be called by the user if it allocated new registers.  */
-
-void
-cselib_update_varray_sizes (void)
-{
-  unsigned int nregs = max_reg_num ();
-
-  if (nregs == cselib_nregs)
-    return;
-
-  cselib_nregs = nregs;
-  VARRAY_GROW (reg_values, nregs);
-  VARRAY_GROW (used_regs, nregs);
-}
-
 /* Initialize cselib for one pass.  The caller must also call
    init_alias_analysis.  */
 
 void
-cselib_init (void)
+cselib_init (bool record_memory)
 {
+  elt_list_pool = create_alloc_pool ("elt_list", 
+                                    sizeof (struct elt_list), 10);
+  elt_loc_list_pool = create_alloc_pool ("elt_loc_list", 
+                                        sizeof (struct elt_loc_list), 10);
+  cselib_val_pool = create_alloc_pool ("cselib_val_list", 
+                                      sizeof (cselib_val), 10);
+  value_pool = create_alloc_pool ("value", 
+                                 RTX_SIZE (VALUE), 100);
+  cselib_record_memory = record_memory;
   /* This is only created once.  */
   if (! callmem)
     callmem = gen_rtx_MEM (BLKmode, const0_rtx);
 
   cselib_nregs = max_reg_num ();
-  if (reg_values_old != NULL && VARRAY_SIZE (reg_values_old) >= cselib_nregs)
-    {
-      reg_values = reg_values_old;
-      used_regs = used_regs_old;
-    }
-  else
+
+  /* We preserve reg_values to allow expensive clearing of the whole thing.
+     Reallocate it however if it happens to be too large.  */
+  if (!reg_values || reg_values_size < cselib_nregs
+      || (reg_values_size > 10 && reg_values_size > cselib_nregs * 4))
     {
-      VARRAY_ELT_LIST_INIT (reg_values, cselib_nregs, "reg_values");
-      VARRAY_UINT_INIT (used_regs, cselib_nregs, "used_regs");
+      if (reg_values)
+       free (reg_values);
+      /* Some space for newly emit instructions so we don't end up
+        reallocating in between passes.  */
+      reg_values_size = cselib_nregs + (63 + cselib_nregs) / 16;
+      reg_values = xcalloc (reg_values_size, sizeof (reg_values));
     }
-  hash_table = htab_create_ggc (31, get_value_hash, entry_and_rtx_equal_p,
-                               NULL);
+  used_regs = xmalloc (sizeof (*used_regs) * cselib_nregs);
+  n_used_regs = 0;
+  hash_table = htab_create (31, get_value_hash, entry_and_rtx_equal_p, NULL);
   cselib_current_insn_in_libcall = false;
 }
 
@@ -1428,10 +1439,13 @@ cselib_init (void)
 void
 cselib_finish (void)
 {
+  free_alloc_pool (elt_list_pool);
+  free_alloc_pool (elt_loc_list_pool);
+  free_alloc_pool (cselib_val_pool);
+  free_alloc_pool (value_pool);
   clear_table ();
-  reg_values_old = reg_values;
-  reg_values = 0;
-  used_regs_old = used_regs;
+  htab_delete (hash_table);
+  free (used_regs);
   used_regs = 0;
   hash_table = 0;
   n_useless_values = 0;