OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / var-tracking.c
index 6e9af6e..d797c40 100644 (file)
@@ -1,5 +1,5 @@
 /* Variable tracking routines for the GNU compiler.
-   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
+   Copyright (C) 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011, 2012
    Free Software Foundation, Inc.
 
    This file is part of GCC.
@@ -34,7 +34,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
@@ -92,6 +92,7 @@
 #include "tm.h"
 #include "rtl.h"
 #include "tree.h"
+#include "tm_p.h"
 #include "hard-reg-set.h"
 #include "basic-block.h"
 #include "flags.h"
 #include "tree-flow.h"
 #include "cselib.h"
 #include "target.h"
-#include "toplev.h"
 #include "params.h"
+#include "diagnostic.h"
+#include "tree-pretty-print.h"
+#include "pointer-set.h"
+#include "recog.h"
+#include "tm_p.h"
 
 /* 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.
@@ -167,6 +172,13 @@ typedef struct micro_operation_def
   /* Type of micro operation.  */
   enum micro_operation_type type;
 
+  /* The instruction which the micro operation is in, for MO_USE,
+     MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
+     instruction or note in the original flow (before any var-tracking
+     notes are inserted, to simplify emission of notes), for MO_SET
+     and MO_CLOBBER.  */
+  rtx insn;
+
   union {
     /* Location.  For MO_SET and MO_COPY, this is the SET that
        performs the assignment, if known, otherwise it is the target
@@ -179,15 +191,11 @@ typedef struct micro_operation_def
     /* Stack adjustment.  */
     HOST_WIDE_INT adjust;
   } u;
-
-  /* The instruction which the micro operation is in, for MO_USE,
-     MO_USE_NO_VAR, MO_CALL and MO_ADJUST, or the subsequent
-     instruction or note in the original flow (before any var-tracking
-     notes are inserted, to simplify emission of notes), for MO_SET
-     and MO_CLOBBER.  */
-  rtx insn;
 } micro_operation;
 
+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;
@@ -256,11 +264,8 @@ typedef struct dataflow_set_def
    needed for variable tracking.  */
 typedef struct variable_tracking_info_def
 {
-  /* Number of micro operations stored in the MOS array.  */
-  int n_mos;
-
-  /* The array of micro operations.  */
-  micro_operation *mos;
+  /* The vector of micro operations.  */
+  VEC(micro_operation, heap) *mos;
 
   /* The IN and OUT set for dataflow analysis.  */
   dataflow_set in;
@@ -296,6 +301,48 @@ typedef struct location_chain_def
   enum var_init_status init;
 } *location_chain;
 
+/* 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.  */
+typedef struct loc_exp_dep_s
+{
+  /* 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_s *next;
+  /* A pointer to the pointer to this entry (head or prev's next) in
+     the doubly-linked list.  */
+  struct loc_exp_dep_s **pprev;
+} loc_exp_dep;
+
+DEF_VEC_O (loc_exp_dep);
+
+/* 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.  */
+  int depth;
+  /* Dependencies actively used when expand FROM into cur_loc.  */
+  VEC (loc_exp_dep, none) deps;
+};
+
 /* Structure describing one part of variable.  */
 typedef struct variable_part_def
 {
@@ -305,13 +352,33 @@ typedef struct variable_part_def
   /* Location which was last emitted to location list.  */
   rtx cur_loc;
 
-  /* The offset in the variable.  */
-  HOST_WIDE_INT offset;
+  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;
 } variable_part;
 
 /* Maximum number of location parts.  */
 #define MAX_VAR_PARTS 16
 
+/* Enumeration type used to discriminate various types of one-part
+   variables.  */
+typedef 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
+} onepart_enum_t;
+
 /* Structure describing where the variable is located.  */
 typedef struct variable_def
 {
@@ -323,35 +390,61 @@ typedef struct variable_def
   int refcount;
 
   /* Number of variable parts.  */
-  int n_var_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];
 } *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)
+#define INT_MEM_OFFSET(mem) (MEM_OFFSET_KNOWN_P (mem) ? MEM_OFFSET (mem) : 0)
+
+#if ENABLE_CHECKING && (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)
 
 /* Alloc pool for struct attrs_def.  */
 static alloc_pool attrs_pool;
@@ -368,24 +461,36 @@ 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;
-
 /* 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;
-
 /* 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 htab_t dropped_values;
+
 /* Empty shared hashtable.  */
 static shared_hash empty_shared_hash;
 
 /* Scratch register bitmap used by cselib_expand_value_rtx.  */
 static bitmap scratch_regs = NULL;
 
+#ifdef HAVE_window_save
+typedef struct GTY(()) parm_reg {
+  rtx outgoing;
+  rtx incoming;
+} parm_reg_t;
+
+DEF_VEC_O(parm_reg_t);
+DEF_VEC_ALLOC_O(parm_reg_t, gc);
+
+/* Vector of windowed parameter registers, if any.  */
+static VEC(parm_reg_t, gc) *windowed_parm_regs = NULL;
+#endif
+
 /* Variable used to tell whether cselib_process_insn called our hook.  */
 static bool cselib_hook_called;
 
@@ -394,9 +499,7 @@ 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 *,
                                               HOST_WIDE_INT *);
-static void bb_stack_adjust_offset (basic_block);
 static bool vt_stack_adjustments (void);
-static rtx adjust_stack_reference (rtx, HOST_WIDE_INT);
 static hashval_t variable_htab_hash (const void *);
 static int variable_htab_eq (const void *, const void *);
 static void variable_htab_free (void *);
@@ -410,7 +513,6 @@ static void attrs_list_union (attrs *, attrs);
 
 static void **unshare_variable (dataflow_set *set, void **slot, variable var,
                                enum var_init_status);
-static int vars_copy_1 (void **, void *);
 static void vars_copy (htab_t, htab_t);
 static tree var_debug_decl (tree);
 static void var_reg_set (dataflow_set *, rtx, enum var_init_status, rtx);
@@ -427,25 +529,19 @@ static void dataflow_set_init (dataflow_set *);
 static void dataflow_set_clear (dataflow_set *);
 static void dataflow_set_copy (dataflow_set *, dataflow_set *);
 static int variable_union_info_cmp_pos (const void *, const void *);
-static int variable_union (void **, void *);
-static int variable_canonicalize (void **, void *);
 static void dataflow_set_union (dataflow_set *, dataflow_set *);
 static location_chain find_loc_in_1pdv (rtx, variable, htab_t);
 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, bool);
-static int dataflow_set_different_1 (void **, void *);
+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 count_uses (rtx *, void *);
-static void count_uses_1 (rtx *, void *);
-static void count_stores (rtx, const_rtx, void *);
 static int add_uses (rtx *, void *);
 static void add_uses_1 (rtx *, void *);
 static void add_stores (rtx, const_rtx, void *);
@@ -459,6 +555,7 @@ static void dump_vars (htab_t);
 static void dump_dataflow_set (dataflow_set *);
 static void dump_dataflow_sets (void);
 
+static inline void set_dv_changed (decl_or_value, bool);
 static void variable_was_changed (variable, dataflow_set *);
 static void **set_slot_part (dataflow_set *, rtx, void **,
                             decl_or_value, HOST_WIDE_INT,
@@ -483,7 +580,7 @@ static void vt_emit_notes (void);
 
 static bool vt_get_decl_and_offset (rtx, tree *, HOST_WIDE_INT *);
 static void vt_add_function_parameters (void);
-static void vt_initialize (void);
+static bool vt_initialize (void);
 static void vt_finalize (void);
 
 /* Given a SET, calculate the amount of stack adjustment it contains
@@ -610,31 +707,6 @@ insn_stack_adjust_offset_pre_post (rtx insn, HOST_WIDE_INT *pre,
     }
 }
 
-/* Compute stack adjustment in basic block BB.  */
-
-static void
-bb_stack_adjust_offset (basic_block bb)
-{
-  HOST_WIDE_INT offset;
-  int i;
-
-  offset = VTI (bb)->in.stack_adjust;
-  for (i = 0; i < VTI (bb)->n_mos; i++)
-    {
-      if (VTI (bb)->mos[i].type == MO_ADJUST)
-       offset += VTI (bb)->mos[i].u.adjust;
-      else if (VTI (bb)->mos[i].type != MO_CALL)
-       {
-         if (MEM_P (VTI (bb)->mos[i].u.loc))
-           {
-             VTI (bb)->mos[i].u.loc
-               = adjust_stack_reference (VTI (bb)->mos[i].u.loc, -offset);
-           }
-       }
-    }
-  VTI (bb)->out.stack_adjust = offset;
-}
-
 /* Compute stack adjustments for all blocks by traversing DFS tree.
    Return true when the adjustments on all incoming edges are consistent.
    Heavily borrowed from pre_and_rev_post_order_compute.  */
@@ -647,6 +719,7 @@ vt_stack_adjustments (void)
 
   /* 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;
 
   /* Allocate stack for back-tracking up CFG.  */
@@ -670,9 +743,22 @@ vt_stack_adjustments (void)
       /* Check if the edge destination has been visited yet.  */
       if (!VTI (dest)->visited)
        {
+         rtx insn;
+         HOST_WIDE_INT pre, post, offset;
          VTI (dest)->visited = true;
-         VTI (dest)->in.stack_adjust = VTI (src)->out.stack_adjust;
-         bb_stack_adjust_offset (dest);
+         VTI (dest)->in.stack_adjust = offset = VTI (src)->out.stack_adjust;
+
+         if (dest != EXIT_BLOCK_PTR)
+           for (insn = BB_HEAD (dest);
+                insn != NEXT_INSN (BB_END (dest));
+                insn = NEXT_INSN (insn))
+             if (INSN_P (insn))
+               {
+                 insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+                 offset += pre + post;
+               }
+
+         VTI (dest)->out.stack_adjust = offset;
 
          if (EDGE_COUNT (dest->succs) > 0)
            /* Since the DEST node has been visited for the first
@@ -701,28 +787,396 @@ vt_stack_adjustments (void)
   return true;
 }
 
-/* Adjust stack reference MEM by ADJUSTMENT bytes and make it relative
-   to the argument pointer.  Return the new rtx.  */
+/* arg_pointer_rtx resp. frame_pointer_rtx if stack_pointer_rtx or
+   hard_frame_pointer_rtx is being mapped to it and offset for it.  */
+static rtx cfa_base_rtx;
+static HOST_WIDE_INT cfa_base_offset;
+
+/* 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);
+}
+
+/* Adjustment for hard_frame_pointer_rtx to cfa base reg,
+   or -1 if the replacement shouldn't be done.  */
+static HOST_WIDE_INT hard_frame_pointer_adjustment = -1;
+
+/* Data for adjust_mems callback.  */
+
+struct adjust_mem_data
+{
+  bool store;
+  enum machine_mode mem_mode;
+  HOST_WIDE_INT stack_adjust;
+  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;
+      if (!validate_subreg (GET_MODE (subreg), GET_MODE (*loc),
+                           *loc, subreg_lowpart_offset (GET_MODE (subreg),
+                                                        GET_MODE (*loc))))
+       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;
+      else
+       return -1;
+    default:
+      return 1;
+    }
+}
+
+/* Transform X into narrower mode MODE from wider mode WMODE.  */
 
 static rtx
-adjust_stack_reference (rtx mem, HOST_WIDE_INT adjustment)
+use_narrower_mode (rtx x, enum machine_mode mode, enum machine_mode wmode)
 {
-  rtx addr, cfa, tmp;
+  rtx op0, op1;
+  if (CONSTANT_P (x))
+    return lowpart_subreg (mode, x, wmode);
+  switch (GET_CODE (x))
+    {
+    case REG:
+      return lowpart_subreg (mode, x, wmode);
+    case PLUS:
+    case MINUS:
+    case MULT:
+      op0 = use_narrower_mode (XEXP (x, 0), mode, wmode);
+      op1 = use_narrower_mode (XEXP (x, 1), mode, wmode);
+      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));
+    default:
+      gcc_unreachable ();
+    }
+}
 
-#ifdef FRAME_POINTER_CFA_OFFSET
-  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
-  cfa = plus_constant (frame_pointer_rtx, adjustment);
-#else
-  adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
-  cfa = plus_constant (arg_pointer_rtx, adjustment);
+/* Helper function for adjusting used MEMs.  */
+
+static rtx
+adjust_mems (rtx loc, const_rtx old_rtx, void *data)
+{
+  struct adjust_mem_data *amd = (struct adjust_mem_data *) data;
+  rtx mem, addr = loc, tem;
+  enum machine_mode mem_mode_save;
+  bool store_save;
+  switch (GET_CODE (loc))
+    {
+    case REG:
+      /* Don't do any sp or fp replacements outside of MEM addresses
+         on the LHS.  */
+      if (amd->mem_mode == VOIDmode && amd->store)
+       return loc;
+      if (loc == stack_pointer_rtx
+         && !frame_pointer_needed
+         && cfa_base_rtx)
+       return compute_cfa_pointer (amd->stack_adjust);
+      else if (loc == hard_frame_pointer_rtx
+              && frame_pointer_needed
+              && hard_frame_pointer_adjustment != -1
+              && cfa_base_rtx)
+       return compute_cfa_pointer (hard_frame_pointer_adjustment);
+      gcc_checking_assert (loc != virtual_incoming_args_rtx);
+      return loc;
+    case MEM:
+      mem = loc;
+      if (!amd->store)
+       {
+         mem = targetm.delegitimize_address (mem);
+         if (mem != loc && !MEM_P (mem))
+           return simplify_replace_fn_rtx (mem, old_rtx, adjust_mems, data);
+       }
+
+      addr = XEXP (mem, 0);
+      mem_mode_save = amd->mem_mode;
+      amd->mem_mode = GET_MODE (mem);
+      store_save = amd->store;
+      amd->store = false;
+      addr = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
+      amd->store = store_save;
+      amd->mem_mode = mem_mode_save;
+      if (mem == loc)
+       addr = targetm.delegitimize_address (addr);
+      if (addr != XEXP (mem, 0))
+       mem = replace_equiv_address_nv (mem, addr);
+      if (!amd->store)
+       mem = avoid_constant_pool_reference (mem);
+      return mem;
+    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)));
+    case POST_INC:
+    case POST_DEC:
+      if (addr == loc)
+       addr = XEXP (loc, 0);
+      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);
+      return addr;
+    case PRE_MODIFY:
+      addr = XEXP (loc, 1);
+    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);
+      return addr;
+    case SUBREG:
+      /* First try without delegitimization of whole MEMs and
+        avoid_constant_pool_reference, which is more likely to succeed.  */
+      store_save = amd->store;
+      amd->store = true;
+      addr = simplify_replace_fn_rtx (SUBREG_REG (loc), old_rtx, adjust_mems,
+                                     data);
+      amd->store = store_save;
+      mem = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
+      if (mem == SUBREG_REG (loc))
+       {
+         tem = loc;
+         goto finish_subreg;
+       }
+      tem = simplify_gen_subreg (GET_MODE (loc), mem,
+                                GET_MODE (SUBREG_REG (loc)),
+                                SUBREG_BYTE (loc));
+      if (tem)
+       goto finish_subreg;
+      tem = simplify_gen_subreg (GET_MODE (loc), addr,
+                                GET_MODE (SUBREG_REG (loc)),
+                                SUBREG_BYTE (loc));
+      if (tem == NULL_RTX)
+       tem = gen_rtx_raw_SUBREG (GET_MODE (loc), addr, SUBREG_BYTE (loc));
+    finish_subreg:
+      if (MAY_HAVE_DEBUG_INSNS
+         && GET_CODE (tem) == SUBREG
+         && (GET_CODE (SUBREG_REG (tem)) == PLUS
+             || 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)))
+         && 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)));
+      return tem;
+    case ASM_OPERANDS:
+      /* Don't do any replacements in second and following
+        ASM_OPERANDS of inline-asm with multiple sets.
+        ASM_OPERANDS_INPUT_VEC, ASM_OPERANDS_INPUT_CONSTRAINT_VEC
+        and ASM_OPERANDS_LABEL_VEC need to be equal between
+        all the ASM_OPERANDs in the insn and adjust_insn will
+        fix this up.  */
+      if (ASM_OPERANDS_OUTPUT_IDX (loc) != 0)
+       return loc;
+      break;
+    default:
+      break;
+    }
+  return NULL_RTX;
+}
+
+/* Helper function for replacement of uses.  */
+
+static void
+adjust_mem_uses (rtx *x, void *data)
+{
+  rtx new_x = simplify_replace_fn_rtx (*x, NULL_RTX, adjust_mems, data);
+  if (new_x != *x)
+    validate_change (NULL_RTX, x, new_x, true);
+}
+
+/* Helper function for replacement of stores.  */
+
+static void
+adjust_mem_stores (rtx loc, const_rtx expr, void *data)
+{
+  if (MEM_P (loc))
+    {
+      rtx new_dest = simplify_replace_fn_rtx (SET_DEST (expr), NULL_RTX,
+                                             adjust_mems, data);
+      if (new_dest != SET_DEST (expr))
+       {
+         rtx xexpr = CONST_CAST_RTX (expr);
+         validate_change (NULL_RTX, &SET_DEST (xexpr), new_dest, true);
+       }
+    }
+}
+
+/* Simplify INSN.  Remove all {PRE,POST}_{INC,DEC,MODIFY} rtxes,
+   replace them with their value in the insn and add the side-effects
+   as other sets to the insn.  */
+
+static void
+adjust_insn (basic_block bb, rtx insn)
+{
+  struct adjust_mem_data amd;
+  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_length(parm_reg_t, windowed_parm_regs);
+      rtx rtl = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nregs * 2));
+      parm_reg_t *p;
+
+      FOR_EACH_VEC_ELT (parm_reg_t, windowed_parm_regs, i, p)
+       {
+         XVECEXP (rtl, 0, i * 2)
+           = gen_rtx_SET (VOIDmode, 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
 
-  addr = replace_rtx (copy_rtx (XEXP (mem, 0)), stack_pointer_rtx, cfa);
-  tmp = simplify_rtx (addr);
-  if (tmp)
-    addr = tmp;
+  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);
+
+  amd.store = false;
+  if (GET_CODE (PATTERN (insn)) == PARALLEL
+      && asm_noperands (PATTERN (insn)) > 0
+      && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
+    {
+      rtx body, set0;
+      int i;
+
+      /* inline-asm with multiple sets is tiny bit more complicated,
+        because the 3 vectors in ASM_OPERANDS need to be shared between
+        all ASM_OPERANDS in the instruction.  adjust_mems will
+        not touch ASM_OPERANDS other than the first one, asm_noperands
+        test above needs to be called before that (otherwise it would fail)
+        and afterwards this code fixes it up.  */
+      note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
+      body = PATTERN (insn);
+      set0 = XVECEXP (body, 0, 0);
+      gcc_checking_assert (GET_CODE (set0) == SET
+                          && GET_CODE (SET_SRC (set0)) == ASM_OPERANDS
+                          && ASM_OPERANDS_OUTPUT_IDX (SET_SRC (set0)) == 0);
+      for (i = 1; i < XVECLEN (body, 0); i++)
+       if (GET_CODE (XVECEXP (body, 0, i)) != SET)
+         break;
+       else
+         {
+           set = XVECEXP (body, 0, i);
+           gcc_checking_assert (GET_CODE (SET_SRC (set)) == ASM_OPERANDS
+                                && ASM_OPERANDS_OUTPUT_IDX (SET_SRC (set))
+                                   == i);
+           if (ASM_OPERANDS_INPUT_VEC (SET_SRC (set))
+               != ASM_OPERANDS_INPUT_VEC (SET_SRC (set0))
+               || ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set))
+                  != ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set0))
+               || ASM_OPERANDS_LABEL_VEC (SET_SRC (set))
+                  != ASM_OPERANDS_LABEL_VEC (SET_SRC (set0)))
+             {
+               rtx newsrc = shallow_copy_rtx (SET_SRC (set));
+               ASM_OPERANDS_INPUT_VEC (newsrc)
+                 = ASM_OPERANDS_INPUT_VEC (SET_SRC (set0));
+               ASM_OPERANDS_INPUT_CONSTRAINT_VEC (newsrc)
+                 = ASM_OPERANDS_INPUT_CONSTRAINT_VEC (SET_SRC (set0));
+               ASM_OPERANDS_LABEL_VEC (newsrc)
+                 = ASM_OPERANDS_LABEL_VEC (SET_SRC (set0));
+               validate_change (NULL_RTX, &SET_SRC (set), newsrc, true);
+             }
+         }
+    }
+  else
+    note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
+
+  /* For read-only MEMs containing some constant, prefer those
+     constants.  */
+  set = single_set (insn);
+  if (set && MEM_P (SET_SRC (set)) && MEM_READONLY_P (SET_SRC (set)))
+    {
+      rtx note = find_reg_equal_equiv_note (insn);
+
+      if (note && CONSTANT_P (XEXP (note, 0)))
+       validate_change (NULL_RTX, &SET_SRC (set), XEXP (note, 0), true);
+    }
+
+  if (amd.side_effects)
+    {
+      rtx *pat, new_pat, s;
+      int i, oldn, newn;
 
-  return replace_equiv_address_nv (mem, addr);
+      pat = &PATTERN (insn);
+      if (GET_CODE (*pat) == COND_EXEC)
+       pat = &COND_EXEC_CODE (*pat);
+      if (GET_CODE (*pat) == PARALLEL)
+       oldn = XVECLEN (*pat, 0);
+      else
+       oldn = 1;
+      for (s = amd.side_effects, newn = 0; s; newn++)
+       s = XEXP (s, 1);
+      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);
+      validate_change (NULL_RTX, pat, new_pat, true);
+    }
 }
 
 /* Return true if a decl_or_value DV is a DECL or NULL.  */
