OSDN Git Service

* config/alpha/alpha.c (alpha_sa_size): Force procedure type to
[pf3gnuchains/gcc-fork.git] / gcc / var-tracking.c
index 6d5c14c..76354d7 100644 (file)
@@ -1,11 +1,12 @@
 /* Variable tracking routines for the GNU compiler.
-   Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009
+   Free Software Foundation, Inc.
 
    This file is part of GCC.
 
    GCC is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
+   the Free Software Foundation; either version 3, or (at your option)
    any later version.
 
    GCC is distributed in the hope that it will be useful, but WITHOUT
@@ -14,9 +15,8 @@
    License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with GCC; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
 
 /* This file contains the variable tracking pass.  It computes where
    variables are located (which registers or where in memory) at each position
 #include "hashtab.h"
 #include "regs.h"
 #include "expr.h"
+#include "timevar.h"
+#include "tree-pass.h"
 
 /* Type of micro operation.  */
 enum micro_operation_type
@@ -112,6 +114,8 @@ enum micro_operation_type
   MO_USE_NO_VAR,/* Use location which is not associated with a variable
                   or the variable is not trackable.  */
   MO_SET,      /* Set location.  */
+  MO_COPY,     /* Copy the same portion of a variable from one
+                  location to another.  */
   MO_CLOBBER,  /* Clobber location.  */
   MO_CALL,     /* Call insn.  */
   MO_ADJUST    /* Adjust stack pointer.  */
@@ -131,14 +135,20 @@ typedef struct micro_operation_def
   enum micro_operation_type type;
 
   union {
-    /* Location.  */
+    /* Location.  For MO_SET and MO_COPY, this is the SET that performs
+       the assignment, if known, otherwise it is the target of the
+       assignment.  */
     rtx loc;
 
     /* Stack adjustment.  */
     HOST_WIDE_INT adjust;
   } u;
 
-  /* The instruction which the micro operation is in.  */
+  /* The instruction which the micro operation is in, for MO_USE,
+     MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
+     instruction or note in the original flow (before any var-tracking
+     notes are inserted, to simplify emission of notes), for MO_SET
+     and MO_CLOBBER.  */
   rtx insn;
 } micro_operation;
 
@@ -172,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
 {
@@ -182,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
@@ -211,6 +232,12 @@ typedef struct location_chain_def
 
   /* The location (REG or MEM).  */
   rtx loc;
+
+  /* The "value" stored in this location.  */
+  rtx set_src;
+
+  /* Initialized? */
+  enum var_init_status init;
 } *location_chain;
 
 /* Structure describing one part of variable.  */
@@ -244,6 +271,7 @@ typedef struct variable_def
   /* The variable parts.  */
   variable_part var_part[MAX_VAR_PARTS];
 } *variable;
+typedef const struct variable_def *const_variable;
 
 /* Hash function for DECL for VARIABLE_HTAB.  */
 #define VARIABLE_HASH_VAL(decl) (DECL_UID (decl))
@@ -251,6 +279,9 @@ typedef struct variable_def
 /* Pointer to the BB's information specific to variable tracking pass.  */
 #define VTI(BB) ((variable_tracking_info) (BB)->aux)
 
+/* Macro to access MEM_OFFSET as an HOST_WIDE_INT.  Evaluates MEM twice.  */
+#define INT_MEM_OFFSET(mem) (MEM_OFFSET (mem) ? INTVAL (MEM_OFFSET (mem)) : 0)
+
 /* Alloc pool for struct attrs_def.  */
 static alloc_pool attrs_pool;
 
@@ -260,17 +291,17 @@ 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;
 
-/* Fake variable for stack pointer.  */
-tree frame_base_decl;
-
-/* Stack adjust caused by function prologue.  */
-static HOST_WIDE_INT frame_stack_adjust;
+/* Empty shared hashtable.  */
+static shared_hash empty_shared_hash;
 
 /* Local function prototypes.  */
 static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
@@ -278,7 +309,6 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
 static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
                                               HOST_WIDE_INT *);
 static void bb_stack_adjust_offset (basic_block);
-static HOST_WIDE_INT prologue_stack_adjust (void);
 static bool vt_stack_adjustments (void);
 static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
 static hashval_t variable_htab_hash (const void *);
@@ -292,37 +322,43 @@ 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);
+static variable unshare_variable (dataflow_set *set, variable var, 
+                                 enum var_init_status);
 static int vars_copy_1 (void **, void *);
 static void vars_copy (htab_t, htab_t);
-static void var_reg_delete_and_set (dataflow_set *, rtx);
-static void var_reg_delete (dataflow_set *, rtx);
+static tree var_debug_decl (tree);
+static void var_reg_set (dataflow_set *, rtx, enum var_init_status, rtx);
+static void var_reg_delete_and_set (dataflow_set *, rtx, bool, 
+                                   enum var_init_status, rtx);
+static void var_reg_delete (dataflow_set *, rtx, bool);
 static void var_regno_delete (dataflow_set *, int);
-static void var_mem_delete_and_set (dataflow_set *, rtx);
-static void var_mem_delete (dataflow_set *, rtx);
+static void var_mem_set (dataflow_set *, rtx, enum var_init_status, rtx);
+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);
 static int dataflow_set_different_1 (void **, void *);
-static int dataflow_set_different_2 (void **, void *);
 static bool dataflow_set_different (dataflow_set *, dataflow_set *);
 static void dataflow_set_destroy (dataflow_set *);
 
 static bool contains_symbol_ref (rtx);
 static bool track_expr_p (tree);
+static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
 static int count_uses (rtx *, void *);
 static void count_uses_1 (rtx *, void *);
-static void count_stores (rtx, rtx, void *);
+static void count_stores (rtx, const_rtx, void *);
 static int add_uses (rtx *, void *);
 static void add_uses_1 (rtx *, void *);
-static void add_stores (rtx, rtx, void *);
+static void add_stores (rtx, const_rtx, void *);
 static bool compute_bb_dataflow (basic_block);
 static void vt_find_locations (void);
 
@@ -332,9 +368,11 @@ 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 set_frame_base_location (dataflow_set *, rtx);
-static void set_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
+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, 
+                                  rtx);
 static void delete_variable_part (dataflow_set *, rtx, tree, HOST_WIDE_INT);
 static int emit_note_insn_var_location (void **, void *);
 static void emit_notes_for_changes (rtx, enum emit_note_where);
@@ -367,7 +405,7 @@ stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
       code = GET_CODE (src);
       if (! (code == PLUS || code == MINUS)
          || XEXP (src, 0) != stack_pointer_rtx
-         || GET_CODE (XEXP (src, 1)) != CONST_INT)
+         || !CONST_INT_P (XEXP (src, 1)))
        return;
 
       if (code == MINUS)
@@ -390,7 +428,7 @@ stack_adjust_offset_pre_post (rtx pattern, HOST_WIDE_INT *pre,
              rtx val = XEXP (XEXP (src, 1), 1);
              /* We handle only adjustments by constant amount.  */
              gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
-                         GET_CODE (val) == CONST_INT);
+                         CONST_INT_P (val));
              
              if (code == PRE_MODIFY)
                *pre -= INTVAL (val);
@@ -445,22 +483,31 @@ static void
 insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
                                   HOST_WIDE_INT *post)
 {
+  rtx pattern;
+
   *pre = 0;
   *post = 0;
 
-  if (GET_CODE (PATTERN (insn)) == SET)
-    stack_adjust_offset_pre_post (PATTERN (insn), pre, post);
-  else if (GET_CODE (PATTERN (insn)) == PARALLEL
-          || GET_CODE (PATTERN (insn)) == SEQUENCE)
+  pattern = PATTERN (insn);
+  if (RTX_FRAME_RELATED_P (insn))
+    {
+      rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
+      if (expr)
+       pattern = XEXP (expr, 0);
+    }
+
+  if (GET_CODE (pattern) == SET)
+    stack_adjust_offset_pre_post (pattern, pre, post);
+  else if (GET_CODE (pattern) == PARALLEL
+          || GET_CODE (pattern) == SEQUENCE)
     {
       int i;
 
       /* There may be stack adjustments inside compound insns.  Search
         for them.  */
-      for ( i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
-       if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET)
-         stack_adjust_offset_pre_post (XVECEXP (PATTERN (insn), 0, i),
-                                       pre, post);
+      for ( i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+       if (GET_CODE (XVECEXP (pattern, 0, i)) == SET)
+         stack_adjust_offset_pre_post (XVECEXP (pattern, 0, i), pre, post);
     }
 }
 
@@ -489,41 +536,9 @@ bb_stack_adjust_offset (basic_block bb)
   VTI (bb)->out.stack_adjust = offset;
 }
 
-/* Compute stack adjustment caused by function prologue.  */
-
-static HOST_WIDE_INT
-prologue_stack_adjust (void)
-{
-  HOST_WIDE_INT offset = 0;
-  basic_block bb = ENTRY_BLOCK_PTR->next_bb;
-  rtx insn;
-  rtx end;
-
-  if (!BB_END (bb))
-    return 0;
-
-  end = NEXT_INSN (BB_END (bb));
-  for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
-    {
-      if (NOTE_P (insn)
-         && NOTE_LINE_NUMBER (insn) == NOTE_INSN_PROLOGUE_END)
-       break;
-
-      if (INSN_P (insn))
-       {
-         HOST_WIDE_INT tmp;
-
-         insn_stack_adjust_offset_pre_post (insn, &tmp, &tmp);
-         offset += tmp;
-       }
-    }
-
-  return offset;
-}
-
 /* Compute stack adjustments for all blocks by traversing DFS tree.
    Return true when the adjustments on all incoming edges are consistent.
-   Heavily borrowed from flow_depth_first_order_compute.  */
+   Heavily borrowed from pre_and_rev_post_order_compute.  */
 
 static bool
 vt_stack_adjustments (void)
