OSDN Git Service

* var-tracking.c (struct shared_hash_def, shared_hash): New types.
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 21 Jun 2009 09:38:34 +0000 (09:38 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 21 Jun 2009 09:38:34 +0000 (09:38 +0000)
(dataflow_set): Change vars type from htab_t to shared_hash.
(shared_hash_pool, empty_shared_hash): New variables.
(vars_clear): Removed.
(shared_hash_shared, shared_hash_htab, shared_hash_copy,
shared_hash_find_slot_unshare, shared_hash_find_slot,
shared_hash_find_slot_noinsert, shared_hash_find): New
static inlines.
(shared_hash_unshare, shared_hash_destroy): New functions.
(unshare_variable): Unshare set->vars if shared, use
shared_hash_htab.
(vars_copy): Use htab_traverse_noresize instead of htab_traverse.
(get_init_value, find_src_set_src, dump_dataflow_set,
clobber_variable_part, emit_notes_for_differences): Use
shared_hash_htab.
(dataflow_set_init): Remove second argument, set vars to
empty_shared_hash instead of creating a new htab.
(dataflow_set_clear): Call shared_hash_destroy and set vars
to empty_shared_hash instead of calling vars_clear.
(dataflow_set_copy): Don't call vars_copy, instead just share
the src htab with dst.
(variable_union): Use shared_hash_*, use initially NO_INSERT
lookup if set->vars is shared.  Don't keep slot cleared before
calling unshare_variable.  Unshare set->vars if needed.
Even ->refcount == 1 vars must be unshared if set->vars is shared
and var needs to be modified.
(variable_canonicalize): New function.
(dataflow_set_union): If dst->vars is empty, just share src->vars
with dst->vars and traverse with variable_canonicalize to canonicalize
and unshare what is needed.
(dataflow_set_different): If old_set and new_set use the same shared
htab, they aren't different.  If number of htab elements is different,
htabs are different.  Use shared_hash_*.
(dataflow_set_destroy): Call shared_hash_destroy instead of
htab_delete.
(compute_bb_dataflow, emit_notes_in_bb, vt_emit_notes): Don't pass
second argument to dataflow_set_init.
(vt_initialize): Likewise.  Initialize shared_hash_pool and
empty_shared_hash, move bb in/out initialization afterwards.
Use variable_htab_free instead of NULL as changed_variables del hook.
(variable_was_changed): Change type of second argument to pointer to
dataflow_set.  When inserting var into changed_variables, bump
refcount.  Unshare set->vars if set is shared htab and slot needs to
be cleared.
(set_variable_part): Use shared_hash_*, use initially NO_INSERT
lookup if set->vars is shared.  Unshare set->vars if needed.
Even ->refcount == 1 vars must be unshared if set->vars is shared
and var needs to be modified.  Adjust variable_was_changed caller.
(delete_variable_part): Use shared_hash_*.  Even ->refcount == 1
vars must be unshared if set->vars is shared and var needs to be
modified.  Adjust variable_was_changed caller.
(emit_note_insn_var_location): Don't pool_free var.
(emit_notes_for_differences_1): Initialize empty_var->refcount to 0
instead of 1.
(vt_finalize): Call htab_delete on empty_shared_hash->htab and
free_alloc_pool on shared_hash_pool.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@148760 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/var-tracking.c

index 50f17b7..4478145 100644 (file)
@@ -1,3 +1,62 @@
+2009-06-21  Jakub Jelinek  <jakub@redhat.com>
+
+       * var-tracking.c (struct shared_hash_def, shared_hash): New types.
+       (dataflow_set): Change vars type from htab_t to shared_hash.
+       (shared_hash_pool, empty_shared_hash): New variables.
+       (vars_clear): Removed.
+       (shared_hash_shared, shared_hash_htab, shared_hash_copy,
+       shared_hash_find_slot_unshare, shared_hash_find_slot,
+       shared_hash_find_slot_noinsert, shared_hash_find): New
+       static inlines.
+       (shared_hash_unshare, shared_hash_destroy): New functions.
+       (unshare_variable): Unshare set->vars if shared, use
+       shared_hash_htab.
+       (vars_copy): Use htab_traverse_noresize instead of htab_traverse.
+       (get_init_value, find_src_set_src, dump_dataflow_set,
+       clobber_variable_part, emit_notes_for_differences): Use
+       shared_hash_htab.
+       (dataflow_set_init): Remove second argument, set vars to
+       empty_shared_hash instead of creating a new htab.
+       (dataflow_set_clear): Call shared_hash_destroy and set vars
+       to empty_shared_hash instead of calling vars_clear.
+       (dataflow_set_copy): Don't call vars_copy, instead just share
+       the src htab with dst.
+       (variable_union): Use shared_hash_*, use initially NO_INSERT
+       lookup if set->vars is shared.  Don't keep slot cleared before
+       calling unshare_variable.  Unshare set->vars if needed.
+       Even ->refcount == 1 vars must be unshared if set->vars is shared
+       and var needs to be modified.
+       (variable_canonicalize): New function.
+       (dataflow_set_union): If dst->vars is empty, just share src->vars
+       with dst->vars and traverse with variable_canonicalize to canonicalize
+       and unshare what is needed.
+       (dataflow_set_different): If old_set and new_set use the same shared
+       htab, they aren't different.  If number of htab elements is different,
+       htabs are different.  Use shared_hash_*.
+       (dataflow_set_destroy): Call shared_hash_destroy instead of
+       htab_delete.
+       (compute_bb_dataflow, emit_notes_in_bb, vt_emit_notes): Don't pass
+       second argument to dataflow_set_init.
+       (vt_initialize): Likewise.  Initialize shared_hash_pool and
+       empty_shared_hash, move bb in/out initialization afterwards.
+       Use variable_htab_free instead of NULL as changed_variables del hook.
+       (variable_was_changed): Change type of second argument to pointer to
+       dataflow_set.  When inserting var into changed_variables, bump
+       refcount.  Unshare set->vars if set is shared htab and slot needs to
+       be cleared.
+       (set_variable_part): Use shared_hash_*, use initially NO_INSERT
+       lookup if set->vars is shared.  Unshare set->vars if needed.
+       Even ->refcount == 1 vars must be unshared if set->vars is shared
+       and var needs to be modified.  Adjust variable_was_changed caller.
+       (delete_variable_part): Use shared_hash_*.  Even ->refcount == 1
+       vars must be unshared if set->vars is shared and var needs to be
+       modified.  Adjust variable_was_changed caller.
+       (emit_note_insn_var_location): Don't pool_free var.
+       (emit_notes_for_differences_1): Initialize empty_var->refcount to 0
+       instead of 1.
+       (vt_finalize): Call htab_delete on empty_shared_hash->htab and
+       free_alloc_pool on shared_hash_pool.
+
 2009-06-20  Anthony Green  <green@moxielogic.com>
 
        * config/moxie/sfp-machine.h (__gcc_CMPtype, CMPtype): Define.
index c4c3f3e..56e50c2 100644 (file)
@@ -182,6 +182,17 @@ typedef struct attrs_def
   HOST_WIDE_INT offset;
 } *attrs;
 
