const char *section; /* Section this loclist is relative to */
dw_loc_descr_ref expr;
hashval_t hash;
+ /* True if all addresses in this and subsequent lists are known to be
+ resolved. */
+ bool resolved_addr;
+ /* True if this list has been replaced by dw_loc_next. */
+ bool replaced;
bool emitted;
} dw_loc_list_node;
DIEs. */
static VEC (dw_die_ref, heap) *block_map;
+/* A cached location list. */
+struct GTY (()) cached_dw_loc_list_def {
+ /* The DECL_UID of the decl that this entry describes. */
+ unsigned int decl_id;
+
+ /* The cached location list. */
+ dw_loc_list_ref loc_list;
+};
+typedef struct cached_dw_loc_list_def cached_dw_loc_list;
+
+/* Table of cached location lists. */
+static GTY ((param_is (cached_dw_loc_list))) htab_t cached_dw_loc_list_table;
+
/* A pointer to the base of a list of references to DIE's that
are uniquely identified by their tag, presence/absence of
children DIE's, and list of attribute/value pairs. */
static void insert_double (double_int, unsigned char *);
static void insert_float (const_rtx, unsigned char *);
static rtx rtl_for_decl_location (tree);
-static bool add_location_or_const_value_attribute (dw_die_ref, tree,
+static bool add_location_or_const_value_attribute (dw_die_ref, tree, bool,
enum dwarf_attribute);
static bool tree_add_const_value_attribute (dw_die_ref, tree);
static bool tree_add_const_value_attribute_for_decl (dw_die_ref, tree);
htab_find_with_hash (decl_loc_table, decl, DECL_UID (decl));
}
+/* Returns a hash value for X (which really is a cached_dw_loc_list_list). */
+
+static hashval_t
+cached_dw_loc_list_table_hash (const void *x)
+{
+ return (hashval_t) ((const cached_dw_loc_list *) x)->decl_id;
+}
+
+/* Return nonzero if decl_id of cached_dw_loc_list X is the same as
+ UID of decl *Y. */
+
+static int
+cached_dw_loc_list_table_eq (const void *x, const void *y)
+{
+ return (((const cached_dw_loc_list *) x)->decl_id
+ == DECL_UID ((const_tree) y));
+}
+
/* Equate a DIE to a particular declaration. */
static void
these things can crop up in other ways also.) Note that one type of
constant value which can be passed into an inlined function is a constant
pointer. This can happen for example if an actual argument in an inlined
- function call evaluates to a compile-time constant address. */
+ function call evaluates to a compile-time constant address.
+
+ CACHE_P is true if it is worth caching the location list for DECL,
+ so that future calls can reuse it rather than regenerate it from scratch.
+ This is true for BLOCK_NONLOCALIZED_VARS in inlined subroutines,
+ since we will need to refer to them each time the function is inlined. */
static bool
-add_location_or_const_value_attribute (dw_die_ref die, tree decl,
+add_location_or_const_value_attribute (dw_die_ref die, tree decl, bool cache_p,
enum dwarf_attribute attr)
{
rtx rtl;
dw_loc_list_ref list;
var_loc_list *loc_list;
+ cached_dw_loc_list *cache;
+ void **slot;
if (TREE_CODE (decl) == ERROR_MARK)
return false;
&& add_const_value_attribute (die, rtl))
return true;
}
- list = loc_list_from_tree (decl, decl_by_reference_p (decl) ? 0 : 2);
+ /* If this decl is from BLOCK_NONLOCALIZED_VARS, we might need its
+ list several times. See if we've already cached the contents. */
+ list = NULL;
+ if (loc_list == NULL || cached_dw_loc_list_table == NULL)
+ cache_p = false;
+ if (cache_p)
+ {
+ cache = (cached_dw_loc_list *)
+ htab_find_with_hash (cached_dw_loc_list_table, decl, DECL_UID (decl));
+ if (cache)
+ list = cache->loc_list;
+ }
+ if (list == NULL)
+ {
+ list = loc_list_from_tree (decl, decl_by_reference_p (decl) ? 0 : 2);
+ /* It is usually worth caching this result if the decl is from
+ BLOCK_NONLOCALIZED_VARS and if the list has at least two elements. */
+ if (cache_p && list && list->dw_loc_next)
+ {
+ slot = htab_find_slot_with_hash (cached_dw_loc_list_table, decl,
+ DECL_UID (decl), INSERT);
+ cache = ggc_alloc_cleared_cached_dw_loc_list ();
+ cache->decl_id = DECL_UID (decl);
+ cache->loc_list = list;
+ *slot = cache;
+ }
+ }
if (list)
{
add_AT_location_description (die, attr, list);
equate_decl_number_to_die (node, parm_die);
if (! DECL_ABSTRACT (node_or_origin))
add_location_or_const_value_attribute (parm_die, node_or_origin,
- DW_AT_location);
+ node == NULL, DW_AT_location);
break;
tree context;
int was_abstract;
htab_t old_decl_loc_table;
+ htab_t old_cached_dw_loc_list_table;
int old_call_site_count, old_tail_call_site_count;
struct call_arg_loc_node *old_call_arg_locations;
get locations in abstract instantces. */
old_decl_loc_table = decl_loc_table;
decl_loc_table = NULL;
+ old_cached_dw_loc_list_table = cached_dw_loc_list_table;
+ cached_dw_loc_list_table = NULL;
old_call_arg_locations = call_arg_locations;
call_arg_locations = NULL;
old_call_site_count = call_site_count;
current_function_decl = save_fn;
decl_loc_table = old_decl_loc_table;
+ cached_dw_loc_list_table = old_cached_dw_loc_list_table;
call_arg_locations = old_call_arg_locations;
call_site_count = old_call_site_count;
tail_call_site_count = old_tail_call_site_count;
&& !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl_or_origin)))
defer_location (decl_or_origin, var_die);
else
- add_location_or_const_value_attribute (var_die,
- decl_or_origin,
- DW_AT_location);
+ add_location_or_const_value_attribute (var_die, decl_or_origin,
+ decl == NULL, DW_AT_location);
add_pubname (decl_or_origin, var_die);
}
else
tail_call_site_count = -1;
VEC_free (dw_die_ref, heap, block_map);
htab_empty (decl_loc_table);
+ htab_empty (cached_dw_loc_list_table);
}
/* Output a marker (i.e. a label) for the beginning of the generated code for
decl_loc_table = htab_create_ggc (10, decl_loc_table_hash,
decl_loc_table_eq, NULL);
+ /* Allocate the cached_dw_loc_list_table. */
+ cached_dw_loc_list_table
+ = htab_create_ggc (10, cached_dw_loc_list_table_hash,
+ cached_dw_loc_list_table_eq, NULL);
+
/* Allocate the initial hunk of the decl_scope_table. */
decl_scope_table = VEC_alloc (tree, gc, 256);
{
dw_die_ref c;
dw_attr_ref a;
- dw_loc_list_ref *curr;
+ dw_loc_list_ref *curr, *start, loc;
unsigned ix;
FOR_EACH_VEC_ELT (dw_attr_node, die->die_attr, ix, a)
switch (AT_class (a))
{
case dw_val_class_loc_list:
- curr = AT_loc_list_ptr (a);
- while (*curr)
+ start = curr = AT_loc_list_ptr (a);
+ loc = *curr;
+ gcc_assert (loc);
+ /* The same list can be referenced more than once. See if we have
+ already recorded the result from a previous pass. */
+ if (loc->replaced)
+ *curr = loc->dw_loc_next;
+ else if (!loc->resolved_addr)
{
- if (!resolve_addr_in_expr ((*curr)->expr))
+ /* As things stand, we do not expect or allow one die to
+ reference a suffix of another die's location list chain.
+ References must be identical or completely separate.
+ There is therefore no need to cache the result of this
+ pass on any list other than the first; doing so
+ would lead to unnecessary writes. */
+ while (*curr)
{
- dw_loc_list_ref next = (*curr)->dw_loc_next;
- if (next && (*curr)->ll_symbol)
+ gcc_assert (!(*curr)->replaced && !(*curr)->resolved_addr);
+ if (!resolve_addr_in_expr ((*curr)->expr))
{
- gcc_assert (!next->ll_symbol);
- next->ll_symbol = (*curr)->ll_symbol;
+ dw_loc_list_ref next = (*curr)->dw_loc_next;
+ if (next && (*curr)->ll_symbol)
+ {
+ gcc_assert (!next->ll_symbol);
+ next->ll_symbol = (*curr)->ll_symbol;
+ }
+ *curr = next;
}
- *curr = next;
+ else
+ curr = &(*curr)->dw_loc_next;
}
+ if (loc == *start)
+ loc->resolved_addr = 1;
else
- curr = &(*curr)->dw_loc_next;
+ {
+ loc->replaced = 1;
+ loc->dw_loc_next = *start;
+ }
}
- if (!AT_loc_list (a))
+ if (!*start)
{
remove_AT (die, a->dw_attr);
ix--;
add_location_or_const_value_attribute (
VEC_index (deferred_locations, deferred_locations_list, i)->die,
VEC_index (deferred_locations, deferred_locations_list, i)->variable,
+ false,
DW_AT_location);
}