@@ -533,10 +548,10 @@ vt_stack_adjustments (void)
 
   /* Initialize entry block.  */
   VTI (ENTRY_BLOCK_PTR)->visited = true;
-  VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = frame_stack_adjust;
+  VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
 
   /* Allocate stack for back-tracking up CFG.  */
-  stack = xmalloc ((n_basic_blocks + 1) * sizeof (edge_iterator));
+  stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
   sp = 0;
 
   /* Push the first edge on to the stack.  */
@@ -587,27 +602,28 @@ vt_stack_adjustments (void)
   return true;
 }
 
-/* Adjust stack reference MEM by ADJUSTMENT bytes and return the new rtx.  */
+/* Adjust stack reference MEM by ADJUSTMENT bytes and make it relative
+   to the argument pointer.  Return the new rtx.  */
 
 static rtx
 adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
 {
-  rtx adjusted_mem;
-  rtx tmp;
+  rtx addr, cfa, tmp;
 
-  if (adjustment == 0)
-    return mem;
+#ifdef FRAME_POINTER_CFA_OFFSET
+  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
+  cfa = plus_constant (frame_pointer_rtx, adjustment);
+#else
+  adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
+  cfa = plus_constant (arg_pointer_rtx, adjustment);
+#endif
 
-  adjusted_mem = copy_rtx (mem);
-  XEXP (adjusted_mem, 0) = replace_rtx (XEXP (adjusted_mem, 0),
-                                       stack_pointer_rtx,
-                                       gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                                                     GEN_INT (adjustment)));
-  tmp = simplify_rtx (XEXP (adjusted_mem, 0));
+  addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
+  tmp = simplify_rtx (addr);
   if (tmp)
-    XEXP (adjusted_mem, 0) = tmp;
+    addr = tmp;
 
-  return adjusted_mem;
+  return replace_equiv_address_nv (mem, addr);
 }
 
 /* The hash function for variable_htab, computes the hash value
@@ -616,7 +632,7 @@ adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
 static hashval_t
 variable_htab_hash (const void *x)
 {
-  const variable v = (const variable) x;
+  const_variable const v = (const_variable) x;
 
   return (VARIABLE_HASH_VAL (v->decl));
 }
@@ -626,8 +642,8 @@ variable_htab_hash (const void *x)
 static int
 variable_htab_eq (const void *x, const void *y)
 {
-  const variable v = (const variable) x;
-  const tree decl = (const tree) y;
+  const_variable const v = (const_variable) x;
+  const_tree const decl = (const_tree) y;
 
   return (VARIABLE_HASH_VAL (v->decl) == VARIABLE_HASH_VAL (decl));
 }
@@ -703,7 +719,7 @@ attrs_list_insert (attrs *listp, tree decl, HOST_WIDE_INT offset, rtx loc)
 {
   attrs list;
 
-  list = pool_alloc (attrs_pool);
+  list = (attrs) pool_alloc (attrs_pool);
   list->loc = loc;
   list->decl = decl;
   list->offset = offset;
@@ -721,7 +737,7 @@ attrs_list_copy (attrs *dstp, attrs src)
   attrs_list_clear (dstp);
   for (; src; src = src->next)
     {
-      n = pool_alloc (attrs_pool);
+      n = (attrs) pool_alloc (attrs_pool);
       n->loc = src->loc;
       n->decl = src->decl;
       n->offset = src->offset;
@@ -742,29 +758,128 @@ 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)
+{
+  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)
 {
-  htab_empty (vars);
+  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.  */
 
 static variable
-unshare_variable (dataflow_set *set, variable var)
+unshare_variable (dataflow_set *set, variable var, 
+                 enum var_init_status initialized)
 {
   void **slot;
   variable new_var;
   int i;
 
-  new_var = pool_alloc (var_pool);
+  new_var = (variable) pool_alloc (var_pool);
   new_var->decl = var->decl;
   new_var->refcount = 1;
   var->refcount--;
   new_var->n_var_parts = var->n_var_parts;
 
+  if (! flag_var_tracking_uninit)
+    initialized = VAR_INIT_STATUS_INITIALIZED;
+
   for (i = 0; i < var->n_var_parts; i++)
     {
       location_chain node;
@@ -776,8 +891,16 @@ unshare_variable (dataflow_set *set, variable var)
        {
          location_chain new_lc;
 
-         new_lc = pool_alloc (loc_chain_pool);
+         new_lc = (location_chain) pool_alloc (loc_chain_pool);
          new_lc->next = NULL;
+         if (node->init > initialized)
+           new_lc->init = node->init;
+         else
+           new_lc->init = initialized;
+         if (node->set_src && !(MEM_P (node->set_src)))
+           new_lc->set_src = node->set_src;
+         else
+           new_lc->set_src = NULL;
          new_lc->loc = node->loc;
 
          *nextp = new_lc;
@@ -792,9 +915,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;
 }
@@ -825,21 +946,91 @@ 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.  */
+
+static inline tree
+var_debug_decl (tree decl)
+{
+  if (decl && DECL_P (decl)
+      && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
+      && DECL_P (DECL_DEBUG_EXPR (decl)))
+    decl = DECL_DEBUG_EXPR (decl);
+
+  return decl;
+}
+
+/* Set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
+
+static void
+var_reg_set (dataflow_set *set, rtx loc, enum var_init_status initialized, 
+            rtx set_src)
+{
+  tree decl = REG_EXPR (loc);
+  HOST_WIDE_INT offset = REG_OFFSET (loc);
+  attrs node;
+
+  decl = var_debug_decl (decl);
+
+  for (node = set->regs[REGNO (loc)]; node; node = node->next)
+    if (node->decl == decl && node->offset == offset)
+      break;
+  if (!node)
+    attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
+  set_variable_part (set, loc, decl, offset, initialized, set_src);
+}
+
+static enum var_init_status
+get_init_value (dataflow_set *set, rtx loc, tree decl)
+{
+  variable var;
+  int i;
+  enum var_init_status ret_val = VAR_INIT_STATUS_UNKNOWN;
+
+  if (! flag_var_tracking_uninit)
+    return VAR_INIT_STATUS_INITIALIZED;
+
+  var = shared_hash_find (set->vars, decl);
+  if (var)
+    {
+      for (i = 0; i < var->n_var_parts && ret_val == VAR_INIT_STATUS_UNKNOWN; i++)
+       {
+         location_chain nextp;
+         for (nextp = var->var_part[i].loc_chain; nextp; nextp = nextp->next)
+           if (rtx_equal_p (nextp->loc, loc))
+             {
+               ret_val = nextp->init;
+               break;
+             }
+       }
+    }
+
+  return ret_val;
 }
 
-/* Delete current content of register LOC in dataflow set SET
-   and set the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  */
+/* Delete current content of register LOC in dataflow set SET and set
+   the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
+   MODIFY is true, any other live copies of the same variable part are
+   also deleted from the dataflow set, otherwise the variable part is
+   assumed to be copied from another location holding the same
+   part.  */
 
 static void
-var_reg_delete_and_set (dataflow_set *set, rtx loc)
+var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify, 
+                       enum var_init_status initialized, rtx set_src)
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
   attrs node, next;
   attrs *nextp;
 
+  decl = var_debug_decl (decl);
+
+  if (initialized == VAR_INIT_STATUS_UNKNOWN)
+    initialized = get_init_value (set, loc, decl);
+
   nextp = &set->regs[REGNO (loc)];
   for (node = *nextp; node; node = next)
     {
@@ -856,19 +1047,31 @@ var_reg_delete_and_set (dataflow_set *set, rtx loc)
          nextp = &node->next;
        }
     }
-  if (set->regs[REGNO (loc)] == NULL)
-    attrs_list_insert (&set->regs[REGNO (loc)], decl, offset, loc);
-  set_variable_part (set, loc, decl, offset);
+  if (modify)
+    clobber_variable_part (set, loc, decl, offset, set_src);
+  var_reg_set (set, loc, initialized, set_src);
 }
 
-/* Delete current content of register LOC in dataflow set SET.  */
+/* Delete current content of register LOC in dataflow set SET.  If
+   CLOBBER is true, also delete any other live copies of the same
+   variable part.  */
 
 static void
-var_reg_delete (dataflow_set *set, rtx loc)
+var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
   attrs *reg = &set->regs[REGNO (loc)];
   attrs node, next;
 
+  if (clobber)
+    {
+      tree decl = REG_EXPR (loc);
+      HOST_WIDE_INT offset = REG_OFFSET (loc);
+
+      decl = var_debug_decl (decl);
+
+      clobber_variable_part (set, NULL, decl, offset, NULL);
+    }
+
   for (node = *reg; node; node = next)
     {
       next = node->next;
@@ -895,28 +1098,59 @@ var_regno_delete (dataflow_set *set, int regno)
   *reg = NULL;
 }
 