+/* Structure holding a refcounted hash table.  If refcount > 1,
+   it must be first unshared before modified.  */
+typedef struct shared_hash_def
+{
+  /* Reference count.  */
+  int refcount;
+
+  /* Actual hash table.  */
+  htab_t htab;
+} *shared_hash;
+
 /* Structure holding the IN or OUT set for a basic block.  */
 typedef struct dataflow_set_def
 {
@@ -192,7 +203,7 @@ typedef struct dataflow_set_def
   attrs regs[FIRST_PSEUDO_REGISTER];
 
   /* Variable locations.  */
-  htab_t vars;
+  shared_hash vars;
 } dataflow_set;
 
 /* The structure (one for each basic block) containing the information
@@ -280,12 +291,18 @@ static alloc_pool var_pool;
 /* Alloc pool for struct location_chain_def.  */
 static alloc_pool loc_chain_pool;
 
+/* Alloc pool for struct shared_hash_def.  */
+static alloc_pool shared_hash_pool;
+
 /* Changed variables, notes will be emitted for them.  */
 static htab_t changed_variables;
 
 /* Shall notes be emitted?  */
 static bool emit_notes;
 
+/* Empty shared hashtable.  */
+static shared_hash empty_shared_hash;
+
 /* Local function prototypes.  */
 static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
                                          HOST_WIDE_INT *);
