c6d9a92dbd
This series optimizes most time-consuming algorithms and data structures in the xtensa link-time relaxation code, leaving relaxation logic intact. Speedup linking typical linux kernel is ~8 times (1 minute instead of 8), pathological cases (linking objects partially linked without relaxation) are handled ~60 times faster (1 minute instead of an hour). Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
357 lines
11 KiB
Diff
357 lines
11 KiB
Diff
From 3e3f60207399ab29dd55af109e5ae9facc7d8e83 Mon Sep 17 00:00:00 2001
|
|
From: Max Filippov <jcmvbkbc@gmail.com>
|
|
Date: Sat, 28 Mar 2015 08:46:28 +0300
|
|
Subject: [PATCH 2/4] xtensa: optimize removed_by_actions
|
|
|
|
The function removed_by_actions iterates through text actions to
|
|
calculate an offset applied by text actions to a given VMA. Although it
|
|
has a parameter p_start_action that allows for incremental offset
|
|
calculation, in many places it's used with p_start_action explicitly set
|
|
to the first action. After the first relaxation pass when the list of
|
|
text actions is finalized, an array of offsets sorted by VMA may be used
|
|
to speed up this function.
|
|
|
|
Original profile:
|
|
|
|
% time self children called name
|
|
-----------------------------------------
|
|
0.35 0.00 33872/4808961 relax_section_symbols
|
|
3.32 0.00 326022/4808961 relax_property_section
|
|
12.83 0.00 1259379/4808961 offset_with_removed_text
|
|
32.50 0.00 3189688/4808961 translate_reloc
|
|
71.5 49.00 0.00 4808961 removed_by_actions
|
|
-----------------------------------------
|
|
|
|
Same data, after optimization:
|
|
|
|
% time self children called name
|
|
-----------------------------------------
|
|
0.00 0.00 33872/4808537 relax_section_symbols
|
|
0.01 0.00 326022/4808537 relax_property_section
|
|
0.05 0.00 1258955/4808537 offset_with_removed_text_map
|
|
0.13 0.00 3189688/4808537 translate_reloc
|
|
1.0 0.20 0.00 4808537 removed_by_actions_map
|
|
0.00 0.00 120/120 map_removal_by_action
|
|
-----------------------------------------
|
|
|
|
2015-04-01 Max Filippov <jcmvbkbc@gmail.com>
|
|
bfd/
|
|
* elf32-xtensa.c (removal_by_action_entry_struct,
|
|
removal_by_action_map_struct): new structures.
|
|
(removal_by_action_entry, removal_by_action_map): new typedefs.
|
|
(text_action_list_struct): add new field: map.
|
|
(map_removal_by_action, removed_by_actions_map,
|
|
offset_with_removed_text_map): new functions.
|
|
(relax_section): replace offset_with_removed_text with
|
|
offset_with_removed_text_map.
|
|
(translate_reloc, relax_property_section, relax_section_symbols):
|
|
replace removed_by_actions with removed_by_actions_map.
|
|
|
|
Backported from: 071aa5c98a31c966f5fbfc573fcee61350fd1936
|
|
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
|
|
---
|
|
bfd/elf32-xtensa.c | 181 +++++++++++++++++++++++++++++++++++++++++++++--------
|
|
1 file changed, 156 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c
|
|
index 872370b..21b2871 100644
|
|
--- a/bfd/elf32-xtensa.c
|
|
+++ b/bfd/elf32-xtensa.c
|
|
@@ -5420,11 +5420,28 @@ struct text_action_struct
|
|
text_action *next;
|
|
};
|
|
|
|
+struct removal_by_action_entry_struct
|
|
+{
|
|
+ bfd_vma offset;
|
|
+ int removed;
|
|
+ int eq_removed;
|
|
+ int eq_removed_before_fill;
|
|
+};
|
|
+typedef struct removal_by_action_entry_struct removal_by_action_entry;
|
|
+
|
|
+struct removal_by_action_map_struct
|
|
+{
|
|
+ unsigned n_entries;
|
|
+ removal_by_action_entry *entry;
|
|
+};
|
|
+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;
|
|
+ removal_by_action_map map;
|
|
};
|
|
|
|
|
|
@@ -5636,6 +5653,101 @@ action_list_count (text_action_list *action_list)
|
|
return count;
|
|
}
|
|
|
|
+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.n_entries = 0;
|
|
+ map.entry = bfd_malloc (action_list_count (action_list) *
|
|
+ sizeof (removal_by_action_entry));
|
|
+ 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;
|
|
+}
|
|
+
|
|
+static int
|
|
+removed_by_actions_map (text_action_list *action_list, bfd_vma offset,
|
|
+ bfd_boolean before_fill)
|
|
+{
|
|
+ unsigned a, b;
|
|
+
|
|
+ if (!action_list->map.entry)
|
|
+ map_removal_by_action (action_list);
|
|
+
|
|
+ if (!action_list->map.n_entries)
|
|
+ return 0;
|
|
+
|
|
+ a = 0;
|
|
+ b = action_list->map.n_entries;
|
|
+
|
|
+ while (b - a > 1)
|
|
+ {
|
|
+ unsigned c = (a + b) / 2;
|
|
+
|
|
+ if (action_list->map.entry[c].offset <= offset)
|
|
+ a = c;
|
|
+ else
|
|
+ b = c;
|
|
+ }
|
|
+
|
|
+ if (action_list->map.entry[a].offset < offset)
|
|
+ {
|
|
+ return action_list->map.entry[a].removed;
|
|
+ }
|
|
+ else if (action_list->map.entry[a].offset == offset)
|
|
+ {
|
|
+ return before_fill ?
|
|
+ action_list->map.entry[a].eq_removed_before_fill :
|
|
+ action_list->map.entry[a].eq_removed;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bfd_vma
|
|
+offset_with_removed_text_map (text_action_list *action_list, bfd_vma offset)
|
|
+{
|
|
+ int removed = removed_by_actions_map (action_list, offset, FALSE);
|
|
+ return offset - removed;
|
|
+}
|
|
+
|
|
|
|
/* The find_insn_action routine will only find non-fill actions. */
|
|
|
|
@@ -5909,6 +6021,9 @@ init_xtensa_relax_info (asection *sec)
|
|
|
|
relax_info->action_list.head = NULL;
|
|
|
|
+ relax_info->action_list.map.n_entries = 0;
|
|
+ relax_info->action_list.map.entry = NULL;
|
|
+
|
|
relax_info->fix_list = NULL;
|
|
relax_info->fix_array = NULL;
|
|
relax_info->fix_array_count = 0;
|
|
@@ -9218,7 +9333,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
|
|
if (elf_hash_table (link_info)->dynamic_sections_created)
|
|
shrink_dynamic_reloc_sections (link_info, abfd, sec, irel);
|
|
irel->r_info = ELF32_R_INFO (0, R_XTENSA_NONE);
|
|
- irel->r_offset = offset_with_removed_text
|
|
+ irel->r_offset = offset_with_removed_text_map
|
|
(&relax_info->action_list, irel->r_offset);
|
|
continue;
|
|
}
|
|
@@ -9255,7 +9370,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
|
|
}
|
|
}
|
|
|
|
- source_offset = offset_with_removed_text
|
|
+ source_offset = offset_with_removed_text_map
|
|
(&relax_info->action_list, irel->r_offset);
|
|
irel->r_offset = source_offset;
|
|
}
|
|
@@ -9352,7 +9467,7 @@ relax_section (bfd *abfd, asection *sec, struct bfd_link_info *link_info)
|
|
break;
|
|
}
|
|
|
|
- new_end_offset = offset_with_removed_text
|
|
+ new_end_offset = offset_with_removed_text_map
|
|
(&target_relax_info->action_list,
|
|
r_rel.target_offset + diff_value);
|
|
diff_value = new_end_offset - new_reloc.target_offset;
|
|
@@ -9750,7 +9865,6 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
|
|
xtensa_relax_info *relax_info;
|
|
removed_literal *removed;
|
|
bfd_vma target_offset, base_offset;
|
|
- text_action *act;
|
|
|
|
*new_rel = *orig_rel;
|
|
|
|
@@ -9803,19 +9917,26 @@ translate_reloc (const r_reloc *orig_rel, r_reloc *new_rel, asection *sec)
|
|
offset. */
|
|
|
|
base_offset = r_reloc_get_target_offset (new_rel) - new_rel->rela.r_addend;
|
|
- act = relax_info->action_list.head;
|
|
if (base_offset <= target_offset)
|
|
{
|
|
- int base_removed = removed_by_actions (&act, base_offset, FALSE);
|
|
- int addend_removed = removed_by_actions (&act, target_offset, FALSE);
|
|
+ int base_removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ base_offset, FALSE);
|
|
+ int addend_removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ target_offset, FALSE) -
|
|
+ base_removed;
|
|
+
|
|
new_rel->target_offset = target_offset - base_removed - addend_removed;
|
|
new_rel->rela.r_addend -= addend_removed;
|
|
}
|
|
else
|
|
{
|
|
/* Handle a negative addend. The base offset comes first. */
|
|
- int tgt_removed = removed_by_actions (&act, target_offset, FALSE);
|
|
- int addend_removed = removed_by_actions (&act, base_offset, FALSE);
|
|
+ int tgt_removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ target_offset, FALSE);
|
|
+ int addend_removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ base_offset, FALSE) -
|
|
+ tgt_removed;
|
|
+
|
|
new_rel->target_offset = target_offset - tgt_removed;
|
|
new_rel->rela.r_addend += addend_removed;
|
|
}
|
|
@@ -10138,9 +10259,10 @@ relax_property_section (bfd *abfd,
|
|
bfd_vma old_offset = val.r_rel.target_offset;
|
|
bfd_vma new_offset;
|
|
long old_size, new_size;
|
|
- text_action *act = target_relax_info->action_list.head;
|
|
- new_offset = old_offset -
|
|
- removed_by_actions (&act, old_offset, FALSE);
|
|
+ int removed_by_old_offset =
|
|
+ removed_by_actions_map (&target_relax_info->action_list,
|
|
+ old_offset, FALSE);
|
|
+ new_offset = old_offset - removed_by_old_offset;
|
|
|
|
/* Assert that we are not out of bounds. */
|
|
old_size = bfd_get_32 (abfd, size_p);
|
|
@@ -10164,9 +10286,10 @@ relax_property_section (bfd *abfd,
|
|
|
|
/* Recompute the new_offset, but this time don't
|
|
include any fill inserted by relaxation. */
|
|
- act = target_relax_info->action_list.head;
|
|
- new_offset = old_offset -
|
|
- removed_by_actions (&act, old_offset, TRUE);
|
|
+ removed_by_old_offset =
|
|
+ removed_by_actions_map (&target_relax_info->action_list,
|
|
+ old_offset, TRUE);
|
|
+ new_offset = old_offset - removed_by_old_offset;
|
|
|
|
/* If it is not unreachable and we have not yet
|
|
seen an unreachable at this address, place it
|
|
@@ -10182,8 +10305,12 @@ relax_property_section (bfd *abfd,
|
|
}
|
|
}
|
|
else
|
|
- new_size -=
|
|
- removed_by_actions (&act, old_offset + old_size, TRUE);
|
|
+ {
|
|
+ int removed_by_old_offset_size =
|
|
+ removed_by_actions_map (&target_relax_info->action_list,
|
|
+ old_offset + old_size, TRUE);
|
|
+ new_size -= removed_by_old_offset_size - removed_by_old_offset;
|
|
+ }
|
|
|
|
if (new_size != old_size)
|
|
{
|
|
@@ -10441,14 +10568,16 @@ relax_section_symbols (bfd *abfd, asection *sec)
|
|
|
|
if (isym->st_shndx == sec_shndx)
|
|
{
|
|
- text_action *act = relax_info->action_list.head;
|
|
bfd_vma orig_addr = isym->st_value;
|
|
+ int removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ orig_addr, FALSE);
|
|
|
|
- isym->st_value -= removed_by_actions (&act, orig_addr, FALSE);
|
|
-
|
|
+ isym->st_value -= removed;
|
|
if (ELF32_ST_TYPE (isym->st_info) == STT_FUNC)
|
|
isym->st_size -=
|
|
- removed_by_actions (&act, orig_addr + isym->st_size, FALSE);
|
|
+ removed_by_actions_map (&relax_info->action_list,
|
|
+ orig_addr + isym->st_size, FALSE) -
|
|
+ removed;
|
|
}
|
|
}
|
|
|
|
@@ -10466,15 +10595,17 @@ relax_section_symbols (bfd *abfd, asection *sec)
|
|
|| sym_hash->root.type == bfd_link_hash_defweak)
|
|
&& sym_hash->root.u.def.section == sec)
|
|
{
|
|
- text_action *act = relax_info->action_list.head;
|
|
bfd_vma orig_addr = sym_hash->root.u.def.value;
|
|
+ int removed = removed_by_actions_map (&relax_info->action_list,
|
|
+ orig_addr, FALSE);
|
|
|
|
- sym_hash->root.u.def.value -=
|
|
- removed_by_actions (&act, orig_addr, FALSE);
|
|
+ sym_hash->root.u.def.value -= removed;
|
|
|
|
if (sym_hash->type == STT_FUNC)
|
|
sym_hash->size -=
|
|
- removed_by_actions (&act, orig_addr + sym_hash->size, FALSE);
|
|
+ removed_by_actions_map (&relax_info->action_list,
|
|
+ orig_addr + sym_hash->size, FALSE) -
|
|
+ removed;
|
|
}
|
|
}
|
|
|
|
--
|
|
1.8.1.4
|
|
|