-/* Delete and set the location part of variable MEM_EXPR (LOC)
-   in dataflow set SET to LOC.
+/* Set the location part of variable MEM_EXPR (LOC) in dataflow set
+   SET to LOC.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete_and_set (dataflow_set *set, rtx loc)
+var_mem_set (dataflow_set *set, rtx loc, enum var_init_status initialized, 
+            rtx set_src)
 {
   tree decl = MEM_EXPR (loc);
-  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+  HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+
+  decl = var_debug_decl (decl);
 
-  set_variable_part (set, loc, decl, offset);
+  set_variable_part (set, loc, decl, offset, initialized, set_src);
 }
 
-/* Delete the location part LOC from dataflow set SET.
+/* Delete and set the location part of variable MEM_EXPR (LOC) in
+   dataflow set SET to LOC.  If MODIFY is true, any other live copies
+   of the same variable part are also deleted from the dataflow set,
+   otherwise the variable part is assumed to be copied from another
+   location holding the same part.
    Adjust the address first if it is stack pointer based.  */
 
 static void
-var_mem_delete (dataflow_set *set, rtx loc)
+var_mem_delete_and_set (dataflow_set *set, rtx loc, bool modify, 
+                       enum var_init_status initialized, rtx set_src)
 {
   tree decl = MEM_EXPR (loc);
-  HOST_WIDE_INT offset = MEM_OFFSET (loc) ? INTVAL (MEM_OFFSET (loc)) : 0;
+  HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+
+  decl = var_debug_decl (decl);
+
+  if (initialized == VAR_INIT_STATUS_UNKNOWN)
+    initialized = get_init_value (set, loc, decl);
+
+  if (modify)
+    clobber_variable_part (set, NULL, decl, offset, set_src);
+  var_mem_set (set, loc, initialized, set_src);
+}
+
+/* Delete the location part LOC from dataflow set SET.  If CLOBBER is
+   true, also delete any other live copies of the same variable part.
+   Adjust the address first if it is stack pointer based.  */
 
+static void
+var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
+{
+  tree decl = MEM_EXPR (loc);
+  HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
+
+  decl = var_debug_decl (decl);
+  if (clobber)
+    clobber_variable_part (set, NULL, decl, offset, NULL);
   delete_variable_part (set, loc, decl, offset);
 }
 
@@ -924,11 +1158,10 @@ var_mem_delete (dataflow_set *set, rtx loc)
    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;
 }
 
@@ -942,7 +1175,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.  */
@@ -955,7 +1189,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;
 }
 
@@ -969,18 +1204,23 @@ struct variable_union_info
   /* The sum of positions in the input chains.  */
   int pos;
 
-  /* The position in the chains of SRC and DST dataflow sets.  */
-  int pos_src;
+  /* The position in the chain of DST dataflow set.  */
   int pos_dst;
 };
 
+/* Buffer for location list sorting and its allocated size.  */
+static struct variable_union_info *vui_vec;
+static int vui_allocated;
+
 /* Compare function for qsort, order the structures by POS element.  */
 
 static int
 variable_union_info_cmp_pos (const void *n1, const void *n2)
 {
-  const struct variable_union_info *i1 = n1;
-  const struct variable_union_info *i2 = n2;
+  const struct variable_union_info *const i1 =
+    (const struct variable_union_info *) n1;
+  const struct variable_union_info *const i2 =
+    ( const struct variable_union_info *) n2;
 
   if (i1->pos != i2->pos)
     return i1->pos - i2->pos;
@@ -1001,15 +1241,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++;
 
@@ -1028,15 +1267,24 @@ variable_union (void **slot, void *data)
            }
        }
       if (k < src->n_var_parts)
-       unshare_variable (set, src);
+       {
+         if (dstp)
+           *dstp = (void *) src;
+         unshare_variable (set, src, VAR_INIT_STATUS_UNKNOWN);
+       }
       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);
 
@@ -1061,8 +1309,9 @@ 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)
-    dst = unshare_variable (set, dst);
+  if ((dst->refcount > 1 || shared_hash_shared (set->vars))
+      && dst->n_var_parts != k)
+    dst = unshare_variable (set, dst, VAR_INIT_STATUS_UNKNOWN);
 
   i = src->n_var_parts - 1;
   j = dst->n_var_parts - 1;
@@ -1085,7 +1334,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;
@@ -1095,10 +1344,14 @@ variable_union (void **slot, void *data)
                         && REG_P (node->loc)
                         && REGNO (node2->loc) == REGNO (node->loc))
                        || rtx_equal_p (node2->loc, node->loc)))
-                   break;
+                   {
+                     if (node2->init < node->init)
+                       node2->init = node->init;
+                     break;
+                   }
                }
              if (node || node2)
-               dst = unshare_variable (set, dst);
+               dst = unshare_variable (set, dst, VAR_INIT_STATUS_UNKNOWN);
            }
 
          src_l = 0;
@@ -1107,65 +1360,152 @@ variable_union (void **slot, void *data)
          dst_l = 0;
          for (node = dst->var_part[j].loc_chain; node; node = node->next)
            dst_l++;
-         vui = xcalloc (src_l + dst_l, sizeof (struct variable_union_info));
 
-         /* Fill in the locations from DST.  */
-         for (node = dst->var_part[j].loc_chain, jj = 0; node;
-              node = node->next, jj++)
+         if (dst_l == 1)
            {
-             vui[jj].lc = node;
-             vui[jj].pos_dst = jj;
-
-             /* Value larger than a sum of 2 valid positions.  */
-             vui[jj].pos_src = src_l + dst_l;
+             /* The most common case, much simpler, no qsort is needed.  */
+             location_chain dstnode = dst->var_part[j].loc_chain;
+             dst->var_part[k].loc_chain = dstnode;
+             dst->var_part[k].offset = dst->var_part[j].offset;
+             node2 = dstnode;
+             for (node = src->var_part[i].loc_chain; node; node = node->next)
+               if (!((REG_P (dstnode->loc)
+                      && REG_P (node->loc)
+                      && REGNO (dstnode->loc) == REGNO (node->loc))
+                     || rtx_equal_p (dstnode->loc, node->loc)))
+                 {
+                   location_chain new_node;
+
+                   /* Copy the location from SRC.  */
+                   new_node = (location_chain) pool_alloc (loc_chain_pool);
+                   new_node->loc = node->loc;
+                   new_node->init = node->init;
+                   if (!node->set_src || MEM_P (node->set_src))
+                     new_node->set_src = NULL;
+                   else
+                     new_node->set_src = node->set_src;
+                   node2->next = new_node;
+                   node2 = new_node;
+                 }
+             node2->next = NULL;
            }
-
-         /* Fill in the locations from SRC.  */
-         n = dst_l;
-         for (node = src->var_part[i].loc_chain, ii = 0; node;
-              node = node->next, ii++)
+         else
            {
-             /* Find location from NODE.  */
-             for (jj = 0; jj < dst_l; jj++)
-               {
-                 if ((REG_P (vui[jj].lc->loc)
-                      && REG_P (node->loc)
-                      && REGNO (vui[jj].lc->loc) == REGNO (node->loc))
-                     || rtx_equal_p (vui[jj].lc->loc, node->loc))
-                   {
-                     vui[jj].pos_src = ii;
-                     break;
-                   }
-               }
-             if (jj >= dst_l)  /* The location has not been found.  */
+             if (src_l + dst_l > vui_allocated)
                {
-                 location_chain new_node;
-
-                 /* Copy the location from SRC.  */
-                 new_node = pool_alloc (loc_chain_pool);
-                 new_node->loc = node->loc;
-                 vui[n].lc = new_node;
-                 vui[n].pos_src = ii;
-                 vui[n].pos_dst = src_l + dst_l;
-                 n++;
+                 vui_allocated = MAX (vui_allocated * 2, src_l + dst_l);
+                 vui_vec = XRESIZEVEC (struct variable_union_info, vui_vec,
+                                       vui_allocated);
                }
-           }
+             vui = vui_vec;
 
-         for (ii = 0; ii < src_l + dst_l; ii++)
-           vui[ii].pos = vui[ii].pos_src + vui[ii].pos_dst;
+             /* Fill in the locations from DST.  */
+             for (node = dst->var_part[j].loc_chain, jj = 0; node;
+                  node = node->next, jj++)
+               {
+                 vui[jj].lc = node;
+                 vui[jj].pos_dst = jj;
 
-         qsort (vui, n, sizeof (struct variable_union_info),
-                variable_union_info_cmp_pos);
+                 /* Pos plus value larger than a sum of 2 valid positions.  */
+                 vui[jj].pos = jj + src_l + dst_l;
+               }
 
-         /* Reconnect the nodes in sorted order.  */
-         for (ii = 1; ii < n; ii++)
-           vui[ii - 1].lc->next = vui[ii].lc;
-         vui[n - 1].lc->next = NULL;
+             /* Fill in the locations from SRC.  */
+             n = dst_l;
+             for (node = src->var_part[i].loc_chain, ii = 0; node;
+                  node = node->next, ii++)
+               {
+                 /* Find location from NODE.  */
+                 for (jj = 0; jj < dst_l; jj++)
+                   {
+                     if ((REG_P (vui[jj].lc->loc)
+                          && REG_P (node->loc)
+                          && REGNO (vui[jj].lc->loc) == REGNO (node->loc))
+                         || rtx_equal_p (vui[jj].lc->loc, node->loc))
+                       {
+                         vui[jj].pos = jj + ii;
+                         break;
+                       }
+                   }
+                 if (jj >= dst_l)      /* The location has not been found.  */
+                   {
+                     location_chain new_node;
+
+                     /* Copy the location from SRC.  */
+                     new_node = (location_chain) pool_alloc (loc_chain_pool);
+                     new_node->loc = node->loc;
+                     new_node->init = node->init;
+                     if (!node->set_src || MEM_P (node->set_src))
+                       new_node->set_src = NULL;
+                     else
+                       new_node->set_src = node->set_src;
+                     vui[n].lc = new_node;
+                     vui[n].pos_dst = src_l + dst_l;
+                     vui[n].pos = ii + src_l + dst_l;
+                     n++;
+                   }
+               }
 
