#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
-#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "function.h"
#include "emit-rtl.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "output.h"
#include "ggc.h"
#include "hashtab.h"
#include "params.h"
#include "alloc-pool.h"
#include "target.h"
+#include "bitmap.h"
+
+/* A list of cselib_val structures. */
+struct elt_list {
+ struct elt_list *next;
+ cselib_val *elt;
+};
static bool cselib_record_memory;
static bool cselib_preserve_constants;
/* The number of registers we had when the varrays were last resized. */
static unsigned int cselib_nregs;
-/* Count values without known locations. Whenever this grows too big, we
- remove these useless values from the table. */
+/* Count values without known locations, or with only locations that
+ wouldn't have been known except for debug insns. Whenever this
+ grows too big, we remove these useless values from the table.
+
+ Counting values with only debug values is a bit tricky. We don't
+ want to increment n_useless_values when we create a value for a
+ debug insn, for this would get n_useless_values out of sync, but we
+ want increment it if all locs in the list that were ever referenced
+ in nondebug insns are removed from the list.
+
+ In the general case, once we do that, we'd have to stop accepting
+ nondebug expressions in the loc list, to avoid having two values
+ equivalent that, without debug insns, would have been made into
+ separate values. However, because debug insns never introduce
+ equivalences themselves (no assignments), the only means for
+ growing loc lists is through nondebug assignments. If the locs
+ also happen to be referenced in debug insns, it will work just fine.
+
+ A consequence of this is that there's at most one debug-only loc in
+ each loc list. If we keep it in the first entry, testing whether
+ we have a debug-only loc list takes O(1).
+
+ Furthermore, since any additional entry in a loc list containing a
+ debug loc would have to come from an assignment (nondebug) that
+ references both the initial debug loc and the newly-equivalent loc,
+ the initial debug loc would be promoted to a nondebug loc, and the
+ loc list would not contain debug locs any more.
+
+ So the only case we have to be careful with in order to keep
+ n_useless_values in sync between debug and nondebug compilations is
+ to avoid incrementing n_useless_values when removing the single loc
+ from a value that turns out to not appear outside debug values. We
+ increment n_useless_debug_values instead, and leave such values
+ alone until, for other reasons, we garbage-collect useless
+ values. */
static int n_useless_values;
+static int n_useless_debug_values;
+
+/* Count values whose locs have been taken exclusively from debug
+ insns for the entire life of the value. */
+static int n_debug_values;
/* Number of useless values before we remove them from the hash table. */
#define MAX_USELESS_VALUES 32
that is constant through the whole function and should never be
eliminated. */
static cselib_val *cfa_base_preserved_val;
+static unsigned int cfa_base_preserved_regno;
/* Used to list all values that contain memory reference.
May or may not contain the useless values - the list is compacted
el->next = next;
el->loc = loc;
el->setting_insn = cselib_current_insn;
+ gcc_assert (!next || !next->setting_insn
+ || !DEBUG_INSN_P (next->setting_insn));
+
+ /* If we're creating the first loc in a debug insn context, we've
+ just created a debug value. Count it. */
+ if (!next && cselib_current_insn && DEBUG_INSN_P (cselib_current_insn))
+ n_debug_values++;
+
return el;
}
+/* Promote loc L to a nondebug cselib_current_insn if L is marked as
+ originating from a debug insn, maintaining the debug values
+ count. */
+
+static inline void
+promote_debug_loc (struct elt_loc_list *l)
+{
+ if (l->setting_insn && DEBUG_INSN_P (l->setting_insn)
+ && (!cselib_current_insn || !DEBUG_INSN_P (cselib_current_insn)))
+ {
+ n_debug_values--;
+ l->setting_insn = cselib_current_insn;
+ gcc_assert (!l->next);
+ }
+}
+
/* The elt_list at *PL is no longer needed. Unchain it and free its
storage. */
if (cfa_base_preserved_val)
{
- unsigned int regno = REGNO (cfa_base_preserved_val->locs->loc);
+ unsigned int regno = cfa_base_preserved_regno;
unsigned int new_used_regs = 0;
for (i = 0; i < n_used_regs; i++)
if (used_regs[i] == regno)
htab_empty (cselib_hash_table);
n_useless_values = 0;
+ n_useless_debug_values = 0;
+ n_debug_values = 0;
next_uid = num;
so we need to do a comparison. */
for (l = v->locs; l; l = l->next)
if (rtx_equal_for_cselib_p (l->loc, x))
- return 1;
+ {
+ promote_debug_loc (l);
+ return 1;
+ }
return 0;
}
{
cselib_val *v = (cselib_val *)*x;
struct elt_loc_list **p = &v->locs;
- int had_locs = v->locs != 0;
+ bool had_locs = v->locs != NULL;
+ rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
while (*p)
{
if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
{
- n_useless_values++;
+ if (setting_insn && DEBUG_INSN_P (setting_insn))
+ n_useless_debug_values++;
+ else
+ n_useless_values++;
values_became_useless = 1;
}
return 1;
remove_useless_values (void)
{
cselib_val **p, *v;
+
/* First pass: eliminate locations that reference the value. That in
turn can make more values useless. */
do
}
*p = &dummy_val;
+ n_useless_values += n_useless_debug_values;
+ n_debug_values -= n_useless_debug_values;
+ n_useless_debug_values = 0;
+
htab_traverse (cselib_hash_table, discard_useless_values, 0);
gcc_assert (!n_useless_values);
never invalidated and preserved across cselib_reset_table calls. */
void
-cselib_preserve_cfa_base_value (cselib_val *v)
+cselib_preserve_cfa_base_value (cselib_val *v, unsigned int regno)
{
if (cselib_preserve_constants
&& v->locs
&& REG_P (v->locs->loc))
- cfa_base_preserved_val = v;
+ {
+ cfa_base_preserved_val = v;
+ cfa_base_preserved_regno = regno;
+ }
}
/* Clean all non-constant expressions in the hash table, but retain
case DEBUG_EXPR:
return 0;
+ case DEBUG_IMPLICIT_PTR:
+ return DEBUG_IMPLICIT_PTR_DECL (x)
+ == DEBUG_IMPLICIT_PTR_DECL (y);
+
case LABEL_REF:
return XEXP (x, 0) == XEXP (y, 0);
+ DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x));
return hash ? hash : (unsigned int) DEBUG_EXPR;
+ case DEBUG_IMPLICIT_PTR:
+ hash += ((unsigned) DEBUG_IMPLICIT_PTR << 7)
+ + DECL_UID (DEBUG_IMPLICIT_PTR_DECL (x));
+ return hash ? hash : (unsigned int) DEBUG_IMPLICIT_PTR;
+
case CONST_INT:
hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
return hash ? hash : (unsigned int) CONST_INT;
for (l = mem_elt->locs; l; l = l->next)
if (MEM_P (l->loc)
&& CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt)
- return;
+ {
+ promote_debug_loc (l);
+ return;
+ }
addr_elt->addr_list = new_elt_list (addr_elt->addr_list, mem_elt);
mem_elt->locs
/* 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)
- return l->elt;
+ {
+ promote_debug_loc (l->elt->locs);
+ return l->elt;
+ }
if (! create)
return 0;
return copy;
}
-/* Log a lookup of X to the cselib table along with the result RET. */
-
-static cselib_val *
-cselib_log_lookup (rtx x, cselib_val *ret)
-{
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fputs ("cselib lookup ", dump_file);
- print_inline_rtx (dump_file, x, 2);
- fprintf (dump_file, " => %u:%u\n",
- ret ? ret->uid : 0,
- ret ? ret->hash : 0);
- }
-
- 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 doesn't have a mode
(i.e. because it's a constant). */
-cselib_val *
-cselib_lookup (rtx x, enum machine_mode mode, int create)
+static cselib_val *
+cselib_lookup_1 (rtx x, enum machine_mode mode, int create)
{
void **slot;
cselib_val *e;
l = l->next;
for (; l; l = l->next)
if (mode == GET_MODE (l->elt->val_rtx))
- return cselib_log_lookup (x, l->elt);
+ {
+ promote_debug_loc (l->elt->locs);
+ return l->elt;
+ }
if (! create)
- return cselib_log_lookup (x, 0);
+ return 0;
if (i < FIRST_PSEUDO_REGISTER)
{
REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
slot = htab_find_slot_with_hash (cselib_hash_table, x, e->hash, INSERT);
*slot = e;
- return cselib_log_lookup (x, e);
+ return e;
}
if (MEM_P (x))
- return cselib_log_lookup (x, cselib_lookup_mem (x, create));
+ return cselib_lookup_mem (x, create);
hashval = cselib_hash_rtx (x, create);
/* Can't even create if hashing is not possible. */
if (! hashval)
- return cselib_log_lookup (x, 0);
+ return 0;
slot = htab_find_slot_with_hash (cselib_hash_table, wrap_constant (mode, x),
hashval, create ? INSERT : NO_INSERT);
if (slot == 0)
- return cselib_log_lookup (x, 0);
+ return 0;
e = (cselib_val *) *slot;
if (e)
- return cselib_log_lookup (x, e);
+ return e;
e = new_cselib_val (hashval, mode, x);
cselib_subst_to_values will need to do lookups. */
*slot = (void *) e;
e->locs = new_elt_loc_list (e->locs, cselib_subst_to_values (x));
- return cselib_log_lookup (x, e);
+ return e;
+}
+
+/* Wrapper for cselib_lookup, that indicates X is in INSN. */
+
+cselib_val *
+cselib_lookup_from_insn (rtx x, enum machine_mode mode,
+ int create, rtx insn)
+{
+ cselib_val *ret;
+
+ gcc_assert (!cselib_current_insn);
+ cselib_current_insn = insn;
+
+ ret = cselib_lookup (x, mode, create);
+
+ cselib_current_insn = NULL;
+
+ return ret;
+}
+
+/* Wrapper for cselib_lookup_1, that logs the lookup result and
+ maintains invariants related with debug insns. */
+
+cselib_val *
+cselib_lookup (rtx x, enum machine_mode mode, int create)
+{
+ cselib_val *ret = cselib_lookup_1 (x, mode, create);
+
+ /* ??? Should we return NULL if we're not to create an entry, the
+ found loc is a debug loc and cselib_current_insn is not DEBUG?
+ If so, we should also avoid converting val to non-DEBUG; probably
+ easiest setting cselib_current_insn to NULL before the call
+ above. */
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fputs ("cselib lookup ", dump_file);
+ print_inline_rtx (dump_file, x, 2);
+ fprintf (dump_file, " => %u:%u\n",
+ ret ? ret->uid : 0,
+ ret ? ret->hash : 0);
+ }
+
+ return ret;
}
/* Invalidate any entries in reg_values that overlap REGNO. This is called
while (*l)
{
cselib_val *v = (*l)->elt;
+ bool had_locs;
+ rtx setting_insn;
struct elt_loc_list **p;
unsigned int this_last = i;
if (i < FIRST_PSEUDO_REGISTER && v != NULL)
this_last = end_hard_regno (GET_MODE (v->val_rtx), i) - 1;
- if (this_last < regno || v == NULL || v == cfa_base_preserved_val)
+ if (this_last < regno || v == NULL
+ || (v == cfa_base_preserved_val
+ && i == cfa_base_preserved_regno))
{
l = &(*l)->next;
continue;
else
unchain_one_elt_list (l);
+ had_locs = v->locs != NULL;
+ setting_insn = v->locs ? v->locs->setting_insn : NULL;
+
/* Now, we clear the mapping from value to reg. It must exist, so
this code will crash intentionally if it doesn't. */
for (p = &v->locs; ; p = &(*p)->next)
break;
}
}
- if (v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
- n_useless_values++;
+
+ if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
+ {
+ if (setting_insn && DEBUG_INSN_P (setting_insn))
+ n_useless_debug_values++;
+ else
+ n_useless_values++;
+ }
}
}
}
{
bool has_mem = false;
struct elt_loc_list **p = &v->locs;
- int had_locs = v->locs != 0;
+ bool had_locs = v->locs != NULL;
+ rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
while (*p)
{
}
if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
- n_useless_values++;
+ {
+ if (setting_insn && DEBUG_INSN_P (setting_insn))
+ n_useless_debug_values++;
+ else
+ n_useless_values++;
+ }
next = v->next_containing_mem;
if (has_mem)
&& MEM_VOLATILE_P (PATTERN (insn))))
{
cselib_reset_table (next_uid);
+ cselib_current_insn = NULL_RTX;
return;
}
if (! INSN_P (insn))
{
- cselib_current_insn = 0;
+ cselib_current_insn = NULL_RTX;
return;
}
if (GET_CODE (XEXP (x, 0)) == CLOBBER)
cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
- cselib_current_insn = 0;
+ cselib_current_insn = NULL_RTX;
if (n_useless_values > MAX_USELESS_VALUES
/* remove_useless_values is linear in the hash table size. Avoid
quadratic behavior for very large hashtables with very few
useless elements. */
- && (unsigned int)n_useless_values > cselib_hash_table->n_elements / 4)
+ && ((unsigned int)n_useless_values
+ > (cselib_hash_table->n_elements
+ - cselib_hash_table->n_deleted
+ - n_debug_values) / 4))
remove_useless_values ();
}
cselib_discard_hook = NULL;
cselib_preserve_constants = false;
cfa_base_preserved_val = NULL;
+ cfa_base_preserved_regno = INVALID_REGNUM;
free_alloc_pool (elt_list_pool);
free_alloc_pool (elt_loc_list_pool);
free_alloc_pool (cselib_val_pool);
used_regs = 0;
cselib_hash_table = 0;
n_useless_values = 0;
+ n_useless_debug_values = 0;
+ n_debug_values = 0;
next_uid = 0;
}