summaryrefslogtreecommitdiff
path: root/variable.c
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2002-07-11 06:38:57 +0000
committerPaul Smith <psmith@gnu.org>2002-07-11 06:38:57 +0000
commit21cf8c64441103bf875a56b39f39397ecd51424e (patch)
tree24ff4cecaa8603feffa1ecf5ed82a4199a51d673 /variable.c
parent4d72c4c11e3aff65e9bb36e5fcf75f088b140049 (diff)
downloadgunmake-21cf8c64441103bf875a56b39f39397ecd51424e.tar.gz
Install Greg McGary's patches to port the id-utils hashing functions to
GNU make. Also he provides some other performance fixups after doing some profiling of make on large makefiles. Modify the test suite to allow the use of Valgrind to find memory problems.
Diffstat (limited to 'variable.c')
-rw-r--r--variable.c505
1 files changed, 200 insertions, 305 deletions
diff --git a/variable.c b/variable.c
index ca9265c..5495fbc 100644
--- a/variable.c
+++ b/variable.c
@@ -1,5 +1,6 @@
/* Internals of variables for GNU Make.
-Copyright (C) 1988,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997,
+2002 Free Software Foundation, Inc.
This file is part of GNU Make.
GNU Make is free software; you can redistribute it and/or modify
@@ -27,9 +28,35 @@ Boston, MA 02111-1307, USA. */
#ifdef WINDOWS32
#include "pathstuff.h"
#endif
+#include "hash.h"
/* Hash table of all global variable definitions. */
+static unsigned long
+variable_hash_1 (void const *keyv)
+{
+ struct variable const *key = (struct variable const *) keyv;
+ return_STRING_N_HASH_1 (key->name, key->length);
+}
+
+static unsigned long
+variable_hash_2 (void const *keyv)
+{
+ struct variable const *key = (struct variable const *) keyv;
+ return_STRING_N_HASH_2 (key->name, key->length);
+}
+
+static int
+variable_hash_cmp (void const *xv, void const *yv)
+{
+ struct variable const *x = (struct variable const *) xv;
+ struct variable const *y = (struct variable const *) yv;
+ int result = x->length - y->length;
+ if (result)
+ return result;
+ return_STRING_N_COMPARE (x->name, y->name, x->length);
+}
+
#ifndef VARIABLE_BUCKETS
#define VARIABLE_BUCKETS 523
#endif
@@ -39,15 +66,21 @@ Boston, MA 02111-1307, USA. */
#ifndef SMALL_SCOPE_VARIABLE_BUCKETS
#define SMALL_SCOPE_VARIABLE_BUCKETS 13
#endif
-static struct variable *variable_table[VARIABLE_BUCKETS];
-static struct variable_set global_variable_set
- = { variable_table, VARIABLE_BUCKETS };
+
+static struct variable_set global_variable_set;
static struct variable_set_list global_setlist
= { 0, &global_variable_set };
struct variable_set_list *current_variable_set_list = &global_setlist;
/* Implement variables. */
+void
+init_hash_global_variable_set ()
+{
+ hash_init (&global_variable_set.table, VARIABLE_BUCKETS,
+ variable_hash_1, variable_hash_2, variable_hash_cmp);
+}
+
/* Define variable named NAME with value VALUE in SET. VALUE is copied.
LENGTH is the length of NAME, which does not need to be null-terminated.
ORIGIN specifies the origin of the variable (makefile, command line
@@ -65,28 +98,22 @@ define_variable_in_set (name, length, value, origin, recursive, set, flocp)
struct variable_set *set;
const struct floc *flocp;
{
- register unsigned int i;
- register unsigned int hashval;
- register struct variable *v;
+ struct variable *v;
+ struct variable **var_slot;
+ struct variable var_key;
if (set == NULL)
set = &global_variable_set;
- hashval = 0;
- for (i = 0; i < length; ++i)
- HASH (hashval, name[i]);
- hashval %= set->buckets;
-
- for (v = set->table[hashval]; v != 0; v = v->next)
- if (*v->name == *name
- && strneq (v->name + 1, name + 1, length - 1)
- && v->name[length] == '\0')
- break;
+ var_key.name = (char *) name;
+ var_key.length = length;
+ var_slot = (struct variable **) hash_find_slot (&set->table, &var_key);
if (env_overrides && origin == o_env)
origin = o_env_override;
- if (v != 0)
+ v = *var_slot;
+ if (! HASH_VACANT (v))
{
if (env_overrides && v->origin == o_env)
/* V came from in the environment. Since it was defined
@@ -115,6 +142,8 @@ define_variable_in_set (name, length, value, origin, recursive, set, flocp)
v = (struct variable *) xmalloc (sizeof (struct variable));
v->name = savestring (name, length);
+ v->length = length;
+ hash_insert_at (&set->table, v, var_slot);
v->value = xstrdup (value);
if (flocp != 0)
v->fileinfo = *flocp;
@@ -127,8 +156,7 @@ define_variable_in_set (name, length, value, origin, recursive, set, flocp)
v->per_target = 0;
v->append = 0;
v->export = v_default;
- v->next = set->table[hashval];
- set->table[hashval] = v;
+
return v;
}
@@ -143,26 +171,20 @@ lookup_variable (name, length)
unsigned int length;
{
const struct variable_set_list *setlist;
+ struct variable var_key;
- unsigned int i;
- unsigned int rawhash = 0;
-
- for (i = 0; i < length; ++i)
- HASH (rawhash, name[i]);
+ var_key.name = (char *) name;
+ var_key.length = length;
for (setlist = current_variable_set_list;
setlist != 0; setlist = setlist->next)
{
const struct variable_set *set = setlist->set;
- unsigned int hashval = rawhash % set->buckets;
struct variable *v;
- /* Look through this set list; return it if found. */
- for (v = set->table[hashval]; v != 0; v = v->next)
- if (*v->name == *name
- && strneq (v->name + 1, name + 1, length - 1)
- && v->name[length] == '\0')
- return v;
+ v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
+ if (v)
+ return v;
}
#ifdef VMS
@@ -235,21 +257,12 @@ lookup_variable_in_set (name, length, set)
unsigned int length;
const struct variable_set *set;
{
- unsigned int i;
- unsigned int hash = 0;
- struct variable *v;
+ struct variable var_key;
- for (i = 0; i < length; ++i)
- HASH (hash, name[i]);
- hash %= set->buckets;
-
- for (v = set->table[hash]; v != 0; v = v->next)
- if (*v->name == *name
- && strneq (v->name + 1, name + 1, length - 1)
- && v->name[length] == 0)
- return v;
+ var_key.name = (char *) name;
+ var_key.length = length;
- return 0;
+ return (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
}
/* Initialize FILE's variable set list. If FILE already has a variable set
@@ -270,11 +283,8 @@ initialize_file_variables (file, reading)
l = (struct variable_set_list *)
xmalloc (sizeof (struct variable_set_list));
l->set = (struct variable_set *) xmalloc (sizeof (struct variable_set));
- l->set->buckets = PERFILE_VARIABLE_BUCKETS;
- l->set->table = (struct variable **)
- xmalloc (l->set->buckets * sizeof (struct variable *));
- bzero ((char *) l->set->table,
- l->set->buckets * sizeof (struct variable *));
+ hash_init (&l->set->table, PERFILE_VARIABLE_BUCKETS,
+ variable_hash_1, variable_hash_2, variable_hash_cmp);
file->variables = l;
}
@@ -316,31 +326,27 @@ initialize_file_variables (file, reading)
/* Pop the top set off the current variable set list,
and free all its storage. */
+static void
+free_variable_name_and_value (item)
+ void *item;
+{
+ struct variable *v = (struct variable *) item;
+ free (v->name);
+ free (v->value);
+}
+
void
pop_variable_scope ()
{
- register struct variable_set_list *setlist = current_variable_set_list;
- register struct variable_set *set = setlist->set;
- register unsigned int i;
+ struct variable_set_list *setlist = current_variable_set_list;
+ struct variable_set *set = setlist->set;
current_variable_set_list = setlist->next;
free ((char *) setlist);
- for (i = 0; i < set->buckets; ++i)
- {
- register struct variable *next = set->table[i];
- while (next != 0)
- {
- register struct variable *v = next;
- next = v->next;
+ hash_map (&set->table, free_variable_name_and_value);
+ hash_free (&set->table, 1);
- free (v->name);
- if (v->value)
- free (v->value);
- free ((char *) v);
- }
- }
- free ((char *) set->table);
free ((char *) set);
}
@@ -351,10 +357,8 @@ create_new_variable_set ()
register struct variable_set *set;
set = (struct variable_set *) xmalloc (sizeof (struct variable_set));
- set->buckets = SMALL_SCOPE_VARIABLE_BUCKETS;
- set->table = (struct variable **)
- xmalloc (set->buckets * sizeof (struct variable *));
- bzero ((char *) set->table, set->buckets * sizeof (struct variable *));
+ hash_init (&set->table, SMALL_SCOPE_VARIABLE_BUCKETS,
+ variable_hash_1, variable_hash_2, variable_hash_cmp);
setlist = (struct variable_set_list *)
xmalloc (sizeof (struct variable_set_list));
@@ -375,52 +379,27 @@ push_new_variable_scope ()
/* Merge SET1 into SET0, freeing unused storage in SET1. */
static void
-merge_variable_sets (set0, set1)
- struct variable_set *set0, *set1;
+merge_variable_sets (to_set, from_set)
+ struct variable_set *to_set, *from_set;
{
- register unsigned int bucket1;
-
- for (bucket1 = 0; bucket1 < set1->buckets; ++bucket1)
- {
- register struct variable *v1 = set1->table[bucket1];
- while (v1 != 0)
- {
- struct variable *next = v1->next;
- unsigned int bucket0;
- register struct variable *v0;
-
- if (set1->buckets >= set0->buckets)
- bucket0 = bucket1;
- else
- {
- register char *n;
- bucket0 = 0;
- for (n = v1->name; *n != '\0'; ++n)
- HASH (bucket0, *n);
- }
- bucket0 %= set0->buckets;
+ struct variable **from_var_slot = (struct variable **) from_set->table.ht_vec;
+ struct variable **from_var_end = from_var_slot + from_set->table.ht_size;
- for (v0 = set0->table[bucket0]; v0 != 0; v0 = v0->next)
- if (streq (v0->name, v1->name))
- break;
-
- if (v0 == 0)
- {
- /* There is no variable in SET0 with the same name. */
- v1->next = set0->table[bucket0];
- set0->table[bucket0] = v1;
- }
- else
- {
- /* The same variable exists in both sets.
- SET0 takes precedence. */
- free (v1->value);
- free ((char *) v1);
- }
-
- v1 = next;
- }
- }
+ for ( ; from_var_slot < from_var_end; from_var_slot++)
+ if (! HASH_VACANT (*from_var_slot))
+ {
+ struct variable *from_var = *from_var_slot;
+ struct variable **to_var_slot
+ = (struct variable **) hash_find_slot (&to_set->table, *from_var_slot);
+ if (HASH_VACANT (*to_var_slot))
+ hash_insert_at (&to_set->table, from_var, to_var_slot);
+ else
+ {
+ /* GKM FIXME: delete in from_set->table */
+ free (from_var->value);
+ free (from_var);
+ }
+ }
}
/* Merge SETLIST1 into SETLIST0, freeing unused storage in SETLIST1. */
@@ -467,7 +446,7 @@ define_automatic_variables ()
char buf[200];
sprintf (buf, "%u", makelevel);
- (void) define_variable ("MAKELEVEL", 9, buf, o_env, 0);
+ (void) define_variable (MAKELEVEL_NAME, MAKELEVEL_LENGTH, buf, o_env, 0);
sprintf (buf, "%s%s%s",
version_string,
@@ -564,184 +543,135 @@ target_environment (file)
{
struct variable_set_list *set_list;
register struct variable_set_list *s;
- struct variable_bucket
- {
- struct variable_bucket *next;
- struct variable *variable;
- };
- struct variable_bucket **table;
- unsigned int buckets;
- register unsigned int i;
- register unsigned nvariables;
+ struct hash_table table;
+ struct variable **v_slot;
+ struct variable **v_end;
+ struct variable makelevel_key;
+ char **result_0;
char **result;
- unsigned int mklev_hash;
if (file == 0)
set_list = current_variable_set_list;
else
set_list = file->variables;
- /* Find the lowest number of buckets in any set in the list. */
- s = set_list;
- buckets = s->set->buckets;
- for (s = s->next; s != 0; s = s->next)
- if (s->set->buckets < buckets)
- buckets = s->set->buckets;
-
- /* Find the hash value of the bucket `MAKELEVEL' will fall into. */
- {
- char *p = "MAKELEVEL";
- mklev_hash = 0;
- while (*p != '\0')
- HASH (mklev_hash, *p++);
- }
-
- /* Temporarily allocate a table with that many buckets. */
- table = (struct variable_bucket **)
- alloca (buckets * sizeof (struct variable_bucket *));
- bzero ((char *) table, buckets * sizeof (struct variable_bucket *));
+ hash_init (&table, VARIABLE_BUCKETS,
+ variable_hash_1, variable_hash_2, variable_hash_cmp);
/* Run through all the variable sets in the list,
accumulating variables in TABLE. */
- nvariables = 0;
for (s = set_list; s != 0; s = s->next)
{
- register struct variable_set *set = s->set;
- for (i = 0; i < set->buckets; ++i)
- {
- register struct variable *v;
- for (v = set->table[i]; v != 0; v = v->next)
- {
- unsigned int j = i % buckets;
- register struct variable_bucket *ov;
- register char *p = v->name;
-
- if (i == mklev_hash % set->buckets
- && streq (v->name, "MAKELEVEL"))
- /* Don't include MAKELEVEL because it will be
- added specially at the end. */
- continue;
-
- /* If this is a per-target variable and it hasn't been touched
- already then look up the global version and take its export
- value. */
- if (v->per_target && v->export == v_default)
- {
- struct variable *gv;
-
- gv = lookup_variable_in_set(v->name, strlen(v->name),
- &global_variable_set);
- if (gv)
- v->export = gv->export;
- }
-
- switch (v->export)
- {
- case v_default:
- if (v->origin == o_default || v->origin == o_automatic)
- /* Only export default variables by explicit request. */
- continue;
-
- if (! export_all_variables
- && v->origin != o_command
- && v->origin != o_env && v->origin != o_env_override)
- continue;
-
- if (*p != '_' && (*p < 'A' || *p > 'Z')
- && (*p < 'a' || *p > 'z'))
- continue;
- for (++p; *p != '\0'; ++p)
- if (*p != '_' && (*p < 'a' || *p > 'z')
- && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9'))
- continue;
- if (*p != '\0')
+ struct variable_set *set = s->set;
+ v_slot = (struct variable **) set->table.ht_vec;
+ v_end = v_slot + set->table.ht_size;
+ for ( ; v_slot < v_end; v_slot++)
+ if (! HASH_VACANT (*v_slot))
+ {
+ struct variable **new_slot;
+ struct variable *v = *v_slot;
+ char *p = v->name;
+
+ /* If this is a per-target variable and it hasn't been touched
+ already then look up the global version and take its export
+ value. */
+ if (v->per_target && v->export == v_default)
+ {
+ struct variable *gv;
+
+ gv = lookup_variable_in_set (v->name, strlen(v->name),
+ &global_variable_set);
+ if (gv)
+ v->export = gv->export;
+ }
+
+ switch (v->export)
+ {
+ case v_default:
+ if (v->origin == o_default || v->origin == o_automatic)
+ /* Only export default variables by explicit request. */
+ continue;
+
+ if (! export_all_variables
+ && v->origin != o_command
+ && v->origin != o_env && v->origin != o_env_override)
+ continue;
+
+ if (*p != '_' && (*p < 'A' || *p > 'Z')
+ && (*p < 'a' || *p > 'z'))
+ continue;
+ for (++p; *p != '\0'; ++p)
+ if (*p != '_' && (*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z') && (*p < '0' || *p > '9'))
continue;
- break;
+ if (*p != '\0')
+ continue;
+ break;
- case v_export:
- break;
+ case v_export:
+ break;
- case v_noexport:
- continue;
-
- case v_ifset:
- if (v->origin == o_default)
- continue;
- break;
- }
+ case v_noexport:
+ continue;
- /* If this was from a different-sized hash table, then
- recalculate the bucket it goes in. */
- if (set->buckets != buckets)
- {
- register char *np;
+ case v_ifset:
+ if (v->origin == o_default)
+ continue;
+ break;
+ }
- j = 0;
- for (np = v->name; *np != '\0'; ++np)
- HASH (j, *np);
- j %= buckets;
- }
+ new_slot = (struct variable **) hash_find_slot (&table, v);
+ if (HASH_VACANT (*new_slot))
+ hash_insert_at (&table, v, new_slot);
+ }
+ }
- for (ov = table[j]; ov != 0; ov = ov->next)
- if (streq (v->name, ov->variable->name))
- break;
+ makelevel_key.name = MAKELEVEL_NAME;
+ makelevel_key.length = MAKELEVEL_LENGTH;
+ hash_delete (&table, &makelevel_key);
- if (ov == 0)
- {
- register struct variable_bucket *entry;
- entry = (struct variable_bucket *)
- alloca (sizeof (struct variable_bucket));
- entry->next = table[j];
- entry->variable = v;
- table[j] = entry;
- ++nvariables;
- }
- }
- }
- }
+ result = result_0 = (char **) xmalloc ((table.ht_fill + 2) * sizeof (char *));
- result = (char **) xmalloc ((nvariables + 2) * sizeof (char *));
- nvariables = 0;
- for (i = 0; i < buckets; ++i)
- {
- register struct variable_bucket *b;
- for (b = table[i]; b != 0; b = b->next)
- {
- register struct variable *v = b->variable;
+ v_slot = (struct variable **) table.ht_vec;
+ v_end = v_slot + table.ht_size;
+ for ( ; v_slot < v_end; v_slot++)
+ if (! HASH_VACANT (*v_slot))
+ {
+ struct variable *v = *v_slot;
- /* If V is recursively expanded and didn't come from the environment,
- expand its value. If it came from the environment, it should
- go back into the environment unchanged. */
- if (v->recursive
- && v->origin != o_env && v->origin != o_env_override)
- {
- char *value = recursively_expand_for_file (v, file);
+ /* If V is recursively expanded and didn't come from the environment,
+ expand its value. If it came from the environment, it should
+ go back into the environment unchanged. */
+ if (v->recursive
+ && v->origin != o_env && v->origin != o_env_override)
+ {
+ char *value = recursively_expand_for_file (v, file);
#ifdef WINDOWS32
- if (strcmp(v->name, "Path") == 0 ||
- strcmp(v->name, "PATH") == 0)
- convert_Path_to_windows32(value, ';');
+ if (strcmp(v->name, "Path") == 0 ||
+ strcmp(v->name, "PATH") == 0)
+ convert_Path_to_windows32(value, ';');
#endif
- result[nvariables++] = concat (v->name, "=", value);
- free (value);
- }
- else
+ *result++ = concat (v->name, "=", value);
+ free (value);
+ }
+ else
+ {
#ifdef WINDOWS32
- {
if (strcmp(v->name, "Path") == 0 ||
strcmp(v->name, "PATH") == 0)
convert_Path_to_windows32(v->value, ';');
- result[nvariables++] = concat (v->name, "=", v->value);
- }
-#else
- result[nvariables++] = concat (v->name, "=", v->value);
#endif
- }
- }
- result[nvariables] = (char *) xmalloc (100);
- (void) sprintf (result[nvariables], "MAKELEVEL=%u", makelevel + 1);
- result[++nvariables] = 0;
+ *result++ = concat (v->name, "=", v->value);
+ }
+ }
+
+ *result = (char *) xmalloc (100);
+ (void) sprintf (*result, "%s=%u", MAKELEVEL_NAME, makelevel + 1);
+ *++result = 0;
- return result;
+ hash_free (&table, 0);
+
+ return result_0;
}
/* Given a variable, a value, and a flavor, define the variable.
@@ -1159,49 +1089,14 @@ print_variable_set (set, prefix)
register struct variable_set *set;
char *prefix;
{
- register unsigned int i, nvariables, per_bucket;
- register struct variable *v;
-
- per_bucket = nvariables = 0;
- for (i = 0; i < set->buckets; ++i)
- {
- register unsigned int this_bucket = 0;
-
- for (v = set->table[i]; v != 0; v = v->next)
- {
- ++this_bucket;
- print_variable (v, prefix);
- }
+ hash_map_arg (&set->table, print_variable, prefix);
- nvariables += this_bucket;
- if (this_bucket > per_bucket)
- per_bucket = this_bucket;
- }
-
- if (nvariables == 0)
- puts (_("# No variables."));
- else
- {
- printf (_("# %u variables in %u hash buckets.\n"),
- nvariables, set->buckets);
-#ifndef NO_FLOAT
- printf (_("# average of %.1f variables per bucket, \
-max %u in one bucket.\n"),
- (double) nvariables / (double) set->buckets,
- per_bucket);
-#else
- {
- int f = (nvariables * 1000 + 5) / set->buckets;
- printf (_("# average of %d.%d variables per bucket, \
-max %u in one bucket.\n"),
- f/10, f%10,
- per_bucket);
- }
-#endif
- }
+ fputs (_("# variable set hash-table stats:\n"), stdout);
+ fputs ("# ", stdout);
+ hash_print_stats (&set->table, stdout);
+ putc ('\n', stdout);
}
-
/* Print the data base of variables. */
void