-         dst->var_part[k].loc_chain = vui[0].lc;
-         dst->var_part[k].offset = dst->var_part[j].offset;
+             if (dst_l == 2)
+               {
+                 /* Special case still very common case.  For dst_l == 2
+                    all entries dst_l ... n-1 are sorted, with for i >= dst_l
+                    vui[i].pos == i + src_l + dst_l.  */
+                 if (vui[0].pos > vui[1].pos)
+                   {
+                     /* Order should be 1, 0, 2... */
+                     dst->var_part[k].loc_chain = vui[1].lc;
+                     vui[1].lc->next = vui[0].lc;
+                     if (n >= 3)
+                       {
+                         vui[0].lc->next = vui[2].lc;
+                         vui[n - 1].lc->next = NULL;
+                       }
+                     else
+                       vui[0].lc->next = NULL;
+                     ii = 3;
+                   }
+                 else
+                   {
+                     dst->var_part[k].loc_chain = vui[0].lc;
+                     if (n >= 3 && vui[2].pos < vui[1].pos)
+                       {
+                         /* Order should be 0, 2, 1, 3... */
+                         vui[0].lc->next = vui[2].lc;
+                         vui[2].lc->next = vui[1].lc;
+                         if (n >= 4)
+                           {
+                             vui[1].lc->next = vui[3].lc;
+                             vui[n - 1].lc->next = NULL;
+                           }
+                         else
+                           vui[1].lc->next = NULL;
+                         ii = 4;
+                       }
+                     else
+                       {
+                         /* Order should be 0, 1, 2... */
+                         ii = 1;
+                         vui[n - 1].lc->next = NULL;
+                       }
+                   }
+                 for (; ii < n; ii++)
+                   vui[ii - 1].lc->next = vui[ii].lc;
+               }
+             else
+               {
+                 qsort (vui, n, sizeof (struct variable_union_info),
+                        variable_union_info_cmp_pos);
+
+                 /* Reconnect the nodes in sorted order.  */
+                 for (ii = 1; ii < n; ii++)
+                   vui[ii - 1].lc->next = vui[ii].lc;
+                 vui[n - 1].lc->next = NULL;
+                 dst->var_part[k].loc_chain = vui[0].lc;
+               }
 
-         free (vui);
+             dst->var_part[k].offset = dst->var_part[j].offset;
+           }
          i--;
          j--;
        }
@@ -1188,8 +1528,13 @@ variable_union (void **slot, void *data)
            {
              location_chain new_lc;
 
-             new_lc = pool_alloc (loc_chain_pool);
+             new_lc = (location_chain) pool_alloc (loc_chain_pool);
              new_lc->next = NULL;
+             new_lc->init = node->init;
+             if (!node->set_src || MEM_P (node->set_src))
+               new_lc->set_src = NULL;
+             else
+               new_lc->set_src = node->set_src;
              new_lc->loc = node->loc;
 
              *nextp = new_lc;
@@ -1208,10 +1553,56 @@ variable_union (void **slot, void *data)
        dst->var_part[k].cur_loc = NULL;
     }
 
+  if (flag_var_tracking_uninit)
+    for (i = 0; i < src->n_var_parts && i < dst->n_var_parts; i++)
+      {
+       location_chain node, node2;
+       for (node = src->var_part[i].loc_chain; node; node = node->next)
+         for (node2 = dst->var_part[i].loc_chain; node2; node2 = node2->next)
+           if (rtx_equal_p (node->loc, node2->loc))
+             {
+               if (node->init > node2->init)
+                 node2->init = node->init;
+             }
+      }
+
   /* Continue traversing the hash table.  */
   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)
+    unshare_variable (set, src, VAR_INIT_STATUS_UNKNOWN);
+  return 1;
+}
+
 /* Compute union of dataflow sets SRC and DST and store it to DST.  */
 
 static void
@@ -1222,7 +1613,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.  */
@@ -1300,7 +1698,7 @@ dataflow_set_different_1 (void **slot, void *data)
   variable var1, var2;
 
   var1 = *(variable *) slot;
-  var2 = htab_find_with_hash (htab, var1->decl,
+  var2 = (variable) htab_find_with_hash (htab, var1->decl,
                              VARIABLE_HASH_VAL (var1->decl));
   if (!var2)
     {
@@ -1322,49 +1720,25 @@ dataflow_set_different_1 (void **slot, void *data)
   return 1;
 }
 
-/* Compare variable *SLOT with the same variable in hash table DATA
-   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
-
-static int
-dataflow_set_different_2 (void **slot, void *data)
-{
-  htab_t htab = (htab_t) data;
-  variable var1, var2;
-
-  var1 = *(variable *) slot;
-  var2 = htab_find_with_hash (htab, var1->decl,
-                             VARIABLE_HASH_VAL (var1->decl));
-  if (!var2)
-    {
-      dataflow_set_different_value = true;
-
-      /* Stop traversing the hash table.  */
-      return 0;
-    }
-
-  /* If both variables are defined they have been already checked for
-     equivalence.  */
-  gcc_assert (!variable_different_p (var1, var2, false));
-
-  /* Continue traversing the hash table.  */
-  return 1;
-}
-
 /* Return true if dataflow sets OLD_SET and NEW_SET differ.  */
 
 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);
-  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 (old_set->vars), dataflow_set_different_1,
+                shared_hash_htab (new_set->vars));
+  /* No need to traverse the second hashtab, if both have the same number
+     of elements and the second one had all entries found in the first one,
+     then it can't have any extra entries.  */
   return dataflow_set_different_value;
 }
 
@@ -1378,7 +1752,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;
 }
 
@@ -1479,7 +1853,8 @@ track_expr_p (tree expr)
   if (MEM_P (decl_rtl))
     {
       /* Do not track structures and arrays.  */
-      if (GET_MODE (decl_rtl) == BLKmode)
+      if (GET_MODE (decl_rtl) == BLKmode
+         || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
        return 0;
       if (MEM_SIZE (decl_rtl)
          && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
@@ -1489,6 +1864,127 @@ track_expr_p (tree expr)
   return 1;
 }
 
+/* Determine whether a given LOC refers to the same variable part as
+   EXPR+OFFSET.  */
+
+static bool
+same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
+{
+  tree expr2;
+  HOST_WIDE_INT offset2;
+
+  if (! DECL_P (expr))
+    return false;
+
+  if (REG_P (loc))
+    {
+      expr2 = REG_EXPR (loc);
+      offset2 = REG_OFFSET (loc);
+    }
+  else if (MEM_P (loc))
+    {
+      expr2 = MEM_EXPR (loc);
+      offset2 = INT_MEM_OFFSET (loc);
+    }
+  else
+    return false;
+
+  if (! expr2 || ! DECL_P (expr2))
+    return false;
+
+  expr = var_debug_decl (expr);
+  expr2 = var_debug_decl (expr2);
+
+  return (expr == expr2 && offset == offset2);
+}
+
+/* LOC is a REG or MEM that we would like to track if possible.
+   If EXPR is null, we don't know what expression LOC refers to,
+   otherwise it refers to EXPR + OFFSET.  STORE_REG_P is true if
+   LOC is an lvalue register.
+
+   Return true if EXPR is nonnull and if LOC, or some lowpart of it,
+   is something we can track.  When returning true, store the mode of
+   the lowpart we can track in *MODE_OUT (if nonnull) and its offset
+   from EXPR in *OFFSET_OUT (if nonnull).  */
+
+static bool
+track_loc_p (rtx loc, tree expr, HOST_WIDE_INT offset, bool store_reg_p,
+            enum machine_mode *mode_out, HOST_WIDE_INT *offset_out)
+{
+  enum machine_mode mode;
+
+  if (expr == NULL || !track_expr_p (expr))
+    return false;
+
+  /* If REG was a paradoxical subreg, its REG_ATTRS will describe the
+     whole subreg, but only the old inner part is really relevant.  */
+  mode = GET_MODE (loc);
+  if (REG_P (loc) && !HARD_REGISTER_NUM_P (ORIGINAL_REGNO (loc)))
+    {
+      enum machine_mode pseudo_mode;
+
+      pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (loc));
+      if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (pseudo_mode))
+       {
+         offset += byte_lowpart_offset (pseudo_mode, mode);
+         mode = pseudo_mode;
+       }
+    }
+
+  /* If LOC is a paradoxical lowpart of EXPR, refer to EXPR itself.
+     Do the same if we are storing to a register and EXPR occupies
+     the whole of register LOC; in that case, the whole of EXPR is
+     being changed.  We exclude complex modes from the second case
+     because the real and imaginary parts are represented as separate
+     pseudo registers, even if the whole complex value fits into one
+     hard register.  */
+  if ((GET_MODE_SIZE (mode) > GET_MODE_SIZE (DECL_MODE (expr))
+       || (store_reg_p
+          && !COMPLEX_MODE_P (DECL_MODE (expr))
+          && hard_regno_nregs[REGNO (loc)][DECL_MODE (expr)] == 1))
+      && offset + byte_lowpart_offset (DECL_MODE (expr), mode) == 0)
+    {
+      mode = DECL_MODE (expr);
+      offset = 0;
+    }
+
+  if (offset < 0 || offset >= MAX_VAR_PARTS)
+    return false;
+
+  if (mode_out)
+    *mode_out = mode;
+  if (offset_out)
+    *offset_out = offset;
+  return true;
+}
+
+/* Return the MODE lowpart of LOC, or null if LOC is not something we
+   want to track.  When returning nonnull, make sure that the attributes
+   on the returned value are updated.  */
+
+static rtx
+var_lowpart (enum machine_mode mode, rtx loc)
+{
+  unsigned int offset, reg_offset, regno;
+
+  if (!REG_P (loc) && !MEM_P (loc))
+    return NULL;
+
+  if (GET_MODE (loc) == mode)
+    return loc;
+
+  offset = byte_lowpart_offset (mode, GET_MODE (loc));
+
+  if (MEM_P (loc))
+    return adjust_address_nv (loc, mode, offset);
+
+  reg_offset = subreg_lowpart_offset (mode, GET_MODE (loc));
+  regno = REGNO (loc) + subreg_regno_offset (REGNO (loc), GET_MODE (loc),
+                                            reg_offset, mode);
+  return gen_rtx_REG_offset (loc, mode, regno, offset);
+}
+
 /* Count uses (register and memory references) LOC which will be tracked.
    INSN is instruction which the LOC is part of.  */
 