@@ -743,9 +1197,7 @@ dv_is_value_p (decl_or_value dv)
 static inline tree
 dv_as_decl (decl_or_value dv)
 {
-#ifdef ENABLE_CHECKING
-  gcc_assert (dv_is_decl_p (dv));
-#endif
+  gcc_checking_assert (dv_is_decl_p (dv));
   return (tree) dv;
 }
 
@@ -753,12 +1205,25 @@ dv_as_decl (decl_or_value dv)
 static inline rtx
 dv_as_value (decl_or_value dv)
 {
-#ifdef ENABLE_CHECKING
-  gcc_assert (dv_is_value_p (dv));
-#endif
+  gcc_checking_assert (dv_is_value_p (dv));
   return (rtx)dv;
 }
 
+/* Return the DEBUG_EXPR of a DEBUG_EXPR_DECL or the VALUE in DV.  */
+static inline rtx
+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 the opaque pointer in the decl_or_value.  */
 static inline void *
 dv_as_opaque (decl_or_value dv)
@@ -766,33 +1231,36 @@ 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
+/* 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_t
 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 ONEPART_DEXPR;
 
-  return (target_for_debug_bind (decl) != NULL_TREE);
+  if (target_for_debug_bind (decl) != NULL_TREE)
+    return ONEPART_VDECL;
+
+  return NOT_ONEPART;
 }
 
-/* Return the variable pool to be used for dv, depending on whether it
-   can have multiple parts or not.  */
+/* Return the variable pool to be used for a dv of type ONEPART.  */
 static inline alloc_pool
-dv_pool (decl_or_value dv)
+onepart_pool (onepart_enum_t onepart)
 {
-  return dv_onepart_p (dv) ? valvar_pool : var_pool;
+  return onepart ? valvar_pool : var_pool;
 }
 
 /* Build a decl_or_value out of a decl.  */
@@ -801,9 +1269,7 @@ dv_from_decl (tree decl)
 {
   decl_or_value dv;
   dv = decl;
-#ifdef ENABLE_CHECKING
-  gcc_assert (dv_is_decl_p (dv));
-#endif
+  gcc_checking_assert (dv_is_decl_p (dv));
   return dv;
 }
 
@@ -813,12 +1279,45 @@ dv_from_value (rtx value)
 {
   decl_or_value dv;
   dv = value;
-#ifdef ENABLE_CHECKING
-  gcc_assert (dv_is_value_p (dv));
-#endif
+  gcc_checking_assert (dv_is_value_p (dv));
+  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
+debug_dv (decl_or_value dv)
+{
+  if (dv_is_value_p (dv))
+    debug_rtx (dv_as_value (dv));
+  else
+    debug_generic_stmt (dv_as_decl (dv));
+}
+
 typedef unsigned int dvuid;
 
 /* Return the uid of DV.  */
@@ -870,6 +1369,8 @@ variable_htab_eq (const void *x, 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).  */
 
 static void
@@ -879,7 +1380,7 @@ variable_htab_free (void *elem)
   variable var = (variable) elem;
   location_chain node, next;
 
-  gcc_assert (var->refcount > 0);
+  gcc_checking_assert (var->refcount > 0);
 
   var->refcount--;
   if (var->refcount > 0)
@@ -894,29 +1395,18 @@ variable_htab_free (void *elem)
        }
       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);
+    }
+  pool_free (onepart_pool (var->onepart), var);
 }
 
 /* Initialize the set (array) SET of attrs to empty lists.  */
@@ -1041,6 +1531,16 @@ shared_hash_htab (shared_hash vars)
   return vars->htab;
 }
 
+/* Return true if VAR is shared, or maybe because VARS is shared.  */
+
+static inline bool
+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)
+         || shared_hash_shared (vars));
+}
+
 /* Copy variables into a new hash table.  */
 
 static shared_hash
@@ -1072,7 +1572,7 @@ shared_hash_copy (shared_hash vars)
 static void
 shared_hash_destroy (shared_hash vars)
 {
-  gcc_assert (vars->refcount > 0);
+  gcc_checking_assert (vars->refcount > 0);
   if (--vars->refcount == 0)
     {
       htab_delete (vars->htab);
@@ -1175,11 +1675,13 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
   variable new_var;
   int i;
 
-  new_var = (variable) pool_alloc (dv_pool (var->dv));
+  new_var = (variable) pool_alloc (onepart_pool (var->onepart));
   new_var->dv = var->dv;
   new_var->refcount = 1;
   var->refcount--;
   new_var->n_var_parts = var->n_var_parts;
+  new_var->onepart = var->onepart;
+  new_var->in_changed_variables = false;
 
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
@@ -1189,7 +1691,18 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
       location_chain node;
       location_chain *nextp;
 
-      new_var->var_part[i].offset = var->var_part[i].offset;
+      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)
        {
@@ -1211,12 +1724,7 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
          nextp = &new_lc->next;
        }
 
-      /* We are at the basic block boundary when copying variable description
-        so set the CUR_LOC to be the first element of the chain.  */
-      if (new_var->var_part[i].loc_chain)
-       new_var->var_part[i].cur_loc = new_var->var_part[i].loc_chain->loc;
-      else
-       new_var->var_part[i].cur_loc = NULL;
+      new_var->var_part[i].cur_loc = var->var_part[i].cur_loc;
     }
 
   dst_can_be_shared = false;
@@ -1225,37 +1733,37 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
   else if (set->traversed_vars && set->vars != set->traversed_vars)
     slot = shared_hash_find_slot_noinsert (set->vars, var->dv);
   *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);
+      gcc_assert (*cslot == (void *) var);
+      var->in_changed_variables = false;
+      variable_htab_free (var);
+      *cslot = new_var;
+      new_var->in_changed_variables = true;
+    }
   return slot;
 }
 
-/* Add a variable from *SLOT to hash table DATA and increase its reference
-   count.  */
+/* Copy all variables from hash table SRC to hash table DST.  */
 
-static int
-vars_copy_1 (void **slot, void *data)
+static void
+vars_copy (htab_t dst, htab_t src)
 {
-  htab_t dst = (htab_t) data;
-  variable src;
-  void **dstp;
-
-  src = (variable) *slot;
-  src->refcount++;
+  htab_iterator hi;
+  variable var;
 
-  dstp = htab_find_slot_with_hash (dst, src->dv,
-                                  dv_htab_hash (src->dv),
-                                  INSERT);
-  *dstp = src;
-
-  /* Continue traversing the hash table.  */
-  return 1;
-}
-
-/* Copy all variables from hash table SRC to hash table DST.  */
-
-static void
-vars_copy (htab_t dst, htab_t src)
-{
-  htab_traverse_noresize (src, vars_copy_1, dst);
+  FOR_EACH_HTAB_ELEMENT (src, var, variable, hi)
+    {
+      void **dstp;
+      var->refcount++;
+      dstp = htab_find_slot_with_hash (dst, var->dv,
+                                      dv_htab_hash (var->dv),
+                                      INSERT);
+      *dstp = var;
+    }
 }
 
 /* Map a decl to its main debug decl.  */
@@ -1264,9 +1772,12 @@ static inline tree
 var_debug_decl (tree decl)
 {
   if (decl && DECL_P (decl)
-      && DECL_DEBUG_EXPR_IS_FROM (decl) && DECL_DEBUG_EXPR (decl)
-      && DECL_P (DECL_DEBUG_EXPR (decl)))
-    decl = DECL_DEBUG_EXPR (decl);
+      && DECL_DEBUG_EXPR_IS_FROM (decl))
+    {
+      tree debugdecl = DECL_DEBUG_EXPR (decl);
+      if (debugdecl && DECL_P (debugdecl))
+       decl = debugdecl;
+    }
 
   return decl;
 }
@@ -1496,6 +2007,70 @@ var_mem_delete (dataflow_set *set, rtx loc, bool clobber)
   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 (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.  */
@@ -1509,10 +2084,10 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
 
   if (dump_file)
     {
-      fprintf (dump_file, "%i: ", INSN_UID (insn));
-      print_inline_rtx (dump_file, val, 0);
-      fprintf (dump_file, " stored in ");
+      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);
       if (v->locs)
        {
          struct elt_loc_list *l;
@@ -1525,19 +2100,9 @@ val_store (dataflow_set *set, rtx val, rtx loc, rtx insn, bool modified)
       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);
 }
 
 /* Reset this node, detaching all its equivalences.  Return the slot
@@ -1602,13 +2167,6 @@ val_reset (dataflow_set *set, decl_or_value dv)
     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
@@ -1634,6 +2192,8 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
 
   val_reset (set, dv);
 
+  gcc_checking_assert (!unsuitable_loc (loc));
+
   if (REG_P (loc))
     {
       attrs node, found = NULL;
@@ -1657,18 +2217,13 @@ val_resolve (dataflow_set *set, rtx val, rtx loc, rtx insn)
 
       /* 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);
+      if (found)
+       return;
     }
-  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);
+  /* ??? Attempt to find and merge equivalent MEMs or other
+     expressions too.  */
+
+  val_bind (set, val, loc, false);
 }
 
 /* Initialize dataflow set SET to be empty.
@@ -1757,14 +2312,12 @@ variable_union_info_cmp_pos (const void *n1, const void *n2)
    we keep the newest locations in the beginning.  */
 
 static int
-variable_union (void **slot, void *data)
+variable_union (variable src, dataflow_set *set)
 {
-  variable src, dst;
+  variable dst;
   void **dstp;
-  dataflow_set *set = (dataflow_set *) data;
   int i, j, k;
 
-  src = (variable) *slot;
   dstp = shared_hash_find_slot (set->vars, src->dv);
   if (!dstp || !*dstp)
     {
@@ -1776,23 +2329,6 @@ variable_union (void **slot, void *data)
 
       *dstp = src;
 
-      /* If CUR_LOC of some variable part is not the first element of
-        the location chain we are going to change it so we have to make
-        a copy of the variable.  */
-      for (k = 0; k < src->n_var_parts; k++)
-       {
-         gcc_assert (!src->var_part[k].loc_chain
-                     == !src->var_part[k].cur_loc);
-         if (src->var_part[k].loc_chain)
-           {
-             gcc_assert (src->var_part[k].cur_loc);
-             if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
-               break;
-           }
-       }
-      if (k < src->n_var_parts)
-       dstp = unshare_variable (set, dstp, src, VAR_INIT_STATUS_UNKNOWN);
-
       /* Continue traversing the hash table.  */
       return 1;
     }
@@ -1800,15 +2336,16 @@ variable_union (void **slot, void *data)
     dst = (variable) *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))
+  if (src->onepart)
     {
       location_chain *nodep, dnode, snode;
 
-      gcc_assert (src->n_var_parts == 1);
-      gcc_assert (dst->n_var_parts == 1);
+      gcc_assert (src->n_var_parts == 1
+                 && dst->n_var_parts == 1);
 
       snode = src->var_part[0].loc_chain;
       gcc_assert (snode);
@@ -1826,7 +2363,7 @@ variable_union (void **slot, void *data)
            {
              location_chain nnode;
 
-             if (dst->refcount != 1 || shared_hash_shared (set->vars))
+             if (shared_var_p (dst, set->vars))
                {
                  dstp = unshare_variable (set, dstp, dst,
                                           VAR_INIT_STATUS_INITIALIZED);
@@ -1844,10 +2381,8 @@ variable_union (void **slot, void *data)
              nnode->next = dnode;
              dnode = nnode;
            }
-#ifdef ENABLE_CHECKING
          else if (r == 0)
-           gcc_assert (rtx_equal_p (dnode->loc, snode->loc));
-#endif
+           gcc_checking_assert (rtx_equal_p (dnode->loc, snode->loc));
 
          if (r >= 0)
            snode = snode->next;
@@ -1856,21 +2391,21 @@ variable_union (void **slot, void *data)
          dnode = *nodep;
        }
 
-      dst->var_part[0].cur_loc = dst->var_part[0].loc_chain->loc;
-
       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++;
@@ -1880,10 +2415,9 @@ variable_union (void **slot, void *data)
 
   /* 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->refcount > 1 || shared_hash_shared (set->vars))
-      && dst->n_var_parts != k)
+  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;
@@ -1898,7 +2432,7 @@ variable_union (void **slot, void *data)
       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
@@ -1910,7 +2444,7 @@ variable_union (void **slot, void *data)
          /* If DST is shared compare the location chains.
             If they are different we will modify the chain in DST with
             high probability so make a copy of DST.  */
-         if (dst->refcount > 1 || shared_hash_shared (set->vars))
+         if (shared_var_p (dst, set->vars))
            {
              for (node = src->var_part[i].loc_chain,
                   node2 = dst->var_part[j].loc_chain; node && node2;
@@ -1946,7 +2480,7 @@ variable_union (void **slot, void *data)
              /* The most common case, much simpler, no qsort is needed.  */
              location_chain dstnode = dst->var_part[j].loc_chain;
              dst->var_part[k].loc_chain = dstnode;
-             dst->var_part[k].offset = dst->var_part[j].offset;
+             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)
@@ -2084,20 +2618,20 @@ variable_union (void **slot, void *data)
                  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;
@@ -2121,16 +2655,10 @@ variable_union (void **slot, void *data)
              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--;
        }
-
-      /* We are at the basic block boundary when computing union
-        so set the CUR_LOC to be the first element of the chain.  */
-      if (dst->var_part[k].loc_chain)
-       dst->var_part[k].cur_loc = dst->var_part[k].loc_chain->loc;
-      else
-       dst->var_part[k].cur_loc = NULL;
+      dst->var_part[k].cur_loc = NULL;
     }
 
   if (flag_var_tracking_uninit)
@@ -2150,39 +2678,6 @@ variable_union (void **slot, void *data)
   return 1;
 }
 
-/* Like variable_union, but only used when doing dataflow_set_union
-   into an empty hashtab.  To allow sharing, dst is initially shared
-   with src (so all variables are "copied" from src to dst hashtab),
-   so only unshare_variable for variables that need canonicalization
-   are needed.  */
-
-static int
-variable_canonicalize (void **slot, void *data)
-{
-  variable src;
-  dataflow_set *set = (dataflow_set *) data;
-  int k;
-
-  src = *(variable *) slot;
-
-  /* If CUR_LOC of some variable part is not the first element of
-     the location chain we are going to change it so we have to make
-     a copy of the variable.  */
-  for (k = 0; k < src->n_var_parts; k++)
-    {
-      gcc_assert (!src->var_part[k].loc_chain == !src->var_part[k].cur_loc);
-      if (src->var_part[k].loc_chain)
-       {
-         gcc_assert (src->var_part[k].cur_loc);
-         if (src->var_part[k].cur_loc != src->var_part[k].loc_chain->loc)
-           break;
-       }
-    }
-  if (k < src->n_var_parts)
-    slot = unshare_variable (set, slot, src, VAR_INIT_STATUS_UNKNOWN);
-  return 1;
-}
-
 /* Compute union of dataflow sets SRC and DST and store it to DST.  */
 
 static void
@@ -2197,36 +2692,60 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
     {
       shared_hash_destroy (dst->vars);
       dst->vars = shared_hash_copy (src->vars);
-      dst->traversed_vars = dst->vars;
-      htab_traverse (shared_hash_htab (dst->vars), variable_canonicalize, dst);
-      dst->traversed_vars = NULL;
     }
   else
-    htab_traverse (shared_hash_htab (src->vars), variable_union, dst);
+    {
+      htab_iterator hi;
+      variable var;
+
+      FOR_EACH_HTAB_ELEMENT (shared_hash_htab (src->vars), var, variable, hi)
+       variable_union (var, dst);
+    }
 }
 
 /* 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;
+  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 is present in changed_variables hash table.  */
+/* Return true if DV needs to have its cur_loc recomputed.  */
 
 static inline bool
 dv_changed_p (decl_or_value dv)
@@ -2236,70 +2755,76 @@ dv_changed_p (decl_or_value dv)
          : DECL_CHANGED (dv_as_decl (dv)));
 }
 
-/* Vector of VALUEs that should have VALUE_RECURSED_INTO bit cleared
-   at the end of find_loc_in_1pdv.  Not a static variable in find_loc_in_1pdv
-   to avoid constant allocation/freeing of it.  */
-static VEC(rtx, heap) *values_to_unmark;
-
-/* Helper function for find_loc_in_1pdv.
-   Return a location list node whose loc is rtx_equal to LOC, in the
+/* Return a location list node whose loc is rtx_equal to LOC, in the
    location list of a one-part variable or value VAR, or in that of
-   any values recursively mentioned in the location lists.  */
+   any values recursively mentioned in the location lists.  VARS must
+   be in star-canonical form.  */
 
 static location_chain
-find_loc_in_1pdv_1 (rtx loc, variable var, htab_t vars)
+find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
 {
   location_chain node;
+  enum rtx_code loc_code;
 
   if (!var)
     return NULL;
 
-  gcc_assert (dv_onepart_p (var->dv));
+  gcc_checking_assert (var->onepart);
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_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)
-    if (rtx_equal_p (loc, node->loc))
-      return node;
-    else if (GET_CODE (node->loc) == VALUE
-            && !VALUE_RECURSED_INTO (node->loc))
-      {
-       decl_or_value dv = dv_from_value (node->loc);
-       variable var = (variable)
-                      htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+    {
+      decl_or_value dv;
+      variable rvar;
 
-       if (var)
-         {
-           location_chain where;
-           VALUE_RECURSED_INTO (node->loc) = true;
-           VEC_safe_push (rtx, heap, values_to_unmark, node->loc);
-           if ((where = find_loc_in_1pdv_1 (loc, var, vars)))
-             return where;
-         }
-      }
+      if (GET_CODE (node->loc) != loc_code)
+       {
+         if (GET_CODE (node->loc) != VALUE)
+           continue;
+       }
+      else if (loc == node->loc)
+       return node;
+      else if (loc_code != VALUE)
+       {
+         if (rtx_equal_p (loc, node->loc))
+           return node;
+         continue;
+       }
 
-  return NULL;
-}
+      /* Since we're in star-canonical form, we don't need to visit
+        non-canonical nodes: one-part variables and non-canonical
+        values would only point back to the canonical node.  */
+      if (dv_is_value_p (var->dv)
+         && !canon_value_cmp (node->loc, dv_as_value (var->dv)))
+       {
+         /* Skip all subsequent VALUEs.  */
+         while (node->next && GET_CODE (node->next->loc) == VALUE)
+           {
+             node = node->next;
+             gcc_checking_assert (!canon_value_cmp (node->loc,
+                                                    dv_as_value (var->dv)));
+             if (loc == node->loc)
+               return node;
+           }
+         continue;
+       }
 
