xtensa: replace action list with splay tree

text_action_add uses linear list search to order text actions list by
action VMA. The list is used at the first relaxation pass, when it's not
fixed yet.
Replace the list with splay tree from libiberty.

Original profile:

% time    self  children    called     name
-----------------------------------------
          0.00    0.00      14/158225      compute_text_actions
          3.62    0.00   25211/158225      remove_dead_literal
          8.42    0.00   58645/158225      coalesce_shared_literal
         10.68    0.00   74355/158225      text_action_add_proposed
  38.8   22.73    0.00  158225         text_action_add
          0.00    0.00  144527/293246      bfd_zmalloc
-----------------------------------------

Same data, after optimization:

% time    self  children    called     name
-----------------------------------------
          0.00    0.00      14/158225      compute_text_actions
          0.00    0.00   25211/158225      remove_dead_literal
          0.00    0.01   58645/158225      coalesce_shared_literal
          0.00    0.01   74355/158225      text_action_add_proposed
   0.1    0.00    0.02  158225         text_action_add
          0.01    0.00  144527/144527      splay_tree_insert
          0.00    0.00  144527/195130      splay_tree_lookup
          0.00    0.00  144527/293246      bfd_zmalloc
-----------------------------------------

2015-04-03  Max Filippov  <jcmvbkbc@gmail.com>
bfd/
	* elf32-xtensa.c (splay-tree.h): include header.
	(text_action_struct): drop next pointer.
	(text_action_list_struct): drop head pointer, add count and
	tree fields.
	(find_fill_action): instead of linear search in text_action_list
	search in the tree.
	(text_action_compare, action_first, action_next): new functions.
	(text_action_add, text_action_add_literal): instead of linear
	search and insertion insert new node into the tree.
	(removed_by_actions): pass additional parameter: action_list,
	use it to traverse the tree.
	(offset_with_removed_text): pass additional action_list parameter
	to removed_by_actions.
	(map_action_fn_context): new typedef.
	(map_action_fn_context_struct): new structure.
	(map_action_fn): new function.
	(map_removal_by_action): use splay_tree_foreach to build map.
	(find_insn_action): replace linear search in text_action_list
	with series of splay_tree_lookups.
	(print_action, print_action_list_fn): new functions.
	(print_action_list): use splay_tree_foreach.
	(init_xtensa_relax_info): drop action_list.head initialization.
	Initialize the tree.
	(compute_text_actions): use non-zero action_list_count instead of
	non-NULL action list.
	(xlate_map_context): new typedef.
	(xlate_map_context_struct): new structure.
	(xlate_map_fn): new function.
	(build_xlate_map): use splay_tree_foreach to build map.
	(action_remove_bytes_fn): new function.
	(relax_section): use zero action_list_count instead of NULL
	action list. Use splay_tree_foreach to count final section size.
	Drop unused variable 'removed'.
This commit is contained in:
Max Filippov 2015-04-05 17:04:22 +03:00
parent 3439c46627
commit 4c2af04fe8

View File

