/* 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.
#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 *);
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. */
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. */
/* 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. */
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;
+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
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;
{
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);
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--;
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)
}
*p = &dummy_val;
+ htab_traverse (hash_table, discard_useless_values, 0);
+
if (n_useless_values != 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;
struct elt_list *l;
if (MEM_VOLATILE_P (x) || mode == BLKmode
+ || !cselib_record_memory
|| (FLOAT_MODE_P (mode) && flag_float_store))
return 0;
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;
/* 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);
else
i = regno - max_value_regs;
- endregno = regno + HARD_REGNO_NREGS (regno, mode);
+ endregno = regno + hard_regno_nregs[regno][mode];
}
else
{
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)
{
while (*p)
{
rtx x = (*p)->loc;
- rtx canon_x = (*p)->canon_loc;
cselib_val *addr;
struct elt_list **mem_chain;
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))
{
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;
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
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--;
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)
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);
}
}
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);
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;
}
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;