-/* Return a location list node whose loc is rtx_equal to LOC, in the
-   location list of a one-part variable or value VAR, or in that of
-   any values recursively mentioned in the location lists.  */
+      gcc_checking_assert (node == var->var_part[0].loc_chain);
+      gcc_checking_assert (!node->next);
 
-static location_chain
-find_loc_in_1pdv (rtx loc, variable var, htab_t vars)
-{
-  location_chain ret;
-  unsigned int i;
-  rtx value;
+      dv = dv_from_value (node->loc);
+      rvar = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+      return find_loc_in_1pdv (loc, rvar, vars);
+    }
 
-  ret = find_loc_in_1pdv_1 (loc, var, vars);
-  for (i = 0; VEC_iterate (rtx, values_to_unmark, i, value); i++)
-    VALUE_RECURSED_INTO (value) = false;
-  VEC_truncate (rtx, values_to_unmark, 0);
-  return ret;
+  /* ??? Gotta look in cselib_val locations too.  */
+
+  return NULL;
 }
 
 /* Hash table iteration argument passed to variable_merge.  */
@@ -2343,7 +2868,7 @@ insert_into_intersection (location_chain *nodep, rtx loc,
   *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.  */
@@ -2356,6 +2881,28 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
   dataflow_set *s2set = dsm->src;
   location_chain found;
 
+  if (s2var)
+    {
+      location_chain s2node;
+
+      gcc_checking_assert (s2var->onepart);
+
+      if (s2var->n_var_parts)
+       {
+         s2node = s2var->var_part[0].loc_chain;
+
+         for (; s1node && s2node;
+              s1node = s1node->next, s2node = s2node->next)
+           if (s1node->loc != s2node->loc)
+             break;
+           else if (s1node->loc == val)
+             continue;
+           else
+             insert_into_intersection (dest, s1node->loc,
+                                       MIN (s1node->init, s2node->init));
+       }
+    }
+
   for (; s1node; s1node = s1node->next)
     {
       if (s1node->loc == val)
@@ -2387,6 +2934,8 @@ intersect_loc_chains (rtx val, location_chain *dest, struct dfset_merge *dsm,
            }
        }
 
+      /* ??? gotta look in cselib_val locations too.  */
+
       /* ??? if the location is equivalent to any location in src,
         searched recursively
 
@@ -2463,7 +3012,10 @@ loc_cmp (rtx x, rtx y)
     {
       if (GET_CODE (y) != VALUE)
        return -1;
-      gcc_assert (GET_MODE (x) == GET_MODE (y));
+      /* Don't assert the modes are the same, that is true only
+        when not recursing.  (subreg:QI (value:SI 1:1) 0)
+        and (subreg:QI (value:DI 2:2) 0) can be compared,
+        even when the modes are different.  */
       if (canon_value_cmp (x, y))
        return -1;
       else
@@ -2473,6 +3025,18 @@ loc_cmp (rtx x, rtx y)
   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))
@@ -2482,6 +3046,16 @@ loc_cmp (rtx x, rtx y)
 
   gcc_assert (GET_MODE (x) == GET_MODE (y));
 
+  if (GET_CODE (x) == DEBUG_EXPR)
+    {
+      if (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+         < DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)))
+       return -1;
+      gcc_checking_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+                          > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
+      return 1;
+    }
+
   fmt = GET_RTX_FORMAT (code);
   for (i = 0; i < GET_RTX_LENGTH (code); i++)
     switch (fmt[i])
@@ -2557,143 +3131,6 @@ loc_cmp (rtx x, rtx y)
   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)
-{
-  if (GET_CODE (*loc) == VALUE && (void *) *loc != dvp)
-    {
-      decl_or_value dv = (decl_or_value) dvp;
-      decl_or_value ldv = dv_from_value (*loc);
-      value_chain vc, nvc;
-      void **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)
-    {
-      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.  */
-
-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; l = l->next)
-    for_each_rtx (&l->loc, add_value_chain, dv_as_opaque (dv));
-}
-
-/* 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)
-{
-  if (GET_CODE (*loc) == VALUE && (void *) *loc != dvp)
-    {
-      decl_or_value dv = (decl_or_value) dvp;
-      decl_or_value ldv = dv_from_value (*loc);
-      value_chain vc, dvc = NULL;
-      void **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))
-         {
-           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 ();
-    }
-  return 0;
-}
-
-/* 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)
-    {
-      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 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));
-}
-
 #if ENABLE_CHECKING
 /* Check the order of entries in one-part variables.   */
 
@@ -2701,10 +3138,16 @@ 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;
 
-  if (!dv_onepart_p (dv))
+#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->in_changed_variables);
+#endif
+
+  if (!var->onepart)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -2738,7 +3181,7 @@ canonicalize_values_mark (void **slot, void *data)
   if (!dv_is_value_p (dv))
     return 1;
 
-  gcc_assert (var->n_var_parts == 1);
+  gcc_checking_assert (var->n_var_parts == 1);
 
   val = dv_as_value (dv);
 
@@ -2752,8 +3195,8 @@ canonicalize_values_mark (void **slot, void *data)
            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);
+           set_slot_part (set, val, oslot, odv, 0,
+                          node->init, NULL_RTX);
 
            VALUE_RECURSED_INTO (node->loc) = true;
          }
@@ -2778,10 +3221,10 @@ canonicalize_values_star (void **slot, void *data)
   bool has_value;
   bool has_marks;
 
-  if (!dv_onepart_p (dv))
+  if (!var->onepart)
     return 1;
 
-  gcc_assert (var->n_var_parts == 1);
+  gcc_checking_assert (var->n_var_parts == 1);
 
   if (dv_is_value_p (dv))
     {
@@ -2964,15 +3407,15 @@ canonicalize_values_star (void **slot, void *data)
       }
 
   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;
-  gcc_assert (var->n_var_parts && var->var_part[0].loc_chain->loc == cval
-             && var->var_part[0].loc_chain->next == NULL);
+  gcc_checking_assert (var->n_var_parts && var->var_part[0].loc_chain->loc == cval
+                      && var->var_part[0].loc_chain->next == NULL);
 
   if (VALUE_RECURSED_INTO (cval))
     goto restart_with_cval;
@@ -2980,36 +3423,94 @@ canonicalize_values_star (void **slot, void *data)
   return 1;
 }
 
-/* Combine variable or value in *S1SLOT (in DSM->cur) with the
-   corresponding entry in DSM->src.  Multi-part variables are combined
-   with variable_union, whereas onepart dvs are combined with
-   intersection.  */
+/* Bind one-part variables to the canonical value in an equivalence
+   set.  Not doing this causes dataflow convergence failure in rare
+   circumstances, see PR42873.  Unfortunately we can't do this
+   efficiently as part of canonicalize_values_star, since we may not
+   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
-variable_merge_over_cur (void **s1slot, void *data)
+canonicalize_vars_star (void **slot, void *data)
 {
-  struct dfset_merge *dsm = (struct dfset_merge *)data;
-  dataflow_set *dst = dsm->dst;
-  void **dstslot;
-  variable s1var = (variable) *s1slot;
-  variable s2var, dvar = NULL;
-  decl_or_value dv = s1var->dv;
-  bool onepart = dv_onepart_p (dv);
-  rtx val;
-  hashval_t dvhash;
+  dataflow_set *set = (dataflow_set *)data;
+  variable var = (variable) *slot;
+  decl_or_value dv = var->dv;
+  location_chain node;
+  rtx cval;
+  decl_or_value cdv;
+  void **cslot;
+  variable cvar;
+  location_chain cnode;
+
+  if (!var->onepart || var->onepart == ONEPART_VALUE)
+    return 1;
+
+  gcc_assert (var->n_var_parts == 1);
+
+  node = var->var_part[0].loc_chain;
+
+  if (GET_CODE (node->loc) != VALUE)
+    return 1;
+
+  gcc_assert (!node->next);
+  cval = node->loc;
+
+  /* Push values to the canonical one.  */
+  cdv = dv_from_value (cval);
+  cslot = shared_hash_find_slot_noinsert (set->vars, cdv);
+  if (!cslot)
+    return 1;
+  cvar = (variable)*cslot;
+  gcc_assert (cvar->n_var_parts == 1);
+
+  cnode = cvar->var_part[0].loc_chain;
+
+  /* CVAL is canonical if its value list contains non-VALUEs or VALUEs
+     that are not “more canonical” than it.  */
+  if (GET_CODE (cnode->loc) != VALUE
+      || !canon_value_cmp (cnode->loc, cval))
+    return 1;
+
+  /* CVAL was found to be non-canonical.  Change the variable to point
+     to the canonical VALUE.  */
+  gcc_assert (!cnode->next);
+  cval = cnode->loc;
+
+  slot = set_slot_part (set, cval, slot, dv, 0,
+                       node->init, node->set_src);
+  clobber_slot_part (set, cval, slot, 0, node->set_src);
+
+  return 1;
+}
+
+/* Combine variable or value in *S1SLOT (in DSM->cur) with the
+   corresponding entry in DSM->src.  Multi-part variables are combined
+   with variable_union, whereas onepart dvs are combined with
+   intersection.  */
+
+static int
+variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
+{
+  dataflow_set *dst = dsm->dst;
+  void **dstslot;
+  variable s2var, dvar = NULL;
+  decl_or_value dv = s1var->dv;
+  onepart_enum_t onepart = s1var->onepart;
+  rtx val;
+  hashval_t dvhash;
   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,
      it's always union.  */
-  gcc_assert (s1var->n_var_parts);
-  gcc_assert (s1var->var_part[0].loc_chain);
+  gcc_checking_assert (s1var->n_var_parts
+                      && s1var->var_part[0].loc_chain);
 
   if (!onepart)
-    return variable_union (s1slot, dst);
+    return variable_union (s1var, dst);
 
-  gcc_assert (s1var->n_var_parts == 1);
-  gcc_assert (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))
@@ -3025,17 +3526,17 @@ variable_merge_over_cur (void **s1slot, void *data)
     }
 
   dsm->src_onepart_cnt--;
-  gcc_assert (s2var->var_part[0].loc_chain);
-  gcc_assert (s2var->n_var_parts == 1);
-  gcc_assert (s2var->var_part[0].offset == 0);
+  gcc_assert (s2var->var_part[0].loc_chain
+             && s2var->onepart == onepart
+             && s2var->n_var_parts == 1);
 
   dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
   if (dstslot)
     {
       dvar = (variable)*dstslot;
-      gcc_assert (dvar->refcount == 1);
-      gcc_assert (dvar->n_var_parts == 1);
-      gcc_assert (dvar->var_part[0].offset == 0);
+      gcc_assert (dvar->refcount == 1
+                 && dvar->onepart == onepart
+                 && dvar->n_var_parts == 1);
       nodep = &dvar->var_part[0].loc_chain;
     }
   else
@@ -3062,13 +3563,18 @@ variable_merge_over_cur (void **s1slot, void *data)
        {
          if (node)
            {
-             dvar = (variable) pool_alloc (dv_pool (dv));
+             dvar = (variable) pool_alloc (onepart_pool (onepart));
              dvar->dv = dv;
              dvar->refcount = 1;
              dvar->n_var_parts = 1;
-             dvar->var_part[0].offset = 0;
+             dvar->onepart = onepart;
+             dvar->in_changed_variables = false;
              dvar->var_part[0].loc_chain = node;
-             dvar->var_part[0].cur_loc = node->loc;
+             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,
@@ -3145,10 +3651,9 @@ variable_merge_over_cur (void **s1slot, void *data)
       dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
       gcc_assert (*dstslot == dvar);
       canonicalize_values_star (dstslot, dst);
-#ifdef ENABLE_CHECKING
-      gcc_assert (dstslot
-                 == shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash));
-#endif
+      gcc_checking_assert (dstslot
+                          == shared_hash_find_slot_noinsert_1 (dst->vars,
+                                                               dv, dvhash));
       dvar = (variable)*dstslot;
     }
   else
@@ -3194,13 +3699,16 @@ variable_merge_over_cur (void **s1slot, void *data)
                                                          INSERT);
                  if (!*slot)
                    {
-                     variable var = (variable) pool_alloc (dv_pool (dv));
+                     variable var = (variable) pool_alloc (onepart_pool
+                                                           (ONEPART_VALUE));
                      var->dv = dv;
                      var->refcount = 1;
                      var->n_var_parts = 1;
-                     var->var_part[0].offset = 0;
+                     var->onepart = ONEPART_VALUE;
+                     var->in_changed_variables = false;
                      var->var_part[0].loc_chain = NULL;
                      var->var_part[0].cur_loc = NULL;
+                     VAR_LOC_1PAUX (var) = NULL;
                      *slot = var;
                    }
 
@@ -3211,11 +3719,9 @@ variable_merge_over_cur (void **s1slot, void *data)
          dstslot = shared_hash_find_slot_noinsert_1 (dst->vars, dv, dvhash);
          gcc_assert (*dstslot == dvar);
          canonicalize_values_star (dstslot, dst);
-#ifdef ENABLE_CHECKING
-         gcc_assert (dstslot
-                     == shared_hash_find_slot_noinsert_1 (dst->vars,
-                                                          dv, dvhash));
-#endif
+         gcc_checking_assert (dstslot
+                              == shared_hash_find_slot_noinsert_1 (dst->vars,
+                                                                   dv, dvhash));
          dvar = (variable)*dstslot;
        }
     }
@@ -3234,80 +3740,76 @@ variable_merge_over_cur (void **s1slot, void *data)
       dst_can_be_shared = false;
     }
   else
-    {
-      if (dvar->refcount == 1)
-        dvar->var_part[0].cur_loc = dvar->var_part[0].loc_chain->loc;
-      dst_can_be_shared = false;
-    }
+    dst_can_be_shared = false;
 
   return 1;
 }
 
-/* Combine variable in *S1SLOT (in DSM->src) with the corresponding
-   entry in DSM->src.  Only multi-part variables are combined, using
-   variable_union.  onepart dvs were already combined with
-   intersection in variable_merge_over_cur().  */
+/* Copy s2slot (in DSM->src) to DSM->dst if the variable is a
+   multi-part variable.  Unions of multi-part variables and
+   intersections of one-part ones will be handled in
+   variable_merge_over_cur().  */
 
 static int
-variable_merge_over_src (void **s2slot, void *data)
+variable_merge_over_src (variable s2var, struct dfset_merge *dsm)
 {
-  struct dfset_merge *dsm = (struct dfset_merge *)data;
   dataflow_set *dst = dsm->dst;
-  variable s2var = (variable) *s2slot;
   decl_or_value dv = s2var->dv;
-  bool onepart = dv_onepart_p (dv);
 
-  if (!onepart)
+  if (!s2var->onepart)
     {
       void **dstp = shared_hash_find_slot (dst->vars, dv);
       *dstp = s2var;
       s2var->refcount++;
-      return variable_canonicalize (dstp, dst);
+      return 1;
     }
 
   dsm->src_onepart_cnt++;
   return 1;
 }
 
-/* Combine dataflow set information from SRC into DST, using PDST
+/* Combine dataflow set information from SRC2 into DST, using PDST
    to carry over information across passes.  */
 
 static void
-dataflow_set_merge (dataflow_set *dst, dataflow_set *src)
+dataflow_set_merge (dataflow_set *dst, dataflow_set *src2)
 {
-  dataflow_set src2 = *dst;
+  dataflow_set cur = *dst;
+  dataflow_set *src1 = &cur;
   struct dfset_merge dsm;
   int i;
-  size_t src_elems, dst_elems;
+  size_t src1_elems, src2_elems;
+  htab_iterator hi;
+  variable var;
 
-  src_elems = htab_elements (shared_hash_htab (src->vars));
-  dst_elems = htab_elements (shared_hash_htab (src2.vars));
+  src1_elems = htab_elements (shared_hash_htab (src1->vars));
+  src2_elems = htab_elements (shared_hash_htab (src2->vars));
   dataflow_set_init (dst);
-  dst->stack_adjust = src2.stack_adjust;
+  dst->stack_adjust = cur.stack_adjust;
   shared_hash_destroy (dst->vars);
   dst->vars = (shared_hash) pool_alloc (shared_hash_pool);
   dst->vars->refcount = 1;
   dst->vars->htab
-    = htab_create (MAX (src_elems, dst_elems), variable_htab_hash,
+    = htab_create (MAX (src1_elems, src2_elems), variable_htab_hash,
                   variable_htab_eq, variable_htab_free);
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    attrs_list_mpdv_union (&dst->regs[i], src->regs[i], src2.regs[i]);
+    attrs_list_mpdv_union (&dst->regs[i], src1->regs[i], src2->regs[i]);
 
   dsm.dst = dst;
-  dsm.src = &src2;
-  dsm.cur = src;
+  dsm.src = src2;
+  dsm.cur = src1;
   dsm.src_onepart_cnt = 0;
 
-  htab_traverse (shared_hash_htab (dsm.src->vars), variable_merge_over_src,
-                &dsm);
-  htab_traverse (shared_hash_htab (dsm.cur->vars), variable_merge_over_cur,
-                &dsm);
+  FOR_EACH_HTAB_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)
+    variable_merge_over_cur (var, &dsm);
 
   if (dsm.src_onepart_cnt)
     dst_can_be_shared = false;
 
-  dataflow_set_destroy (&src2);
+  dataflow_set_destroy (src1);
 }
 
 /* Mark register equivalences.  */
@@ -3322,6 +3824,11 @@ dataflow_set_equiv_regs (dataflow_set *set)
     {
       rtx canon[NUM_MACHINE_MODES];
 
+      /* If the list is empty or one entry, no need to canonicalize
+        anything.  */
+      if (set->regs[i] == NULL || set->regs[i]->next == NULL)
+       continue;
+
       memset (canon, 0, sizeof (canon));
 
       for (list = set->regs[i]; list; list = list->next)
@@ -3394,7 +3901,7 @@ remove_duplicate_values (variable var)
 {
   location_chain node, *nodep;
 
-  gcc_assert (dv_onepart_p (var->dv));
+  gcc_assert (var->onepart);
   gcc_assert (var->n_var_parts == 1);
   gcc_assert (var->refcount == 1);
 
@@ -3445,7 +3952,7 @@ variable_post_merge_new_vals (void **slot, void *info)
   variable var = (variable)*slot;
   location_chain node;
 
-  if (!dv_onepart_p (var->dv) || !var->n_var_parts)
+  if (!var->onepart || !var->n_var_parts)
     return 1;
 
   gcc_assert (var->n_var_parts == 1);
@@ -3515,8 +4022,8 @@ variable_post_merge_new_vals (void **slot, void *info)
                       att; att = att->next)
                    if (GET_MODE (att->loc) == GET_MODE (node->loc))
                      {
-                       gcc_assert (att->offset == 0);
-                       gcc_assert (dv_is_value_p (att->dv));
+                       gcc_assert (att->offset == 0
+                                   && dv_is_value_p (att->dv));
                        val_reset (set, att->dv);
                        break;
                      }
@@ -3533,8 +4040,10 @@ variable_post_merge_new_vals (void **slot, void *info)
                         subsequent rounds.  */
                      cselib_val *v;
                      gcc_assert (!cselib_lookup (node->loc,
-                                                 GET_MODE (node->loc), 0));
-                     v = cselib_lookup (node->loc, GET_MODE (node->loc), 1);
+                                                 GET_MODE (node->loc), 0,
+                                                 VOIDmode));
+                     v = cselib_lookup (node->loc, GET_MODE (node->loc), 1,
+                                        VOIDmode);
                      cselib_preserve_value (v);
                      cselib_invalidate_rtx (node->loc);
                      cval = v->val_rtx;
@@ -3582,18 +4091,24 @@ variable_post_merge_perm_vals (void **pslot, void *info)
   decl_or_value dv;
   attrs att;
 
