OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / var-tracking.c
index 362a881..d5a4548 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
    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.
@@ -363,7 +365,7 @@ typedef const struct value_chain_def *const_value_chain;
 #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)
 
 /* Alloc pool for struct attrs_def.  */
 static alloc_pool attrs_pool;
@@ -398,6 +400,17 @@ static shared_hash empty_shared_hash;
 /* Scratch register bitmap used by cselib_expand_value_rtx.  */
 static bitmap scratch_regs = NULL;
 
+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;
+
 /* Variable used to tell whether cselib_process_insn called our hook.  */
 static bool cselib_hook_called;
 
@@ -407,7 +420,7 @@ static void stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
 static void insn_stack_adjust_offset_pre_post (rtx, HOST_WIDE_INT *,
                                               HOST_WIDE_INT *);
 static bool vt_stack_adjustments (void);
-static rtx compute_cfa_pointer (HOST_WIDE_INT);
+static void note_register_arguments (rtx);
 static hashval_t variable_htab_hash (const void *);
 static int variable_htab_eq (const void *, const void *);
 static void variable_htab_free (void *);
@@ -421,7 +434,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);
@@ -438,7 +450,6 @@ 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 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);
@@ -446,7 +457,6 @@ static int loc_cmp (rtx, rtx);
 static bool variable_part_different_p (variable_part *, variable_part *);
 static bool onepart_variable_different_p (variable, variable);
 static bool variable_different_p (variable, variable);
-static int dataflow_set_different_1 (void **, void *);
 static bool dataflow_set_different (dataflow_set *, dataflow_set *);
 static void dataflow_set_destroy (dataflow_set *);
 
@@ -662,11 +672,15 @@ vt_stack_adjustments (void)
            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;
-               }
+             {
+               if (INSN_P (insn))
+                 {
+                   insn_stack_adjust_offset_pre_post (insn, &pre, &post);
+                   offset += pre + post;
+                 }
+               if (CALL_P (insn))
+                 note_register_arguments (insn);
+             }
 
          VTI (dest)->out.stack_adjust = offset;
 
@@ -697,22 +711,18 @@ vt_stack_adjustments (void)
   return true;
 }
 
-/* Compute a CFA-based value for the stack pointer.  */
+/* 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 rtx
+static inline rtx
 compute_cfa_pointer (HOST_WIDE_INT adjustment)
 {
-  rtx cfa;
-
-#ifdef FRAME_POINTER_CFA_OFFSET
-  adjustment -= FRAME_POINTER_CFA_OFFSET (current_function_decl);
-  cfa = plus_constant (frame_pointer_rtx, adjustment);
-#else
-  adjustment -= ARG_POINTER_CFA_OFFSET (current_function_decl);
-  cfa = plus_constant (arg_pointer_rtx, adjustment);
-#endif
-
-  return cfa;
+  return plus_constant (cfa_base_rtx, adjustment + cfa_base_offset);
 }
 
 /* Adjustment for hard_frame_pointer_rtx to cfa base reg,
@@ -729,6 +739,69 @@ struct adjust_mem_data
   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
+use_narrower_mode (rtx x, enum machine_mode mode, enum machine_mode wmode)
+{
+  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 ();
+    }
+}
+
 /* Helper function for adjusting used MEMs.  */
 
 static rtx
@@ -741,16 +814,20 @@ adjust_mems (rtx loc, const_rtx old_rtx, void *data)
   switch (GET_CODE (loc))
     {
     case REG:
-      /* Don't do any sp or fp replacements outside of MEM addresses.  */
-      if (amd->mem_mode == VOIDmode)
+      /* 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)
+         && !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)
+              && 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;
@@ -804,7 +881,7 @@ adjust_mems (rtx loc, const_rtx old_rtx, void *data)
     case POST_MODIFY:
       if (addr == loc)
        addr = XEXP (loc, 0);
-      gcc_assert (amd->mem_mode != VOIDmode && amd->mem_mode != BLKmode);
+      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,
@@ -822,18 +899,46 @@ adjust_mems (rtx loc, const_rtx old_rtx, void *data)
       amd->store = store_save;
       mem = simplify_replace_fn_rtx (addr, old_rtx, adjust_mems, data);
       if (mem == SUBREG_REG (loc))
-       return loc;
+       {
+         tem = loc;
+         goto finish_subreg;
+       }
       tem = simplify_gen_subreg (GET_MODE (loc), mem,
                                 GET_MODE (SUBREG_REG (loc)),
                                 SUBREG_BYTE (loc));
       if (tem)
-       return tem;
+       goto finish_subreg;
       tem = simplify_gen_subreg (GET_MODE (loc), addr,
                                 GET_MODE (SUBREG_REG (loc)),
                                 SUBREG_BYTE (loc));
-      if (tem)
-       return tem;
-      return gen_rtx_raw_SUBREG (GET_MODE (loc), addr, 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;
     }
@@ -876,6 +981,33 @@ 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
+
   amd.mem_mode = VOIDmode;
   amd.stack_adjust = -VTI (bb)->out.stack_adjust;
   amd.side_effects = NULL_RTX;
@@ -884,7 +1016,54 @@ adjust_insn (basic_block bb, rtx insn)
   note_stores (PATTERN (insn), adjust_mem_stores, &amd);
 
   amd.store = false;
-  note_uses (&PATTERN (insn), adjust_mem_uses, &amd);
+  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.  */
@@ -942,9 +1121,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;
 }
 