@@ -1503,8 +1999,8 @@ count_uses (rtx *loc, void *insn)
       VTI (bb)->n_mos++;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, NULL, NULL))
     {
       VTI (bb)->n_mos++;
     }
@@ -1524,7 +2020,7 @@ count_uses_1 (rtx *x, void *insn)
    INSN is instruction which the LOC is part of.  */
 
 static void
-count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn)
+count_stores (rtx loc, const_rtx expr ATTRIBUTE_UNUSED, void *insn)
 {
   count_uses (&loc, insn);
 }
@@ -1535,25 +2031,35 @@ count_stores (rtx loc, rtx expr ATTRIBUTE_UNUSED, void *insn)
 static int
 add_uses (rtx *loc, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (*loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = ((REG_EXPR (*loc) && track_expr_p (REG_EXPR (*loc)))
-                 ? MO_USE : MO_USE_NO_VAR);
-      mo->u.loc = *loc;
+      if (track_loc_p (*loc, REG_EXPR (*loc), REG_OFFSET (*loc),
+                      false, &mode, NULL))
+       {
+         mo->type = MO_USE;
+         mo->u.loc = var_lowpart (mode, *loc);
+       }
+      else
+       {
+         mo->type = MO_USE_NO_VAR;
+         mo->u.loc = *loc;
+       }
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       mo->type = MO_USE;
-      mo->u.loc = *loc;
+      mo->u.loc = var_lowpart (mode, *loc);
       mo->insn = (rtx) insn;
     }
 
@@ -1573,32 +2079,150 @@ add_uses_1 (rtx *x, void *insn)
    INSN is instruction which the LOC is part of.  */
 
 static void
-add_stores (rtx loc, rtx expr, void *insn)
+add_stores (rtx loc, const_rtx expr, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = ((GET_CODE (expr) != CLOBBER && REG_EXPR (loc)
-                  && track_expr_p (REG_EXPR (loc)))
-                 ? MO_SET : MO_CLOBBER);
-      mo->u.loc = loc;
+      if (GET_CODE (expr) == CLOBBER
+         || !track_loc_p (loc, REG_EXPR (loc), REG_OFFSET (loc),
+                          true, &mode, NULL))
+       {
+         mo->type = MO_CLOBBER;
+         mo->u.loc = loc;
+       }
+      else
+       {
+         rtx src = NULL;
+
+         if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
+           src = var_lowpart (mode, SET_SRC (expr));
+         loc = var_lowpart (mode, loc);
+
+         if (src == NULL)
+           {
+             mo->type = MO_SET;
+             mo->u.loc = loc;
+           }
+         else
+           {
+             if (SET_SRC (expr) != src)
+               expr = gen_rtx_SET (VOIDmode, loc, src);
+             if (same_variable_part_p (src, REG_EXPR (loc), REG_OFFSET (loc)))
+               mo->type = MO_COPY;
+             else
+               mo->type = MO_SET;
+             mo->u.loc = CONST_CAST_RTX (expr);
+           }
+       }
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (loc)
-          && MEM_EXPR (loc)
-          && track_expr_p (MEM_EXPR (loc)))
+          && track_loc_p (loc, MEM_EXPR (loc), INT_MEM_OFFSET (loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      mo->type = GET_CODE (expr) == CLOBBER ? MO_CLOBBER : MO_SET;
-      mo->u.loc = loc;
+      if (GET_CODE (expr) == CLOBBER)
+       {
+         mo->type = MO_CLOBBER;
+         mo->u.loc = var_lowpart (mode, loc);
+       }
+      else
+       {
+         rtx src = NULL;
+
+         if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
+           src = var_lowpart (mode, SET_SRC (expr));
+         loc = var_lowpart (mode, loc);
+
+         if (src == NULL)
+           {
+             mo->type = MO_SET;
+             mo->u.loc = loc;
+           }
+         else
+           {
+             if (SET_SRC (expr) != src)
+               expr = gen_rtx_SET (VOIDmode, loc, src);
+             if (same_variable_part_p (SET_SRC (expr),
+                                       MEM_EXPR (loc),
+                                       INT_MEM_OFFSET (loc)))
+               mo->type = MO_COPY;
+             else
+               mo->type = MO_SET;
+             mo->u.loc = CONST_CAST_RTX (expr);
+           }
+       }
       mo->insn = (rtx) insn;
     }
 }
 
+static enum var_init_status
+find_src_status (dataflow_set *in, rtx src)
+{
+  tree decl = NULL_TREE;
+  enum var_init_status status = VAR_INIT_STATUS_UNINITIALIZED;
+
+  if (! flag_var_tracking_uninit)
+    status = VAR_INIT_STATUS_INITIALIZED;
+
+  if (src && REG_P (src))
+    decl = var_debug_decl (REG_EXPR (src));
+  else if (src && MEM_P (src))
+    decl = var_debug_decl (MEM_EXPR (src));
+
+  if (src && decl)
+    status = get_init_value (in, src, decl);
+
+  return status;
+}
+
+/* SRC is the source of an assignment.  Use SET to try to find what
+   was ultimately assigned to SRC.  Return that value if known,
+   otherwise return SRC itself.  */
+
+static rtx
+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".      */
+  variable var;
+  location_chain nextp;
+  int i;
+  bool found;
+
+  if (src && REG_P (src))
+    decl = var_debug_decl (REG_EXPR (src));
+  else if (src && MEM_P (src))
+    decl = var_debug_decl (MEM_EXPR (src));
+
+  if (src && decl)
+    {
+      var = shared_hash_find (set->vars, decl);
+      if (var)
+       {
+         found = false;
+         for (i = 0; i < var->n_var_parts && !found; i++)
+           for (nextp = var->var_part[i].loc_chain; nextp && !found; 
+                nextp = nextp->next)
+             if (rtx_equal_p (nextp->loc, src))
+               {
+                 set_src = nextp->set_src;
+                 found = true;
+               }
+             
+       }
+    }
+
+  return set_src;
+}
+
 /* Compute the changes of variable locations in the basic block BB.  */
 
 static bool
@@ -1610,7 +2234,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);
 
@@ -1626,39 +2250,92 @@ compute_bb_dataflow (basic_block bb)
            break;
 
          case MO_USE:
+           {
+             rtx loc = VTI (bb)->mos[i].u.loc;
+
+             if (REG_P (loc))
+               var_reg_set (out, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
+             else if (MEM_P (loc))
+               var_mem_set (out, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
+           }
+           break;
+
          case MO_SET:
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx set_src = NULL;
+
+             if (GET_CODE (loc) == SET)
+               {
+                 set_src = SET_SRC (loc);
+                 loc = SET_DEST (loc);
+               }
 
              if (REG_P (loc))
-               var_reg_delete_and_set (out, loc);
+               var_reg_delete_and_set (out, loc, true, VAR_INIT_STATUS_INITIALIZED,
+                                       set_src);
              else if (MEM_P (loc))
-               var_mem_delete_and_set (out, loc);
+               var_mem_delete_and_set (out, loc, true, VAR_INIT_STATUS_INITIALIZED,
+                                       set_src);
+           }
+           break;
+
+         case MO_COPY:
+           {
+             rtx loc = VTI (bb)->mos[i].u.loc;
+             enum var_init_status src_status;
+             rtx set_src = NULL;
+
+             if (GET_CODE (loc) == SET)
+               {
+                 set_src = SET_SRC (loc);
+                 loc = SET_DEST (loc);
+               }
+
+             if (! flag_var_tracking_uninit)
+               src_status = VAR_INIT_STATUS_INITIALIZED;
+             else
+               {
+                 src_status = find_src_status (in, set_src);
+
+                 if (src_status == VAR_INIT_STATUS_UNKNOWN)
+                   src_status = find_src_status (out, set_src);
+               }
+
+             set_src = find_src_set_src (in, set_src);
+
+             if (REG_P (loc))
+               var_reg_delete_and_set (out, loc, false, src_status, set_src);
+             else if (MEM_P (loc))
+               var_mem_delete_and_set (out, loc, false, src_status, set_src);
            }
            break;
 
          case MO_USE_NO_VAR:
-         case MO_CLOBBER:
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
 
              if (REG_P (loc))
-               var_reg_delete (out, loc);
+               var_reg_delete (out, loc, false);
              else if (MEM_P (loc))
-               var_mem_delete (out, loc);
+               var_mem_delete (out, loc, false);
            }
            break;
 