-  gcc_assert (dv_is_value_p (pvar->dv));
-  gcc_assert (pvar->n_var_parts == 1);
+  gcc_assert (dv_is_value_p (pvar->dv)
+             && pvar->n_var_parts == 1);
   pnode = pvar->var_part[0].loc_chain;
-  gcc_assert (pnode);
-  gcc_assert (!pnode->next);
-  gcc_assert (REG_P (pnode->loc));
+  gcc_assert (pnode
+             && !pnode->next
+             && REG_P (pnode->loc));
 
   dv = pvar->dv;
 
   var = shared_hash_find (set->vars, dv);
   if (var)
     {
+      /* Although variable_post_merge_new_vals may have made decls
+        non-star-canonical, values that pre-existed in canonical form
+        remain canonical, and newly-created values reference a single
+        REG, so they are canonical as well.  Since VAR has the
+        location list for a VALUE, using find_loc_in_1pdv for it is
+        fine, since VALUEs don't map back to DECLs.  */
       if (find_loc_in_1pdv (pnode->loc, var, shared_hash_htab (set->vars)))
        return 1;
       val_reset (set, dv);
@@ -3618,7 +4133,7 @@ variable_post_merge_perm_vals (void **pslot, void *info)
     {
       attrs_list_insert (&set->regs[REGNO (pnode->loc)],
                         dv, 0, pnode->loc);
-      variable_union (pslot, set);
+      variable_union (pvar, set);
     }
 
   return 1;
@@ -3641,6 +4156,7 @@ dataflow_post_merge_adjust (dataflow_set *set, dataflow_set **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);
 }
 
 /* Return a node whose loc is a MEM that refers to EXPR in the
@@ -3658,9 +4174,8 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
   if (!val)
     return NULL;
 
-  gcc_assert (GET_CODE (val) == VALUE);
-
-  gcc_assert (!VALUE_RECURSED_INTO (val));
+  gcc_assert (GET_CODE (val) == VALUE
+             && !VALUE_RECURSED_INTO (val));
 
   dv = dv_from_value (val);
   var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
@@ -3668,18 +4183,17 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
   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;
@@ -3727,25 +4241,25 @@ 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))
+  if (var->onepart == ONEPART_VDECL || var->onepart == ONEPART_DEXPR)
     {
       tree decl = dv_as_decl (var->dv);
       location_chain loc, *locp;
+      bool changed = false;
 
       if (!var->n_var_parts)
        return 1;
 
       gcc_assert (var->n_var_parts == 1);
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          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 doesn't refer to DECL.  */
              if (GET_CODE (loc->loc) == MEM
                  && (MEM_EXPR (loc->loc) != decl
-                     || MEM_OFFSET (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.  */
@@ -3789,20 +4303,29 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
 
          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)
                {
-                 add_value_chains (var->dv, loc->loc);
-                 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;
+                   }
                }
              locp = &loc->next;
              continue;
            }
 
          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;
+               }
+           }
          *locp = loc->next;
          pool_free (loc_chain_pool, loc);
        }
@@ -3810,10 +4333,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
       if (!var->var_part[0].loc_chain)
        {
          var->n_var_parts--;
-         if (emit_notes && dv_is_value_p (var->dv))
-           remove_cselib_value_chains (var->dv);
-         variable_was_changed (var, set);
+         changed = true;
        }
+      if (changed)
+       variable_was_changed (var, set);
     }
 
   return 1;
@@ -3828,14 +4351,15 @@ 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))
+  if (var->onepart == ONEPART_VALUE)
     {
       location_chain loc, *locp;
       bool changed = false;
+      rtx cur_loc;
 
       gcc_assert (var->n_var_parts == 1);
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
            if (GET_CODE (loc->loc) == MEM
@@ -3850,6 +4374,11 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
          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)
        {
@@ -3860,31 +4389,27 @@ dataflow_set_remove_mem_locs (void **slot, void *data)
              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
-             && rtx_equal_p (loc->loc, var->var_part[0].cur_loc))
-           changed = true;
+         if (cur_loc == loc->loc)
+           {
+             changed = true;
+             var->var_part[0].cur_loc = NULL;
+             if (VAR_LOC_1PAUX (var))
+               VAR_LOC_FROM (var) = NULL;
+           }
          pool_free (loc_chain_pool, loc);
        }
 
       if (!var->var_part[0].loc_chain)
        {
          var->n_var_parts--;
-         if (emit_notes && dv_is_value_p (var->dv))
-           remove_cselib_value_chains (var->dv);
-         gcc_assert (changed);
+         changed = true;
        }
       if (changed)
-       {
-         if (var->n_var_parts && var->var_part[0].loc_chain)
-           var->var_part[0].cur_loc = var->var_part[0].loc_chain->loc;
-         variable_was_changed (var, set);
-       }
+       variable_was_changed (var, set);
     }
 
   return 1;
@@ -3899,7 +4424,7 @@ dataflow_set_clear_at_call (dataflow_set *set)
   int r;
 
   for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
-    if (TEST_HARD_REG_BIT (call_used_reg_set, r))
+    if (TEST_HARD_REG_BIT (regs_invalidated_by_call, r))
       var_regno_delete (set, r);
 
   if (MAY_HAVE_DEBUG_INSNS)
@@ -3914,10 +4439,6 @@ dataflow_set_clear_at_call (dataflow_set *set)
     }
 }
 
-/* Flag whether two dataflow sets being compared contain different data.  */
-static bool
-dataflow_set_different_value;
-
 static bool
 variable_part_different_p (variable_part *vp1, variable_part *vp2)
 {
@@ -3952,14 +4473,13 @@ onepart_variable_different_p (variable var1, variable var2)
   if (var1 == var2)
     return false;
 
-  gcc_assert (var1->n_var_parts == 1);
-  gcc_assert (var2->n_var_parts == 1);
+  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);
-  gcc_assert (lc2);
+  gcc_assert (lc1 && lc2);
 
   while (lc1 && lc2)
     {
@@ -3972,43 +4492,34 @@ onepart_variable_different_p (variable var1, variable var2)
   return lc1 != lc2;
 }
 
-/* Return true if variables VAR1 and VAR2 are different.
-   If COMPARE_CURRENT_LOCATION is true compare also the cur_loc of each
-   variable part.  */
+/* Return true if variables VAR1 and VAR2 are different.  */
 
 static bool
-variable_different_p (variable var1, variable var2,
-                     bool compare_current_location)
+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;
-      if (compare_current_location)
-       {
-         if (!((REG_P (var1->var_part[i].cur_loc)
-                && REG_P (var2->var_part[i].cur_loc)
-                && (REGNO (var1->var_part[i].cur_loc)
-                    == REGNO (var2->var_part[i].cur_loc)))
-               || rtx_equal_p (var1->var_part[i].cur_loc,
-                               var2->var_part[i].cur_loc)))
-           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);
-         gcc_assert (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]))
@@ -4017,56 +4528,14 @@ variable_different_p (variable var1, variable var2,
   return false;
 }
 
-/* Compare variable *SLOT with the same variable in hash table DATA
-   and set DATAFLOW_SET_DIFFERENT_VALUE if they are different.  */
-
-static int
-dataflow_set_different_1 (void **slot, void *data)
-{
-  htab_t htab = (htab_t) data;
-  variable var1, var2;
-
-  var1 = (variable) *slot;
-  var2 = (variable) htab_find_with_hash (htab, var1->dv,
-                                        dv_htab_hash (var1->dv));
-  if (!var2)
-    {
-      dataflow_set_different_value = true;
-
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       {
-         fprintf (dump_file, "dataflow difference found: removal of:\n");
-         dump_var (var1);
-       }
-
-      /* Stop traversing the hash table.  */
-      return 0;
-    }
-
-  if (variable_different_p (var1, var2, false))
-    {
-      dataflow_set_different_value = true;
-
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       {
-         fprintf (dump_file, "dataflow difference found: old and new follow:\n");
-         dump_var (var1);
-         dump_var (var2);
-       }
-
-      /* Stop traversing the hash table.  */
-      return 0;
-    }
-
-  /* Continue traversing the hash table.  */
-  return 1;
-}
-
 /* Return true if dataflow sets OLD_SET and NEW_SET differ.  */
 
 static bool
 dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
 {
+  htab_iterator hi;
+  variable var1;
+
   if (old_set->vars == new_set->vars)
     return false;
 
@@ -4074,14 +4543,38 @@ dataflow_set_different (dataflow_set *old_set, dataflow_set *new_set)
       != htab_elements (shared_hash_htab (new_set->vars)))
     return true;
 
-  dataflow_set_different_value = false;
+  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 (!var2)
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             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))
+           {
+             fprintf (dump_file, "dataflow difference found: "
+                      "old and new follow:\n");
+             dump_var (var1);
+             dump_var (var2);
+           }
+         return true;
+       }
+    }
 
-  htab_traverse (shared_hash_htab (old_set->vars), dataflow_set_different_1,
-                shared_hash_htab (new_set->vars));
   /* No need to traverse the second hashtab, if both have the same number
      of elements and the second one had all entries found in the first one,
      then it can't have any extra entries.  */
-  return dataflow_set_different_value;
+  return false;
 }
 
 /* Free the contents of dataflow set SET.  */
@@ -4162,13 +4655,32 @@ track_expr_p (tree expr, bool need_rtl)
      don't need to track this expression if the ultimate declaration is
      ignored.  */
   realdecl = expr;
-  if (DECL_DEBUG_EXPR_IS_FROM (realdecl) && DECL_DEBUG_EXPR (realdecl))
+  if (DECL_DEBUG_EXPR_IS_FROM (realdecl))
     {
       realdecl = DECL_DEBUG_EXPR (realdecl);
-      /* ??? We don't yet know how to emit DW_OP_piece for variable
-        that has been SRA'ed.  */
-      if (!DECL_P (realdecl))
-       return 0;
+      if (realdecl == NULL_TREE)
+       realdecl = expr;
+      else if (!DECL_P (realdecl))
+       {
+         if (handled_component_p (realdecl))
+           {
+             HOST_WIDE_INT bitsize, bitpos, maxsize;
+             tree innerdecl
+               = get_ref_base_and_extent (realdecl, &bitpos, &bitsize,
+                                          &maxsize);
+             if (!DECL_P (innerdecl)
+                 || DECL_IGNORED_P (innerdecl)
+                 || TREE_STATIC (innerdecl)
+                 || bitsize <= 0
+                 || bitpos + bitsize > 256
+                 || bitsize != maxsize)
+               return 0;
+             else
+               realdecl = expr;
+           }
+         else
+           return 0;
+       }
     }
 
   /* Do not track EXPR if REALDECL it should be ignored for debugging
@@ -4201,8 +4713,8 @@ track_expr_p (tree expr, bool need_rtl)
       if (GET_MODE (decl_rtl) == BLKmode
          || AGGREGATE_TYPE_P (TREE_TYPE (realdecl)))
        return 0;
-      if (MEM_SIZE (decl_rtl)
-         && INTVAL (MEM_SIZE (decl_rtl)) > MAX_VAR_PARTS)
+      if (MEM_SIZE_KNOWN_P (decl_rtl)
+         && MEM_SIZE (decl_rtl) > MAX_VAR_PARTS)
        return 0;
     }
 
@@ -4360,23 +4872,41 @@ find_use_val (rtx x, enum machine_mode mode, struct count_use_info *cui)
   if (cui->sets)
     {
       /* This is called after uses are set up and before stores are
-        processed bycselib, so it's safe to look up srcs, but not
+        processed by cselib, so it's safe to look up srcs, but not
         dsts.  So we look up expressions that appear in srcs or in
         dest expressions, but we search the sets array for dests of
         stores.  */
       if (cui->store_p)
        {
+         /* Some targets represent memset and memcpy patterns
+            by (set (mem:BLK ...) (reg:[QHSD]I ...)) or
+            (set (mem:BLK ...) (const_int ...)) or
+            (set (mem:BLK ...) (mem:BLK ...)).  Don't return anything
+            in that case, otherwise we end up with mode mismatches.  */
+         if (mode == BLKmode && MEM_P (x))
+           return NULL;
          for (i = 0; i < cui->n_sets; i++)
            if (cui->sets[i].dest == x)
              return cui->sets[i].src_elt;
        }
       else
-       return cselib_lookup (x, mode, 0);
+       return cselib_lookup (x, mode, 0, VOIDmode);
     }
 
   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.  */
@@ -4384,20 +4914,32 @@ find_use_val (rtx x, enum machine_mode mode, struct count_use_info *cui)
 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))
     {
-      enum machine_mode address_mode
-       = targetm.addr_space.address_mode (MEM_ADDR_SPACE (loc));
-      cselib_val *addr = cselib_lookup (XEXP (loc, 0), address_mode, 0);
+      cselib_val *addr = cselib_lookup (XEXP (loc, 0),
+                                       get_address_mode (loc), 0,
+                                       GET_MODE (loc));
       if (addr)
        return replace_equiv_address_nv (loc, addr->val_rtx);
       else
        return NULL;
     }
   else
-    return cselib_subst_to_values (loc);
+    return cselib_subst_to_values (loc, VOIDmode);
+}
+
+/* Return true if *X is a DEBUG_EXPR.  Usable as an argument to
+   for_each_rtx to tell whether there are any DEBUG_EXPRs within
+   RTX.  */
+
+static int
+rtx_debug_expr_p (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+  rtx loc = *x;
+
+  return GET_CODE (loc) == DEBUG_EXPR;
 }
 
 /* Determine what kind of micro operation to choose for a USE.  Return
@@ -4415,12 +4957,16 @@ use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
          if (track_expr_p (PAT_VAR_LOCATION_DECL (loc), false))
            {
              rtx ploc = PAT_VAR_LOCATION_LOC (loc);
-             cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1);
+             if (! VAR_LOC_UNKNOWN_P (ploc))
+               {
+                 cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1,
+                                                  VOIDmode);
 
-             /* ??? flag_float_store and volatile mems are never
-                given values, but we could in theory use them for
-                locations.  */
-             gcc_assert (val || 1);
+                 /* ??? flag_float_store and volatile mems are never
+                    given values, but we could in theory use them for
+                    locations.  */
+                 gcc_assert (val || 1);
+               }
              return MO_VAL_LOC;
            }
          else
@@ -4435,7 +4981,9 @@ use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
            {
              if (REG_P (loc)
                  || (find_use_val (loc, GET_MODE (loc), cui)
-                     && cselib_lookup (XEXP (loc, 0), GET_MODE (loc), 0)))
+                     && cselib_lookup (XEXP (loc, 0),
+                                       get_address_mode (loc), 0,
+                                       GET_MODE (loc))))
                return MO_VAL_SET;
            }
          else
@@ -4452,6 +5000,8 @@ use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
     {
       gcc_assert (REGNO (loc) < FIRST_PSEUDO_REGISTER);
 
+      if (loc == cfa_base_rtx)
+       return MO_CLOBBER;
       expr = REG_EXPR (loc);
 
       if (!expr)
@@ -4473,7 +5023,13 @@ use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
       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
+                  || !for_each_rtx (&XEXP (loc, 0), rtx_debug_expr_p, NULL)))
        return MO_USE;
       else
        return MO_CLOBBER;
@@ -4490,145 +5046,12 @@ log_op_type (rtx x, basic_block bb, rtx insn,
             enum micro_operation_type mopt, FILE *out)
 {
   fprintf (out, "bb %i op %i insn %i %s ",
-          bb->index, VTI (bb)->n_mos - 1,
+          bb->index, VEC_length (micro_operation, VTI (bb)->mos),
           INSN_UID (insn), micro_operation_type_name[mopt]);
   print_inline_rtx (out, x, 2);
   fputc ('\n', out);
 }
 
-/* Count uses (register and memory references) LOC which will be tracked.
-   INSN is instruction which the LOC is part of.  */
-
-static int
-count_uses (rtx *ploc, void *cuip)
-{
-  rtx loc = *ploc;
-  struct count_use_info *cui = (struct count_use_info *) cuip;
-  enum micro_operation_type mopt = use_type (loc, cui, NULL);
-
-  if (mopt != MO_CLOBBER)
-    {
-      cselib_val *val;
-      enum machine_mode mode = GET_MODE (loc);
-
-      switch (mopt)
-       {
-       case MO_VAL_LOC:
-         loc = PAT_VAR_LOCATION_LOC (loc);
-         if (VAR_LOC_UNKNOWN_P (loc))
-           break;
-         /* Fall through.  */
-
-       case MO_VAL_USE:
-       case MO_VAL_SET:
-         if (MEM_P (loc)
-             && !REG_P (XEXP (loc, 0)) && !MEM_P (XEXP (loc, 0)))
-           {
-             enum machine_mode address_mode
-               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (loc));
-             val = cselib_lookup (XEXP (loc, 0), address_mode, 0);
-
-             if (val && !cselib_preserved_value_p (val))
-               {
-                 VTI (cui->bb)->n_mos++;
-                 cselib_preserve_value (val);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   log_op_type (XEXP (loc, 0), cui->bb, cui->insn,
-                                MO_VAL_USE, dump_file);
-               }
-           }
-
-         val = find_use_val (loc, mode, cui);
-         if (val)
-           {
-             if (mopt == MO_VAL_SET
-                 && GET_CODE (PATTERN (cui->insn)) == COND_EXEC
-                 && (REG_P (loc)
-                     || (MEM_P (loc)
-                         && (use_type (loc, NULL, NULL) == MO_USE
-                             || cui->sets))))
-               {
-                 cselib_val *oval = cselib_lookup (loc, GET_MODE (loc), 0);
-
-                 gcc_assert (oval != val);
-                 gcc_assert (REG_P (loc) || MEM_P (loc));
-
-                 if (!cselib_preserved_value_p (oval))
-                   {
-                     VTI (cui->bb)->n_mos++;
-                     cselib_preserve_value (oval);
-                     if (dump_file && (dump_flags & TDF_DETAILS))
-                       log_op_type (loc, cui->bb, cui->insn,
-                                    MO_VAL_USE, dump_file);
-                   }
-               }
-
-             cselib_preserve_value (val);
-           }
-         else
-           gcc_assert (mopt == MO_VAL_LOC
-                       || (mopt == MO_VAL_SET && cui->store_p));
-
-         break;
-
-       default:
-         break;
-       }
-
-      VTI (cui->bb)->n_mos++;
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       log_op_type (loc, cui->bb, cui->insn, mopt, dump_file);
-    }
-
-  return 0;
-}
-
-/* Helper function for finding all uses of REG/MEM in X in CUI's
-   insn.  */
-
-static void
-count_uses_1 (rtx *x, void *cui)
-{
-  for_each_rtx (x, count_uses, cui);
-}
-
-/* Count stores (register and memory references) LOC which will be
-   tracked.  CUI is a count_use_info object containing the instruction
-   which the LOC is part of.  */
-
-static void
-count_stores (rtx loc, const_rtx expr ATTRIBUTE_UNUSED, void *cui)
-{
-  count_uses (&loc, cui);
-}
-
-/* Callback for cselib_record_sets_hook, that counts how many micro
-   operations it takes for uses and stores in an insn after
-   cselib_record_sets has analyzed the sets in an insn, but before it
-   modifies the stored values in the internal tables, unless
-   cselib_record_sets doesn't call it directly (perhaps because we're
-   not doing cselib in the first place, in which case sets and n_sets
-   will be 0).  */
-
-static void
-count_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
-{
-  basic_block bb = BLOCK_FOR_INSN (insn);
-  struct count_use_info cui;
-
-  cselib_hook_called = true;
-
-  cui.insn = insn;
-  cui.bb = bb;
-  cui.sets = sets;
-  cui.n_sets = n_sets;
-
-  cui.store_p = false;
-  note_uses (&PATTERN (insn), count_uses_1, &cui);
-  cui.store_p = true;
-  note_stores (PATTERN (insn), count_stores, &cui);
-}
-
 /* Tell whether the CONCAT used to holds a VALUE and its location
    needs value resolution, i.e., an attempt of mapping the location
    back to other incoming values.  */