@@ -305,7 +322,6 @@ static void attrs_list_insert (attrs *, tree, HOST_WIDE_INT, rtx);
 static void attrs_list_copy (attrs *, attrs);
 static void attrs_list_union (attrs *, attrs);
 
-static void vars_clear (htab_t);
 static variable unshare_variable (dataflow_set *set, variable var, 
                                  enum var_init_status);
 static int vars_copy_1 (void **, void *);
@@ -321,11 +337,12 @@ static void var_mem_delete_and_set (dataflow_set *, rtx, bool,
                                    enum var_init_status, rtx);
 static void var_mem_delete (dataflow_set *, rtx, bool);
 
-static void dataflow_set_init (dataflow_set *, int);
+static void dataflow_set_init (dataflow_set *);
 static void dataflow_set_clear (dataflow_set *);
 static void dataflow_set_copy (dataflow_set *, dataflow_set *);
 static int variable_union_info_cmp_pos (const void *, const void *);
 static int variable_union (void **, void *);
+static int variable_canonicalize (void **, void *);
 static void dataflow_set_union (dataflow_set *, dataflow_set *);
 static bool variable_part_different_p (variable_part *, variable_part *);
 static bool variable_different_p (variable, variable, bool);
@@ -352,7 +369,7 @@ static void dump_vars (htab_t);
 static void dump_dataflow_set (dataflow_set *);
 static void dump_dataflow_sets (void);
 
-static void variable_was_changed (variable, htab_t);
+static void variable_was_changed (variable, dataflow_set *);
 static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT, 
                               enum var_init_status, rtx);
 static void clobber_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT, 
@@ -742,12 +759,107 @@ attrs_list_union (attrs *dstp, attrs src)
     }
 }
 
-/* Delete all variables from hash table VARS.  */
+/* Shared hashtable support.  */
+
+/* Return true if VARS is shared.  */
+
+static inline bool
+shared_hash_shared (shared_hash vars)
+{
+  return vars->refcount > 1;
+}
+
+/* Return the hash table for VARS.  */
+
+static inline htab_t
+shared_hash_htab (shared_hash vars)
+{
+  return vars->htab;
+}
+
+/* Copy variables into a new hash table.  */
+
+static shared_hash
+shared_hash_unshare (shared_hash vars)
+{
+  shared_hash new_vars = (shared_hash) pool_alloc (shared_hash_pool);
+  gcc_assert (vars->refcount > 1);
+  new_vars->refcount = 1;
+  new_vars->htab
+    = htab_create (htab_elements (vars->htab) + 3, variable_htab_hash,
+                  variable_htab_eq, variable_htab_free);
+  vars_copy (new_vars->htab, vars->htab);
+  vars->refcount--;
+  return new_vars;
+}
+
+/* Increment reference counter on VARS and return it.  */
+
+static inline shared_hash
+shared_hash_copy (shared_hash vars)
+{
+  vars->refcount++;
+  return vars;
+}
+
+/* Decrement reference counter and destroy hash table if not shared
+   anymore.  */
 
 static void
-vars_clear (htab_t vars)
+shared_hash_destroy (shared_hash vars)
 {
-  htab_empty (vars);
+  gcc_assert (vars->refcount > 0);
+  if (--vars->refcount == 0)
+    {
+      htab_delete (vars->htab);
+      pool_free (shared_hash_pool, vars);
+    }
+}
+
+/* Unshare *PVARS if shared and return slot for DECL.  If INS is
+   INSERT, insert it if not already present.  */
+
+static inline void **
+shared_hash_find_slot_unshare (shared_hash *pvars, tree decl,
+                              enum insert_option ins)
+{
+  if (shared_hash_shared (*pvars))
+    *pvars = shared_hash_unshare (*pvars);
+  return htab_find_slot_with_hash (shared_hash_htab (*pvars), decl,
+                                  VARIABLE_HASH_VAL (decl), ins);
+}
+
+/* Return slot for DECL, if it is already present in the hash table.
+   If it is not present, insert it only VARS is not shared, otherwise
+   return NULL.  */
+
+static inline void **
+shared_hash_find_slot (shared_hash vars, tree decl)
+{
+  return htab_find_slot_with_hash (shared_hash_htab (vars), decl,
+                                  VARIABLE_HASH_VAL (decl),
+                                  shared_hash_shared (vars)
+                                  ? NO_INSERT : INSERT);
+}
+
+/* Return slot for DECL only if it is already present in the hash table.  */
+
+static inline void **
+shared_hash_find_slot_noinsert (shared_hash vars, tree decl)
+{
+  return htab_find_slot_with_hash (shared_hash_htab (vars), decl,
+                                  VARIABLE_HASH_VAL (decl), NO_INSERT);
+}
+
+/* Return variable for DECL or NULL if not already present in the hash
+   table.  */
+
+static inline variable
+shared_hash_find (shared_hash vars, tree decl)
+{
+  return (variable)
+        htab_find_with_hash (shared_hash_htab (vars), decl,
+                             VARIABLE_HASH_VAL (decl));
 }
 
 /* Return a copy of a variable VAR and insert it to dataflow set SET.  */
