diff gcc/var-tracking.c @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents f6334be47118
children 84e7813d76e9
line wrap: on
line diff
--- a/gcc/var-tracking.c	Sun Aug 21 07:07:55 2011 +0900
+++ b/gcc/var-tracking.c	Fri Oct 27 22:46:09 2017 +0900
@@ -1,6 +1,5 @@
 /* Variable tracking routines for the GNU compiler.
-   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 2002-2017 Free Software Foundation, Inc.
 
    This file is part of GCC.
 
@@ -34,7 +33,7 @@
    operations.
    The micro operations of one instruction are ordered so that
    pre-modifying stack adjustment < use < use with no var < call insn <
-     < set < clobber < post-modifying stack adjustment
+     < clobber < set < post-modifying stack adjustment
 
    Then, a forward dataflow analysis is performed to find out how locations
    of variables change through code and to propagate the variable locations
@@ -89,32 +88,36 @@
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
 #include "rtl.h"
 #include "tree.h"
+#include "cfghooks.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "memmodel.h"
 #include "tm_p.h"
-#include "hard-reg-set.h"
-#include "basic-block.h"
-#include "flags.h"
-#include "output.h"
 #include "insn-config.h"
-#include "reload.h"
-#include "sbitmap.h"
-#include "alloc-pool.h"
-#include "fibheap.h"
-#include "hashtab.h"
 #include "regs.h"
-#include "expr.h"
-#include "timevar.h"
-#include "tree-pass.h"
-#include "tree-flow.h"
+#include "emit-rtl.h"
+#include "recog.h"
+#include "diagnostic.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "cfgrtl.h"
+#include "cfganal.h"
+#include "reload.h"
+#include "calls.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
 #include "cselib.h"
-#include "target.h"
 #include "params.h"
-#include "diagnostic.h"
 #include "tree-pretty-print.h"
-#include "pointer-set.h"
-#include "recog.h"
+#include "rtl-iter.h"
+#include "fibonacci_heap.h"
+
+typedef fibonacci_heap <long, basic_block_def> bb_heap_t;
+typedef fibonacci_node <long, basic_block_def> bb_heap_node_t;
 
 /* var-tracking.c assumes that tree code with the same value as VALUE rtx code
    has no chance to appear in REG_EXPR/MEM_EXPRs and isn't a decl.
@@ -166,7 +169,7 @@
 };
 
 /* Structure holding information about micro operation.  */
-typedef struct micro_operation_def
+struct micro_operation
 {
   /* Type of micro operation.  */
   enum micro_operation_type type;
@@ -176,7 +179,7 @@
      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;
+  rtx_insn *insn;
 
   union {
     /* Location.  For MO_SET and MO_COPY, this is the SET that
@@ -190,37 +193,59 @@
     /* Stack adjustment.  */
     HOST_WIDE_INT adjust;
   } u;
-} micro_operation;
-
-DEF_VEC_O(micro_operation);
-DEF_VEC_ALLOC_O(micro_operation,heap);
+};
+
 
 /* A declaration of a variable, or an RTL value being handled like a
    declaration.  */
 typedef void *decl_or_value;
 
-/* Structure for passing some other parameters to function
-   emit_note_insn_var_location.  */
-typedef struct emit_note_data_def
-{
-  /* The instruction which the note will be emitted before/after.  */
-  rtx insn;
-
-  /* Where the note will be emitted (before/after insn)?  */
-  enum emit_note_where where;
-
-  /* The variables and values active at this point.  */
-  htab_t vars;
-} emit_note_data;
+/* Return true if a decl_or_value DV is a DECL or NULL.  */
+static inline bool
+dv_is_decl_p (decl_or_value dv)
+{
+  return !dv || (int) TREE_CODE ((tree) dv) != (int) VALUE;
+}
+
+/* Return true if a decl_or_value is a VALUE rtl.  */
+static inline bool
+dv_is_value_p (decl_or_value dv)
+{
+  return dv && !dv_is_decl_p (dv);
+}
+
+/* Return the decl in the decl_or_value.  */
+static inline tree
+dv_as_decl (decl_or_value dv)
+{
+  gcc_checking_assert (dv_is_decl_p (dv));
+  return (tree) dv;
+}
+
+/* Return the value in the decl_or_value.  */
+static inline rtx
+dv_as_value (decl_or_value dv)
+{
+  gcc_checking_assert (dv_is_value_p (dv));
+  return (rtx)dv;
+}
+
+/* Return the opaque pointer in the decl_or_value.  */
+static inline void *
+dv_as_opaque (decl_or_value dv)
+{
+  return dv;
+}
+
 
 /* Description of location of a part of a variable.  The content of a physical
    register is described by a chain of these structures.
    The chains are pretty short (usually 1 or 2 elements) and thus
    chain is the best data structure.  */
-typedef struct attrs_def
+struct attrs
 {
   /* Pointer to next member of the list.  */
-  struct attrs_def *next;
+  attrs *next;
 
   /* The rtx of register.  */
   rtx loc;
@@ -230,41 +255,300 @@
 
   /* Offset from start of DECL.  */
   HOST_WIDE_INT offset;
-} *attrs;
+};
+
+/* Structure for chaining the locations.  */
+struct location_chain
+{
+  /* Next element in the chain.  */
+  location_chain *next;
+
+  /* The location (REG, MEM or VALUE).  */
+  rtx loc;
+
+  /* The "value" stored in this location.  */
+  rtx set_src;
+
+  /* Initialized? */
+  enum var_init_status init;
+};
+
+/* A vector of loc_exp_dep holds the active dependencies of a one-part
+   DV on VALUEs, i.e., the VALUEs expanded so as to form the current
+   location of DV.  Each entry is also part of VALUE' s linked-list of
+   backlinks back to DV.  */
+struct loc_exp_dep
+{
+  /* The dependent DV.  */
+  decl_or_value dv;
+  /* The dependency VALUE or DECL_DEBUG.  */
+  rtx value;
+  /* The next entry in VALUE's backlinks list.  */
+  struct loc_exp_dep *next;
+  /* A pointer to the pointer to this entry (head or prev's next) in
+     the doubly-linked list.  */
+  struct loc_exp_dep **pprev;
+};
+
+
+/* This data structure holds information about the depth of a variable
+   expansion.  */
+struct expand_depth
+{
+  /* This measures the complexity of the expanded expression.  It
+     grows by one for each level of expansion that adds more than one
+     operand.  */
+  int complexity;
+  /* This counts the number of ENTRY_VALUE expressions in an
+     expansion.  We want to minimize their use.  */
+  int entryvals;
+};
+
+/* This data structure is allocated for one-part variables at the time
+   of emitting notes.  */
+struct onepart_aux
+{
+  /* Doubly-linked list of dependent DVs.  These are DVs whose cur_loc
+     computation used the expansion of this variable, and that ought
+     to be notified should this variable change.  If the DV's cur_loc
+     expanded to NULL, all components of the loc list are regarded as
+     active, so that any changes in them give us a chance to get a
+     location.  Otherwise, only components of the loc that expanded to
+     non-NULL are regarded as active dependencies.  */
+  loc_exp_dep *backlinks;
+  /* This holds the LOC that was expanded into cur_loc.  We need only
+     mark a one-part variable as changed if the FROM loc is removed,
+     or if it has no known location and a loc is added, or if it gets
+     a change notification from any of its active dependencies.  */
+  rtx from;
+  /* The depth of the cur_loc expression.  */
+  expand_depth depth;
+  /* Dependencies actively used when expand FROM into cur_loc.  */
+  vec<loc_exp_dep, va_heap, vl_embed> deps;
+};
+
+/* Structure describing one part of variable.  */
+struct variable_part
+{
+  /* Chain of locations of the part.  */
+  location_chain *loc_chain;
+
+  /* Location which was last emitted to location list.  */
+  rtx cur_loc;
+
+  union variable_aux
+  {
+    /* The offset in the variable, if !var->onepart.  */
+    HOST_WIDE_INT offset;
+
+    /* Pointer to auxiliary data, if var->onepart and emit_notes.  */
+    struct onepart_aux *onepaux;
+  } aux;
+};
+
+/* Maximum number of location parts.  */
+#define MAX_VAR_PARTS 16
+
+/* Enumeration type used to discriminate various types of one-part
+   variables.  */
+enum onepart_enum
+{
+  /* Not a one-part variable.  */
+  NOT_ONEPART = 0,
+  /* A one-part DECL that is not a DEBUG_EXPR_DECL.  */
+  ONEPART_VDECL = 1,
+  /* A DEBUG_EXPR_DECL.  */
+  ONEPART_DEXPR = 2,
+  /* A VALUE.  */
+  ONEPART_VALUE = 3
+};
+
+/* Structure describing where the variable is located.  */
+struct variable
+{
+  /* The declaration of the variable, or an RTL value being handled
+     like a declaration.  */
+  decl_or_value dv;
+
+  /* Reference count.  */
+  int refcount;
+
+  /* Number of variable parts.  */
+  char n_var_parts;
+
+  /* What type of DV this is, according to enum onepart_enum.  */
+  ENUM_BITFIELD (onepart_enum) onepart : CHAR_BIT;
+
+  /* True if this variable_def struct is currently in the
+     changed_variables hash table.  */
+  bool in_changed_variables;
+
+  /* The variable parts.  */
+  variable_part var_part[1];
+};
+
+/* 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_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0)
+
+#if CHECKING_P && (GCC_VERSION >= 2007)
+
+/* Access VAR's Ith part's offset, checking that it's not a one-part
+   variable.  */
+#define VAR_PART_OFFSET(var, i) __extension__			\
+(*({  variable *const __v = (var);				\
+      gcc_checking_assert (!__v->onepart);			\
+      &__v->var_part[(i)].aux.offset; }))
+
+/* Access VAR's one-part auxiliary data, checking that it is a
+   one-part variable.  */
+#define VAR_LOC_1PAUX(var) __extension__			\
+(*({  variable *const __v = (var);				\
+      gcc_checking_assert (__v->onepart);			\
+      &__v->var_part[0].aux.onepaux; }))
+
+#else
+#define VAR_PART_OFFSET(var, i) ((var)->var_part[(i)].aux.offset)
+#define VAR_LOC_1PAUX(var) ((var)->var_part[0].aux.onepaux)
+#endif
+
+/* These are accessor macros for the one-part auxiliary data.  When
+   convenient for users, they're guarded by tests that the data was
+   allocated.  */
+#define VAR_LOC_DEP_LST(var) (VAR_LOC_1PAUX (var)		  \
+			      ? VAR_LOC_1PAUX (var)->backlinks	  \
+			      : NULL)
+#define VAR_LOC_DEP_LSTP(var) (VAR_LOC_1PAUX (var)		  \
+			       ? &VAR_LOC_1PAUX (var)->backlinks  \
+			       : NULL)
+#define VAR_LOC_FROM(var) (VAR_LOC_1PAUX (var)->from)
+#define VAR_LOC_DEPTH(var) (VAR_LOC_1PAUX (var)->depth)
+#define VAR_LOC_DEP_VEC(var) (VAR_LOC_1PAUX (var)		  \
+			      ? &VAR_LOC_1PAUX (var)->deps	  \
+			      : NULL)
+
+
+
+typedef unsigned int dvuid;
+
+/* Return the uid of DV.  */
+
+static inline dvuid
+dv_uid (decl_or_value dv)
+{
+  if (dv_is_value_p (dv))
+    return CSELIB_VAL_PTR (dv_as_value (dv))->uid;
+  else
+    return DECL_UID (dv_as_decl (dv));
+}
+
+/* Compute the hash from the uid.  */
+
+static inline hashval_t
+dv_uid2hash (dvuid uid)
+{
+  return uid;
+}
+
+/* The hash function for a mask table in a shared_htab chain.  */
+
+static inline hashval_t
+dv_htab_hash (decl_or_value dv)
+{
+  return dv_uid2hash (dv_uid (dv));
+}
+
+static void variable_htab_free (void *);
+
+/* Variable hashtable helpers.  */
+
+struct variable_hasher : pointer_hash <variable>
+{
+  typedef void *compare_type;
+  static inline hashval_t hash (const variable *);
+  static inline bool equal (const variable *, const void *);
+  static inline void remove (variable *);
+};
+
+/* The hash function for variable_htab, computes the hash value
+   from the declaration of variable X.  */
+
+inline hashval_t
+variable_hasher::hash (const variable *v)
+{
+  return dv_htab_hash (v->dv);
+}
+
+/* Compare the declaration of variable X with declaration Y.  */
+
+inline bool
+variable_hasher::equal (const variable *v, const void *y)
+{
+  decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
+
+  return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
+}
+
+/* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
+
+inline void
+variable_hasher::remove (variable *var)
+{
+  variable_htab_free (var);
+}
+
+typedef hash_table<variable_hasher> variable_table_type;
+typedef variable_table_type::iterator variable_iterator_type;
+
+/* Structure for passing some other parameters to function
+   emit_note_insn_var_location.  */
+struct emit_note_data
+{
+  /* The instruction which the note will be emitted before/after.  */
+  rtx_insn *insn;
+
+  /* Where the note will be emitted (before/after insn)?  */
+  enum emit_note_where where;
+
+  /* The variables and values active at this point.  */
+  variable_table_type *vars;
+};
 
 /* Structure holding a refcounted hash table.  If refcount > 1,
    it must be first unshared before modified.  */
-typedef struct shared_hash_def
+struct shared_hash
 {
   /* Reference count.  */
   int refcount;
 
   /* Actual hash table.  */
-  htab_t htab;
-} *shared_hash;
+  variable_table_type *htab;
+};
 
 /* Structure holding the IN or OUT set for a basic block.  */
-typedef struct dataflow_set_def
+struct dataflow_set
 {
   /* Adjustment of stack offset.  */
   HOST_WIDE_INT stack_adjust;
 
   /* Attributes for registers (lists of attrs).  */
-  attrs regs[FIRST_PSEUDO_REGISTER];
+  attrs *regs[FIRST_PSEUDO_REGISTER];
 
   /* Variable locations.  */
-  shared_hash vars;
+  shared_hash *vars;
 
   /* Vars that is being traversed.  */
-  shared_hash traversed_vars;
-} dataflow_set;
+  shared_hash *traversed_vars;
+};
 
 /* The structure (one for each basic block) containing the information
    needed for variable tracking.  */
-typedef struct variable_tracking_info_def
+struct variable_tracking_info
 {
   /* The vector of micro operations.  */
-  VEC(micro_operation, heap) *mos;
+  vec<micro_operation> mos;
 
   /* The IN and OUT set for dataflow analysis.  */
   dataflow_set in;
@@ -282,146 +566,79 @@
   /* Has the block been flooded in VTA?  */
   bool flooded;
 
-} *variable_tracking_info;
-
-/* Structure for chaining the locations.  */
-typedef struct location_chain_def
-{
-  /* Next element in the chain.  */
-  struct location_chain_def *next;
-
-  /* The location (REG, MEM or VALUE).  */
-  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.  */
-typedef struct variable_part_def
-{
-  /* Chain of locations of the part.  */
-  location_chain loc_chain;
-
-  /* Location which was last emitted to location list.  */
-  rtx cur_loc;
-
-  /* The offset in the variable.  */
-  HOST_WIDE_INT offset;
-} variable_part;
-
-/* Maximum number of location parts.  */
-#define MAX_VAR_PARTS 16
-
-/* Structure describing where the variable is located.  */
-typedef struct variable_def
-{
-  /* The declaration of the variable, or an RTL value being handled
-     like a declaration.  */
-  decl_or_value dv;
-
-  /* Reference count.  */
-  int refcount;
-
-  /* Number of variable parts.  */
-  char n_var_parts;
-
-  /* True if this variable changed (any of its) cur_loc fields
-     during the current emit_notes_for_changes resp.
-     emit_notes_for_differences call.  */
-  bool cur_loc_changed;
-
-  /* True if this variable_def struct is currently in the
-     changed_variables hash table.  */
-  bool in_changed_variables;
-
-  /* The variable parts.  */
-  variable_part var_part[1];
-} *variable;
-typedef const struct variable_def *const_variable;
-
-/* Structure for chaining backlinks from referenced VALUEs to
-   DVs that are referencing them.  */
-typedef struct value_chain_def
-{
-  /* Next value_chain entry.  */
-  struct value_chain_def *next;
-
-  /* The declaration of the variable, or an RTL value
-     being handled like a declaration, whose var_parts[0].loc_chain
-     references the VALUE owning this value_chain.  */
-  decl_or_value dv;
-
-  /* Reference count.  */
-  int refcount;
-} *value_chain;
-typedef const struct value_chain_def *const_value_chain;
-
-/* 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;
+object_allocator<attrs> attrs_pool ("attrs pool");
 
 /* Alloc pool for struct variable_def with MAX_VAR_PARTS entries.  */
-static alloc_pool var_pool;
+
+static pool_allocator var_pool
+  ("variable_def pool", sizeof (variable) +
+   (MAX_VAR_PARTS - 1) * sizeof (((variable *)NULL)->var_part[0]));
 
 /* Alloc pool for struct variable_def with a single var_part entry.  */
-static alloc_pool valvar_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;
-
-/* Alloc pool for struct value_chain_def.  */
-static alloc_pool value_chain_pool;
+static pool_allocator valvar_pool
+  ("small variable_def pool", sizeof (variable));
+
+/* Alloc pool for struct location_chain.  */
+static object_allocator<location_chain> location_chain_pool
+  ("location_chain pool");
+
+/* Alloc pool for struct shared_hash.  */
+static object_allocator<shared_hash> shared_hash_pool ("shared_hash pool");
+
+/* Alloc pool for struct loc_exp_dep_s for NOT_ONEPART variables.  */
+object_allocator<loc_exp_dep> loc_exp_dep_pool ("loc_exp_dep pool");
 
 /* Changed variables, notes will be emitted for them.  */
-static htab_t changed_variables;
-
-/* Links from VALUEs to DVs referencing them in their current loc_chains.  */
-static htab_t value_chains;
+static variable_table_type *changed_variables;
 
 /* Shall notes be emitted?  */
 static bool emit_notes;
 
+/* Values whose dynamic location lists have gone empty, but whose
+   cselib location lists are still usable.  Use this to hold the
+   current location, the backlinks, etc, during emit_notes.  */
+static variable_table_type *dropped_values;
+
 /* Empty shared hashtable.  */
-static shared_hash empty_shared_hash;
+static shared_hash *empty_shared_hash;
 
 /* Scratch register bitmap used by cselib_expand_value_rtx.  */
 static bitmap scratch_regs = NULL;
 
+#ifdef HAVE_window_save
+struct GTY(()) parm_reg {
+  rtx outgoing;
+  rtx incoming;
+};
+
+
+/* Vector of windowed parameter registers, if any.  */
+static vec<parm_reg, va_gc> *windowed_parm_regs = NULL;
+#endif
+
 /* Variable used to tell whether cselib_process_insn called our hook.  */
 static bool cselib_hook_called;
 
 /* Local function prototypes.  */
 static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
 					  HOST_WIDE_INT *);
-static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
+static void insn_stack_adjust_offset_pre_post (rtx_insn *, HOST_WIDE_INT *,
 					       HOST_WIDE_INT *);
 static bool vt_stack_adjustments (void);
-static hashval_t variable_htab_hash (const void *);
-static int variable_htab_eq (const void *, const void *);
-static void variable_htab_free (void *);
-
-static void init_attrs_list_set (attrs *);
-static void attrs_list_clear (attrs *);
-static attrs attrs_list_member (attrs, decl_or_value, HOST_WIDE_INT);
-static void attrs_list_insert (attrs *, decl_or_value, HOST_WIDE_INT, rtx);
-static void attrs_list_copy (attrs *, attrs);
-static void attrs_list_union (attrs *, attrs);
-
-static void **unshare_variable (dataflow_set *set, void **slot, variable var,
-				enum var_init_status);
-static void vars_copy (htab_t, htab_t);
+
+static void init_attrs_list_set (attrs **);
+static void attrs_list_clear (attrs **);
+static attrs *attrs_list_member (attrs *, decl_or_value, HOST_WIDE_INT);
+static void attrs_list_insert (attrs **, decl_or_value, HOST_WIDE_INT, rtx);
+static void attrs_list_copy (attrs **, attrs *);
+static void attrs_list_union (attrs **, attrs *);
+
+static variable **unshare_variable (dataflow_set *set, variable **slot,
+					variable *var, enum var_init_status);
+static void vars_copy (variable_table_type *, variable_table_type *);
 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,
@@ -438,50 +655,45 @@
 static void dataflow_set_copy (dataflow_set *, dataflow_set *);
 static int variable_union_info_cmp_pos (const void *, const void *);
 static void dataflow_set_union (dataflow_set *, dataflow_set *);
-static location_chain find_loc_in_1pdv (rtx, variable, htab_t);
+static location_chain *find_loc_in_1pdv (rtx, variable *,
+					 variable_table_type *);
 static bool canon_value_cmp (rtx, rtx);
 static int loc_cmp (rtx, rtx);
 static bool variable_part_different_p (variable_part *, variable_part *);
-static bool onepart_variable_different_p (variable, variable);
-static bool variable_different_p (variable, variable);
+static bool onepart_variable_different_p (variable *, variable *);
+static bool variable_different_p (variable *, variable *);
 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, bool);
 static bool same_variable_part_p (rtx, tree, HOST_WIDE_INT);
-static int add_uses (rtx *, void *);
 static void add_uses_1 (rtx *, void *);
 static void add_stores (rtx, const_rtx, void *);
 static bool compute_bb_dataflow (basic_block);
 static bool vt_find_locations (void);
 
-static void dump_attrs_list (attrs);
-static int dump_var_slot (void **, void *);
-static void dump_var (variable);
-static void dump_vars (htab_t);
+static void dump_attrs_list (attrs *);
+static void dump_var (variable *);
+static void dump_vars (variable_table_type *);
 static void dump_dataflow_set (dataflow_set *);
 static void dump_dataflow_sets (void);
 
-static void variable_was_changed (variable, dataflow_set *);
-static void **set_slot_part (dataflow_set *, rtx, void **,
-			     decl_or_value, HOST_WIDE_INT,
-			     enum var_init_status, rtx);
+static void set_dv_changed (decl_or_value, bool);
+static void variable_was_changed (variable *, dataflow_set *);
+static variable **set_slot_part (dataflow_set *, rtx, variable **,
+				 decl_or_value, HOST_WIDE_INT,
+				 enum var_init_status, rtx);
 static void set_variable_part (dataflow_set *, rtx,
 			       decl_or_value, HOST_WIDE_INT,
 			       enum var_init_status, rtx, enum insert_option);
-static void **clobber_slot_part (dataflow_set *, rtx,
-				 void **, HOST_WIDE_INT, rtx);
+static variable **clobber_slot_part (dataflow_set *, rtx,
+				     variable **, HOST_WIDE_INT, rtx);
 static void clobber_variable_part (dataflow_set *, rtx,
 				   decl_or_value, HOST_WIDE_INT, rtx);
-static void **delete_slot_part (dataflow_set *, rtx, void **, HOST_WIDE_INT);
+static variable **delete_slot_part (dataflow_set *, rtx, variable **,
+				    HOST_WIDE_INT);
 static void delete_variable_part (dataflow_set *, rtx,
 				  decl_or_value, HOST_WIDE_INT);
-static int emit_note_insn_var_location (void **, void *);
-static void emit_notes_for_changes (rtx, enum emit_note_where, shared_hash);
-static int emit_notes_for_differences_1 (void **, void *);
-static int emit_notes_for_differences_2 (void **, void *);
-static void emit_notes_for_differences (rtx, dataflow_set *, dataflow_set *);
 static void emit_notes_in_bb (basic_block, dataflow_set *);
 static void vt_emit_notes (void);
 
@@ -490,6 +702,39 @@
 static bool vt_initialize (void);
 static void vt_finalize (void);
 
+/* Callback for stack_adjust_offset_pre_post, called via for_each_inc_dec.  */
+
+static int
+stack_adjust_offset_pre_post_cb (rtx, rtx op, rtx dest, rtx src, rtx srcoff,
+				 void *arg)
+{
+  if (dest != stack_pointer_rtx)
+    return 0;
+
+  switch (GET_CODE (op))
+    {
+    case PRE_INC:
+    case PRE_DEC:
+      ((HOST_WIDE_INT *)arg)[0] -= INTVAL (srcoff);
+      return 0;
+    case POST_INC:
+    case POST_DEC:
+      ((HOST_WIDE_INT *)arg)[1] -= INTVAL (srcoff);
+      return 0;
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      /* We handle only adjustments by constant amount.  */
+      gcc_assert (GET_CODE (src) == PLUS
+		  && CONST_INT_P (XEXP (src, 1))
+		  && XEXP (src, 0) == stack_pointer_rtx);
+      ((HOST_WIDE_INT *)arg)[GET_CODE (op) == POST_MODIFY]
+	-= INTVAL (XEXP (src, 1));
+      return 0;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Given a SET, calculate the amount of stack adjustment it contains
    PRE- and POST-modifying stack pointer.
    This function is similar to stack_adjust_offset.  */
@@ -515,75 +760,19 @@
 	*post += INTVAL (XEXP (src, 1));
       else
 	*post -= INTVAL (XEXP (src, 1));
-    }
-  else if (MEM_P (dest))
-    {
-      /* (set (mem (pre_dec (reg sp))) (foo)) */
-      src = XEXP (dest, 0);
-      code = GET_CODE (src);
-
-      switch (code)
-	{
-	case PRE_MODIFY:
-	case POST_MODIFY:
-	  if (XEXP (src, 0) == stack_pointer_rtx)
-	    {
-	      rtx val = XEXP (XEXP (src, 1), 1);
-	      /* We handle only adjustments by constant amount.  */
-	      gcc_assert (GET_CODE (XEXP (src, 1)) == PLUS &&
-			  CONST_INT_P (val));
-
-	      if (code == PRE_MODIFY)
-		*pre -= INTVAL (val);
-	      else
-		*post -= INTVAL (val);
-	      break;
-	    }
-	  return;
-
-	case PRE_DEC:
-	  if (XEXP (src, 0) == stack_pointer_rtx)
-	    {
-	      *pre += GET_MODE_SIZE (GET_MODE (dest));
-	      break;
-	    }
-	  return;
-
-	case POST_DEC:
-	  if (XEXP (src, 0) == stack_pointer_rtx)
-	    {
-	      *post += GET_MODE_SIZE (GET_MODE (dest));
-	      break;
-	    }
-	  return;
-
-	case PRE_INC:
-	  if (XEXP (src, 0) == stack_pointer_rtx)
-	    {
-	      *pre -= GET_MODE_SIZE (GET_MODE (dest));
-	      break;
-	    }
-	  return;
-
-	case POST_INC:
-	  if (XEXP (src, 0) == stack_pointer_rtx)
-	    {
-	      *post -= GET_MODE_SIZE (GET_MODE (dest));
-	      break;
-	    }
-	  return;
-
-	default:
-	  return;
-	}
-    }
+      return;
+    }
+  HOST_WIDE_INT res[2] = { 0, 0 };
+  for_each_inc_dec (pattern, stack_adjust_offset_pre_post_cb, res);
+  *pre += res[0];
+  *post += res[1];
 }
 
 /* Given an INSN, calculate the amount of stack adjustment it contains
    PRE- and POST-modifying stack pointer.  */
 
 static void
-insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
+insn_stack_adjust_offset_pre_post (rtx_insn *insn, HOST_WIDE_INT *pre,
 				   HOST_WIDE_INT *post)
 {
   rtx pattern;
@@ -625,16 +814,18 @@
   int sp;
 
   /* Initialize entry block.  */
-  VTI (ENTRY_BLOCK_PTR)->visited = true;
-  VTI (ENTRY_BLOCK_PTR)->in.stack_adjust = INCOMING_FRAME_SP_OFFSET;
-  VTI (ENTRY_BLOCK_PTR)->out.stack_adjust = INCOMING_FRAME_SP_OFFSET;
+  VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->visited = true;
+  VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->in.stack_adjust
+    = INCOMING_FRAME_SP_OFFSET;
+  VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->out.stack_adjust
+    = INCOMING_FRAME_SP_OFFSET;
 
   /* Allocate stack for back-tracking up CFG.  */
-  stack = XNEWVEC (edge_iterator, n_basic_blocks + 1);
+  stack = XNEWVEC (edge_iterator, n_basic_blocks_for_fn (cfun) + 1);
   sp = 0;
 
   /* Push the first edge on to the stack.  */
-  stack[sp++] = ei_start (ENTRY_BLOCK_PTR->succs);
+  stack[sp++] = ei_start (ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs);
 
   while (sp)
     {
@@ -650,12 +841,12 @@
       /* Check if the edge destination has been visited yet.  */
       if (!VTI (dest)->visited)
 	{
-	  rtx insn;
+	  rtx_insn *insn;
 	  HOST_WIDE_INT pre, post, offset;
 	  VTI (dest)->visited = true;
 	  VTI (dest)->in.stack_adjust = offset = VTI (src)->out.stack_adjust;
 
-	  if (dest != EXIT_BLOCK_PTR)
+	  if (dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
 	    for (insn = BB_HEAD (dest);
 		 insn != NEXT_INSN (BB_END (dest));
 		 insn = NEXT_INSN (insn))
@@ -674,8 +865,25 @@
 	}
       else
 	{
-	  /* Check whether the adjustments on the edges are the same.  */
-	  if (VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust)
+	  /* We can end up with different stack adjustments for the exit block
+	     of a shrink-wrapped function if stack_adjust_offset_pre_post
+	     doesn't understand the rtx pattern used to restore the stack
+	     pointer in the epilogue.  For example, on s390(x), the stack
+	     pointer is often restored via a load-multiple instruction
+	     and so no stack_adjust offset is recorded for it.  This means
+	     that the stack offset at the end of the epilogue block is the
+	     same as the offset before the epilogue, whereas other paths
+	     to the exit block will have the correct stack_adjust.
+
+	     It is safe to ignore these differences because (a) we never
+	     use the stack_adjust for the exit block in this pass and
+	     (b) dwarf2cfi checks whether the CFA notes in a shrink-wrapped
+	     function are correct.
+
+	     We must check whether the adjustments on other edges are
+	     the same though.  */
+	  if (dest != EXIT_BLOCK_PTR_FOR_FN (cfun)
+	      && VTI (dest)->in.stack_adjust != VTI (src)->out.stack_adjust)
 	    {
 	      free (stack);
 	      return false;
@@ -699,12 +907,13 @@
 static rtx cfa_base_rtx;
 static HOST_WIDE_INT cfa_base_offset;
 
-/* Compute a CFA-based value for the stack pointer.  */
+/* Compute a CFA-based value for an ADJUSTMENT made to stack_pointer_rtx
+   or hard_frame_pointer_rtx.  */
 
 static inline rtx
 compute_cfa_pointer (HOST_WIDE_INT adjustment)
 {
-  return plus_constant (cfa_base_rtx, adjustment + cfa_base_offset);
+  return plus_constant (Pmode, cfa_base_rtx, adjustment + cfa_base_offset);
 }
 
 /* Adjustment for hard_frame_pointer_rtx to cfa base reg,
@@ -716,48 +925,52 @@
 struct adjust_mem_data
 {
   bool store;
-  enum machine_mode mem_mode;
+  machine_mode mem_mode;
   HOST_WIDE_INT stack_adjust;
-  rtx side_effects;
+  auto_vec<rtx> side_effects;
 };
 
-/* Helper for adjust_mems.  Return 1 if *loc is unsuitable for
-   transformation of wider mode arithmetics to narrower mode,
-   -1 if it is suitable and subexpressions shouldn't be
-   traversed and 0 if it is suitable and subexpressions should
-   be traversed.  Called through for_each_rtx.  */
-
-static int
-use_narrower_mode_test (rtx *loc, void *data)
-{
-  rtx subreg = (rtx) data;
-
-  if (CONSTANT_P (*loc))
-    return -1;
-  switch (GET_CODE (*loc))
-    {
-    case REG:
-      if (cselib_lookup (*loc, GET_MODE (SUBREG_REG (subreg)), 0, VOIDmode))
-	return 1;
-      return -1;
-    case PLUS:
-    case MINUS:
-    case MULT:
-      return 0;
-    case ASHIFT:
-      if (for_each_rtx (&XEXP (*loc, 0), use_narrower_mode_test, data))
-	return 1;
+/* Helper for adjust_mems.  Return true if X is suitable for
+   transformation of wider mode arithmetics to narrower mode.  */
+
+static bool
+use_narrower_mode_test (rtx x, const_rtx subreg)
+{
+  subrtx_var_iterator::array_type array;
+  FOR_EACH_SUBRTX_VAR (iter, array, x, NONCONST)
+    {
+      rtx x = *iter;
+      if (CONSTANT_P (x))
+	iter.skip_subrtxes ();
       else
-	return -1;
-    default:
-      return 1;
-    }
+	switch (GET_CODE (x))
+	  {
+	  case REG:
+	    if (cselib_lookup (x, GET_MODE (SUBREG_REG (subreg)), 0, VOIDmode))
+	      return false;
+	    if (!validate_subreg (GET_MODE (subreg), GET_MODE (x), x,
+				  subreg_lowpart_offset (GET_MODE (subreg),
+							 GET_MODE (x))))
+	      return false;
+	    break;
+	  case PLUS:
+	  case MINUS:
+	  case MULT:
+	    break;
+	  case ASHIFT:
+	    iter.substitute (XEXP (x, 0));
+	    break;
+	  default:
+	    return false;
+	  }
+    }
+  return true;
 }
 
 /* Transform X into narrower mode MODE from wider mode WMODE.  */
 
 static rtx
-use_narrower_mode (rtx x, enum machine_mode mode, enum machine_mode wmode)
+use_narrower_mode (rtx x, scalar_int_mode mode, scalar_int_mode wmode)
 {
   rtx op0, op1;
   if (CONSTANT_P (x))
@@ -774,7 +987,14 @@
       return simplify_gen_binary (GET_CODE (x), mode, op0, op1);
     case ASHIFT:
       op0 = use_narrower_mode (XEXP (x, 0), mode, wmode);
-      return simplify_gen_binary (ASHIFT, mode, op0, XEXP (x, 1));
+      op1 = XEXP (x, 1);
+      /* Ensure shift amount is not wider than mode.  */
+      if (GET_MODE (op1) == VOIDmode)
+	op1 = lowpart_subreg (mode, op1, wmode);
+      else if (GET_MODE_PRECISION (mode)
+	       < GET_MODE_PRECISION (as_a <scalar_int_mode> (GET_MODE (op1))))
+	op1 = lowpart_subreg (mode, op1, GET_MODE (op1));
+      return simplify_gen_binary (ASHIFT, mode, op0, op1);
     default:
       gcc_unreachable ();
     }
@@ -787,8 +1007,9 @@
 {
   struct adjust_mem_data *amd = (struct adjust_mem_data *) data;
   rtx mem, addr = loc, tem;
-  enum machine_mode mem_mode_save;
+  machine_mode mem_mode_save;
   bool store_save;
+  scalar_int_mode tem_mode, tem_subreg_mode;
   switch (GET_CODE (loc))
     {
     case REG:
@@ -834,9 +1055,11 @@
     case PRE_INC:
     case PRE_DEC:
       addr = gen_rtx_PLUS (GET_MODE (loc), XEXP (loc, 0),
-			   GEN_INT (GET_CODE (loc) == PRE_INC
-				    ? GET_MODE_SIZE (amd->mem_mode)
-				    : -GET_MODE_SIZE (amd->mem_mode)));
+			   gen_int_mode (GET_CODE (loc) == PRE_INC
+					 ? GET_MODE_SIZE (amd->mem_mode)
+					 : -GET_MODE_SIZE (amd->mem_mode),
+					 GET_MODE (loc)));
+      /* FALLTHRU */
     case POST_INC:
     case POST_DEC:
       if (addr == loc)
@@ -844,28 +1067,31 @@
       gcc_assert (amd->mem_mode != VOIDmode && amd->mem_mode != BLKmode);
       addr = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
       tem = gen_rtx_PLUS (GET_MODE (loc), XEXP (loc, 0),
-			   GEN_INT ((GET_CODE (loc) == PRE_INC
-				     || GET_CODE (loc) == POST_INC)
-				    ? GET_MODE_SIZE (amd->mem_mode)
-				    : -GET_MODE_SIZE (amd->mem_mode)));
-      amd->side_effects = alloc_EXPR_LIST (0,
-					   gen_rtx_SET (VOIDmode,
-							XEXP (loc, 0),
-							tem),
-					   amd->side_effects);
+			  gen_int_mode ((GET_CODE (loc) == PRE_INC
+					 || GET_CODE (loc) == POST_INC)
+					? GET_MODE_SIZE (amd->mem_mode)
+					: -GET_MODE_SIZE (amd->mem_mode),
+					GET_MODE (loc)));
+      store_save = amd->store;
+      amd->store = false;
+      tem = simplify_replace_fn_rtx (tem, old_rtx, adjust_mems, data);
+      amd->store = store_save;
+      amd->side_effects.safe_push (gen_rtx_SET (XEXP (loc, 0), tem));
       return addr;
     case PRE_MODIFY:
       addr = XEXP (loc, 1);
+      /* FALLTHRU */
     case POST_MODIFY:
       if (addr == loc)
 	addr = XEXP (loc, 0);
       gcc_assert (amd->mem_mode != VOIDmode);
       addr = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
-      amd->side_effects = alloc_EXPR_LIST (0,
-					   gen_rtx_SET (VOIDmode,
-							XEXP (loc, 0),
-							XEXP (loc, 1)),
-					   amd->side_effects);
+      store_save = amd->store;
+      amd->store = false;
+      tem = simplify_replace_fn_rtx (XEXP (loc, 1), old_rtx,
+				     adjust_mems, data);
+      amd->store = store_save;
+      amd->side_effects.safe_push (gen_rtx_SET (XEXP (loc, 0), tem));
       return addr;
     case SUBREG:
       /* First try without delegitimization of whole MEMs and
@@ -898,14 +1124,14 @@
 	      || GET_CODE (SUBREG_REG (tem)) == MINUS
 	      || GET_CODE (SUBREG_REG (tem)) == MULT
 	      || GET_CODE (SUBREG_REG (tem)) == ASHIFT)
-	  && GET_MODE_CLASS (GET_MODE (tem)) == MODE_INT
-	  && GET_MODE_CLASS (GET_MODE (SUBREG_REG (tem))) == MODE_INT
-	  && GET_MODE_SIZE (GET_MODE (tem))
-	     < GET_MODE_SIZE (GET_MODE (SUBREG_REG (tem)))
+	  && is_a <scalar_int_mode> (GET_MODE (tem), &tem_mode)
+	  && is_a <scalar_int_mode> (GET_MODE (SUBREG_REG (tem)),
+				     &tem_subreg_mode)
+	  && (GET_MODE_PRECISION (tem_mode)
+	      < GET_MODE_PRECISION (tem_subreg_mode))
 	  && subreg_lowpart_p (tem)
-	  && !for_each_rtx (&SUBREG_REG (tem), use_narrower_mode_test, tem))
-	return use_narrower_mode (SUBREG_REG (tem), GET_MODE (tem),
-				  GET_MODE (SUBREG_REG (tem)));
+	  && use_narrower_mode_test (SUBREG_REG (tem), tem))
+	return use_narrower_mode (SUBREG_REG (tem), tem_mode, tem_subreg_mode);
       return tem;
     case ASM_OPERANDS:
       /* Don't do any replacements in second and following
@@ -955,13 +1181,39 @@
    as other sets to the insn.  */
 
 static void
-adjust_insn (basic_block bb, rtx insn)
-{
-  struct adjust_mem_data amd;
+adjust_insn (basic_block bb, rtx_insn *insn)
+{
   rtx set;
+
+#ifdef HAVE_window_save
+  /* If the target machine has an explicit window save instruction, the
+     transformation OUTGOING_REGNO -> INCOMING_REGNO is done there.  */
+  if (RTX_FRAME_RELATED_P (insn)
+      && find_reg_note (insn, REG_CFA_WINDOW_SAVE, NULL_RTX))
+    {
+      unsigned int i, nregs = vec_safe_length (windowed_parm_regs);
+      rtx rtl = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nregs * 2));
+      parm_reg *p;
+
+      FOR_EACH_VEC_SAFE_ELT (windowed_parm_regs, i, p)
+	{
+	  XVECEXP (rtl, 0, i * 2)
+	    = gen_rtx_SET (p->incoming, p->outgoing);
+	  /* Do not clobber the attached DECL, but only the REG.  */
+	  XVECEXP (rtl, 0, i * 2 + 1)
+	    = gen_rtx_CLOBBER (GET_MODE (p->outgoing),
+			       gen_raw_REG (GET_MODE (p->outgoing),
+					    REGNO (p->outgoing)));
+	}
+
+      validate_change (NULL_RTX, &PATTERN (insn), rtl, true);
+      return;
+    }
+#endif
+
+  adjust_mem_data amd;
   amd.mem_mode = VOIDmode;
   amd.stack_adjust = -VTI (bb)->out.stack_adjust;
-  amd.side_effects = NULL_RTX;
 
   amd.store = true;
   note_stores (PATTERN (insn), adjust_mem_stores, &amd);
@@ -1027,10 +1279,10 @@
 	validate_change (NULL_RTX, &SET_SRC (set), XEXP (note, 0), true);
     }
 
-  if (amd.side_effects)
-    {
-      rtx *pat, new_pat, s;
-      int i, oldn, newn;
+  if (!amd.side_effects.is_empty ())
+    {
+      rtx *pat, new_pat;
+      int i, oldn;
 
       pat = &PATTERN (insn);
       if (GET_CODE (*pat) == COND_EXEC)
@@ -1039,88 +1291,74 @@
 	oldn = XVECLEN (*pat, 0);
       else
 	oldn = 1;
-      for (s = amd.side_effects, newn = 0; s; newn++)
-	s = XEXP (s, 1);
+      unsigned int newn = amd.side_effects.length ();
       new_pat = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (oldn + newn));
       if (GET_CODE (*pat) == PARALLEL)
 	for (i = 0; i < oldn; i++)
 	  XVECEXP (new_pat, 0, i) = XVECEXP (*pat, 0, i);
       else
 	XVECEXP (new_pat, 0, 0) = *pat;
-      for (s = amd.side_effects, i = oldn; i < oldn + newn; i++, s = XEXP (s, 1))
-	XVECEXP (new_pat, 0, i) = XEXP (s, 0);
-      free_EXPR_LIST_list (&amd.side_effects);
+
+      rtx effect;
+      unsigned int j;
+      FOR_EACH_VEC_ELT_REVERSE (amd.side_effects, j, effect)
+	XVECEXP (new_pat, 0, j + oldn) = effect;
       validate_change (NULL_RTX, pat, new_pat, true);
     }
 }
 
-/* Return true if a decl_or_value DV is a DECL or NULL.  */
-static inline bool
-dv_is_decl_p (decl_or_value dv)
-{
-  return !dv || (int) TREE_CODE ((tree) dv) != (int) VALUE;
-}
-
-/* Return true if a decl_or_value is a VALUE rtl.  */
-static inline bool
-dv_is_value_p (decl_or_value dv)
-{
-  return dv && !dv_is_decl_p (dv);
-}
-
-/* Return the decl in the decl_or_value.  */
-static inline tree
-dv_as_decl (decl_or_value dv)
-{
-  gcc_checking_assert (dv_is_decl_p (dv));
-  return (tree) dv;
-}
-
-/* Return the value in the decl_or_value.  */
+/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV.  */
 static inline rtx
-dv_as_value (decl_or_value dv)
-{
-  gcc_checking_assert (dv_is_value_p (dv));
-  return (rtx)dv;
-}
-
-/* Return the opaque pointer in the decl_or_value.  */
-static inline void *
-dv_as_opaque (decl_or_value dv)
-{
-  return dv;
-}
-
-/* Return true if a decl_or_value must not have more than one variable
-   part.  */
-static inline bool
+dv_as_rtx (decl_or_value dv)
+{
+  tree decl;
+
+  if (dv_is_value_p (dv))
+    return dv_as_value (dv);
+
+  decl = dv_as_decl (dv);
+
+  gcc_checking_assert (TREE_CODE (decl) == DEBUG_EXPR_DECL);
+  return DECL_RTL_KNOWN_SET (decl);
+}
+
+/* Return nonzero if a decl_or_value must not have more than one
+   variable part.  The returned value discriminates among various
+   kinds of one-part DVs ccording to enum onepart_enum.  */
+static inline onepart_enum
 dv_onepart_p (decl_or_value dv)
 {
   tree decl;
 
   if (!MAY_HAVE_DEBUG_INSNS)
-    return false;
+    return NOT_ONEPART;
 
   if (dv_is_value_p (dv))
-    return true;
+    return ONEPART_VALUE;
 
   decl = dv_as_decl (dv);
 
-  if (!decl)
-    return true;
-
   if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
-    return true;
-
-  return (target_for_debug_bind (decl) != NULL_TREE);
-}
-
-/* Return the variable pool to be used for dv, depending on whether it
-   can have multiple parts or not.  */
-static inline alloc_pool
-dv_pool (decl_or_value dv)
-{
-  return dv_onepart_p (dv) ? valvar_pool : var_pool;
+    return ONEPART_DEXPR;
+
+  if (target_for_debug_bind (decl) != NULL_TREE)
+    return ONEPART_VDECL;
+
+  return NOT_ONEPART;
+}
+
+/* Return the variable pool to be used for a dv of type ONEPART.  */
+static inline pool_allocator &
+onepart_pool (onepart_enum onepart)
+{
+  return onepart ? valvar_pool : var_pool;
+}
+
+/* Allocate a variable_def from the corresponding variable pool.  */
+static inline variable *
+onepart_pool_allocate (onepart_enum onepart)
+{
+  return (variable*) onepart_pool (onepart).allocate ();
 }
 
 /* Build a decl_or_value out of a decl.  */
@@ -1143,6 +1381,30 @@
   return dv;
 }
 
+/* Return a value or the decl of a debug_expr as a decl_or_value.  */
+static inline decl_or_value
+dv_from_rtx (rtx x)
+{
+  decl_or_value dv;
+
+  switch (GET_CODE (x))
+    {
+    case DEBUG_EXPR:
+      dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
+      gcc_checking_assert (DECL_RTL_KNOWN_SET (DEBUG_EXPR_TREE_DECL (x)) == x);
+      break;
+
+    case VALUE:
+      dv = dv_from_value (x);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  return dv;
+}
+
 extern void debug_dv (decl_or_value dv);
 
 DEBUG_FUNCTION void
@@ -1154,56 +1416,7 @@
     debug_generic_stmt (dv_as_decl (dv));
 }
 
-typedef unsigned int dvuid;
-
-/* Return the uid of DV.  */
-
-static inline dvuid
-dv_uid (decl_or_value dv)
-{
-  if (dv_is_value_p (dv))
-    return CSELIB_VAL_PTR (dv_as_value (dv))->uid;
-  else
-    return DECL_UID (dv_as_decl (dv));
-}
-
-/* Compute the hash from the uid.  */
-
-static inline hashval_t
-dv_uid2hash (dvuid uid)
-{
-  return uid;
-}
-
-/* The hash function for a mask table in a shared_htab chain.  */
-
-static inline hashval_t
-dv_htab_hash (decl_or_value dv)
-{
-  return dv_uid2hash (dv_uid (dv));
-}
-
-/* The hash function for variable_htab, computes the hash value
-   from the declaration of variable X.  */
-
-static hashval_t
-variable_htab_hash (const void *x)
-{
-  const_variable const v = (const_variable) x;
-
-  return dv_htab_hash (v->dv);
-}
-
-/* Compare the declaration of variable X with declaration Y.  */
-
-static int
-variable_htab_eq (const void *x, const void *y)
-{
-  const_variable const v = (const_variable) x;
-  decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
-
-  return (dv_as_opaque (v->dv) == dv_as_opaque (dv));
-}
+static void loc_exp_dep_clear (variable *var);
 
 /* Free the element of VARIABLE_HTAB (its type is struct variable_def).  */
 
@@ -1211,8 +1424,8 @@
 variable_htab_free (void *elem)
 {
   int i;
-  variable var = (variable) elem;
-  location_chain node, next;
+  variable *var = (variable *) elem;
+  location_chain *node, *next;
 
   gcc_checking_assert (var->refcount > 0);
 
@@ -1225,39 +1438,28 @@
       for (node = var->var_part[i].loc_chain; node; node = next)
 	{
 	  next = node->next;
-	  pool_free (loc_chain_pool, node);
+	  delete node;
 	}
       var->var_part[i].loc_chain = NULL;
     }
-  pool_free (dv_pool (var->dv), var);
-}
-
-/* The hash function for value_chains htab, computes the hash value
-   from the VALUE.  */
-
-static hashval_t
-value_chain_htab_hash (const void *x)
-{
-  const_value_chain const v = (const_value_chain) x;
-
-  return dv_htab_hash (v->dv);
-}
-
-/* Compare the VALUE X with VALUE Y.  */
-
-static int
-value_chain_htab_eq (const void *x, const void *y)
-{
-  const_value_chain const v = (const_value_chain) x;
-  decl_or_value dv = CONST_CAST2 (decl_or_value, const void *, y);
-
-  return dv_as_opaque (v->dv) == dv_as_opaque (dv);
+  if (var->onepart && VAR_LOC_1PAUX (var))
+    {
+      loc_exp_dep_clear (var);
+      if (VAR_LOC_DEP_LST (var))
+	VAR_LOC_DEP_LST (var)->pprev = NULL;
+      XDELETE (VAR_LOC_1PAUX (var));
+      /* These may be reused across functions, so reset
+	 e.g. NO_LOC_P.  */
+      if (var->onepart == ONEPART_DEXPR)
+	set_dv_changed (var->dv, true);
+    }
+  onepart_pool (var->onepart).remove (var);
 }
 
 /* Initialize the set (array) SET of attrs to empty lists.  */
 
 static void
-init_attrs_list_set (attrs *set)
+init_attrs_list_set (attrs **set)
 {
   int i;
 
@@ -1268,22 +1470,22 @@
 /* Make the list *LISTP empty.  */
 
 static void
-attrs_list_clear (attrs *listp)
-{
-  attrs list, next;
+attrs_list_clear (attrs **listp)
+{
+  attrs *list, *next;
 
   for (list = *listp; list; list = next)
     {
       next = list->next;
-      pool_free (attrs_pool, list);
+      delete list;
     }
   *listp = NULL;
 }
 
 /* Return true if the pair of DECL and OFFSET is the member of the LIST.  */
 
-static attrs
-attrs_list_member (attrs list, decl_or_value dv, HOST_WIDE_INT offset)
+static attrs *
+attrs_list_member (attrs *list, decl_or_value dv, HOST_WIDE_INT offset)
 {
   for (; list; list = list->next)
     if (dv_as_opaque (list->dv) == dv_as_opaque (dv) && list->offset == offset)
@@ -1294,12 +1496,10 @@
 /* Insert the triplet DECL, OFFSET, LOC to the list *LISTP.  */
 
 static void
-attrs_list_insert (attrs *listp, decl_or_value dv,
+attrs_list_insert (attrs **listp, decl_or_value dv,
 		   HOST_WIDE_INT offset, rtx loc)
 {
-  attrs list;
-
-  list = (attrs) pool_alloc (attrs_pool);
+  attrs *list = new attrs;
   list->loc = loc;
   list->dv = dv;
   list->offset = offset;
@@ -1310,14 +1510,12 @@
 /* Copy all nodes from SRC and create a list *DSTP of the copies.  */
 
 static void
-attrs_list_copy (attrs *dstp, attrs src)
-{
-  attrs n;
-
+attrs_list_copy (attrs **dstp, attrs *src)
+{
   attrs_list_clear (dstp);
   for (; src; src = src->next)
     {
-      n = (attrs) pool_alloc (attrs_pool);
+      attrs *n = new attrs;
       n->loc = src->loc;
       n->dv = src->dv;
       n->offset = src->offset;
@@ -1329,7 +1527,7 @@
 /* Add all nodes from SRC which are not in *DSTP to *DSTP.  */
 
 static void
-attrs_list_union (attrs *dstp, attrs src)
+attrs_list_union (attrs **dstp, attrs *src)
 {
   for (; src; src = src->next)
     {
@@ -1342,7 +1540,7 @@
    *DSTP.  */
 
 static void
-attrs_list_mpdv_union (attrs *dstp, attrs src, attrs src2)
+attrs_list_mpdv_union (attrs **dstp, attrs *src, attrs *src2)
 {
   gcc_assert (!*dstp);
   for (; src; src = src->next)
@@ -1363,15 +1561,15 @@
 /* Return true if VARS is shared.  */
 
 static inline bool
-shared_hash_shared (shared_hash vars)
+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)
+static inline variable_table_type *
+shared_hash_htab (shared_hash *vars)
 {
   return vars->htab;
 }
@@ -1379,7 +1577,7 @@
 /* Return true if VAR is shared, or maybe because VARS is shared.  */
 
 static inline bool
-shared_var_p (variable var, shared_hash vars)
+shared_var_p (variable *var, shared_hash *vars)
 {
   /* Don't count an entry in the changed_variables table as a duplicate.  */
   return ((var->refcount > 1 + (int) var->in_changed_variables)
@@ -1388,15 +1586,13 @@
 
 /* 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);
+static shared_hash *
+shared_hash_unshare (shared_hash *vars)
+{
+  shared_hash *new_vars = new shared_hash;
   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);
+  new_vars->htab = new variable_table_type (vars->htab->elements () + 3);
   vars_copy (new_vars->htab, vars->htab);
   vars->refcount--;
   return new_vars;
@@ -1404,8 +1600,8 @@
 
 /* Increment reference counter on VARS and return it.  */
 
-static inline shared_hash
-shared_hash_copy (shared_hash vars)
+static inline shared_hash *
+shared_hash_copy (shared_hash *vars)
 {
   vars->refcount++;
   return vars;
@@ -1415,30 +1611,30 @@
    anymore.  */
 
 static void
-shared_hash_destroy (shared_hash vars)
+shared_hash_destroy (shared_hash *vars)
 {
   gcc_checking_assert (vars->refcount > 0);
   if (--vars->refcount == 0)
     {
-      htab_delete (vars->htab);
-      pool_free (shared_hash_pool, vars);
+      delete vars->htab;
+      delete vars;
     }
 }
 
 /* Unshare *PVARS if shared and return slot for DV.  If INS is
    INSERT, insert it if not already present.  */
 
-static inline void **
-shared_hash_find_slot_unshare_1 (shared_hash *pvars, decl_or_value dv,
+static inline variable **
+shared_hash_find_slot_unshare_1 (shared_hash **pvars, decl_or_value dv,
 				 hashval_t dvhash, enum insert_option ins)
 {
   if (shared_hash_shared (*pvars))
     *pvars = shared_hash_unshare (*pvars);
-  return htab_find_slot_with_hash (shared_hash_htab (*pvars), dv, dvhash, ins);
-}
-
-static inline void **
-shared_hash_find_slot_unshare (shared_hash *pvars, decl_or_value dv,
+  return shared_hash_htab (*pvars)->find_slot_with_hash (dv, dvhash, ins);
+}
+
+static inline variable **
+shared_hash_find_slot_unshare (shared_hash **pvars, decl_or_value dv,
 			       enum insert_option ins)
 {
   return shared_hash_find_slot_unshare_1 (pvars, dv, dv_htab_hash (dv), ins);
@@ -1448,32 +1644,31 @@
    If it is not present, insert it only VARS is not shared, otherwise
    return NULL.  */
 
-static inline void **
-shared_hash_find_slot_1 (shared_hash vars, decl_or_value dv, hashval_t dvhash)
-{
-  return htab_find_slot_with_hash (shared_hash_htab (vars), dv, dvhash,
-				   shared_hash_shared (vars)
-				   ? NO_INSERT : INSERT);
-}
-
-static inline void **
-shared_hash_find_slot (shared_hash vars, decl_or_value dv)
+static inline variable **
+shared_hash_find_slot_1 (shared_hash *vars, decl_or_value dv, hashval_t dvhash)
+{
+  return shared_hash_htab (vars)->find_slot_with_hash (dv, dvhash,
+						       shared_hash_shared (vars)
+						       ? NO_INSERT : INSERT);
+}
+
+static inline variable **
+shared_hash_find_slot (shared_hash *vars, decl_or_value dv)
 {
   return shared_hash_find_slot_1 (vars, dv, dv_htab_hash (dv));
 }
 
 /* Return slot for DV only if it is already present in the hash table.  */
 
-static inline void **
-shared_hash_find_slot_noinsert_1 (shared_hash vars, decl_or_value dv,
+static inline variable **
+shared_hash_find_slot_noinsert_1 (shared_hash *vars, decl_or_value dv,
 				  hashval_t dvhash)
 {
-  return htab_find_slot_with_hash (shared_hash_htab (vars), dv, dvhash,
-				   NO_INSERT);
-}
-
-static inline void **
-shared_hash_find_slot_noinsert (shared_hash vars, decl_or_value dv)
+  return shared_hash_htab (vars)->find_slot_with_hash (dv, dvhash, NO_INSERT);
+}
+
+static inline variable **
+shared_hash_find_slot_noinsert (shared_hash *vars, decl_or_value dv)
 {
   return shared_hash_find_slot_noinsert_1 (vars, dv, dv_htab_hash (dv));
 }
@@ -1481,14 +1676,14 @@
 /* Return variable for DV or NULL if not already present in the hash
    table.  */
 
-static inline variable
-shared_hash_find_1 (shared_hash vars, decl_or_value dv, hashval_t dvhash)
-{
-  return (variable) htab_find_with_hash (shared_hash_htab (vars), dv, dvhash);
-}
-
-static inline variable
-shared_hash_find (shared_hash vars, decl_or_value dv)
+static inline variable *
+shared_hash_find_1 (shared_hash *vars, decl_or_value dv, hashval_t dvhash)
+{
+  return shared_hash_htab (vars)->find_with_hash (dv, dvhash);
+}
+
+static inline variable *
+shared_hash_find (shared_hash *vars, decl_or_value dv)
 {
   return shared_hash_find_1 (vars, dv, dv_htab_hash (dv));
 }
@@ -1513,20 +1708,19 @@
 
 /* Return a copy of a variable VAR and insert it to dataflow set SET.  */
 
-static void **
-unshare_variable (dataflow_set *set, void **slot, variable var,
+static variable **
+unshare_variable (dataflow_set *set, variable **slot, variable *var,
 		  enum var_init_status initialized)
 {
-  variable new_var;
+  variable *new_var;
   int i;
 
-  new_var = (variable) pool_alloc (dv_pool (var->dv));
+  new_var = onepart_pool_allocate (var->onepart);
   new_var->dv = var->dv;
   new_var->refcount = 1;
   var->refcount--;
   new_var->n_var_parts = var->n_var_parts;
-  new_var->cur_loc_changed = var->cur_loc_changed;
-  var->cur_loc_changed = false;
+  new_var->onepart = var->onepart;
   new_var->in_changed_variables = false;
 
   if (! flag_var_tracking_uninit)
@@ -1534,16 +1728,27 @@
 
   for (i = 0; i < var->n_var_parts; i++)
     {
-      location_chain node;
-      location_chain *nextp;
-
-      new_var->var_part[i].offset = var->var_part[i].offset;
+      location_chain *node;
+      location_chain **nextp;
+
+      if (i == 0 && var->onepart)
+	{
+	  /* One-part auxiliary data is only used while emitting
+	     notes, so propagate it to the new variable in the active
+	     dataflow set.  If we're not emitting notes, this will be
+	     a no-op.  */
+	  gcc_checking_assert (!VAR_LOC_1PAUX (var) || emit_notes);
+	  VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (var);
+	  VAR_LOC_1PAUX (var) = NULL;
+	}
+      else
+	VAR_PART_OFFSET (new_var, i) = VAR_PART_OFFSET (var, i);
       nextp = &new_var->var_part[i].loc_chain;
       for (node = var->var_part[i].loc_chain; node; node = node->next)
 	{
-	  location_chain new_lc;
-
-	  new_lc = (location_chain) pool_alloc (loc_chain_pool);
+	  location_chain *new_lc;
+
+	  new_lc = new location_chain;
 	  new_lc->next = NULL;
 	  if (node->init > initialized)
 	    new_lc->init = node->init;
@@ -1570,9 +1775,10 @@
   *slot = new_var;
   if (var->in_changed_variables)
     {
-      void **cslot
-	= htab_find_slot_with_hash (changed_variables, var->dv,
-				    dv_htab_hash (var->dv), NO_INSERT);
+      variable **cslot
+	= changed_variables->find_slot_with_hash (var->dv,
+						  dv_htab_hash (var->dv),
+						  NO_INSERT);
       gcc_assert (*cslot == (void *) var);
       var->in_changed_variables = false;
       variable_htab_free (var);
@@ -1585,17 +1791,16 @@
 /* Copy all variables from hash table SRC to hash table DST.  */
 
 static void
-vars_copy (htab_t dst, htab_t src)
-{
-  htab_iterator hi;
-  variable var;
-
-  FOR_EACH_HTAB_ELEMENT (src, var, variable, hi)
-    {
-      void **dstp;
+vars_copy (variable_table_type *dst, variable_table_type *src)
+{
+  variable_iterator_type hi;
+  variable *var;
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*src, var, variable, hi)
+    {
+      variable **dstp;
       var->refcount++;
-      dstp = htab_find_slot_with_hash (dst, var->dv,
-				       dv_htab_hash (var->dv),
+      dstp = dst->find_slot_with_hash (var->dv, dv_htab_hash (var->dv),
 				       INSERT);
       *dstp = var;
     }
@@ -1606,11 +1811,10 @@
 static inline tree
 var_debug_decl (tree decl)
 {
-  if (decl && DECL_P (decl)
-      && DECL_DEBUG_EXPR_IS_FROM (decl))
+  if (decl && VAR_P (decl) && DECL_HAS_DEBUG_EXPR_P (decl))
     {
       tree debugdecl = DECL_DEBUG_EXPR (decl);
-      if (debugdecl && DECL_P (debugdecl))
+      if (DECL_P (debugdecl))
 	decl = debugdecl;
     }
 
@@ -1624,7 +1828,7 @@
 		  decl_or_value dv, HOST_WIDE_INT offset, rtx set_src,
 		  enum insert_option iopt)
 {
-  attrs node;
+  attrs *node;
   bool decl_p = dv_is_decl_p (dv);
 
   if (decl_p)
@@ -1655,7 +1859,7 @@
 static enum var_init_status
 get_init_value (dataflow_set *set, rtx loc, decl_or_value dv)
 {
-  variable var;
+  variable *var;
   int i;
   enum var_init_status ret_val = VAR_INIT_STATUS_UNKNOWN;
 
@@ -1667,7 +1871,7 @@
     {
       for (i = 0; i < var->n_var_parts && ret_val == VAR_INIT_STATUS_UNKNOWN; i++)
 	{
-	  location_chain nextp;
+	  location_chain *nextp;
 	  for (nextp = var->var_part[i].loc_chain; nextp; nextp = nextp->next)
 	    if (rtx_equal_p (nextp->loc, loc))
 	      {
@@ -1693,8 +1897,8 @@
 {
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
-  attrs node, next;
-  attrs *nextp;
+  attrs *node, *next;
+  attrs **nextp;
 
   decl = var_debug_decl (decl);
 
@@ -1708,7 +1912,7 @@
       if (dv_as_opaque (node->dv) != decl || node->offset != offset)
 	{
 	  delete_variable_part (set, node->loc, node->dv, node->offset);
-	  pool_free (attrs_pool, node);
+	  delete node;
 	  *nextp = next;
 	}
       else
@@ -1730,8 +1934,8 @@
 static void
 var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
-  attrs *nextp = &set->regs[REGNO (loc)];
-  attrs node, next;
+  attrs **nextp = &set->regs[REGNO (loc)];
+  attrs *node, *next;
 
   if (clobber)
     {
@@ -1749,7 +1953,7 @@
       if (clobber || !dv_onepart_p (node->dv))
 	{
 	  delete_variable_part (set, node->loc, node->dv, node->offset);
-	  pool_free (attrs_pool, node);
+	  delete node;
 	  *nextp = next;
 	}
       else
@@ -1762,18 +1966,354 @@
 static void
 var_regno_delete (dataflow_set *set, int regno)
 {
-  attrs *reg = &set->regs[regno];
-  attrs node, next;
+  attrs **reg = &set->regs[regno];
+  attrs *node, *next;
 
   for (node = *reg; node; node = next)
     {
       next = node->next;
       delete_variable_part (set, node->loc, node->dv, node->offset);
-      pool_free (attrs_pool, node);
+      delete node;
     }
   *reg = NULL;
 }
 
+/* Return true if I is the negated value of a power of two.  */
+static bool
+negative_power_of_two_p (HOST_WIDE_INT i)
+{
+  unsigned HOST_WIDE_INT x = -(unsigned HOST_WIDE_INT)i;
+  return pow2_or_zerop (x);
+}
+
+/* Strip constant offsets and alignments off of LOC.  Return the base
+   expression.  */
+
+static rtx
+vt_get_canonicalize_base (rtx loc)
+{
+  while ((GET_CODE (loc) == PLUS
+	  || GET_CODE (loc) == AND)
+	 && GET_CODE (XEXP (loc, 1)) == CONST_INT
+	 && (GET_CODE (loc) != AND
+	     || negative_power_of_two_p (INTVAL (XEXP (loc, 1)))))
+    loc = XEXP (loc, 0);
+
+  return loc;
+}
+
+/* This caches canonicalized addresses for VALUEs, computed using
+   information in the global cselib table.  */
+static hash_map<rtx, rtx> *global_get_addr_cache;
+
+/* This caches canonicalized addresses for VALUEs, computed using
+   information from the global cache and information pertaining to a
+   basic block being analyzed.  */
+static hash_map<rtx, rtx> *local_get_addr_cache;
+
+static rtx vt_canonicalize_addr (dataflow_set *, rtx);
+
+/* Return the canonical address for LOC, that must be a VALUE, using a
+   cached global equivalence or computing it and storing it in the
+   global cache.  */
+
+static rtx
+get_addr_from_global_cache (rtx const loc)
+{
+  rtx x;
+
+  gcc_checking_assert (GET_CODE (loc) == VALUE);
+
+  bool existed;
+  rtx *slot = &global_get_addr_cache->get_or_insert (loc, &existed);
+  if (existed)
+    return *slot;
+
+  x = canon_rtx (get_addr (loc));
+
+  /* Tentative, avoiding infinite recursion.  */
+  *slot = x;
+
+  if (x != loc)
+    {
+      rtx nx = vt_canonicalize_addr (NULL, x);
+      if (nx != x)
+	{
+	  /* The table may have moved during recursion, recompute
+	     SLOT.  */
+	  *global_get_addr_cache->get (loc) = x = nx;
+	}
+    }
+
+  return x;
+}
+
+/* Return the canonical address for LOC, that must be a VALUE, using a
+   cached local equivalence or computing it and storing it in the
+   local cache.  */
+
+static rtx
+get_addr_from_local_cache (dataflow_set *set, rtx const loc)
+{
+  rtx x;
+  decl_or_value dv;
+  variable *var;
+  location_chain *l;
+
+  gcc_checking_assert (GET_CODE (loc) == VALUE);
+
+  bool existed;
+  rtx *slot = &local_get_addr_cache->get_or_insert (loc, &existed);
+  if (existed)
+    return *slot;
+
+  x = get_addr_from_global_cache (loc);
+
+  /* Tentative, avoiding infinite recursion.  */
+  *slot = x;
+
+  /* Recurse to cache local expansion of X, or if we need to search
+     for a VALUE in the expansion.  */
+  if (x != loc)
+    {
+      rtx nx = vt_canonicalize_addr (set, x);
+      if (nx != x)
+	{
+	  slot = local_get_addr_cache->get (loc);
+	  *slot = x = nx;
+	}
+      return x;
+    }
+
+  dv = dv_from_rtx (x);
+  var = shared_hash_find (set->vars, dv);
+  if (!var)
+    return x;
+
+  /* Look for an improved equivalent expression.  */
+  for (l = var->var_part[0].loc_chain; l; l = l->next)
+    {
+      rtx base = vt_get_canonicalize_base (l->loc);
+      if (GET_CODE (base) == VALUE
+	  && canon_value_cmp (base, loc))
+	{
+	  rtx nx = vt_canonicalize_addr (set, l->loc);
+	  if (x != nx)
+	    {
+	      slot = local_get_addr_cache->get (loc);
+	      *slot = x = nx;
+	    }
+	  break;
+	}
+    }
+
+  return x;
+}
+
+/* Canonicalize LOC using equivalences from SET in addition to those
+   in the cselib static table.  It expects a VALUE-based expression,
+   and it will only substitute VALUEs with other VALUEs or
+   function-global equivalences, so that, if two addresses have base
+   VALUEs that are locally or globally related in ways that
+   memrefs_conflict_p cares about, they will both canonicalize to
+   expressions that have the same base VALUE.
+
+   The use of VALUEs as canonical base addresses enables the canonical
+   RTXs to remain unchanged globally, if they resolve to a constant,
+   or throughout a basic block otherwise, so that they can be cached
+   and the cache needs not be invalidated when REGs, MEMs or such
+   change.  */
+
+static rtx
+vt_canonicalize_addr (dataflow_set *set, rtx oloc)
+{
+  HOST_WIDE_INT ofst = 0;
+  machine_mode mode = GET_MODE (oloc);
+  rtx loc = oloc;
+  rtx x;
+  bool retry = true;
+
+  while (retry)
+    {
+      while (GET_CODE (loc) == PLUS
+	     && GET_CODE (XEXP (loc, 1)) == CONST_INT)
+	{
+	  ofst += INTVAL (XEXP (loc, 1));
+	  loc = XEXP (loc, 0);
+	}
+
+      /* Alignment operations can't normally be combined, so just
+	 canonicalize the base and we're done.  We'll normally have
+	 only one stack alignment anyway.  */
+      if (GET_CODE (loc) == AND
+	  && GET_CODE (XEXP (loc, 1)) == CONST_INT
+	  && negative_power_of_two_p (INTVAL (XEXP (loc, 1))))
+	{
+	  x = vt_canonicalize_addr (set, XEXP (loc, 0));
+	  if (x != XEXP (loc, 0))
+	    loc = gen_rtx_AND (mode, x, XEXP (loc, 1));
+	  retry = false;
+	}
+
+      if (GET_CODE (loc) == VALUE)
+	{
+	  if (set)
+	    loc = get_addr_from_local_cache (set, loc);
+	  else
+	    loc = get_addr_from_global_cache (loc);
+
+	  /* Consolidate plus_constants.  */
+	  while (ofst && GET_CODE (loc) == PLUS
+		 && GET_CODE (XEXP (loc, 1)) == CONST_INT)
+	    {
+	      ofst += INTVAL (XEXP (loc, 1));
+	      loc = XEXP (loc, 0);
+	    }
+
+	  retry = false;
+	}
+      else
+	{
+	  x = canon_rtx (loc);
+	  if (retry)
+	    retry = (x != loc);
+	  loc = x;
+	}
+    }
+
+  /* Add OFST back in.  */
+  if (ofst)
+    {
+      /* Don't build new RTL if we can help it.  */
+      if (GET_CODE (oloc) == PLUS
+	  && XEXP (oloc, 0) == loc
+	  && INTVAL (XEXP (oloc, 1)) == ofst)
+	return oloc;
+
+      loc = plus_constant (mode, loc, ofst);
+    }
+
+  return loc;
+}
+
+/* Return true iff there's a true dependence between MLOC and LOC.
+   MADDR must be a canonicalized version of MLOC's address.  */
+
+static inline bool
+vt_canon_true_dep (dataflow_set *set, rtx mloc, rtx maddr, rtx loc)
+{
+  if (GET_CODE (loc) != MEM)
+    return false;
+
+  rtx addr = vt_canonicalize_addr (set, XEXP (loc, 0));
+  if (!canon_true_dependence (mloc, GET_MODE (mloc), maddr, loc, addr))
+    return false;
+
+  return true;
+}
+
+/* Hold parameters for the hashtab traversal function
+   drop_overlapping_mem_locs, see below.  */
+
+struct overlapping_mems
+{
+  dataflow_set *set;
+  rtx loc, addr;
+};
+
+/* Remove all MEMs that overlap with COMS->LOC from the location list
+   of a hash table entry for a onepart variable.  COMS->ADDR must be a
+   canonicalized form of COMS->LOC's address, and COMS->LOC must be
+   canonicalized itself.  */
+
+int
+drop_overlapping_mem_locs (variable **slot, overlapping_mems *coms)
+{
+  dataflow_set *set = coms->set;
+  rtx mloc = coms->loc, addr = coms->addr;
+  variable *var = *slot;
+
+  if (var->onepart != NOT_ONEPART)
+    {
+      location_chain *loc, **locp;
+      bool changed = false;
+      rtx cur_loc;
+
+      gcc_assert (var->n_var_parts == 1);
+
+      if (shared_var_p (var, set->vars))
+	{
+	  for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+	    if (vt_canon_true_dep (set, mloc, addr, loc->loc))
+	      break;
+
+	  if (!loc)
+	    return 1;
+
+	  slot = unshare_variable (set, slot, var, VAR_INIT_STATUS_UNKNOWN);
+	  var = *slot;
+	  gcc_assert (var->n_var_parts == 1);
+	}
+
+      if (VAR_LOC_1PAUX (var))
+	cur_loc = VAR_LOC_FROM (var);
+      else
+	cur_loc = var->var_part[0].cur_loc;
+
+      for (locp = &var->var_part[0].loc_chain, loc = *locp;
+	   loc; loc = *locp)
+	{
+	  if (!vt_canon_true_dep (set, mloc, addr, loc->loc))
+	    {
+	      locp = &loc->next;
+	      continue;
+	    }
+
+	  *locp = loc->next;
+	  /* If we have deleted the location which was last emitted
+	     we have to emit new location so add the variable to set
+	     of changed variables.  */
+	  if (cur_loc == loc->loc)
+	    {
+	      changed = true;
+	      var->var_part[0].cur_loc = NULL;
+	      if (VAR_LOC_1PAUX (var))
+		VAR_LOC_FROM (var) = NULL;
+	    }
+	  delete loc;
+	}
+
+      if (!var->var_part[0].loc_chain)
+	{
+	  var->n_var_parts--;
+	  changed = true;
+	}
+      if (changed)
+	variable_was_changed (var, set);
+    }
+
+  return 1;
+}
+
+/* Remove from SET all VALUE bindings to MEMs that overlap with LOC.  */
+
+static void
+clobber_overlapping_mems (dataflow_set *set, rtx loc)
+{
+  struct overlapping_mems coms;
+
+  gcc_checking_assert (GET_CODE (loc) == MEM);
+
+  coms.set = set;
+  coms.loc = canon_rtx (loc);
+  coms.addr = vt_canonicalize_addr (set, XEXP (loc, 0));
+
+  set->traversed_vars = set->vars;
+  shared_hash_htab (set->vars)
+    ->traverse <overlapping_mems*, drop_overlapping_mem_locs> (&coms);
+  set->traversed_vars = NULL;
+}
+
 /* Set the location of DV, OFFSET as the MEM LOC.  */
 
 static void
@@ -1816,6 +2356,7 @@
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
 
+  clobber_overlapping_mems (set, loc);
   decl = var_debug_decl (decl);
 
   if (initialized == VAR_INIT_STATUS_UNKNOWN)
@@ -1836,18 +2377,87 @@
   tree decl = MEM_EXPR (loc);
   HOST_WIDE_INT offset = INT_MEM_OFFSET (loc);
 
+  clobber_overlapping_mems (set, loc);
   decl = var_debug_decl (decl);
   if (clobber)
     clobber_variable_part (set, NULL, dv_from_decl (decl), offset, NULL);
   delete_variable_part (set, loc, dv_from_decl (decl), offset);
 }
 
+/* Return true if LOC should not be expanded for location expressions,
+   or used in them.  */
+
+static inline bool
+unsuitable_loc (rtx loc)
+{
+  switch (GET_CODE (loc))
+    {
+    case PC:
+    case SCRATCH:
+    case CC0:
+    case ASM_INPUT:
+    case ASM_OPERANDS:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Bind VAL to LOC in SET.  If MODIFIED, detach LOC from any values
+   bound to it.  */
+
+static inline void
+val_bind (dataflow_set *set, rtx val, rtx loc, bool modified)
+{
+  if (REG_P (loc))
+    {
+      if (modified)
+	var_regno_delete (set, REGNO (loc));
+      var_reg_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
+			dv_from_value (val), 0, NULL_RTX, INSERT);
+    }
+  else if (MEM_P (loc))
+    {
+      struct elt_loc_list *l = CSELIB_VAL_PTR (val)->locs;
+
+      if (modified)
+	clobber_overlapping_mems (set, loc);
+
+      if (l && GET_CODE (l->loc) == VALUE)
+	l = canonical_cselib_val (CSELIB_VAL_PTR (l->loc))->locs;
+
+      /* If this MEM is a global constant, we don't need it in the
+	 dynamic tables.  ??? We should test this before emitting the
+	 micro-op in the first place.  */
+      while (l)
+	if (GET_CODE (l->loc) == MEM && XEXP (l->loc, 0) == XEXP (loc, 0))
+	  break;
+	else
+	  l = l->next;
+
+      if (!l)
+	var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
+			  dv_from_value (val), 0, NULL_RTX, INSERT);
+    }
+  else
+    {
+      /* Other kinds of equivalences are necessarily static, at least
+	 so long as we do not perform substitutions while merging
+	 expressions.  */
+      gcc_unreachable ();
+      set_variable_part (set, loc, dv_from_value (val), 0,
+			 VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
+    }
+}
+
 /* Bind a value to a location it was just stored in.  If MODIFIED
    holds, assume the location was modified, detaching it from any
    values bound to it.  */
 
 static void
-val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
+val_store (dataflow_set *set, rtx val, rtx loc, rtx_insn *insn,
+	   bool modified)
 {
   cselib_val *v = CSELIB_VAL_PTR (val);
 
@@ -1855,10 +2465,10 @@
 
   if (dump_file)
     {
-      fprintf (dump_file, "%i: ", INSN_UID (insn));
+      fprintf (dump_file, "%i: ", insn ? INSN_UID (insn) : 0);
+      print_inline_rtx (dump_file, loc, 0);
+      fprintf (dump_file, " evaluates to ");
       print_inline_rtx (dump_file, val, 0);
-      fprintf (dump_file, " stored in ");
-      print_inline_rtx (dump_file, loc, 0);
       if (v->locs)
 	{
 	  struct elt_loc_list *l;
@@ -1871,19 +2481,19 @@
       fprintf (dump_file, "\n");
     }
 
-  if (REG_P (loc))
-    {
-      if (modified)
-	var_regno_delete (set, REGNO (loc));
-      var_reg_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
-			dv_from_value (val), 0, NULL_RTX, INSERT);
-    }
-  else if (MEM_P (loc))
-    var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
-		      dv_from_value (val), 0, NULL_RTX, INSERT);
-  else
-    set_variable_part (set, loc, dv_from_value (val), 0,
-		       VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
+  gcc_checking_assert (!unsuitable_loc (loc));
+
+  val_bind (set, val, loc, modified);
+}
+
+/* Clear (canonical address) slots that reference X.  */
+
+bool
+local_get_addr_clear_given_value (rtx const &, rtx *slot, rtx x)
+{
+  if (vt_get_canonicalize_base (*slot) == x)
+    *slot = NULL;
+  return true;
 }
 
 /* Reset this node, detaching all its equivalences.  Return the slot
@@ -1892,8 +2502,8 @@
 static void
 val_reset (dataflow_set *set, decl_or_value dv)
 {
-  variable var = shared_hash_find (set->vars, dv) ;
-  location_chain node;
+  variable *var = shared_hash_find (set->vars, dv) ;
+  location_chain *node;
   rtx cval;
 
   if (!var || !var->n_var_parts)
@@ -1901,6 +2511,27 @@
 
   gcc_assert (var->n_var_parts == 1);
 
+  if (var->onepart == ONEPART_VALUE)
+    {
+      rtx x = dv_as_value (dv);
+
+      /* Relationships in the global cache don't change, so reset the
+	 local cache entry only.  */
+      rtx *slot = local_get_addr_cache->get (x);
+      if (slot)
+	{
+	  /* If the value resolved back to itself, odds are that other
+	     values may have cached it too.  These entries now refer
+	     to the old X, so detach them too.  Entries that used the
+	     old X but resolved to something else remain ok as long as
+	     that something else isn't also reset.  */
+	  if (*slot == x)
+	    local_get_addr_cache
+	      ->traverse<rtx, local_get_addr_clear_given_value> (x);
+	  *slot = NULL;
+	}
+    }
+
   cval = NULL;
   for (node = var->var_part[0].loc_chain; node; node = node->next)
     if (GET_CODE (node->loc) == VALUE
@@ -1924,7 +2555,7 @@
     {
       decl_or_value cdv = dv_from_value (cval);
 
-      /* Keep the remaining values connected, accummulating links
+      /* Keep the remaining values connected, accumulating links
 	 in the canonical value.  */
       for (node = var->var_part[0].loc_chain; node; node = node->next)
 	{
@@ -1948,13 +2579,6 @@
     delete_variable_part (set, dv_as_value (dv), dv_from_value (cval), 0);
 
   clobber_variable_part (set, NULL, dv, 0, NULL);
-
-  /* ??? Should we make sure there aren't other available values or
-     variables whose values involve this one other than by
-     equivalence?  E.g., at the very least we should reset MEMs, those
-     shouldn't be too hard to find cselib-looking up the value as an
-     address, then locating the resulting value in our own hash
-     table.  */
 }
 
 /* Find the values in a given location and map the val to another
@@ -1962,7 +2586,7 @@
    value.  */
 
 static void
-val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
+val_resolve (dataflow_set *set, rtx val, rtx loc, rtx_insn *insn)
 {
   decl_or_value dv = dv_from_value (val);
 
@@ -1980,9 +2604,11 @@
 
   val_reset (set, dv);
 
+  gcc_checking_assert (!unsuitable_loc (loc));
+
   if (REG_P (loc))
     {
-      attrs node, found = NULL;
+      attrs *node, *found = NULL;
 
       for (node = set->regs[REGNO (loc)]; node; node = node->next)
 	if (dv_is_value_p (node->dv)
@@ -2003,18 +2629,13 @@
 
       /* If we didn't find any equivalence, we need to remember that
 	 this value is held in the named register.  */
-      if (!found)
-	var_reg_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
-			  dv_from_value (val), 0, NULL_RTX, INSERT);
-    }
-  else if (MEM_P (loc))
-    /* ??? Merge equivalent MEMs.  */
-    var_mem_decl_set (set, loc, VAR_INIT_STATUS_INITIALIZED,
-		      dv_from_value (val), 0, NULL_RTX, INSERT);
-  else
-    /* ??? Merge equivalent expressions.  */
-    set_variable_part (set, loc, dv_from_value (val), 0,
-		       VAR_INIT_STATUS_INITIALIZED, NULL_RTX, INSERT);
+      if (found)
+	return;
+    }
+  /* ??? Attempt to find and merge equivalent MEMs or other
+     expressions too.  */
+
+  val_bind (set, val, loc, false);
 }
 
 /* Initialize dataflow set SET to be empty.
@@ -2063,7 +2684,7 @@
 struct variable_union_info
 {
   /* Node of the location chain.  */
-  location_chain lc;
+  location_chain *lc;
 
   /* The sum of positions in the input chains.  */
   int pos;
@@ -2103,10 +2724,10 @@
    we keep the newest locations in the beginning.  */
 
 static int
-variable_union (variable src, dataflow_set *set)
-{
-  variable dst;
-  void **dstp;
+variable_union (variable *src, dataflow_set *set)
+{
+  variable *dst;
+  variable **dstp;
   int i, j, k;
 
   dstp = shared_hash_find_slot (set->vars, src->dv);
@@ -2124,15 +2745,16 @@
       return 1;
     }
   else
-    dst = (variable) *dstp;
+    dst = *dstp;
 
   gcc_assert (src->n_var_parts);
+  gcc_checking_assert (src->onepart == dst->onepart);
 
   /* We can combine one-part variables very efficiently, because their
      entries are in canonical order.  */
-  if (dv_onepart_p (src->dv))
-    {
-      location_chain *nodep, dnode, snode;
+  if (src->onepart)
+    {
+      location_chain **nodep, *dnode, *snode;
 
       gcc_assert (src->n_var_parts == 1
 		  && dst->n_var_parts == 1);
@@ -2151,17 +2773,17 @@
 
 	  if (r > 0)
 	    {
-	      location_chain nnode;
+	      location_chain *nnode;
 
 	      if (shared_var_p (dst, set->vars))
 		{
 		  dstp = unshare_variable (set, dstp, dst,
 					   VAR_INIT_STATUS_INITIALIZED);
-		  dst = (variable)*dstp;
+		  dst = *dstp;
 		  goto restart_onepart_unshared;
 		}
 
-	      *nodep = nnode = (location_chain) pool_alloc (loc_chain_pool);
+	      *nodep = nnode = new location_chain;
 	      nnode->loc = snode->loc;
 	      nnode->init = snode->init;
 	      if (!snode->set_src || MEM_P (snode->set_src))
@@ -2184,16 +2806,18 @@
       return 1;
     }
 
+  gcc_checking_assert (!src->onepart);
+
   /* Count the number of location parts, result is K.  */
   for (i = 0, j = 0, k = 0;
        i < src->n_var_parts && j < dst->n_var_parts; k++)
     {
-      if (src->var_part[i].offset == dst->var_part[j].offset)
+      if (VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
 	{
 	  i++;
 	  j++;
 	}
-      else if (src->var_part[i].offset < dst->var_part[j].offset)
+      else if (VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
 	i++;
       else
 	j++;
@@ -2203,12 +2827,12 @@
 
   /* We track only variables whose size is <= MAX_VAR_PARTS bytes
      thus there are at most MAX_VAR_PARTS different offsets.  */
-  gcc_assert (dv_onepart_p (dst->dv) ? k == 1 : k <= MAX_VAR_PARTS);
+  gcc_checking_assert (dst->onepart ? k == 1 : k <= MAX_VAR_PARTS);
 
   if (dst->n_var_parts != k && shared_var_p (dst, set->vars))
     {
       dstp = unshare_variable (set, dstp, dst, VAR_INIT_STATUS_UNKNOWN);
-      dst = (variable)*dstp;
+      dst = *dstp;
     }
 
   i = src->n_var_parts - 1;
@@ -2217,10 +2841,10 @@
 
   for (k--; k >= 0; k--)
     {
-      location_chain node, node2;
+      location_chain *node, *node2;
 
       if (i >= 0 && j >= 0
-	  && src->var_part[i].offset == dst->var_part[j].offset)
+	  && VAR_PART_OFFSET (src, i) == VAR_PART_OFFSET (dst, j))
 	{
 	  /* Compute the "sorted" union of the chains, i.e. the locations which
 	     are in both chains go first, they are sorted by the sum of
@@ -2252,7 +2876,7 @@
 		{
 		  dstp = unshare_variable (set, dstp, dst,
 					   VAR_INIT_STATUS_UNKNOWN);
-		  dst = (variable)*dstp;
+		  dst = (variable *)*dstp;
 		}
 	    }
 
@@ -2266,9 +2890,9 @@
 	  if (dst_l == 1)
 	    {
 	      /* The most common case, much simpler, no qsort is needed.  */
-	      location_chain dstnode = dst->var_part[j].loc_chain;
+	      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;
+	      VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j);
 	      node2 = dstnode;
 	      for (node = src->var_part[i].loc_chain; node; node = node->next)
 		if (!((REG_P (dstnode->loc)
@@ -2276,10 +2900,10 @@
 		       && REGNO (dstnode->loc) == REGNO (node->loc))
 		      || rtx_equal_p (dstnode->loc, node->loc)))
 		  {
-		    location_chain new_node;
+		    location_chain *new_node;
 
 		    /* Copy the location from SRC.  */
-		    new_node = (location_chain) pool_alloc (loc_chain_pool);
+		    new_node = new location_chain;
 		    new_node->loc = node->loc;
 		    new_node->init = node->init;
 		    if (!node->set_src || MEM_P (node->set_src))
@@ -2331,10 +2955,10 @@
 		    }
 		  if (jj >= dst_l)	/* The location has not been found.  */
 		    {
-		      location_chain new_node;
+		      location_chain *new_node;
 
 		      /* Copy the location from SRC.  */
-		      new_node = (location_chain) pool_alloc (loc_chain_pool);
+		      new_node = new location_chain;
 		      new_node->loc = node->loc;
 		      new_node->init = node->init;
 		      if (!node->set_src || MEM_P (node->set_src))
@@ -2406,31 +3030,31 @@
 		  dst->var_part[k].loc_chain = vui[0].lc;
 		}
 
-	      dst->var_part[k].offset = dst->var_part[j].offset;
+	      VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (dst, j);
 	    }
 	  i--;
 	  j--;
 	}
       else if ((i >= 0 && j >= 0
-		&& src->var_part[i].offset < dst->var_part[j].offset)
+		&& VAR_PART_OFFSET (src, i) < VAR_PART_OFFSET (dst, j))
 	       || i < 0)
 	{
 	  dst->var_part[k] = dst->var_part[j];
 	  j--;
 	}
       else if ((i >= 0 && j >= 0
-		&& src->var_part[i].offset > dst->var_part[j].offset)
+		&& VAR_PART_OFFSET (src, i) > VAR_PART_OFFSET (dst, j))
 	       || j < 0)
 	{
-	  location_chain *nextp;
+	  location_chain **nextp;
 
 	  /* Copy the chain from SRC.  */
 	  nextp = &dst->var_part[k].loc_chain;
 	  for (node = src->var_part[i].loc_chain; node; node = node->next)
 	    {
-	      location_chain new_lc;
-
-	      new_lc = (location_chain) pool_alloc (loc_chain_pool);
+	      location_chain *new_lc;
+
+	      new_lc = new location_chain;
 	      new_lc->next = NULL;
 	      new_lc->init = node->init;
 	      if (!node->set_src || MEM_P (node->set_src))
@@ -2443,7 +3067,7 @@
 	      nextp = &new_lc->next;
 	    }
 
-	  dst->var_part[k].offset = src->var_part[i].offset;
+	  VAR_PART_OFFSET (dst, k) = VAR_PART_OFFSET (src, i);
 	  i--;
 	}
       dst->var_part[k].cur_loc = NULL;
@@ -2452,7 +3076,7 @@
   if (flag_var_tracking_uninit)
     for (i = 0; i < src->n_var_parts && i < dst->n_var_parts; i++)
       {
-	location_chain node, node2;
+	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))
@@ -2483,10 +3107,11 @@
     }
   else
     {
-      htab_iterator hi;
-      variable var;
-
-      FOR_EACH_HTAB_ELEMENT (shared_hash_htab (src->vars), var, variable, hi)
+      variable_iterator_type hi;
+      variable *var;
+
+      FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (src->vars),
+				   var, variable, hi)
 	variable_union (var, dst);
     }
 }
@@ -2494,25 +3119,46 @@
 /* Whether the value is currently being expanded.  */
 #define VALUE_RECURSED_INTO(x) \
   (RTL_FLAG_CHECK2 ("VALUE_RECURSED_INTO", (x), VALUE, DEBUG_EXPR)->used)
-/* Whether the value is in changed_variables hash table.  */
+
+/* Whether no expansion was found, saving useless lookups.
+   It must only be set when VALUE_CHANGED is clear.  */
+#define NO_LOC_P(x) \
+  (RTL_FLAG_CHECK2 ("NO_LOC_P", (x), VALUE, DEBUG_EXPR)->return_val)
+
+/* Whether cur_loc in the value needs to be (re)computed.  */
 #define VALUE_CHANGED(x) \
   (RTL_FLAG_CHECK1 ("VALUE_CHANGED", (x), VALUE)->frame_related)
-/* Whether the decl is in changed_variables hash table.  */
+/* Whether cur_loc in the decl needs to be (re)computed.  */
 #define DECL_CHANGED(x) TREE_VISITED (x)
 
-/* Record that DV has been added into resp. removed from changed_variables
-   hashtable.  */
+/* Record (if NEWV) that DV needs to have its cur_loc recomputed.  For
+   user DECLs, this means they're in changed_variables.  Values and
+   debug exprs may be left with this flag set if no user variable
+   requires them to be evaluated.  */
 
 static inline void
 set_dv_changed (decl_or_value dv, bool newv)
 {
-  if (dv_is_value_p (dv))
-    VALUE_CHANGED (dv_as_value (dv)) = newv;
-  else
-    DECL_CHANGED (dv_as_decl (dv)) = newv;
-}
-
-/* Return true if DV is present in changed_variables hash table.  */
+  switch (dv_onepart_p (dv))
+    {
+    case ONEPART_VALUE:
+      if (newv)
+	NO_LOC_P (dv_as_value (dv)) = false;
+      VALUE_CHANGED (dv_as_value (dv)) = newv;
+      break;
+
+    case ONEPART_DEXPR:
+      if (newv)
+	NO_LOC_P (DECL_RTL_KNOWN_SET (dv_as_decl (dv))) = false;
+      /* Fall through.  */
+
+    default:
+      DECL_CHANGED (dv_as_decl (dv)) = newv;
+      break;
+    }
+}
+
+/* Return true if DV needs to have its cur_loc recomputed.  */
 
 static inline bool
 dv_changed_p (decl_or_value dv)
@@ -2527,28 +3173,27 @@
    any values recursively mentioned in the location lists.  VARS must
    be in star-canonical form.  */
 
-static location_chain
-find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
-{
-  location_chain node;
+static location_chain *
+find_loc_in_1pdv (rtx loc, variable *var, variable_table_type *vars)
+{
+  location_chain *node;
   enum rtx_code loc_code;
 
   if (!var)
     return NULL;
 
-  gcc_checking_assert (dv_onepart_p (var->dv));
+  gcc_checking_assert (var->onepart);
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_checking_assert (var->var_part[0].offset == 0);
   gcc_checking_assert (loc != dv_as_opaque (var->dv));
 
   loc_code = GET_CODE (loc);
   for (node = var->var_part[0].loc_chain; node; node = node->next)
     {
       decl_or_value dv;
-      variable rvar;
+      variable *rvar;
 
       if (GET_CODE (node->loc) != loc_code)
 	{
@@ -2586,10 +3231,12 @@
       gcc_checking_assert (!node->next);
 
       dv = dv_from_value (node->loc);
-      rvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+      rvar = vars->find_with_hash (dv, dv_htab_hash (dv));
       return find_loc_in_1pdv (loc, rvar, vars);
     }
 
+  /* ??? Gotta look in cselib_val locations too.  */
+
   return NULL;
 }
 
@@ -2610,10 +3257,10 @@
    loc_cmp order, and it is maintained as such.  */
 
 static void
-insert_into_intersection (location_chain *nodep, rtx loc,
+insert_into_intersection (location_chain **nodep, rtx loc,
 			  enum var_init_status status)
 {
-  location_chain node;
+  location_chain *node;
   int r;
 
   for (node = *nodep; node; nodep = &node->next, node = *nodep)
@@ -2625,7 +3272,7 @@
     else if (r > 0)
       break;
 
-  node = (location_chain) pool_alloc (loc_chain_pool);
+  node = new location_chain;
 
   node->loc = loc;
   node->set_src = NULL;
@@ -2634,28 +3281,27 @@
   *nodep = node;
 }
 
-/* Insert in DEST the intersection the locations present in both
+/* Insert in DEST the intersection of the locations present in both
    S1NODE and S2VAR, directly or indirectly.  S1NODE is from a
    variable in DSM->cur, whereas S2VAR is from DSM->src.  dvar is in
    DSM->dst.  */
 
 static void
-intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
-		      location_chain s1node, variable s2var)
+intersect_loc_chains (rtx val, location_chain **dest, struct dfset_merge *dsm,
+		      location_chain *s1node, variable *s2var)
 {
   dataflow_set *s1set = dsm->cur;
   dataflow_set *s2set = dsm->src;
-  location_chain found;
+  location_chain *found;
 
   if (s2var)
     {
-      location_chain s2node;
-
-      gcc_checking_assert (dv_onepart_p (s2var->dv));
+      location_chain *s2node;
+
+      gcc_checking_assert (s2var->onepart);
 
       if (s2var->n_var_parts)
 	{
-	  gcc_checking_assert (s2var->var_part[0].offset == 0);
 	  s2node = s2var->var_part[0].loc_chain;
 
 	  for (; s1node && s2node;
@@ -2687,7 +3333,7 @@
 	  && !VALUE_RECURSED_INTO (s1node->loc))
 	{
 	  decl_or_value dv = dv_from_value (s1node->loc);
-	  variable svar = shared_hash_find (s1set->vars, dv);
+	  variable *svar = shared_hash_find (s1set->vars, dv);
 	  if (svar)
 	    {
 	      if (svar->n_var_parts == 1)
@@ -2701,6 +3347,8 @@
 	    }
 	}
 
+      /* ??? gotta look in cselib_val locations too.  */
+
       /* ??? if the location is equivalent to any location in src,
 	 searched recursively
 
@@ -2790,6 +3438,18 @@
   if (GET_CODE (y) == VALUE)
     return 1;
 
+  /* Entry value is the least preferable kind of expression.  */
+  if (GET_CODE (x) == ENTRY_VALUE)
+    {
+      if (GET_CODE (y) != ENTRY_VALUE)
+	return 1;
+      gcc_assert (GET_MODE (x) == GET_MODE (y));
+      return loc_cmp (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y));
+    }
+
+  if (GET_CODE (y) == ENTRY_VALUE)
+    return -1;
+
   if (GET_CODE (x) == GET_CODE (y))
     /* Compare operands below.  */;
   else if (GET_CODE (x) < GET_CODE (y))
@@ -2880,190 +3540,44 @@
       default:
 	gcc_unreachable ();
       }
+  if (CONST_WIDE_INT_P (x))
+    {
+      /* Compare the vector length first.  */
+      if (CONST_WIDE_INT_NUNITS (x) >= CONST_WIDE_INT_NUNITS (y))
+	return 1;
+      else if (CONST_WIDE_INT_NUNITS (x) < CONST_WIDE_INT_NUNITS (y))
+	return -1;
+
+      /* Compare the vectors elements.  */;
+      for (j = CONST_WIDE_INT_NUNITS (x) - 1; j >= 0 ; j--)
+	{
+	  if (CONST_WIDE_INT_ELT (x, j) < CONST_WIDE_INT_ELT (y, j))
+	    return -1;
+	  if (CONST_WIDE_INT_ELT (x, j) > CONST_WIDE_INT_ELT (y, j))
+	    return 1;
+	}
+    }
 
   return 0;
 }
 
-/* If decl or value DVP refers to VALUE from *LOC, add backlinks
-   from VALUE to DVP.  */
-
-static int
-add_value_chain (rtx *loc, void *dvp)
-{
-  decl_or_value dv, ldv;
-  value_chain vc, nvc;
-  void **slot;
-
-  if (GET_CODE (*loc) == VALUE)
-    ldv = dv_from_value (*loc);
-  else if (GET_CODE (*loc) == DEBUG_EXPR)
-    ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
-  else
-    return 0;
-
-  if (dv_as_opaque (ldv) == dvp)
-    return 0;
-
-  dv = (decl_or_value) dvp;
-  slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
-				   INSERT);
-  if (!*slot)
-    {
-      vc = (value_chain) pool_alloc (value_chain_pool);
-      vc->dv = ldv;
-      vc->next = NULL;
-      vc->refcount = 0;
-      *slot = (void *) vc;
-    }
-  else
-    {
-      for (vc = ((value_chain) *slot)->next; vc; vc = vc->next)
-	if (dv_as_opaque (vc->dv) == dv_as_opaque (dv))
-	  break;
-      if (vc)
-	{
-	  vc->refcount++;
-	  return 0;
-	}
-    }
-  vc = (value_chain) *slot;
-  nvc = (value_chain) pool_alloc (value_chain_pool);
-  nvc->dv = dv;
-  nvc->next = vc->next;
-  nvc->refcount = 1;
-  vc->next = nvc;
-  return 0;
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, add backlinks
-   from those VALUEs to DVP.  */
-
-static void
-add_value_chains (decl_or_value dv, rtx loc)
-{
-  if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
-    {
-      add_value_chain (&loc, dv_as_opaque (dv));
-      return;
-    }
-  if (REG_P (loc))
-    return;
-  if (MEM_P (loc))
-    loc = XEXP (loc, 0);
-  for_each_rtx (&loc, add_value_chain, dv_as_opaque (dv));
-}
-
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those
-   VALUEs to DV.  Add the same time get rid of ASM_OPERANDS from locs list,
-   that is something we never can express in .debug_info and can prevent
-   reverse ops from being used.  */
-
-static void
-add_cselib_value_chains (decl_or_value dv)
-{
-  struct elt_loc_list **l;
-
-  for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;)
-    if (GET_CODE ((*l)->loc) == ASM_OPERANDS)
-      *l = (*l)->next;
-    else
-      {
-	for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv));
-	l = &(*l)->next;
-      }
-}
-
-/* If decl or value DVP refers to VALUE from *LOC, remove backlinks
-   from VALUE to DVP.  */
-
-static int
-remove_value_chain (rtx *loc, void *dvp)
-{
-  decl_or_value dv, ldv;
-  value_chain vc;
-  void **slot;
-
-  if (GET_CODE (*loc) == VALUE)
-    ldv = dv_from_value (*loc);
-  else if (GET_CODE (*loc) == DEBUG_EXPR)
-    ldv = dv_from_decl (DEBUG_EXPR_TREE_DECL (*loc));
-  else
-    return 0;
-
-  if (dv_as_opaque (ldv) == dvp)
-    return 0;
-
-  dv = (decl_or_value) dvp;
-  slot = htab_find_slot_with_hash (value_chains, ldv, dv_htab_hash (ldv),
-				   NO_INSERT);
-  for (vc = (value_chain) *slot; vc->next; vc = vc->next)
-    if (dv_as_opaque (vc->next->dv) == dv_as_opaque (dv))
-      {
-	value_chain dvc = vc->next;
-	gcc_assert (dvc->refcount > 0);
-	if (--dvc->refcount == 0)
-	  {
-	    vc->next = dvc->next;
-	    pool_free (value_chain_pool, dvc);
-	    if (vc->next == NULL && vc == (value_chain) *slot)
-	      {
-		pool_free (value_chain_pool, vc);
-		htab_clear_slot (value_chains, slot);
-	      }
-	  }
-	return 0;
-      }
-  gcc_unreachable ();
-}
-
-/* If decl or value DVP refers to VALUEs from within LOC, remove backlinks
-   from those VALUEs to DVP.  */
-
-static void
-remove_value_chains (decl_or_value dv, rtx loc)
-{
-  if (GET_CODE (loc) == VALUE || GET_CODE (loc) == DEBUG_EXPR)
-    {
-      remove_value_chain (&loc, dv_as_opaque (dv));
-      return;
-    }
-  if (REG_P (loc))
-    return;
-  if (MEM_P (loc))
-    loc = XEXP (loc, 0);
-  for_each_rtx (&loc, remove_value_chain, dv_as_opaque (dv));
-}
-
-#if ENABLE_CHECKING
-/* If CSELIB_VAL_PTR of value DV refer to VALUEs, remove backlinks from those
-   VALUEs to DV.  */
-
-static void
-remove_cselib_value_chains (decl_or_value dv)
-{
-  struct elt_loc_list *l;
-
-  for (l = CSELIB_VAL_PTR (dv_as_value (dv))->locs; l; l = l->next)
-    for_each_rtx (&l->loc, remove_value_chain, dv_as_opaque (dv));
-}
-
 /* Check the order of entries in one-part variables.   */
 
-static int
-canonicalize_loc_order_check (void **slot, void *data ATTRIBUTE_UNUSED)
-{
-  variable var = (variable) *slot;
-  decl_or_value dv = var->dv;
-  location_chain node, next;
+int
+canonicalize_loc_order_check (variable **slot,
+			      dataflow_set *data ATTRIBUTE_UNUSED)
+{
+  variable *var = *slot;
+  location_chain *node, *next;
 
 #ifdef ENABLE_RTL_CHECKING
   int i;
   for (i = 0; i < var->n_var_parts; i++)
     gcc_assert (var->var_part[0].cur_loc == NULL);
-  gcc_assert (!var->cur_loc_changed && !var->in_changed_variables);
+  gcc_assert (!var->in_changed_variables);
 #endif
 
-  if (!dv_onepart_p (dv))
+  if (!var->onepart)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3078,21 +3592,19 @@
 
   return 1;
 }
-#endif
 
 /* Mark with VALUE_RECURSED_INTO values that have neighbors that are
    more likely to be chosen as canonical for an equivalence set.
    Ensure less likely values can reach more likely neighbors, making
    the connections bidirectional.  */
 
-static int
-canonicalize_values_mark (void **slot, void *data)
-{
-  dataflow_set *set = (dataflow_set *)data;
-  variable var = (variable) *slot;
+int
+canonicalize_values_mark (variable **slot, dataflow_set *set)
+{
+  variable *var = *slot;
   decl_or_value dv = var->dv;
   rtx val;
-  location_chain node;
+  location_chain *node;
 
   if (!dv_is_value_p (dv))
     return 1;
@@ -3109,10 +3621,11 @@
 	else
 	  {
 	    decl_or_value odv = dv_from_value (node->loc);
-	    void **oslot = shared_hash_find_slot_noinsert (set->vars, odv);
-
-	    oslot = set_slot_part (set, val, oslot, odv, 0,
-				   node->init, NULL_RTX);
+	    variable **oslot;
+	    oslot = shared_hash_find_slot_noinsert (set->vars, odv);
+
+	    set_slot_part (set, val, oslot, odv, 0,
+			   node->init, NULL_RTX);
 
 	    VALUE_RECURSED_INTO (node->loc) = true;
 	  }
@@ -3124,20 +3637,19 @@
 /* Remove redundant entries from equivalence lists in onepart
    variables, canonicalizing equivalence sets into star shapes.  */
 
-static int
-canonicalize_values_star (void **slot, void *data)
-{
-  dataflow_set *set = (dataflow_set *)data;
-  variable var = (variable) *slot;
+int
+canonicalize_values_star (variable **slot, dataflow_set *set)
+{
+  variable *var = *slot;
   decl_or_value dv = var->dv;
-  location_chain node;
+  location_chain *node;
   decl_or_value cdv;
   rtx val, cval;
-  void **cslot;
+  variable **cslot;
   bool has_value;
   bool has_marks;
 
-  if (!dv_onepart_p (dv))
+  if (!var->onepart)
     return 1;
 
   gcc_checking_assert (var->n_var_parts == 1);
@@ -3199,7 +3711,7 @@
 		clobber_variable_part (set, NULL, var->dv, 0, NULL);
 		return 1;
 	      }
-	    var = (variable)*slot;
+	    var = *slot;
 	    gcc_assert (dv_is_value_p (var->dv));
 	    if (var->n_var_parts == 0)
 	      return 1;
@@ -3250,7 +3762,7 @@
 	  }
 	else if (GET_CODE (node->loc) == REG)
 	  {
-	    attrs list = set->regs[REGNO (node->loc)], *listp;
+	    attrs *list = set->regs[REGNO (node->loc)], **listp;
 
 	    /* Change an existing attribute referring to dv so that it
 	       refers to cdv, removing any duplicate this might
@@ -3279,7 +3791,7 @@
 		    if (dv_as_opaque (list->dv) == dv_as_opaque (cdv))
 		      {
 			*listp = list->next;
-			pool_free (attrs_pool, list);
+			delete list;
 			list = *listp;
 			break;
 		      }
@@ -3297,7 +3809,7 @@
 		    if (dv_as_opaque (list->dv) == dv_as_opaque (dv))
 		      {
 			*listp = list->next;
-			pool_free (attrs_pool, list);
+			delete list;
 			list = *listp;
 			break;
 		      }
@@ -3308,28 +3820,27 @@
 	    else
 	      gcc_unreachable ();
 
-#if ENABLE_CHECKING
-	    while (list)
-	      {
-		if (list->offset == 0
-		    && (dv_as_opaque (list->dv) == dv_as_opaque (dv)
-			|| dv_as_opaque (list->dv) == dv_as_opaque (cdv)))
-		  gcc_unreachable ();
-
-		list = list->next;
-	      }
-#endif
+	    if (flag_checking)
+	      while (list)
+		{
+		  if (list->offset == 0
+		      && (dv_as_opaque (list->dv) == dv_as_opaque (dv)
+			  || dv_as_opaque (list->dv) == dv_as_opaque (cdv)))
+		    gcc_unreachable ();
+
+		  list = list->next;
+		}
 	  }
       }
 
   if (val)
-    cslot = set_slot_part (set, val, cslot, cdv, 0,
-			   VAR_INIT_STATUS_INITIALIZED, NULL_RTX);
+    set_slot_part (set, val, cslot, cdv, 0,
+		   VAR_INIT_STATUS_INITIALIZED, NULL_RTX);
 
   slot = clobber_slot_part (set, cval, slot, 0, NULL);
 
   /* Variable may have been unshared.  */
-  var = (variable)*slot;
+  var = *slot;
   gcc_checking_assert (var->n_var_parts && var->var_part[0].loc_chain->loc == cval
 		       && var->var_part[0].loc_chain->next == NULL);
 
@@ -3346,20 +3857,19 @@
    have determined or even seen the canonical value of a set when we
    get to a variable that references another member of the set.  */
 
-static int
-canonicalize_vars_star (void **slot, void *data)
-{
-  dataflow_set *set = (dataflow_set *)data;
-  variable var = (variable) *slot;
+int
+canonicalize_vars_star (variable **slot, dataflow_set *set)
+{
+  variable *var = *slot;
   decl_or_value dv = var->dv;
-  location_chain node;
+  location_chain *node;
   rtx cval;
   decl_or_value cdv;
-  void **cslot;
-  variable cvar;
-  location_chain cnode;
-
-  if (!dv_onepart_p (dv) || dv_is_value_p (dv))
+  variable **cslot;
+  variable *cvar;
+  location_chain *cnode;
+
+  if (!var->onepart || var->onepart == ONEPART_VALUE)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3377,7 +3887,7 @@
   cslot = shared_hash_find_slot_noinsert (set->vars, cdv);
   if (!cslot)
     return 1;
-  cvar = (variable)*cslot;
+  cvar = *cslot;
   gcc_assert (cvar->n_var_parts == 1);
 
   cnode = cvar->var_part[0].loc_chain;
@@ -3395,7 +3905,7 @@
 
   slot = set_slot_part (set, cval, slot, dv, 0,
 			node->init, node->set_src);
-  slot = clobber_slot_part (set, cval, slot, 0, node->set_src);
+  clobber_slot_part (set, cval, slot, 0, node->set_src);
 
   return 1;
 }
@@ -3406,16 +3916,16 @@
    intersection.  */
 
 static int
-variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
+variable_merge_over_cur (variable *s1var, struct dfset_merge *dsm)
 {
   dataflow_set *dst = dsm->dst;
-  void **dstslot;
-  variable s2var, dvar = NULL;
+  variable **dstslot;
+  variable *s2var, *dvar = NULL;
   decl_or_value dv = s1var->dv;
-  bool onepart = dv_onepart_p (dv);
+  onepart_enum onepart = s1var->onepart;
   rtx val;
   hashval_t dvhash;
-  location_chain node, *nodep;
+  location_chain *node, **nodep;
 
   /* If the incoming onepart variable has an empty location list, then
      the intersection will be just as empty.  For other variables,
@@ -3426,8 +3936,7 @@
   if (!onepart)
     return variable_union (s1var, dst);
 
-  gcc_checking_assert (s1var->n_var_parts == 1
-		       && s1var->var_part[0].offset == 0);
+  gcc_checking_assert (s1var->n_var_parts == 1);
 
   dvhash = dv_htab_hash (dv);
   if (dv_is_value_p (dv))
@@ -3444,16 +3953,16 @@
 
   dsm->src_onepart_cnt--;
   gcc_assert (s2var->var_part[0].loc_chain
-	      && s2var->n_var_parts == 1
-	      && s2var->var_part[0].offset == 0);
+	      && s2var->onepart == onepart
+	      && s2var->n_var_parts == 1);
 
   dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
   if (dstslot)
     {
-      dvar = (variable)*dstslot;
+      dvar = *dstslot;
       gcc_assert (dvar->refcount == 1
-		  && dvar->n_var_parts == 1
-		  && dvar->var_part[0].offset == 0);
+		  && dvar->onepart == onepart
+		  && dvar->n_var_parts == 1);
       nodep = &dvar->var_part[0].loc_chain;
     }
   else
@@ -3480,15 +3989,18 @@
 	{
 	  if (node)
 	    {
-	      dvar = (variable) pool_alloc (dv_pool (dv));
+	      dvar = onepart_pool_allocate (onepart);
 	      dvar->dv = dv;
 	      dvar->refcount = 1;
 	      dvar->n_var_parts = 1;
-	      dvar->cur_loc_changed = false;
+	      dvar->onepart = onepart;
 	      dvar->in_changed_variables = false;
-	      dvar->var_part[0].offset = 0;
 	      dvar->var_part[0].loc_chain = node;
 	      dvar->var_part[0].cur_loc = NULL;
+	      if (onepart)
+		VAR_LOC_1PAUX (dvar) = NULL;
+	      else
+		VAR_PART_OFFSET (dvar, 0) = 0;
 
 	      dstslot
 		= shared_hash_find_slot_unshare_1 (&dst->vars, dv, dvhash,
@@ -3504,11 +4016,11 @@
   nodep = &dvar->var_part[0].loc_chain;
   while ((node = *nodep))
     {
-      location_chain *nextp = &node->next;
+      location_chain **nextp = &node->next;
 
       if (GET_CODE (node->loc) == REG)
 	{
-	  attrs list;
+	  attrs *list;
 
 	  for (list = dst->regs[REGNO (node->loc)]; list; list = list->next)
 	    if (GET_MODE (node->loc) == GET_MODE (list->loc)
@@ -3545,8 +4057,8 @@
       nodep = nextp;
     }
 
-  if (dvar != (variable)*dstslot)
-    dvar = (variable)*dstslot;
+  if (dvar != *dstslot)
+    dvar = *dstslot;
   nodep = &dvar->var_part[0].loc_chain;
 
   if (val)
@@ -3568,7 +4080,7 @@
       gcc_checking_assert (dstslot
 			   == shared_hash_find_slot_noinsert_1 (dst->vars,
 								dv, dvhash));
-      dvar = (variable)*dstslot;
+      dvar = *dstslot;
     }
   else
     {
@@ -3604,7 +4116,7 @@
 	      if (GET_CODE (node->loc) == VALUE)
 		{
 		  decl_or_value dv = dv_from_value (node->loc);
-		  void **slot = NULL;
+		  variable **slot = NULL;
 
 		  if (shared_hash_shared (dst->vars))
 		    slot = shared_hash_find_slot_noinsert (dst->vars, dv);
@@ -3613,15 +4125,15 @@
 							  INSERT);
 		  if (!*slot)
 		    {
-		      variable var = (variable) pool_alloc (dv_pool (dv));
+		      variable *var = onepart_pool_allocate (ONEPART_VALUE);
 		      var->dv = dv;
 		      var->refcount = 1;
 		      var->n_var_parts = 1;
-		      var->cur_loc_changed = false;
+		      var->onepart = ONEPART_VALUE;
 		      var->in_changed_variables = false;
-		      var->var_part[0].offset = 0;
 		      var->var_part[0].loc_chain = NULL;
 		      var->var_part[0].cur_loc = NULL;
+		      VAR_LOC_1PAUX (var) = NULL;
 		      *slot = var;
 		    }
 
@@ -3635,7 +4147,7 @@
 	  gcc_checking_assert (dstslot
 			       == shared_hash_find_slot_noinsert_1 (dst->vars,
 								    dv, dvhash));
-	  dvar = (variable)*dstslot;
+	  dvar = *dstslot;
 	}
     }
 
@@ -3664,15 +4176,14 @@
    variable_merge_over_cur().  */
 
 static int
-variable_merge_over_src (variable s2var, struct dfset_merge *dsm)
+variable_merge_over_src (variable *s2var, struct dfset_merge *dsm)
 {
   dataflow_set *dst = dsm->dst;
   decl_or_value dv = s2var->dv;
-  bool onepart = dv_onepart_p (dv);
-
-  if (!onepart)
-    {
-      void **dstp = shared_hash_find_slot (dst->vars, dv);
+
+  if (!s2var->onepart)
+    {
+      variable **dstp = shared_hash_find_slot (dst->vars, dv);
       *dstp = s2var;
       s2var->refcount++;
       return 1;
@@ -3693,19 +4204,17 @@
   struct dfset_merge dsm;
   int i;
   size_t src1_elems, src2_elems;
-  htab_iterator hi;
-  variable var;
-
-  src1_elems = htab_elements (shared_hash_htab (src1->vars));
-  src2_elems = htab_elements (shared_hash_htab (src2->vars));
+  variable_iterator_type hi;
+  variable *var;
+
+  src1_elems = shared_hash_htab (src1->vars)->elements ();
+  src2_elems = shared_hash_htab (src2->vars)->elements ();
   dataflow_set_init (dst);
   dst->stack_adjust = cur.stack_adjust;
   shared_hash_destroy (dst->vars);
-  dst->vars = (shared_hash) pool_alloc (shared_hash_pool);
+  dst->vars = new shared_hash;
   dst->vars->refcount = 1;
-  dst->vars->htab
-    = htab_create (MAX (src1_elems, src2_elems), variable_htab_hash,
-		   variable_htab_eq, variable_htab_free);
+  dst->vars->htab = new variable_table_type (MAX (src1_elems, src2_elems));
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     attrs_list_mpdv_union (&dst->regs[i], src1->regs[i], src2->regs[i]);
@@ -3715,9 +4224,11 @@
   dsm.cur = src1;
   dsm.src_onepart_cnt = 0;
 
-  FOR_EACH_HTAB_ELEMENT (shared_hash_htab (dsm.src->vars), var, variable, hi)
+  FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (dsm.src->vars),
+			       var, variable, hi)
     variable_merge_over_src (var, &dsm);
-  FOR_EACH_HTAB_ELEMENT (shared_hash_htab (dsm.cur->vars), var, variable, hi)
+  FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (dsm.cur->vars),
+			       var, variable, hi)
     variable_merge_over_cur (var, &dsm);
 
   if (dsm.src_onepart_cnt)
@@ -3732,7 +4243,7 @@
 dataflow_set_equiv_regs (dataflow_set *set)
 {
   int i;
-  attrs list, *listp;
+  attrs *list, **listp;
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
@@ -3787,7 +4298,7 @@
 	if (list->offset == 0 && dv_onepart_p (list->dv))
 	  {
 	    rtx cval = canon[(int)GET_MODE (list->loc)];
-	    void **slot;
+	    variable **slot;
 
 	    if (!cval)
 	      continue;
@@ -3811,11 +4322,11 @@
    be unshared and 1-part.  */
 
 static void
-remove_duplicate_values (variable var)
-{
-  location_chain node, *nodep;
-
-  gcc_assert (dv_onepart_p (var->dv));
+remove_duplicate_values (variable *var)
+{
+  location_chain *node, **nodep;
+
+  gcc_assert (var->onepart);
   gcc_assert (var->n_var_parts == 1);
   gcc_assert (var->refcount == 1);
 
@@ -3827,7 +4338,7 @@
 	    {
 	      /* Remove duplicate value node.  */
 	      *nodep = node->next;
-	      pool_free (loc_chain_pool, node);
+	      delete node;
 	      continue;
 	    }
 	  else
@@ -3858,15 +4369,14 @@
 /* Create values for incoming expressions associated with one-part
    variables that don't have value numbers for them.  */
 
-static int
-variable_post_merge_new_vals (void **slot, void *info)
-{
-  struct dfset_post_merge *dfpm = (struct dfset_post_merge *)info;
+int
+variable_post_merge_new_vals (variable **slot, dfset_post_merge *dfpm)
+{
   dataflow_set *set = dfpm->set;
-  variable var = (variable)*slot;
-  location_chain node;
-
-  if (!dv_onepart_p (var->dv) || !var->n_var_parts)
+  variable *var = *slot;
+  location_chain *node;
+
+  if (!var->onepart || !var->n_var_parts)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3882,13 +4392,13 @@
 	    gcc_assert (!VALUE_RECURSED_INTO (node->loc));
 	  else if (GET_CODE (node->loc) == REG)
 	    {
-	      attrs att, *attp, *curp = NULL;
+	      attrs *att, **attp, **curp = NULL;
 
 	      if (var->refcount != 1)
 		{
 		  slot = unshare_variable (set, slot, var,
 					   VAR_INIT_STATUS_INITIALIZED);
-		  var = (variable)*slot;
+		  var = *slot;
 		  goto restart;
 		}
 
@@ -3981,7 +4491,7 @@
 		 to be added when we bring perm in.  */
 	      att = *curp;
 	      *curp = att->next;
-	      pool_free (attrs_pool, att);
+	      delete att;
 	    }
 	}
 
@@ -3995,15 +4505,14 @@
 /* Reset values in the permanent set that are not associated with the
    chosen expression.  */
 
-static int
-variable_post_merge_perm_vals (void **pslot, void *info)
-{
-  struct dfset_post_merge *dfpm = (struct dfset_post_merge *)info;
+int
+variable_post_merge_perm_vals (variable **pslot, dfset_post_merge *dfpm)
+{
   dataflow_set *set = dfpm->set;
-  variable pvar = (variable)*pslot, var;
-  location_chain pnode;
+  variable *pvar = *pslot, *var;
+  location_chain *pnode;
   decl_or_value dv;
-  attrs att;
+  attrs *att;
 
   gcc_assert (dv_is_value_p (pvar->dv)
 	      && pvar->n_var_parts == 1);
@@ -4064,26 +4573,28 @@
   dfpm.set = set;
   dfpm.permp = permp;
 
-  htab_traverse (shared_hash_htab (set->vars), variable_post_merge_new_vals,
-		 &dfpm);
+  shared_hash_htab (set->vars)
+    ->traverse <dfset_post_merge*, variable_post_merge_new_vals> (&dfpm);
   if (*permp)
-    htab_traverse (shared_hash_htab ((*permp)->vars),
-		   variable_post_merge_perm_vals, &dfpm);
-  htab_traverse (shared_hash_htab (set->vars), canonicalize_values_star, set);
-  htab_traverse (shared_hash_htab (set->vars), canonicalize_vars_star, set);
+    shared_hash_htab ((*permp)->vars)
+      ->traverse <dfset_post_merge*, variable_post_merge_perm_vals> (&dfpm);
+  shared_hash_htab (set->vars)
+    ->traverse <dataflow_set *, canonicalize_values_star> (set);
+  shared_hash_htab (set->vars)
+    ->traverse <dataflow_set *, canonicalize_vars_star> (set);
 }
 
 /* Return a node whose loc is a MEM that refers to EXPR in the
    location list of a one-part variable or value VAR, or in that of
    any values recursively mentioned in the location lists.  */
 
-static location_chain
-find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
-{
-  location_chain node;
+static location_chain *
+find_mem_expr_in_1pdv (tree expr, rtx val, variable_table_type *vars)
+{
+  location_chain *node;
   decl_or_value dv;
-  variable var;
-  location_chain where = NULL;
+  variable *var;
+  location_chain *where = NULL;
 
   if (!val)
     return NULL;
@@ -4092,23 +4603,22 @@
 	      && !VALUE_RECURSED_INTO (val));
 
   dv = dv_from_value (val);
-  var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+  var = vars->find_with_hash (dv, dv_htab_hash (dv));
 
   if (!var)
     return NULL;
 
-  gcc_assert (dv_onepart_p (var->dv));
+  gcc_assert (var->onepart);
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_assert (var->var_part[0].offset == 0);
-
   VALUE_RECURSED_INTO (val) = true;
 
   for (node = var->var_part[0].loc_chain; node; node = node->next)
-    if (MEM_P (node->loc) && MEM_EXPR (node->loc) == expr
-	&& MEM_OFFSET (node->loc) == 0)
+    if (MEM_P (node->loc)
+	&& MEM_EXPR (node->loc) == expr
+	&& INT_MEM_OFFSET (node->loc) == 0)
       {
 	where = node;
 	break;
@@ -4150,16 +4660,15 @@
    one-part variable, except those whose MEM attributes map back to
    the variable itself, directly or within a VALUE.  */
 
-static int
-dataflow_set_preserve_mem_locs (void **slot, void *data)
-{
-  dataflow_set *set = (dataflow_set *) data;
-  variable var = (variable) *slot;
-
-  if (dv_is_decl_p (var->dv) && dv_onepart_p (var->dv))
+int
+dataflow_set_preserve_mem_locs (variable **slot, dataflow_set *set)
+{
+  variable *var = *slot;
+
+  if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR)
     {
       tree decl = dv_as_decl (var->dv);
-      location_chain loc, *locp;
+      location_chain *loc, **locp;
       bool changed = false;
 
       if (!var->n_var_parts)
@@ -4171,12 +4680,11 @@
 	{
 	  for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
 	    {
-	      /* We want to remove dying MEMs that doesn't refer to
-		 DECL.  */
+	      /* We want to remove dying MEMs that don't refer to DECL.  */
 	      if (GET_CODE (loc->loc) == MEM
 		  && (MEM_EXPR (loc->loc) != decl
-		      || MEM_OFFSET (loc->loc))
-		  && !mem_dies_at_call (loc->loc))
+		      || INT_MEM_OFFSET (loc->loc) != 0)
+		  && mem_dies_at_call (loc->loc))
 		break;
 	      /* We want to move here MEMs that do refer to DECL.  */
 	      else if (GET_CODE (loc->loc) == VALUE
@@ -4189,7 +4697,7 @@
 	    return 1;
 
 	  slot = unshare_variable (set, slot, var, VAR_INIT_STATUS_UNKNOWN);
-	  var = (variable)*slot;
+	  var = *slot;
 	  gcc_assert (var->n_var_parts == 1);
 	}
 
@@ -4199,7 +4707,7 @@
 	  rtx old_loc = loc->loc;
 	  if (GET_CODE (old_loc) == VALUE)
 	    {
-	      location_chain mem_node
+	      location_chain *mem_node
 		= find_mem_expr_in_1pdv (decl, loc->loc,
 					 shared_hash_htab (set->vars));
 
@@ -4219,7 +4727,7 @@
 
 	  if (GET_CODE (loc->loc) != MEM
 	      || (MEM_EXPR (loc->loc) == decl
-		  && MEM_OFFSET (loc->loc) == 0)
+		  && INT_MEM_OFFSET (loc->loc) == 0)
 	      || !mem_dies_at_call (loc->loc))
 	    {
 	      if (old_loc != loc->loc && emit_notes)
@@ -4228,10 +4736,7 @@
 		    {
 		      changed = true;
 		      var->var_part[0].cur_loc = NULL;
-		      var->cur_loc_changed = true;
 		    }
-		  add_value_chains (var->dv, loc->loc);
-		  remove_value_chains (var->dv, old_loc);
 		}
 	      locp = &loc->next;
 	      continue;
@@ -4239,16 +4744,14 @@
 
 	  if (emit_notes)
 	    {
-	      remove_value_chains (var->dv, old_loc);
 	      if (old_loc == var->var_part[0].cur_loc)
 		{
 		  changed = true;
 		  var->var_part[0].cur_loc = NULL;
-		  var->cur_loc_changed = true;
 		}
 	    }
 	  *locp = loc->next;
-	  pool_free (loc_chain_pool, loc);
+	  delete loc;
 	}
 
       if (!var->var_part[0].loc_chain)
@@ -4264,18 +4767,18 @@
 }
 
 /* Remove all MEMs from the location list of a hash table entry for a
-   value.  */
-
-static int
-dataflow_set_remove_mem_locs (void **slot, void *data)
-{
-  dataflow_set *set = (dataflow_set *) data;
-  variable var = (variable) *slot;
-
-  if (dv_is_value_p (var->dv))
-    {
-      location_chain loc, *locp;
+   onepart variable.  */
+
+int
+dataflow_set_remove_mem_locs (variable **slot, dataflow_set *set)
+{
+  variable *var = *slot;
+
+  if (var->onepart != NOT_ONEPART)
+    {
+      location_chain *loc, **locp;
       bool changed = false;
+      rtx cur_loc;
 
       gcc_assert (var->n_var_parts == 1);
 
@@ -4290,10 +4793,15 @@
 	    return 1;
 
 	  slot = unshare_variable (set, slot, var, VAR_INIT_STATUS_UNKNOWN);
-	  var = (variable)*slot;
+	  var = *slot;
 	  gcc_assert (var->n_var_parts == 1);
 	}
 
+      if (VAR_LOC_1PAUX (var))
+	cur_loc = VAR_LOC_FROM (var);
+      else
+	cur_loc = var->var_part[0].cur_loc;
+
       for (locp = &var->var_part[0].loc_chain, loc = *locp;
 	   loc; loc = *locp)
 	{
@@ -4304,19 +4812,18 @@
 	      continue;
 	    }
 
-	  if (emit_notes)
-	    remove_value_chains (var->dv, loc->loc);
 	  *locp = loc->next;
 	  /* If we have deleted the location which was last emitted
 	     we have to emit new location so add the variable to set
 	     of changed variables.  */
-	  if (var->var_part[0].cur_loc == loc->loc)
+	  if (cur_loc == loc->loc)
 	    {
 	      changed = true;
 	      var->var_part[0].cur_loc = NULL;
-	      var->cur_loc_changed = true;
+	      if (VAR_LOC_1PAUX (var))
+		VAR_LOC_FROM (var) = NULL;
 	    }
-	  pool_free (loc_chain_pool, loc);
+	  delete loc;
 	}
 
       if (!var->var_part[0].loc_chain)
@@ -4335,22 +4842,26 @@
    registers, as well as associations between MEMs and VALUEs.  */
 
 static void
-dataflow_set_clear_at_call (dataflow_set *set)
-{
-  int r;
-
-  for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
-    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, r))
-      var_regno_delete (set, r);
+dataflow_set_clear_at_call (dataflow_set *set, rtx_insn *call_insn)
+{
+  unsigned int r;
+  hard_reg_set_iterator hrsi;
+  HARD_REG_SET invalidated_regs;
+
+  get_call_reg_set_usage (call_insn, &invalidated_regs,
+			  regs_invalidated_by_call);
+
+  EXECUTE_IF_SET_IN_HARD_REG_SET (invalidated_regs, 0, r, hrsi)
+    var_regno_delete (set, r);
 
   if (MAY_HAVE_DEBUG_INSNS)
     {
       set->traversed_vars = set->vars;
-      htab_traverse (shared_hash_htab (set->vars),
-		     dataflow_set_preserve_mem_locs, set);
+      shared_hash_htab (set->vars)
+	->traverse <dataflow_set *, dataflow_set_preserve_mem_locs> (set);
       set->traversed_vars = set->vars;
-      htab_traverse (shared_hash_htab (set->vars), dataflow_set_remove_mem_locs,
-		     set);
+      shared_hash_htab (set->vars)
+	->traverse <dataflow_set *, dataflow_set_remove_mem_locs> (set);
       set->traversed_vars = NULL;
     }
 }
@@ -4358,7 +4869,7 @@
 static bool
 variable_part_different_p (variable_part *vp1, variable_part *vp2)
 {
-  location_chain lc1, lc2;
+  location_chain *lc1, *lc2;
 
   for (lc1 = vp1->loc_chain; lc1; lc1 = lc1->next)
     {
@@ -4382,9 +4893,9 @@
    They must be in canonical order.  */
 
 static bool
-onepart_variable_different_p (variable var1, variable var2)
-{
-  location_chain lc1, lc2;
+onepart_variable_different_p (variable *var1, variable *var2)
+{
+  location_chain *lc1, *lc2;
 
   if (var1 == var2)
     return false;
@@ -4408,30 +4919,91 @@
   return lc1 != lc2;
 }
 
+/* Return true if one-part variables VAR1 and VAR2 are different.
+   They must be in canonical order.  */
+
+static void
+dump_onepart_variable_differences (variable *var1, variable *var2)
+{
+  location_chain *lc1, *lc2;
+
+  gcc_assert (var1 != var2);
+  gcc_assert (dump_file);
+  gcc_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
+  gcc_assert (var1->n_var_parts == 1
+	      && var2->n_var_parts == 1);
+
+  lc1 = var1->var_part[0].loc_chain;
+  lc2 = var2->var_part[0].loc_chain;
+
+  gcc_assert (lc1 && lc2);
+
+  while (lc1 && lc2)
+    {
+      switch (loc_cmp (lc1->loc, lc2->loc))
+	{
+	case -1:
+	  fprintf (dump_file, "removed: ");
+	  print_rtl_single (dump_file, lc1->loc);
+	  lc1 = lc1->next;
+	  continue;
+	case 0:
+	  break;
+	case 1:
+	  fprintf (dump_file, "added: ");
+	  print_rtl_single (dump_file, lc2->loc);
+	  lc2 = lc2->next;
+	  continue;
+	default:
+	  gcc_unreachable ();
+	}
+      lc1 = lc1->next;
+      lc2 = lc2->next;
+    }
+
+  while (lc1)
+    {
+      fprintf (dump_file, "removed: ");
+      print_rtl_single (dump_file, lc1->loc);
+      lc1 = lc1->next;
+    }
+
+  while (lc2)
+    {
+      fprintf (dump_file, "added: ");
+      print_rtl_single (dump_file, lc2->loc);
+      lc2 = lc2->next;
+    }
+}
+
 /* Return true if variables VAR1 and VAR2 are different.  */
 
 static bool
-variable_different_p (variable var1, variable var2)
+variable_different_p (variable *var1, variable *var2)
 {
   int i;
 
   if (var1 == var2)
     return false;
 
+  if (var1->onepart != var2->onepart)
+    return true;
+
   if (var1->n_var_parts != var2->n_var_parts)
     return true;
 
+  if (var1->onepart && var1->n_var_parts)
+    {
+      gcc_checking_assert (dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv)
+			   && var1->n_var_parts == 1);
+      /* One-part values have locations in a canonical order.  */
+      return onepart_variable_different_p (var1, var2);
+    }
+
   for (i = 0; i < var1->n_var_parts; i++)
     {
-      if (var1->var_part[i].offset != var2->var_part[i].offset)
+      if (VAR_PART_OFFSET (var1, i) != VAR_PART_OFFSET (var2, i))
 	return true;
-      /* One-part values have locations in a canonical order.  */
-      if (i == 0 && var1->var_part[i].offset == 0 && dv_onepart_p (var1->dv))
-	{
-	  gcc_assert (var1->n_var_parts == 1
-		      && dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
-	  return onepart_variable_different_p (var1, var2);
-	}
       if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
 	return true;
       if (variable_part_different_p (&var2->var_part[i], &var1->var_part[i]))
@@ -4445,21 +5017,34 @@
 static bool
 dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
 {
-  htab_iterator hi;
-  variable var1;
+  variable_iterator_type hi;
+  variable *var1;
+  bool diffound = false;
+  bool details = (dump_file && (dump_flags & TDF_DETAILS));
+
+#define RETRUE					\
+  do						\
+    {						\
+      if (!details)				\
+	return true;				\
+      else					\
+	diffound = true;			\
+    }						\
+  while (0)
 
   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;
-
-  FOR_EACH_HTAB_ELEMENT (shared_hash_htab (old_set->vars), var1, variable, hi)
-    {
-      htab_t htab = shared_hash_htab (new_set->vars);
-      variable var2 = (variable) htab_find_with_hash (htab, var1->dv,
-						      dv_htab_hash (var1->dv));
+  if (shared_hash_htab (old_set->vars)->elements ()
+      != shared_hash_htab (new_set->vars)->elements ())
+    RETRUE;
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (old_set->vars),
+			       var1, variable, hi)
+    {
+      variable_table_type *htab = shared_hash_htab (new_set->vars);
+      variable *var2 = htab->find_with_hash (var1->dv, dv_htab_hash (var1->dv));
+
       if (!var2)
 	{
 	  if (dump_file && (dump_flags & TDF_DETAILS))
@@ -4467,26 +5052,49 @@
 	      fprintf (dump_file, "dataflow difference found: removal of:\n");
 	      dump_var (var1);
 	    }
-	  return true;
-	}
-
-      if (variable_different_p (var1, var2))
-	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
+	  RETRUE;
+	}
+      else if (variable_different_p (var1, var2))
+	{
+	  if (details)
 	    {
 	      fprintf (dump_file, "dataflow difference found: "
 		       "old and new follow:\n");
 	      dump_var (var1);
+	      if (dv_onepart_p (var1->dv))
+		dump_onepart_variable_differences (var1, var2);
 	      dump_var (var2);
 	    }
-	  return true;
-	}
-    }
-
-  /* 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 false;
+	  RETRUE;
+	}
+    }
+
+  /* There's no need to traverse the second hashtab unless we want to
+     print the details.  If both have the same number of elements and
+     the second one had all entries found in the first one, then the
+     second can't have any extra entries.  */
+  if (!details)
+    return diffound;
+
+  FOR_EACH_HASH_TABLE_ELEMENT (*shared_hash_htab (new_set->vars),
+			       var1, variable, hi)
+    {
+      variable_table_type *htab = shared_hash_htab (old_set->vars);
+      variable *var2 = htab->find_with_hash (var1->dv, dv_htab_hash (var1->dv));
+      if (!var2)
+	{
+	  if (details)
+	    {
+	      fprintf (dump_file, "dataflow difference found: addition of:\n");
+	      dump_var (var1);
+	    }
+	  RETRUE;
+	}
+    }
+
+#undef RETRUE
+
+  return diffound;
 }
 
 /* Free the contents of dataflow set SET.  */
@@ -4503,40 +5111,26 @@
   set->vars = NULL;
 }
 
-/* Return true if RTL X contains a SYMBOL_REF.  */
+/* Return true if T is a tracked parameter with non-degenerate record type.  */
 
 static bool
-contains_symbol_ref (rtx x)
-{
-  const char *fmt;
-  RTX_CODE code;
-  int i;
-
-  if (!x)
+tracked_record_parameter_p (tree t)
+{
+  if (TREE_CODE (t) != PARM_DECL)
+    return false;
+
+  if (DECL_MODE (t) == BLKmode)
     return false;
 
-  code = GET_CODE (x);
-  if (code == SYMBOL_REF)
-    return true;
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-	{
-	  if (contains_symbol_ref (XEXP (x, i)))
-	    return true;
-	}
-      else if (fmt[i] == 'E')
-	{
-	  int j;
-	  for (j = 0; j < XVECLEN (x, i); j++)
-	    if (contains_symbol_ref (XVECEXP (x, i, j)))
-	      return true;
-	}
-    }
-
-  return false;
+  tree type = TREE_TYPE (t);
+  if (TREE_CODE (type) != RECORD_TYPE)
+    return false;
+
+  if (TYPE_FIELDS (type) == NULL_TREE
+      || DECL_CHAIN (TYPE_FIELDS (type)) == NULL_TREE)
+    return false;
+
+  return true;
 }
 
 /* Shall EXPR be tracked?  */
@@ -4551,7 +5145,7 @@
     return DECL_RTL_SET_P (expr);
 
   /* If EXPR is not a parameter or a variable do not track it.  */
-  if (TREE_CODE (expr) != VAR_DECL && TREE_CODE (expr) != PARM_DECL)
+  if (!VAR_P (expr) && TREE_CODE (expr) != PARM_DECL)
     return 0;
 
   /* It also must have a name...  */
@@ -4567,21 +5161,25 @@
      don't need to track this expression if the ultimate declaration is
      ignored.  */
   realdecl = expr;
-  if (DECL_DEBUG_EXPR_IS_FROM (realdecl))
+  if (VAR_P (realdecl) && DECL_HAS_DEBUG_EXPR_P (realdecl))
     {
       realdecl = DECL_DEBUG_EXPR (realdecl);
-      if (realdecl == NULL_TREE)
-	realdecl = expr;
-      else if (!DECL_P (realdecl))
-	{
-	  if (handled_component_p (realdecl))
+      if (!DECL_P (realdecl))
+	{
+	  if (handled_component_p (realdecl)
+	      || (TREE_CODE (realdecl) == MEM_REF
+		  && TREE_CODE (TREE_OPERAND (realdecl, 0)) == ADDR_EXPR))
 	    {
 	      HOST_WIDE_INT bitsize, bitpos, maxsize;
+	      bool reverse;
 	      tree innerdecl
 		= get_ref_base_and_extent (realdecl, &bitpos, &bitsize,
-					   &maxsize);
+					   &maxsize, &reverse);
 	      if (!DECL_P (innerdecl)
 		  || DECL_IGNORED_P (innerdecl)
+		  /* Do not track declarations for parts of tracked record
+		     parameters since we want to track them as a whole.  */
+		  || tracked_record_parameter_p (innerdecl)
 		  || TREE_STATIC (innerdecl)
 		  || bitsize <= 0
 		  || bitpos + bitsize > 256
@@ -4614,7 +5212,7 @@
      char **_dl_argv;
   */
   if (decl_rtl && MEM_P (decl_rtl)
-      && contains_symbol_ref (XEXP (decl_rtl, 0)))
+      && contains_symbol_ref_p (XEXP (decl_rtl, 0)))
     return 0;
 
   /* If RTX is a memory it should not be very large (because it would be
@@ -4622,11 +5220,12 @@
   if (decl_rtl && MEM_P (decl_rtl))
     {
       /* Do not track structures and arrays.  */
-      if (GET_MODE (decl_rtl) == BLKmode
-	  || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
+      if ((GET_MODE (decl_rtl) == BLKmode
+	   || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
+	  && !tracked_record_parameter_p (realdecl))
 	return 0;
-      if (MEM_SIZE (decl_rtl)
-	  && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
+      if (MEM_SIZE_KNOWN_P (decl_rtl)
+	  && MEM_SIZE (decl_rtl) > MAX_VAR_PARTS)
 	return 0;
     }
 
@@ -4681,9 +5280,9 @@
 
 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;
+	     machine_mode *mode_out, HOST_WIDE_INT *offset_out)
+{
+  machine_mode mode;
 
   if (expr == NULL || !track_expr_p (expr, true))
     return false;
@@ -4693,10 +5292,10 @@
   mode = GET_MODE (loc);
   if (REG_P (loc) && !HARD_REGISTER_NUM_P (ORIGINAL_REGNO (loc)))
     {
-      enum machine_mode pseudo_mode;
+      machine_mode pseudo_mode;
 
       pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (loc));
-      if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (pseudo_mode))
+      if (paradoxical_subreg_p (mode, pseudo_mode))
 	{
 	  offset += byte_lowpart_offset (pseudo_mode, mode);
 	  mode = pseudo_mode;
@@ -4710,10 +5309,10 @@
      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))
+  if ((paradoxical_subreg_p (mode, DECL_MODE (expr))
        || (store_reg_p
 	   && !COMPLEX_MODE_P (DECL_MODE (expr))
-	   && hard_regno_nregs[REGNO (loc)][DECL_MODE (expr)] == 1))
+	   && hard_regno_nregs (REGNO (loc), DECL_MODE (expr)) == 1))
       && offset + byte_lowpart_offset (DECL_MODE (expr), mode) == 0)
     {
       mode = DECL_MODE (expr);
@@ -4735,16 +5334,16 @@
    on the returned value are updated.  */
 
 static rtx
-var_lowpart (enum machine_mode mode, rtx loc)
+var_lowpart (machine_mode mode, rtx loc)
 {
   unsigned int offset, reg_offset, regno;
 
+  if (GET_MODE (loc) == mode)
+    return loc;
+
   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))
@@ -4761,7 +5360,7 @@
 struct count_use_info
 {
   /* The insn where the RTX is.  */
-  rtx insn;
+  rtx_insn *insn;
 
   /* The basic block where insn is.  */
   basic_block bb;
@@ -4777,7 +5376,7 @@
 /* Find a VALUE corresponding to X.   */
 
 static inline cselib_val *
-find_use_val (rtx x, enum machine_mode mode, struct count_use_info *cui)
+find_use_val (rtx x, machine_mode mode, struct count_use_info *cui)
 {
   int i;
 
@@ -4808,17 +5407,6 @@
   return NULL;
 }
 
-/* Helper function to get mode of MEM's address.  */
-
-static inline enum machine_mode
-get_address_mode (rtx mem)
-{
-  enum machine_mode mode = GET_MODE (XEXP (mem, 0));
-  if (mode != VOIDmode)
-    return mode;
-  return targetm.addr_space.address_mode (MEM_ADDR_SPACE (mem));
-}
-
 /* Replace all registers and addresses in an expression with VALUE
    expressions that map back to them, unless the expression is a
    register.  If no mapping is or can be performed, returns NULL.  */
@@ -4826,7 +5414,7 @@
 static rtx
 replace_expr_with_values (rtx loc)
 {
-  if (REG_P (loc))
+  if (REG_P (loc) || GET_CODE (loc) == ENTRY_VALUE)
     return NULL;
   else if (MEM_P (loc))
     {
@@ -4842,11 +5430,23 @@
     return cselib_subst_to_values (loc, VOIDmode);
 }
 
+/* Return true if X contains a DEBUG_EXPR.  */
+
+static bool
+rtx_debug_expr_p (const_rtx x)
+{
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, x, ALL)
+    if (GET_CODE (*iter) == DEBUG_EXPR)
+      return true;
+  return false;
+}
+
 /* Determine what kind of micro operation to choose for a USE.  Return
    MO_CLOBBER if no micro operation is to be generated.  */
 
 static enum micro_operation_type
-use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
+use_type (rtx loc, struct count_use_info *cui, machine_mode *modep)
 {
   tree expr;
 
@@ -4923,7 +5523,13 @@
       else if (target_for_debug_bind (var_debug_decl (expr)))
 	return MO_CLOBBER;
       else if (track_loc_p (loc, expr, INT_MEM_OFFSET (loc),
-			    false, modep, NULL))
+			    false, modep, NULL)
+	       /* Multi-part variables shouldn't refer to one-part
+		  variable names such as VALUEs (never happens) or
+		  DEBUG_EXPRs (only happens in the presence of debug
+		  insns).  */
+	       && (!MAY_HAVE_DEBUG_INSNS
+		   || !rtx_debug_expr_p (XEXP (loc, 0))))
 	return MO_USE;
       else
 	return MO_CLOBBER;
@@ -4936,11 +5542,11 @@
    INSN of BB.  */
 
 static inline void
-log_op_type (rtx x, basic_block bb, rtx insn,
+log_op_type (rtx x, basic_block bb, rtx_insn *insn,
 	     enum micro_operation_type mopt, FILE *out)
 {
   fprintf (out, "bb %i op %i insn %i %s ",
-	   bb->index, VEC_length (micro_operation, VTI (bb)->mos),
+	   bb->index, VTI (bb)->mos.length (),
 	   INSN_UID (insn), micro_operation_type_name[mopt]);
   print_inline_rtx (out, x, 2);
   fputc ('\n', out);
@@ -4963,13 +5569,9 @@
    MO_CLOBBER as well.  */
 #define VAL_EXPR_IS_CLOBBERED(x) \
   (RTL_FLAG_CHECK1 ("VAL_EXPR_IS_CLOBBERED", (x), CONCAT)->unchanging)
-/* Whether the location is a CONCAT of the MO_VAL_SET expression and
-   a reverse operation that should be handled afterwards.  */
-#define VAL_EXPR_HAS_REVERSE(x) \
-  (RTL_FLAG_CHECK1 ("VAL_EXPR_HAS_REVERSE", (x), CONCAT)->return_val)
 
 /* All preserved VALUEs.  */
-static VEC (rtx, heap) *preserved_values;
+static vec<rtx> preserved_values;
 
 /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes.  */
 
@@ -4977,45 +5579,48 @@
 preserve_value (cselib_val *val)
 {
   cselib_preserve_value (val);
-  VEC_safe_push (rtx, heap, preserved_values, val->val_rtx);
+  preserved_values.safe_push (val->val_rtx);
 }
 
 /* Helper function for MO_VAL_LOC handling.  Return non-zero if
    any rtxes not suitable for CONST use not replaced by VALUEs
    are discovered.  */
 
-static int
-non_suitable_const (rtx *x, void *data ATTRIBUTE_UNUSED)
-{
-  if (*x == NULL_RTX)
-    return 0;
-
-  switch (GET_CODE (*x))
-    {
-    case REG:
-    case DEBUG_EXPR:
-    case PC:
-    case SCRATCH:
-    case CC0:
-    case ASM_INPUT:
-    case ASM_OPERANDS:
-      return 1;
-    case MEM:
-      return !MEM_READONLY_P (*x);
-    default:
-      return 0;
-    }
+static bool
+non_suitable_const (const_rtx x)
+{
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, x, ALL)
+    {
+      const_rtx x = *iter;
+      switch (GET_CODE (x))
+	{
+	case REG:
+	case DEBUG_EXPR:
+	case PC:
+	case SCRATCH:
+	case CC0:
+	case ASM_INPUT:
+	case ASM_OPERANDS:
+	  return true;
+	case MEM:
+	  if (!MEM_READONLY_P (x))
+	    return true;
+	  break;
+	default:
+	  break;
+	}
+    }
+  return false;
 }
 
 /* Add uses (register and memory references) LOC which will be tracked
-   to VTI (bb)->mos.  INSN is instruction which the LOC is part of.  */
-
-static int
-add_uses (rtx *ploc, void *data)
-{
-  rtx loc = *ploc;
-  enum machine_mode mode = VOIDmode;
-  struct count_use_info *cui = (struct count_use_info *)data;
+   to VTI (bb)->mos.  */
+
+static void
+add_uses (rtx loc, struct count_use_info *cui)
+{
+  machine_mode mode = VOIDmode;
   enum micro_operation_type type = use_type (loc, cui, &mode);
 
   if (type != MO_CLOBBER)
@@ -5037,44 +5642,31 @@
 
 	  if (MEM_P (vloc)
 	      && !REG_P (XEXP (vloc, 0))
-	      && !MEM_P (XEXP (vloc, 0))
-	      && (GET_CODE (XEXP (vloc, 0)) != PLUS
-		  || XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx
-		  || !CONST_INT_P (XEXP (XEXP (vloc, 0), 1))))
+	      && !MEM_P (XEXP (vloc, 0)))
 	    {
 	      rtx mloc = vloc;
-	      enum machine_mode address_mode = get_address_mode (mloc);
+	      machine_mode address_mode = get_address_mode (mloc);
 	      cselib_val *val
 		= cselib_lookup (XEXP (mloc, 0), address_mode, 0,
 				 GET_MODE (mloc));
 
 	      if (val && !cselib_preserved_value_p (val))
-		{
-		  micro_operation moa;
-		  preserve_value (val);
-		  mloc = cselib_subst_to_values (XEXP (mloc, 0),
-						 GET_MODE (mloc));
-		  moa.type = MO_VAL_USE;
-		  moa.insn = cui->insn;
-		  moa.u.loc = gen_rtx_CONCAT (address_mode,
-					      val->val_rtx, mloc);
-		  if (dump_file && (dump_flags & TDF_DETAILS))
-		    log_op_type (moa.u.loc, cui->bb, cui->insn,
-				 moa.type, dump_file);
-		  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
-		}
+		preserve_value (val);
 	    }
 
 	  if (CONSTANT_P (vloc)
-	      && (GET_CODE (vloc) != CONST
-		  || for_each_rtx (&vloc, non_suitable_const, NULL)))
+	      && (GET_CODE (vloc) != CONST || non_suitable_const (vloc)))
 	    /* For constants don't look up any value.  */;
-	  else if (!VAR_LOC_UNKNOWN_P (vloc)
+	  else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc)
 		   && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
 	    {
-	      enum machine_mode mode2;
+	      machine_mode mode2;
 	      enum micro_operation_type type2;
-	      rtx nloc = replace_expr_with_values (vloc);
+	      rtx nloc = NULL;
+	      bool resolvable = REG_P (vloc) || MEM_P (vloc);
+
+	      if (resolvable)
+		nloc = replace_expr_with_values (vloc);
 
 	      if (nloc)
 		{
@@ -5092,7 +5684,7 @@
 	      if (type2 == MO_CLOBBER
 		  && !cselib_preserved_value_p (val))
 		{
-		  VAL_NEEDS_RESOLUTION (oloc) = 1;
+		  VAL_NEEDS_RESOLUTION (oloc) = resolvable;
 		  preserve_value (val);
 		}
 	    }
@@ -5106,7 +5698,7 @@
 	}
       else if (type == MO_VAL_USE)
 	{
-	  enum machine_mode mode2 = VOIDmode;
+	  machine_mode mode2 = VOIDmode;
 	  enum micro_operation_type type2;
 	  cselib_val *val = find_use_val (loc, GET_MODE (loc), cui);
 	  rtx vloc, oloc = loc, nloc;
@@ -5115,32 +5707,16 @@
 
 	  if (MEM_P (oloc)
 	      && !REG_P (XEXP (oloc, 0))
-	      && !MEM_P (XEXP (oloc, 0))
-	      && (GET_CODE (XEXP (oloc, 0)) != PLUS
-		  || XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx
-		  || !CONST_INT_P (XEXP (XEXP (oloc, 0), 1))))
+	      && !MEM_P (XEXP (oloc, 0)))
 	    {
 	      rtx mloc = oloc;
-	      enum machine_mode address_mode = get_address_mode (mloc);
+	      machine_mode address_mode = get_address_mode (mloc);
 	      cselib_val *val
 		= cselib_lookup (XEXP (mloc, 0), address_mode, 0,
-				GET_MODE (mloc));
+				 GET_MODE (mloc));
 
 	      if (val && !cselib_preserved_value_p (val))
-		{
-		  micro_operation moa;
-		  preserve_value (val);
-		  mloc = cselib_subst_to_values (XEXP (mloc, 0),
-						GET_MODE (mloc));
-		  moa.type = MO_VAL_USE;
-		  moa.insn = cui->insn;
-		  moa.u.loc = gen_rtx_CONCAT (address_mode,
-					      val->val_rtx, mloc);
-		  if (dump_file && (dump_flags & TDF_DETAILS))
-		    log_op_type (moa.u.loc, cui->bb, cui->insn,
-				 moa.type, dump_file);
-		  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
-		}
+		preserve_value (val);
 	    }
 
 	  type2 = use_type (loc, 0, &mode2);
@@ -5163,6 +5739,7 @@
 
 	  */
 
+	  gcc_checking_assert (REG_P (loc) || MEM_P (loc));
 	  nloc = replace_expr_with_values (loc);
 	  if (!nloc)
 	    nloc = oloc;
@@ -5187,10 +5764,8 @@
 
       if (dump_file && (dump_flags & TDF_DETAILS))
 	log_op_type (mo.u.loc, cui->bb, cui->insn, mo.type, dump_file);
-      VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
-    }
-
-  return 0;
+      VTI (bb)->mos.safe_push (mo);
+    }
 }
 
 /* Helper function for finding all uses of REG/MEM in X in insn INSN.  */
@@ -5198,25 +5773,42 @@
 static void
 add_uses_1 (rtx *x, void *cui)
 {
-  for_each_rtx (x, add_uses, cui);
-}
-
-/* Attempt to reverse the EXPR operation in the debug info.  Say for
-   reg1 = reg2 + 6 even when reg2 is no longer live we
-   can express its value as VAL - 6.  */
-
-static rtx
-reverse_op (rtx val, const_rtx expr)
+  subrtx_var_iterator::array_type array;
+  FOR_EACH_SUBRTX_VAR (iter, array, *x, NONCONST)
+    add_uses (*iter, (struct count_use_info *) cui);
+}
+
+/* This is the value used during expansion of locations.  We want it
+   to be unbounded, so that variables expanded deep in a recursion
+   nest are fully evaluated, so that their values are cached
+   correctly.  We avoid recursion cycles through other means, and we
+   don't unshare RTL, so excess complexity is not a problem.  */
+#define EXPR_DEPTH (INT_MAX)
+/* We use this to keep too-complex expressions from being emitted as
+   location notes, and then to debug information.  Users can trade
+   compile time for ridiculously complex expressions, although they're
+   seldom useful, and they may often have to be discarded as not
+   representable anyway.  */
+#define EXPR_USE_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
+
+/* Attempt to reverse the EXPR operation in the debug info and record
+   it in the cselib table.  Say for reg1 = reg2 + 6 even when reg2 is
+   no longer live we can express its value as VAL - 6.  */
+
+static void
+reverse_op (rtx val, const_rtx expr, rtx_insn *insn)
 {
   rtx src, arg, ret;
   cselib_val *v;
+  struct elt_loc_list *l;
   enum rtx_code code;
+  int count;
 
   if (GET_CODE (expr) != SET)
-    return NULL_RTX;
+    return;
 
   if (!REG_P (SET_DEST (expr)) || GET_MODE (val) != GET_MODE (SET_DEST (expr)))
-    return NULL_RTX;
+    return;
 
   src = SET_SRC (expr);
   switch (GET_CODE (src))
@@ -5227,30 +5819,45 @@
     case NOT:
     case NEG:
       if (!REG_P (XEXP (src, 0)))
-	return NULL_RTX;
+	return;
       break;
     case SIGN_EXTEND:
     case ZERO_EXTEND:
       if (!REG_P (XEXP (src, 0)) && !MEM_P (XEXP (src, 0)))
-	return NULL_RTX;
+	return;
       break;
     default:
-      return NULL_RTX;
+      return;
     }
 
   if (!SCALAR_INT_MODE_P (GET_MODE (src)) || XEXP (src, 0) == cfa_base_rtx)
-    return NULL_RTX;
+    return;
 
   v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0, VOIDmode);
   if (!v || !cselib_preserved_value_p (v))
-    return NULL_RTX;
+    return;
+
+  /* Use canonical V to avoid creating multiple redundant expressions
+     for different VALUES equivalent to V.  */
+  v = canonical_cselib_val (v);
+
+  /* Adding a reverse op isn't useful if V already has an always valid
+     location.  Ignore ENTRY_VALUE, while it is always constant, we should
+     prefer non-ENTRY_VALUE locations whenever possible.  */
+  for (l = v->locs, count = 0; l; l = l->next, count++)
+    if (CONSTANT_P (l->loc)
+	&& (GET_CODE (l->loc) != CONST || !references_value_p (l->loc, 0)))
+      return;
+    /* Avoid creating too large locs lists.  */
+    else if (count == PARAM_VALUE (PARAM_MAX_VARTRACK_REVERSE_OP_SIZE))
+      return;
 
   switch (GET_CODE (src))
     {
     case NOT:
     case NEG:
       if (GET_MODE (v->val_rtx) != GET_MODE (val))
-	return NULL_RTX;
+	return;
       ret = gen_rtx_fmt_e (GET_CODE (src), GET_MODE (val), val);
       break;
     case SIGN_EXTEND:
@@ -5268,28 +5875,23 @@
       goto binary;
     binary:
       if (GET_MODE (v->val_rtx) != GET_MODE (val))
-	return NULL_RTX;
+	return;
       arg = XEXP (src, 1);
       if (!CONST_INT_P (arg) && GET_CODE (arg) != SYMBOL_REF)
 	{
 	  arg = cselib_expand_value_rtx (arg, scratch_regs, 5);
 	  if (arg == NULL_RTX)
-	    return NULL_RTX;
+	    return;
 	  if (!CONST_INT_P (arg) && GET_CODE (arg) != SYMBOL_REF)
-	    return NULL_RTX;
+	    return;
 	}
       ret = simplify_gen_binary (code, GET_MODE (val), val, arg);
-      if (ret == val)
-	/* Ensure ret isn't VALUE itself (which can happen e.g. for
-	   (plus (reg1) (reg2)) when reg2 is known to be 0), as that
-	   breaks a lot of routines during var-tracking.  */
-	ret = gen_rtx_fmt_ee (PLUS, GET_MODE (val), val, const0_rtx);
       break;
     default:
       gcc_unreachable ();
     }
 
-  return gen_rtx_CONCAT (GET_MODE (v->val_rtx), v->val_rtx, ret);
+  cselib_add_permanent_equiv (v, ret, insn);
 }
 
 /* Add stores (register and memory references) LOC which will be tracked
@@ -5299,7 +5901,7 @@
 static void
 add_stores (rtx loc, const_rtx expr, void *cuip)
 {
-  enum machine_mode mode = VOIDmode, mode2;
+  machine_mode mode = VOIDmode, mode2;
   struct count_use_info *cui = (struct count_use_info *)cuip;
   basic_block bb = cui->bb;
   micro_operation mo;
@@ -5308,7 +5910,6 @@
   bool track_p = false;
   cselib_val *v;
   bool resolve, preserve;
-  rtx reverse;
 
   if (type == MO_CLOBBER)
     return;
@@ -5324,10 +5925,20 @@
 	{
 	  mo.type = MO_CLOBBER;
 	  mo.u.loc = loc;
+	  if (GET_CODE (expr) == SET
+	      && SET_DEST (expr) == loc
+	      && !unsuitable_loc (SET_SRC (expr))
+	      && find_use_val (loc, mode, cui))
+	    {
+	      gcc_checking_assert (type == MO_VAL_SET);
+	      mo.u.loc = gen_rtx_SET (loc, SET_SRC (expr));
+	    }
 	}
       else
 	{
-	  if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
+	  if (GET_CODE (expr) == SET
+	      && SET_DEST (expr) == loc
+	      && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
 	    src = var_lowpart (mode2, SET_SRC (expr));
 	  loc = var_lowpart (mode2, loc);
 
@@ -5338,9 +5949,26 @@
 	    }
 	  else
 	    {
-	      rtx xexpr = gen_rtx_SET (VOIDmode, loc, src);
+	      rtx xexpr = gen_rtx_SET (loc, src);
 	      if (same_variable_part_p (src, REG_EXPR (loc), REG_OFFSET (loc)))
-		mo.type = MO_COPY;
+		{
+		  /* If this is an instruction copying (part of) a parameter
+		     passed by invisible reference to its register location,
+		     pretend it's a SET so that the initial memory location
+		     is discarded, as the parameter register can be reused
+		     for other purposes and we do not track locations based
+		     on generic registers.  */
+		  if (MEM_P (src)
+		      && REG_EXPR (loc)
+		      && TREE_CODE (REG_EXPR (loc)) == PARM_DECL
+		      && DECL_MODE (REG_EXPR (loc)) != BLKmode
+		      && MEM_P (DECL_INCOMING_RTL (REG_EXPR (loc)))
+		      && XEXP (DECL_INCOMING_RTL (REG_EXPR (loc)), 0)
+			 != arg_pointer_rtx)
+		    mo.type = MO_SET;
+		  else
+		    mo.type = MO_COPY;
+		}
 	      else
 		mo.type = MO_SET;
 	      mo.u.loc = xexpr;
@@ -5354,30 +5982,16 @@
     {
       if (MEM_P (loc) && type == MO_VAL_SET
 	  && !REG_P (XEXP (loc, 0))
-	  && !MEM_P (XEXP (loc, 0))
-	  && (GET_CODE (XEXP (loc, 0)) != PLUS
-	      || XEXP (XEXP (loc, 0), 0) != cfa_base_rtx
-	      || !CONST_INT_P (XEXP (XEXP (loc, 0), 1))))
+	  && !MEM_P (XEXP (loc, 0)))
 	{
 	  rtx mloc = loc;
-	  enum machine_mode address_mode = get_address_mode (mloc);
+	  machine_mode address_mode = get_address_mode (mloc);
 	  cselib_val *val = cselib_lookup (XEXP (mloc, 0),
 					   address_mode, 0,
 					   GET_MODE (mloc));
 
 	  if (val && !cselib_preserved_value_p (val))
-	    {
-	      preserve_value (val);
-	      mo.type = MO_VAL_USE;
-	      mloc = cselib_subst_to_values (XEXP (mloc, 0),
-					     GET_MODE (mloc));
-	      mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc);
-	      mo.insn = cui->insn;
-	      if (dump_file && (dump_flags & TDF_DETAILS))
-		log_op_type (mo.u.loc, cui->bb, cui->insn,
-			     mo.type, dump_file);
-	      VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
-	    }
+	    preserve_value (val);
 	}
 
       if (GET_CODE (expr) == CLOBBER || !track_p)
@@ -5387,7 +6001,9 @@
 	}
       else
 	{
-	  if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
+	  if (GET_CODE (expr) == SET
+	      && SET_DEST (expr) == loc
+	      && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
 	    src = var_lowpart (mode2, SET_SRC (expr));
 	  loc = var_lowpart (mode2, loc);
 
@@ -5398,7 +6014,7 @@
 	    }
 	  else
 	    {
-	      rtx xexpr = gen_rtx_SET (VOIDmode, loc, src);
+	      rtx xexpr = gen_rtx_SET (loc, src);
 	      if (same_variable_part_p (SET_SRC (xexpr),
 					MEM_EXPR (loc),
 					INT_MEM_OFFSET (loc)))
@@ -5423,6 +6039,27 @@
 
   resolve = preserve = !cselib_preserved_value_p (v);
 
+  /* We cannot track values for multiple-part variables, so we track only
+     locations for tracked record parameters.  */
+  if (track_p
+      && REG_P (loc)
+      && REG_EXPR (loc)
+      && tracked_record_parameter_p (REG_EXPR (loc)))
+    {
+      /* Although we don't use the value here, it could be used later by the
+	 mere virtue of its existence as the operand of the reverse operation
+	 that gave rise to it (typically extension/truncation).  Make sure it
+	 is preserved as required by vt_expand_var_loc_chain.  */
+      if (preserve)
+	preserve_value (v);
+      goto log_and_return;
+    }
+
+  if (loc == stack_pointer_rtx
+      && hard_frame_pointer_adjustment != -1
+      && preserve)
+    cselib_set_value_sp_based (v);
+
   nloc = replace_expr_with_values (oloc);
   if (nloc)
     oloc = nloc;
@@ -5431,10 +6068,11 @@
     {
       cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0, VOIDmode);
 
-      gcc_assert (oval != v);
+      if (oval == v)
+	return;
       gcc_assert (REG_P (oloc) || MEM_P (oloc));
 
-      if (!cselib_preserved_value_p (oval))
+      if (oval && !cselib_preserved_value_p (oval))
 	{
 	  micro_operation moa;
 
@@ -5448,14 +6086,17 @@
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    log_op_type (moa.u.loc, cui->bb, cui->insn,
 			 moa.type, dump_file);
-	  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
+	  VTI (bb)->mos.safe_push (moa);
 	}
 
       resolve = false;
     }
   else if (resolve && GET_CODE (mo.u.loc) == SET)
     {
-      nloc = replace_expr_with_values (SET_SRC (expr));
+      if (REG_P (SET_SRC (expr)) || MEM_P (SET_SRC (expr)))
+	nloc = replace_expr_with_values (SET_SRC (expr));
+      else
+	nloc = NULL_RTX;
 
       /* Avoid the mode mismatch between oexpr and expr.  */
       if (!nloc && mode != mode2)
@@ -5464,8 +6105,8 @@
 	  gcc_assert (oloc == SET_DEST (expr));
 	}
 
-      if (nloc)
-	oloc = gen_rtx_SET (GET_MODE (mo.u.loc), oloc, nloc);
+      if (nloc && nloc != SET_SRC (mo.u.loc))
+	oloc = gen_rtx_SET (oloc, nloc);
       else
 	{
 	  if (oloc == SET_DEST (mo.u.loc))
@@ -5511,14 +6152,7 @@
   */
 
   if (GET_CODE (PATTERN (cui->insn)) != COND_EXEC)
-    {
-      reverse = reverse_op (v->val_rtx, expr);
-      if (reverse)
-	{
-	  loc = gen_rtx_CONCAT (GET_MODE (mo.u.loc), loc, reverse);
-	  VAL_EXPR_HAS_REVERSE (loc) = 1;
-	}
-    }
+    reverse_op (v->val_rtx, expr, cui->insn);
 
   mo.u.loc = loc;
 
@@ -5539,7 +6173,347 @@
  log_and_return:
   if (dump_file && (dump_flags & TDF_DETAILS))
     log_op_type (mo.u.loc, cui->bb, cui->insn, mo.type, dump_file);
-  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+  VTI (bb)->mos.safe_push (mo);
+}
+
+/* Arguments to the call.  */
+static rtx call_arguments;
+
+/* Compute call_arguments.  */
+
+static void
+prepare_call_arguments (basic_block bb, rtx_insn *insn)
+{
+  rtx link, x, call;
+  rtx prev, cur, next;
+  rtx this_arg = NULL_RTX;
+  tree type = NULL_TREE, t, fndecl = NULL_TREE;
+  tree obj_type_ref = NULL_TREE;
+  CUMULATIVE_ARGS args_so_far_v;
+  cumulative_args_t args_so_far;
+
+  memset (&args_so_far_v, 0, sizeof (args_so_far_v));
+  args_so_far = pack_cumulative_args (&args_so_far_v);
+  call = get_call_rtx_from (insn);
+  if (call)
+    {
+      if (GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF)
+	{
+	  rtx symbol = XEXP (XEXP (call, 0), 0);
+	  if (SYMBOL_REF_DECL (symbol))
+	    fndecl = SYMBOL_REF_DECL (symbol);
+	}
+      if (fndecl == NULL_TREE)
+	fndecl = MEM_EXPR (XEXP (call, 0));
+      if (fndecl
+	  && TREE_CODE (TREE_TYPE (fndecl)) != FUNCTION_TYPE
+	  && TREE_CODE (TREE_TYPE (fndecl)) != METHOD_TYPE)
+	fndecl = NULL_TREE;
+      if (fndecl && TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
+	type = TREE_TYPE (fndecl);
+      if (fndecl && TREE_CODE (fndecl) != FUNCTION_DECL)
+	{
+	  if (TREE_CODE (fndecl) == INDIRECT_REF
+	      && TREE_CODE (TREE_OPERAND (fndecl, 0)) == OBJ_TYPE_REF)
+	    obj_type_ref = TREE_OPERAND (fndecl, 0);
+	  fndecl = NULL_TREE;
+	}
+      if (type)
+	{
+	  for (t = TYPE_ARG_TYPES (type); t && t != void_list_node;
+	       t = TREE_CHAIN (t))
+	    if (TREE_CODE (TREE_VALUE (t)) == REFERENCE_TYPE
+		&& INTEGRAL_TYPE_P (TREE_TYPE (TREE_VALUE (t))))
+	      break;
+	  if ((t == NULL || t == void_list_node) && obj_type_ref == NULL_TREE)
+	    type = NULL;
+	  else
+	    {
+	      int nargs ATTRIBUTE_UNUSED = list_length (TYPE_ARG_TYPES (type));
+	      link = CALL_INSN_FUNCTION_USAGE (insn);
+#ifndef PCC_STATIC_STRUCT_RETURN
+	      if (aggregate_value_p (TREE_TYPE (type), type)
+		  && targetm.calls.struct_value_rtx (type, 0) == 0)
+		{
+		  tree struct_addr = build_pointer_type (TREE_TYPE (type));
+		  machine_mode mode = TYPE_MODE (struct_addr);
+		  rtx reg;
+		  INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
+					nargs + 1);
+		  reg = targetm.calls.function_arg (args_so_far, mode,
+						    struct_addr, true);
+		  targetm.calls.function_arg_advance (args_so_far, mode,
+						      struct_addr, true);
+		  if (reg == NULL_RTX)
+		    {
+		      for (; link; link = XEXP (link, 1))
+			if (GET_CODE (XEXP (link, 0)) == USE
+			    && MEM_P (XEXP (XEXP (link, 0), 0)))
+			  {
+			    link = XEXP (link, 1);
+			    break;
+			  }
+		    }
+		}
+	      else
+#endif
+		INIT_CUMULATIVE_ARGS (args_so_far_v, type, NULL_RTX, fndecl,
+				      nargs);
+	      if (obj_type_ref && TYPE_ARG_TYPES (type) != void_list_node)
+		{
+		  machine_mode mode;
+		  t = TYPE_ARG_TYPES (type);
+		  mode = TYPE_MODE (TREE_VALUE (t));
+		  this_arg = targetm.calls.function_arg (args_so_far, mode,
+							 TREE_VALUE (t), true);
+		  if (this_arg && !REG_P (this_arg))
+		    this_arg = NULL_RTX;
+		  else if (this_arg == NULL_RTX)
+		    {
+		      for (; link; link = XEXP (link, 1))
+			if (GET_CODE (XEXP (link, 0)) == USE
+			    && MEM_P (XEXP (XEXP (link, 0), 0)))
+			  {
+			    this_arg = XEXP (XEXP (link, 0), 0);
+			    break;
+			  }
+		    }
+		}
+	    }
+	}
+    }
+  t = type ? TYPE_ARG_TYPES (type) : NULL_TREE;
+
+  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+    if (GET_CODE (XEXP (link, 0)) == USE)
+      {
+	rtx item = NULL_RTX;
+	x = XEXP (XEXP (link, 0), 0);
+	if (GET_MODE (link) == VOIDmode
+	    || GET_MODE (link) == BLKmode
+	    || (GET_MODE (link) != GET_MODE (x)
+		&& ((GET_MODE_CLASS (GET_MODE (link)) != MODE_INT
+		     && GET_MODE_CLASS (GET_MODE (link)) != MODE_PARTIAL_INT)
+		    || (GET_MODE_CLASS (GET_MODE (x)) != MODE_INT
+			&& GET_MODE_CLASS (GET_MODE (x)) != MODE_PARTIAL_INT))))
+	  /* Can't do anything for these, if the original type mode
+	     isn't known or can't be converted.  */;
+	else if (REG_P (x))
+	  {
+	    cselib_val *val = cselib_lookup (x, GET_MODE (x), 0, VOIDmode);
+	    scalar_int_mode mode;
+	    if (val && cselib_preserved_value_p (val))
+	      item = val->val_rtx;
+	    else if (is_a <scalar_int_mode> (GET_MODE (x), &mode))
+	      {
+		opt_scalar_int_mode mode_iter;
+		FOR_EACH_WIDER_MODE (mode_iter, mode)
+		  {
+		    mode = mode_iter.require ();
+		    if (GET_MODE_BITSIZE (mode) > BITS_PER_WORD)
+		      break;
+
+		    rtx reg = simplify_subreg (mode, x, GET_MODE (x), 0);
+		    if (reg == NULL_RTX || !REG_P (reg))
+		      continue;
+		    val = cselib_lookup (reg, mode, 0, VOIDmode);
+		    if (val && cselib_preserved_value_p (val))
+		      {
+			item = val->val_rtx;
+			break;
+		      }
+		  }
+	      }
+	  }
+	else if (MEM_P (x))
+	  {
+	    rtx mem = x;
+	    cselib_val *val;
+
+	    if (!frame_pointer_needed)
+	      {
+		struct adjust_mem_data amd;
+		amd.mem_mode = VOIDmode;
+		amd.stack_adjust = -VTI (bb)->out.stack_adjust;
+		amd.store = true;
+		mem = simplify_replace_fn_rtx (mem, NULL_RTX, adjust_mems,
+					       &amd);
+		gcc_assert (amd.side_effects.is_empty ());
+	      }
+	    val = cselib_lookup (mem, GET_MODE (mem), 0, VOIDmode);
+	    if (val && cselib_preserved_value_p (val))
+	      item = val->val_rtx;
+	    else if (GET_MODE_CLASS (GET_MODE (mem)) != MODE_INT
+		     && GET_MODE_CLASS (GET_MODE (mem)) != MODE_PARTIAL_INT)
+	      {
+		/* For non-integer stack argument see also if they weren't
+		   initialized by integers.  */
+		scalar_int_mode imode;
+		if (int_mode_for_mode (GET_MODE (mem)).exists (&imode)
+		    && imode != GET_MODE (mem))
+		  {
+		    val = cselib_lookup (adjust_address_nv (mem, imode, 0),
+					 imode, 0, VOIDmode);
+		    if (val && cselib_preserved_value_p (val))
+		      item = lowpart_subreg (GET_MODE (x), val->val_rtx,
+					     imode);
+		  }
+	      }
+	  }
+	if (item)
+	  {
+	    rtx x2 = x;
+	    if (GET_MODE (item) != GET_MODE (link))
+	      item = lowpart_subreg (GET_MODE (link), item, GET_MODE (item));
+	    if (GET_MODE (x2) != GET_MODE (link))
+	      x2 = lowpart_subreg (GET_MODE (link), x2, GET_MODE (x2));
+	    item = gen_rtx_CONCAT (GET_MODE (link), x2, item);
+	    call_arguments
+	      = gen_rtx_EXPR_LIST (VOIDmode, item, call_arguments);
+	  }
+	if (t && t != void_list_node)
+	  {
+	    tree argtype = TREE_VALUE (t);
+	    machine_mode mode = TYPE_MODE (argtype);
+	    rtx reg;
+	    if (pass_by_reference (&args_so_far_v, mode, argtype, true))
+	      {
+		argtype = build_pointer_type (argtype);
+		mode = TYPE_MODE (argtype);
+	      }
+	    reg = targetm.calls.function_arg (args_so_far, mode,
+					      argtype, true);
+	    if (TREE_CODE (argtype) == REFERENCE_TYPE
+		&& INTEGRAL_TYPE_P (TREE_TYPE (argtype))
+		&& reg
+		&& REG_P (reg)
+		&& GET_MODE (reg) == mode
+		&& (GET_MODE_CLASS (mode) == MODE_INT
+		    || GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+		&& REG_P (x)
+		&& REGNO (x) == REGNO (reg)
+		&& GET_MODE (x) == mode
+		&& item)
+	      {
+		machine_mode indmode
+		  = TYPE_MODE (TREE_TYPE (argtype));
+		rtx mem = gen_rtx_MEM (indmode, x);
+		cselib_val *val = cselib_lookup (mem, indmode, 0, VOIDmode);
+		if (val && cselib_preserved_value_p (val))
+		  {
+		    item = gen_rtx_CONCAT (indmode, mem, val->val_rtx);
+		    call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
+							call_arguments);
+		  }
+		else
+		  {
+		    struct elt_loc_list *l;
+		    tree initial;
+
+		    /* Try harder, when passing address of a constant
+		       pool integer it can be easily read back.  */
+		    item = XEXP (item, 1);
+		    if (GET_CODE (item) == SUBREG)
+		      item = SUBREG_REG (item);
+		    gcc_assert (GET_CODE (item) == VALUE);
+		    val = CSELIB_VAL_PTR (item);
+		    for (l = val->locs; l; l = l->next)
+		      if (GET_CODE (l->loc) == SYMBOL_REF
+			  && TREE_CONSTANT_POOL_ADDRESS_P (l->loc)
+			  && SYMBOL_REF_DECL (l->loc)
+			  && DECL_INITIAL (SYMBOL_REF_DECL (l->loc)))
+			{
+			  initial = DECL_INITIAL (SYMBOL_REF_DECL (l->loc));
+			  if (tree_fits_shwi_p (initial))
+			    {
+			      item = GEN_INT (tree_to_shwi (initial));
+			      item = gen_rtx_CONCAT (indmode, mem, item);
+			      call_arguments
+				= gen_rtx_EXPR_LIST (VOIDmode, item,
+						     call_arguments);
+			    }
+			  break;
+			}
+		  }
+	      }
+	    targetm.calls.function_arg_advance (args_so_far, mode,
+						argtype, true);
+	    t = TREE_CHAIN (t);
+	  }
+      }
+
+  /* Add debug arguments.  */
+  if (fndecl
+      && TREE_CODE (fndecl) == FUNCTION_DECL
+      && DECL_HAS_DEBUG_ARGS_P (fndecl))
+    {
+      vec<tree, va_gc> **debug_args = decl_debug_args_lookup (fndecl);
+      if (debug_args)
+	{
+	  unsigned int ix;
+	  tree param;
+	  for (ix = 0; vec_safe_iterate (*debug_args, ix, &param); ix += 2)
+	    {
+	      rtx item;
+	      tree dtemp = (**debug_args)[ix + 1];
+	      machine_mode mode = DECL_MODE (dtemp);
+	      item = gen_rtx_DEBUG_PARAMETER_REF (mode, param);
+	      item = gen_rtx_CONCAT (mode, item, DECL_RTL_KNOWN_SET (dtemp));
+	      call_arguments = gen_rtx_EXPR_LIST (VOIDmode, item,
+						  call_arguments);
+	    }
+	}
+    }
+
+  /* Reverse call_arguments chain.  */
+  prev = NULL_RTX;
+  for (cur = call_arguments; cur; cur = next)
+    {
+      next = XEXP (cur, 1);
+      XEXP (cur, 1) = prev;
+      prev = cur;
+    }
+  call_arguments = prev;
+
+  x = get_call_rtx_from (insn);
+  if (x)
+    {
+      x = XEXP (XEXP (x, 0), 0);
+      if (GET_CODE (x) == SYMBOL_REF)
+	/* Don't record anything.  */;
+      else if (CONSTANT_P (x))
+	{
+	  x = gen_rtx_CONCAT (GET_MODE (x) == VOIDmode ? Pmode : GET_MODE (x),
+			      pc_rtx, x);
+	  call_arguments
+	    = gen_rtx_EXPR_LIST (VOIDmode, x, call_arguments);
+	}
+      else
+	{
+	  cselib_val *val = cselib_lookup (x, GET_MODE (x), 0, VOIDmode);
+	  if (val && cselib_preserved_value_p (val))
+	    {
+	      x = gen_rtx_CONCAT (GET_MODE (x), pc_rtx, val->val_rtx);
+	      call_arguments
+		= gen_rtx_EXPR_LIST (VOIDmode, x, call_arguments);
+	    }
+	}
+    }
+  if (this_arg)
+    {
+      machine_mode mode
+	= TYPE_MODE (TREE_TYPE (OBJ_TYPE_REF_EXPR (obj_type_ref)));
+      rtx clobbered = gen_rtx_MEM (mode, this_arg);
+      HOST_WIDE_INT token
+	= tree_to_shwi (OBJ_TYPE_REF_TOKEN (obj_type_ref));
+      if (token)
+	clobbered = plus_constant (mode, clobbered,
+				   token * GET_MODE_SIZE (mode));
+      clobbered = gen_rtx_MEM (mode, clobbered);
+      x = gen_rtx_CONCAT (mode, gen_rtx_CLOBBER (VOIDmode, pc_rtx), clobbered);
+      call_arguments
+	= gen_rtx_EXPR_LIST (VOIDmode, x, call_arguments);
+    }
 }
 
 /* Callback for cselib_record_sets_hook, that records as micro
@@ -5550,7 +6524,7 @@
    first place, in which case sets and n_sets will be 0).  */
 
 static void
-add_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
+add_with_sets (rtx_insn *insn, struct cselib_set *sets, int n_sets)
 {
   basic_block bb = BLOCK_FOR_INSN (insn);
   int n1, n2;
@@ -5564,11 +6538,11 @@
   cui.sets = sets;
   cui.n_sets = n_sets;
 
-  n1 = VEC_length (micro_operation, VTI (bb)->mos);
+  n1 = VTI (bb)->mos.length ();
   cui.store_p = false;
   note_uses (&PATTERN (insn), add_uses_1, &cui);
-  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
-  mos = VEC_address (micro_operation, VTI (bb)->mos);
+  n2 = VTI (bb)->mos.length () - 1;
+  mos = VTI (bb)->mos.address ();
 
   /* Order the MO_USEs to be before MO_USE_NO_VARs and MO_VAL_USE, and
      MO_VAL_LOC last.  */
@@ -5579,16 +6553,10 @@
       while (n1 < n2 && mos[n2].type != MO_USE)
 	n2--;
       if (n1 < n2)
-	{
-	  micro_operation sw;
-
-	  sw = mos[n1];
-	  mos[n1] = mos[n2];
-	  mos[n2] = sw;
-	}
-    }
-
-  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
+	std::swap (mos[n1], mos[n2]);
+    }
+
+  n2 = VTI (bb)->mos.length () - 1;
   while (n1 < n2)
     {
       while (n1 < n2 && mos[n1].type != MO_VAL_LOC)
@@ -5596,13 +6564,7 @@
       while (n1 < n2 && mos[n2].type == MO_VAL_LOC)
 	n2--;
       if (n1 < n2)
-	{
-	  micro_operation sw;
-
-	  sw = mos[n1];
-	  mos[n1] = mos[n2];
-	  mos[n2] = sw;
-	}
+	std::swap (mos[n1], mos[n2]);
     }
 
   if (CALL_P (insn))
@@ -5611,21 +6573,22 @@
 
       mo.type = MO_CALL;
       mo.insn = insn;
-      mo.u.loc = NULL_RTX;
+      mo.u.loc = call_arguments;
+      call_arguments = NULL_RTX;
 
       if (dump_file && (dump_flags & TDF_DETAILS))
 	log_op_type (PATTERN (insn), bb, insn, mo.type, dump_file);
-      VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
-    }
-
-  n1 = VEC_length (micro_operation, VTI (bb)->mos);
+      VTI (bb)->mos.safe_push (mo);
+    }
+
+  n1 = VTI (bb)->mos.length ();
   /* 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.  */
   cui.store_p = true;
   note_stores (PATTERN (insn), add_stores, &cui);
-  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
-  mos = VEC_address (micro_operation, VTI (bb)->mos);
+  n2 = VTI (bb)->mos.length () - 1;
+  mos = VTI (bb)->mos.address ();
 
   /* Order the MO_VAL_USEs first (note_stores does nothing
      on DEBUG_INSNs, so there are no MO_VAL_LOCs from this
@@ -5637,16 +6600,10 @@
       while (n1 < n2 && mos[n2].type != MO_VAL_USE)
 	n2--;
       if (n1 < n2)
-	{
-	  micro_operation sw;
-
-	  sw = mos[n1];
-	  mos[n1] = mos[n2];
-	  mos[n2] = sw;
-	}
-    }
-
-  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
+	std::swap (mos[n1], mos[n2]);
+    }
+
+  n2 = VTI (bb)->mos.length () - 1;
   while (n1 < n2)
     {
       while (n1 < n2 && mos[n1].type == MO_CLOBBER)
@@ -5654,13 +6611,7 @@
       while (n1 < n2 && mos[n2].type != MO_CLOBBER)
 	n2--;
       if (n1 < n2)
-	{
-	  micro_operation sw;
-
-	  sw = mos[n1];
-	  mos[n1] = mos[n2];
-	  mos[n2] = sw;
-	}
+	std::swap (mos[n1], mos[n2]);
     }
 }
 
@@ -5693,8 +6644,8 @@
 {
   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;
+  variable *var;
+  location_chain *nextp;
   int i;
   bool found;
 
@@ -5742,14 +6693,17 @@
   dataflow_set_copy (&old_out, out);
   dataflow_set_copy (out, in);
 
-  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
-    {
-      rtx insn = mo->insn;
+  if (MAY_HAVE_DEBUG_INSNS)
+    local_get_addr_cache = new hash_map<rtx, rtx>;
+
+  FOR_EACH_VEC_ELT (VTI (bb)->mos, i, mo)
+    {
+      rtx_insn *insn = mo->insn;
 
       switch (mo->type)
 	{
 	  case MO_CALL:
-	    dataflow_set_clear_at_call (out);
+	    dataflow_set_clear_at_call (out, insn);
 	    break;
 
 	  case MO_USE:
@@ -5834,35 +6788,42 @@
 	  case MO_VAL_SET:
 	    {
 	      rtx loc = mo->u.loc;
-	      rtx val, vloc, uloc, reverse = NULL_RTX;
+	      rtx val, vloc, uloc;
+	      rtx dstv, srcv;
 
 	      vloc = loc;
-	      if (VAL_EXPR_HAS_REVERSE (loc))
-		{
-		  reverse = XEXP (loc, 1);
-		  vloc = XEXP (loc, 0);
-		}
 	      uloc = XEXP (vloc, 1);
 	      val = XEXP (vloc, 0);
 	      vloc = uloc;
 
+	      if (GET_CODE (uloc) == SET)
+		{
+		  dstv = SET_DEST (uloc);
+		  srcv = SET_SRC (uloc);
+		}
+	      else
+		{
+		  dstv = uloc;
+		  srcv = NULL;
+		}
+
 	      if (GET_CODE (val) == CONCAT)
 		{
-		  vloc = XEXP (val, 1);
+		  dstv = vloc = XEXP (val, 1);
 		  val = XEXP (val, 0);
 		}
 
 	      if (GET_CODE (vloc) == SET)
 		{
-		  rtx vsrc = SET_SRC (vloc);
-
-		  gcc_assert (val != vsrc);
+		  srcv = SET_SRC (vloc);
+
+		  gcc_assert (val != srcv);
 		  gcc_assert (vloc == uloc || VAL_NEEDS_RESOLUTION (loc));
 
-		  vloc = SET_DEST (vloc);
+		  dstv = vloc = SET_DEST (vloc);
 
 		  if (VAL_NEEDS_RESOLUTION (loc))
-		    val_resolve (out, val, vsrc, insn);
+		    val_resolve (out, val, srcv, insn);
 		}
 	      else if (VAL_NEEDS_RESOLUTION (loc))
 		{
@@ -5878,49 +6839,60 @@
 		      if (REG_P (uloc))
 			var_reg_delete (out, uloc, true);
 		      else if (MEM_P (uloc))
-			var_mem_delete (out, uloc, true);
+			{
+			  gcc_assert (MEM_P (dstv));
+			  gcc_assert (MEM_ATTRS (dstv) == MEM_ATTRS (uloc));
+			  var_mem_delete (out, dstv, true);
+			}
 		    }
 		  else
 		    {
 		      bool copied_p = VAL_EXPR_IS_COPIED (loc);
-		      rtx set_src = NULL;
+		      rtx src = NULL, dst = uloc;
 		      enum var_init_status status = VAR_INIT_STATUS_INITIALIZED;
 
 		      if (GET_CODE (uloc) == SET)
 			{
-			  set_src = SET_SRC (uloc);
-			  uloc = SET_DEST (uloc);
+			  src = SET_SRC (uloc);
+			  dst = SET_DEST (uloc);
 			}
 
 		      if (copied_p)
 			{
 			  if (flag_var_tracking_uninit)
 			    {
-			      status = find_src_status (in, set_src);
+			      status = find_src_status (in, src);
 
 			      if (status == VAR_INIT_STATUS_UNKNOWN)
-				status = find_src_status (out, set_src);
+				status = find_src_status (out, src);
 			    }
 
-			  set_src = find_src_set_src (in, set_src);
+			  src = find_src_set_src (in, src);
 			}
 
-		      if (REG_P (uloc))
-			var_reg_delete_and_set (out, uloc, !copied_p,
-						status, set_src);
-		      else if (MEM_P (uloc))
-			var_mem_delete_and_set (out, uloc, !copied_p,
-						status, set_src);
+		      if (REG_P (dst))
+			var_reg_delete_and_set (out, dst, !copied_p,
+						status, srcv);
+		      else if (MEM_P (dst))
+			{
+			  gcc_assert (MEM_P (dstv));
+			  gcc_assert (MEM_ATTRS (dstv) == MEM_ATTRS (dst));
+			  var_mem_delete_and_set (out, dstv, !copied_p,
+						  status, srcv);
+			}
 		    }
 		}
 	      else if (REG_P (uloc))
 		var_regno_delete (out, REGNO (uloc));
-
-	      val_store (out, val, vloc, insn, true);
-
-	      if (reverse)
-		val_store (out, XEXP (reverse, 0), XEXP (reverse, 1),
-			   insn, false);
+	      else if (MEM_P (uloc))
+		{
+		  gcc_checking_assert (GET_CODE (vloc) == MEM);
+		  gcc_checking_assert (dstv == vloc);
+		  if (dstv != vloc)
+		    clobber_overlapping_mems (out, vloc);
+		}
+
+	      val_store (out, val, dstv, insn, true);
 	    }
 	    break;
 
@@ -6005,15 +6977,17 @@
 
   if (MAY_HAVE_DEBUG_INSNS)
     {
+      delete local_get_addr_cache;
+      local_get_addr_cache = NULL;
+
       dataflow_set_equiv_regs (out);
-      htab_traverse (shared_hash_htab (out->vars), canonicalize_values_mark,
-		     out);
-      htab_traverse (shared_hash_htab (out->vars), canonicalize_values_star,
-		     out);
-#if ENABLE_CHECKING
-      htab_traverse (shared_hash_htab (out->vars),
-		     canonicalize_loc_order_check, out);
-#endif
+      shared_hash_htab (out->vars)
+	->traverse <dataflow_set *, canonicalize_values_mark> (out);
+      shared_hash_htab (out->vars)
+	->traverse <dataflow_set *, canonicalize_values_star> (out);
+      if (flag_checking)
+	shared_hash_htab (out->vars)
+	  ->traverse <dataflow_set *, canonicalize_loc_order_check> (out);
     }
   changed = dataflow_set_different (&old_out, out);
   dataflow_set_destroy (&old_out);
@@ -6025,8 +6999,9 @@
 static bool
 vt_find_locations (void)
 {
-  fibheap_t worklist, pending, fibheap_swap;
-  sbitmap visited, in_worklist, in_pending, sbitmap_swap;
+  bb_heap_t *worklist = new bb_heap_t (LONG_MIN);
+  bb_heap_t *pending = new bb_heap_t (LONG_MIN);
+  sbitmap in_worklist, in_pending;
   basic_block bb;
   edge e;
   int *bb_order;
@@ -6039,57 +7014,50 @@
   timevar_push (TV_VAR_TRACKING_DATAFLOW);
   /* Compute reverse completion order of depth first search of the CFG
      so that the data-flow runs faster.  */
-  rc_order = XNEWVEC (int, n_basic_blocks - NUM_FIXED_BLOCKS);
-  bb_order = XNEWVEC (int, last_basic_block);
+  rc_order = XNEWVEC (int, n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS);
+  bb_order = XNEWVEC (int, last_basic_block_for_fn (cfun));
   pre_and_rev_post_order_compute (NULL, rc_order, false);
-  for (i = 0; i < n_basic_blocks - NUM_FIXED_BLOCKS; i++)
+  for (i = 0; i < n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS; i++)
     bb_order[rc_order[i]] = i;
   free (rc_order);
 
-  worklist = fibheap_new ();
-  pending = fibheap_new ();
-  visited = sbitmap_alloc (last_basic_block);
-  in_worklist = sbitmap_alloc (last_basic_block);
-  in_pending = sbitmap_alloc (last_basic_block);
-  sbitmap_zero (in_worklist);
-
-  FOR_EACH_BB (bb)
-    fibheap_insert (pending, bb_order[bb->index], bb);
-  sbitmap_ones (in_pending);
-
-  while (success && !fibheap_empty (pending))
-    {
-      fibheap_swap = pending;
-      pending = worklist;
-      worklist = fibheap_swap;
-      sbitmap_swap = in_pending;
-      in_pending = in_worklist;
-      in_worklist = sbitmap_swap;
-
-      sbitmap_zero (visited);
-
-      while (!fibheap_empty (worklist))
-	{
-	  bb = (basic_block) fibheap_extract_min (worklist);
-	  RESET_BIT (in_worklist, bb->index);
-	  gcc_assert (!TEST_BIT (visited, bb->index));
-	  if (!TEST_BIT (visited, bb->index))
+  auto_sbitmap visited (last_basic_block_for_fn (cfun));
+  in_worklist = sbitmap_alloc (last_basic_block_for_fn (cfun));
+  in_pending = sbitmap_alloc (last_basic_block_for_fn (cfun));
+  bitmap_clear (in_worklist);
+
+  FOR_EACH_BB_FN (bb, cfun)
+    pending->insert (bb_order[bb->index], bb);
+  bitmap_ones (in_pending);
+
+  while (success && !pending->empty ())
+    {
+      std::swap (worklist, pending);
+      std::swap (in_worklist, in_pending);
+
+      bitmap_clear (visited);
+
+      while (!worklist->empty ())
+	{
+	  bb = worklist->extract_min ();
+	  bitmap_clear_bit (in_worklist, bb->index);
+	  gcc_assert (!bitmap_bit_p (visited, bb->index));
+	  if (!bitmap_bit_p (visited, bb->index))
 	    {
 	      bool changed;
 	      edge_iterator ei;
 	      int oldinsz, oldoutsz;
 
-	      SET_BIT (visited, bb->index);
+	      bitmap_set_bit (visited, bb->index);
 
 	      if (VTI (bb)->in.vars)
 		{
 		  htabsz
-		    -= (htab_size (shared_hash_htab (VTI (bb)->in.vars))
-			+ htab_size (shared_hash_htab (VTI (bb)->out.vars)));
-		  oldinsz
-		    = htab_elements (shared_hash_htab (VTI (bb)->in.vars));
+		    -= shared_hash_htab (VTI (bb)->in.vars)->size ()
+			+ shared_hash_htab (VTI (bb)->out.vars)->size ();
+		  oldinsz = shared_hash_htab (VTI (bb)->in.vars)->elements ();
 		  oldoutsz
-		    = htab_elements (shared_hash_htab (VTI (bb)->out.vars));
+		    = shared_hash_htab (VTI (bb)->out.vars)->elements ();
 		}
 	      else
 		oldinsz = oldoutsz = 0;
@@ -6124,13 +7092,14 @@
 		  if (adjust)
 		    {
 		      dataflow_post_merge_adjust (in, &VTI (bb)->permp);
-#if ENABLE_CHECKING
-		      /* Merge and merge_adjust should keep entries in
-			 canonical order.  */
-		      htab_traverse (shared_hash_htab (in->vars),
-				     canonicalize_loc_order_check,
-				     in);
-#endif
+
+		      if (flag_checking)
+			/* Merge and merge_adjust should keep entries in
+			   canonical order.  */
+			shared_hash_htab (in->vars)
+			  ->traverse <dataflow_set *,
+				      canonicalize_loc_order_check> (in);
+
 		      if (dst_can_be_shared)
 			{
 			  shared_hash_destroy (in->vars);
@@ -6149,8 +7118,8 @@
 		}
 
 	      changed = compute_bb_dataflow (bb);
-	      htabsz += (htab_size (shared_hash_htab (VTI (bb)->in.vars))
-			 + htab_size (shared_hash_htab (VTI (bb)->out.vars)));
+	      htabsz += shared_hash_htab (VTI (bb)->in.vars)->size ()
+			 + shared_hash_htab (VTI (bb)->out.vars)->size ();
 
 	      if (htabmax && htabsz > htabmax)
 		{
@@ -6169,26 +7138,25 @@
 		{
 		  FOR_EACH_EDGE (e, ei, bb->succs)
 		    {
-		      if (e->dest == EXIT_BLOCK_PTR)
+		      if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
 			continue;
 
-		      if (TEST_BIT (visited, e->dest->index))
+		      if (bitmap_bit_p (visited, e->dest->index))
 			{
-			  if (!TEST_BIT (in_pending, e->dest->index))
+			  if (!bitmap_bit_p (in_pending, e->dest->index))
 			    {
 			      /* Send E->DEST to next round.  */
-			      SET_BIT (in_pending, e->dest->index);
-			      fibheap_insert (pending,
-					      bb_order[e->dest->index],
-					      e->dest);
+			      bitmap_set_bit (in_pending, e->dest->index);
+			      pending->insert (bb_order[e->dest->index],
+					       e->dest);
 			    }
 			}
-		      else if (!TEST_BIT (in_worklist, e->dest->index))
+		      else if (!bitmap_bit_p (in_worklist, e->dest->index))
 			{
 			  /* Add E->DEST to current round.  */
-			  SET_BIT (in_worklist, e->dest->index);
-			  fibheap_insert (worklist, bb_order[e->dest->index],
-					  e->dest);
+			  bitmap_set_bit (in_worklist, e->dest->index);
+			  worklist->insert (bb_order[e->dest->index],
+					    e->dest);
 			}
 		    }
 		}
@@ -6197,11 +7165,12 @@
 		fprintf (dump_file,
 			 "BB %i: in %i (was %i), out %i (was %i), rem %i + %i, tsz %i\n",
 			 bb->index,
-			 (int)htab_elements (shared_hash_htab (VTI (bb)->in.vars)),
+			 (int)shared_hash_htab (VTI (bb)->in.vars)->size (),
 			 oldinsz,
-			 (int)htab_elements (shared_hash_htab (VTI (bb)->out.vars)),
+			 (int)shared_hash_htab (VTI (bb)->out.vars)->size (),
 			 oldoutsz,
-			 (int)worklist->nodes, (int)pending->nodes, htabsz);
+			 (int)worklist->nodes (), (int)pending->nodes (),
+			 htabsz);
 
 	      if (dump_file && (dump_flags & TDF_DETAILS))
 		{
@@ -6215,13 +7184,12 @@
     }
 
   if (success && MAY_HAVE_DEBUG_INSNS)
-    FOR_EACH_BB (bb)
+    FOR_EACH_BB_FN (bb, cfun)
       gcc_assert (VTI (bb)->flooded);
 
   free (bb_order);
-  fibheap_delete (worklist);
-  fibheap_delete (pending);
-  sbitmap_free (visited);
+  delete worklist;
+  delete pending;
   sbitmap_free (in_worklist);
   sbitmap_free (in_pending);
 
@@ -6232,7 +7200,7 @@
 /* Print the content of the LIST to dump file.  */
 
 static void
-dump_attrs_list (attrs list)
+dump_attrs_list (attrs *list)
 {
   for (; list; list = list->next)
     {
@@ -6247,10 +7215,10 @@
 
 /* Print the information about variable *SLOT to dump file.  */
 
-static int
-dump_var_slot (void **slot, void *data ATTRIBUTE_UNUSED)
-{
-  variable var = (variable) *slot;
+int
+dump_var_tracking_slot (variable **slot, void *data ATTRIBUTE_UNUSED)
+{
+  variable *var = *slot;
 
   dump_var (var);
 
@@ -6261,10 +7229,10 @@
 /* Print the information about variable VAR to dump file.  */
 
 static void
-dump_var (variable var)
+dump_var (variable *var)
 {
   int i;
-  location_chain node;
+  location_chain *node;
 
   if (dv_is_decl_p (var->dv))
     {
@@ -6292,7 +7260,7 @@
   for (i = 0; i < var->n_var_parts; i++)
     {
       fprintf (dump_file, "    offset %ld\n",
-	       (long) var->var_part[i].offset);
+	       (long)(var->onepart ? 0 : VAR_PART_OFFSET (var, i)));
       for (node = var->var_part[i].loc_chain; node; node = node->next)
 	{
 	  fprintf (dump_file, "      ");
@@ -6306,12 +7274,12 @@
 /* Print the information about variables from hash table VARS to dump file.  */
 
 static void
-dump_vars (htab_t vars)
-{
-  if (htab_elements (vars) > 0)
+dump_vars (variable_table_type *vars)
+{
+  if (vars->elements () > 0)
     {
       fprintf (dump_file, "Variables:\n");
-      htab_traverse (vars, dump_var_slot, NULL);
+      vars->traverse <void *, dump_var_tracking_slot> (NULL);
     }
 }
 
@@ -6343,7 +7311,7 @@
 {
   basic_block bb;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       fprintf (dump_file, "\nBasic block %d:\n", bb->index);
       fprintf (dump_file, "IN:\n");
@@ -6353,55 +7321,164 @@
     }
 }
 
+/* Return the variable for DV in dropped_values, inserting one if
+   requested with INSERT.  */
+
+static inline variable *
+variable_from_dropped (decl_or_value dv, enum insert_option insert)
+{
+  variable **slot;
+  variable *empty_var;
+  onepart_enum onepart;
+
+  slot = dropped_values->find_slot_with_hash (dv, dv_htab_hash (dv), insert);
+
+  if (!slot)
+    return NULL;
+
+  if (*slot)
+    return *slot;
+
+  gcc_checking_assert (insert == INSERT);
+
+  onepart = dv_onepart_p (dv);
+
+  gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR);
+
+  empty_var = onepart_pool_allocate (onepart);
+  empty_var->dv = dv;
+  empty_var->refcount = 1;
+  empty_var->n_var_parts = 0;
+  empty_var->onepart = onepart;
+  empty_var->in_changed_variables = false;
+  empty_var->var_part[0].loc_chain = NULL;
+  empty_var->var_part[0].cur_loc = NULL;
+  VAR_LOC_1PAUX (empty_var) = NULL;
+  set_dv_changed (dv, true);
+
+  *slot = empty_var;
+
+  return empty_var;
+}
+
+/* Recover the one-part aux from dropped_values.  */
+
+static struct onepart_aux *
+recover_dropped_1paux (variable *var)
+{
+  variable *dvar;
+
+  gcc_checking_assert (var->onepart);
+
+  if (VAR_LOC_1PAUX (var))
+    return VAR_LOC_1PAUX (var);
+
+  if (var->onepart == ONEPART_VDECL)
+    return NULL;
+
+  dvar = variable_from_dropped (var->dv, NO_INSERT);
+
+  if (!dvar)
+    return NULL;
+
+  VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (dvar);
+  VAR_LOC_1PAUX (dvar) = NULL;
+
+  return VAR_LOC_1PAUX (var);
+}
+
 /* Add variable VAR to the hash table of changed variables and
    if it has no locations delete it from SET's hash table.  */
 
 static void
-variable_was_changed (variable var, dataflow_set *set)
+variable_was_changed (variable *var, dataflow_set *set)
 {
   hashval_t hash = dv_htab_hash (var->dv);
 
   if (emit_notes)
     {
-      void **slot;
-      bool old_cur_loc_changed = false;
+      variable **slot;
 
       /* Remember this decl or VALUE has been added to changed_variables.  */
       set_dv_changed (var->dv, true);
 
-      slot = htab_find_slot_with_hash (changed_variables,
-				       var->dv,
-				       hash, INSERT);
+      slot = changed_variables->find_slot_with_hash (var->dv, hash, INSERT);
 
       if (*slot)
 	{
-	  variable old_var = (variable) *slot;
+	  variable *old_var = *slot;
 	  gcc_assert (old_var->in_changed_variables);
 	  old_var->in_changed_variables = false;
-	  old_cur_loc_changed = old_var->cur_loc_changed;
+	  if (var != old_var && var->onepart)
+	    {
+	      /* Restore the auxiliary info from an empty variable
+		 previously created for changed_variables, so it is
+		 not lost.  */
+	      gcc_checking_assert (!VAR_LOC_1PAUX (var));
+	      VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (old_var);
+	      VAR_LOC_1PAUX (old_var) = NULL;
+	    }
 	  variable_htab_free (*slot);
 	}
+
       if (set && var->n_var_parts == 0)
 	{
-	  variable empty_var;
-
-	  empty_var = (variable) pool_alloc (dv_pool (var->dv));
-	  empty_var->dv = var->dv;
-	  empty_var->refcount = 1;
-	  empty_var->n_var_parts = 0;
-	  empty_var->cur_loc_changed = true;
+	  onepart_enum onepart = var->onepart;
+	  variable *empty_var = NULL;
+	  variable **dslot = NULL;
+
+	  if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR)
+	    {
+	      dslot = dropped_values->find_slot_with_hash (var->dv,
+							   dv_htab_hash (var->dv),
+							   INSERT);
+	      empty_var = *dslot;
+
+	      if (empty_var)
+		{
+		  gcc_checking_assert (!empty_var->in_changed_variables);
+		  if (!VAR_LOC_1PAUX (var))
+		    {
+		      VAR_LOC_1PAUX (var) = VAR_LOC_1PAUX (empty_var);
+		      VAR_LOC_1PAUX (empty_var) = NULL;
+		    }
+		  else
+		    gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
+		}
+	    }
+
+	  if (!empty_var)
+	    {
+	      empty_var = onepart_pool_allocate (onepart);
+	      empty_var->dv = var->dv;
+	      empty_var->refcount = 1;
+	      empty_var->n_var_parts = 0;
+	      empty_var->onepart = onepart;
+	      if (dslot)
+		{
+		  empty_var->refcount++;
+		  *dslot = empty_var;
+		}
+	    }
+	  else
+	    empty_var->refcount++;
 	  empty_var->in_changed_variables = true;
 	  *slot = empty_var;
+	  if (onepart)
+	    {
+	      empty_var->var_part[0].loc_chain = NULL;
+	      empty_var->var_part[0].cur_loc = NULL;
+	      VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (var);
+	      VAR_LOC_1PAUX (var) = NULL;
+	    }
 	  goto drop_var;
 	}
       else
 	{
+	  if (var->onepart && !VAR_LOC_1PAUX (var))
+	    recover_dropped_1paux (var);
 	  var->refcount++;
 	  var->in_changed_variables = true;
-	  /* If within processing one uop a variable is deleted
-	     and then readded, we need to assume it has changed.  */
-	  if (old_cur_loc_changed)
-	    var->cur_loc_changed = true;
 	  *slot = var;
 	}
     }
@@ -6410,7 +7487,7 @@
       gcc_assert (set);
       if (var->n_var_parts == 0)
 	{
-	  void **slot;
+	  variable **slot;
 
 	drop_var:
 	  slot = shared_hash_find_slot_noinsert (set->vars, var->dv);
@@ -6419,7 +7496,7 @@
 	      if (shared_hash_shared (set->vars))
 		slot = shared_hash_find_slot_unshare (&set->vars, var->dv,
 						      NO_INSERT);
-	      htab_clear_slot (shared_hash_htab (set->vars), slot);
+	      shared_hash_htab (set->vars)->clear_slot (slot);
 	    }
 	}
     }
@@ -6431,18 +7508,29 @@
    have, if it should be inserted.  */
 
 static inline int
-find_variable_location_part (variable var, HOST_WIDE_INT offset,
+find_variable_location_part (variable *var, HOST_WIDE_INT offset,
 			     int *insertion_point)
 {
   int pos, low, high;
 
+  if (var->onepart)
+    {
+      if (offset != 0)
+	return -1;
+
+      if (insertion_point)
+	*insertion_point = 0;
+
+      return var->n_var_parts - 1;
+    }
+
   /* 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)
+      if (VAR_PART_OFFSET (var, pos) < offset)
 	low = pos + 1;
       else
 	high = pos;
@@ -6452,27 +7540,32 @@
   if (insertion_point)
     *insertion_point = pos;
 
-  if (pos < var->n_var_parts && var->var_part[pos].offset == offset)
+  if (pos < var->n_var_parts && VAR_PART_OFFSET (var, pos) == offset)
     return pos;
 
   return -1;
 }
 
-static void **
-set_slot_part (dataflow_set *set, rtx loc, void **slot,
+static variable **
+set_slot_part (dataflow_set *set, rtx loc, variable **slot,
 	       decl_or_value dv, HOST_WIDE_INT offset,
 	       enum var_init_status initialized, rtx set_src)
 {
   int pos;
-  location_chain node, next;
-  location_chain *nextp;
-  variable var;
-  bool onepart = dv_onepart_p (dv);
-
-  gcc_assert (offset == 0 || !onepart);
-  gcc_assert (loc != dv_as_opaque (dv));
-
-  var = (variable) *slot;
+  location_chain *node, *next;
+  location_chain **nextp;
+  variable *var;
+  onepart_enum onepart;
+
+  var = *slot;
+
+  if (var)
+    onepart = var->onepart;
+  else
+    onepart = dv_onepart_p (dv);
+
+  gcc_checking_assert (offset == 0 || !onepart);
+  gcc_checking_assert (loc != dv_as_opaque (dv));
 
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
@@ -6480,13 +7573,16 @@
   if (!var)
     {
       /* Create new variable information.  */
-      var = (variable) pool_alloc (dv_pool (dv));
+      var = onepart_pool_allocate (onepart);
       var->dv = dv;
       var->refcount = 1;
       var->n_var_parts = 1;
-      var->cur_loc_changed = false;
+      var->onepart = onepart;
       var->in_changed_variables = false;
-      var->var_part[0].offset = offset;
+      if (var->onepart)
+	VAR_LOC_1PAUX (var) = NULL;
+      else
+	VAR_PART_OFFSET (var, 0) = offset;
       var->var_part[0].loc_chain = NULL;
       var->var_part[0].cur_loc = NULL;
       *slot = var;
@@ -6584,7 +7680,7 @@
       if (shared_var_p (var, set->vars))
 	{
 	  slot = unshare_variable (set, slot, var, initialized);
-	  var = (variable)*slot;
+	  var = *slot;
 	  for (nextp = &var->var_part[0].loc_chain; c;
 	       nextp = &(*nextp)->next)
 	    c--;
@@ -6623,7 +7719,7 @@
 	      if (shared_var_p (var, set->vars))
 		{
 		  slot = unshare_variable (set, slot, var, initialized);
-		  var = (variable)*slot;
+		  var = *slot;
 		}
 	    }
 	}
@@ -6635,13 +7731,13 @@
 	  if (shared_var_p (var, set->vars))
 	    {
 	      slot = unshare_variable (set, slot, var, initialized);
-	      var = (variable)*slot;
+	      var = *slot;
 	    }
 
 	  /* 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
-		      && (!var->n_var_parts || !dv_onepart_p (var->dv)));
+		      && (!var->n_var_parts || !onepart));
 
 	  /* We have to move the elements of array starting at index
 	     inspos to the next position.  */
@@ -6649,7 +7745,8 @@
 	    var->var_part[pos] = var->var_part[pos - 1];
 
 	  var->n_var_parts++;
-	  var->var_part[pos].offset = offset;
+	  gcc_checking_assert (!onepart);
+	  VAR_PART_OFFSET (var, pos) = offset;
 	  var->var_part[pos].loc_chain = NULL;
 	  var->var_part[pos].cur_loc = NULL;
 	}
@@ -6670,11 +7767,8 @@
 	      if (node->set_src != NULL && set_src == NULL)
 		set_src = node->set_src;
 	      if (var->var_part[pos].cur_loc == node->loc)
-		{
-		  var->var_part[pos].cur_loc = NULL;
-		  var->cur_loc_changed = true;
-		}
-	      pool_free (loc_chain_pool, node);
+		var->var_part[pos].cur_loc = NULL;
+	      delete node;
 	      *nextp = next;
 	      break;
 	    }
@@ -6686,16 +7780,13 @@
     }
 
   /* Add the location to the beginning.  */
-  node = (location_chain) pool_alloc (loc_chain_pool);
+  node = new location_chain;
   node->loc = loc;
   node->init = initialized;
   node->set_src = set_src;
   node->next = *nextp;
   *nextp = node;
 
-  if (onepart && emit_notes)
-    add_value_chains (var->dv, loc);
-
   /* If no location was emitted do so.  */
   if (var->var_part[pos].cur_loc == NULL)
     variable_was_changed (var, set);
@@ -6715,7 +7806,7 @@
 		   enum var_init_status initialized, rtx set_src,
 		   enum insert_option iopt)
 {
-  void **slot;
+  variable **slot;
 
   if (iopt == NO_INSERT)
     slot = shared_hash_find_slot_noinsert (set->vars, dv);
@@ -6725,7 +7816,7 @@
       if (!slot)
 	slot = shared_hash_find_slot_unshare (&set->vars, dv, iopt);
     }
-  slot = set_slot_part (set, loc, slot, dv, offset, initialized, set_src);
+  set_slot_part (set, loc, slot, dv, offset, initialized, set_src);
 }
 
 /* Remove all recorded register locations for the given variable part
@@ -6733,16 +7824,16 @@
    The variable part is specified by variable's declaration or value
    DV and offset OFFSET.  */
 
-static void **
-clobber_slot_part (dataflow_set *set, rtx loc, void **slot,
+static variable **
+clobber_slot_part (dataflow_set *set, rtx loc, variable **slot,
 		   HOST_WIDE_INT offset, rtx set_src)
 {
-  variable var = (variable) *slot;
+  variable *var = *slot;
   int pos = find_variable_location_part (var, offset, NULL);
 
   if (pos >= 0)
     {
-      location_chain node, next;
+      location_chain *node, *next;
 
       /* Remove the register locations from the dataflow set.  */
       next = var->var_part[pos].loc_chain;
@@ -6757,8 +7848,8 @@
 	    {
 	      if (REG_P (node->loc))
 		{
-		  attrs anode, anext;
-		  attrs *anextp;
+		  attrs *anode, *anext;
+		  attrs **anextp;
 
 		  /* Remove the variable part from the register's
 		     list, but preserve any other variable parts
@@ -6771,7 +7862,7 @@
 		      if (dv_as_opaque (anode->dv) == dv_as_opaque (var->dv)
 			  && anode->offset == offset)
 			{
-			  pool_free (attrs_pool, anode);
+			  delete anode;
 			  *anextp = anext;
 			}
 		      else
@@ -6796,7 +7887,7 @@
 clobber_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
 		       HOST_WIDE_INT offset, rtx set_src)
 {
-  void **slot;
+  variable **slot;
 
   if (!dv_as_opaque (dv)
       || (!dv_is_value_p (dv) && ! DECL_P (dv_as_decl (dv))))
@@ -6806,25 +7897,26 @@
   if (!slot)
     return;
 
-  slot = clobber_slot_part (set, loc, slot, offset, set_src);
+  clobber_slot_part (set, loc, slot, offset, set_src);
 }
 
 /* Delete the part of variable's location from dataflow set SET.  The
    variable part is specified by its SET->vars slot SLOT and offset
    OFFSET and the part's location by LOC.  */
 
-static void **
-delete_slot_part (dataflow_set *set, rtx loc, void **slot,
+static variable **
+delete_slot_part (dataflow_set *set, rtx loc, variable **slot,
 		  HOST_WIDE_INT offset)
 {
-  variable var = (variable) *slot;
+  variable *var = *slot;
   int pos = find_variable_location_part (var, offset, NULL);
 
   if (pos >= 0)
     {
-      location_chain node, next;
-      location_chain *nextp;
+      location_chain *node, *next;
+      location_chain **nextp;
       bool changed;
+      rtx cur_loc;
 
       if (shared_var_p (var, set->vars))
 	{
@@ -6839,12 +7931,17 @@
 		{
 		  slot = unshare_variable (set, slot, var,
 					   VAR_INIT_STATUS_UNKNOWN);
-		  var = (variable)*slot;
+		  var = *slot;
 		  break;
 		}
 	    }
 	}
 
+      if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+	cur_loc = VAR_LOC_FROM (var);
+      else
+	cur_loc = var->var_part[pos].cur_loc;
+
       /* Delete the location part.  */
       changed = false;
       nextp = &var->var_part[pos].loc_chain;
@@ -6855,18 +7952,17 @@
 	       && REGNO (node->loc) == REGNO (loc))
 	      || rtx_equal_p (node->loc, loc))
 	    {
-	      if (emit_notes && pos == 0 && dv_onepart_p (var->dv))
-		remove_value_chains (var->dv, node->loc);
 	      /* If we have deleted the location which was last emitted
 		 we have to emit new location so add the variable to set
 		 of changed variables.  */
-	      if (var->var_part[pos].cur_loc == node->loc)
+	      if (cur_loc == node->loc)
 		{
 		  changed = true;
 		  var->var_part[pos].cur_loc = NULL;
-		  var->cur_loc_changed = true;
+		  if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+		    VAR_LOC_FROM (var) = NULL;
 		}
-	      pool_free (loc_chain_pool, node);
+	      delete node;
 	      *nextp = next;
 	      break;
 	    }
@@ -6878,8 +7974,6 @@
 	{
 	  changed = true;
 	  var->n_var_parts--;
-	  if (emit_notes)
-	    var->cur_loc_changed = true;
 	  while (pos < var->n_var_parts)
 	    {
 	      var->var_part[pos] = var->var_part[pos + 1];
@@ -6901,65 +7995,435 @@
 delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
 		      HOST_WIDE_INT offset)
 {
-  void **slot = shared_hash_find_slot_noinsert (set->vars, dv);
+  variable **slot = shared_hash_find_slot_noinsert (set->vars, dv);
   if (!slot)
     return;
 
-  slot = delete_slot_part (set, loc, slot, offset);
-}
+  delete_slot_part (set, loc, slot, offset);
+}
+
 
 /* Structure for passing some other parameters to function
    vt_expand_loc_callback.  */
 struct expand_loc_callback_data
 {
   /* The variables and values active at this point.  */
-  htab_t vars;
-
-  /* True in vt_expand_loc_dummy calls, no rtl should be allocated.
-     Non-NULL should be returned if vt_expand_loc would return
-     non-NULL in that case, NULL otherwise.  cur_loc_changed should be
-     computed and cur_loc recomputed when possible (but just once
-     per emit_notes_for_changes call).  */
-  bool dummy;
-
-  /* True if expansion of subexpressions had to recompute some
-     VALUE/DEBUG_EXPR_DECL's cur_loc or used a VALUE/DEBUG_EXPR_DECL
-     whose cur_loc has been already recomputed during current
-     emit_notes_for_changes call.  */
-  bool cur_loc_changed;
+  variable_table_type *vars;
+
+  /* Stack of values and debug_exprs under expansion, and their
+     children.  */
+  auto_vec<rtx, 4> expanding;
+
+  /* Stack of values and debug_exprs whose expansion hit recursion
+     cycles.  They will have VALUE_RECURSED_INTO marked when added to
+     this list.  This flag will be cleared if any of its dependencies
+     resolves to a valid location.  So, if the flag remains set at the
+     end of the search, we know no valid location for this one can
+     possibly exist.  */
+  auto_vec<rtx, 4> pending;
+
+  /* The maximum depth among the sub-expressions under expansion.
+     Zero indicates no expansion so far.  */
+  expand_depth depth;
 };
 
+/* Allocate the one-part auxiliary data structure for VAR, with enough
+   room for COUNT dependencies.  */
+
+static void
+loc_exp_dep_alloc (variable *var, int count)
+{
+  size_t allocsize;
+
+  gcc_checking_assert (var->onepart);
+
+  /* We can be called with COUNT == 0 to allocate the data structure
+     without any dependencies, e.g. for the backlinks only.  However,
+     if we are specifying a COUNT, then the dependency list must have
+     been emptied before.  It would be possible to adjust pointers or
+     force it empty here, but this is better done at an earlier point
+     in the algorithm, so we instead leave an assertion to catch
+     errors.  */
+  gcc_checking_assert (!count
+		       || VAR_LOC_DEP_VEC (var) == NULL
+		       || VAR_LOC_DEP_VEC (var)->is_empty ());
+
+  if (VAR_LOC_1PAUX (var) && VAR_LOC_DEP_VEC (var)->space (count))
+    return;
+
+  allocsize = offsetof (struct onepart_aux, deps)
+	      + vec<loc_exp_dep, va_heap, vl_embed>::embedded_size (count);
+
+  if (VAR_LOC_1PAUX (var))
+    {
+      VAR_LOC_1PAUX (var) = XRESIZEVAR (struct onepart_aux,
+					VAR_LOC_1PAUX (var), allocsize);
+      /* If the reallocation moves the onepaux structure, the
+	 back-pointer to BACKLINKS in the first list member will still
+	 point to its old location.  Adjust it.  */
+      if (VAR_LOC_DEP_LST (var))
+	VAR_LOC_DEP_LST (var)->pprev = VAR_LOC_DEP_LSTP (var);
+    }
+  else
+    {
+      VAR_LOC_1PAUX (var) = XNEWVAR (struct onepart_aux, allocsize);
+      *VAR_LOC_DEP_LSTP (var) = NULL;
+      VAR_LOC_FROM (var) = NULL;
+      VAR_LOC_DEPTH (var).complexity = 0;
+      VAR_LOC_DEPTH (var).entryvals = 0;
+    }
+  VAR_LOC_DEP_VEC (var)->embedded_init (count);
+}
+
+/* Remove all entries from the vector of active dependencies of VAR,
+   removing them from the back-links lists too.  */
+
+static void
+loc_exp_dep_clear (variable *var)
+{
+  while (VAR_LOC_DEP_VEC (var) && !VAR_LOC_DEP_VEC (var)->is_empty ())
+    {
+      loc_exp_dep *led = &VAR_LOC_DEP_VEC (var)->last ();
+      if (led->next)
+	led->next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = led->next;
+      VAR_LOC_DEP_VEC (var)->pop ();
+    }
+}
+
+/* Insert an active dependency from VAR on X to the vector of
+   dependencies, and add the corresponding back-link to X's list of
+   back-links in VARS.  */
+
+static void
+loc_exp_insert_dep (variable *var, rtx x, variable_table_type *vars)
+{
+  decl_or_value dv;
+  variable *xvar;
+  loc_exp_dep *led;
+
+  dv = dv_from_rtx (x);
+
+  /* ??? Build a vector of variables parallel to EXPANDING, to avoid
+     an additional look up?  */
+  xvar = vars->find_with_hash (dv, dv_htab_hash (dv));
+
+  if (!xvar)
+    {
+      xvar = variable_from_dropped (dv, NO_INSERT);
+      gcc_checking_assert (xvar);
+    }
+
+  /* No point in adding the same backlink more than once.  This may
+     arise if say the same value appears in two complex expressions in
+     the same loc_list, or even more than once in a single
+     expression.  */
+  if (VAR_LOC_DEP_LST (xvar) && VAR_LOC_DEP_LST (xvar)->dv == var->dv)
+    return;
+
+  if (var->onepart == NOT_ONEPART)
+    led = new loc_exp_dep;
+  else
+    {
+      loc_exp_dep empty;
+      memset (&empty, 0, sizeof (empty));
+      VAR_LOC_DEP_VEC (var)->quick_push (empty);
+      led = &VAR_LOC_DEP_VEC (var)->last ();
+    }
+  led->dv = var->dv;
+  led->value = x;
+
+  loc_exp_dep_alloc (xvar, 0);
+  led->pprev = VAR_LOC_DEP_LSTP (xvar);
+  led->next = *led->pprev;
+  if (led->next)
+    led->next->pprev = &led->next;
+  *led->pprev = led;
+}
+
+/* Create active dependencies of VAR on COUNT values starting at
+   VALUE, and corresponding back-links to the entries in VARS.  Return
+   true if we found any pending-recursion results.  */
+
+static bool
+loc_exp_dep_set (variable *var, rtx result, rtx *value, int count,
+		 variable_table_type *vars)
+{
+  bool pending_recursion = false;
+
+  gcc_checking_assert (VAR_LOC_DEP_VEC (var) == NULL
+		       || VAR_LOC_DEP_VEC (var)->is_empty ());
+
+  /* Set up all dependencies from last_child (as set up at the end of
+     the loop above) to the end.  */
+  loc_exp_dep_alloc (var, count);
+
+  while (count--)
+    {
+      rtx x = *value++;
+
+      if (!pending_recursion)
+	pending_recursion = !result && VALUE_RECURSED_INTO (x);
+
+      loc_exp_insert_dep (var, x, vars);
+    }
+
+  return pending_recursion;
+}
+
+/* Notify the back-links of IVAR that are pending recursion that we
+   have found a non-NIL value for it, so they are cleared for another
+   attempt to compute a current location.  */
+
+static void
+notify_dependents_of_resolved_value (variable *ivar, variable_table_type *vars)
+{
+  loc_exp_dep *led, *next;
+
+  for (led = VAR_LOC_DEP_LST (ivar); led; led = next)
+    {
+      decl_or_value dv = led->dv;
+      variable *var;
+
+      next = led->next;
+
+      if (dv_is_value_p (dv))
+	{
+	  rtx value = dv_as_value (dv);
+
+	  /* If we have already resolved it, leave it alone.  */
+	  if (!VALUE_RECURSED_INTO (value))
+	    continue;
+
+	  /* Check that VALUE_RECURSED_INTO, true from the test above,
+	     implies NO_LOC_P.  */
+	  gcc_checking_assert (NO_LOC_P (value));
+
+	  /* We won't notify variables that are being expanded,
+	     because their dependency list is cleared before
+	     recursing.  */
+	  NO_LOC_P (value) = false;
+	  VALUE_RECURSED_INTO (value) = false;
+
+	  gcc_checking_assert (dv_changed_p (dv));
+	}
+      else
+	{
+	  gcc_checking_assert (dv_onepart_p (dv) != NOT_ONEPART);
+	  if (!dv_changed_p (dv))
+	    continue;
+      }
+
+      var = vars->find_with_hash (dv, dv_htab_hash (dv));
+
+      if (!var)
+	var = variable_from_dropped (dv, NO_INSERT);
+
+      if (var)
+	notify_dependents_of_resolved_value (var, vars);
+
+      if (next)
+	next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = next;
+      led->next = NULL;
+      led->pprev = NULL;
+    }
+}
+
+static rtx vt_expand_loc_callback (rtx x, bitmap regs,
+				   int max_depth, void *data);
+
+/* Return the combined depth, when one sub-expression evaluated to
+   BEST_DEPTH and the previous known depth was SAVED_DEPTH.  */
+
+static inline expand_depth
+update_depth (expand_depth saved_depth, expand_depth best_depth)
+{
+  /* If we didn't find anything, stick with what we had.  */
+  if (!best_depth.complexity)
+    return saved_depth;
+
+  /* If we found hadn't found anything, use the depth of the current
+     expression.  Do NOT add one extra level, we want to compute the
+     maximum depth among sub-expressions.  We'll increment it later,
+     if appropriate.  */
+  if (!saved_depth.complexity)
+    return best_depth;
+
+  /* Combine the entryval count so that regardless of which one we
+     return, the entryval count is accurate.  */
+  best_depth.entryvals = saved_depth.entryvals
+    = best_depth.entryvals + saved_depth.entryvals;
+
+  if (saved_depth.complexity < best_depth.complexity)
+    return best_depth;
+  else
+    return saved_depth;
+}
+
+/* Expand VAR to a location RTX, updating its cur_loc.  Use REGS and
+   DATA for cselib expand callback.  If PENDRECP is given, indicate in
+   it whether any sub-expression couldn't be fully evaluated because
+   it is pending recursion resolution.  */
+
+static inline rtx
+vt_expand_var_loc_chain (variable *var, bitmap regs, void *data,
+			 bool *pendrecp)
+{
+  struct expand_loc_callback_data *elcd
+    = (struct expand_loc_callback_data *) data;
+  location_chain *loc, *next;
+  rtx result = NULL;
+  int first_child, result_first_child, last_child;
+  bool pending_recursion;
+  rtx loc_from = NULL;
+  struct elt_loc_list *cloc = NULL;
+  expand_depth depth = { 0, 0 }, saved_depth = elcd->depth;
+  int wanted_entryvals, found_entryvals = 0;
+
+  /* Clear all backlinks pointing at this, so that we're not notified
+     while we're active.  */
+  loc_exp_dep_clear (var);
+
+ retry:
+  if (var->onepart == ONEPART_VALUE)
+    {
+      cselib_val *val = CSELIB_VAL_PTR (dv_as_value (var->dv));
+
+      gcc_checking_assert (cselib_preserved_value_p (val));
+
+      cloc = val->locs;
+    }
+
+  first_child = result_first_child = last_child
+    = elcd->expanding.length ();
+
+  wanted_entryvals = found_entryvals;
+
+  /* Attempt to expand each available location in turn.  */
+  for (next = loc = var->n_var_parts ? var->var_part[0].loc_chain : NULL;
+       loc || cloc; loc = next)
+    {
+      result_first_child = last_child;
+
+      if (!loc)
+	{
+	  loc_from = cloc->loc;
+	  next = loc;
+	  cloc = cloc->next;
+	  if (unsuitable_loc (loc_from))
+	    continue;
+	}
+      else
+	{
+	  loc_from = loc->loc;
+	  next = loc->next;
+	}
+
+      gcc_checking_assert (!unsuitable_loc (loc_from));
+
+      elcd->depth.complexity = elcd->depth.entryvals = 0;
+      result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH,
+					   vt_expand_loc_callback, data);
+      last_child = elcd->expanding.length ();
+
+      if (result)
+	{
+	  depth = elcd->depth;
+
+	  gcc_checking_assert (depth.complexity
+			       || result_first_child == last_child);
+
+	  if (last_child - result_first_child != 1)
+	    {
+	      if (!depth.complexity && GET_CODE (result) == ENTRY_VALUE)
+		depth.entryvals++;
+	      depth.complexity++;
+	    }
+
+	  if (depth.complexity <= EXPR_USE_DEPTH)
+	    {
+	      if (depth.entryvals <= wanted_entryvals)
+		break;
+	      else if (!found_entryvals || depth.entryvals < found_entryvals)
+		found_entryvals = depth.entryvals;
+	    }
+
+	  result = NULL;
+	}
+
+      /* Set it up in case we leave the loop.  */
+      depth.complexity = depth.entryvals = 0;
+      loc_from = NULL;
+      result_first_child = first_child;
+    }
+
+  if (!loc_from && wanted_entryvals < found_entryvals)
+    {
+      /* We found entries with ENTRY_VALUEs and skipped them.  Since
+	 we could not find any expansions without ENTRY_VALUEs, but we
+	 found at least one with them, go back and get an entry with
+	 the minimum number ENTRY_VALUE count that we found.  We could
+	 avoid looping, but since each sub-loc is already resolved,
+	 the re-expansion should be trivial.  ??? Should we record all
+	 attempted locs as dependencies, so that we retry the
+	 expansion should any of them change, in the hope it can give
+	 us a new entry without an ENTRY_VALUE?  */
+      elcd->expanding.truncate (first_child);
+      goto retry;
+    }
+
+  /* Register all encountered dependencies as active.  */
+  pending_recursion = loc_exp_dep_set
+    (var, result, elcd->expanding.address () + result_first_child,
+     last_child - result_first_child, elcd->vars);
+
+  elcd->expanding.truncate (first_child);
+
+  /* Record where the expansion came from.  */
+  gcc_checking_assert (!result || !pending_recursion);
+  VAR_LOC_FROM (var) = loc_from;
+  VAR_LOC_DEPTH (var) = depth;
+
+  gcc_checking_assert (!depth.complexity == !result);
+
+  elcd->depth = update_depth (saved_depth, depth);
+
+  /* Indicate whether any of the dependencies are pending recursion
+     resolution.  */
+  if (pendrecp)
+    *pendrecp = pending_recursion;
+
+  if (!pendrecp || !pending_recursion)
+    var->var_part[0].cur_loc = result;
+
+  return result;
+}
+
 /* Callback for cselib_expand_value, that looks for expressions
    holding the value in the var-tracking hash tables.  Return X for
    standard processing, anything else is to be used as-is.  */
 
 static rtx
-vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
+vt_expand_loc_callback (rtx x, bitmap regs,
+			int max_depth ATTRIBUTE_UNUSED,
+			void *data)
 {
   struct expand_loc_callback_data *elcd
     = (struct expand_loc_callback_data *) data;
-  bool dummy = elcd->dummy;
-  bool cur_loc_changed = elcd->cur_loc_changed;
   decl_or_value dv;
-  variable var;
-  location_chain loc;
-  rtx result, subreg, xret;
+  variable *var;
+  rtx result, subreg;
+  bool pending_recursion = false;
+  bool from_empty = false;
 
   switch (GET_CODE (x))
     {
     case SUBREG:
-      if (dummy)
-	{
-	  if (cselib_dummy_expand_value_rtx_cb (SUBREG_REG (x), regs,
-						max_depth - 1,
-						vt_expand_loc_callback, data))
-	    return pc_rtx;
-	  else
-	    return NULL;
-	}
-
       subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
-					   max_depth - 1,
+					   EXPR_DEPTH,
 					   vt_expand_loc_callback, data);
 
       if (!subreg)
@@ -6977,155 +8441,188 @@
       return result;
 
     case DEBUG_EXPR:
-      dv = dv_from_decl (DEBUG_EXPR_TREE_DECL (x));
-      xret = NULL;
-      break;
-
     case VALUE:
-      dv = dv_from_value (x);
-      xret = x;
+      dv = dv_from_rtx (x);
       break;
 
     default:
       return x;
     }
 
-  if (VALUE_RECURSED_INTO (x))
-    return NULL;
-
-  var = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
+  elcd->expanding.safe_push (x);
+
+  /* Check that VALUE_RECURSED_INTO implies NO_LOC_P.  */
+  gcc_checking_assert (!VALUE_RECURSED_INTO (x) || NO_LOC_P (x));
+
+  if (NO_LOC_P (x))
+    {
+      gcc_checking_assert (VALUE_RECURSED_INTO (x) || !dv_changed_p (dv));
+      return NULL;
+    }
+
+  var = elcd->vars->find_with_hash (dv, dv_htab_hash (dv));
 
   if (!var)
     {
-      if (dummy && dv_changed_p (dv))
-	elcd->cur_loc_changed = true;
-      return xret;
-    }
-
-  if (var->n_var_parts == 0)
-    {
-      if (dummy)
-	elcd->cur_loc_changed = true;
-      return xret;
-    }
-
-  gcc_assert (var->n_var_parts == 1);
+      from_empty = true;
+      var = variable_from_dropped (dv, INSERT);
+    }
+
+  gcc_checking_assert (var);
+
+  if (!dv_changed_p (dv))
+    {
+      gcc_checking_assert (!NO_LOC_P (x));
+      gcc_checking_assert (var->var_part[0].cur_loc);
+      gcc_checking_assert (VAR_LOC_1PAUX (var));
+      gcc_checking_assert (VAR_LOC_1PAUX (var)->depth.complexity);
+
+      elcd->depth = update_depth (elcd->depth, VAR_LOC_1PAUX (var)->depth);
+
+      return var->var_part[0].cur_loc;
+    }
 
   VALUE_RECURSED_INTO (x) = true;
-  result = NULL;
-
-  if (var->var_part[0].cur_loc)
-    {
-      if (dummy)
-	{
-	  if (cselib_dummy_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
-						max_depth,
-						vt_expand_loc_callback, data))
-	    result = pc_rtx;
-	}
-      else
-	result = cselib_expand_value_rtx_cb (var->var_part[0].cur_loc, regs,
-					     max_depth,
-					     vt_expand_loc_callback, data);
-      if (result)
-	set_dv_changed (dv, false);
-    }
-  if (!result && dv_changed_p (dv))
-    {
+  /* This is tentative, but it makes some tests simpler.  */
+  NO_LOC_P (x) = true;
+
+  gcc_checking_assert (var->n_var_parts == 1 || from_empty);
+
+  result = vt_expand_var_loc_chain (var, regs, data, &pending_recursion);
+
+  if (pending_recursion)
+    {
+      gcc_checking_assert (!result);
+      elcd->pending.safe_push (x);
+    }
+  else
+    {
+      NO_LOC_P (x) = !result;
+      VALUE_RECURSED_INTO (x) = false;
       set_dv_changed (dv, false);
-      for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
-	if (loc->loc == var->var_part[0].cur_loc)
-	  continue;
-	else if (dummy)
-	  {
-	    elcd->cur_loc_changed = cur_loc_changed;
-	    if (cselib_dummy_expand_value_rtx_cb (loc->loc, regs, max_depth,
-						  vt_expand_loc_callback,
-						  data))
-	      {
-		result = pc_rtx;
-		break;
-	      }
-	  }
-	else
-	  {
-	    result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
-						 vt_expand_loc_callback, data);
-	    if (result)
-	      break;
-	  }
-      if (dummy && (result || var->var_part[0].cur_loc))
-	var->cur_loc_changed = true;
-      var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
-    }
-  if (dummy)
-    {
-      if (var->cur_loc_changed)
-	elcd->cur_loc_changed = true;
-      else if (!result && var->var_part[0].cur_loc == NULL_RTX)
-	elcd->cur_loc_changed = cur_loc_changed;
-    }
-
-  VALUE_RECURSED_INTO (x) = false;
-  if (result)
-    return result;
-  else
-    return xret;
-}
-
-/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence
-   tables.  */
+
+      if (result)
+	notify_dependents_of_resolved_value (var, elcd->vars);
+    }
+
+  return result;
+}
+
+/* While expanding variables, we may encounter recursion cycles
+   because of mutual (possibly indirect) dependencies between two
+   particular variables (or values), say A and B.  If we're trying to
+   expand A when we get to B, which in turn attempts to expand A, if
+   we can't find any other expansion for B, we'll add B to this
+   pending-recursion stack, and tentatively return NULL for its
+   location.  This tentative value will be used for any other
+   occurrences of B, unless A gets some other location, in which case
+   it will notify B that it is worth another try at computing a
+   location for it, and it will use the location computed for A then.
+   At the end of the expansion, the tentative NULL locations become
+   final for all members of PENDING that didn't get a notification.
+   This function performs this finalization of NULL locations.  */
+
+static void
+resolve_expansions_pending_recursion (vec<rtx, va_heap> *pending)
+{
+  while (!pending->is_empty ())
+    {
+      rtx x = pending->pop ();
+      decl_or_value dv;
+
+      if (!VALUE_RECURSED_INTO (x))
+	continue;
+
+      gcc_checking_assert (NO_LOC_P (x));
+      VALUE_RECURSED_INTO (x) = false;
+      dv = dv_from_rtx (x);
+      gcc_checking_assert (dv_changed_p (dv));
+      set_dv_changed (dv, false);
+    }
+}
+
+/* Initialize expand_loc_callback_data D with variable hash table V.
+   It must be a macro because of alloca (vec stack).  */
+#define INIT_ELCD(d, v)						\
+  do								\
+    {								\
+      (d).vars = (v);						\
+      (d).depth.complexity = (d).depth.entryvals = 0;		\
+    }								\
+  while (0)
+/* Finalize expand_loc_callback_data D, resolved to location L.  */
+#define FINI_ELCD(d, l)						\
+  do								\
+    {								\
+      resolve_expansions_pending_recursion (&(d).pending);	\
+      (d).pending.release ();					\
+      (d).expanding.release ();					\
+								\
+      if ((l) && MEM_P (l))					\
+	(l) = targetm.delegitimize_address (l);			\
+    }								\
+  while (0)
+
+/* Expand VALUEs and DEBUG_EXPRs in LOC to a location, using the
+   equivalences in VARS, updating their CUR_LOCs in the process.  */
 
 static rtx
-vt_expand_loc (rtx loc, htab_t vars)
+vt_expand_loc (rtx loc, variable_table_type *vars)
 {
   struct expand_loc_callback_data data;
+  rtx result;
 
   if (!MAY_HAVE_DEBUG_INSNS)
     return loc;
 
-  data.vars = vars;
-  data.dummy = false;
-  data.cur_loc_changed = false;
-  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 8,
-				    vt_expand_loc_callback, &data);
-
-  if (loc && MEM_P (loc))
-    loc = targetm.delegitimize_address (loc);
-  return loc;
-}
-
-/* Like vt_expand_loc, but only return true/false (whether vt_expand_loc
-   would succeed or not, without actually allocating new rtxes.  */
-
-static bool
-vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
+  INIT_ELCD (data, vars);
+
+  result = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
+				       vt_expand_loc_callback, &data);
+
+  FINI_ELCD (data, result);
+
+  return result;
+}
+
+/* Expand the one-part VARiable to a location, using the equivalences
+   in VARS, updating their CUR_LOCs in the process.  */
+
+static rtx
+vt_expand_1pvar (variable *var, variable_table_type *vars)
 {
   struct expand_loc_callback_data data;
-  bool ret;
-
-  gcc_assert (MAY_HAVE_DEBUG_INSNS);
-  data.vars = vars;
-  data.dummy = true;
-  data.cur_loc_changed = false;
-  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 8,
-					  vt_expand_loc_callback, &data);
-  *pcur_loc_changed = data.cur_loc_changed;
-  return ret;
+  rtx loc;
+
+  gcc_checking_assert (var->onepart && var->n_var_parts == 1);
+
+  if (!dv_changed_p (var->dv))
+    return var->var_part[0].cur_loc;
+
+  INIT_ELCD (data, vars);
+
+  loc = vt_expand_var_loc_chain (var, scratch_regs, &data, NULL);
+
+  gcc_checking_assert (data.expanding.is_empty ());
+
+  FINI_ELCD (data, loc);
+
+  return loc;
 }
 
 /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
    additional parameters: WHERE specifies whether the note shall be emitted
    before or after instruction INSN.  */
 
-static int
-emit_note_insn_var_location (void **varp, void *data)
-{
-  variable var = (variable) *varp;
-  rtx insn = ((emit_note_data *)data)->insn;
-  enum emit_note_where where = ((emit_note_data *)data)->where;
-  htab_t vars = ((emit_note_data *)data)->vars;
-  rtx note, note_vl;
+int
+emit_note_insn_var_location (variable **varp, emit_note_data *data)
+{
+  variable *var = *varp;
+  rtx_insn *insn = data->insn;
+  enum emit_note_where where = data->where;
+  variable_table_type *vars = data->vars;
+  rtx_note *note;
+  rtx note_vl;
   int i, j, n_var_parts;
   bool complete;
   enum var_init_status initialized = VAR_INIT_STATUS_UNINITIALIZED;
@@ -7134,51 +8631,70 @@
   HOST_WIDE_INT offsets[MAX_VAR_PARTS];
   rtx loc[MAX_VAR_PARTS];
   tree decl;
-  location_chain lc;
-
-  if (dv_is_value_p (var->dv))
-    goto value_or_debug_decl;
+  location_chain *lc;
+
+  gcc_checking_assert (var->onepart == NOT_ONEPART
+		       || var->onepart == ONEPART_VDECL);
 
   decl = dv_as_decl (var->dv);
 
-  if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
-    goto value_or_debug_decl;
-
   complete = true;
   last_limit = 0;
   n_var_parts = 0;
-  if (!MAY_HAVE_DEBUG_INSNS)
-    {
-      for (i = 0; i < var->n_var_parts; i++)
-	if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain)
-	  {
-	    var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc;
-	    var->cur_loc_changed = true;
-	  }
-      if (var->n_var_parts == 0)
-	var->cur_loc_changed = true;
-    }
-  if (!var->cur_loc_changed)
-    goto clear;
+  if (!var->onepart)
+    for (i = 0; i < var->n_var_parts; i++)
+      if (var->var_part[i].cur_loc == NULL && var->var_part[i].loc_chain)
+	var->var_part[i].cur_loc = var->var_part[i].loc_chain->loc;
   for (i = 0; i < var->n_var_parts; i++)
     {
-      enum machine_mode mode, wider_mode;
+      machine_mode mode, wider_mode;
       rtx loc2;
-
-      if (last_limit < var->var_part[i].offset)
-	{
-	  complete = false;
-	  break;
-	}
-      else if (last_limit > var->var_part[i].offset)
-	continue;
-      offsets[n_var_parts] = var->var_part[i].offset;
-      if (!var->var_part[i].cur_loc)
-	{
-	  complete = false;
-	  continue;
-	}
-      loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars);
+      HOST_WIDE_INT offset;
+
+      if (i == 0 && var->onepart)
+	{
+	  gcc_checking_assert (var->n_var_parts == 1);
+	  offset = 0;
+	  initialized = VAR_INIT_STATUS_INITIALIZED;
+	  loc2 = vt_expand_1pvar (var, vars);
+	}
+      else
+	{
+	  if (last_limit < VAR_PART_OFFSET (var, i))
+	    {
+	      complete = false;
+	      break;
+	    }
+	  else if (last_limit > VAR_PART_OFFSET (var, i))
+	    continue;
+	  offset = VAR_PART_OFFSET (var, i);
+	  loc2 = var->var_part[i].cur_loc;
+	  if (loc2 && GET_CODE (loc2) == MEM
+	      && GET_CODE (XEXP (loc2, 0)) == VALUE)
+	    {
+	      rtx depval = XEXP (loc2, 0);
+
+	      loc2 = vt_expand_loc (loc2, vars);
+
+	      if (loc2)
+		loc_exp_insert_dep (var, depval, vars);
+	    }
+	  if (!loc2)
+	    {
+	      complete = false;
+	      continue;
+	    }
+	  gcc_checking_assert (GET_CODE (loc2) != VALUE);
+	  for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
+	    if (var->var_part[i].cur_loc == lc->loc)
+	      {
+		initialized = lc->init;
+		break;
+	      }
+	  gcc_assert (lc);
+	}
+
+      offsets[n_var_parts] = offset;
       if (!loc2)
 	{
 	  complete = false;
@@ -7186,36 +8702,28 @@
 	}
       loc[n_var_parts] = loc2;
       mode = GET_MODE (var->var_part[i].cur_loc);
-      if (mode == VOIDmode && dv_onepart_p (var->dv))
+      if (mode == VOIDmode && var->onepart)
 	mode = DECL_MODE (decl);
-      for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
-	if (var->var_part[i].cur_loc == lc->loc)
-	  {
-	    initialized = lc->init;
-	    break;
-	  }
-      gcc_assert (lc);
       last_limit = offsets[n_var_parts] + GET_MODE_SIZE (mode);
 
       /* Attempt to merge adjacent registers or memory.  */
-      wider_mode = GET_MODE_WIDER_MODE (mode);
       for (j = i + 1; j < var->n_var_parts; j++)
-	if (last_limit <= var->var_part[j].offset)
+	if (last_limit <= VAR_PART_OFFSET (var, j))
 	  break;
       if (j < var->n_var_parts
-	  && wider_mode != VOIDmode
+	  && GET_MODE_WIDER_MODE (mode).exists (&wider_mode)
 	  && var->var_part[j].cur_loc
 	  && mode == GET_MODE (var->var_part[j].cur_loc)
 	  && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts]))
-	  && last_limit == var->var_part[j].offset
+	  && last_limit == (var->onepart ? 0 : VAR_PART_OFFSET (var, j))
 	  && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
 	  && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
 	{
 	  rtx new_loc = NULL;
 
 	  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]
+	      && hard_regno_nregs (REGNO (loc[n_var_parts]), mode) * 2
+		 == hard_regno_nregs (REGNO (loc[n_var_parts]), wider_mode)
 	      && end_hard_regno (mode, REGNO (loc[n_var_parts]))
 		 == REGNO (loc2))
 	    {
@@ -7273,8 +8781,7 @@
 
   note_vl = NULL_RTX;
   if (!complete)
-    note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, NULL_RTX,
-				    (int) initialized);
+    note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, NULL_RTX, initialized);
   else if (n_var_parts == 1)
     {
       rtx expr_list;
@@ -7284,8 +8791,7 @@
       else
 	expr_list = loc[0];
 
-      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, expr_list,
-				      (int) initialized);
+      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, expr_list, initialized);
     }
   else if (n_var_parts)
     {
@@ -7298,7 +8804,7 @@
       parallel = gen_rtx_PARALLEL (VOIDmode,
 				   gen_rtvec_v (n_var_parts, loc));
       note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-				      parallel, (int) initialized);
+				      parallel, initialized);
     }
 
   if (where != EMIT_NOTE_BEFORE_INSN)
@@ -7312,282 +8818,273 @@
       /* Make sure that the call related notes come first.  */
       while (NEXT_INSN (insn)
 	     && NOTE_P (insn)
-	     && NOTE_DURING_CALL_P (insn))
+	     && ((NOTE_KIND (insn) == NOTE_INSN_VAR_LOCATION
+		  && NOTE_DURING_CALL_P (insn))
+		 || NOTE_KIND (insn) == NOTE_INSN_CALL_ARG_LOCATION))
 	insn = NEXT_INSN (insn);
-      if (NOTE_P (insn) && NOTE_DURING_CALL_P (insn))
+      if (NOTE_P (insn)
+	  && ((NOTE_KIND (insn) == NOTE_INSN_VAR_LOCATION
+	       && NOTE_DURING_CALL_P (insn))
+	      || NOTE_KIND (insn) == NOTE_INSN_CALL_ARG_LOCATION))
 	note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
       else
 	note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
     }
   NOTE_VAR_LOCATION (note) = note_vl;
 
- clear:
   set_dv_changed (var->dv, false);
-  var->cur_loc_changed = false;
   gcc_assert (var->in_changed_variables);
   var->in_changed_variables = false;
-  htab_clear_slot (changed_variables, varp);
+  changed_variables->clear_slot (varp);
 
   /* Continue traversing the hash table.  */
   return 1;
-
- value_or_debug_decl:
-  if (dv_changed_p (var->dv) && var->n_var_parts)
-    {
-      location_chain lc;
-      bool cur_loc_changed;
-
-      if (var->var_part[0].cur_loc
-	  && vt_expand_loc_dummy (var->var_part[0].cur_loc, vars,
-				  &cur_loc_changed))
-	goto clear;
-      for (lc = var->var_part[0].loc_chain; lc; lc = lc->next)
-	if (lc->loc != var->var_part[0].cur_loc
-	    && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
-	  break;
-      var->var_part[0].cur_loc = lc ? lc->loc : NULL_RTX;
-    }
-  goto clear;
-}
-
-DEF_VEC_P (variable);
-DEF_VEC_ALLOC_P (variable, heap);
-
-/* Stack of variable_def pointers that need processing with
-   check_changed_vars_2.  */
-
-static VEC (variable, heap) *changed_variables_stack;
-
-/* VALUEs with no variables that need set_dv_changed (val, false)
-   called before check_changed_vars_3.  */
-
-static VEC (rtx, heap) *changed_values_stack;
-
-/* Helper function for check_changed_vars_1 and check_changed_vars_2.  */
+}
+
+/* While traversing changed_variables, push onto DATA (a stack of RTX
+   values) entries that aren't user variables.  */
+
+int
+var_track_values_to_stack (variable **slot,
+			   vec<rtx, va_heap> *changed_values_stack)
+{
+  variable *var = *slot;
+
+  if (var->onepart == ONEPART_VALUE)
+    changed_values_stack->safe_push (dv_as_value (var->dv));
+  else if (var->onepart == ONEPART_DEXPR)
+    changed_values_stack->safe_push (DECL_RTL_KNOWN_SET (dv_as_decl (var->dv)));
+
+  return 1;
+}
+
+/* Remove from changed_variables the entry whose DV corresponds to
+   value or debug_expr VAL.  */
+static void
+remove_value_from_changed_variables (rtx val)
+{
+  decl_or_value dv = dv_from_rtx (val);
+  variable **slot;
+  variable *var;
+
+  slot = changed_variables->find_slot_with_hash (dv, dv_htab_hash (dv),
+						NO_INSERT);
+  var = *slot;
+  var->in_changed_variables = false;
+  changed_variables->clear_slot (slot);
+}
+
+/* If VAL (a value or debug_expr) has backlinks to variables actively
+   dependent on it in HTAB or in CHANGED_VARIABLES, mark them as
+   changed, adding to CHANGED_VALUES_STACK any dependencies that may
+   have dependencies of their own to notify.  */
 
 static void
-check_changed_vars_0 (decl_or_value dv, htab_t htab)
-{
-  value_chain vc
-    = (value_chain) htab_find_with_hash (value_chains, dv, dv_htab_hash (dv));
-
-  if (vc == NULL)
-    return;
-  for (vc = vc->next; vc; vc = vc->next)
-    if (!dv_changed_p (vc->dv))
-      {
-	variable vcvar
-	  = (variable) htab_find_with_hash (htab, vc->dv,
-					    dv_htab_hash (vc->dv));
-	if (vcvar)
-	  {
-	    set_dv_changed (vc->dv, true);
-	    VEC_safe_push (variable, heap, changed_variables_stack, vcvar);
-	  }
-	else if (dv_is_value_p (vc->dv))
-	  {
-	    set_dv_changed (vc->dv, true);
-	    VEC_safe_push (rtx, heap, changed_values_stack,
-			   dv_as_value (vc->dv));
-	    check_changed_vars_0 (vc->dv, htab);
-	  }
-      }
-}
-
-/* Populate changed_variables_stack with variable_def pointers
-   that need variable_was_changed called on them.  */
-
-static int
-check_changed_vars_1 (void **slot, void *data)
-{
-  variable var = (variable) *slot;
-  htab_t htab = (htab_t) data;
-
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    check_changed_vars_0 (var->dv, htab);
-  return 1;
-}
-
-/* Add VAR to changed_variables and also for VALUEs add recursively
-   all DVs that aren't in changed_variables yet but reference the
-   VALUE from its loc_chain.  */
+notify_dependents_of_changed_value (rtx val, variable_table_type *htab,
+				    vec<rtx, va_heap> *changed_values_stack)
+{
+  variable **slot;
+  variable *var;
+  loc_exp_dep *led;
+  decl_or_value dv = dv_from_rtx (val);
+
+  slot = changed_variables->find_slot_with_hash (dv, dv_htab_hash (dv),
+						NO_INSERT);
+  if (!slot)
+    slot = htab->find_slot_with_hash (dv, dv_htab_hash (dv), NO_INSERT);
+  if (!slot)
+    slot = dropped_values->find_slot_with_hash (dv, dv_htab_hash (dv),
+						NO_INSERT);
+  var = *slot;
+
+  while ((led = VAR_LOC_DEP_LST (var)))
+    {
+      decl_or_value ldv = led->dv;
+      variable *ivar;
+
+      /* Deactivate and remove the backlink, as it was “used up”.  It
+	 makes no sense to attempt to notify the same entity again:
+	 either it will be recomputed and re-register an active
+	 dependency, or it will still have the changed mark.  */
+      if (led->next)
+	led->next->pprev = led->pprev;
+      if (led->pprev)
+	*led->pprev = led->next;
+      led->next = NULL;
+      led->pprev = NULL;
+
+      if (dv_changed_p (ldv))
+	continue;
+
+      switch (dv_onepart_p (ldv))
+	{
+	case ONEPART_VALUE:
+	case ONEPART_DEXPR:
+	  set_dv_changed (ldv, true);
+	  changed_values_stack->safe_push (dv_as_rtx (ldv));
+	  break;
+
+	case ONEPART_VDECL:
+	  ivar = htab->find_with_hash (ldv, dv_htab_hash (ldv));
+	  gcc_checking_assert (!VAR_LOC_DEP_LST (ivar));
+	  variable_was_changed (ivar, NULL);
+	  break;
+
+	case NOT_ONEPART:
+	  delete led;
+	  ivar = htab->find_with_hash (ldv, dv_htab_hash (ldv));
+	  if (ivar)
+	    {
+	      int i = ivar->n_var_parts;
+	      while (i--)
+		{
+		  rtx loc = ivar->var_part[i].cur_loc;
+
+		  if (loc && GET_CODE (loc) == MEM
+		      && XEXP (loc, 0) == val)
+		    {
+		      variable_was_changed (ivar, NULL);
+		      break;
+		    }
+		}
+	    }
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+}
+
+/* Take out of changed_variables any entries that don't refer to use
+   variables.  Back-propagate change notifications from values and
+   debug_exprs to their active dependencies in HTAB or in
+   CHANGED_VARIABLES.  */
 
 static void
-check_changed_vars_2 (variable var, htab_t htab)
-{
-  variable_was_changed (var, NULL);
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    check_changed_vars_0 (var->dv, htab);
-}
-
-/* For each changed decl (except DEBUG_EXPR_DECLs) recompute
-   cur_loc if needed (and cur_loc of all VALUEs and DEBUG_EXPR_DECLs
-   it needs and are also in changed variables) and track whether
-   cur_loc (or anything it uses to compute location) had to change
-   during the current emit_notes_for_changes call.  */
-
-static int
-check_changed_vars_3 (void **slot, void *data)
-{
-  variable var = (variable) *slot;
-  htab_t vars = (htab_t) data;
-  int i;
-  location_chain lc;
-  bool cur_loc_changed;
-
-  if (dv_is_value_p (var->dv)
-      || TREE_CODE (dv_as_decl (var->dv)) == DEBUG_EXPR_DECL)
-    return 1;
-
-  for (i = 0; i < var->n_var_parts; i++)
-    {
-      if (var->var_part[i].cur_loc
-	  && vt_expand_loc_dummy (var->var_part[i].cur_loc, vars,
-				  &cur_loc_changed))
-	{
-	  if (cur_loc_changed)
-	    var->cur_loc_changed = true;
-	  continue;
-	}
-      for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
-	if (lc->loc != var->var_part[i].cur_loc
-	    && vt_expand_loc_dummy (lc->loc, vars, &cur_loc_changed))
-	  break;
-      if (lc || var->var_part[i].cur_loc)
-	var->cur_loc_changed = true;
-      var->var_part[i].cur_loc = lc ? lc->loc : NULL_RTX;
-    }
-  if (var->n_var_parts == 0)
-    var->cur_loc_changed = true;
-  return 1;
+process_changed_values (variable_table_type *htab)
+{
+  int i, n;
+  rtx val;
+  auto_vec<rtx, 20> changed_values_stack;
+
+  /* Move values from changed_variables to changed_values_stack.  */
+  changed_variables
+    ->traverse <vec<rtx, va_heap>*, var_track_values_to_stack>
+      (&changed_values_stack);
+
+  /* Back-propagate change notifications in values while popping
+     them from the stack.  */
+  for (n = i = changed_values_stack.length ();
+       i > 0; i = changed_values_stack.length ())
+    {
+      val = changed_values_stack.pop ();
+      notify_dependents_of_changed_value (val, htab, &changed_values_stack);
+
+      /* This condition will hold when visiting each of the entries
+	 originally in changed_variables.  We can't remove them
+	 earlier because this could drop the backlinks before we got a
+	 chance to use them.  */
+      if (i == n)
+	{
+	  remove_value_from_changed_variables (val);
+	  n--;
+	}
+    }
 }
 
 /* Emit NOTE_INSN_VAR_LOCATION note for each variable from a chain
-   CHANGED_VARIABLES and delete this chain.  WHERE specifies whether the notes
-   shall be emitted before of after instruction INSN.  */
+   CHANGED_VARIABLES and delete this chain.  WHERE specifies whether
+   the notes shall be emitted before of after instruction INSN.  */
 
 static void
-emit_notes_for_changes (rtx insn, enum emit_note_where where,
-			shared_hash vars)
+emit_notes_for_changes (rtx_insn *insn, enum emit_note_where where,
+			shared_hash *vars)
 {
   emit_note_data data;
-  htab_t htab = shared_hash_htab (vars);
-
-  if (!htab_elements (changed_variables))
+  variable_table_type *htab = shared_hash_htab (vars);
+
+  if (!changed_variables->elements ())
     return;
 
   if (MAY_HAVE_DEBUG_INSNS)
-    {
-      /* Unfortunately this has to be done in two steps, because
-	 we can't traverse a hashtab into which we are inserting
-	 through variable_was_changed.  */
-      htab_traverse (changed_variables, check_changed_vars_1, htab);
-      while (VEC_length (variable, changed_variables_stack) > 0)
-	check_changed_vars_2 (VEC_pop (variable, changed_variables_stack),
-			      htab);
-      while (VEC_length (rtx, changed_values_stack) > 0)
-	set_dv_changed (dv_from_value (VEC_pop (rtx, changed_values_stack)),
-			false);
-      htab_traverse (changed_variables, check_changed_vars_3, htab);
-    }
+    process_changed_values (htab);
 
   data.insn = insn;
   data.where = where;
   data.vars = htab;
 
-  htab_traverse (changed_variables, emit_note_insn_var_location, &data);
+  changed_variables
+    ->traverse <emit_note_data*, emit_note_insn_var_location> (&data);
 }
 
 /* Add variable *SLOT to the chain CHANGED_VARIABLES if it differs from the
    same variable in hash table DATA or is not there at all.  */
 
-static int
-emit_notes_for_differences_1 (void **slot, void *data)
-{
-  htab_t new_vars = (htab_t) data;
-  variable old_var, new_var;
-
-  old_var = (variable) *slot;
-  new_var = (variable) htab_find_with_hash (new_vars, old_var->dv,
-					    dv_htab_hash (old_var->dv));
+int
+emit_notes_for_differences_1 (variable **slot, variable_table_type *new_vars)
+{
+  variable *old_var, *new_var;
+
+  old_var = *slot;
+  new_var = new_vars->find_with_hash (old_var->dv, dv_htab_hash (old_var->dv));
 
   if (!new_var)
     {
       /* Variable has disappeared.  */
-      variable empty_var;
-
-      empty_var = (variable) pool_alloc (dv_pool (old_var->dv));
-      empty_var->dv = old_var->dv;
-      empty_var->refcount = 0;
-      empty_var->n_var_parts = 0;
-      empty_var->cur_loc_changed = false;
-      empty_var->in_changed_variables = false;
-      if (dv_onepart_p (old_var->dv))
-	{
-	  location_chain lc;
-
-	  gcc_assert (old_var->n_var_parts == 1);
-	  for (lc = old_var->var_part[0].loc_chain; lc; lc = lc->next)
-	    remove_value_chains (old_var->dv, lc->loc);
+      variable *empty_var = NULL;
+
+      if (old_var->onepart == ONEPART_VALUE
+	  || old_var->onepart == ONEPART_DEXPR)
+	{
+	  empty_var = variable_from_dropped (old_var->dv, NO_INSERT);
+	  if (empty_var)
+	    {
+	      gcc_checking_assert (!empty_var->in_changed_variables);
+	      if (!VAR_LOC_1PAUX (old_var))
+		{
+		  VAR_LOC_1PAUX (old_var) = VAR_LOC_1PAUX (empty_var);
+		  VAR_LOC_1PAUX (empty_var) = NULL;
+		}
+	      else
+		gcc_checking_assert (!VAR_LOC_1PAUX (empty_var));
+	    }
+	}
+
+      if (!empty_var)
+	{
+	  empty_var = onepart_pool_allocate (old_var->onepart);
+	  empty_var->dv = old_var->dv;
+	  empty_var->refcount = 0;
+	  empty_var->n_var_parts = 0;
+	  empty_var->onepart = old_var->onepart;
+	  empty_var->in_changed_variables = false;
+	}
+
+      if (empty_var->onepart)
+	{
+	  /* Propagate the auxiliary data to (ultimately)
+	     changed_variables.  */
+	  empty_var->var_part[0].loc_chain = NULL;
+	  empty_var->var_part[0].cur_loc = NULL;
+	  VAR_LOC_1PAUX (empty_var) = VAR_LOC_1PAUX (old_var);
+	  VAR_LOC_1PAUX (old_var) = NULL;
 	}
       variable_was_changed (empty_var, NULL);
       /* Continue traversing the hash table.  */
       return 1;
     }
+  /* Update cur_loc and one-part auxiliary data, before new_var goes
+     through variable_was_changed.  */
+  if (old_var != new_var && new_var->onepart)
+    {
+      gcc_checking_assert (VAR_LOC_1PAUX (new_var) == NULL);
+      VAR_LOC_1PAUX (new_var) = VAR_LOC_1PAUX (old_var);
+      VAR_LOC_1PAUX (old_var) = NULL;
+      new_var->var_part[0].cur_loc = old_var->var_part[0].cur_loc;
+    }
   if (variable_different_p (old_var, new_var))
-    {
-      if (dv_onepart_p (old_var->dv))
-	{
-	  location_chain lc1, lc2;
-
-	  gcc_assert (old_var->n_var_parts == 1
-		      && new_var->n_var_parts == 1);
-	  lc1 = old_var->var_part[0].loc_chain;
-	  lc2 = new_var->var_part[0].loc_chain;
-	  while (lc1
-		 && lc2
-		 && ((REG_P (lc1->loc) && REG_P (lc2->loc))
-		     || rtx_equal_p (lc1->loc, lc2->loc)))
-	    {
-	      lc1 = lc1->next;
-	      lc2 = lc2->next;
-	    }
-	  for (; lc2; lc2 = lc2->next)
-	    add_value_chains (old_var->dv, lc2->loc);
-	  for (; lc1; lc1 = lc1->next)
-	    remove_value_chains (old_var->dv, lc1->loc);
-	}
-      variable_was_changed (new_var, NULL);
-    }
-  /* Update cur_loc.  */
-  if (old_var != new_var)
-    {
-      int i;
-      for (i = 0; i < new_var->n_var_parts; i++)
-	{
-	  new_var->var_part[i].cur_loc = NULL;
-	  if (old_var->n_var_parts != new_var->n_var_parts
-	      || old_var->var_part[i].offset != new_var->var_part[i].offset)
-	    new_var->cur_loc_changed = true;
-	  else if (old_var->var_part[i].cur_loc != NULL)
-	    {
-	      location_chain lc;
-	      rtx cur_loc = old_var->var_part[i].cur_loc;
-
-	      for (lc = new_var->var_part[i].loc_chain; lc; lc = lc->next)
-		if (lc->loc == cur_loc
-		    || rtx_equal_p (cur_loc, lc->loc))
-		  {
-		    new_var->var_part[i].cur_loc = lc->loc;
-		    break;
-		  }
-	      if (lc == NULL)
-		new_var->cur_loc_changed = true;
-	    }
-	}
-    }
+    variable_was_changed (new_var, NULL);
 
   /* Continue traversing the hash table.  */
   return 1;
@@ -7596,27 +9093,16 @@
 /* Add variable *SLOT to the chain CHANGED_VARIABLES if it is not in hash
    table DATA.  */
 
-static int
-emit_notes_for_differences_2 (void **slot, void *data)
-{
-  htab_t old_vars = (htab_t) data;
-  variable old_var, new_var;
-
-  new_var = (variable) *slot;
-  old_var = (variable) htab_find_with_hash (old_vars, new_var->dv,
-					    dv_htab_hash (new_var->dv));
+int
+emit_notes_for_differences_2 (variable **slot, variable_table_type *old_vars)
+{
+  variable *old_var, *new_var;
+
+  new_var = *slot;
+  old_var = old_vars->find_with_hash (new_var->dv, dv_htab_hash (new_var->dv));
   if (!old_var)
     {
       int i;
-      /* Variable has appeared.  */
-      if (dv_onepart_p (new_var->dv))
-	{
-	  location_chain lc;
-
-	  gcc_assert (new_var->n_var_parts == 1);
-	  for (lc = new_var->var_part[0].loc_chain; lc; lc = lc->next)
-	    add_value_chains (new_var->dv, lc->loc);
-	}
       for (i = 0; i < new_var->n_var_parts; i++)
 	new_var->var_part[i].cur_loc = NULL;
       variable_was_changed (new_var, NULL);
@@ -7630,18 +9116,35 @@
    NEW_SET.  */
 
 static void
-emit_notes_for_differences (rtx insn, dataflow_set *old_set,
+emit_notes_for_differences (rtx_insn *insn, dataflow_set *old_set,
 			    dataflow_set *new_set)
 {
-  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));
+  shared_hash_htab (old_set->vars)
+    ->traverse <variable_table_type *, emit_notes_for_differences_1>
+      (shared_hash_htab (new_set->vars));
+  shared_hash_htab (new_set->vars)
+    ->traverse <variable_table_type *, emit_notes_for_differences_2>
+      (shared_hash_htab (old_set->vars));
   emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN, new_set->vars);
 }
 
+/* Return the next insn after INSN that is not a NOTE_INSN_VAR_LOCATION.  */
+
+static rtx_insn *
+next_non_note_insn_var_location (rtx_insn *insn)
+{
+  while (insn)
+    {
+      insn = NEXT_INSN (insn);
+      if (insn == 0
+	  || !NOTE_P (insn)
+	  || NOTE_KIND (insn) != NOTE_INSN_VAR_LOCATION)
+	break;
+    }
+
+  return insn;
+}
+
 /* Emit the notes for changes of location parts in the basic block BB.  */
 
 static void
@@ -7653,15 +9156,45 @@
   dataflow_set_clear (set);
   dataflow_set_copy (set, &VTI (bb)->in);
 
-  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
-    {
-      rtx insn = mo->insn;
+  FOR_EACH_VEC_ELT (VTI (bb)->mos, i, mo)
+    {
+      rtx_insn *insn = mo->insn;
+      rtx_insn *next_insn = next_non_note_insn_var_location (insn);
 
       switch (mo->type)
 	{
 	  case MO_CALL:
-	    dataflow_set_clear_at_call (set);
+	    dataflow_set_clear_at_call (set, insn);
 	    emit_notes_for_changes (insn, EMIT_NOTE_AFTER_CALL_INSN, set->vars);
+	    {
+	      rtx arguments = mo->u.loc, *p = &arguments;
+	      rtx_note *note;
+	      while (*p)
+		{
+		  XEXP (XEXP (*p, 0), 1)
+		    = vt_expand_loc (XEXP (XEXP (*p, 0), 1),
+				     shared_hash_htab (set->vars));
+		  /* If expansion is successful, keep it in the list.  */
+		  if (XEXP (XEXP (*p, 0), 1))
+		    p = &XEXP (*p, 1);
+		  /* Otherwise, if the following item is data_value for it,
+		     drop it too too.  */
+		  else if (XEXP (*p, 1)
+			   && REG_P (XEXP (XEXP (*p, 0), 0))
+			   && MEM_P (XEXP (XEXP (XEXP (*p, 1), 0), 0))
+			   && REG_P (XEXP (XEXP (XEXP (XEXP (*p, 1), 0), 0),
+					   0))
+			   && REGNO (XEXP (XEXP (*p, 0), 0))
+			      == REGNO (XEXP (XEXP (XEXP (XEXP (*p, 1), 0),
+						    0), 0)))
+		    *p = XEXP (XEXP (*p, 1), 1);
+		  /* Just drop this item.  */
+		  else
+		    *p = XEXP (*p, 1);
+		}
+	      note = emit_note_after (NOTE_INSN_CALL_ARG_LOCATION, insn);
+	      NOTE_VAR_LOCATION (note) = arguments;
+	    }
 	    break;
 
 	  case MO_USE:
@@ -7673,7 +9206,7 @@
 	      else
 		var_mem_set (set, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
 
-	      emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN, set->vars);
+	      emit_notes_for_changes (insn, EMIT_NOTE_BEFORE_INSN, set->vars);
 	    }
 	    break;
 
@@ -7752,35 +9285,42 @@
 	  case MO_VAL_SET:
 	    {
 	      rtx loc = mo->u.loc;
-	      rtx val, vloc, uloc, reverse = NULL_RTX;
+	      rtx val, vloc, uloc;
+	      rtx dstv, srcv;
 
 	      vloc = loc;
-	      if (VAL_EXPR_HAS_REVERSE (loc))
-		{
-		  reverse = XEXP (loc, 1);
-		  vloc = XEXP (loc, 0);
-		}
 	      uloc = XEXP (vloc, 1);
 	      val = XEXP (vloc, 0);
 	      vloc = uloc;
 
+	      if (GET_CODE (uloc) == SET)
+		{
+		  dstv = SET_DEST (uloc);
+		  srcv = SET_SRC (uloc);
+		}
+	      else
+		{
+		  dstv = uloc;
+		  srcv = NULL;
+		}
+
 	      if (GET_CODE (val) == CONCAT)
 		{
-		  vloc = XEXP (val, 1);
+		  dstv = vloc = XEXP (val, 1);
 		  val = XEXP (val, 0);
 		}
 
 	      if (GET_CODE (vloc) == SET)
 		{
-		  rtx vsrc = SET_SRC (vloc);
-
-		  gcc_assert (val != vsrc);
+		  srcv = SET_SRC (vloc);
+
+		  gcc_assert (val != srcv);
 		  gcc_assert (vloc == uloc || VAL_NEEDS_RESOLUTION (loc));
 
-		  vloc = SET_DEST (vloc);
+		  dstv = vloc = SET_DEST (vloc);
 
 		  if (VAL_NEEDS_RESOLUTION (loc))
-		    val_resolve (set, val, vsrc, insn);
+		    val_resolve (set, val, srcv, insn);
 		}
 	      else if (VAL_NEEDS_RESOLUTION (loc))
 		{
@@ -7796,45 +9336,56 @@
 		      if (REG_P (uloc))
 			var_reg_delete (set, uloc, true);
 		      else if (MEM_P (uloc))
-			var_mem_delete (set, uloc, true);
+			{
+			  gcc_assert (MEM_P (dstv));
+			  gcc_assert (MEM_ATTRS (dstv) == MEM_ATTRS (uloc));
+			  var_mem_delete (set, dstv, true);
+			}
 		    }
 		  else
 		    {
 		      bool copied_p = VAL_EXPR_IS_COPIED (loc);
-		      rtx set_src = NULL;
+		      rtx src = NULL, dst = uloc;
 		      enum var_init_status status = VAR_INIT_STATUS_INITIALIZED;
 
 		      if (GET_CODE (uloc) == SET)
 			{
-			  set_src = SET_SRC (uloc);
-			  uloc = SET_DEST (uloc);
+			  src = SET_SRC (uloc);
+			  dst = SET_DEST (uloc);
 			}
 
 		      if (copied_p)
 			{
-			  status = find_src_status (set, set_src);
-
-			  set_src = find_src_set_src (set, set_src);
+			  status = find_src_status (set, src);
+
+			  src = find_src_set_src (set, src);
 			}
 
-		      if (REG_P (uloc))
-			var_reg_delete_and_set (set, uloc, !copied_p,
-						status, set_src);
-		      else if (MEM_P (uloc))
-			var_mem_delete_and_set (set, uloc, !copied_p,
-						status, set_src);
+		      if (REG_P (dst))
+			var_reg_delete_and_set (set, dst, !copied_p,
+						status, srcv);
+		      else if (MEM_P (dst))
+			{
+			  gcc_assert (MEM_P (dstv));
+			  gcc_assert (MEM_ATTRS (dstv) == MEM_ATTRS (dst));
+			  var_mem_delete_and_set (set, dstv, !copied_p,
+						  status, srcv);
+			}
 		    }
 		}
 	      else if (REG_P (uloc))
 		var_regno_delete (set, REGNO (uloc));
-
-	      val_store (set, val, vloc, insn, true);
-
-	      if (reverse)
-		val_store (set, XEXP (reverse, 0), XEXP (reverse, 1),
-			   insn, false);
-
-	      emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+	      else if (MEM_P (uloc))
+		{
+		  gcc_checking_assert (GET_CODE (vloc) == MEM);
+		  gcc_checking_assert (vloc == dstv);
+		  if (vloc != dstv)
+		    clobber_overlapping_mems (set, vloc);
+		}
+
+	      val_store (set, val, dstv, insn, true);
+
+	      emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
 				      set->vars);
 	    }
 	    break;
@@ -7857,7 +9408,7 @@
 		var_mem_delete_and_set (set, loc, true, VAR_INIT_STATUS_INITIALIZED,
 					set_src);
 
-	      emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+	      emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
 				      set->vars);
 	    }
 	    break;
@@ -7882,7 +9433,7 @@
 	      else
 		var_mem_delete_and_set (set, loc, false, src_status, set_src);
 
-	      emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+	      emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
 				      set->vars);
 	    }
 	    break;
@@ -7909,7 +9460,7 @@
 	      else
 		var_mem_delete (set, loc, true);
 
-	      emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+	      emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
 				      set->vars);
 	    }
 	    break;
@@ -7929,11 +9480,11 @@
   basic_block bb;
   dataflow_set cur;
 
-  gcc_assert (!htab_elements (changed_variables));
+  gcc_assert (!changed_variables->elements ());
 
   /* Free memory occupied by the out hash tables, as they aren't used
      anymore.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     dataflow_set_clear (&VTI (bb)->out);
 
   /* Enable emitting notes by functions (mainly by set_variable_part and
@@ -7942,51 +9493,42 @@
 
   if (MAY_HAVE_DEBUG_INSNS)
     {
-      unsigned int i;
-      rtx val;
-
-      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
-	add_cselib_value_chains (dv_from_value (val));
-      changed_variables_stack = VEC_alloc (variable, heap, 40);
-      changed_values_stack = VEC_alloc (rtx, heap, 40);
+      dropped_values = new variable_table_type (cselib_get_next_uid () * 2);
     }
 
   dataflow_set_init (&cur);
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       /* Emit the notes for changes of variable locations between two
 	 subsequent basic blocks.  */
       emit_notes_for_differences (BB_HEAD (bb), &cur, &VTI (bb)->in);
 
+      if (MAY_HAVE_DEBUG_INSNS)
+	local_get_addr_cache = new hash_map<rtx, rtx>;
+
       /* Emit the notes for the changes in the basic block itself.  */
       emit_notes_in_bb (bb, &cur);
 
+      if (MAY_HAVE_DEBUG_INSNS)
+	delete local_get_addr_cache;
+      local_get_addr_cache = NULL;
+
       /* Free memory occupied by the in hash table, we won't need it
 	 again.  */
       dataflow_set_clear (&VTI (bb)->in);
     }
-#ifdef ENABLE_CHECKING
-  htab_traverse (shared_hash_htab (cur.vars),
-		 emit_notes_for_differences_1,
-		 shared_hash_htab (empty_shared_hash));
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      unsigned int i;
-      rtx val;
-
-      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
-	remove_cselib_value_chains (dv_from_value (val));
-      gcc_assert (htab_elements (value_chains) == 0);
-    }
-#endif
+
+  if (flag_checking)
+    shared_hash_htab (cur.vars)
+      ->traverse <variable_table_type *, emit_notes_for_differences_1>
+	(shared_hash_htab (empty_shared_hash));
+
   dataflow_set_destroy (&cur);
 
   if (MAY_HAVE_DEBUG_INSNS)
-    {
-      VEC_free (variable, heap, changed_variables_stack);
-      VEC_free (rtx, heap, changed_values_stack);
-    }
+    delete dropped_values;
+  dropped_values = NULL;
 
   emit_notes = false;
 }
@@ -8006,6 +9548,32 @@
 	  return true;
 	}
     }
+  else if (GET_CODE (rtl) == PARALLEL)
+    {
+      tree decl = NULL_TREE;
+      HOST_WIDE_INT offset = MAX_VAR_PARTS;
+      int len = XVECLEN (rtl, 0), i;
+
+      for (i = 0; i < len; i++)
+	{
+	  rtx reg = XEXP (XVECEXP (rtl, 0, i), 0);
+	  if (!REG_P (reg) || !REG_ATTRS (reg))
+	    break;
+	  if (!decl)
+	    decl = REG_EXPR (reg);
+	  if (REG_EXPR (reg) != decl)
+	    break;
+	  if (REG_OFFSET (reg) < offset)
+	    offset = REG_OFFSET (reg);
+	}
+
+      if (i == len)
+	{
+	  *declp = decl;
+	  *offsetp = offset;
+	  return true;
+	}
+    }
   else if (MEM_P (rtl))
     {
       if (MEM_ATTRS (rtl))
@@ -8018,6 +9586,19 @@
   return false;
 }
 
+/* Record the value for the ENTRY_VALUE of RTL as a global equivalence
+   of VAL.  */
+
+static void
+record_entry_value (cselib_val *val, rtx rtl)
+{
+  rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
+
+  ENTRY_VALUE_EXP (ev) = rtl;
+
+  cselib_add_permanent_equiv (val, ev, get_insns ());
+}
+
 /* Insert function parameter PARM in IN and OUT sets of ENTRY_BLOCK.  */
 
 static void
@@ -8026,7 +9607,7 @@
   rtx decl_rtl = DECL_RTL_IF_SET (parm);
   rtx incoming = DECL_INCOMING_RTL (parm);
   tree decl;
-  enum machine_mode mode;
+  machine_mode mode;
   HOST_WIDE_INT offset;
   dataflow_set *out;
   decl_or_value dv;
@@ -8040,14 +9621,91 @@
   if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
     return;
 
+  /* If there is a DRAP register or a pseudo in internal_arg_pointer,
+     rewrite the incoming location of parameters passed on the stack
+     into MEMs based on the argument pointer, so that incoming doesn't
+     depend on a pseudo.  */
+  if (MEM_P (incoming)
+      && (XEXP (incoming, 0) == crtl->args.internal_arg_pointer
+	  || (GET_CODE (XEXP (incoming, 0)) == PLUS
+	      && XEXP (XEXP (incoming, 0), 0)
+		 == crtl->args.internal_arg_pointer
+	      && CONST_INT_P (XEXP (XEXP (incoming, 0), 1)))))
+    {
+      HOST_WIDE_INT off = -FIRST_PARM_OFFSET (current_function_decl);
+      if (GET_CODE (XEXP (incoming, 0)) == PLUS)
+	off += INTVAL (XEXP (XEXP (incoming, 0), 1));
+      incoming
+	= replace_equiv_address_nv (incoming,
+				    plus_constant (Pmode,
+						   arg_pointer_rtx, off));
+    }
+
+#ifdef HAVE_window_save
+  /* DECL_INCOMING_RTL uses the INCOMING_REGNO of parameter registers.
+     If the target machine has an explicit window save instruction, the
+     actual entry value is the corresponding OUTGOING_REGNO instead.  */
+  if (HAVE_window_save && !crtl->uses_only_leaf_regs)
+    {
+      if (REG_P (incoming)
+	  && HARD_REGISTER_P (incoming)
+	  && OUTGOING_REGNO (REGNO (incoming)) != REGNO (incoming))
+	{
+	  parm_reg p;
+	  p.incoming = incoming;
+	  incoming
+	    = gen_rtx_REG_offset (incoming, GET_MODE (incoming),
+				  OUTGOING_REGNO (REGNO (incoming)), 0);
+	  p.outgoing = incoming;
+	  vec_safe_push (windowed_parm_regs, p);
+	}
+      else if (GET_CODE (incoming) == PARALLEL)
+	{
+	  rtx outgoing
+	    = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (XVECLEN (incoming, 0)));
+	  int i;
+
+	  for (i = 0; i < XVECLEN (incoming, 0); i++)
+	    {
+	      rtx reg = XEXP (XVECEXP (incoming, 0, i), 0);
+	      parm_reg p;
+	      p.incoming = reg;
+	      reg = gen_rtx_REG_offset (reg, GET_MODE (reg),
+					OUTGOING_REGNO (REGNO (reg)), 0);
+	      p.outgoing = reg;
+	      XVECEXP (outgoing, 0, i)
+		= gen_rtx_EXPR_LIST (VOIDmode, reg,
+				     XEXP (XVECEXP (incoming, 0, i), 1));
+	      vec_safe_push (windowed_parm_regs, p);
+	    }
+
+	  incoming = outgoing;
+	}
+      else if (MEM_P (incoming)
+	       && REG_P (XEXP (incoming, 0))
+	       && HARD_REGISTER_P (XEXP (incoming, 0)))
+	{
+	  rtx reg = XEXP (incoming, 0);
+	  if (OUTGOING_REGNO (REGNO (reg)) != REGNO (reg))
+	    {
+	      parm_reg p;
+	      p.incoming = reg;
+	      reg = gen_raw_REG (GET_MODE (reg), OUTGOING_REGNO (REGNO (reg)));
+	      p.outgoing = reg;
+	      vec_safe_push (windowed_parm_regs, p);
+	      incoming = replace_equiv_address_nv (incoming, reg);
+	    }
+	}
+    }
+#endif
+
   if (!vt_get_decl_and_offset (incoming, &decl, &offset))
     {
-      if (REG_P (incoming) || MEM_P (incoming))
+      if (MEM_P (incoming))
 	{
 	  /* This means argument is passed by invisible reference.  */
 	  offset = 0;
 	  decl = parm;
-	  incoming = gen_rtx_MEM (GET_MODE (decl_rtl), incoming);
 	}
       else
 	{
@@ -8063,19 +9721,20 @@
 
   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));
+      /* If that DECL_RTL wasn't a pseudo that got spilled to
+	 memory, bail out.  Otherwise, 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.  */
+      if (decl != get_spill_slot_decl (false))
+        return;
       offset = 0;
     }
 
   if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
     return;
 
-  out = &VTI (ENTRY_BLOCK_PTR)->out;
+  out = &VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->out;
 
   dv = dv_from_decl (parm);
 
@@ -8087,6 +9746,7 @@
       && GET_CODE (incoming) != PARALLEL)
     {
       cselib_val *val;
+      rtx lowpart;
 
       /* ??? We shouldn't ever hit this, but it may happen because
 	 arguments passed by invisible reference aren't dealt with
@@ -8095,8 +9755,12 @@
       if (offset)
 	return;
 
-      val = cselib_lookup (var_lowpart (mode, incoming), mode, true,
-			   VOIDmode);
+      lowpart = var_lowpart (mode, incoming);
+      if (!lowpart)
+	return;
+
+      val = cselib_lookup_from_insn (lowpart, mode, true,
+				     VOIDmode, get_insns ());
 
       /* ??? Float-typed values in memory are not handled by
 	 cselib.  */
@@ -8107,6 +9771,17 @@
 			     VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
 	  dv = dv_from_value (val->val_rtx);
 	}
+
+      if (MEM_P (incoming))
+	{
+	  val = cselib_lookup_from_insn (XEXP (incoming, 0), mode, true,
+					 VOIDmode, get_insns ());
+	  if (val)
+	    {
+	      preserve_value (val);
+	      incoming = replace_equiv_address_nv (incoming, val->val_rtx);
+	    }
+	}
     }
 
   if (REG_P (incoming))
@@ -8117,6 +9792,41 @@
 			 incoming);
       set_variable_part (out, incoming, dv, offset,
 			 VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+      if (dv_is_value_p (dv))
+	{
+	  record_entry_value (CSELIB_VAL_PTR (dv_as_value (dv)), incoming);
+	  if (TREE_CODE (TREE_TYPE (parm)) == REFERENCE_TYPE
+	      && INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (parm))))
+	    {
+	      machine_mode indmode
+		= TYPE_MODE (TREE_TYPE (TREE_TYPE (parm)));
+	      rtx mem = gen_rtx_MEM (indmode, incoming);
+	      cselib_val *val = cselib_lookup_from_insn (mem, indmode, true,
+							 VOIDmode,
+							 get_insns ());
+	      if (val)
+		{
+		  preserve_value (val);
+		  record_entry_value (val, mem);
+		  set_variable_part (out, mem, dv_from_value (val->val_rtx), 0,
+				     VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+		}
+	    }
+	}
+    }
+  else if (GET_CODE (incoming) == PARALLEL && !dv_onepart_p (dv))
+    {
+      int i;
+
+      for (i = 0; i < XVECLEN (incoming, 0); i++)
+	{
+	  rtx reg = XEXP (XVECEXP (incoming, 0, i), 0);
+	  offset = REG_OFFSET (reg);
+	  gcc_assert (REGNO (reg) < FIRST_PSEUDO_REGISTER);
+	  attrs_list_insert (&out->regs[REGNO (reg)], dv, offset, reg);
+	  set_variable_part (out, reg, dv, offset,
+			     VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+	}
     }
   else if (MEM_P (incoming))
     {
@@ -8135,7 +9845,8 @@
 
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm; parm = DECL_CHAIN (parm))
-    vt_add_function_parameter (parm);
+    if (!POINTER_BOUNDS_P (parm))
+      vt_add_function_parameter (parm);
 
   if (DECL_HAS_VALUE_EXPR_P (DECL_RESULT (current_function_decl)))
     {
@@ -8150,38 +9861,6 @@
 	  && DECL_NAMELESS (vexpr))
 	vt_add_function_parameter (vexpr);
     }
-
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      cselib_preserve_only_values ();
-      cselib_reset_table (cselib_get_next_uid ());
-    }
-
-}
-
-/* Return true if INSN in the prologue initializes hard_frame_pointer_rtx.  */
-
-static bool
-fp_setter (rtx insn)
-{
-  rtx pat = PATTERN (insn);
-  if (RTX_FRAME_RELATED_P (insn))
-    {
-      rtx expr = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
-      if (expr)
-	pat = XEXP (expr, 0);
-    }
-  if (GET_CODE (pat) == SET)
-    return SET_DEST (pat) == hard_frame_pointer_rtx;
-  else if (GET_CODE (pat) == PARALLEL)
-    {
-      int i;
-      for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
-	if (GET_CODE (XVECEXP (pat, 0, i)) == SET
-	    && SET_DEST (XVECEXP (pat, 0, i)) == hard_frame_pointer_rtx)
-	  return true;
-    }
-  return false;
 }
 
 /* Initialize cfa_base_rtx, create a preserved VALUE for it and
@@ -8212,16 +9891,15 @@
 
   /* Tell alias analysis that cfa_base_rtx should share
      find_base_term value with stack pointer or hard frame pointer.  */
-  vt_equate_reg_base_value (cfa_base_rtx,
-			    frame_pointer_needed
-			    ? hard_frame_pointer_rtx : stack_pointer_rtx);
+  if (!frame_pointer_needed)
+    vt_equate_reg_base_value (cfa_base_rtx, stack_pointer_rtx);
+  else if (!crtl->stack_realign_tried)
+    vt_equate_reg_base_value (cfa_base_rtx, hard_frame_pointer_rtx);
+
   val = cselib_lookup_from_insn (cfa_base_rtx, GET_MODE (cfa_base_rtx), 1,
 				 VOIDmode, get_insns ());
   preserve_value (val);
   cselib_preserve_cfa_base_value (val, REGNO (cfa_base_rtx));
-  var_reg_decl_set (&VTI (ENTRY_BLOCK_PTR)->out, cfa_base_rtx,
-		    VAR_INIT_STATUS_INITIALIZED, dv_from_value (val->val_rtx),
-		    0, NULL_RTX, INSERT);
 }
 
 /* Allocate and initialize the data structures for variable tracking
@@ -8230,40 +9908,18 @@
 static bool
 vt_initialize (void)
 {
-  basic_block bb, prologue_bb = NULL;
+  basic_block bb;
   HOST_WIDE_INT fp_cfa_offset = -1;
 
-  alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def));
-
-  attrs_pool = create_alloc_pool ("attrs_def pool",
-				  sizeof (struct attrs_def), 1024);
-  var_pool = create_alloc_pool ("variable_def pool",
-				sizeof (struct variable_def)
-				+ (MAX_VAR_PARTS - 1)
-				* sizeof (((variable)NULL)->var_part[0]), 64);
-  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);
+  alloc_aux_for_blocks (sizeof (variable_tracking_info));
+
+  empty_shared_hash = shared_hash_pool.allocate ();
   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,
-				   variable_htab_free);
-  if (MAY_HAVE_DEBUG_INSNS)
-    {
-      value_chain_pool = create_alloc_pool ("value_chain_def pool",
-					    sizeof (struct value_chain_def),
-					    1024);
-      value_chains = htab_create (32, value_chain_htab_hash,
-				  value_chain_htab_eq, NULL);
-    }
+  empty_shared_hash->htab = new variable_table_type (1);
+  changed_variables = new variable_table_type (10);
 
   /* Init the IN and OUT sets.  */
-  FOR_ALL_BB (bb)
+  FOR_ALL_BB_FN (bb, cfun)
     {
       VTI (bb)->visited = false;
       VTI (bb)->flooded = false;
@@ -8276,16 +9932,61 @@
     {
       cselib_init (CSELIB_RECORD_MEMORY | CSELIB_PRESERVE_CONSTANTS);
       scratch_regs = BITMAP_ALLOC (NULL);
-      valvar_pool = create_alloc_pool ("small variable_def pool",
-				       sizeof (struct variable_def), 256);
-      preserved_values = VEC_alloc (rtx, heap, 256);
+      preserved_values.create (256);
+      global_get_addr_cache = new hash_map<rtx, rtx>;
     }
   else
     {
       scratch_regs = NULL;
-      valvar_pool = NULL;
-    }
-
+      global_get_addr_cache = NULL;
+    }
+
+  if (MAY_HAVE_DEBUG_INSNS)
+    {
+      rtx reg, expr;
+      int ofst;
+      cselib_val *val;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+      reg = frame_pointer_rtx;
+      ofst = FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
+      reg = arg_pointer_rtx;
+      ofst = ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
+
+      ofst -= INCOMING_FRAME_SP_OFFSET;
+
+      val = cselib_lookup_from_insn (reg, GET_MODE (reg), 1,
+				     VOIDmode, get_insns ());
+      preserve_value (val);
+      if (reg != hard_frame_pointer_rtx && fixed_regs[REGNO (reg)])
+	cselib_preserve_cfa_base_value (val, REGNO (reg));
+      expr = plus_constant (GET_MODE (stack_pointer_rtx),
+			    stack_pointer_rtx, -ofst);
+      cselib_add_permanent_equiv (val, expr, get_insns ());
+
+      if (ofst)
+	{
+	  val = cselib_lookup_from_insn (stack_pointer_rtx,
+					 GET_MODE (stack_pointer_rtx), 1,
+					 VOIDmode, get_insns ());
+	  preserve_value (val);
+	  expr = plus_constant (GET_MODE (reg), reg, ofst);
+	  cselib_add_permanent_equiv (val, expr, get_insns ());
+	}
+    }
+
+  /* In order to factor out the adjustments made to the stack pointer or to
+     the hard frame pointer and thus be able to use DW_OP_fbreg operations
+     instead of individual location lists, we're going to rewrite MEMs based
+     on them into MEMs based on the CFA by de-eliminating stack_pointer_rtx
+     or hard_frame_pointer_rtx to the virtual CFA pointer frame_pointer_rtx
+     resp. arg_pointer_rtx.  We can do this either when there is no frame
+     pointer in the function and stack adjustments are consistent for all
+     basic blocks or when there is a frame pointer and no stack realignment.
+     But we first have to check that frame_pointer_rtx resp. arg_pointer_rtx
+     has been eliminated.  */
   if (!frame_pointer_needed)
     {
       rtx reg, elim;
@@ -8328,16 +10029,45 @@
 	    }
 	  if (elim != hard_frame_pointer_rtx)
 	    fp_cfa_offset = -1;
-	  else
-	    prologue_bb = single_succ (ENTRY_BLOCK_PTR);
+	}
+      else
+	fp_cfa_offset = -1;
+    }
+
+  /* If the stack is realigned and a DRAP register is used, we're going to
+     rewrite MEMs based on it representing incoming locations of parameters
+     passed on the stack into MEMs based on the argument pointer.  Although
+     we aren't going to rewrite other MEMs, we still need to initialize the
+     virtual CFA pointer in order to ensure that the argument pointer will
+     be seen as a constant throughout the function.
+
+     ??? This doesn't work if FRAME_POINTER_CFA_OFFSET is defined.  */
+  else if (stack_realign_drap)
+    {
+      rtx reg, elim;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+      reg = frame_pointer_rtx;
+#else
+      reg = arg_pointer_rtx;
+#endif
+      elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+      if (elim != reg)
+	{
+	  if (GET_CODE (elim) == PLUS)
+	    elim = XEXP (elim, 0);
+	  if (elim == hard_frame_pointer_rtx)
+	    vt_init_cfa_base ();
 	}
     }
 
   hard_frame_pointer_adjustment = -1;
 
-  FOR_EACH_BB (bb)
-    {
-      rtx insn;
+  vt_add_function_parameters ();
+
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      rtx_insn *insn;
       HOST_WIDE_INT pre, post = 0;
       basic_block first_bb, last_bb;
 
@@ -8353,7 +10083,7 @@
       for (;;)
 	{
 	  edge e;
-	  if (bb->next_bb == EXIT_BLOCK_PTR
+	  if (bb->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun)
 	      || ! single_pred_p (bb->next_bb))
 	    break;
 	  e = find_edge (bb, bb->next_bb);
@@ -8385,8 +10115,7 @@
 			  if (dump_file && (dump_flags & TDF_DETAILS))
 			    log_op_type (PATTERN (insn), bb, insn,
 					 MO_ADJUST, dump_file);
-			  VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
-					 &mo);
+			  VTI (bb)->mos.safe_push (mo);
 			  VTI (bb)->out.stack_adjust += pre;
 			}
 		    }
@@ -8395,6 +10124,8 @@
 		  adjust_insn (bb, insn);
 		  if (MAY_HAVE_DEBUG_INSNS)
 		    {
+		      if (CALL_P (insn))
+			prepare_call_arguments (bb, insn);
 		      cselib_process_insn (insn);
 		      if (dump_file && (dump_flags & TDF_DETAILS))
 			{
@@ -8415,18 +10146,29 @@
 		      if (dump_file && (dump_flags & TDF_DETAILS))
 			log_op_type (PATTERN (insn), bb, insn,
 				     MO_ADJUST, dump_file);
-		      VEC_safe_push (micro_operation, heap, VTI (bb)->mos,
-				     &mo);
+		      VTI (bb)->mos.safe_push (mo);
 		      VTI (bb)->out.stack_adjust += post;
 		    }
 
-		  if (bb == prologue_bb
+		  if (fp_cfa_offset != -1
 		      && hard_frame_pointer_adjustment == -1
-		      && RTX_FRAME_RELATED_P (insn)
-		      && fp_setter (insn))
+		      && fp_setter_insn (insn))
 		    {
 		      vt_init_cfa_base ();
 		      hard_frame_pointer_adjustment = fp_cfa_offset;
+		      /* Disassociate sp from fp now.  */
+		      if (MAY_HAVE_DEBUG_INSNS)
+			{
+			  cselib_val *v;
+			  cselib_invalidate_rtx (stack_pointer_rtx);
+			  v = cselib_lookup (stack_pointer_rtx, Pmode, 1,
+					     VOIDmode);
+			  if (v && !cselib_preserved_value_p (v))
+			    {
+			      cselib_set_value_sp_based (v);
+			      preserve_value (v);
+			    }
+			}
 		    }
 		}
 	    }
@@ -8444,28 +10186,48 @@
     }
 
   hard_frame_pointer_adjustment = -1;
-  VTI (ENTRY_BLOCK_PTR)->flooded = true;
-  vt_add_function_parameters ();
+  VTI (ENTRY_BLOCK_PTR_FOR_FN (cfun))->flooded = true;
   cfa_base_rtx = NULL_RTX;
   return true;
 }
 
+/* This is *not* reset after each function.  It gives each
+   NOTE_INSN_DELETED_DEBUG_LABEL in the entire compilation
+   a unique label number.  */
+
+static int debug_label_num = 1;
+
 /* Get rid of all debug insns from the insn stream.  */
 
 static void
 delete_debug_insns (void)
 {
   basic_block bb;
-  rtx insn, next;
+  rtx_insn *insn, *next;
 
   if (!MAY_HAVE_DEBUG_INSNS)
     return;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       FOR_BB_INSNS_SAFE (bb, insn, next)
 	if (DEBUG_INSN_P (insn))
-	  delete_insn (insn);
+	  {
+	    tree decl = INSN_VAR_LOCATION_DECL (insn);
+	    if (TREE_CODE (decl) == LABEL_DECL
+		&& DECL_NAME (decl)
+		&& !DECL_RTL_SET_P (decl))
+	      {
+		PUT_CODE (insn, NOTE);
+		NOTE_KIND (insn) = NOTE_INSN_DELETED_DEBUG_LABEL;
+		NOTE_DELETED_LABEL_NAME (insn)
+		  = IDENTIFIER_POINTER (DECL_NAME (decl));
+		SET_DECL_RTL (decl, insn);
+		CODE_LABEL_NUMBER (insn) = debug_label_num++;
+	      }
+	    else
+	      delete_insn (insn);
+	  }
     }
 }
 
@@ -8489,12 +10251,12 @@
 {
   basic_block bb;
 
-  FOR_EACH_BB (bb)
-    {
-      VEC_free (micro_operation, heap, VTI (bb)->mos);
-    }
-
-  FOR_ALL_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      VTI (bb)->mos.release ();
+    }
+
+  FOR_ALL_BB_FN (bb, cfun)
     {
       dataflow_set_destroy (&VTI (bb)->in);
       dataflow_set_destroy (&VTI (bb)->out);
@@ -8505,24 +10267,32 @@
 	}
     }
   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);
-  free_alloc_pool (shared_hash_pool);
+  delete empty_shared_hash->htab;
+  empty_shared_hash->htab = NULL;
+  delete changed_variables;
+  changed_variables = NULL;
+  attrs_pool.release ();
+  var_pool.release ();
+  location_chain_pool.release ();
+  shared_hash_pool.release ();
 
   if (MAY_HAVE_DEBUG_INSNS)
     {
-      htab_delete (value_chains);
-      free_alloc_pool (value_chain_pool);
-      free_alloc_pool (valvar_pool);
-      VEC_free (rtx, heap, preserved_values);
+      if (global_get_addr_cache)
+	delete global_get_addr_cache;
+      global_get_addr_cache = NULL;
+      loc_exp_dep_pool.release ();
+      valvar_pool.release ();
+      preserved_values.release ();
       cselib_finish ();
       BITMAP_FREE (scratch_regs);
       scratch_regs = NULL;
     }
 
+#ifdef HAVE_window_save
+  vec_free (windowed_parm_regs);
+#endif
+
   if (vui_vec)
     XDELETEVEC (vui_vec);
   vui_vec = NULL;
@@ -8536,13 +10306,17 @@
 {
   bool success;
 
-  if (flag_var_tracking_assignments < 0)
+  if (flag_var_tracking_assignments < 0
+      /* Var-tracking right now assumes the IR doesn't contain
+	 any pseudos at this point.  */
+      || targetm.no_register_allocation)
     {
       delete_debug_insns ();
       return 0;
     }
 
-  if (n_basic_blocks > 500 && n_edges / n_basic_blocks >= 20)
+  if (n_basic_blocks_for_fn (cfun) > 500 &&
+      n_edges_for_fn (cfun) / n_basic_blocks_for_fn (cfun) >= 20)
     {
       vt_debug_insns_local (true);
       return 0;
@@ -8583,6 +10357,7 @@
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       dump_dataflow_sets ();
+      dump_reg_info (dump_file);
       dump_flow_info (dump_file, dump_flags);
     }
 
@@ -8608,29 +10383,45 @@
   return ret;
 }
 
-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 */
- }
+namespace {
+
+const pass_data pass_data_variable_tracking =
+{
+  RTL_PASS, /* type */
+  "vartrack", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_VAR_TRACKING, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
+
+class pass_variable_tracking : public rtl_opt_pass
+{
+public:
+  pass_variable_tracking (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_variable_tracking, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *)
+    {
+      return (flag_var_tracking && !targetm.delay_vartrack);
+    }
+
+  virtual unsigned int execute (function *)
+    {
+      return variable_tracking_main ();
+    }
+
+}; // class pass_variable_tracking
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_variable_tracking (gcc::context *ctxt)
+{
+  return new pass_variable_tracking (ctxt);
+}