@@ -952,9 +1129,7 @@ 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;
 }
 
@@ -1003,9 +1178,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;
 }
 
@@ -1015,15 +1188,13 @@ 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;
 }
 
 extern void debug_dv (decl_or_value dv);
 
-void
+DEBUG_FUNCTION void
 debug_dv (decl_or_value dv)
 {
   if (dv_is_value_p (dv))
@@ -1092,7 +1263,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)
@@ -1295,7 +1466,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);
@@ -1460,34 +1631,23 @@ unshare_variable (dataflow_set *set, void **slot, variable var,
   return slot;
 }
 
-/* Add a variable from *SLOT to hash table DATA and increase its reference
-   count.  */
-
-static int
-vars_copy_1 (void **slot, void *data)
-{
-  htab_t dst = (htab_t) data;
-  variable src;
-  void **dstp;
-
-  src = (variable) *slot;
-  src->refcount++;
-
-  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);
+  htab_iterator hi;
+  variable var;
+
+  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.  */
@@ -1496,9 +1656,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;
 }
@@ -1989,14 +2152,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)
     {
@@ -2022,8 +2183,8 @@ variable_union (void **slot, void *data)
     {
       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);
@@ -2059,10 +2220,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;
@@ -2372,7 +2531,13 @@ dataflow_set_union (dataflow_set *dst, dataflow_set *src)
       dst->vars = shared_hash_copy (src->vars);
     }
   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.  */
@@ -2408,45 +2573,71 @@ dv_changed_p (decl_or_value dv)
 
 /* 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 (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 (dv_onepart_p (var->dv));
 
   if (!var->n_var_parts)
     return NULL;
 
-  gcc_assert (var->var_part[0].offset == 0);
+  gcc_checking_assert (var->var_part[0].offset == 0);
+  gcc_checking_assert (loc != dv_as_opaque (var->dv));
 
+  loc_code = GET_CODE (loc);
   for (node = var->var_part[0].loc_chain; node; node = node->next)
-    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;
-           if ((where = find_loc_in_1pdv (loc, var, vars)))
-             {
-               VALUE_RECURSED_INTO (node->loc) = false;
-               return where;
-             }
-           VALUE_RECURSED_INTO (node->loc) = false;
-         }
-      }
+      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;
+       }
+
+      /* 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;
+       }
+
+      gcc_checking_assert (node == var->var_part[0].loc_chain);
+      gcc_checking_assert (!node->next);
+
+      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);
+    }
 
   return NULL;
 }
@@ -2505,6 +2696,29 @@ 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 (dv_onepart_p (s2var->dv));
+
+      if (s2var->n_var_parts)
+       {
+         gcc_checking_assert (s2var->var_part[0].offset == 0);
+         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)
@@ -2639,10 +2853,8 @@ loc_cmp (rtx x, rtx y)
       if (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
          < DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)))
        return -1;
-#ifdef ENABLE_CHECKING
-      gcc_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
-                 > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
-#endif
+      gcc_checking_assert (DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x))
+                          > DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (y)));
       return 1;
     }
 
@@ -2791,15 +3003,23 @@ add_value_chains (decl_or_value dv, rtx loc)
 }
 
 /* If CSELIB_VAL_PTR of value DV refer to VALUEs, add backlinks from those
-   VALUEs to DV.  */
+   VALUEs to DV.  Add the same time get rid of ASM_OPERANDS from locs list,
+   that is something we never can express in .debug_info and can prevent
+   reverse ops from being used.  */
 
 static void
 add_cselib_value_chains (decl_or_value dv)
 {
-  struct elt_loc_list *l;
+  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));
+  for (l = &CSELIB_VAL_PTR (dv_as_value (dv))->locs; *l;)
+    if (GET_CODE ((*l)->loc) == ASM_OPERANDS)
+      *l = (*l)->next;
+    else
+      {
+       for_each_rtx (&(*l)->loc, add_value_chain, dv_as_opaque (dv));
+       l = &(*l)->next;
+      }
 }
 
 /* If decl or value DVP refers to VALUE from *LOC, remove backlinks
@@ -2926,7 +3146,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);
 
@@ -2940,8 +3160,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;
          }
@@ -2969,7 +3189,7 @@ canonicalize_values_star (void **slot, void *data)
   if (!dv_onepart_p (dv))
     return 1;
 
-  gcc_assert (var->n_var_parts == 1);
+  gcc_checking_assert (var->n_var_parts == 1);
 
   if (dv_is_value_p (dv))
     {
@@ -3152,15 +3372,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;
@@ -3168,18 +3388,77 @@ canonicalize_values_star (void **slot, void *data)
   return 1;
 }
 
+/* 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
+canonicalize_vars_star (void **slot, void *data)
+{
+  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 (!dv_onepart_p (dv) || dv_is_value_p (dv))
+    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 (void **s1slot, void *data)
+variable_merge_over_cur (variable s1var, struct dfset_merge *dsm)
 {
-  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);
@@ -3190,14 +3469,14 @@ variable_merge_over_cur (void **s1slot, void *data)
   /* 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
+                      && s1var->var_part[0].offset == 0);
 
   dvhash = dv_htab_hash (dv);
   if (dv_is_value_p (dv))
@@ -3213,17 +3492,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->n_var_parts == 1
+             && s2var->var_part[0].offset == 0);
 
   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->n_var_parts == 1
+                 && dvar->var_part[0].offset == 0);
       nodep = &dvar->var_part[0].loc_chain;
     }
   else
@@ -3335,10 +3614,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
@@ -3403,11 +3681,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;
        }
     }
@@ -3437,11 +3713,9 @@ variable_merge_over_cur (void **s1slot, void *data)
    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);
 
@@ -3468,6 +3742,8 @@ dataflow_set_merge (dataflow_set *dst, dataflow_set *src2)
   struct dfset_merge dsm;
   int i;
   size_t src1_elems, src2_elems;
+  htab_iterator hi;
+  variable var;
 
   src1_elems = htab_elements (shared_hash_htab (src1->vars));
   src2_elems = htab_elements (shared_hash_htab (src2->vars));
@@ -3488,10 +3764,10 @@ dataflow_set_merge (dataflow_set *dst, dataflow_set *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;
@@ -3511,6 +3787,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)
@@ -3704,8 +3985,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;
                      }
@@ -3722,8 +4003,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;
@@ -3771,18 +4054,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);
@@ -3807,7 +4096,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;
@@ -3830,6 +4119,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
@@ -3847,9 +4137,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));
@@ -3867,8 +4156,9 @@ find_mem_expr_in_1pdv (tree expr, rtx val, htab_t vars)
   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;
@@ -3931,11 +4221,10 @@ dataflow_set_preserve_mem_locs (void **slot, void *data)
        {
          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.  */