@@ -801,9 +913,7 @@ unshare_variable (dataflow_set *set, variable var,
        new_var->var_part[i].cur_loc = NULL;
     }
 
-  slot = htab_find_slot_with_hash (set->vars, new_var->decl,
-                                  VARIABLE_HASH_VAL (new_var->decl),
-                                  INSERT);
+  slot = shared_hash_find_slot_unshare (&set->vars, new_var->decl, INSERT);
   *slot = new_var;
   return new_var;
 }
@@ -834,8 +944,7 @@ vars_copy_1 (void **slot, void *data)
 static void
 vars_copy (htab_t dst, htab_t src)
 {
-  vars_clear (dst);
-  htab_traverse (src, vars_copy_1, dst);
+  htab_traverse_noresize (src, vars_copy_1, dst);
 }
 
 /* Map a decl to its main debug decl.  */
@@ -874,7 +983,6 @@ var_reg_set (dataflow_set *set, rtx loc, enum var_init_status initialized,
 static enum var_init_status
 get_init_value (dataflow_set *set, rtx loc, tree decl)
 {
-  void **slot;
   variable var;
   int i;
   enum var_init_status ret_val = VAR_INIT_STATUS_UNKNOWN;
@@ -882,11 +990,9 @@ get_init_value (dataflow_set *set, rtx loc, tree decl)
   if (! flag_var_tracking_uninit)
     return VAR_INIT_STATUS_INITIALIZED;
 
-  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
-                                  NO_INSERT);
-  if (slot)
+  var = shared_hash_find (set->vars, decl);
+  if (var)
     {
-      var = * (variable *) slot;
       for (i = 0; i < var->n_var_parts && ret_val == VAR_INIT_STATUS_UNKNOWN; i++)
        {
          location_chain nextp;
@@ -1050,11 +1156,10 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
    VARS_SIZE is the initial size of hash table VARS.  */
 
 static void
-dataflow_set_init (dataflow_set *set, int vars_size)
+dataflow_set_init (dataflow_set *set)
 {
   init_attrs_list_set (set->regs);
-  set->vars = htab_create (vars_size, variable_htab_hash, variable_htab_eq,
-                          variable_htab_free);
+  set->vars = shared_hash_copy (empty_shared_hash);
   set->stack_adjust = 0;
 }
 
@@ -1068,7 +1173,8 @@ dataflow_set_clear (dataflow_set *set)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     attrs_list_clear (&set->regs[i]);
 
-  vars_clear (set->vars);
+  shared_hash_destroy (set->vars);
+  set->vars = shared_hash_copy (empty_shared_hash);
 }
 
 /* Copy the contents of dataflow set SRC to DST.  */
@@ -1081,7 +1187,8 @@ dataflow_set_copy (dataflow_set *dst, dataflow_set *src)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     attrs_list_copy (&dst->regs[i], src->regs[i]);
 
-  vars_copy (dst->vars, src->vars);
+  shared_hash_destroy (dst->vars);
+  dst->vars = shared_hash_copy (src->vars);
   dst->stack_adjust = src->stack_adjust;
 }
 
@@ -1129,15 +1236,14 @@ variable_union_info_cmp_pos (const void *n1, const void *n2)
 static int
 variable_union (void **slot, void *data)
 {
-  variable src, dst, *dstp;
+  variable src, dst;
+  void **dstp;
   dataflow_set *set = (dataflow_set *) data;
   int i, j, k;
 
   src = *(variable *) slot;
-  dstp = (variable *) htab_find_slot_with_hash (set->vars, src->decl,
-                                               VARIABLE_HASH_VAL (src->decl),
-                                               INSERT);
-  if (!*dstp)
+  dstp = shared_hash_find_slot (set->vars, src->decl);
+  if (!dstp || !*dstp)
     {
       src->refcount++;
 
@@ -1162,16 +1268,23 @@ variable_union (void **slot, void *data)
          if (! flag_var_tracking_uninit)
            status = VAR_INIT_STATUS_INITIALIZED;
 
+         if (dstp)
+           *dstp = (void *) src;
          unshare_variable (set, src, status);
        }
       else
-       *dstp = src;
+       {
+         if (!dstp)
+           dstp = shared_hash_find_slot_unshare (&set->vars, src->decl,
+                                                 INSERT);
+         *dstp = (void *) src;
+       }
 
       /* Continue traversing the hash table.  */
       return 1;
     }
   else
-    dst = *dstp;
+    dst = (variable) *dstp;
 
   gcc_assert (src->n_var_parts);
 
@@ -1196,7 +1309,8 @@ variable_union (void **slot, void *data)
      thus there are at most MAX_VAR_PARTS different offsets.  */
   gcc_assert (k <= MAX_VAR_PARTS);
 
-  if (dst->refcount > 1 && dst->n_var_parts != k)
+  if ((dst->refcount > 1 || shared_hash_shared (set->vars))
+      && dst->n_var_parts != k)
     {
       enum var_init_status status = VAR_INIT_STATUS_UNKNOWN;
       
@@ -1226,7 +1340,7 @@ variable_union (void **slot, void *data)
          /* If DST is shared compare the location chains.
             If they are different we will modify the chain in DST with
             high probability so make a copy of DST.  */
-         if (dst->refcount > 1)
+         if (dst->refcount > 1 || shared_hash_shared (set->vars))
            {
              for (node = src->var_part[i].loc_chain,
                   node2 = dst->var_part[j].loc_chain; node && node2;
@@ -1379,6 +1493,46 @@ variable_union (void **slot, void *data)
   return 1;
 }
 
+/* Like variable_union, but only used when doing dataflow_set_union
+   into an empty hashtab.  To allow sharing, dst is initially shared
+   with src (so all variables are "copied" from src to dst hashtab),
+   so only unshare_variable for variables that need canonicalization
+   are needed.  */
+
+static int
+variable_canonicalize (void **slot, void *data)
+{
+  variable src;
+  dataflow_set *set = (dataflow_set *) data;
+  int k;
+
+  src = *(variable *) slot;
+
+  /* If CUR_LOC of some variable part is not the first element of
+     the location chain we are going to change it so we have to make
+     a copy of the variable.  */
+  for (k = 0; k < src->n_var_parts; k++)
+    {
+      gcc_assert (!src->var_part[k].loc_chain == !src->var_part[k].cur_loc);
+      if (src->var_part[k].loc_chain)
+       {
+         gcc_assert (src->var_part[k].cur_loc);
+         if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
+           break;
+       }
+    }
+  if (k < src->n_var_parts)
+    {
+      enum var_init_status status = VAR_INIT_STATUS_UNKNOWN;
+
+      if (! flag_var_tracking_uninit)
+       status = VAR_INIT_STATUS_INITIALIZED;
+
+      unshare_variable (set, src, status);
+    }
+  return 1;
+}
+
 /* Compute union of dataflow sets SRC and DST and store it to DST.  */
 
 static void
@@ -1389,7 +1543,14 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     attrs_list_union (&dst->regs[i], src->regs[i]);
 
-  htab_traverse (src->vars, variable_union, dst);
+  if (dst->vars == empty_shared_hash)
+    {
+      shared_hash_destroy (dst->vars);
+      dst->vars = shared_hash_copy (src->vars);
+      htab_traverse (shared_hash_htab (src->vars), variable_canonicalize, dst);
+    }
+  else
+    htab_traverse (shared_hash_htab (src->vars), variable_union, dst);
 }
 
 /* Flag whether two dataflow sets being compared contain different data.  */
@@ -1522,15 +1683,24 @@ dataflow_set_different_2 (void **slot, void *data)
 static bool
 dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
 {
+  if (old_set->vars == new_set->vars)
+    return false;
+
+  if (htab_elements (shared_hash_htab (old_set->vars))
+      != htab_elements (shared_hash_htab (new_set->vars)))
+    return true;
+
   dataflow_set_different_value = false;
 
-  htab_traverse (old_set->vars, dataflow_set_different_1, new_set->vars);
+  htab_traverse (shared_hash_htab (old_set->vars), dataflow_set_different_1,
+                shared_hash_htab (new_set->vars));
   if (!dataflow_set_different_value)
     {
       /* We have compared the variables which are in both hash tables
         so now only check whether there are some variables in NEW_SET->VARS
         which are not in OLD_SET->VARS.  */
-      htab_traverse (new_set->vars, dataflow_set_different_2, old_set->vars);
+      htab_traverse (shared_hash_htab (new_set->vars), dataflow_set_different_2,
+                    shared_hash_htab (old_set->vars));
     }
   return dataflow_set_different_value;
 }
@@ -1545,7 +1715,7 @@ dataflow_set_destroy (dataflow_set *set)
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     attrs_list_clear (&set->regs[i]);
 
-  htab_delete (set->vars);
+  shared_hash_destroy (set->vars);
   set->vars = NULL;
 }
 
@@ -1985,7 +2155,6 @@ find_src_set_src (dataflow_set *set, rtx src)
 {
   tree decl = NULL_TREE;   /* The variable being copied around.          */
   rtx set_src = NULL_RTX;  /* The value for "decl" stored in "src".      */
-  void **slot;
   variable var;
   location_chain nextp;
   int i;
@@ -1998,12 +2167,9 @@ find_src_set_src (dataflow_set *set, rtx src)
 
   if (src && decl)
     {
-      slot = htab_find_slot_with_hash (set->vars, decl, 
-                                      VARIABLE_HASH_VAL (decl), NO_INSERT);
-
-      if (slot)
+      var = shared_hash_find (set->vars, decl);
+      if (var)
        {
-         var = *(variable *) slot;
          found = false;
          for (i = 0; i < var->n_var_parts && !found; i++)
            for (nextp = var->var_part[i].loc_chain; nextp && !found; 
@@ -2031,7 +2197,7 @@ compute_bb_dataflow (basic_block bb)
   dataflow_set *in = &VTI (bb)->in;
   dataflow_set *out = &VTI (bb)->out;
 
-  dataflow_set_init (&old_out, htab_elements (VTI (bb)->out.vars) + 3);
+  dataflow_set_init (&old_out);
   dataflow_set_copy (&old_out, out);
   dataflow_set_copy (out, in);
 
@@ -2323,7 +2489,7 @@ dump_dataflow_set (dataflow_set *set)
          dump_attrs_list (set->regs[i]);
        }
     }
-  dump_vars (set->vars);
+  dump_vars (shared_hash_htab (set->vars));
   fprintf (dump_file, "\n");
 }
 
@@ -2345,10 +2511,10 @@ dump_dataflow_sets (void)
 }
 
 /* Add variable VAR to the hash table of changed variables and
-   if it has no locations delete it from hash table HTAB.  */
+   if it has no locations delete it from SET's hash table.  */
 
 static void
-variable_was_changed (variable var, htab_t htab)
+variable_was_changed (variable var, dataflow_set *set)
 {
   hashval_t hash = VARIABLE_HASH_VAL (var->decl);
 
@@ -2359,36 +2525,39 @@ variable_was_changed (variable var, htab_t htab)
       slot = (variable *) htab_find_slot_with_hash (changed_variables,
                                                    var->decl, hash, INSERT);
 
-      if (htab && var->n_var_parts == 0)
+      if (set && var->n_var_parts == 0)
        {
          variable empty_var;
-         void **old;
 
          empty_var = (variable) pool_alloc (var_pool);
          empty_var->decl = var->decl;
          empty_var->refcount = 1;
          empty_var->n_var_parts = 0;
          *slot = empty_var;
-
-         old = htab_find_slot_with_hash (htab, var->decl, hash,
-                                         NO_INSERT);
-         if (old)
-           htab_clear_slot (htab, old);
+         goto drop_var;
        }
       else
        {
+         var->refcount++;
          *slot = var;
        }
     }
   else
     {
-      gcc_assert (htab);
+      gcc_assert (set);
       if (var->n_var_parts == 0)
        {
-         void **slot = htab_find_slot_with_hash (htab, var->decl, hash,
-                                                 NO_INSERT);
+         void **slot;
+
+       drop_var:
+         slot = shared_hash_find_slot_noinsert (set->vars, var->decl);
          if (slot)
-           htab_clear_slot (htab, slot);
+           {
+             if (shared_hash_shared (set->vars))
+               slot = shared_hash_find_slot_unshare (&set->vars, var->decl,
+                                                     NO_INSERT);
+             htab_clear_slot (shared_hash_htab (set->vars), slot);
+           }
        }
     }
 }