-         case MO_ADJUST:
+         case MO_CLOBBER:
            {
-             rtx base;
+             rtx loc = VTI (bb)->mos[i].u.loc;
 
-             out->stack_adjust += VTI (bb)->mos[i].u.adjust;
-             base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
-                                                       out->stack_adjust));
-             set_frame_base_location (out, base);
+             if (REG_P (loc))
+               var_reg_delete (out, loc, true);
+             else if (MEM_P (loc))
+               var_mem_delete (out, loc, true);
            }
            break;
+
+         case MO_ADJUST:
+           out->stack_adjust += VTI (bb)->mos[i].u.adjust;
+           break;
        }
     }
 
@@ -1682,10 +2359,10 @@ vt_find_locations (void)
 
   /* Compute reverse completion order of depth first search of the CFG
      so that the data-flow runs faster.  */
-  rc_order = xmalloc (n_basic_blocks * sizeof (int));
-  bb_order = xmalloc (last_basic_block * sizeof (int));
-  flow_depth_first_order_compute (NULL, rc_order);
-  for (i = 0; i < n_basic_blocks; i++)
+  rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
+  bb_order = XNEWVEC (int, last_basic_block);
+  pre_and_rev_post_order_compute (NULL, rc_order, false);
+  for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++)
     bb_order[rc_order[i]] = i;
   free (rc_order);
 
@@ -1713,7 +2390,7 @@ vt_find_locations (void)
 
       while (!fibheap_empty (worklist))
        {
-         bb = fibheap_extract_min (worklist);
+         bb = (basic_block) fibheap_extract_min (worklist);
          RESET_BIT (in_worklist, bb->index);
          if (!TEST_BIT (visited, bb->index))
            {
@@ -1780,8 +2457,7 @@ dump_attrs_list (attrs list)
   for (; list; list = list->next)
     {
       print_mem_expr (dump_file, list->decl);
-      fprintf (dump_file, "+");
-      fprintf (dump_file, HOST_WIDE_INT_PRINT_DEC, list->offset);
+      fprintf (dump_file, "+" HOST_WIDE_INT_PRINT_DEC, list->offset);
     }
   fprintf (dump_file, "\n");
 }
@@ -1795,8 +2471,13 @@ dump_variable (void **slot, void *data ATTRIBUTE_UNUSED)
   int i;
   location_chain node;
 
-  fprintf (dump_file, "  name: %s\n",
+  fprintf (dump_file, "  name: %s",
           IDENTIFIER_POINTER (DECL_NAME (var->decl)));
+  if (dump_flags & TDF_UID)
+    fprintf (dump_file, " D.%u\n", DECL_UID (var->decl));
+  else
+    fprintf (dump_file, "\n");
+
   for (i = 0; i < var->n_var_parts; i++)
     {
       fprintf (dump_file, "    offset %ld\n",
@@ -1804,6 +2485,8 @@ dump_variable (void **slot, void *data ATTRIBUTE_UNUSED)
       for (node = var->var_part[i].loc_chain; node; node = node->next)
        {
          fprintf (dump_file, "      ");
+         if (node->init == VAR_INIT_STATUS_UNINITIALIZED)
+           fprintf (dump_file, "[uninit]");
          print_rtl_single (dump_file, node->loc);
        }
     }
@@ -1831,10 +2514,9 @@ dump_dataflow_set (dataflow_set *set)
 {
   int i;
 
-  fprintf (dump_file, "Stack adjustment: ");
-  fprintf (dump_file, HOST_WIDE_INT_PRINT_DEC, set->stack_adjust);
-  fprintf (dump_file, "\n");
-  for (i = 1; i < FIRST_PSEUDO_REGISTER; i++)
+  fprintf (dump_file, "Stack adjustment: " HOST_WIDE_INT_PRINT_DEC "\n",
+          set->stack_adjust);
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
       if (set->regs[i])
        {
@@ -1842,7 +2524,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");
 }
 
@@ -1864,10 +2546,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);
 
@@ -1878,63 +2560,74 @@ 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 = pool_alloc (var_pool);
+         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);
+           }
        }
     }
 }
 
-/* Set the location of frame_base_decl to LOC in dataflow set SET.  This
-   function expects that frame_base_decl has already one location for offset 0
-   in the variable table.  */
+/* Look for the index in VAR->var_part corresponding to OFFSET.
+   Return -1 if not found.  If INSERTION_POINT is non-NULL, the
+   referenced int will be set to the index that the part has or should
+   have, if it should be inserted.  */
 
-static void
-set_frame_base_location (dataflow_set *set, rtx loc)
+static inline int
+find_variable_location_part (variable var, HOST_WIDE_INT offset,
+                            int *insertion_point)
 {
-  variable var;
-  
-  var = htab_find_with_hash (set->vars, frame_base_decl,
-                            VARIABLE_HASH_VAL (frame_base_decl));
-  gcc_assert (var);
-  gcc_assert (var->n_var_parts == 1);
-  gcc_assert (!var->var_part[0].offset);
-  gcc_assert (var->var_part[0].loc_chain);
+  int pos, low, high;
+
+  /* Find the location part.  */
+  low = 0;
+  high = var->n_var_parts;
+  while (low != high)
+    {
+      pos = (low + high) / 2;
+      if (var->var_part[pos].offset < offset)
+       low = pos + 1;
+      else
+       high = pos;
+    }
+  pos = low;
+
+  if (insertion_point)
+    *insertion_point = pos;
 
-  /* If frame_base_decl is shared unshare it first.  */
-  if (var->refcount > 1)
-    var = unshare_variable (set, var);
+  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+    return pos;
 
-  var->var_part[0].loc_chain->loc = loc;
-  var->var_part[0].cur_loc = loc;
-  variable_was_changed (var, set->vars);
+  return -1;
 }
 
 /* Set the part of variable's location in the dataflow set SET.  The variable
@@ -1942,20 +2635,24 @@ set_frame_base_location (dataflow_set *set, rtx loc)
    part's location by LOC.  */
 
 static void
-set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
+set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset,
+                  enum var_init_status initialized, rtx set_src)
 {
-  int pos, low, high;
+  int pos;
   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 (! flag_var_tracking_uninit)
+    initialized = VAR_INIT_STATUS_INITIALIZED;
+
+  if (!slot || !*slot)
     {
+      if (!slot)
+       slot = shared_hash_find_slot_unshare (&set->vars, decl, INSERT);
       /* Create new variable information.  */
-      var = pool_alloc (var_pool);
+      var = (variable) pool_alloc (var_pool);
       var->decl = decl;
       var->refcount = 1;
       var->n_var_parts = 1;
@@ -1967,22 +2664,13 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
     }
   else
     {
+      int inspos = 0;
+
       var = (variable) *slot;
 
-      /* Find the location part.  */
-      low = 0;
-      high = var->n_var_parts;
-      while (low != high)
-       {
-         pos = (low + high) / 2;
-         if (var->var_part[pos].offset < offset)
-           low = pos + 1;
-         else
-           high = pos;
-       }
-      pos = low;
+      pos = find_variable_location_part (var, offset, &inspos);
 
-      if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+      if (pos >= 0)
        {
          node = var->var_part[pos].loc_chain;
 
@@ -1993,13 +2681,18 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
            {
              /* LOC is in the beginning of the chain so we have nothing
                 to do.  */
+             if (node->init < initialized)
+               node->init = initialized;
+             if (set_src != NULL)
+               node->set_src = set_src;
+
              return;
            }
          else
            {
              /* We have to make a copy of a shared variable.  */
-             if (var->refcount > 1)
-               var = unshare_variable (set, var);
+             if (var->refcount > 1 || shared_hash_shared (set->vars))
+               var = unshare_variable (set, var, initialized);
            }
        }
       else
@@ -2007,17 +2700,17 @@ 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)
-           var = unshare_variable (set, var);
+         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
             thus there are at most MAX_VAR_PARTS different offsets.  */
          gcc_assert (var->n_var_parts < MAX_VAR_PARTS);
 
-         /* We have to move the elements of array starting at index low to the
-            next position.  */
-         for (high = var->n_var_parts; high > low; high--)
-           var->var_part[high] = var->var_part[high - 1];
+         /* We have to move the elements of array starting at index
+            inspos to the next position.  */
+         for (pos = var->n_var_parts; pos > inspos; pos--)
+           var->var_part[pos] = var->var_part[pos - 1];
 
          var->n_var_parts++;
          var->var_part[pos].offset = offset;
@@ -2035,6 +2728,12 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
           && REGNO (node->loc) == REGNO (loc))
          || rtx_equal_p (node->loc, loc))
        {
+         /* Save these values, to assign to the new node, before
+            deleting this one.  */
+         if (node->init > initialized)
+           initialized = node->init;
+         if (node->set_src != NULL && set_src == NULL)
+           set_src = node->set_src;
          pool_free (loc_chain_pool, node);
          *nextp = next;
          break;
@@ -2044,8 +2743,10 @@ set_variable_part (dataflow_set *set, rtx loc, tree decl, HOST_WIDE_INT offset)
     }
 
   /* Add the location to the beginning.  */