@@ -3979,7 +4268,7 @@ 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)
@@ -4100,7 +4389,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)
@@ -4115,10 +4404,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)
 {
@@ -4153,14 +4438,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)
     {
@@ -4193,8 +4477,8 @@ variable_different_p (variable var1, variable var2)
       /* 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));
+         gcc_assert (var1->n_var_parts == 1
+                     && dv_as_opaque (var1->dv) == dv_as_opaque (var2->dv));
          return onepart_variable_different_p (var1, var2);
        }
       if (variable_part_different_p (&var1->var_part[i], &var2->var_part[i]))
@@ -4205,56 +4489,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))
-    {
-      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;
 
@@ -4262,14 +4504,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.  */
@@ -4350,13 +4616,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
@@ -4389,8 +4674,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;
     }
 
@@ -4520,10 +4805,6 @@ var_lowpart (enum machine_mode mode, rtx loc)
   return gen_rtx_REG_offset (loc, mode, regno, offset);
 }
 
-/* arg_pointer_rtx resp. frame_pointer_rtx if stack_pointer_rtx or
-   hard_frame_pointer_rtx is being mapped to it.  */
-static rtx cfa_base_rtx;
-
 /* Carry information about uses and stores while walking rtx.  */
 
 struct count_use_info
@@ -4552,18 +4833,25 @@ 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;
@@ -4587,19 +4875,20 @@ get_address_mode (rtx mem)
 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))
     {
       cselib_val *addr = cselib_lookup (XEXP (loc, 0),
-                                       get_address_mode (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);
 }
 
 /* Determine what kind of micro operation to choose for a USE.  Return
@@ -4619,7 +4908,8 @@ use_type (rtx loc, struct count_use_info *cui, enum machine_mode *modep)
              rtx ploc = PAT_VAR_LOCATION_LOC (loc);
              if (! VAR_LOC_UNKNOWN_P (ploc))
                {
-                 cselib_val *val = cselib_lookup (ploc, GET_MODE (loc), 1);
+                 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
@@ -4641,7 +4931,8 @@ 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_address_mode (loc), 0)))
+                                       get_address_mode (loc), 0,
+                                       GET_MODE (loc))))
                return MO_VAL_SET;
            }
          else
@@ -4729,6 +5020,9 @@ log_op_type (rtx x, basic_block bb, rtx insn,
 /* All preserved VALUEs.  */
 static VEC (rtx, heap) *preserved_values;
 
+/* Registers used in the current function for passing parameters.  */
+static HARD_REG_SET argument_reg_set;
+
 /* Ensure VAL is preserved and remember it in a vector for vt_emit_notes.  */
 
 static void
@@ -4738,6 +5032,33 @@ preserve_value (cselib_val *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.  */
 
@@ -4769,6 +5090,7 @@ add_uses (rtx *ploc, void *data)
          if (MEM_P (vloc)
              && !REG_P (XEXP (vloc, 0))
              && !MEM_P (XEXP (vloc, 0))
+             && GET_CODE (XEXP (vloc, 0)) != ENTRY_VALUE
              && (GET_CODE (XEXP (vloc, 0)) != PLUS
                  || XEXP (XEXP (vloc, 0), 0) != cfa_base_rtx
                  || !CONST_INT_P (XEXP (XEXP (vloc, 0), 1))))
@@ -4776,13 +5098,15 @@ add_uses (rtx *ploc, void *data)
              rtx mloc = vloc;
              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 moa;
                  preserve_value (val);
-                 mloc = cselib_subst_to_values (XEXP (mloc, 0));
+                 mloc = cselib_subst_to_values (XEXP (mloc, 0),
+                                                GET_MODE (mloc));
                  moa.type = MO_VAL_USE;
                  moa.insn = cui->insn;
                  moa.u.loc = gen_rtx_CONCAT (address_mode,
@@ -4794,8 +5118,12 @@ add_uses (rtx *ploc, void *data)
                }
            }
 
-         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)
+                  && (val = find_use_val (vloc, GET_MODE (oloc), cui)))
            {
              enum machine_mode mode2;
              enum micro_operation_type type2;
@@ -4841,6 +5169,7 @@ add_uses (rtx *ploc, void *data)
          if (MEM_P (oloc)
              && !REG_P (XEXP (oloc, 0))
              && !MEM_P (XEXP (oloc, 0))
+             && GET_CODE (XEXP (oloc, 0)) != ENTRY_VALUE
              && (GET_CODE (XEXP (oloc, 0)) != PLUS
                  || XEXP (XEXP (oloc, 0), 0) != cfa_base_rtx
                  || !CONST_INT_P (XEXP (XEXP (oloc, 0), 1))))
@@ -4848,13 +5177,15 @@ add_uses (rtx *ploc, void *data)
              rtx mloc = oloc;
              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 moa;
                  preserve_value (val);
-                 mloc = cselib_subst_to_values (XEXP (mloc, 0));
+                 mloc = cselib_subst_to_values (XEXP (mloc, 0),
+                                               GET_MODE (mloc));
                  moa.type = MO_VAL_USE;
                  moa.insn = cui->insn;
                  moa.u.loc = gen_rtx_CONCAT (address_mode,
@@ -4924,6 +5255,8 @@ add_uses_1 (rtx *x, void *cui)
   for_each_rtx (x, add_uses, cui);
 }
 
+#define EXPR_DEPTH (PARAM_VALUE (PARAM_MAX_VARTRACK_EXPR_DEPTH))
+
 /* Attempt to reverse the EXPR operation in the debug info.  Say for
    reg1 = reg2 + 6 even when reg2 is no longer live we
    can express its value as VAL - 6.  */
@@ -4949,17 +5282,22 @@ reverse_op (rtx val, const_rtx expr)
     case XOR:
     case NOT:
     case NEG:
+      if (!REG_P (XEXP (src, 0)))
+       return NULL_RTX;
+      break;
     case SIGN_EXTEND:
     case ZERO_EXTEND:
+      if (!REG_P (XEXP (src, 0)) && !MEM_P (XEXP (src, 0)))
+       return NULL_RTX;
       break;
     default:
       return NULL_RTX;
     }
 