@@ -4647,6 +5070,45 @@ count_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
 #define VAL_EXPR_IS_CLOBBERED(x) \
   (RTL_FLAG_CHECK1 ("VAL_EXPR_IS_CLOBBERED", (x), CONCAT)->unchanging)
 
+/* All preserved VALUEs.  */
+static VEC (rtx, heap) *preserved_values;
+
+/* Ensure VAL is preserved and remember it in a vector for vt_emit_notes.  */
+
+static void
+preserve_value (cselib_val *val)
+{
+  cselib_preserve_value (val);
+  VEC_safe_push (rtx, heap, preserved_values, 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;
+    }
+}
+
 /* Add uses (register and memory references) LOC which will be tracked
    to VTI (bb)->mos.  INSN is instruction which the LOC is part of.  */
 
@@ -4661,11 +5123,11 @@ add_uses (rtx *ploc, void *data)
   if (type != MO_CLOBBER)
     {
       basic_block bb = cui->bb;
-      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
+      micro_operation mo;
 
-      mo->type = type;
-      mo->u.loc = type == MO_USE ? var_lowpart (mode, loc) : loc;
-      mo->insn = cui->insn;
+      mo.type = type;
+      mo.u.loc = type == MO_USE ? var_lowpart (mode, loc) : loc;
+      mo.insn = cui->insn;
 
       if (type == MO_VAL_LOC)
        {
@@ -4676,38 +5138,33 @@ add_uses (rtx *ploc, void *data)
          gcc_assert (cui->sets);
 
          if (MEM_P (vloc)
-             && !REG_P (XEXP (vloc, 0)) && !MEM_P (XEXP (vloc, 0)))
+             && !REG_P (XEXP (vloc, 0))
+             && !MEM_P (XEXP (vloc, 0)))
            {
              rtx mloc = vloc;
-             enum machine_mode address_mode
-               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (mloc));
+             enum machine_mode address_mode = get_address_mode (mloc);
              cselib_val *val
-               = cselib_lookup (XEXP (mloc, 0), address_mode, 0);
+               = cselib_lookup (XEXP (mloc, 0), address_mode, 0,
+                                GET_MODE (mloc));
 
              if (val && !cselib_preserved_value_p (val))
-               {
-                 micro_operation *mon = VTI (bb)->mos + VTI (bb)->n_mos++;
-                 mon->type = mo->type;
-                 mon->u.loc = mo->u.loc;
-                 mon->insn = mo->insn;
-                 cselib_preserve_value (val);
-                 mo->type = MO_VAL_USE;
-                 mloc = cselib_subst_to_values (XEXP (mloc, 0));
-                 mo->u.loc = gen_rtx_CONCAT (address_mode,
-                                             val->val_rtx, mloc);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   log_op_type (mo->u.loc, cui->bb, cui->insn,
-                                mo->type, dump_file);
-                 mo = mon;
-               }
+               preserve_value (val);
            }
 
-         if (!VAR_LOC_UNKNOWN_P (vloc)
-             && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
+         if (CONSTANT_P (vloc)
+             && (GET_CODE (vloc) != CONST
+                 || for_each_rtx (&vloc, non_suitable_const, NULL)))
+           /* For constants don't look up any value.  */;
+         else if (!VAR_LOC_UNKNOWN_P (vloc) && !unsuitable_loc (vloc)
+                  && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
            {
              enum 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)
                {
@@ -4725,8 +5182,8 @@ add_uses (rtx *ploc, void *data)
              if (type2 == MO_CLOBBER
                  && !cselib_preserved_value_p (val))
                {
-                 VAL_NEEDS_RESOLUTION (oloc) = 1;
-                 cselib_preserve_value (val);
+                 VAL_NEEDS_RESOLUTION (oloc) = resolvable;
+                 preserve_value (val);
                }
            }
          else if (!VAR_LOC_UNKNOWN_P (vloc))
@@ -4735,7 +5192,7 @@ add_uses (rtx *ploc, void *data)
              PAT_VAR_LOCATION_LOC (oloc) = gen_rtx_UNKNOWN_VAR_LOC ();
            }
 
-         mo->u.loc = oloc;
+         mo.u.loc = oloc;
        }
       else if (type == MO_VAL_USE)
        {
@@ -4747,31 +5204,17 @@ add_uses (rtx *ploc, void *data)
          gcc_assert (cui->sets);
 
          if (MEM_P (oloc)
-             && !REG_P (XEXP (oloc, 0)) && !MEM_P (XEXP (oloc, 0)))
+             && !REG_P (XEXP (oloc, 0))
+             && !MEM_P (XEXP (oloc, 0)))
            {
              rtx mloc = oloc;
-             enum machine_mode address_mode
-               = targetm.addr_space.address_mode (MEM_ADDR_SPACE (mloc));
+             enum machine_mode address_mode = get_address_mode (mloc);
              cselib_val *val
-               = cselib_lookup (XEXP (mloc, 0), address_mode, 0);
+               = cselib_lookup (XEXP (mloc, 0), address_mode, 0,
+                                GET_MODE (mloc));
 
              if (val && !cselib_preserved_value_p (val))
-               {
-                 micro_operation *mon = VTI (bb)->mos + VTI (bb)->n_mos++;
-                 mon->type = mo->type;
-                 mon->u.loc = mo->u.loc;
-                 mon->insn = mo->insn;
-                 cselib_preserve_value (val);
-                 mo->type = MO_VAL_USE;
-                 mloc = cselib_subst_to_values (XEXP (mloc, 0));
-                 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);
-                 mo = mon;
-               }
+               preserve_value (val);
            }
 
          type2 = use_type (loc, 0, &mode2);
@@ -4794,6 +5237,7 @@ add_uses (rtx *ploc, void *data)
 
          */
 
+         gcc_checking_assert (REG_P (loc) || MEM_P (loc));
          nloc = replace_expr_with_values (loc);
          if (!nloc)
            nloc = oloc;
@@ -4803,21 +5247,22 @@ add_uses (rtx *ploc, void *data)
          else
            oloc = val->val_rtx;
 
-         mo->u.loc = gen_rtx_CONCAT (mode, oloc, nloc);
+         mo.u.loc = gen_rtx_CONCAT (mode, oloc, nloc);
 
          if (type2 == MO_USE)
-           VAL_HOLDS_TRACK_EXPR (mo->u.loc) = 1;
+           VAL_HOLDS_TRACK_EXPR (mo.u.loc) = 1;
          if (!cselib_preserved_value_p (val))
            {
-             VAL_NEEDS_RESOLUTION (mo->u.loc) = 1;
-             cselib_preserve_value (val);
+             VAL_NEEDS_RESOLUTION (mo.u.loc) = 1;
+             preserve_value (val);
            }
        }
       else
        gcc_assert (type == MO_USE || type == MO_USE_NO_VAR);
 
       if (dump_file && (dump_flags & TDF_DETAILS))
-       log_op_type (mo->u.loc, cui->bb, cui->insn, mo->type, dump_file);
+       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;
@@ -4831,9 +5276,126 @@ add_uses_1 (rtx *x, void *cui)
   for_each_rtx (x, add_uses, cui);
 }
 
-/* Add stores (register and memory references) LOC which will be tracked
-   to VTI (bb)->mos.  EXPR is the RTL expression containing the store.
-   CUIP->insn is instruction which the LOC is part of.  */
+/* 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)
+{
+  rtx src, arg, ret;
+  cselib_val *v;
+  struct elt_loc_list *l;
+  enum rtx_code code;
+
+  if (GET_CODE (expr) != SET)
+    return;
+
+  if (!REG_P (SET_DEST (expr)) || GET_MODE (val) != GET_MODE (SET_DEST (expr)))
+    return;
+
+  src = SET_SRC (expr);
+  switch (GET_CODE (src))
+    {
+    case PLUS:
+    case MINUS:
+    case XOR:
+    case NOT:
+    case NEG:
+      if (!REG_P (XEXP (src, 0)))
+       return;
+      break;
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      if (!REG_P (XEXP (src, 0)) && !MEM_P (XEXP (src, 0)))
+       return;
+      break;
+    default:
+      return;
+    }
+
+  if (!SCALAR_INT_MODE_P (GET_MODE (src)) || XEXP (src, 0) == cfa_base_rtx)
+    return;
+
+  v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0, VOIDmode);
+  if (!v || !cselib_preserved_value_p (v))
+    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; l; l = l->next)
+    if (CONSTANT_P (l->loc)
+       && (GET_CODE (l->loc) != CONST || !references_value_p (l->loc, 0)))
+      return;
+
+  switch (GET_CODE (src))
+    {
+    case NOT:
+    case NEG:
+      if (GET_MODE (v->val_rtx) != GET_MODE (val))
+       return;
+      ret = gen_rtx_fmt_e (GET_CODE (src), GET_MODE (val), val);
+      break;
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      ret = gen_lowpart_SUBREG (GET_MODE (v->val_rtx), val);
+      break;
+    case XOR:
+      code = XOR;
+      goto binary;
+    case PLUS:
+      code = MINUS;
+      goto binary;
+    case MINUS:
+      code = PLUS;
+      goto binary;
+    binary:
+      if (GET_MODE (v->val_rtx) != GET_MODE (val))
+       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;
+         if (!CONST_INT_P (arg) && GET_CODE (arg) != SYMBOL_REF)
+           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 ();
+    }
+
+  cselib_add_permanent_equiv (v, ret, insn);
+}
+
+/* Add stores (register and memory references) LOC which will be tracked
+   to VTI (bb)->mos.  EXPR is the RTL expression containing the store.
+   CUIP->insn is instruction which the LOC is part of.  */
 
 static void
 add_stores (rtx loc, const_rtx expr, void *cuip)
@@ -4841,7 +5403,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
   enum machine_mode mode = VOIDmode, mode2;
   struct count_use_info *cui = (struct count_use_info *)cuip;
   basic_block bb = cui->bb;
-  micro_operation *mo;
+  micro_operation mo;
   rtx oloc = loc, nloc, src = NULL;
   enum micro_operation_type type = use_type (loc, cui, &mode);
   bool track_p = false;
@@ -4855,101 +5417,96 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
 
   if (REG_P (loc))
     {
-      mo = VTI (bb)->mos + VTI (bb)->n_mos++;
-
+      gcc_assert (loc != cfa_base_rtx);
       if ((GET_CODE (expr) == CLOBBER && type != MO_VAL_SET)
          || !(track_p = use_type (loc, NULL, &mode2) == MO_USE)
          || GET_CODE (expr) == CLOBBER)
        {
-         mo->type = MO_CLOBBER;
-         mo->u.loc = loc;
+         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 (VOIDmode, 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);
 
          if (src == NULL)
            {
-             mo->type = MO_SET;
-             mo->u.loc = loc;
+             mo.type = MO_SET;
+             mo.u.loc = loc;
            }
          else
            {
-             rtx xexpr = CONST_CAST_RTX (expr);
-
-             if (SET_SRC (expr) != src)
-               xexpr = gen_rtx_SET (VOIDmode, loc, src);
+             rtx xexpr = gen_rtx_SET (VOIDmode, loc, src);
              if (same_variable_part_p (src, REG_EXPR (loc), REG_OFFSET (loc)))
-               mo->type = MO_COPY;
+               mo.type = MO_COPY;
              else
-               mo->type = MO_SET;
-             mo->u.loc = xexpr;
+               mo.type = MO_SET;
+             mo.u.loc = xexpr;
            }
        }
-      mo->insn = cui->insn;
+      mo.insn = cui->insn;
     }
   else if (MEM_P (loc)
           && ((track_p = use_type (loc, NULL, &mode2) == MO_USE)
               || cui->sets))
     {
-      mo = VTI (bb)->mos + VTI (bb)->n_mos++;
-
       if (MEM_P (loc) && type == MO_VAL_SET
-         && !REG_P (XEXP (loc, 0)) && !MEM_P (XEXP (loc, 0)))
+         && !REG_P (XEXP (loc, 0))
+         && !MEM_P (XEXP (loc, 0)))
        {
          rtx mloc = loc;
-         enum machine_mode address_mode
-           = targetm.addr_space.address_mode (MEM_ADDR_SPACE (mloc));
-         cselib_val *val = cselib_lookup (XEXP (mloc, 0), address_mode, 0);
+         enum 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))
-           {
-             cselib_preserve_value (val);
-             mo->type = MO_VAL_USE;
-             mloc = cselib_subst_to_values (XEXP (mloc, 0));
-             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);
-             mo = VTI (bb)->mos + VTI (bb)->n_mos++;
-           }
+           preserve_value (val);
        }
 
       if (GET_CODE (expr) == CLOBBER || !track_p)
        {
-         mo->type = MO_CLOBBER;
-         mo->u.loc = track_p ? var_lowpart (mode2, loc) : loc;
+         mo.type = MO_CLOBBER;
+         mo.u.loc = track_p ? var_lowpart (mode2, loc) : loc;
        }
       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);
 
          if (src == NULL)
            {
-             mo->type = MO_SET;
-             mo->u.loc = loc;
+             mo.type = MO_SET;
+             mo.u.loc = loc;
            }
          else
            {
-             rtx xexpr = CONST_CAST_RTX (expr);
-
-             if (SET_SRC (expr) != src)
-               xexpr = gen_rtx_SET (VOIDmode, loc, src);
+             rtx xexpr = gen_rtx_SET (VOIDmode, loc, src);
              if (same_variable_part_p (SET_SRC (xexpr),
                                        MEM_EXPR (loc),
                                        INT_MEM_OFFSET (loc)))
-               mo->type = MO_COPY;
+               mo.type = MO_COPY;
              else
-               mo->type = MO_SET;
-             mo->u.loc = xexpr;
+               mo.type = MO_SET;
+             mo.u.loc = xexpr;
            }
        }
-      mo->insn = cui->insn;
+      mo.insn = cui->insn;
     }
   else
     return;
@@ -4970,32 +5527,36 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
 
   if (GET_CODE (PATTERN (cui->insn)) == COND_EXEC)
     {
-      cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0);
+      cselib_val *oval = cselib_lookup (oloc, GET_MODE (oloc), 0, VOIDmode);
 
       gcc_assert (oval != v);
       gcc_assert (REG_P (oloc) || MEM_P (oloc));
 
-      if (!cselib_preserved_value_p (oval))
+      if (oval && !cselib_preserved_value_p (oval))
        {
-         micro_operation *nmo = VTI (bb)->mos + VTI (bb)->n_mos++;
+         micro_operation moa;
 
-         cselib_preserve_value (oval);
+         preserve_value (oval);
 
-         nmo->type = MO_VAL_USE;
-         nmo->u.loc = gen_rtx_CONCAT (mode, oval->val_rtx, oloc);
-         VAL_NEEDS_RESOLUTION (nmo->u.loc) = 1;
-         nmo->insn = mo->insn;
+         moa.type = MO_VAL_USE;
+         moa.u.loc = gen_rtx_CONCAT (mode, oval->val_rtx, oloc);
+         VAL_NEEDS_RESOLUTION (moa.u.loc) = 1;
+         moa.insn = cui->insn;
 
          if (dump_file && (dump_flags & TDF_DETAILS))
-           log_op_type (nmo->u.loc, cui->bb, cui->insn,
-                        nmo->type, dump_file);
+           log_op_type (moa.u.loc, cui->bb, cui->insn,
+                        moa.type, dump_file);
+         VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &moa);
        }
 
       resolve = false;
     }
-  else if (resolve && GET_CODE (mo->u.loc) == SET)
+  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)
@@ -5004,31 +5565,31 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
          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 (GET_MODE (mo.u.loc), oloc, nloc);
       else
        {
-         if (oloc == SET_DEST (mo->u.loc))
+         if (oloc == SET_DEST (mo.u.loc))
            /* No point in duplicating.  */
-           oloc = mo->u.loc;
-         if (!REG_P (SET_SRC (mo->u.loc)))
+           oloc = mo.u.loc;
+         if (!REG_P (SET_SRC (mo.u.loc)))
            resolve = false;
        }
     }
   else if (!resolve)
     {
-      if (GET_CODE (mo->u.loc) == SET
-         && oloc == SET_DEST (mo->u.loc))
+      if (GET_CODE (mo.u.loc) == SET
+         && oloc == SET_DEST (mo.u.loc))
        /* No point in duplicating.  */
-       oloc = mo->u.loc;
+       oloc = mo.u.loc;
     }
   else
     resolve = false;
 
   loc = gen_rtx_CONCAT (mode, v->val_rtx, oloc);
 
-  if (mo->u.loc != oloc)
-    loc = gen_rtx_CONCAT (GET_MODE (mo->u.loc), loc, mo->u.loc);
+  if (mo.u.loc != oloc)
+    loc = gen_rtx_CONCAT (GET_MODE (mo.u.loc), loc, mo.u.loc);
 
   /* The loc of a MO_VAL_SET may have various forms:
 
@@ -5050,25 +5611,370 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
 
   */
 
-  mo->u.loc = loc;
+  if (GET_CODE (PATTERN (cui->insn)) != COND_EXEC)
+    reverse_op (v->val_rtx, expr, cui->insn);
+
+  mo.u.loc = loc;
 
   if (track_p)
     VAL_HOLDS_TRACK_EXPR (loc) = 1;
   if (preserve)
     {
       VAL_NEEDS_RESOLUTION (loc) = resolve;
-      cselib_preserve_value (v);
+      preserve_value (v);
     }
-  if (mo->type == MO_CLOBBER)
+  if (mo.type == MO_CLOBBER)
     VAL_EXPR_IS_CLOBBERED (loc) = 1;
-  if (mo->type == MO_COPY)
+  if (mo.type == MO_COPY)
     VAL_EXPR_IS_COPIED (loc) = 1;
 
-  mo->type = MO_VAL_SET;
+  mo.type = MO_VAL_SET;
 
  log_and_return:
   if (dump_file && (dump_flags & TDF_DETAILS))