@ -28,6 +28,7 @@
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/xtensa.h"
#include "splay-tree.h"
#include "xtensa-isa.h"
#include "xtensa-config.h"
@ -5416,8 +5417,6 @@ struct text_action_struct
bfd_vma virtual_offset; /* Zero except for adding literals. */
int removed_bytes;
literal_value value; /* Only valid when adding literals. */
text_action *next;
};
struct removal_by_action_entry_struct
@ -5440,7 +5439,8 @@ typedef struct removal_by_action_map_struct removal_by_action_map;
/* List of all of the actions taken on a text section. */
struct text_action_list_struct
{
text_action *head;
unsigned count;
splay_tree tree;
removal_by_action_map map;
};
@ -5448,20 +5448,18 @@ struct text_action_list_struct
static text_action *
find_fill_action (text_action_list *l, asection *sec, bfd_vma offset)
{
text_action **m_p;
text_action a;
/* It is not necessary to fill at the end of a section. */
if (sec->size == offset)
return NULL;
for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
{
text_action *t = *m_p;
/* When the action is another fill at the same address,
just increase the size. */
if (t->offset == offset && t->action == ta_fill)
return t;
}
a.offset = offset;
a.action = ta_fill;
splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a);
if (node)
return (text_action *)node->value;
return NULL;
}
@ -5509,6 +5507,49 @@ adjust_fill_action (text_action *ta, int fill_diff)
}
static int
text_action_compare (splay_tree_key a, splay_tree_key b)
{
text_action *pa = (text_action *)a;
text_action *pb = (text_action *)b;
static const int action_priority[] =
{
[ta_fill] = 0,
[ta_none] = 1,
[ta_convert_longcall] = 2,
[ta_narrow_insn] = 3,
[ta_remove_insn] = 4,
[ta_remove_longcall] = 5,
[ta_remove_literal] = 6,
[ta_widen_insn] = 7,
[ta_add_literal] = 8,
};
if (pa->offset == pb->offset)
{
if (pa->action == pb->action)
return 0;
return action_priority[pa->action] - action_priority[pb->action];
}
else
return pa->offset < pb->offset ? -1 : 1;
}
static text_action *
action_first (text_action_list *action_list)
{
splay_tree_node node = splay_tree_min (action_list->tree);
return node ? (text_action *)node->value : NULL;
}
static text_action *
action_next (text_action_list *action_list, text_action *action)
{
splay_tree_node node = splay_tree_successor (action_list->tree,
(splay_tree_key)action);
return node ? (text_action *)node->value : NULL;
}
/* Add a modification action to the text. For the case of adding or
removing space, modify any current fill and assume that
"unreachable_space" bytes can be freely contracted. Note that a
@ -5521,8 +5562,8 @@ text_action_add (text_action_list *l,
bfd_vma offset,
int removed)
{
text_action **m_p;
text_action *ta;
text_action a;
/* It is not necessary to fill at the end of a section. */
if (action == ta_fill && sec->size == offset)
@ -5532,34 +5573,30 @@ text_action_add (text_action_list *l,
if (action == ta_fill && removed == 0)
return;
for (m_p = &l->head; *m_p && (*m_p)->offset <= offset; m_p = &(*m_p)->next)
{
text_action *t = *m_p;
a.action = action;
a.offset = offset;
if (action == ta_fill)
if (action == ta_fill)
{
splay_tree_node node = splay_tree_lookup (l->tree, (splay_tree_key)&a);
if (node)
{
/* When the action is another fill at the same address,
just increase the size. */
if (t->offset == offset && t->action == ta_fill)
{
t->removed_bytes += removed;
return;
}
/* Fills need to happen before widens so that we don't
insert fill bytes into the instruction stream. */
if (t->offset == offset && t->action == ta_widen_insn)
break;
ta = (text_action *)node->value;
ta->removed_bytes += removed;
return;
}
}
else
BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)&a) == NULL);
/* Create a new record and fill it up. */
ta = (text_action *) bfd_zmalloc (sizeof (text_action));
ta->action = action;
ta->sec = sec;
ta->offset = offset;
ta->removed_bytes = removed;
ta->next = (*m_p);
*m_p = ta;
splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta);
++l->count;
}
@ -5570,7 +5607,6 @@ text_action_add_literal (text_action_list *l,
const literal_value *value,
int removed)
{
text_action **m_p;
text_action *ta;
asection *sec = r_reloc_get_section (loc);
bfd_vma offset = loc->target_offset;
@ -5578,14 +5614,6 @@ text_action_add_literal (text_action_list *l,
BFD_ASSERT (action == ta_add_literal);
for (m_p = &l->head; *m_p != NULL; m_p = &(*m_p)->next)
{
if ((*m_p)->offset > offset
&& ((*m_p)->offset != offset
|| (*m_p)->virtual_offset > virtual_offset))
break;
}
/* Create a new record and fill it up. */
ta = (text_action *) bfd_zmalloc (sizeof (text_action));
ta->action = action;
@ -5594,8 +5622,10 @@ text_action_add_literal (text_action_list *l,
ta->virtual_offset = virtual_offset;
ta->value = *value;
ta->removed_bytes = removed;
ta->next = (*m_p);
*m_p = ta;
BFD_ASSERT (splay_tree_lookup (l->tree, (splay_tree_key)ta) == NULL);
splay_tree_insert (l->tree, (splay_tree_key)ta, (splay_tree_value)ta);
++l->count;
}
@ -5606,7 +5636,8 @@ text_action_add_literal (text_action_list *l,
so that each search may begin where the previous one left off. */
static int
removed_by_actions (text_action **p_start_action,
removed_by_actions (text_action_list *action_list,
text_action **p_start_action,
bfd_vma offset,
bfd_boolean before_fill)
{
@ -5614,6 +5645,13 @@ removed_by_actions (text_action **p_start_action,
int removed = 0;
r = *p_start_action;
if (r)
{
splay_tree_node node = splay_tree_lookup (action_list->tree,
(splay_tree_key)r);
BFD_ASSERT (node != NULL && r == (text_action *)node->value);
}
while (r)
{
if (r->offset > offset)
@ -5625,7 +5663,7 @@ removed_by_actions (text_action **p_start_action,
removed += r->removed_bytes;
r = r->next;
r = action_next (action_list, r);
}
*p_start_action = r;
@ -5636,68 +5674,74 @@ removed_by_actions (text_action **p_start_action,
static bfd_vma
offset_with_removed_text (text_action_list *action_list, bfd_vma offset)
{
text_action *r = action_list->head;
return offset - removed_by_actions (&r, offset, FALSE);
text_action *r = action_first (action_list);
return offset - removed_by_actions (action_list, &r, offset, FALSE);
}
static unsigned
action_list_count (text_action_list *action_list)
{
text_action *r = action_list->head;
unsigned count = 0;
for (r = action_list->head; r != NULL; r = r->next)
return action_list->count;
}
typedef struct map_action_fn_context_struct map_action_fn_context;
struct map_action_fn_context_struct
{
int removed;
removal_by_action_map map;
bfd_boolean eq_complete;
};
static int
map_action_fn (splay_tree_node node, void *p)
{
map_action_fn_context *ctx = p;
text_action *r = (text_action *)node->value;
removal_by_action_entry *ientry = ctx->map.entry + ctx->map.n_entries;
if (ctx->map.n_entries && (ientry - 1)->offset == r->offset)
{
count++;
--ientry;
}
return count;
else
{
++ctx->map.n_entries;
ctx->eq_complete = FALSE;
ientry->offset = r->offset;
ientry->eq_removed_before_fill = ctx->removed;
}
if (!ctx->eq_complete)
{
if (r->action != ta_fill || r->removed_bytes >= 0)
{
ientry->eq_removed = ctx->removed;
ctx->eq_complete = TRUE;
}
else
ientry->eq_removed = ctx->removed + r->removed_bytes;
}
ctx->removed += r->removed_bytes;
ientry->removed = ctx->removed;
return 0;
}
static void
map_removal_by_action (text_action_list *action_list)
{
text_action *r;
int removed = 0;
removal_by_action_map map;
bfd_boolean eq_complete;
map_action_fn_context ctx;
map.n_entries = 0;
map.entry = bfd_malloc (action_list_count (action_list) *
sizeof (removal_by_action_entry));
eq_complete = FALSE;
ctx.removed = 0;
ctx.map.n_entries = 0;
ctx.map.entry = bfd_malloc (action_list_count (action_list) *
sizeof (removal_by_action_entry));
ctx.eq_complete = FALSE;
for (r = action_list->head; r;)
{
removal_by_action_entry *ientry = map.entry + map.n_entries;
if (map.n_entries && (ientry - 1)->offset == r->offset)
{
--ientry;
}
else
{
++map.n_entries;
eq_complete = FALSE;
ientry->offset = r->offset;
ientry->eq_removed_before_fill = removed;
}
if (!eq_complete)
{
if (r->action != ta_fill || r->removed_bytes >= 0)
{
ientry->eq_removed = removed;
eq_complete = TRUE;
}
else
ientry->eq_removed = removed + r->removed_bytes;
}
removed += r->removed_bytes;
ientry->removed = removed;
r = r->next;
}
action_list->map = map;
splay_tree_foreach (action_list->tree, map_action_fn, &ctx);
action_list->map = ctx.map;
}
static int
@ -5754,28 +5798,26 @@ offset_with_removed_text_map (text_action_list *action_list, bfd_vma offset)
static text_action *
find_insn_action (text_action_list *action_list, bfd_vma offset)
{
text_action *t;
for (t = action_list->head; t; t = t->next)
static const text_action_t action[] =
{
if (t->offset == offset)
{
switch (t->action)
{
case ta_none:
case ta_fill:
break;
case ta_remove_insn:
case ta_remove_longcall:
case ta_convert_longcall:
case ta_narrow_insn:
case ta_widen_insn:
return t;
case ta_remove_literal:
case ta_add_literal:
BFD_ASSERT (0);
break;
}
}
ta_convert_longcall,
ta_remove_longcall,
ta_widen_insn,
ta_narrow_insn,
ta_remove_insn,
};
text_action a;
unsigned i;
a.offset = offset;
for (i = 0; i < sizeof (action) / sizeof (*action); ++i)
{
splay_tree_node node;
a.action = action[i];
node = splay_tree_lookup (action_list->tree, (splay_tree_key)&a);
if (node)
return (text_action *)node->value;
}
return NULL;
}
@ -5783,41 +5825,51 @@ find_insn_action (text_action_list *action_list, bfd_vma offset)
#if DEBUG
static void
print_action (FILE *fp, text_action *r)
{
const char *t = "unknown";
switch (r->action)
{
case ta_remove_insn:
t = "remove_insn"; break;
case ta_remove_longcall:
t = "remove_longcall"; break;
case ta_convert_longcall:
t = "convert_longcall"; break;
case ta_narrow_insn:
t = "narrow_insn"; break;
case ta_widen_insn:
t = "widen_insn"; break;
case ta_fill:
t = "fill"; break;
case ta_none:
t = "none"; break;
case ta_remove_literal:
t = "remove_literal"; break;
case ta_add_literal:
t = "add_literal"; break;
}
fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
r->sec->owner->filename,
r->sec->name, (unsigned long) r->offset, t, r->removed_bytes);
}
static int
print_action_list_fn (splay_tree_node node, void *p)
{
text_action *r = (text_action *)node->value;
print_action (p, r);
return 0;
}
static void
print_action_list (FILE *fp, text_action_list *action_list)
{
text_action *r;
fprintf (fp, "Text Action\n");
for (r = action_list->head; r != NULL; r = r->next)
{
const char *t = "unknown";
switch (r->action)
{
case ta_remove_insn:
t = "remove_insn"; break;
case ta_remove_longcall:
t = "remove_longcall"; break;
case ta_convert_longcall:
t = "convert_longcall"; break;
case ta_narrow_insn:
t = "narrow_insn"; break;
case ta_widen_insn:
t = "widen_insn"; break;
case ta_fill:
t = "fill"; break;
case ta_none:
t = "none"; break;
case ta_remove_literal:
t = "remove_literal"; break;
case ta_add_literal:
t = "add_literal"; break;
}
fprintf (fp, "%s: %s[0x%lx] \"%s\" %d\n",
r->sec->owner->filename,
r->sec->name, (unsigned long) r->offset, t, r->removed_bytes);
}
splay_tree_foreach (action_list->tree, print_action_list_fn, fp);
}
#endif /* DEBUG */
@ -6071,8 +6123,8 @@ init_xtensa_relax_info (asection *sec)
relax_info->removed_list.head = NULL;
relax_info->removed_list.tail = NULL;
relax_info->action_list.head = NULL;
relax_info->action_list.tree = splay_tree_new (text_action_compare,
NULL, NULL);
relax_info->action_list.map.n_entries = 0;
relax_info->action_list.map.entry = NULL;
@ -7762,7 +7814,7 @@ compute_text_actions (bfd *abfd,
free_reloc_range_list (&relevant_relocs);
#if DEBUG
if (relax_info->action_list.head)
if (action_list_count (&relax_info->action_list))
print_action_list (stderr, &relax_info->action_list);
#endif
@ -8263,6 +8315,54 @@ xlate_offset_with_removed_text (const xlate_map_t *map,
return e->new_address - e->orig_address + offset;
}
typedef struct xlate_map_context_struct xlate_map_context;
struct xlate_map_context_struct
{
xlate_map_t *map;
xlate_map_entry_t *current_entry;
int removed;
};
static int
xlate_map_fn (splay_tree_node node, void *p)
{
text_action *r = (text_action *)node->value;
xlate_map_context *ctx = p;
unsigned orig_size = 0;
switch (r->action)
{
case ta_none:
case ta_remove_insn:
case ta_convert_longcall:
case ta_remove_literal:
case ta_add_literal:
break;
case ta_remove_longcall:
orig_size = 6;
break;
case ta_narrow_insn:
orig_size = 3;
break;
case ta_widen_insn:
orig_size = 2;
break;
case ta_fill:
break;
}
ctx->current_entry->size =
r->offset + orig_size - ctx->current_entry->orig_address;
if (ctx->current_entry->size != 0)
{
ctx->current_entry++;
ctx->map->entry_count++;
}
ctx->current_entry->orig_address = r->offset + orig_size;
ctx->removed += r->removed_bytes;
ctx->current_entry->new_address = r->offset + orig_size - ctx->removed;
ctx->current_entry->size = 0;
return 0;
}
/* Build a binary searchable offset translation map from a section's
action list. */
@ -8270,75 +8370,40 @@ xlate_offset_with_removed_text (const xlate_map_t *map,
static xlate_map_t *
build_xlate_map (asection *sec, xtensa_relax_info *relax_info)
{
xlate_map_t *map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t));
text_action_list *action_list = &relax_info->action_list;
unsigned num_actions = 0;
text_action *r;
int removed;
xlate_map_entry_t *current_entry;
xlate_map_context ctx;
if (map == NULL)
ctx.map = (xlate_map_t *) bfd_malloc (sizeof (xlate_map_t));
if (ctx.map == NULL)
return NULL;
num_actions = action_list_count (action_list);
map->entry = (xlate_map_entry_t *)
ctx.map->entry = (xlate_map_entry_t *)
bfd_malloc (sizeof (xlate_map_entry_t) * (num_actions + 1));
if (map->entry == NULL)
if (ctx.map->entry == NULL)
{
free (map);
free (ctx.map);
return NULL;
}
map->entry_count = 0;
ctx.map->entry_count = 0;
removed = 0;
current_entry = &map->entry[0];
ctx.removed = 0;
ctx.current_entry = &ctx.map->entry[0];
current_entry->orig_address = 0;
current_entry->new_address = 0;
current_entry->size = 0;
ctx.current_entry->orig_address = 0;
ctx.current_entry->new_address = 0;
ctx.current_entry->size = 0;
for (r = action_list->head; r != NULL; r = r->next)
{
unsigned orig_size = 0;
switch (r->action)
{
case ta_none:
case ta_remove_insn:
case ta_convert_longcall:
case ta_remove_literal:
case ta_add_literal:
break;
case ta_remove_longcall:
orig_size = 6;
break;
case ta_narrow_insn:
orig_size = 3;
break;
case ta_widen_insn:
orig_size = 2;
break;
case ta_fill:
break;
}
current_entry->size =
r->offset + orig_size - current_entry->orig_address;
if (current_entry->size != 0)
{
current_entry++;
map->entry_count++;
}
current_entry->orig_address = r->offset + orig_size;
removed += r->removed_bytes;
current_entry->new_address = r->offset + orig_size - removed;
current_entry->size = 0;
}
splay_tree_foreach (action_list->tree, xlate_map_fn, &ctx);
current_entry->size = (bfd_get_section_limit (sec->owner, sec)
- current_entry->orig_address);
if (current_entry->size != 0)
map->entry_count++;
ctx.current_entry->size = (bfd_get_section_limit (sec->owner, sec)
- ctx.current_entry->orig_address);
if (ctx.current_entry->size != 0)
ctx.map->entry_count++;
return map;
return ctx.map;
}
@ -9302,6 +9367,16 @@ move_shared_literal (asection *sec,
/* Second relaxation pass. */
static int
action_remove_bytes_fn (splay_tree_node node, void *p)
{
bfd_size_type *final_size = p;
text_action *action = (text_action *)node->value;
*final_size -= action->removed_bytes;
return 0;
}
/* Modify all of the relocations to point to the right spot, and if this
is a relaxable section, delete the unwanted literals and fix the
section size. */
@ -9334,7 +9409,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
internal_relocs = retrieve_internal_relocs (abfd, sec,
link_info->keep_memory);
if (!internal_relocs && !relax_info->action_list.head)
if (!internal_relocs && !action_list_count (&relax_info->action_list))
return TRUE;
contents = retrieve_contents (abfd, sec, link_info->keep_memory);
@ -9412,6 +9487,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
}
/* Update the action so that the code that moves
the contents will do the right thing. */
/* ta_remove_longcall and ta_remove_insn actions are
grouped together in the tree as well as
ta_convert_longcall and ta_none, so that changes below
can be done w/o removing and reinserting action into
the tree. */
if (action->action == ta_remove_longcall)
action->action = ta_remove_insn;
else
@ -9584,13 +9665,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
if ((relax_info->is_relaxable_literal_section
|| relax_info->is_relaxable_asm_section)
&& relax_info->action_list.head)
&& action_list_count (&relax_info->action_list))
{
/* Walk through the planned actions and build up a table
of move, copy and fill records. Use the move, copy and
fill records to perform the actions once. */
int removed = 0;
bfd_size_type final_size, copy_size, orig_insn_size;
bfd_byte *scratch = NULL;
bfd_byte *dup_contents = NULL;
@ -9601,15 +9681,12 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
bfd_vma orig_dot_vo = 0; /* Virtual offset from orig_dot. */
bfd_vma dup_dot = 0;
text_action *action = relax_info->action_list.head;
text_action *action;
final_size = sec->size;
for (action = relax_info->action_list.head; action;
action = action->next)
{
final_size -= action->removed_bytes;
}
splay_tree_foreach (relax_info->action_list.tree,
action_remove_bytes_fn, &final_size);
scratch = (bfd_byte *) bfd_zmalloc (final_size);
dup_contents = (bfd_byte *) bfd_zmalloc (final_size);
@ -9618,8 +9695,8 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
print_action_list (stderr, &relax_info->action_list);
#endif
for (action = relax_info->action_list.head; action;
action = action->next)
for (action = action_first (&relax_info->action_list); action;
action = action_next (&relax_info->action_list, action))
{
virtual_action = FALSE;
if (action->offset > orig_dot)
@ -9748,7 +9825,6 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
break;
}
removed += action->removed_bytes;
BFD_ASSERT (dup_dot <= final_size);
BFD_ASSERT (orig_dot <= orig_size);
}