-  if (!REG_P (XEXP (src, 0)) || !SCALAR_INT_MODE_P (GET_MODE (src)))
+  if (!SCALAR_INT_MODE_P (GET_MODE (src)) || XEXP (src, 0) == cfa_base_rtx)
     return NULL_RTX;
 
-  v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0);
+  v = cselib_lookup (XEXP (src, 0), GET_MODE (XEXP (src, 0)), 0, VOIDmode);
   if (!v || !cselib_preserved_value_p (v))
     return NULL_RTX;
 
@@ -5042,10 +5380,22 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
        {
          mo.type = MO_CLOBBER;
          mo.u.loc = loc;
+         if (GET_CODE (expr) == SET
+             && SET_DEST (expr) == loc
+             && REGNO (loc) < FIRST_PSEUDO_REGISTER
+             && TEST_HARD_REG_BIT (argument_reg_set, REGNO (loc))
+             && find_use_val (loc, mode, cui)
+             && GET_CODE (SET_SRC (expr)) != ASM_OPERANDS)
+           {
+             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);
 
@@ -5073,6 +5423,7 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
       if (MEM_P (loc) && type == MO_VAL_SET
          && !REG_P (XEXP (loc, 0))
          && !MEM_P (XEXP (loc, 0))
+         && GET_CODE (XEXP (loc, 0)) != ENTRY_VALUE
          && (GET_CODE (XEXP (loc, 0)) != PLUS
              || XEXP (XEXP (loc, 0), 0) != cfa_base_rtx
              || !CONST_INT_P (XEXP (XEXP (loc, 0), 1))))
@@ -5080,13 +5431,15 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
          rtx mloc = loc;
          enum machine_mode address_mode = get_address_mode (mloc);
          cselib_val *val = cselib_lookup (XEXP (mloc, 0),
-                                          address_mode, 0);
+                                          address_mode, 0,
+                                          GET_MODE (mloc));
 
          if (val && !cselib_preserved_value_p (val))
            {
              preserve_value (val);
              mo.type = MO_VAL_USE;
-             mloc = cselib_subst_to_values (XEXP (mloc, 0));
+             mloc = cselib_subst_to_values (XEXP (mloc, 0),
+                                            GET_MODE (mloc));
              mo.u.loc = gen_rtx_CONCAT (address_mode, val->val_rtx, mloc);
              mo.insn = cui->insn;
              if (dump_file && (dump_flags & TDF_DETAILS))
@@ -5103,7 +5456,9 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
        }
       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);
 
@@ -5145,7 +5500,7 @@ 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));
@@ -5258,6 +5613,320 @@ add_stores (rtx loc, const_rtx expr, void *cuip)
   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 (REG_P (x))