-    log_op_type (mo->u.loc, cui->bb, cui->insn, mo->type, dump_file);
+    log_op_type (mo.u.loc, cui->bb, cui->insn, mo.type, dump_file);
+  VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
+}
+
+/* Arguments to the call.  */
+static rtx call_arguments;
+
+/* Compute call_arguments.  */
+
+static void
+prepare_call_arguments (basic_block bb, rtx insn)
+{
+  rtx link, x;
+  rtx prev, cur, next;
+  rtx call = PATTERN (insn);
+  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);
+  if (GET_CODE (call) == PARALLEL)
+    call = XVECEXP (call, 0, 0);
+  if (GET_CODE (call) == SET)
+    call = SET_SRC (call);
+  if (GET_CODE (call) == CALL && MEM_P (XEXP (call, 0)))
+    {
+      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));
+                 enum 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)
+               {
+                 enum 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 (x)) != MODE_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);
+           if (val && cselib_preserved_value_p (val))
+             item = val->val_rtx;
+           else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_INT)
+             {
+               enum machine_mode mode = GET_MODE (x);
+
+               while ((mode = GET_MODE_WIDER_MODE (mode)) != VOIDmode
+                      && GET_MODE_BITSIZE (mode) <= BITS_PER_WORD)
+                 {
+                   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.side_effects = NULL_RTX;
+               amd.store = true;
+               mem = simplify_replace_fn_rtx (mem, NULL_RTX, adjust_mems,
+                                              &amd);
+               gcc_assert (amd.side_effects == NULL_RTX);
+             }
+           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)
+             {
+               /* For non-integer stack argument see also if they weren't
+                  initialized by integers.  */
+               enum machine_mode imode = int_mode_for_mode (GET_MODE (mem));
+               if (imode != GET_MODE (mem) && imode != BLKmode)
+                 {
+                   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);
+           enum 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
+               && REG_P (x)
+               && REGNO (x) == REGNO (reg)
+               && GET_MODE (x) == mode
+               && item)
+             {
+               enum 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 (host_integerp (initial, 0))
+                           {
+                             item = GEN_INT (tree_low_cst (initial, 0));
+                             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, gc) **debug_args = decl_debug_args_lookup (fndecl);
+      if (debug_args)
+       {
+         unsigned int ix;
+         tree param;
+         for (ix = 0; VEC_iterate (tree, *debug_args, ix, param); ix += 2)
+           {
+             rtx item;
+             tree dtemp = VEC_index (tree, *debug_args, ix + 1);
+             enum 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 = PATTERN (insn);
+  if (GET_CODE (x) == PARALLEL)
+    x = XVECEXP (x, 0, 0);
+  if (GET_CODE (x) == SET)
+    x = SET_SRC (x);
+  if (GET_CODE (x) == CALL && MEM_P (XEXP (x, 0)))
+    {
+      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)
+    {
+      enum 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_low_cst (OBJ_TYPE_REF_TOKEN (obj_type_ref), 0);
+      if (token)
+       clobbered = plus_constant (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
@@ -5084,6 +5990,7 @@ add_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
   basic_block bb = BLOCK_FOR_INSN (insn);
   int n1, n2;
   struct count_use_info cui;
+  micro_operation *mos;
 
   cselib_hook_called = true;
 
@@ -5092,80 +5999,103 @@ add_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
   cui.sets = sets;
   cui.n_sets = n_sets;
 
-  n1 = VTI (bb)->n_mos;
+  n1 = VEC_length (micro_operation, VTI (bb)->mos);
   cui.store_p = false;
   note_uses (&PATTERN (insn), add_uses_1, &cui);
-  n2 = VTI (bb)->n_mos - 1;
+  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
+  mos = VEC_address (micro_operation, VTI (bb)->mos);
 
   /* Order the MO_USEs to be before MO_USE_NO_VARs and MO_VAL_USE, and
      MO_VAL_LOC last.  */
   while (n1 < n2)
     {
-      while (n1 < n2 && VTI (bb)->mos[n1].type == MO_USE)
+      while (n1 < n2 && mos[n1].type == MO_USE)
        n1++;
-      while (n1 < n2 && VTI (bb)->mos[n2].type != MO_USE)
+      while (n1 < n2 && mos[n2].type != MO_USE)
        n2--;
       if (n1 < n2)
        {
          micro_operation sw;
 
-         sw = VTI (bb)->mos[n1];
-         VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
-         VTI (bb)->mos[n2] = sw;
+         sw = mos[n1];
+         mos[n1] = mos[n2];
+         mos[n2] = sw;
        }
     }
 
-  n2 = VTI (bb)->n_mos - 1;
-
+  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
   while (n1 < n2)
     {
-      while (n1 < n2 && VTI (bb)->mos[n1].type != MO_VAL_LOC)
+      while (n1 < n2 && mos[n1].type != MO_VAL_LOC)
        n1++;
-      while (n1 < n2 && VTI (bb)->mos[n2].type == MO_VAL_LOC)
+      while (n1 < n2 && mos[n2].type == MO_VAL_LOC)
        n2--;
       if (n1 < n2)
        {
          micro_operation sw;
 
-         sw = VTI (bb)->mos[n1];
-         VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
-         VTI (bb)->mos[n2] = sw;
+         sw = mos[n1];
+         mos[n1] = mos[n2];
+         mos[n2] = sw;
        }
     }
 
   if (CALL_P (insn))
     {
-      micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
+      micro_operation mo;
 
-      mo->type = MO_CALL;
-      mo->insn = insn;
+      mo.type = MO_CALL;
+      mo.insn = insn;
+      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);
+       log_op_type (PATTERN (insn), bb, insn, mo.type, dump_file);
+      VEC_safe_push (micro_operation, heap, VTI (bb)->mos, &mo);
     }
 
-  n1 = VTI (bb)->n_mos;
+  n1 = VEC_length (micro_operation, VTI (bb)->mos);
   /* This will record NEXT_INSN (insn), such that we can
      insert notes before it without worrying about any
      notes that MO_USEs might emit after the insn.  */
   cui.store_p = true;
   note_stores (PATTERN (insn), add_stores, &cui);
-  n2 = VTI (bb)->n_mos - 1;
+  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
+  mos = VEC_address (micro_operation, VTI (bb)->mos);
+
+  /* Order the MO_VAL_USEs first (note_stores does nothing
+     on DEBUG_INSNs, so there are no MO_VAL_LOCs from this
+     insn), then MO_CLOBBERs, then MO_SET/MO_COPY/MO_VAL_SET.  */
+  while (n1 < n2)
+    {
+      while (n1 < n2 && mos[n1].type == MO_VAL_USE)
+       n1++;
+      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;
+       }
+    }
 
-  /* Order the MO_CLOBBERs to be before MO_SETs.  */
+  n2 = VEC_length (micro_operation, VTI (bb)->mos) - 1;
   while (n1 < n2)
     {
-      while (n1 < n2 && VTI (bb)->mos[n1].type == MO_CLOBBER)
+      while (n1 < n2 && mos[n1].type == MO_CLOBBER)
        n1++;
-      while (n1 < n2 && VTI (bb)->mos[n2].type != MO_CLOBBER)
+      while (n1 < n2 && mos[n2].type != MO_CLOBBER)
        n2--;
       if (n1 < n2)
        {
          micro_operation sw;
 
-         sw = VTI (bb)->mos[n1];
-         VTI (bb)->mos[n1] = VTI (bb)->mos[n2];
-         VTI (bb)->mos[n2] = sw;
+         sw = mos[n1];
+         mos[n1] = mos[n2];
+         mos[n2] = sw;
        }
     }
 }
@@ -5237,7 +6167,8 @@ find_src_set_src (dataflow_set *set, rtx src)
 static bool
 compute_bb_dataflow (basic_block bb)
 {
-  int i, n;
+  unsigned int i;
+  micro_operation *mo;
   bool changed;
   dataflow_set old_out;
   dataflow_set *in = &VTI (bb)->in;
@@ -5247,12 +6178,11 @@ compute_bb_dataflow (basic_block bb)
   dataflow_set_copy (&old_out, out);
   dataflow_set_copy (out, in);
 
-  n = VTI (bb)->n_mos;
-  for (i = 0; i < n; i++)
+  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
     {
-      rtx insn = VTI (bb)->mos[i].insn;
+      rtx insn = mo->insn;
 
-      switch (VTI (bb)->mos[i].type)
+      switch (mo->type)
        {
          case MO_CALL:
            dataflow_set_clear_at_call (out);
@@ -5260,7 +6190,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_USE:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_set (out, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
@@ -5271,7 +6201,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_VAL_LOC:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc;
              tree var;
 
@@ -5298,12 +6228,17 @@ compute_bb_dataflow (basic_block bb)
                                     VAR_INIT_STATUS_INITIALIZED, NULL_RTX,
                                     INSERT);
                }
+             else if (!VAR_LOC_UNKNOWN_P (PAT_VAR_LOCATION_LOC (vloc)))
+               set_variable_part (out, PAT_VAR_LOCATION_LOC (vloc),
+                                  dv_from_decl (var), 0,
+                                  VAR_INIT_STATUS_INITIALIZED, NULL_RTX,
+                                  INSERT);
            }
            break;
 
          case MO_VAL_USE:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc, uloc;
 
              vloc = uloc = XEXP (loc, 1);
@@ -5334,11 +6269,13 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_VAL_SET:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc, uloc;
 
-             vloc = uloc = XEXP (loc, 1);
-             val = XEXP (loc, 0);
+             vloc = loc;
+             uloc = XEXP (vloc, 1);
+             val = XEXP (vloc, 0);
+             vloc = uloc;
 
              if (GET_CODE (val) == CONCAT)
                {
@@ -5416,7 +6353,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_SET:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx set_src = NULL;
 
              if (GET_CODE (loc) == SET)
@@ -5436,7 +6373,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_COPY:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              enum var_init_status src_status;
              rtx set_src = NULL;
 
@@ -5467,7 +6404,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_USE_NO_VAR:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_delete (out, loc, false);
@@ -5478,7 +6415,7 @@ compute_bb_dataflow (basic_block bb)
 
          case MO_CLOBBER:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_delete (out, loc, true);
@@ -5488,7 +6425,7 @@ compute_bb_dataflow (basic_block bb)
            break;
 
          case MO_ADJUST:
-           out->stack_adjust += VTI (bb)->mos[i].u.adjust;
+           out->stack_adjust += mo->u.adjust;
            break;
        }
     }
@@ -5526,6 +6463,7 @@ vt_find_locations (void)
   int htabmax = PARAM_VALUE (PARAM_MAX_VARTRACK_SIZE);
   bool success = true;
 
+  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);
@@ -5561,6 +6499,7 @@ vt_find_locations (void)
        {
          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))
            {
              bool changed;
@@ -5706,7 +6645,6 @@ vt_find_locations (void)
     FOR_EACH_BB (bb)
       gcc_assert (VTI (bb)->flooded);
 
-  VEC_free (rtx, heap, values_to_unmark);
   free (bb_order);
   fibheap_delete (worklist);
   fibheap_delete (pending);
@@ -5714,6 +6652,7 @@ vt_find_locations (void)
   sbitmap_free (in_worklist);
   sbitmap_free (in_pending);
 
+  timevar_pop (TV_VAR_TRACKING_DATAFLOW);
   return success;
 }
 
@@ -5759,14 +6698,17 @@ dump_var (variable var)
       const_tree decl = dv_as_decl (var->dv);
 
       if (DECL_NAME (decl))
-       fprintf (dump_file, "  name: %s",
-                IDENTIFIER_POINTER (DECL_NAME (decl)));
+       {
+         fprintf (dump_file, "  name: %s",
+                  IDENTIFIER_POINTER (DECL_NAME (decl)));
+         if (dump_flags & TDF_UID)
+           fprintf (dump_file, "D.%u", DECL_UID (decl));
+       }
+      else if (TREE_CODE (decl) == DEBUG_EXPR_DECL)
+       fprintf (dump_file, "  name: D#%u", DEBUG_TEMP_UID (decl));
       else
        fprintf (dump_file, "  name: D.%u", DECL_UID (decl));
-      if (dump_flags & TDF_UID)
-       fprintf (dump_file, " D.%u\n", DECL_UID (decl));
-      else
-       fprintf (dump_file, "\n");
+      fprintf (dump_file, "\n");
     }
   else
     {
@@ -5777,7 +6719,7 @@ dump_var (variable var)
   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, "      ");
@@ -5838,6 +6780,73 @@ dump_dataflow_sets (void)
     }
 }
 
+/* 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)
+{
+  void **slot;
+  variable empty_var;
+  onepart_enum_t onepart;
+
+  slot = htab_find_slot_with_hash (dropped_values, dv, dv_htab_hash (dv),
+                                  insert);
+
+  if (!slot)
+    return NULL;
+
+  if (*slot)
+    return (variable) *slot;
+
+  gcc_checking_assert (insert == INSERT);
+
+  onepart = dv_onepart_p (dv);
+
+  gcc_checking_assert (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR);
+
+  empty_var = (variable) pool_alloc (onepart_pool (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.  */
 
@@ -5857,20 +6866,81 @@ variable_was_changed (variable var, dataflow_set *set)
                                       var->dv,
                                       hash, INSERT);
 