@@ -2438,12 +2607,12 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
   location_chain node, next;
   location_chain *nextp;
   variable var;
-  void **slot;
-  
-  slot = htab_find_slot_with_hash (set->vars, decl,
-                                  VARIABLE_HASH_VAL (decl), INSERT);
-  if (!*slot)
+  void **slot = shared_hash_find_slot (set->vars, decl);
+
+  if (!slot || !*slot)
     {
+      if (!slot)
+       slot = shared_hash_find_slot_unshare (&set->vars, decl, INSERT);
       /* Create new variable information.  */
       var = (variable) pool_alloc (var_pool);
       var->decl = decl;
@@ -2479,13 +2648,12 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
              if (set_src != NULL)
                node->set_src = set_src;
 
-             *slot = var;
              return;
            }
          else
            {
              /* We have to make a copy of a shared variable.  */
-             if (var->refcount > 1)
+             if (var->refcount > 1 || shared_hash_shared (set->vars))
                var = unshare_variable (set, var, initialized);
            }
        }
@@ -2494,7 +2662,7 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
          /* We have not found the location part, new one will be created.  */
 
          /* We have to make a copy of the shared variable.  */
-         if (var->refcount > 1)
+         if (var->refcount > 1 || shared_hash_shared (set->vars))
            var = unshare_variable (set, var, initialized);
 
          /* We track only variables whose size is <= MAX_VAR_PARTS bytes
@@ -2548,7 +2716,7 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
   if (var->var_part[pos].cur_loc == NULL)
     {
       var->var_part[pos].cur_loc = loc;
-      variable_was_changed (var, set->vars);
+      variable_was_changed (var, set);
     }
 }
 
@@ -2561,16 +2729,14 @@ static void
 clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
                       HOST_WIDE_INT offset, rtx set_src)
 {
-  void **slot;
+  variable var;
 
   if (! decl || ! DECL_P (decl))
     return;
 
-  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
-                                  NO_INSERT);
-  if (slot)
+  var = shared_hash_find (set->vars, decl);
+  if (var)
     {
-      variable var = (variable) *slot;
       int pos = find_variable_location_part (var, offset, NULL);
 
       if (pos >= 0)
@@ -2627,13 +2793,9 @@ static void
 delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                      HOST_WIDE_INT offset)
 {
-  void **slot;
-    
-  slot = htab_find_slot_with_hash (set->vars, decl, VARIABLE_HASH_VAL (decl),
-                                  NO_INSERT);
-  if (slot)
+  variable var = shared_hash_find (set->vars, decl);;
+  if (var)
     {
-      variable var = (variable) *slot;
       int pos = find_variable_location_part (var, offset, NULL);
 
       if (pos >= 0)
@@ -2642,7 +2804,7 @@ delete_variable_part (dataflow_set *set, rtx loc, tree decl,
          location_chain *nextp;
          bool changed;
 
-         if (var->refcount > 1)
+         if (var->refcount > 1 || shared_hash_shared (set->vars))
            {
              /* If the variable contains the location part we have to
                 make a copy of the variable.  */