+         {
+           cselib_val *val = cselib_lookup (x, GET_MODE (x), 0, VOIDmode);
+           if (val && cselib_preserved_value_p (val))
+             item = gen_rtx_CONCAT (GET_MODE (x), x, 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 = gen_rtx_CONCAT (GET_MODE (x), x,
+                                              lowpart_subreg (GET_MODE (x),
+                                                              val->val_rtx,
+                                                              mode));
+                       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 = gen_rtx_CONCAT (GET_MODE (x), copy_rtx (x), val->val_rtx);
+         }
+       if (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 (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
    operations uses and stores in an insn after cselib_record_sets has
    analyzed the sets in an insn, but before it modifies the stored
@@ -5327,7 +5996,8 @@ add_with_sets (rtx insn, struct cselib_set *sets, int n_sets)
 
       mo.type = MO_CALL;
       mo.insn = insn;
-      mo.u.loc = NULL_RTX;
+      mo.u.loc = call_arguments;
+      call_arguments = NULL_RTX;
 
       if (dump_file && (dump_flags & TDF_DETAILS))
        log_op_type (PATTERN (insn), bb, insn, mo.type, dump_file);
@@ -5458,7 +6128,7 @@ compute_bb_dataflow (basic_block bb)
   dataflow_set_copy (&old_out, out);
   dataflow_set_copy (out, in);
 
-  for (i = 0; VEC_iterate (micro_operation, VTI (bb)->mos, i, mo); i++)
+  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
     {
       rtx insn = mo->insn;
 
@@ -5508,6 +6178,11 @@ 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;
 
@@ -5747,6 +6422,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);
@@ -5782,6 +6458,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;
@@ -5934,6 +6611,7 @@ vt_find_locations (void)
   sbitmap_free (in_worklist);
   sbitmap_free (in_pending);
 
+  timevar_pop (TV_VAR_TRACKING_DATAFLOW);
   return success;
 }
 
@@ -6433,7 +7111,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
@@ -6514,7 +7192,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
@@ -6613,7 +7291,7 @@ 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);
 }
 
 /* Structure for passing some other parameters to function
@@ -6635,6 +7313,10 @@ struct expand_loc_callback_data
      whose cur_loc has been already recomputed during current
      emit_notes_for_changes call.  */
   bool cur_loc_changed;
+
+  /* True if cur_loc should be ignored and any possible location
+     returned.  */
+  bool ignore_cur_loc;
 };
 
 /* Callback for cselib_expand_value, that looks for expressions
@@ -6648,6 +7330,7 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
     = (struct expand_loc_callback_data *) data;
   bool dummy = elcd->dummy;
   bool cur_loc_changed = elcd->cur_loc_changed;
+  rtx cur_loc;
   decl_or_value dv;
   variable var;
   location_chain loc;
@@ -6722,7 +7405,7 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
   VALUE_RECURSED_INTO (x) = true;
   result = NULL;
 
-  if (var->var_part[0].cur_loc)
+  if (var->var_part[0].cur_loc && !elcd->ignore_cur_loc)
     {
       if (dummy)
        {
@@ -6737,12 +7420,16 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
                                             vt_expand_loc_callback, data);
       if (result)
        set_dv_changed (dv, false);
+      cur_loc = var->var_part[0].cur_loc;
     }
-  if (!result && dv_changed_p (dv))
+  else
+    cur_loc = NULL_RTX;
+  if (!result && (dv_changed_p (dv) || elcd->ignore_cur_loc))
     {
-      set_dv_changed (dv, false);
+      if (!elcd->ignore_cur_loc)
+       set_dv_changed (dv, false);
       for (loc = var->var_part[0].loc_chain; loc; loc = loc->next)
-       if (loc->loc == var->var_part[0].cur_loc)
+       if (loc->loc == cur_loc)
          continue;
        else if (dummy)
          {
@@ -6754,18 +7441,18 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
                result = pc_rtx;
                break;
              }
-           else
-             {
-               result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
-                                                    vt_expand_loc_callback,
-                                                    data);
-               if (result)
-                 break;
-             }
+         }
+       else
+         {
+           result = cselib_expand_value_rtx_cb (loc->loc, regs, max_depth,
+                                                vt_expand_loc_callback, data);
+           if (result)
+             break;
          }
       if (dummy && (result || var->var_part[0].cur_loc))
        var->cur_loc_changed = true;
-      var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
+      if (!elcd->ignore_cur_loc)
+       var->var_part[0].cur_loc = loc ? loc->loc : NULL_RTX;
     }
   if (dummy)
     {
@@ -6786,7 +7473,7 @@ vt_expand_loc_callback (rtx x, bitmap regs, int max_depth, void *data)
    tables.  */
 
 static rtx
-vt_expand_loc (rtx loc, htab_t vars)
+vt_expand_loc (rtx loc, htab_t vars, bool ignore_cur_loc)
 {
   struct expand_loc_callback_data data;
 
@@ -6796,7 +7483,8 @@ vt_expand_loc (rtx loc, htab_t vars)
   data.vars = vars;
   data.dummy = false;
   data.cur_loc_changed = false;
-  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, 5,
+  data.ignore_cur_loc = ignore_cur_loc;
+  loc = cselib_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
                                    vt_expand_loc_callback, &data);
 
   if (loc && MEM_P (loc))