+      if (*slot)
+       {
+         variable old_var = (variable) *slot;
+         gcc_assert (old_var->in_changed_variables);
+         old_var->in_changed_variables = false;
+         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;
+         onepart_enum_t onepart = var->onepart;
+         variable empty_var = NULL;
+         void **dslot = NULL;
 
-         empty_var = (variable) pool_alloc (dv_pool (var->dv));
-         empty_var->dv = var->dv;
-         empty_var->refcount = 1;
-         empty_var->n_var_parts = 0;
+         if (onepart == ONEPART_VALUE || onepart == ONEPART_DEXPR)
+           {
+             dslot = htab_find_slot_with_hash (dropped_values, var->dv,
+                                               dv_htab_hash (var->dv),
+                                               INSERT);
+             empty_var = (variable) *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 = (variable) pool_alloc (onepart_pool (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;
          *slot = var;
        }
     }
@@ -5905,13 +6975,24 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
 {
   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;
@@ -5921,7 +7002,7 @@ find_variable_location_part (variable var, HOST_WIDE_INT offset,
   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;
@@ -5936,31 +7017,39 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
   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));
+  onepart_enum_t onepart;
 
   var = (variable) *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;
 
   if (!var)
     {
       /* Create new variable information.  */
-      var = (variable) pool_alloc (dv_pool (dv));
+      var = (variable) pool_alloc (onepart_pool (onepart));
       var->dv = dv;
       var->refcount = 1;
       var->n_var_parts = 1;
-      var->var_part[0].offset = offset;
+      var->onepart = onepart;
+      var->in_changed_variables = false;
+      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;
       pos = 0;
       nextp = &var->var_part[0].loc_chain;
-      if (emit_notes && dv_is_value_p (dv))
-       add_cselib_value_chains (dv);
     }
   else if (onepart)
     {
@@ -6050,7 +7139,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
       if (r == 0)
        return slot;
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          slot = unshare_variable (set, slot, var, initialized);
          var = (variable)*slot;
@@ -6089,7 +7178,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
          else
            {
              /* We have to make a copy of a shared variable.  */
-             if (var->refcount > 1 || shared_hash_shared (set->vars))
+             if (shared_var_p (var, set->vars))
                {
                  slot = unshare_variable (set, slot, var, initialized);
                  var = (variable)*slot;
@@ -6101,7 +7190,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
          /* We have not found the location part, new one will be created.  */
 
          /* We have to make a copy of the shared variable.  */
-         if (var->refcount > 1 || shared_hash_shared (set->vars))
+         if (shared_var_p (var, set->vars))
            {
              slot = unshare_variable (set, slot, var, initialized);
              var = (variable)*slot;
@@ -6110,7 +7199,7 @@ set_slot_part (dataflow_set *set, rtx loc, void **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.  */
@@ -6118,7 +7207,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
            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;
        }
@@ -6138,6 +7228,8 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
                initialized = node->init;
              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;
              pool_free (loc_chain_pool, node);
              *nextp = next;
              break;
@@ -6157,15 +7249,9 @@ set_slot_part (dataflow_set *set, rtx loc, void **slot,
   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)
-    {
-      var->var_part[pos].cur_loc = loc;
-      variable_was_changed (var, set);
-    }
+    variable_was_changed (var, set);
 
   return slot;
 }
@@ -6192,7 +7278,7 @@ set_variable_part (dataflow_set *set, rtx loc,
       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
@@ -6273,7 +7359,7 @@ clobber_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
   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
@@ -6292,8 +7378,9 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
       location_chain node, next;
       location_chain *nextp;
       bool changed;
+      rtx cur_loc;
 
-      if (var->refcount > 1 || shared_hash_shared (set->vars))
+      if (shared_var_p (var, set->vars))
        {
          /* If the variable contains the location part we have to
             make a copy of the variable.  */
@@ -6312,7 +7399,13 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
            }
        }
 
+      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;
       for (node = *nextp; node; node = next)
        {
@@ -6321,8 +7414,16 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
               && 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 (cur_loc == node->loc)
+               {
+                 changed = true;
+                 var->var_part[pos].cur_loc = NULL;
+                 if (pos == 0 && var->onepart && VAR_LOC_1PAUX (var))
+                   VAR_LOC_FROM (var) = NULL;
+               }
              pool_free (loc_chain_pool, node);
              *nextp = next;
              break;
@@ -6331,28 +7432,10 @@ delete_slot_part (dataflow_set *set, rtx loc, void **slot,
            nextp = &node->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[pos].cur_loc
-         && ((REG_P (loc)
-              && REG_P (var->var_part[pos].cur_loc)
-              && REGNO (loc) == REGNO (var->var_part[pos].cur_loc))
-             || rtx_equal_p (loc, var->var_part[pos].cur_loc)))
-       {
-         changed = true;
-         if (var->var_part[pos].loc_chain)
-           var->var_part[pos].cur_loc = var->var_part[pos].loc_chain->loc;
-       }
-      else
-       changed = false;
-
       if (var->var_part[pos].loc_chain == NULL)
        {
-         gcc_assert (changed);
+         changed = true;
          var->n_var_parts--;
-         if (emit_notes && var->n_var_parts == 0 && dv_is_value_p (var->dv))
-           remove_cselib_value_chains (var->dv);
          while (pos < var->n_var_parts)
            {
              var->var_part[pos] = var->var_part[pos + 1];
@@ -6378,32 +7461,387 @@ delete_variable_part (dataflow_set *set, rtx loc, decl_or_value dv,
   if (!slot)
     return;
 
-  slot = delete_slot_part (set, loc, slot, offset);
+  delete_slot_part (set, loc, slot, offset);
 }
 
-/* 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.  */
+DEF_VEC_P (variable);
+DEF_VEC_ALLOC_P (variable, heap);
 
-static rtx
-vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
-{
-  htab_t vars = (htab_t)data;
-  decl_or_value dv;
-  variable var;
-  location_chain loc;
-  rtx result, subreg, xret;
+DEF_VEC_ALLOC_P_STACK (rtx);
+#define VEC_rtx_stack_alloc(alloc) VEC_stack_alloc (rtx, alloc)
 
-  switch (GET_CODE (x))
+/* 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;
+
+  /* Stack of values and debug_exprs under expansion, and their
+     children.  */
+  VEC (rtx, stack) *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.  */
+  VEC (rtx, stack) *pending;
+
+  /* The maximum depth among the sub-expressions under expansion.
+     Zero indicates no expansion so far.  */
+  int 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
+                      || VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+  if (VAR_LOC_1PAUX (var)
+      && VEC_space (loc_exp_dep, VAR_LOC_DEP_VEC (var), count))
+    return;
+
+  allocsize = offsetof (struct onepart_aux, deps)
+    + VEC_embedded_size (loc_exp_dep, count);
+
+  if (VAR_LOC_1PAUX (var))
     {
-    case SUBREG:
-      subreg = SUBREG_REG (x);
+      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) = 0;
+    }
+  VEC_embedded_init (loc_exp_dep, VAR_LOC_DEP_VEC (var), 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 (!VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)))
+    {
+      loc_exp_dep *led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+      if (led->next)
+       led->next->pprev = led->pprev;
+      if (led->pprev)
+       *led->pprev = led->next;
+      VEC_pop (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+    }
+}
+
+/* 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, htab_t 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 = (variable) htab_find_with_hash (vars, 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;
+
+  VEC_quick_push (loc_exp_dep, VAR_LOC_DEP_VEC (var), NULL);
+  led = VEC_last (loc_exp_dep, VAR_LOC_DEP_VEC (var));
+  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, htab_t vars)
+{
+  bool pending_recursion = false;
+
+  gcc_checking_assert (VEC_empty (loc_exp_dep, VAR_LOC_DEP_VEC (var)));
+
+  /* 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, htab_t 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 if (!dv_changed_p (dv))
+       continue;
+
+      var = (variable) htab_find_with_hash (vars, 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);
 
-      if (GET_CODE (SUBREG_REG (x)) != VALUE)
-       return x;
+/* Return the combined depth, when one sub-expression evaluated to
+   BEST_DEPTH and the previous known depth was SAVED_DEPTH.  */
 
+static inline int
+update_depth (int saved_depth, int best_depth)
+{
+  /* If we didn't find anything, stick with what we had.  */
+  if (!best_depth)
+    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)
+    return best_depth;
+
+  if (saved_depth < best_depth)
+    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;
+  int depth = 0, saved_depth = elcd->depth;
+
+  /* Clear all backlinks pointing at this, so that we're not notified
+     while we're active.  */
+  loc_exp_dep_clear (var);
+
+  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
+    = VEC_length (rtx, elcd->expanding);
+
+  /* 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 || (GET_CODE (loc->loc) == ENTRY_VALUE && cloc))
+       {
+         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 = 0;
+      result = cselib_expand_value_rtx_cb (loc_from, regs, EXPR_DEPTH,
+                                          vt_expand_loc_callback, data);
+      last_child = VEC_length (rtx, elcd->expanding);
+
+      if (result)
+       {
+         depth = elcd->depth;
+
+         gcc_checking_assert (depth || result_first_child == last_child);
+
+         if (last_child - result_first_child != 1)
+           depth++;
+
+         if (depth <= EXPR_USE_DEPTH)
+           break;
+
+         result = NULL;
+       }
+
+      /* Set it up in case we leave the loop.  */
+      depth = 0;
+      loc_from = NULL;
+      result_first_child = first_child;
+    }
+
+  /* Register all encountered dependencies as active.  */
+  pending_recursion = loc_exp_dep_set
+    (var, result, VEC_address (rtx, elcd->expanding) + result_first_child,
+     last_child - result_first_child, elcd->vars);
+
+  VEC_truncate (rtx, elcd->expanding, 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 == !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 ATTRIBUTE_UNUSED,
+                       void *data)
+{
+  struct expand_loc_callback_data *elcd
+    = (struct expand_loc_callback_data *) data;
+  decl_or_value dv;
+  variable var;
+  rtx result, subreg;
+  bool pending_recursion = false;
+  bool from_empty = false;
+
+  switch (GET_CODE (x))
+    {
+    case SUBREG:
       subreg = cselib_expand_value_rtx_cb (SUBREG_REG (x), regs,
-                                          max_depth - 1,
+                                          EXPR_DEPTH,
                                           vt_expand_loc_callback, data);
 
       if (!subreg)
@@ -6415,70 +7853,179 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
 
       /* Invalid SUBREGs are ok in debug info.  ??? We could try
         alternate expansions for the VALUE as well.  */
-      if (!result && (REG_P (subreg) || MEM_P (subreg)))
+      if (!result)
        result = gen_rtx_raw_SUBREG (GET_MODE (x), subreg, SUBREG_BYTE (x));
 
       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;
+  VEC_safe_push (rtx, stack, elcd->expanding, x);
 
-  var = (variable) htab_find_with_hash (vars, dv, dv_htab_hash (dv));
+  /* 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 = (variable) htab_find_with_hash (elcd->vars, dv, dv_htab_hash (dv));
 
   if (!var)
-    return xret;
+    {
+      from_empty = true;
+      var = variable_from_dropped (dv, INSERT);
+    }
 
-  if (var->n_var_parts == 0)
-    return xret;
+  gcc_checking_assert (var);
 
-  gcc_assert (var->n_var_parts == 1);
+  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);
+
+      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;
+  /* 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);
 
-  for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
+  if (pending_recursion)
     {
-      result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
-                                          vt_expand_loc_callback, vars);
+      gcc_checking_assert (!result);
+      VEC_safe_push (rtx, stack, elcd->pending, x);
+    }
+  else
+    {
+      NO_LOC_P (x) = !result;
+      VALUE_RECURSED_INTO (x) = false;
+      set_dv_changed (dv, false);
+
       if (result)
-       break;
+       notify_dependents_of_resolved_value (var, elcd->vars);
     }
 
-  VALUE_RECURSED_INTO (x) = false;
-  if (result)
-    return result;
-  else
-    return xret;
+  return result;
 }
 
-/* Expand VALUEs in LOC, using VARS as well as cselib's equivalence
-   tables.  */
+/* 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, stack) *pending)
+{
+  while (!VEC_empty (rtx, pending))
+    {
+      rtx x = VEC_pop (rtx, pending);
+      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).expanding = VEC_alloc (rtx, stack, 4);               \
+      (d).pending = VEC_alloc (rtx, stack, 4);                 \
+      (d).depth = 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);      \
+      VEC_free (rtx, stack, (d).pending);                      \
+      VEC_free (rtx, stack, (d).expanding);                    \
+                                                               \
+      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)
 {
+  struct expand_loc_callback_data data;
+  rtx result;
+
   if (!MAY_HAVE_DEBUG_INSNS)
     return loc;
 
-  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 5,
-                                   vt_expand_loc_callback, vars);
+  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, htab_t vars)
+{
+  struct expand_loc_callback_data data;
+  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 (VEC_empty (rtx, data.expanding));
 
-  if (loc && MEM_P (loc))
-    loc = targetm.delegitimize_address (loc);
+  FINI_ELCD (data, loc);
 
   return loc;
 }
@@ -6494,7 +8041,7 @@ emit_note_insn_var_location (void **varp, void *data)
   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;
+  rtx note, note_vl;
   int i, j, n_var_parts;
   bool complete;
   enum var_init_status initialized = VAR_INIT_STATUS_UNINITIALIZED;
@@ -6503,56 +8050,83 @@ emit_note_insn_var_location (void **varp, void *data)
   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 clear;
+  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 clear;
-
-  gcc_assert (decl);
-
   complete = true;
   last_limit = 0;
   n_var_parts = 0;
+  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;
       rtx loc2;
+      HOST_WIDE_INT offset;
 
-      if (last_limit < var->var_part[i].offset)
+      if (i == 0 && var->onepart)
        {
-         complete = false;
-         break;
+         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->var_part[i].offset)
-       continue;
-      offsets[n_var_parts] = var->var_part[i].offset;
-      loc2 = vt_expand_loc (var->var_part[i].loc_chain->loc, 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);
+         if (!var->var_part[i].cur_loc)
+           {
+             complete = false;
+             continue;
+           }
+         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);
+         loc2 = var->var_part[i].cur_loc;
+       }
+
+      offsets[n_var_parts] = offset;
       if (!loc2)
        {
          complete = false;
          continue;
        }
       loc[n_var_parts] = loc2;
-      mode = GET_MODE (var->var_part[i].loc_chain->loc);
-      initialized = var->var_part[i].loc_chain->init;
+      mode = GET_MODE (var->var_part[i].cur_loc);
+      if (mode == VOIDmode && var->onepart)
+       mode = DECL_MODE (decl);
       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
-         && mode == GET_MODE (var->var_part[j].loc_chain->loc)
+         && 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]))
-         && (loc2 = vt_expand_loc (var->var_part[j].loc_chain->loc, vars))
-         && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2)
-         && 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;
 
@@ -6611,31 +8185,24 @@ emit_note_insn_var_location (void **varp, void *data)
   if ((unsigned HOST_WIDE_INT) last_limit < TREE_INT_CST_LOW (type_size_unit))
     complete = false;
 
-  if (where != EMIT_NOTE_BEFORE_INSN)
-    {
-      note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
-      if (where == EMIT_NOTE_AFTER_CALL_INSN)
-       NOTE_DURING_CALL_P (note) = true;
-    }
-  else
-    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
-
   if (! flag_var_tracking_uninit)
     initialized = VAR_INIT_STATUS_INITIALIZED;
 
+  note_vl = NULL_RTX;
   if (!complete)
-    {
-      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      NULL_RTX, (int) initialized);
-    }
+    note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, NULL_RTX,
+                                   (int) initialized);
   else if (n_var_parts == 1)
     {
-      rtx expr_list
-       = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
+      rtx expr_list;
 
-      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      expr_list,
-                                                      (int) initialized);
+      if (offsets[0] || GET_CODE (loc[0]) == PARALLEL)
+       expr_list = gen_rtx_EXPR_LIST (VOIDmode, loc[0], GEN_INT (offsets[0]));
+      else
+       expr_list = loc[0];
+
+      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl, expr_list,
+                                     (int) initialized);
     }
   else if (n_var_parts)
     {
@@ -6647,89 +8214,181 @@ emit_note_insn_var_location (void **varp, void *data)
 
       parallel = gen_rtx_PARALLEL (VOIDmode,
                                   gen_rtvec_v (n_var_parts, loc));
-      NOTE_VAR_LOCATION (note) = gen_rtx_VAR_LOCATION (VOIDmode, decl,
-                                                      parallel,
-                                                      (int) initialized);
+      note_vl = gen_rtx_VAR_LOCATION (VOIDmode, decl,
+                                     parallel, (int) initialized);
+    }
+
+  if (where != EMIT_NOTE_BEFORE_INSN)
+    {
+      note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
+      if (where == EMIT_NOTE_AFTER_CALL_INSN)
+       NOTE_DURING_CALL_P (note) = true;
     }
+  else
+    {
+      /* Make sure that the call related notes come first.  */
+      while (NEXT_INSN (insn)
+            && NOTE_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_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);
+  gcc_assert (var->in_changed_variables);
+  var->in_changed_variables = false;
   htab_clear_slot (changed_variables, varp);
 
   /* Continue traversing the hash table.  */
   return 1;
 }
 
-DEF_VEC_P (variable);
-DEF_VEC_ALLOC_P (variable, heap);
+/* While traversing changed_variables, push onto DATA (a stack of RTX
+   values) entries that aren't user variables.  */
+
+static int
+values_to_stack (void **slot, void *data)
+{
+  VEC (rtx, stack) **changed_values_stack = (VEC (rtx, stack) **)data;
+  variable var = (variable) *slot;
 
-/* Stack of variable_def pointers that need processing with
-   check_changed_vars_2.  */
+  if (var->onepart == ONEPART_VALUE)
+    VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_value (var->dv));
+  else if (var->onepart == ONEPART_DEXPR)
+    VEC_safe_push (rtx, stack, *changed_values_stack,
+                  DECL_RTL_KNOWN_SET (dv_as_decl (var->dv)));
 
-static VEC (variable, heap) *changed_variables_stack;
+  return 1;
+}
 
-/* Populate changed_variables_stack with variable_def pointers
-   that need variable_was_changed called on them.  */
+/* 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);
+  void **slot;
+  variable var;
 
-static int
-check_changed_vars_1 (void **slot, void *data)
+  slot = htab_find_slot_with_hash (changed_variables,
+                                  dv, dv_htab_hash (dv), NO_INSERT);
+  var = (variable) *slot;
+  var->in_changed_variables = false;
+  htab_clear_slot (changed_variables, 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
+notify_dependents_of_changed_value (rtx val, htab_t htab,
+                                   VEC (rtx, stack) **changed_values_stack)
 {
-  variable var = (variable) *slot;
-  htab_t htab = (htab_t) data;
+  void **slot;
+  variable var;
+  loc_exp_dep *led;
+  decl_or_value dv = dv_from_rtx (val);
+
+  slot = htab_find_slot_with_hash (changed_variables,
+                                  dv, dv_htab_hash (dv), NO_INSERT);
+  if (!slot)
+    slot = htab_find_slot_with_hash (htab,
+                                    dv, dv_htab_hash (dv), NO_INSERT);
+  if (!slot)
+    slot = htab_find_slot_with_hash (dropped_values,
+                                    dv, dv_htab_hash (dv), NO_INSERT);
+  var = (variable) *slot;
 
-  if (dv_is_value_p (var->dv))
+  while ((led = VAR_LOC_DEP_LST (var)))
     {
-      value_chain vc
-       = (value_chain) htab_find_with_hash (value_chains, var->dv,
-                                            dv_htab_hash (var->dv));
+      decl_or_value ldv = led->dv;
+      void **islot;
+      variable ivar;
 
-      if (vc == NULL)
-       return 1;
-      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)
-             VEC_safe_push (variable, heap, changed_variables_stack,
-                            vcvar);
-         }
+      /* 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);
+         VEC_safe_push (rtx, stack, *changed_values_stack, dv_as_rtx (ldv));
+         break;
+
+       default:
+         islot = htab_find_slot_with_hash (htab, ldv, dv_htab_hash (ldv),
+                                           NO_INSERT);
+         ivar = (variable) *islot;
+         gcc_checking_assert (!VAR_LOC_DEP_LST (ivar));
+         variable_was_changed (ivar, NULL);
+         break;
+       }
     }
-  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.  */
+/* 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)
+process_changed_values (htab_t htab)
 {
-  variable_was_changed (var, NULL);
-  if (dv_is_value_p (var->dv))
+  int i, n;
+  rtx val;
+  VEC (rtx, stack) *changed_values_stack = VEC_alloc (rtx, stack, 20);
+
+  /* Move values from changed_variables to changed_values_stack.  */
+  htab_traverse (changed_variables, values_to_stack, &changed_values_stack);
+
+  /* Back-propagate change notifications in values while popping
+     them from the stack.  */
+  for (n = i = VEC_length (rtx, changed_values_stack);
+       i > 0; i = VEC_length (rtx, changed_values_stack))
     {
-      value_chain vc
-       = (value_chain) htab_find_with_hash (value_chains, var->dv,
-                                            dv_htab_hash (var->dv));
+      val = VEC_pop (rtx, changed_values_stack);
+      notify_dependents_of_changed_value (val, htab, &changed_values_stack);
 
-      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)
-             check_changed_vars_2 (vcvar, htab);
-         }
+      /* 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--;
+       }
     }
+
+  VEC_free (rtx, stack, changed_values_stack);
 }
 
 /* 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,
@@ -6742,15 +8401,7 @@ emit_notes_for_changes (rtx insn, enum emit_note_where where,
     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);
-    }
+    process_changed_values (htab);
 
   data.insn = insn;
   data.where = where;
@@ -6775,49 +8426,59 @@ emit_notes_for_differences_1 (void **slot, void *data)
   if (!new_var)
     {
       /* Variable has disappeared.  */
-      variable empty_var;
+      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));
+           }
+       }
 
-      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;
-      if (dv_onepart_p (old_var->dv))
+      if (!empty_var)
        {
-         location_chain lc;
+         empty_var = (variable) pool_alloc (onepart_pool (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;
+       }
 
-         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);
-         if (dv_is_value_p (old_var->dv))
-           remove_cselib_value_chains (old_var->dv);
+      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;
     }
-  else if (variable_different_p (old_var, new_var, true))
+  /* Update cur_loc and one-part auxiliary data, before new_var goes
+     through variable_was_changed.  */
+  if (old_var != new_var && new_var->onepart)
     {
-      if (dv_onepart_p (old_var->dv))
-       {
-         location_chain lc1, lc2;
-
-         gcc_assert (old_var->n_var_parts == 1);
-         gcc_assert (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);
+      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))
+    variable_was_changed (new_var, NULL);
 
   /* Continue traversing the hash table.  */
   return 1;
@@ -6837,17 +8498,9 @@ emit_notes_for_differences_2 (void **slot, void *data)
                                            dv_htab_hash (new_var->dv));
   if (!old_var)
     {
-      /* 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);
-         if (dv_is_value_p (new_var->dv))
-           add_cselib_value_chains (new_var->dv);
-       }
+      int i;
+      for (i = 0; i < new_var->n_var_parts; i++)
+       new_var->var_part[i].cur_loc = NULL;
       variable_was_changed (new_var, NULL);
     }
 
@@ -6871,30 +8524,77 @@ emit_notes_for_differences (rtx insn, dataflow_set *old_set,
   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
+next_non_note_insn_var_location (rtx 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
 emit_notes_in_bb (basic_block bb, dataflow_set *set)
 {
-  int i;
+  unsigned int i;
+  micro_operation *mo;
 
   dataflow_set_clear (set);
   dataflow_set_copy (set, &VTI (bb)->in);
 
-  for (i = 0; i < VTI (bb)->n_mos; i++)
+  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
     {
-      rtx insn = VTI (bb)->mos[i].insn;
+      rtx insn = mo->insn;
+      rtx next_insn = next_non_note_insn_var_location (insn);
 
-      switch (VTI (bb)->mos[i].type)
+      switch (mo->type)
        {
          case MO_CALL:
            dataflow_set_clear_at_call (set);
            emit_notes_for_changes (insn, EMIT_NOTE_AFTER_CALL_INSN, set->vars);
+           {
+             rtx arguments = mo->u.loc, *p = &arguments, 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:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_set (set, loc, VAR_INIT_STATUS_UNINITIALIZED, NULL);
@@ -6907,7 +8607,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
          case MO_VAL_LOC:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc;
              tree var;
 
@@ -6934,6 +8634,11 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
                                     VAR_INIT_STATUS_INITIALIZED, NULL_RTX,
                                     INSERT);
                }
+             else if (!VAR_LOC_UNKNOWN_P (PAT_VAR_LOCATION_LOC (vloc)))
+               set_variable_part (set, PAT_VAR_LOCATION_LOC (vloc),
+                                  dv_from_decl (var), 0,
+                                  VAR_INIT_STATUS_INITIALIZED, NULL_RTX,
+                                  INSERT);
 
              emit_notes_for_changes (insn, EMIT_NOTE_AFTER_INSN, set->vars);
            }
@@ -6941,7 +8646,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
          case MO_VAL_USE:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc, uloc;
 
              vloc = uloc = XEXP (loc, 1);
@@ -6974,11 +8679,13 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
          case MO_VAL_SET:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx val, vloc, uloc;
 
-             vloc = uloc = XEXP (loc, 1);
-             val = XEXP (loc, 0);
+             vloc = loc;
+             uloc = XEXP (vloc, 1);
+             val = XEXP (vloc, 0);
+             vloc = uloc;
 
              if (GET_CODE (val) == CONCAT)
                {
@@ -7046,14 +8753,14 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
              val_store (set, val, vloc, insn, 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;
 
          case MO_SET:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              rtx set_src = NULL;
 
              if (GET_CODE (loc) == SET)
@@ -7069,14 +8776,14 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
                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;
 
          case MO_COPY:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
              enum var_init_status src_status;
              rtx set_src = NULL;
 
@@ -7094,14 +8801,14 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
              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;
 
          case MO_USE_NO_VAR:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_delete (set, loc, false);
@@ -7114,20 +8821,20 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
 
          case MO_CLOBBER:
            {
-             rtx loc = VTI (bb)->mos[i].u.loc;
+             rtx loc = mo->u.loc;
 
              if (REG_P (loc))
                var_reg_delete (set, loc, true);
              else
                var_mem_delete (set, loc, true);
 
-             emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+             emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
                                      set->vars);
            }
            break;
 
          case MO_ADJUST:
-           set->stack_adjust += VTI (bb)->mos[i].u.adjust;
+           set->stack_adjust += mo->u.adjust;
            break;
        }
     }
@@ -7153,7 +8860,9 @@ vt_emit_notes (void)
   emit_notes = true;
 
   if (MAY_HAVE_DEBUG_INSNS)
-    changed_variables_stack = VEC_alloc (variable, heap, 40);
+    dropped_values = htab_create (cselib_get_next_uid () * 2,
+                                 variable_htab_hash, variable_htab_eq,
+                                 variable_htab_free);
 
   dataflow_set_init (&cur);
 
@@ -7174,13 +8883,11 @@ vt_emit_notes (void)
   htab_traverse (shared_hash_htab (cur.vars),
                 emit_notes_for_differences_1,
                 shared_hash_htab (empty_shared_hash));
-  if (MAY_HAVE_DEBUG_INSNS)
-    gcc_assert (htab_elements (value_chains) == 0);
 #endif
   dataflow_set_destroy (&cur);
 
   if (MAY_HAVE_DEBUG_INSNS)
-    VEC_free (variable, heap, changed_variables_stack);
+    htab_delete (dropped_values);
 
   emit_notes = false;
 }
@@ -7212,145 +8919,346 @@ vt_get_decl_and_offset (rtx rtl, tree *declp, HOST_WIDE_INT *offsetp)
   return false;
 }
 
-/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK.  */
+/* Record the value for the ENTRY_VALUE of RTL as a global equivalence
+   of VAL.  */
 
 static void
-vt_add_function_parameters (void)
+record_entry_value (cselib_val *val, rtx rtl)
 {
-  tree parm;
+  rtx ev = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
 
-  for (parm = DECL_ARGUMENTS (current_function_decl);
-       parm; parm = TREE_CHAIN (parm))
-    {
-      rtx decl_rtl = DECL_RTL_IF_SET (parm);
-      rtx incoming = DECL_INCOMING_RTL (parm);
-      tree decl;
-      enum machine_mode mode;
-      HOST_WIDE_INT offset;
-      dataflow_set *out;
-      decl_or_value dv;
+  ENTRY_VALUE_EXP (ev) = rtl;
 
-      if (TREE_CODE (parm) != PARM_DECL)
-       continue;
+  cselib_add_permanent_equiv (val, ev, get_insns ());
+}
 
-      if (!DECL_NAME (parm))
-       continue;
+/* Insert function parameter PARM in IN and OUT sets of ENTRY_BLOCK.  */
+
+static void
+vt_add_function_parameter (tree parm)
+{
+  rtx decl_rtl = DECL_RTL_IF_SET (parm);
+  rtx incoming = DECL_INCOMING_RTL (parm);
+  tree decl;
+  enum machine_mode mode;
+  HOST_WIDE_INT offset;
+  dataflow_set *out;
+  decl_or_value dv;
+
+  if (TREE_CODE (parm) != PARM_DECL)
+    return;
 
-      if (!decl_rtl || !incoming)
-       continue;
+  if (!decl_rtl || !incoming)
+    return;
 
-      if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
-       continue;
+  if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
+    return;
 
-      if (!vt_get_decl_and_offset (incoming, &decl, &offset))
+  /* If there is a DRAP register, rewrite the incoming location of parameters
+     passed on the stack into MEMs based on the argument pointer, as the DRAP
+     register can be reused for other purposes and we do not track locations
+     based on generic registers.  But the prerequisite is that this argument
+     pointer be also the virtual CFA pointer, see vt_initialize.  */
+  if (MEM_P (incoming)
+      && stack_realign_drap
+      && arg_pointer_rtx == cfa_base_rtx
+      && (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 (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 && !current_function_uses_only_leaf_regs)
+    {
+      if (REG_P (incoming)
+         && HARD_REGISTER_P (incoming)
+         && OUTGOING_REGNO (REGNO (incoming)) != REGNO (incoming))
        {
-         if (REG_P (incoming) || 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
+         parm_reg_t *p
+           = VEC_safe_push (parm_reg_t, gc, windowed_parm_regs, NULL);
+         p->incoming = incoming;
+         incoming
+           = gen_rtx_REG_offset (incoming, GET_MODE (incoming),
+                                 OUTGOING_REGNO (REGNO (incoming)), 0);
+         p->outgoing = incoming;
+       }
+      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))
            {
-             if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
-               continue;
-             offset += byte_lowpart_offset (GET_MODE (incoming),
-                                            GET_MODE (decl_rtl));
+             parm_reg_t *p
+               = VEC_safe_push (parm_reg_t, gc, windowed_parm_regs, NULL);
+             p->incoming = reg;
+             reg = gen_raw_REG (GET_MODE (reg), OUTGOING_REGNO (REGNO (reg)));
+             p->outgoing = reg;
+             incoming = replace_equiv_address_nv (incoming, reg);
            }
        }
+    }
+#endif
 
-      if (!decl)
-       continue;
-
-      if (parm != decl)
+  if (!vt_get_decl_and_offset (incoming, &decl, &offset))
+    {
+      if (REG_P (incoming) || MEM_P (incoming))
        {
-         /* 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));
+         /* This means argument is passed by invisible reference.  */
          offset = 0;
+         decl = parm;
+         incoming = gen_rtx_MEM (GET_MODE (decl_rtl), incoming);
+       }
+      else
+       {
+         if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
+           return;
+         offset += byte_lowpart_offset (GET_MODE (incoming),
+                                        GET_MODE (decl_rtl));
        }
+    }
 
-      if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
-       continue;
+  if (!decl)
+    return;
 
-      out = &VTI (ENTRY_BLOCK_PTR)->out;
+  if (parm != decl)
+    {
+      /* Assume that DECL_RTL was a pseudo that got spilled to
+        memory.  The spill slot sharing code will force the
+        memory to reference spill_slot_decl (%sfp), so we don't
+        match above.  That's ok, the pseudo must have referenced
+        the entire parameter, so just reset OFFSET.  */
+      gcc_assert (decl == get_spill_slot_decl (false));
+      offset = 0;
+    }
 
-      dv = dv_from_decl (parm);
+  if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+    return;
 
-      if (target_for_debug_bind (parm)
-         /* We can't deal with these right now, because this kind of
-            variable is single-part.  ??? We could handle parallels
-            that describe multiple locations for the same single
-            value, but ATM we don't.  */
-         && GET_CODE (incoming) != PARALLEL)
-       {
-         cselib_val *val;
+  out = &VTI (ENTRY_BLOCK_PTR)->out;
 
-         /* ??? We shouldn't ever hit this, but it may happen because
-            arguments passed by invisible reference aren't dealt with
-            above: incoming-rtl will have Pmode rather than the
-            expected mode for the type.  */
-         if (offset)
-           continue;
+  dv = dv_from_decl (parm);
 
-         val = cselib_lookup (var_lowpart (mode, incoming), mode, true);
+  if (target_for_debug_bind (parm)
+      /* We can't deal with these right now, because this kind of
+        variable is single-part.  ??? We could handle parallels
+        that describe multiple locations for the same single
+        value, but ATM we don't.  */
+      && GET_CODE (incoming) != PARALLEL)
+    {
+      cselib_val *val;
 
-         /* ??? Float-typed values in memory are not handled by
-            cselib.  */
-         if (val)
-           {
-             cselib_preserve_value (val);
-             set_variable_part (out, val->val_rtx, dv, offset,
-                                VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
-             dv = dv_from_value (val->val_rtx);
-           }
-       }
+      /* ??? We shouldn't ever hit this, but it may happen because
+        arguments passed by invisible reference aren't dealt with
+        above: incoming-rtl will have Pmode rather than the
+        expected mode for the type.  */
+      if (offset)
+       return;
+
+      val = cselib_lookup_from_insn (var_lowpart (mode, incoming), mode, true,
+                                    VOIDmode, get_insns ());
 
-      if (REG_P (incoming))
+      /* ??? Float-typed values in memory are not handled by
+        cselib.  */
+      if (val)
        {
-         incoming = var_lowpart (mode, incoming);
-         gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
-         attrs_list_insert (&out->regs[REGNO (incoming)], dv, offset,
-                            incoming);
-         set_variable_part (out, incoming, dv, offset,
+         preserve_value (val);
+         set_variable_part (out, val->val_rtx, dv, offset,
                             VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+         dv = dv_from_value (val->val_rtx);
        }
-      else if (MEM_P (incoming))
+    }
+
+  if (REG_P (incoming))
+    {
+      incoming = var_lowpart (mode, incoming);
+      gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
+      attrs_list_insert (&out->regs[REGNO (incoming)], dv, offset,
+                        incoming);
+      set_variable_part (out, incoming, dv, offset,
+                        VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+      if (dv_is_value_p (dv))
        {
-         incoming = var_lowpart (mode, incoming);
-         set_variable_part (out, incoming, dv, offset,
-                            VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+         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))))
+           {
+             enum 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 (MEM_P (incoming))
+    {
+      incoming = var_lowpart (mode, incoming);
+      set_variable_part (out, incoming, dv, offset,
+                        VAR_INIT_STATUS_INITIALIZED, NULL, INSERT);
+    }
+}
+
+/* Insert function parameters to IN and OUT sets of ENTRY_BLOCK.  */
+
+static void
+vt_add_function_parameters (void)
+{
+  tree parm;
+
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm; parm = DECL_CHAIN (parm))
+    vt_add_function_parameter (parm);
+
+  if (DECL_HAS_VALUE_EXPR_P (DECL_RESULT (current_function_decl)))
+    {
+      tree vexpr = DECL_VALUE_EXPR (DECL_RESULT (current_function_decl));
+
+      if (TREE_CODE (vexpr) == INDIRECT_REF)
+       vexpr = TREE_OPERAND (vexpr, 0);
+
+      if (TREE_CODE (vexpr) == PARM_DECL
+         && DECL_ARTIFICIAL (vexpr)
+         && !DECL_IGNORED_P (vexpr)
+         && DECL_NAMELESS (vexpr))
+       vt_add_function_parameter (vexpr);
+    }
+}
 
-  if (MAY_HAVE_DEBUG_INSNS)
+/* 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
+   ensure it isn't flushed during cselib_reset_table.
+   Can be called only if frame_pointer_rtx resp. arg_pointer_rtx
+   has been eliminated.  */
+
+static void
+vt_init_cfa_base (void)
+{
+  cselib_val *val;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+  cfa_base_rtx = frame_pointer_rtx;
+  cfa_base_offset = -FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
+  cfa_base_rtx = arg_pointer_rtx;
+  cfa_base_offset = -ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
+  if (cfa_base_rtx == hard_frame_pointer_rtx
+      || !fixed_regs[REGNO (cfa_base_rtx)])
     {
-      cselib_preserve_only_values (true);
-      cselib_reset_table (cselib_get_next_uid ());
+      cfa_base_rtx = NULL_RTX;
+      return;
     }
+  if (!MAY_HAVE_DEBUG_INSNS)
+    return;
+
+  /* Tell alias analysis that cfa_base_rtx should share
+     find_base_term value with stack pointer or hard frame pointer.  */
+  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
    and parse the RTL to get the micro operations.  */
 
-static void
+static bool
 vt_initialize (void)
 {
-  basic_block bb;
+  basic_block bb, prologue_bb = single_succ (ENTRY_BLOCK_PTR);
+  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);
+  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);
+
+  /* Init the IN and OUT sets.  */
+  FOR_ALL_BB (bb)
+    {
+      VTI (bb)->visited = false;
+      VTI (bb)->flooded = false;
+      dataflow_set_init (&VTI (bb)->in);
+      dataflow_set_init (&VTI (bb)->out);
+      VTI (bb)->permp = NULL;
+    }
+
   if (MAY_HAVE_DEBUG_INSNS)
     {
-      cselib_init (true);
+      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);
     }
   else
     {
@@ -7358,185 +9266,217 @@ vt_initialize (void)
       valvar_pool = NULL;
     }
 
-  FOR_EACH_BB (bb)
+  /* 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 insn;
-      HOST_WIDE_INT pre, post = 0;
-      int count;
-      unsigned int next_uid_before = cselib_get_next_uid ();
-      unsigned int next_uid_after = next_uid_before;
+      rtx reg, elim;
 
-      if (MAY_HAVE_DEBUG_INSNS)
+      if (!vt_stack_adjustments ())
+       return false;
+
+#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)
        {
-         cselib_record_sets_hook = count_with_sets;
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "first value: %i\n",
-                    cselib_get_next_uid ());
+         if (GET_CODE (elim) == PLUS)
+           elim = XEXP (elim, 0);
+         if (elim == stack_pointer_rtx)
+           vt_init_cfa_base ();
        }
+    }
+  else if (!crtl->stack_realign_tried)
+    {
+      rtx reg, elim;
 
-      /* Count the number of micro operations.  */
-      VTI (bb)->n_mos = 0;
-      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
-          insn = NEXT_INSN (insn))
+#ifdef FRAME_POINTER_CFA_OFFSET
+      reg = frame_pointer_rtx;
+      fp_cfa_offset = FRAME_POINTER_CFA_OFFSET (current_function_decl);
+#else
+      reg = arg_pointer_rtx;
+      fp_cfa_offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
+#endif
+      elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+      if (elim != reg)
        {
-         if (INSN_P (insn))
+         if (GET_CODE (elim) == PLUS)
            {
-             if (!frame_pointer_needed)
-               {
-                 insn_stack_adjust_offset_pre_post (insn, &pre, &post);
-                 if (pre)
-                   {
-                     VTI (bb)->n_mos++;
-                     if (dump_file && (dump_flags & TDF_DETAILS))
-                       log_op_type (GEN_INT (pre), bb, insn,
-                                    MO_ADJUST, dump_file);
-                   }
-                 if (post)
-                   {
-                     VTI (bb)->n_mos++;
-                     if (dump_file && (dump_flags & TDF_DETAILS))
-                       log_op_type (GEN_INT (post), bb, insn,
-                                    MO_ADJUST, dump_file);
-                   }
-               }
-             cselib_hook_called = false;
-             if (MAY_HAVE_DEBUG_INSNS)
-               {
-                 cselib_process_insn (insn);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   {
-                     print_rtl_single (dump_file, insn);
-                     dump_cselib_table (dump_file);
-                   }
-               }
-             if (!cselib_hook_called)
-               count_with_sets (insn, 0, 0);
-             if (CALL_P (insn))
-               {
-                 VTI (bb)->n_mos++;
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   log_op_type (PATTERN (insn), bb, insn,
-                                MO_CALL, dump_file);
-               }
+             fp_cfa_offset -= INTVAL (XEXP (elim, 1));
+             elim = XEXP (elim, 0);
            }
+         if (elim != hard_frame_pointer_rtx)
+           fp_cfa_offset = -1;
+       }
+      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;
 
-      count = VTI (bb)->n_mos;
+  vt_add_function_parameters ();
+
+  FOR_EACH_BB (bb)
+    {
+      rtx insn;
+      HOST_WIDE_INT pre, post = 0;
+      basic_block first_bb, last_bb;
 
       if (MAY_HAVE_DEBUG_INSNS)
        {
-         cselib_preserve_only_values (false);
-         next_uid_after = cselib_get_next_uid ();
-         cselib_reset_table (next_uid_before);
          cselib_record_sets_hook = add_with_sets;
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "first value: %i\n",
                     cselib_get_next_uid ());
        }
 
-      /* Add the micro-operations to the array.  */
-      VTI (bb)->mos = XNEWVEC (micro_operation, VTI (bb)->n_mos);
-      VTI (bb)->n_mos = 0;
-      for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
-          insn = NEXT_INSN (insn))
+      first_bb = bb;
+      for (;;)
+       {
+         edge e;
+         if (bb->next_bb == EXIT_BLOCK_PTR
+             || ! single_pred_p (bb->next_bb))
+           break;
+         e = find_edge (bb, bb->next_bb);
+         if (! e || (e->flags & EDGE_FALLTHRU) == 0)
+           break;
+         bb = bb->next_bb;
+       }
+      last_bb = bb;
+
+      /* Add the micro-operations to the vector.  */
+      FOR_BB_BETWEEN (bb, first_bb, last_bb->next_bb, next_bb)
        {
-         if (INSN_P (insn))
+         HOST_WIDE_INT offset = VTI (bb)->out.stack_adjust;
+         VTI (bb)->out.stack_adjust = VTI (bb)->in.stack_adjust;
+         for (insn = BB_HEAD (bb); insn != NEXT_INSN (BB_END (bb));
+              insn = NEXT_INSN (insn))
            {
-             if (!frame_pointer_needed)
+             if (INSN_P (insn))
                {
-                 insn_stack_adjust_offset_pre_post (insn, &pre, &post);
-                 if (pre)
+                 if (!frame_pointer_needed)
                    {
-                     micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
+                     insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+                     if (pre)
+                       {
+                         micro_operation mo;
+                         mo.type = MO_ADJUST;
+                         mo.u.adjust = pre;
+                         mo.insn = insn;
+                         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)->out.stack_adjust += pre;
+                       }
+                   }
 
-                     mo->type = MO_ADJUST;
-                     mo->u.adjust = pre;
-                     mo->insn = insn;
+                 cselib_hook_called = false;
+                 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))
+                       {
+                         print_rtl_single (dump_file, insn);
+                         dump_cselib_table (dump_file);
+                       }
+                   }
+                 if (!cselib_hook_called)
+                   add_with_sets (insn, 0, 0);
+                 cancel_changes (0);
 
+                 if (!frame_pointer_needed && post)
+                   {
+                     micro_operation mo;
+                     mo.type = MO_ADJUST;
+                     mo.u.adjust = post;
+                     mo.insn = insn;
                      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)->out.stack_adjust += post;
                    }