-  node = pool_alloc (loc_chain_pool);
+  node = (location_chain) pool_alloc (loc_chain_pool);
   node->loc = loc;
+  node->init = initialized;
+  node->set_src = set_src;
   node->next = var->var_part[pos].loc_chain;
   var->var_part[pos].loc_chain = node;
 
@@ -2053,7 +2754,72 @@ 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);
+    }
+}
+
+/* Remove all recorded register locations for the given variable part
+   from dataflow set SET, except for those that are identical to loc.
+   The variable part is specified by variable's declaration DECL and
+   offset OFFSET.  */
+
+static void
+clobber_variable_part (dataflow_set *set, rtx loc, tree decl,
+                      HOST_WIDE_INT offset, rtx set_src)
+{
+  variable var;
+
+  if (! decl || ! DECL_P (decl))
+    return;
+
+  var = shared_hash_find (set->vars, decl);
+  if (var)
+    {
+      int pos = find_variable_location_part (var, offset, NULL);
+
+      if (pos >= 0)
+       {
+         location_chain node, next;
+
+         /* Remove the register locations from the dataflow set.  */
+         next = var->var_part[pos].loc_chain;
+         for (node = next; node; node = next)
+           {
+             next = node->next;
+             if (node->loc != loc 
+                 && (!flag_var_tracking_uninit
+                     || !set_src 
+                     || MEM_P (set_src)
+                     || !rtx_equal_p (set_src, node->set_src)))
+               {
+                 if (REG_P (node->loc))
+                   {
+                     attrs anode, anext;
+                     attrs *anextp;
+
+                     /* Remove the variable part from the register's
+                        list, but preserve any other variable parts
+                        that might be regarded as live in that same
+                        register.  */
+                     anextp = &set->regs[REGNO (node->loc)];
+                     for (anode = *anextp; anode; anode = anext)
+                       {
+                         anext = anode->next;
+                         if (anode->decl == decl
+                             && anode->offset == offset)
+                           {
+                             pool_free (attrs_pool, anode);
+                             *anextp = anext;
+                           }
+                         else
+                           anextp = &anode->next;
+                       }
+                   }
+
+                 delete_variable_part (set, node->loc, decl, offset);
+               }
+           }
+       }
     }
 }
 
@@ -2065,35 +2831,18 @@ static void
 delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                      HOST_WIDE_INT offset)
 {
-  int pos, low, high;
-  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;
-
-      /* Find the location part.  */
-      low = 0;
-      high = var->n_var_parts;
-      while (low != high)
-       {
-         pos = (low + high) / 2;
-         if (var->var_part[pos].offset < offset)
-           low = pos + 1;
-         else
-           high = pos;
-       }
-      pos = low;
+      int pos = find_variable_location_part (var, offset, NULL);
 
-      if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+      if (pos >= 0)
        {
          location_chain node, next;
          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.  */
@@ -2104,7 +2853,8 @@ delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                       && REGNO (node->loc) == REGNO (loc))
                      || rtx_equal_p (node->loc, loc))
                    {
-                     var = unshare_variable (set, var);
+                     var = unshare_variable (set, var,
+                                             VAR_INIT_STATUS_UNKNOWN);
                      break;
                    }
                }
@@ -2153,7 +2903,7 @@ delete_variable_part (dataflow_set *set, rtx loc, tree decl,
                }
            }
          if (changed)
-             variable_was_changed (var, set->vars);
+           variable_was_changed (var, set);
        }
     }
 }
@@ -2171,6 +2921,7 @@ emit_note_insn_var_location (void **varp, void *data)
   rtx note;
   int i, j, n_var_parts;
   bool complete;
+  enum var_init_status initialized = VAR_INIT_STATUS_UNINITIALIZED;
   HOST_WIDE_INT last_limit;
   tree type_size_unit;
   HOST_WIDE_INT offsets[MAX_VAR_PARTS];
@@ -2195,6 +2946,7 @@ emit_note_insn_var_location (void **varp, void *data)
       offsets[n_var_parts] = var->var_part[i].offset;
       loc[n_var_parts] = var->var_part[i].loc_chain->loc;
       mode = GET_MODE (loc[n_var_parts]);
+      initialized = var->var_part[i].loc_chain->init;
       last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
 
       /* Attempt to merge adjacent registers or memory.  */
@@ -2215,8 +2967,7 @@ emit_note_insn_var_location (void **varp, void *data)
          if (REG_P (loc[n_var_parts])
              && hard_regno_nregs[REGNO (loc[n_var_parts])][mode] * 2
                 == hard_regno_nregs[REGNO (loc[n_var_parts])][wider_mode]
-             && REGNO (loc[n_var_parts])
-                + hard_regno_nregs[REGNO (loc[n_var_parts])][mode]
+             && end_hard_regno (mode, REGNO (loc[n_var_parts]))
                 == REGNO (loc2))
            {
              if (! WORDS_BIG_ENDIAN && ! BYTES_BIG_ENDIAN)
@@ -2235,17 +2986,16 @@ emit_note_insn_var_location (void **varp, void *data)
            }
          else if (MEM_P (loc[n_var_parts])
                   && GET_CODE (XEXP (loc2, 0)) == PLUS
-                  && GET_CODE (XEXP (XEXP (loc2, 0), 0)) == REG
-                  && GET_CODE (XEXP (XEXP (loc2, 0), 1)) == CONST_INT)
+                  && REG_P (XEXP (XEXP (loc2, 0), 0))
+                  && CONST_INT_P (XEXP (XEXP (loc2, 0), 1)))
            {
-             if ((GET_CODE (XEXP (loc[n_var_parts], 0)) == REG
+             if ((REG_P (XEXP (loc[n_var_parts], 0))
                   && rtx_equal_p (XEXP (loc[n_var_parts], 0),
                                   XEXP (XEXP (loc2, 0), 0))
                   && INTVAL (XEXP (XEXP (loc2, 0), 1))
                      == GET_MODE_SIZE (mode))
                  || (GET_CODE (XEXP (loc[n_var_parts], 0)) == PLUS
-                     && GET_CODE (XEXP (XEXP (loc[n_var_parts], 0), 1))
-                        == CONST_INT
+                     && CONST_INT_P (XEXP (XEXP (loc[n_var_parts], 0), 1))
                      && rtx_equal_p (XEXP (XEXP (loc[n_var_parts], 0), 0),
                                      XEXP (XEXP (loc2, 0), 0))
                      && INTVAL (XEXP (XEXP (loc[n_var_parts], 0), 1))
@@ -2274,10 +3024,13 @@ emit_note_insn_var_location (void **varp, void *data)
   else
     note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
 
+  if (! flag_var_tracking_uninit)
+    initialized = VAR_INIT_STATUS_INITIALIZED;
+
   if (!complete)
     {
       NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
-                                                      NULL_RTX);
+                                                      NULL_RTX, (int) initialized);
     }
   else if (n_var_parts == 1)
     {
@@ -2285,7 +3038,8 @@ emit_note_insn_var_location (void **varp, void *data)
        = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
 
       NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
-                                                      expr_list);
+                                                      expr_list, 
+                                                      (int) initialized);
     }
   else if (n_var_parts)
     {
@@ -2298,19 +3052,12 @@ emit_note_insn_var_location (void **varp, void *data)
       parallel = gen_rtx_PARALLEL (VOIDmode,
                                   gen_rtvec_v (n_var_parts, loc));
       NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, var->decl,
-                                                      parallel);
+                                                      parallel, 
+                                                      (int) initialized);
     }
 
   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;
 }
@@ -2339,7 +3086,7 @@ emit_notes_for_differences_1 (void **slot, void *data)
   variable old_var, new_var;
 
   old_var = *(variable *) slot;
-  new_var = htab_find_with_hash (new_vars, old_var->decl,
+  new_var = (variable) htab_find_with_hash (new_vars, old_var->decl,
                                 VARIABLE_HASH_VAL (old_var->decl));
 
   if (!new_var)
@@ -2347,9 +3094,9 @@ emit_notes_for_differences_1 (void **slot, void *data)
       /* Variable has disappeared.  */
       variable empty_var;
 
-      empty_var = pool_alloc (var_pool);
+      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);
     }
@@ -2372,7 +3119,7 @@ emit_notes_for_differences_2 (void **slot, void *data)
   variable old_var, new_var;
 
   new_var = *(variable *) slot;
-  old_var = htab_find_with_hash (old_vars, new_var->decl,
+  old_var = (variable) htab_find_with_hash (old_vars, new_var->decl,
                                 VARIABLE_HASH_VAL (new_var->decl));
   if (!old_var)
     {
@@ -2391,8 +3138,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);
 }
 
@@ -2404,7 +3155,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++)
@@ -2427,50 +3178,93 @@ emit_notes_in_bb (basic_block bb)
            break;
 
          case MO_USE:
+           {
+             rtx loc = VTI (bb)->mos[i].u.loc;
+
+             if (REG_P (loc))
+               var_reg_set (&set, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
+             else
+               var_mem_set (&set, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
+
+             emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+           }
+           break;
+
          case MO_SET:
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx set_src = NULL;
+
+             if (GET_CODE (loc) == SET)
+               {
+                 set_src = SET_SRC (loc);
+                 loc = SET_DEST (loc);
+               }
 
              if (REG_P (loc))
-               var_reg_delete_and_set (&set, loc);
+               var_reg_delete_and_set (&set, loc, true, VAR_INIT_STATUS_INITIALIZED, 
+                                       set_src);
              else
-               var_mem_delete_and_set (&set, loc);
+               var_mem_delete_and_set (&set, loc, true, VAR_INIT_STATUS_INITIALIZED, 
+                                       set_src);
 
-             if (VTI (bb)->mos[i].type == MO_USE)
-               emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
+             emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
+           }
+           break;
+
+         case MO_COPY:
+           {
+             rtx loc = VTI (bb)->mos[i].u.loc;
+             enum var_init_status src_status;
+             rtx set_src = NULL;
+
+             if (GET_CODE (loc) == SET)
+               {
+                 set_src = SET_SRC (loc);
+                 loc = SET_DEST (loc);
+               }
+
+             src_status = find_src_status (&set, set_src);
+             set_src = find_src_set_src (&set, set_src);
+
+             if (REG_P (loc))
+               var_reg_delete_and_set (&set, loc, false, src_status, set_src);
              else
-               emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+               var_mem_delete_and_set (&set, loc, false, src_status, set_src);
+
+             emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
            }
            break;
 
          case MO_USE_NO_VAR:
-         case MO_CLOBBER:
            {
              rtx loc = VTI (bb)->mos[i].u.loc;
 
              if (REG_P (loc))
-               var_reg_delete (&set, loc);
+               var_reg_delete (&set, loc, false);
              else
-               var_mem_delete (&set, loc);
+               var_mem_delete (&set, loc, false);
 
-             if (VTI (bb)->mos[i].type == MO_USE_NO_VAR)
-               emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN);
-             else
-               emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+             emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
            }
            break;
 
-         case MO_ADJUST:
+         case MO_CLOBBER:
            {
-             rtx base;
+             rtx loc = VTI (bb)->mos[i].u.loc;
 
-             set.stack_adjust += VTI (bb)->mos[i].u.adjust;
-             base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
-                                                       set.stack_adjust));
-             set_frame_base_location (&set, base);
-             emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN);
+             if (REG_P (loc))
+               var_reg_delete (&set, loc, true);
+             else
+               var_mem_delete (&set, loc, true);
+
+             emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN);
            }
            break;
+
+         case MO_ADJUST:
+           set.stack_adjust += VTI (bb)->mos[i].u.adjust;
+           break;
        }
     }
   dataflow_set_destroy (&set);
@@ -2491,7 +3285,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)
@@ -2529,7 +3323,7 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
       if (MEM_ATTRS (rtl))
        {
          *declp = MEM_EXPR (rtl);
-         *offsetp = MEM_OFFSET (rtl) ? INTVAL (MEM_OFFSET (rtl)) : 0;
+         *offsetp = INT_MEM_OFFSET (rtl);
          return true;
        }
     }
@@ -2549,6 +3343,7 @@ vt_add_function_parameters (void)
       rtx decl_rtl = DECL_RTL_IF_SET (parm);
       rtx incoming = DECL_INCOMING_RTL (parm);
       tree decl;
+      enum machine_mode mode;
       HOST_WIDE_INT offset;
       dataflow_set *out;
 
@@ -2565,27 +3360,46 @@ vt_add_function_parameters (void)
        continue;
 
       if (!vt_get_decl_and_offset (incoming, &decl, &offset))
-       if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
-         continue;
+       {
+         if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
+           continue;
+         offset += byte_lowpart_offset (GET_MODE (incoming),
+                                        GET_MODE (decl_rtl));
+       }
 
       if (!decl)
        continue;
 
-      gcc_assert (parm == decl);
+      if (parm != decl)
+       {
+         /* Assume that DECL_RTL was a pseudo that got spilled to
+            memory.  The spill slot sharing code will force the
+            memory to reference spill_slot_decl (%sfp), so we don't
+            match above.  That's ok, the pseudo must have referenced
+            the entire parameter, so just reset OFFSET.  */
+         gcc_assert (decl == get_spill_slot_decl (false));
+         offset = 0;
+       }
+
+      if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+       continue;
 
-      incoming = eliminate_regs (incoming, 0, NULL_RTX);
       out = &VTI (ENTRY_BLOCK_PTR)->out;
 
       if (REG_P (incoming))
        {
+         incoming = var_lowpart (mode, incoming);
          gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
          attrs_list_insert (&out->regs[REGNO (incoming)],
                             parm, offset, incoming);
-         set_variable_part (out, incoming, parm, offset);
+         set_variable_part (out, incoming, parm, offset, VAR_INIT_STATUS_INITIALIZED, 
+                            NULL);
        }
       else if (MEM_P (incoming))
        {
-         set_variable_part (out, incoming, parm, offset);
+         incoming = var_lowpart (mode, incoming);
+         set_variable_part (out, incoming, parm, offset,
+                            VAR_INIT_STATUS_INITIALIZED, NULL);
        }
     }
 }
@@ -2603,7 +3417,7 @@ vt_initialize (void)
   FOR_EACH_BB (bb)
     {
       rtx insn;
-      HOST_WIDE_INT pre, post;
+      HOST_WIDE_INT pre, post = 0;
 
       /* Count the number of micro operations.  */
       VTI (bb)->n_mos = 0;
@@ -2628,8 +3442,7 @@ vt_initialize (void)
        }
 
       /* Add the micro-operations to the array.  */
-      VTI (bb)->mos = xmalloc (VTI (bb)->n_mos
-                              * sizeof (struct micro_operation_def));
+      VTI (bb)->mos = XNEWVEC (micro_operation, VTI (bb)->n_mos);
       VTI (bb)->n_mos = 0;
       for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
           insn = NEXT_INSN (insn))
@@ -2681,15 +3494,19 @@ vt_initialize (void)
                }
 
              n1 = VTI (bb)->n_mos;
+             /* This will record NEXT_INSN (insn), such that we can
+                insert notes before it without worrying about any
+                notes that MO_USEs might emit after the insn.  */
              note_stores (PATTERN (insn), add_stores, insn);
              n2 = VTI (bb)->n_mos - 1;
 
-             /* Order the MO_SETs to be before MO_CLOBBERs.  */
+             /* Order the MO_CLOBBERs to be before MO_SETs.  */
              while (n1 < n2)
                {
-                 while (n1 < n2 && VTI (bb)->mos[n1].type == MO_SET)
+                 while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
                    n1++;
-                 while (n1 < n2 && VTI (bb)->mos[n2].type == MO_CLOBBER)
+                 while (n1 < n2 && (VTI (bb)->mos[n2].type == MO_SET
+                                    || VTI (bb)->mos[n2].type == MO_COPY))
                    n2--;
                  if (n1 < n2)
                    {
@@ -2713,14 +3530,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",
@@ -2728,31 +3537,25 @@ 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);
-  vt_add_function_parameters ();
-
-  if (!frame_pointer_needed)
-    {
-      rtx base;
-
-      /* Create fake variable for tracking stack pointer changes.  */
-      frame_base_decl = make_node (VAR_DECL);
-      DECL_NAME (frame_base_decl) = get_identifier ("___frame_base_decl");
-      TREE_TYPE (frame_base_decl) = char_type_node;
-      DECL_ARTIFICIAL (frame_base_decl) = 1;
-      DECL_IGNORED_P (frame_base_decl) = 1;
+                                  variable_htab_free);
 
-      /* Set its initial "location".  */
-      frame_stack_adjust = -prologue_stack_adjust ();
-      base = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
-                                               frame_stack_adjust));
-      set_variable_part (&VTI (ENTRY_BLOCK_PTR)->out, base, frame_base_decl, 0);
-    }
-  else
+  /* Init the IN and OUT sets.  */
+  FOR_ALL_BB (bb)
     {
-      frame_base_decl = NULL;
+      VTI (bb)->visited = false;
+      dataflow_set_init (&VTI (bb)->in);
+      dataflow_set_init (&VTI (bb)->out);
     }
+
+  vt_add_function_parameters ();
 }
 
 /* Free the data structures needed for variable tracking.  */
@@ -2773,19 +3576,25 @@ 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);
+  if (vui_vec)
+    free (vui_vec);
+  vui_vec = NULL;
+  vui_allocated = 0;
 }
 
 /* The entry point to variable tracking pass.  */
 
-void
+unsigned int
 variable_tracking_main (void)
 {
   if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
-    return;
+    return 0;
 
   mark_dfs_back_edges ();
   vt_initialize ();
@@ -2794,18 +3603,46 @@ variable_tracking_main (void)
       if (!vt_stack_adjustments ())
        {
          vt_finalize ();
-         return;
+         return 0;
        }
     }
 
   vt_find_locations ();
   vt_emit_notes ();
 
-  if (dump_file)
+  if (dump_file && (dump_flags & TDF_DETAILS))
     {
       dump_dataflow_sets ();
-      dump_flow_info (dump_file);
+      dump_flow_info (dump_file, dump_flags);
     }
 
   vt_finalize ();
+  return 0;
 }
+\f
+static bool
+gate_handle_var_tracking (void)
+{
+  return (flag_var_tracking);
+}
+
+
+
+struct rtl_opt_pass pass_variable_tracking =
+{
+ {
+  RTL_PASS,
+  "vartrack",                           /* name */
+  gate_handle_var_tracking,             /* gate */
+  variable_tracking_main,               /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_VAR_TRACKING,                      /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func | TODO_verify_rtl_sharing/* todo_flags_finish */
+ }
+};