@@ -6817,17 +7505,13 @@ vt_expand_loc_dummy (rtx loc, htab_t vars, bool *pcur_loc_changed)
   data.vars = vars;
   data.dummy = true;
   data.cur_loc_changed = false;
-  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, 5,
+  data.ignore_cur_loc = false;
+  ret = cselib_dummy_expand_value_rtx_cb (loc, scratch_regs, EXPR_DEPTH,
                                          vt_expand_loc_callback, &data);
   *pcur_loc_changed = data.cur_loc_changed;
   return ret;
 }
 
-#ifdef ENABLE_RTL_CHECKING
-/* Used to verify that cur_loc_changed updating is safe.  */
-static struct pointer_map_t *emitted_notes;
-#endif
-
 /* Emit the NOTE_INSN_VAR_LOCATION for variable *VARP.  DATA contains
    additional parameters: WHERE specifies whether the note shall be emitted
    before or after instruction INSN.  */
@@ -6872,10 +7556,8 @@ emit_note_insn_var_location (void **varp, void *data)
       if (var->n_var_parts == 0)
        var->cur_loc_changed = true;
     }
-#ifndef ENABLE_RTL_CHECKING
   if (!var->cur_loc_changed)
     goto clear;
-#endif
   for (i = 0; i < var->n_var_parts; i++)
     {
       enum machine_mode mode, wider_mode;
@@ -6894,7 +7576,7 @@ emit_note_insn_var_location (void **varp, void *data)
          complete = false;
          continue;
        }
-      loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars);
+      loc2 = vt_expand_loc (var->var_part[i].cur_loc, vars, false);
       if (!loc2)
        {
          complete = false;
@@ -6902,6 +7584,8 @@ emit_note_insn_var_location (void **varp, void *data)
        }
       loc[n_var_parts] = loc2;
       mode = GET_MODE (var->var_part[i].cur_loc);
+      if (mode == VOIDmode && dv_onepart_p (var->dv))
+       mode = DECL_MODE (decl);
       for (lc = var->var_part[i].loc_chain; lc; lc = lc->next)
        if (var->var_part[i].cur_loc == lc->loc)
          {
@@ -6922,7 +7606,7 @@ emit_note_insn_var_location (void **varp, void *data)
          && mode == GET_MODE (var->var_part[j].cur_loc)
          && (REG_P (loc[n_var_parts]) || MEM_P (loc[n_var_parts]))
          && last_limit == var->var_part[j].offset
-         && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars))
+         && (loc2 = vt_expand_loc (var->var_part[j].cur_loc, vars, false))
          && GET_CODE (loc[n_var_parts]) == GET_CODE (loc2))
        {
          rtx new_loc = NULL;
@@ -6991,8 +7675,12 @@ emit_note_insn_var_location (void **varp, void *data)
                                    (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;
+
+      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);
@@ -7011,23 +7699,6 @@ emit_note_insn_var_location (void **varp, void *data)
                                      parallel, (int) initialized);
     }
 
-#ifdef ENABLE_RTL_CHECKING
-  if (note_vl)
-    {
-      void **note_slot = pointer_map_insert (emitted_notes, decl);
-      rtx pnote = (rtx) *note_slot;
-      if (!var->cur_loc_changed && (pnote || PAT_VAR_LOCATION_LOC (note_vl)))
-       {
-         gcc_assert (pnote);
-         gcc_assert (rtx_equal_p (PAT_VAR_LOCATION_LOC (pnote),
-                                  PAT_VAR_LOCATION_LOC (note_vl)));
-       }
-      *note_slot = (void *) note_vl;
-    }
-  if (!var->cur_loc_changed)
-    goto clear;
-#endif
-
   if (where != EMIT_NOTE_BEFORE_INSN)
     {
       note = emit_note_after (NOTE_INSN_VAR_LOCATION, insn);
@@ -7035,7 +7706,17 @@ emit_note_insn_var_location (void **varp, void *data)
        NOTE_DURING_CALL_P (note) = true;
     }
   else
