diff gcc/valtrack.c @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/valtrack.c	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,773 @@
+/* Infrastructure for tracking user variable locations and values
+   throughout compilation.
+   Copyright (C) 2010-2017 Free Software Foundation, Inc.
+   Contributed by Alexandre Oliva <aoliva@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "rtl.h"
+#include "df.h"
+#include "valtrack.h"
+#include "regs.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "rtl-iter.h"
+
+/* gen_lowpart_no_emit hook implementation for DEBUG_INSNs.  In DEBUG_INSNs,
+   all lowpart SUBREGs are valid, despite what the machine requires for
+   instructions.  */
+
+static rtx
+gen_lowpart_for_debug (machine_mode mode, rtx x)
+{
+  rtx result = gen_lowpart_if_possible (mode, x);
+  if (result)
+    return result;
+
+  if (GET_MODE (x) != VOIDmode)
+    return gen_rtx_raw_SUBREG (mode, x,
+			       subreg_lowpart_offset (mode, GET_MODE (x)));
+
+  return NULL_RTX;
+}
+
+/* Replace auto-increment addressing modes with explicit operations to access
+   the same addresses without modifying the corresponding registers.  */
+
+static rtx
+cleanup_auto_inc_dec (rtx src, machine_mode mem_mode ATTRIBUTE_UNUSED)
+{
+  rtx x = src;
+  if (!AUTO_INC_DEC)
+    return copy_rtx (x);
+
+  const RTX_CODE code = GET_CODE (x);
+  int i;
+  const char *fmt;
+
+  switch (code)
+    {
+    case REG:
+    CASE_CONST_ANY:
+    case SYMBOL_REF:
+    case CODE_LABEL:
+    case PC:
+    case CC0:
+    case SCRATCH:
+      /* SCRATCH must be shared because they represent distinct values.  */
+      return x;
+    case CLOBBER:
+      /* Share clobbers of hard registers (like cc0), but do not share pseudo reg
+         clobbers or clobbers of hard registers that originated as pseudos.
+         This is needed to allow safe register renaming.  */
+      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER
+	  && ORIGINAL_REGNO (XEXP (x, 0)) == REGNO (XEXP (x, 0)))
+	return x;
+      break;
+
+    case CONST:
+      if (shared_const_p (x))
+	return x;
+      break;
+
+    case MEM:
+      mem_mode = GET_MODE (x);
+      break;
+
+    case PRE_INC:
+    case PRE_DEC:
+      gcc_assert (mem_mode != VOIDmode && mem_mode != BLKmode);
+      return gen_rtx_PLUS (GET_MODE (x),
+			   cleanup_auto_inc_dec (XEXP (x, 0), mem_mode),
+			   gen_int_mode (code == PRE_INC
+					 ? GET_MODE_SIZE (mem_mode)
+					 : -GET_MODE_SIZE (mem_mode),
+					 GET_MODE (x)));
+
+    case POST_INC:
+    case POST_DEC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      return cleanup_auto_inc_dec (code == PRE_MODIFY
+				   ? XEXP (x, 1) : XEXP (x, 0),
+				   mem_mode);
+
+    default:
+      break;
+    }
+
+  /* Copy the various flags, fields, and other information.  We assume
+     that all fields need copying, and then clear the fields that should
+     not be copied.  That is the sensible default behavior, and forces
+     us to explicitly document why we are *not* copying a flag.  */
+  x = shallow_copy_rtx (x);
+
+  /* We do not copy FRAME_RELATED for INSNs.  */
+  if (INSN_P (x))
+    RTX_FLAG (x, frame_related) = 0;
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+    if (fmt[i] == 'e')
+      XEXP (x, i) = cleanup_auto_inc_dec (XEXP (x, i), mem_mode);
+    else if (fmt[i] == 'E' || fmt[i] == 'V')
+      {
+	int j;
+	XVEC (x, i) = rtvec_alloc (XVECLEN (x, i));
+	for (j = 0; j < XVECLEN (x, i); j++)
+	  XVECEXP (x, i, j)
+	    = cleanup_auto_inc_dec (XVECEXP (src, i, j), mem_mode);
+      }
+
+  return x;
+}
+
+/* Auxiliary data structure for propagate_for_debug_stmt.  */
+
+struct rtx_subst_pair
+{
+  rtx to;
+  bool adjusted;
+  rtx_insn *insn;
+};
+
+/* DATA points to an rtx_subst_pair.  Return the value that should be
+   substituted.  */
+
+static rtx
+propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
+{
+  struct rtx_subst_pair *pair = (struct rtx_subst_pair *)data;
+
+  if (!rtx_equal_p (from, old_rtx))
+    return NULL_RTX;
+  if (!pair->adjusted)
+    {
+      pair->adjusted = true;
+      pair->to = cleanup_auto_inc_dec (pair->to, VOIDmode);
+      pair->to = make_compound_operation (pair->to, SET);
+      /* Avoid propagation from growing DEBUG_INSN expressions too much.  */
+      int cnt = 0;
+      subrtx_iterator::array_type array;
+      FOR_EACH_SUBRTX (iter, array, pair->to, ALL)
+	if (REG_P (*iter) && ++cnt > 1)
+	  {
+	    rtx dval = make_debug_expr_from_rtl (old_rtx);
+	    /* Emit a debug bind insn.  */
+	    rtx bind
+	      = gen_rtx_VAR_LOCATION (GET_MODE (old_rtx),
+				      DEBUG_EXPR_TREE_DECL (dval), pair->to,
+				      VAR_INIT_STATUS_INITIALIZED);
+	    rtx_insn *bind_insn = emit_debug_insn_before (bind, pair->insn);
+	    df_insn_rescan (bind_insn);
+	    pair->to = dval;
+	    break;
+	  }
+      return pair->to;
+    }
+  return copy_rtx (pair->to);
+}
+
+/* Replace all the occurrences of DEST with SRC in DEBUG_INSNs between INSN
+   and LAST, not including INSN, but including LAST.  Also stop at the end
+   of THIS_BASIC_BLOCK.  */
+
+void
+propagate_for_debug (rtx_insn *insn, rtx_insn *last, rtx dest, rtx src,
+		     basic_block this_basic_block)
+{
+  rtx_insn *next, *end = NEXT_INSN (BB_END (this_basic_block));
+  rtx loc;
+  rtx (*saved_rtl_hook_no_emit) (machine_mode, rtx);
+
+  struct rtx_subst_pair p;
+  p.to = src;
+  p.adjusted = false;
+  p.insn = NEXT_INSN (insn);
+
+  next = NEXT_INSN (insn);
+  last = NEXT_INSN (last);
+  saved_rtl_hook_no_emit = rtl_hooks.gen_lowpart_no_emit;
+  rtl_hooks.gen_lowpart_no_emit = gen_lowpart_for_debug;
+  while (next != last && next != end)
+    {
+      insn = next;
+      next = NEXT_INSN (insn);
+      if (DEBUG_INSN_P (insn))
+	{
+	  loc = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn),
+					 dest, propagate_for_debug_subst, &p);
+	  if (loc == INSN_VAR_LOCATION_LOC (insn))
+	    continue;
+	  INSN_VAR_LOCATION_LOC (insn) = loc;
+	  df_insn_rescan (insn);
+	}
+    }
+  rtl_hooks.gen_lowpart_no_emit = saved_rtl_hook_no_emit;
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  */
+
+void
+dead_debug_global_init (struct dead_debug_global *debug, bitmap used)
+{
+  debug->used = used;
+  debug->htab = NULL;
+  if (used)
+    bitmap_clear (used);
+}
+
+/* Initialize DEBUG to an empty list, and clear USED, if given.  Link
+   back to GLOBAL, if given, and bring in used bits from it.  */
+
+void
+dead_debug_local_init (struct dead_debug_local *debug, bitmap used,
+		       struct dead_debug_global *global)
+{
+  if (!used && global && global->used)
+    used = BITMAP_ALLOC (NULL);
+
+  debug->head = NULL;
+  debug->global = global;
+  debug->used = used;
+  debug->to_rescan = NULL;
+
+  if (used)
+    {
+      if (global && global->used)
+	bitmap_copy (used, global->used);
+      else
+	bitmap_clear (used);
+    }
+}
+
+/* Locate the entry for REG in GLOBAL->htab.  */
+
+static dead_debug_global_entry *
+dead_debug_global_find (struct dead_debug_global *global, rtx reg)
+{
+  dead_debug_global_entry temp_entry;
+  temp_entry.reg = reg;
+
+  dead_debug_global_entry *entry = global->htab->find (&temp_entry);
+  gcc_checking_assert (entry && entry->reg == temp_entry.reg);
+
+  return entry;
+}
+
+/* Insert an entry mapping REG to DTEMP in GLOBAL->htab.  */
+
+static dead_debug_global_entry *
+dead_debug_global_insert (struct dead_debug_global *global, rtx reg, rtx dtemp)
+{
+  dead_debug_global_entry temp_entry;
+  temp_entry.reg = reg;
+  temp_entry.dtemp = dtemp;
+
+  if (!global->htab)
+    global->htab = new hash_table<dead_debug_hash_descr> (31);
+
+  dead_debug_global_entry **slot = global->htab->find_slot (&temp_entry,
+							    INSERT);
+  gcc_checking_assert (!*slot);
+  *slot = XNEW (dead_debug_global_entry);
+  **slot = temp_entry;
+  return *slot;
+}
+
+/* If UREGNO, referenced by USE, is a pseudo marked as used in GLOBAL,
+   replace it with a USE of the debug temp recorded for it, and
+   return TRUE.  Otherwise, just return FALSE.
+
+   If PTO_RESCAN is given, instead of rescanning modified INSNs right
+   away, add their UIDs to the bitmap, allocating one of *PTO_RESCAN
+   is NULL.  */
+
+static bool
+dead_debug_global_replace_temp (struct dead_debug_global *global,
+				df_ref use, unsigned int uregno,
+				bitmap *pto_rescan)
+{
+  if (!global || uregno < FIRST_PSEUDO_REGISTER
+      || !global->used
+      || !REG_P (*DF_REF_REAL_LOC (use))
+      || REGNO (*DF_REF_REAL_LOC (use)) != uregno
+      || !bitmap_bit_p (global->used, uregno))
+    return false;
+
+  dead_debug_global_entry *entry
+    = dead_debug_global_find (global, *DF_REF_REAL_LOC (use));
+  gcc_checking_assert (GET_CODE (entry->reg) == REG
+		       && REGNO (entry->reg) == uregno);
+
+  if (!entry->dtemp)
+    return true;
+
+  *DF_REF_REAL_LOC (use) = entry->dtemp;
+  if (!pto_rescan)
+    df_insn_rescan (DF_REF_INSN (use));
+  else
+    {
+      if (!*pto_rescan)
+	*pto_rescan = BITMAP_ALLOC (NULL);
+      bitmap_set_bit (*pto_rescan, INSN_UID (DF_REF_INSN (use)));
+    }
+
+  return true;
+}
+
+/* Reset all debug uses in HEAD, and clear DEBUG->to_rescan bits of
+   each reset insn.  DEBUG is not otherwise modified.  If HEAD is
+   DEBUG->head, DEBUG->head will be set to NULL at the end.
+   Otherwise, entries from DEBUG->head that pertain to reset insns
+   will be removed, and only then rescanned.  */
+
+static void
+dead_debug_reset_uses (struct dead_debug_local *debug,
+		       struct dead_debug_use *head)
+{
+  bool got_head = (debug->head == head);
+  bitmap rescan;
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  bitmap_iterator bi;
+  unsigned int uid;
+
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
+
+  while (head)
+    {
+      struct dead_debug_use *next = head->next;
+      rtx_insn *insn;
+
+      insn = DF_REF_INSN (head->use);
+      if (!next || DF_REF_INSN (next->use) != insn)
+	{
+	  INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+	  if (got_head)
+	    df_insn_rescan_debug_internal (insn);
+	  else
+	    bitmap_set_bit (rescan, INSN_UID (insn));
+	  if (debug->to_rescan)
+	    bitmap_clear_bit (debug->to_rescan, INSN_UID (insn));
+	}
+      XDELETE (head);
+      head = next;
+    }
+
+  if (got_head)
+    {
+      debug->head = NULL;
+      return;
+    }
+
+  while ((cur = *tailp))
+    if (bitmap_bit_p (rescan, INSN_UID (DF_REF_INSN (cur->use))))
+      {
+	*tailp = cur->next;
+	XDELETE (cur);
+      }
+    else
+      tailp = &cur->next;
+
+  EXECUTE_IF_SET_IN_BITMAP (rescan, 0, uid, bi)
+    {
+      struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+      if (insn_info)
+	df_insn_rescan_debug_internal (insn_info->insn);
+    }
+
+  BITMAP_FREE (rescan);
+}
+
+/* Promote pending local uses of pseudos in DEBUG to global
+   substitutions.  Uses of non-pseudos are left alone for
+   resetting.  */
+
+static void
+dead_debug_promote_uses (struct dead_debug_local *debug)
+{
+  for (struct dead_debug_use *head = debug->head, **headp = &debug->head;
+       head; head = *headp)
+    {
+      rtx reg = *DF_REF_REAL_LOC (head->use);
+      df_ref ref;
+      dead_debug_global_entry *entry;
+
+      if (GET_CODE (reg) != REG
+	  || REGNO (reg) < FIRST_PSEUDO_REGISTER)
+	{
+	  headp = &head->next;
+	  continue;
+	}
+
+      if (!debug->global->used)
+	debug->global->used = BITMAP_ALLOC (NULL);
+
+      bool added = bitmap_set_bit (debug->global->used, REGNO (reg));
+      gcc_checking_assert (added);
+
+      entry = dead_debug_global_insert (debug->global, reg,
+					make_debug_expr_from_rtl (reg));
+
+      gcc_checking_assert (entry->dtemp);
+
+      /* Tentatively remove the USE from the list.  */
+      *headp = head->next;
+
+      if (!debug->to_rescan)
+	debug->to_rescan = BITMAP_ALLOC (NULL);
+
+      for (ref = DF_REG_USE_CHAIN (REGNO (reg)); ref;
+	   ref = DF_REF_NEXT_REG (ref))
+	if (DEBUG_INSN_P (DF_REF_INSN (ref)))
+	  {
+	    if (!dead_debug_global_replace_temp (debug->global, ref,
+						 REGNO (reg),
+						 &debug->to_rescan))
+	      {
+		rtx_insn *insn = DF_REF_INSN (ref);
+		INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
+		bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
+	      }
+	  }
+
+      for (ref = DF_REG_DEF_CHAIN (REGNO (reg)); ref;
+	   ref = DF_REF_NEXT_REG (ref))
+	if (!dead_debug_insert_temp (debug, REGNO (reg), DF_REF_INSN (ref),
+				     DEBUG_TEMP_BEFORE_WITH_VALUE))
+	  {
+	    rtx bind;
+	    bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
+					 DEBUG_EXPR_TREE_DECL (entry->dtemp),
+					 gen_rtx_UNKNOWN_VAR_LOC (),
+					 VAR_INIT_STATUS_INITIALIZED);
+	    rtx_insn *insn = emit_debug_insn_before (bind, DF_REF_INSN (ref));
+	    bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
+	  }
+
+      entry->dtemp = NULL;
+      XDELETE (head);
+    }
+}
+
+/* Reset all debug insns with pending uses.  Release the bitmap in it,
+   unless it is USED.  USED must be the same bitmap passed to
+   dead_debug_local_init.  */
+
+void
+dead_debug_local_finish (struct dead_debug_local *debug, bitmap used)
+{
+  if (debug->global)
+    dead_debug_promote_uses (debug);
+
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
+  if (debug->to_rescan)
+    {
+      bitmap_iterator bi;
+      unsigned int uid;
+
+      EXECUTE_IF_SET_IN_BITMAP (debug->to_rescan, 0, uid, bi)
+	{
+	  struct df_insn_info *insn_info = DF_INSN_UID_SAFE_GET (uid);
+	  if (insn_info)
+	    df_insn_rescan (insn_info->insn);
+	}
+      BITMAP_FREE (debug->to_rescan);
+    }
+}
+
+/* Release GLOBAL->used unless it is the same as USED.  Release the
+   mapping hash table if it was initialized.  */
+
+void
+dead_debug_global_finish (struct dead_debug_global *global, bitmap used)
+{
+  if (global->used != used)
+    BITMAP_FREE (global->used);
+
+  delete global->htab;
+  global->htab = NULL;
+}
+
+/* Add USE to DEBUG, or substitute it right away if it's a pseudo in
+   the global substitution list.  USE must be a dead reference to
+   UREGNO in a debug insn.  Create a bitmap for DEBUG as needed.  */
+
+void
+dead_debug_add (struct dead_debug_local *debug, df_ref use, unsigned int uregno)
+{
+  if (dead_debug_global_replace_temp (debug->global, use, uregno,
+				      &debug->to_rescan))
+    return;
+
+  struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
+
+  newddu->use = use;
+  newddu->next = debug->head;
+  debug->head = newddu;
+
+  if (!debug->used)
+    debug->used = BITMAP_ALLOC (NULL);
+
+  /* ??? If we dealt with split multi-registers below, we should set
+     all registers for the used mode in case of hardware
+     registers.  */
+  bitmap_set_bit (debug->used, uregno);
+}
+
+/* Like lowpart_subreg, but if a subreg is not valid for machine, force
+   it anyway - for use in debug insns.  */
+
+static rtx
+debug_lowpart_subreg (machine_mode outer_mode, rtx expr,
+		      machine_mode inner_mode)
+{
+  if (inner_mode == VOIDmode)
+    inner_mode = GET_MODE (expr);
+  int offset = subreg_lowpart_offset (outer_mode, inner_mode);
+  rtx ret = simplify_gen_subreg (outer_mode, expr, inner_mode, offset);
+  if (ret)
+    return ret;
+  return gen_rtx_raw_SUBREG (outer_mode, expr, offset);
+}
+
+/* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
+   before or after INSN (depending on WHERE), that binds a (possibly
+   global) debug temp to the widest-mode use of UREGNO, if WHERE is
+   *_WITH_REG, or the value stored in UREGNO by INSN otherwise, and
+   replace all uses of UREGNO in DEBUG with uses of the debug temp.
+   INSN must be where UREGNO dies, if WHERE is *_BEFORE_*, or where it
+   is set otherwise.  Return the number of debug insns emitted.  */
+
+int
+dead_debug_insert_temp (struct dead_debug_local *debug, unsigned int uregno,
+			rtx_insn *insn, enum debug_temp_where where)
+{
+  struct dead_debug_use **tailp = &debug->head;
+  struct dead_debug_use *cur;
+  struct dead_debug_use *uses = NULL;
+  struct dead_debug_use **usesp = &uses;
+  rtx reg = NULL_RTX;
+  rtx breg;
+  rtx dval = NULL_RTX;
+  rtx bind;
+  bool global;
+
+  if (!debug->used)
+    return 0;
+
+  global = (debug->global && debug->global->used
+	    && bitmap_bit_p (debug->global->used, uregno));
+
+  if (!global && !bitmap_clear_bit (debug->used, uregno))
+    return 0;
+
+  /* Move all uses of uregno from debug->head to uses, setting mode to
+     the widest referenced mode.  */
+  while ((cur = *tailp))
+    {
+      if (DF_REF_REGNO (cur->use) == uregno)
+	{
+	  /* If this loc has been changed e.g. to debug_expr already
+	     as part of a multi-register use, just drop it.  */
+	  if (!REG_P (*DF_REF_REAL_LOC (cur->use)))
+	    {
+	      *tailp = cur->next;
+	      XDELETE (cur);
+	      continue;
+	    }
+	  *usesp = cur;
+	  usesp = &cur->next;
+	  *tailp = cur->next;
+	  cur->next = NULL;
+	  if (!reg
+	      || (GET_MODE_BITSIZE (GET_MODE (reg))
+		  < GET_MODE_BITSIZE (GET_MODE (*DF_REF_REAL_LOC (cur->use)))))
+	    reg = *DF_REF_REAL_LOC (cur->use);
+	}
+      else
+	tailp = &(*tailp)->next;
+    }
+
+  /* We may have dangling bits in debug->used for registers that were part
+     of a multi-register use, one component of which has been reset.  */
+  if (reg == NULL)
+    {
+      gcc_checking_assert (!uses);
+      if (!global)
+	return 0;
+    }
+
+  if (global)
+    {
+      if (!reg)
+	reg = regno_reg_rtx[uregno];
+      dead_debug_global_entry *entry
+	= dead_debug_global_find (debug->global, reg);
+      gcc_checking_assert (entry->reg == reg);
+      dval = entry->dtemp;
+      if (!dval)
+	return 0;
+    }
+
+  gcc_checking_assert (uses || global);
+
+  breg = reg;
+  /* Recover the expression INSN stores in REG.  */
+  if (where == DEBUG_TEMP_BEFORE_WITH_VALUE)
+    {
+      rtx set = single_set (insn);
+      rtx dest, src;
+
+      if (set)
+	{
+	  dest = SET_DEST (set);
+	  src = SET_SRC (set);
+	  /* Lose if the REG-setting insn is a CALL.  */
+	  if (GET_CODE (src) == CALL)
+	    {
+	      while (uses)
+		{
+		  cur = uses->next;
+		  XDELETE (uses);
+		  uses = cur;
+		}
+	      return 0;
+	    }
+	}
+
+      /* ??? Should we try to extract it from a PARALLEL?  */
+      if (!set)
+	breg = NULL;
+      /* Cool, it's the same REG, we can use SRC.  */
+      else if (dest == reg)
+	breg = cleanup_auto_inc_dec (src, VOIDmode);
+      else if (REG_P (dest))
+	{
+	  /* Hmm...  Something's fishy, we should be setting REG here.  */
+	  if (REGNO (dest) != REGNO (reg))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  ??? We could bind the debug_expr to a
+	     CONCAT or PARALLEL with the split multi-registers, and
+	     replace them as we found the corresponding sets.  */
+	  else if (REG_NREGS (reg) != REG_NREGS (dest))
+	    breg = NULL;
+	  /* Ok, it's the same (hardware) REG, but with a different
+	     mode, so SUBREG it.  */
+	  else
+	    breg = debug_lowpart_subreg (GET_MODE (reg),
+					 cleanup_auto_inc_dec (src, VOIDmode),
+					 GET_MODE (dest));
+	}
+      else if (GET_CODE (dest) == SUBREG)
+	{
+	  /* We should be setting REG here.  Lose.  */
+	  if (REGNO (SUBREG_REG (dest)) != REGNO (reg))
+	    breg = NULL;
+	  /* Lose if we're setting something other than the lowpart of
+	     REG.  */
+	  else if (!subreg_lowpart_p (dest))
+	    breg = NULL;
+	  /* If we're not overwriting all the hardware registers that
+	     setting REG in its mode would, we won't know what to bind
+	     the debug temp to.  */
+	  else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+		   && (REG_NREGS (reg)
+		       != hard_regno_nregs (REGNO (reg), GET_MODE (dest))))
+	    breg = NULL;
+	  /* Yay, we can use SRC, just adjust its mode.  */
+	  else
+	    breg = debug_lowpart_subreg (GET_MODE (reg),
+					 cleanup_auto_inc_dec (src, VOIDmode),
+					 GET_MODE (dest));
+	}
+      /* Oh well, we're out of luck.  */
+      else
+	breg = NULL;
+
+      /* We couldn't figure out the value stored in REG, so reset all
+	 of its pending debug uses.  */
+      if (!breg)
+	{
+	  dead_debug_reset_uses (debug, uses);
+	  return 0;
+	}
+    }
+
+  /* If there's a single (debug) use of an otherwise unused REG, and
+     the debug use is not part of a larger expression, then it
+     probably doesn't make sense to introduce a new debug temp.  */
+  if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
+    {
+      rtx_insn *next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+	{
+	  XDELETE (uses);
+	  return 0;
+	}
+    }
+
+  if (!global)
+    /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
+    dval = make_debug_expr_from_rtl (reg);
+
+  /* Emit a debug bind insn before the insn in which reg dies.  */
+  bind = gen_rtx_VAR_LOCATION (GET_MODE (reg),
+			       DEBUG_EXPR_TREE_DECL (dval), breg,
+			       VAR_INIT_STATUS_INITIALIZED);
+
+  if (where == DEBUG_TEMP_AFTER_WITH_REG
+      || where == DEBUG_TEMP_AFTER_WITH_REG_FORCE)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
+  if (debug->to_rescan == NULL)
+    debug->to_rescan = BITMAP_ALLOC (NULL);
+  bitmap_set_bit (debug->to_rescan, INSN_UID (bind));
+
+  /* Adjust all uses.  */
+  while ((cur = uses))
+    {
+      if (GET_MODE (*DF_REF_REAL_LOC (cur->use)) == GET_MODE (reg))
+	*DF_REF_REAL_LOC (cur->use) = dval;
+      else
+	*DF_REF_REAL_LOC (cur->use)
+	  = debug_lowpart_subreg (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval,
+				  GET_MODE (dval));
+      /* ??? Should we simplify subreg of subreg?  */
+      bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
+      uses = cur->next;
+      XDELETE (cur);
+    }
+
+  return 1;
+}