@@ -2705,7 +2867,7 @@ delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                }
            }
          if (changed)
-           variable_was_changed (var, set->vars);
+           variable_was_changed (var, set);
        }
     }
 }
@@ -2864,14 +3026,6 @@ emit_note_insn_var_location (void **varp, void *data)
 
   htab_clear_slot (changed_variables, varp);
 
-  /* When there are no location parts the variable has been already
-     removed from hash table and a new empty variable was created.
-     Free the empty variable.  */
-  if (var->n_var_parts == 0)
-    {
-      pool_free (var_pool, var);
-    }
-
   /* Continue traversing the hash table.  */
   return 1;
 }
@@ -2910,7 +3064,7 @@ emit_notes_for_differences_1 (void **slot, void *data)
 
       empty_var = (variable) pool_alloc (var_pool);
       empty_var->decl = old_var->decl;
-      empty_var->refcount = 1;
+      empty_var->refcount = 0;
       empty_var->n_var_parts = 0;
       variable_was_changed (empty_var, NULL);
     }
@@ -2952,8 +3106,12 @@ static void
 emit_notes_for_differences (rtx insn, dataflow_set *old_set,
                            dataflow_set *new_set)
 {
-  htab_traverse (old_set->vars, emit_notes_for_differences_1, new_set->vars);
-  htab_traverse (new_set->vars, emit_notes_for_differences_2, old_set->vars);
+  htab_traverse (shared_hash_htab (old_set->vars),
+                emit_notes_for_differences_1,
+                shared_hash_htab (new_set->vars));
+  htab_traverse (shared_hash_htab (new_set->vars),
+                emit_notes_for_differences_2,
+                shared_hash_htab (old_set->vars));
   emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
 }
 