-    note = emit_note_before (NOTE_INSN_VAR_LOCATION, insn);
+    {
+      /* Make sure that the call related notes come first.  */
+      while (NEXT_INSN (insn)
+            && NOTE_P (insn)
+            && NOTE_DURING_CALL_P (insn))
+       insn = NEXT_INSN (insn);
+      if (NOTE_P (insn) && NOTE_DURING_CALL_P (insn))
+       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:
@@ -7259,8 +7940,8 @@ emit_notes_for_differences_1 (void **slot, void *data)
        {
          location_chain lc1, lc2;
 
-         gcc_assert (old_var->n_var_parts == 1);
-         gcc_assert (new_var->n_var_parts == 1);
+         gcc_assert (old_var->n_var_parts == 1
+                     && new_var->n_var_parts == 1);
          lc1 = old_var->var_part[0].loc_chain;
          lc2 = new_var->var_part[0].loc_chain;
          while (lc1
@@ -7359,6 +8040,23 @@ 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
@@ -7370,15 +8068,44 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
   dataflow_set_clear (set);
   dataflow_set_copy (set, &VTI (bb)->in);
 
-  for (i = 0; VEC_iterate (micro_operation, VTI (bb)->mos, i, mo); i++)
+  FOR_EACH_VEC_ELT (micro_operation, VTI (bb)->mos, i, mo)
     {
       rtx insn = mo->insn;
+      rtx next_insn = next_non_note_insn_var_location (insn);
 
       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), true);
+                 /* 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:
@@ -7423,6 +8150,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);
            }
@@ -7546,7 +8278,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
                val_store (set, XEXP (reverse, 0), XEXP (reverse, 1),
                           insn, false);
 
-             emit_notes_for_changes (NEXT_INSN (insn), EMIT_NOTE_BEFORE_INSN,
+             emit_notes_for_changes (next_insn, EMIT_NOTE_BEFORE_INSN,
                                      set->vars);
            }
            break;
@@ -7569,7 +8301,7 @@ 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;
@@ -7594,7 +8326,7 @@ 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;
@@ -7621,7 +8353,7 @@ emit_notes_in_bb (basic_block bb, dataflow_set *set)
              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;
@@ -7641,9 +8373,6 @@ vt_emit_notes (void)
   basic_block bb;
   dataflow_set cur;
 
-#ifdef ENABLE_RTL_CHECKING
-  emitted_notes = pointer_map_create ();
-#endif
   gcc_assert (!htab_elements (changed_variables));
 
   /* Free memory occupied by the out hash tables, as they aren't used
@@ -7660,7 +8389,7 @@ vt_emit_notes (void)
       unsigned int i;
       rtx val;
 
-      for (i = 0; VEC_iterate (rtx, preserved_values, i, val); i++)
+      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
        add_cselib_value_chains (dv_from_value (val));
       changed_variables_stack = VEC_alloc (variable, heap, 40);
       changed_values_stack = VEC_alloc (rtx, heap, 40);
@@ -7690,7 +8419,7 @@ vt_emit_notes (void)
       unsigned int i;
       rtx val;
 
-      for (i = 0; VEC_iterate (rtx, preserved_values, i, val); i++)
+      FOR_EACH_VEC_ELT (rtx, preserved_values, i, val)
        remove_cselib_value_chains (dv_from_value (val));
       gcc_assert (htab_elements (value_chains) == 0);
     }
@@ -7703,9 +8432,6 @@ vt_emit_notes (void)
       VEC_free (rtx, heap, changed_values_stack);
     }
 
-#ifdef ENABLE_RTL_CHECKING
-  pointer_map_destroy (emitted_notes);
-#endif
   emit_notes = false;
 }
 
@@ -7736,127 +8462,245 @@ 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.  */
+/* Helper function for vt_add_function_parameter.  RTL is
+   the expression and VAL corresponding cselib_val pointer
+   for which ENTRY_VALUE should be created.  */
 
 static void
-vt_add_function_parameters (void)
+create_entry_value (rtx rtl, cselib_val *val)
 {
-  tree parm;
+  cselib_val *val2;
+  struct elt_loc_list *el;
+  el = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el));
+  el->next = val->locs;
+  el->loc = gen_rtx_ENTRY_VALUE (GET_MODE (rtl));
+  ENTRY_VALUE_EXP (el->loc) = rtl;
+  el->setting_insn = get_insns ();
+  val->locs = el;
+  val2 = cselib_lookup_from_insn (el->loc, GET_MODE (rtl), true,
+                                 VOIDmode, get_insns ());
+  if (val2
+      && val2 != val
+      && val2->locs
+      && rtx_equal_p (val2->locs->loc, el->loc))
+    {
+      struct elt_loc_list *el2;
 
-  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;
+      preserve_value (val2);
+      el2 = (struct elt_loc_list *) ggc_alloc_cleared_atomic (sizeof (*el2));
+      el2->next = val2->locs;
+      el2->loc = val->val_rtx;
+      el2->setting_insn = get_insns ();
+      val2->locs = el2;
+    }
+}
 
-      if (TREE_CODE (parm) != PARM_DECL)
-       continue;
+/* Insert function parameter PARM in IN and OUT sets of ENTRY_BLOCK.  */
 
-      if (!DECL_NAME (parm))
-       continue;
+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 (!decl_rtl || !incoming)
-       continue;
+  if (TREE_CODE (parm) != PARM_DECL)
+    return;
 
-      if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
-       continue;
+  if (!decl_rtl || !incoming)
+    return;
 
-      if (!vt_get_decl_and_offset (incoming, &decl, &offset))
+  if (GET_MODE (decl_rtl) == BLKmode || GET_MODE (incoming) == BLKmode)
+    return;
+
+  /* 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 (REG_P (incoming)
+      && HARD_REGISTER_P (incoming)
+      && OUTGOING_REGNO (REGNO (incoming)) != REGNO (incoming))
+    {
+      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 (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
-           {
-             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)
-           {
-             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);
+         cselib_val *val = CSELIB_VAL_PTR (dv_as_value (dv));
+         create_entry_value (incoming, val);
+         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);
+             val = cselib_lookup_from_insn (mem, indmode, true,
+                                            VOIDmode, get_insns ());
+             if (val)
+               {
+                 preserve_value (val);
+                 create_entry_value (mem, val);
+               }
+           }
        }
     }
-
-  if (MAY_HAVE_DEBUG_INSNS)
+  else if (MEM_P (incoming))
     {
-      cselib_preserve_only_values ();
-      cselib_reset_table (cselib_get_next_uid ());
+      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);
+    }
 }
 
 /* Return true if INSN in the prologue initializes hard_frame_pointer_rtx.  */