-               }
 
-             cselib_hook_called = false;
-             if (MAY_HAVE_DEBUG_INSNS)
-               {
-                 cselib_process_insn (insn);
-                 if (dump_file && (dump_flags & TDF_DETAILS))
+                 if (bb == prologue_bb
+                     && fp_cfa_offset != -1
+                     && hard_frame_pointer_adjustment == -1
+                     && RTX_FRAME_RELATED_P (insn)
+                     && fp_setter (insn))
                    {
-                     print_rtl_single (dump_file, insn);
-                     dump_cselib_table (dump_file);
+                     vt_init_cfa_base ();
+                     hard_frame_pointer_adjustment = fp_cfa_offset;
                    }
                }
-             if (!cselib_hook_called)
-               add_with_sets (insn, 0, 0);
-
-             if (!frame_pointer_needed && post)
-               {
-                 micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
-
-                 mo->type = MO_ADJUST;
-                 mo->u.adjust = post;
-                 mo->insn = insn;
-
-                 if (dump_file && (dump_flags & TDF_DETAILS))
-                   log_op_type (PATTERN (insn), bb, insn,
-                                MO_ADJUST, dump_file);
-               }
            }
+         gcc_assert (offset == VTI (bb)->out.stack_adjust);
        }
-      gcc_assert (count == VTI (bb)->n_mos);
+
+      bb = last_bb;
+
       if (MAY_HAVE_DEBUG_INSNS)
        {
-         cselib_preserve_only_values (true);
-         gcc_assert (next_uid_after == cselib_get_next_uid ());
-         cselib_reset_table (next_uid_after);
+         cselib_preserve_only_values ();
+         cselib_reset_table (cselib_get_next_uid ());
          cselib_record_sets_hook = NULL;
        }
     }
 
-  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);
-  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);
-    }
-
-  /* Init the IN and OUT sets.  */
-  FOR_ALL_BB (bb)
-    {
-      VTI (bb)->visited = false;
-      VTI (bb)->flooded = false;
-      dataflow_set_init (&VTI (bb)->in);
-      dataflow_set_init (&VTI (bb)->out);
-      VTI (bb)->permp = NULL;
-    }
-
+  hard_frame_pointer_adjustment = -1;
   VTI (ENTRY_BLOCK_PTR)->flooded = true;
-  vt_add_function_parameters ();
+  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
@@ -7552,7 +9492,22 @@ delete_debug_insns (void)
     {
       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);
+         }
     }
 }
 
@@ -7578,7 +9533,7 @@ vt_finalize (void)
 
   FOR_EACH_BB (bb)
     {
-      free (VTI (bb)->mos);
+      VEC_free (micro_operation, heap, VTI (bb)->mos);
     }
 
   FOR_ALL_BB (bb)
@@ -7601,14 +9556,17 @@ vt_finalize (void)
 
   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);
       cselib_finish ();
       BITMAP_FREE (scratch_regs);
       scratch_regs = NULL;
     }
 
+#ifdef HAVE_window_save
+  VEC_free (parm_reg_t, gc, windowed_parm_regs);
+#endif
+
   if (vui_vec)
     XDELETEVEC (vui_vec);
   vui_vec = NULL;
@@ -7635,15 +9593,11 @@ variable_tracking_main_1 (void)
     }
 
   mark_dfs_back_edges ();
-  vt_initialize ();
-  if (!frame_pointer_needed)
+  if (!vt_initialize ())
     {
-      if (!vt_stack_adjustments ())
-       {
-         vt_finalize ();
-         vt_debug_insns_local (true);
-         return 0;
-       }
+      vt_finalize ();
+      vt_debug_insns_local (true);
+      return 0;
     }
 
   success = vt_find_locations ();
@@ -7657,10 +9611,8 @@ variable_tracking_main_1 (void)
       /* This is later restored by our caller.  */
       flag_var_tracking_assignments = 0;
 
-      vt_initialize ();
-
-      if (!frame_pointer_needed && !vt_stack_adjustments ())
-       gcc_unreachable ();
+      success = vt_initialize ();
+      gcc_assert (success);
 
       success = vt_find_locations ();
     }
@@ -7678,7 +9630,9 @@ variable_tracking_main_1 (void)
       dump_flow_info (dump_file, dump_flags);
     }
 
+  timevar_push (TV_VAR_TRACKING_EMIT);
   vt_emit_notes ();
+  timevar_pop (TV_VAR_TRACKING_EMIT);
 
   vt_finalize ();
   vt_debug_insns_local (false);
@@ -7701,7 +9655,7 @@ variable_tracking_main (void)
 static bool
 gate_handle_var_tracking (void)
 {
-  return (flag_var_tracking);
+  return (flag_var_tracking && !targetm.delay_vartrack);
 }
 
 
@@ -7721,6 +9675,6 @@ struct rtl_opt_pass pass_variable_tracking =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func | TODO_verify_rtl_sharing/* todo_flags_finish */
+  TODO_verify_rtl_sharing               /* todo_flags_finish */
  }
 };