public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* patch / RFD: speed up linker map file generation
@ 2004-05-05 15:48 Joern Rennecke
  2004-05-17 17:34 ` Nick Clifton
  0 siblings, 1 reply; 12+ messages in thread
From: Joern Rennecke @ 2004-05-05 15:48 UTC (permalink / raw)
  To: binutils; +Cc: joern.rennecke

The linker map file generation has quadratic time complexity, because for
every input section, it goes through the entire hash table to look for
symbols defined in that section.  A customer found that ld takes seven times
as long with map file generation enabled as without.
The problem of finding the list of symbols defined in every section can be
solved in linear time by going through the hash table once and put each
defined symbol into a linked list associated with the section the symbol
is defined in.
To avoid reversing the output, I use a list head / tail setup.
(Although the traversal of a hash table seems arbitrary, it is still done in
 a specific order, and preserving that order allows to use diff in a
 meaningful way.)
These are put as new fields into the user_section_struct.
Initially, I thought I could just map over the input bfds and initialize
all the userdata fields of the input sections, but it turns out this misses
some sections; there are even some where userdata has already been set
but without the map_symbol_def_tail initialization, so presumambly this
must be an output section?

I'm also not quite sure where we stand with memory consumption vs. speed.
The linear time algorithm needs two pointers per hash table entry to
form the linear lists.  The hash table entries appear to be the size of five
pointers, and the overhead with objalloc_alloc / obstacks should be
negligible.  Thus, it appears the memory used by defined symbols will grow
by 40% when creating a map file with the linear time algorithm.
So, should the linear time algotithm be the only one, on the grounds that
quadratic time tendstu hurt you much sooner than the 40% memory increase?
Or should there be an option to use the old algorithm for people who simply
can't create enough swap for the linker to create a map file with the new
algorithm, but are prepared to wait a few hours for the result?
I've put the new code in guarded with if (1) for now so that a flag set
by an option could be used if we want implement such an option.

2004-05-05  J"orn Rennecke <joern.rennecke@superh.com>

	* ld.h (map_symbol_def): New struct.
	(struct user_section_struct): New members map_symbol_def_head
	and map_symbol_def_tail.
	* ldlang.c (map_obstack): New static variable.
	(init_map_userdata, print_all_symbols, sort_def_symbol): New functions.
	(lang_map): Initialize lists of defined symbols for each section.
	(print_input_section): Use print_all_symbols.

diff -pu orig-dir/ld.h ./ld.h
--- orig-dir/ld.h	Wed May  5 13:13:33 2004
+++ ./ld.h	Wed May  5 14:45:02 2004
@@ -78,10 +78,19 @@ struct wildcard_list {
   struct wildcard_spec spec;
 };
 
+struct map_symbol_def {
+  struct bfd_link_hash_entry *entry;
+  struct map_symbol_def *next;
+};
+
 /* Extra information we hold on sections */
 typedef struct user_section_struct {
-  /* Pointer to the section where this data will go */
+  /* For output sections: pointer to the section where this data will go */
   struct lang_input_statement_struct *file;
+  /* For input sections, when writing a map file: head / tail of a linked
+     list of hash table entries for symbols defined in this section.  */
+  struct map_symbol_def *map_symbol_def_head;
+  struct map_symbol_def **map_symbol_def_tail;
 } section_userdata_type;
 
 #define get_userdata(x) ((x)->userdata)
diff -pu orig-dir/ldlang.c ./ldlang.c
--- orig-dir/ldlang.c	Wed May  5 13:13:25 2004
+++ ./ldlang.c	Wed May  5 15:50:30 2004
@@ -46,6 +46,7 @@
 
 /* Locals variables.  */
 static struct obstack stat_obstack;
+static struct obstack map_obstack;
 
 #define obstack_chunk_alloc xmalloc
 #define obstack_chunk_free free
@@ -78,6 +79,8 @@ static void init_os
   PARAMS ((lang_output_section_statement_type *));
 static void exp_init_os
   PARAMS ((etree_type *));
+static void init_map_userdata
+  PARAMS ((bfd *, asection *, PTR));
 static void section_already_linked
   PARAMS ((bfd *, asection *, PTR));
 static struct bfd_hash_entry *already_linked_newfunc
@@ -127,6 +130,10 @@ static void print_input_statement
   PARAMS ((lang_input_statement_type *));
 static bfd_boolean print_one_symbol
   PARAMS ((struct bfd_link_hash_entry *, PTR));
+static void print_all_symbols
+  PARAMS ((asection *));
+static bfd_boolean sort_def_symbol
+  PARAMS ((struct bfd_link_hash_entry *, PTR));
 static void print_input_section
   PARAMS ((lang_input_section_type *));
 static void print_fill_statement
@@ -829,6 +836,7 @@ void
 lang_map ()
 {
   lang_memory_region_type *m;
+  bfd *p;
 
   minfo (_("\nMemory Configuration\n\n"));
   fprintf (config.map_file, "%-16s %-18s %-18s %s\n",
@@ -876,9 +884,59 @@ lang_map ()
 
   fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
 
+  if (1)
+    {
+      obstack_begin (&map_obstack, 1000);
+      for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
+	bfd_map_over_sections (p, init_map_userdata, 0);
+      bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
+    }
   print_statements ();
 }
 
+static void
+init_map_userdata (abfd, sec, data)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *sec;
+     PTR data ATTRIBUTE_UNUSED;
+{
+  section_userdata_type *new_data
+    = ((section_userdata_type *) stat_alloc (sizeof (section_userdata_type)));
+
+  ASSERT (get_userdata (sec) == NULL);
+  get_userdata (sec) = (PTR) new_data;
+  new_data->map_symbol_def_tail = &new_data->map_symbol_def_head;
+}
+
+static bfd_boolean
+sort_def_symbol (hash_entry, info)
+     struct bfd_link_hash_entry *hash_entry;
+     PTR info ATTRIBUTE_UNUSED;
+{
+  if (hash_entry->type == bfd_link_hash_defined
+      || hash_entry->type == bfd_link_hash_defweak)
+    {
+      struct user_section_struct *ud;
+      struct map_symbol_def *def;
+
+      ud = get_userdata (hash_entry->u.def.section);
+      if  (! ud)
+	{
+	  /* ??? What do we have to do to initialize this beforehand?  */
+	  /* 1st we get here is bfd_abs_section... */
+	  init_map_userdata (0, hash_entry->u.def.section, 0);
+	  ud = get_userdata (hash_entry->u.def.section);
+	}
+      else if  (!ud->map_symbol_def_tail)
+	ud->map_symbol_def_tail = &ud->map_symbol_def_head;
+      def = (struct map_symbol_def *) obstack_alloc (&map_obstack, sizeof *def);
+      def->entry = hash_entry;
+      *ud->map_symbol_def_tail = def;
+      ud->map_symbol_def_tail = &def->next;
+    }
+  return TRUE;
+}
+
 /* Initialize an output section.  */
 
 static void
@@ -2357,7 +2415,7 @@ print_input_statement (statm)
 }
 
 /* Print all symbols defined in a particular section.  This is called
-   via bfd_link_hash_traverse.  */
+   via bfd_link_hash_traverse, or by print_all_symbols.  */
 
 static bfd_boolean
 print_one_symbol (hash_entry, ptr)
@@ -2385,6 +2443,18 @@ print_one_symbol (hash_entry, ptr)
   return TRUE;
 }
 
+static void
+print_all_symbols (sec)
+     asection *sec;
+{
+  struct user_section_struct *ud = get_userdata (sec);
+  struct map_symbol_def *def;
+
+  *ud->map_symbol_def_tail = 0;
+  for (def = ud->map_symbol_def_head; def; def = def->next)
+    print_one_symbol (def->entry, (PTR) sec);
+}
+
 /* Print information about an input section to the map file.  */
 
 static void
@@ -2438,7 +2508,10 @@ print_input_section (in)
 	      minfo (_("%W (size before relaxing)\n"), i->_raw_size);
 	    }
 
-	  bfd_link_hash_traverse (link_info.hash, print_one_symbol, (PTR) i);
+	  if (1)
+	    print_all_symbols (i);
+	  else
+	    bfd_link_hash_traverse (link_info.hash, print_one_symbol, (PTR) i);
 
 	  print_dot = i->output_section->vma + i->output_offset + size / opb;
 	}

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2004-06-09 11:39 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-05-05 15:48 patch / RFD: speed up linker map file generation Joern Rennecke
2004-05-17 17:34 ` Nick Clifton
2004-05-18 15:06   ` Joern Rennecke
2004-05-19  9:52     ` Nick Clifton
2004-05-19 12:23       ` Joern Rennecke
2004-05-19 13:59         ` Nick Clifton
2004-06-09  1:59   ` Ben Elliston
2004-06-09  2:25     ` Ian Lance Taylor
2004-06-09  2:47       ` Daniel Jacobowitz
2004-06-09  3:10         ` Ian Lance Taylor
2004-06-09  3:21     ` Hans-Peter Nilsson
2004-06-09 11:39     ` Nick Clifton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).