@@ -7884,6 +8728,23 @@ fp_setter (rtx insn)
   return false;
 }
 
+/* Gather all registers used for passing arguments to other functions
+   called from the current routine.  */
+
+static void
+note_register_arguments (rtx insn)
+{
+  rtx link, x;
+
+  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+    if (GET_CODE (XEXP (link, 0)) == USE)
+      {
+       x = XEXP (XEXP (link, 0), 0);
+       if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
+         SET_HARD_REG_BIT (argument_reg_set, REGNO (x));
+      }
+}
+
 /* 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
@@ -7896,16 +8757,31 @@ vt_init_cfa_base (void)
 
 #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)])
+    {
+      cfa_base_rtx = NULL_RTX;
+      return;
+    }
   if (!MAY_HAVE_DEBUG_INSNS)
     return;
 
-  val = cselib_lookup (cfa_base_rtx, GET_MODE (cfa_base_rtx), 1);
+  /* 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);
-  val->locs->setting_insn = get_insns ();
+  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);
@@ -7917,7 +8793,7 @@ vt_init_cfa_base (void)
 static bool
 vt_initialize (void)
 {
-  basic_block bb, prologue_bb = NULL;
+  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));
@@ -7973,6 +8849,18 @@ vt_initialize (void)
       valvar_pool = NULL;
     }
 
+  CLEAR_HARD_REG_SET (argument_reg_set);
+
+  /* In order to factor out the adjustments made to the stack pointer or to
+     the hard frame pointer and thus be able to use DW_OP_fbreg operations
+     instead of individual location lists, we're going to rewrite MEMs based
+     on them into MEMs based on the CFA by de-eliminating stack_pointer_rtx
+     or hard_frame_pointer_rtx to the virtual CFA pointer frame_pointer_rtx
+     resp. arg_pointer_rtx.  We can do this either when there is no frame
+     pointer in the function and stack adjustments are consistent for all
+     basic blocks or when there is a frame pointer and no stack realignment.
+     But we first have to check that frame_pointer_rtx resp. arg_pointer_rtx
+     has been eliminated.  */
   if (!frame_pointer_needed)
     {
       rtx reg, elim;
@@ -8015,13 +8903,50 @@ vt_initialize (void)
            }
          if (elim != hard_frame_pointer_rtx)
            fp_cfa_offset = -1;
-         else
-           prologue_bb = single_succ (ENTRY_BLOCK_PTR);
        }
+      else
+       fp_cfa_offset = -1;
+    }
+
+  /* If the stack is realigned and a DRAP register is used, we're going to
+     rewrite MEMs based on it representing incoming locations of parameters
+     passed on the stack into MEMs based on the argument pointer.  Although
+     we aren't going to rewrite other MEMs, we still need to initialize the
+     virtual CFA pointer in order to ensure that the argument pointer will
+     be seen as a constant throughout the function.
+
+     ??? This doesn't work if FRAME_POINTER_CFA_OFFSET is defined.  */
+  else if (stack_realign_drap)
+    {
+      rtx reg, elim;
+
+#ifdef FRAME_POINTER_CFA_OFFSET
+      reg = frame_pointer_rtx;
+#else
+      reg = arg_pointer_rtx;
+#endif
+      elim = eliminate_regs (reg, VOIDmode, NULL_RTX);
+      if (elim != reg)
+       {
+         if (GET_CODE (elim) == PLUS)
+           elim = XEXP (elim, 0);
+         if (elim == hard_frame_pointer_rtx)
+           vt_init_cfa_base ();
+       }
+    }
+
+  if (frame_pointer_needed)
+    {
+      rtx insn;
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       if (CALL_P (insn))
+         note_register_arguments (insn);
     }
 
   hard_frame_pointer_adjustment = -1;
 
+  vt_add_function_parameters ();
+
   FOR_EACH_BB (bb)
     {
       rtx insn;
@@ -8082,6 +9007,8 @@ vt_initialize (void)
                  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))
                        {
@@ -8108,6 +9035,7 @@ vt_initialize (void)
                    }
 
                  if (bb == prologue_bb
+                     && fp_cfa_offset != -1
                      && hard_frame_pointer_adjustment == -1
                      && RTX_FRAME_RELATED_P (insn)
                      && fp_setter (insn))
@@ -8132,7 +9060,6 @@ vt_initialize (void)
 
   hard_frame_pointer_adjustment = -1;
   VTI (ENTRY_BLOCK_PTR)->flooded = true;
-  vt_add_function_parameters ();
   cfa_base_rtx = NULL_RTX;
   return true;
 }
@@ -8208,6 +9135,7 @@ vt_finalize (void)
       cselib_finish ();
       BITMAP_FREE (scratch_regs);
       scratch_regs = NULL;
+      VEC_free (parm_reg_t, gc, windowed_parm_regs);
     }
 
   if (vui_vec)
@@ -8273,7 +9201,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);
@@ -8296,7 +9226,7 @@ variable_tracking_main (void)
 static bool
 gate_handle_var_tracking (void)
 {
-  return (flag_var_tracking);
+  return (flag_var_tracking && !targetm.delay_vartrack);
 }
 
 
@@ -8316,6 +9246,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 */
  }
 };