diff -p -u bfd-old/ChangeLog bfd-new/ChangeLog --- bfd-old/ChangeLog Mon Sep 8 18:49:37 2003 +++ bfd-new/ChangeLog Mon Sep 8 18:48:10 2003 @@ -1,3 +1,14 @@ +2003-09-08 Yuri Victorovich + + * elf-strtab.c: Changed mechanics of string table comparison. + Removed hashtable, made it splay-tree based. + Reason for the change: performance of linker was degrading + rapidly with the size of the project on C++ project with many + templates. + Since hash was based on last four characters only and mangled + names all ended similarly almost all names were in collision. + * merge.c: similar changes + 2003-09-08 Joel Brobecker * archures.c: Add new machine names for hppa. diff -p -u bfd-old/elf-strtab.c bfd-new/elf-strtab.c --- bfd-old/elf-strtab.c Mon Sep 8 18:49:37 2003 +++ bfd-new/elf-strtab.c Mon Sep 8 17:49:55 2003 @@ -22,7 +22,7 @@ #include "sysdep.h" #include "libbfd.h" #include "elf-bfd.h" -#include "hashtab.h" +#include "splay-tree.h" #include "libiberty.h" /* An entry in the strtab hash table. */ @@ -253,7 +253,30 @@ _bfd_elf_strtab_emit (register bfd *abfd return TRUE; } -/* Compare two elf_strtab_hash_entry structures. This is called via qsort. */ +/* + * Compare two elf_strtab_hash_entry structures. + * This is called via qsort and splay tree. + */ + +inline static int +rmemcmp(const char *s1, const char *s2, int len) { +/* + * improvement proposition is submitted to GCC to add similar + * functionality into GCC-builtin functions. + */ + s1 += len; + s2 += len; + while (len-- > 0) { + char c1 = *--s1; + char c2 = *--s2; + if (c1 < c2) + return (-1); + if (c1 > c2) + return (+1); + } + + return (0); +} static int cmplengthentry (const void *a, const void *b) @@ -270,25 +293,20 @@ cmplengthentry (const void *a, const voi } static int -last4_eq (const void *a, const void *b) +cmpsuffixentry (const void *a, const void *b) { - const struct elf_strtab_hash_entry *A = a; - const struct elf_strtab_hash_entry *B = b; - - if (memcmp (A->root.string + A->len - 5, B->root.string + B->len - 5, 4) - != 0) - /* This was a hashtable collision. */ - return 0; + struct elf_strtab_hash_entry *A = (struct elf_strtab_hash_entry *) a; + struct elf_strtab_hash_entry *B = (struct elf_strtab_hash_entry *) b; - if (A->len <= B->len) - /* B cannot be a suffix of A unless A is equal to B, which is guaranteed - not to be equal by the hash table. */ - return 0; - - return memcmp (A->root.string + (A->len - B->len), - B->root.string, B->len - 5) == 0; + if (A->len > B->len) + return rmemcmp(A->root.string + (A->len - B->len), B->root.string, B->len); + else if (A->len < B->len) /*should never happen due to sortedness*/ + return rmemcmp(A->root.string, B->root.string + (B->len - A->len), A->len); + else + return rmemcmp(A->root.string, B->root.string, A->len); } + /* This function assigns final string table offsets for used strings, merging strings matching suffixes of longer strings if possible. */ @@ -296,9 +314,8 @@ void _bfd_elf_strtab_finalize (struct elf_strtab_hash *tab) { struct elf_strtab_hash_entry **array, **a, **end, *e; - htab_t last4tab = NULL; + splay_tree tree = NULL; bfd_size_type size, amt; - struct elf_strtab_hash_entry *last[256], **last_ptr[256]; /* GCC 2.91.66 (egcs-1.1.2) on i386 miscompiles this function when i is a 64-bit bfd_size_type: a 64-bit target or --enable-64-bit-bfd. @@ -307,15 +324,11 @@ _bfd_elf_strtab_finalize (struct elf_str size_t i; /* Now sort the strings by length, longest first. */ - array = NULL; amt = tab->size * sizeof (struct elf_strtab_hash_entry *); array = bfd_malloc (amt); if (array == NULL) goto alloc_failure; - memset (last, 0, sizeof (last)); - for (i = 0; i < 256; ++i) - last_ptr[i] = &last[i]; for (i = 1, a = array; i < tab->size; ++i) if (tab->array[i]->refcount) *a++ = tab->array[i]; @@ -326,78 +339,40 @@ _bfd_elf_strtab_finalize (struct elf_str qsort (array, size, sizeof (struct elf_strtab_hash_entry *), cmplengthentry); - last4tab = htab_create_alloc (size * 4, NULL, last4_eq, NULL, calloc, free); - if (last4tab == NULL) + /* Create tree */ + tree = splay_tree_new ((splay_tree_compare_fn)cmpsuffixentry, 0, 0); + if (tree == NULL) goto alloc_failure; - /* Now insert the strings into hash tables (strings with last 4 characters - and strings with last character equal), look for longer strings which - we're suffix of. */ + /* Insert strings into splay tree. Every collision means suffix. */ for (a = array, end = array + size; a < end; a++) { - register hashval_t hash; - unsigned int c; - unsigned int j; - const unsigned char *s; - void **p; + register splay_tree_node n; e = *a; - if (e->len > 4) - { - s = e->root.string + e->len - 1; - hash = 0; - for (j = 0; j < 4; j++) - { - c = *--s; - hash += c + (c << 17); - hash ^= hash >> 2; - } - p = htab_find_slot_with_hash (last4tab, e, hash, INSERT); - if (p == NULL) - goto alloc_failure; - if (*p) - { - struct elf_strtab_hash_entry *ent; - - ent = *p; - e->u.suffix = ent; - e->len = 0; - continue; - } - else - *p = e; - } - else - { - struct elf_strtab_hash_entry *tem; - - c = e->root.string[e->len - 2] & 0xff; - - for (tem = last[c]; tem; tem = tem->u.next) - if (tem->len > e->len - && memcmp (tem->root.string + (tem->len - e->len), - e->root.string, e->len - 1) == 0) - break; - if (tem) - { - e->u.suffix = tem; - e->len = 0; - continue; - } - } - c = e->root.string[e->len - 2] & 0xff; - /* Put longest strings first. */ - *last_ptr[c] = e; - last_ptr[c] = &e->u.next; - e->u.next = NULL; + n = splay_tree_insert (tree, (splay_tree_key)e, (splay_tree_value)e); + if (n == NULL) + goto alloc_failure; + if (n->key != (splay_tree_key)e) + { /* collision just occured and we have suffix of another one */ + register struct elf_strtab_hash_entry *ent; + ent = (struct elf_strtab_hash_entry *)n->key; + + n->value = (splay_tree_value)ent; /* back to what it was */ + + e->u.suffix = ent; + e->len = 0; + } } alloc_failure: if (array) free (array); - if (last4tab) - htab_delete (last4tab); + if (tree) + splay_tree_delete (tree); + /* XXX: In case allocation failure occured we just output "wrong" table ? + * It's a good idea to pass this out as an error. */ /* Now assign positions to the strings we want to keep. */ size = 1; diff -p -u bfd-old/merge.c bfd-new/merge.c --- bfd-old/merge.c Mon Sep 8 18:49:37 2003 +++ bfd-new/merge.c Mon Sep 8 17:43:33 2003 @@ -24,7 +24,7 @@ #include "bfd.h" #include "sysdep.h" #include "libbfd.h" -#include "hashtab.h" +#include "splay-tree.h" #include "libiberty.h" struct sec_merge_sec_info; @@ -420,7 +420,30 @@ _bfd_merge_section (bfd *abfd, void **ps return FALSE; } -/* Compare two sec_merge_hash_entry structures. This is called via qsort. */ +/* + * Compare two sec_merge_hash_entry structures. + * This is called via qsort and splay tree. + */ + +inline static int +rmemcmp(const char *s1, const char *s2, int len) { +/* + * improvement proposition is submitted to GCC to add similar + * functionality into GCC-builtin functions. + */ + s1 += len; + s2 += len; + while (len-- > 0) { + char c1 = *--s1; + char c2 = *--s2; + if (c1 < c2) + return (-1); + if (c1 > c2) + return (+1); + } + + return (0); +} static int cmplengthentry (const void *a, const void *b) @@ -437,60 +460,31 @@ cmplengthentry (const void *a, const voi } static int -last4_eq (const void *a, const void *b) +cmpsuffixentry (const void *a, const void *b) { struct sec_merge_hash_entry * A = (struct sec_merge_hash_entry *) a; struct sec_merge_hash_entry * B = (struct sec_merge_hash_entry *) b; - if (memcmp (A->root.string + A->len - 5 * A->u.entsize, - B->root.string + B->len - 5 * A->u.entsize, - 4 * A->u.entsize) != 0) - /* This was a hashtable collision. */ - return 0; - - if (A->len <= B->len) - /* B cannot be a suffix of A unless A is equal to B, which is guaranteed - not to be equal by the hash table. */ - return 0; - - if (A->alignment < B->alignment - || ((A->len - B->len) & (B->alignment - 1))) - /* The suffix is not sufficiently aligned. */ - return 0; - - return memcmp (A->root.string + (A->len - B->len), - B->root.string, B->len - 5 * A->u.entsize) == 0; -} + register int res; -static int -last_eq (const void *a, const void *b) -{ - struct sec_merge_hash_entry * A = (struct sec_merge_hash_entry *) a; - struct sec_merge_hash_entry * B = (struct sec_merge_hash_entry *) b; + if (A->len > B->len) + res = rmemcmp(A->root.string + (A->len - B->len), B->root.string, B->len); + else if (A->len < B->len) /*should never happen due to sortedness*/ + res = rmemcmp(A->root.string, B->root.string + (B->len - A->len), A->len); + else + res = rmemcmp(A->root.string, B->root.string, A->len); - if (B->len >= 5 * A->u.entsize) - /* Longer strings are just pushed into the hash table, - they'll be used when looking up for very short strings. */ - return 0; - - if (memcmp (A->root.string + A->len - 2 * A->u.entsize, - B->root.string + B->len - 2 * A->u.entsize, - A->u.entsize) != 0) - /* This was a hashtable collision. */ - return 0; - - if (A->len <= B->len) - /* B cannot be a suffix of A unless A is equal to B, which is guaranteed - not to be equal by the hash table. */ - return 0; + if (res != 0) + return (res); - if (A->alignment < B->alignment - || ((A->len - B->len) & (B->alignment - 1))) + if ((A->alignment < B->alignment) || ((A->len - B->len) & (B->alignment - 1))) + /* The suffix is not sufficiently aligned. */ + return +1; + if ((A->alignment > B->alignment) || ((B->len - A->len) & (A->alignment - 1))) /* The suffix is not sufficiently aligned. */ - return 0; + return -1; - return memcmp (A->root.string + (A->len - B->len), - B->root.string, B->len - 2 * A->u.entsize) == 0; + return (0); } /* Record one section into the hash table. */ @@ -583,11 +577,10 @@ merge_strings (struct sec_merge_info *si { struct sec_merge_hash_entry **array, **a, **end, *e; struct sec_merge_sec_info *secinfo; - htab_t lasttab = NULL, last4tab = NULL; + splay_tree tree = NULL; bfd_size_type size, amt; /* Now sort the strings by length, longest first. */ - array = NULL; amt = sinfo->htab->size * sizeof (struct sec_merge_hash_entry *); array = (struct sec_merge_hash_entry **) bfd_malloc (amt); if (array == NULL) @@ -597,88 +590,43 @@ merge_strings (struct sec_merge_info *si if (e->alignment) *a++ = e; - sinfo->htab->size = a - array; + size = sinfo->htab->size = a - array; qsort (array, (size_t) sinfo->htab->size, sizeof (struct sec_merge_hash_entry *), cmplengthentry); - last4tab = htab_create_alloc ((size_t) sinfo->htab->size * 4, - NULL, last4_eq, NULL, calloc, free); - lasttab = htab_create_alloc ((size_t) sinfo->htab->size * 4, - NULL, last_eq, NULL, calloc, free); - if (lasttab == NULL || last4tab == NULL) + /* Create tree */ + tree = splay_tree_new ((splay_tree_compare_fn)cmpsuffixentry, 0, 0); + if (tree == NULL) goto alloc_failure; - /* Now insert the strings into hash tables (strings with last 4 characters - and strings with last character equal), look for longer strings which - we're suffix of. */ - for (a = array, end = array + sinfo->htab->size; a < end; a++) - { - register hashval_t hash; - unsigned int c; - unsigned int i; - const unsigned char *s; - void **p; + /* Insert strings into splay tree. Every collision means suffix. */ + for (a = array, end = array + size; a < end; a++) + { + register splay_tree_node n; e = *a; - e->u.entsize = sinfo->htab->entsize; - if (e->len <= e->u.entsize) - break; - if (e->len > 4 * e->u.entsize) - { - s = (const unsigned char *) (e->root.string + e->len - e->u.entsize); - hash = 0; - for (i = 0; i < 4 * e->u.entsize; i++) - { - c = *--s; - hash += c + (c << 17); - hash ^= hash >> 2; - } - p = htab_find_slot_with_hash (last4tab, e, hash, INSERT); - if (p == NULL) - goto alloc_failure; - if (*p) - { - struct sec_merge_hash_entry *ent; - ent = (struct sec_merge_hash_entry *) *p; - e->u.suffix = ent; - e->alignment = 0; - continue; - } - else - *p = e; - } - s = (const unsigned char *) (e->root.string + e->len - e->u.entsize); - hash = 0; - for (i = 0; i < e->u.entsize; i++) - { - c = *--s; - hash += c + (c << 17); - hash ^= hash >> 2; - } - p = htab_find_slot_with_hash (lasttab, e, hash, INSERT); - if (p == NULL) - goto alloc_failure; - if (*p) - { - struct sec_merge_hash_entry *ent; + n = splay_tree_insert (tree, (splay_tree_key)e, (splay_tree_value)e); + if (n == NULL) + goto alloc_failure; + if (n->key != (splay_tree_key)e) + { /* collision just occured and we have suffix of another one */ + register struct sec_merge_hash_entry *ent; + ent = (struct sec_merge_hash_entry *)n->key; + + n->value = (splay_tree_value)ent; /* back to what it was */ - ent = (struct sec_merge_hash_entry *) *p; - e->u.suffix = ent; - e->alignment = 0; + e->u.suffix = ent; + e->alignment = 0; } - else - *p = e; } alloc_failure: if (array) free (array); - if (lasttab) - htab_delete (lasttab); - if (last4tab) - htab_delete (last4tab); + if (tree) + splay_tree_delete (tree); /* Now assign positions to the strings we want to keep. */ size = 0;