@@ -2965,7 +3123,7 @@ emit_notes_in_bb (basic_block bb)
   int i;
   dataflow_set set;
 
-  dataflow_set_init (&set, htab_elements (VTI (bb)->in.vars) + 3);
+  dataflow_set_init (&set);
   dataflow_set_copy (&set, &VTI (bb)->in);
 
   for (i = 0; i < VTI (bb)->n_mos; i++)
@@ -3098,7 +3256,7 @@ vt_emit_notes (void)
      delete_variable_part).  */
   emit_notes = true;
 
-  dataflow_set_init (&empty, 7);
+  dataflow_set_init (&empty);
   last_out = &empty;
 
   FOR_EACH_BB (bb)
@@ -3343,14 +3501,6 @@ vt_initialize (void)
        }
     }
 
-  /* Init the IN and OUT sets.  */
-  FOR_ALL_BB (bb)
-    {
-      VTI (bb)->visited = false;
-      dataflow_set_init (&VTI (bb)->in, 7);
-      dataflow_set_init (&VTI (bb)->out, 7);
-    }
-
   attrs_pool = create_alloc_pool ("attrs_def pool",
                                  sizeof (struct attrs_def), 1024);
   var_pool = create_alloc_pool ("variable_def pool",
@@ -3358,8 +3508,24 @@ vt_initialize (void)
   loc_chain_pool = create_alloc_pool ("location_chain_def pool",
                                      sizeof (struct location_chain_def),
                                      1024);
+  shared_hash_pool = create_alloc_pool ("shared_hash_def pool",
+                                       sizeof (struct shared_hash_def), 256);
+  empty_shared_hash = (shared_hash) pool_alloc (shared_hash_pool);
+  empty_shared_hash->refcount = 1;
+  empty_shared_hash->htab
+    = htab_create (1, variable_htab_hash, variable_htab_eq,
+                  variable_htab_free);
   changed_variables = htab_create (10, variable_htab_hash, variable_htab_eq,
-                                  NULL);
+                                  variable_htab_free);
+
+  /* Init the IN and OUT sets.  */
+  FOR_ALL_BB (bb)
+    {
+      VTI (bb)->visited = false;
+      dataflow_set_init (&VTI (bb)->in);
+      dataflow_set_init (&VTI (bb)->out);
+    }
+
   vt_add_function_parameters ();
 }
 
@@ -3381,10 +3547,12 @@ vt_finalize (void)
       dataflow_set_destroy (&VTI (bb)->out);
     }
   free_aux_for_blocks ();
+  htab_delete (empty_shared_hash->htab);
+  htab_delete (changed_variables);
   free_alloc_pool (attrs_pool);
   free_alloc_pool (var_pool);
   free_alloc_pool (loc_chain_pool);
-  htab_delete (changed_variables);
+  free_alloc_pool (shared_hash_pool);
 }
 
 /* The entry point to variable tracking pass.  */