re PR debug/66691 (ICE on valid code at -O3 with -g enabled in simplify_subreg, at...
[gcc.git] / gcc / valtrack.c
index 2cdb06b174e7c916c62f055ee8c7d45ace4c2848..80a0043196935469449731f367bafe5201420b8a 100644 (file)
@@ -1,6 +1,6 @@
 /* Infrastructure for tracking user variable locations and values
    throughout compilation.
-   Copyright (C) 2010, 2011, 2012  Free Software Foundation, Inc.
+   Copyright (C) 2010-2015 Free Software Foundation, Inc.
    Contributed by Alexandre Oliva <aoliva@redhat.com>.
 
 This file is part of GCC.
@@ -24,16 +24,37 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "rtl.h"
+#include "predict.h"
+#include "basic-block.h"
 #include "valtrack.h"
+#include "hard-reg-set.h"
 #include "function.h"
 #include "regs.h"
 #include "emit-rtl.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, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
+cleanup_auto_inc_dec (rtx src, machine_mode mem_mode ATTRIBUTE_UNUSED)
 {
   rtx x = src;
 #ifdef AUTO_INC_DEC
@@ -53,7 +74,11 @@ cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
       /* SCRATCH must be shared because they represent distinct values.  */
       return x;
     case CLOBBER:
-      if (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)
+      /* 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;
 
@@ -71,9 +96,10 @@ cleanup_auto_inc_dec (rtx src, enum machine_mode mem_mode ATTRIBUTE_UNUSED)
       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 (code == PRE_INC
-                                   ? GET_MODE_SIZE (mem_mode)
-                                   : -GET_MODE_SIZE (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:
@@ -154,10 +180,12 @@ propagate_for_debug_subst (rtx from, const_rtx old_rtx, void *data)
    of THIS_BASIC_BLOCK.  */
 
 void
-propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
+propagate_for_debug (rtx_insn *insn, rtx_insn *last, rtx dest, rtx src,
                     basic_block this_basic_block)
 {
-  rtx next, loc, end = NEXT_INSN (BB_END (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;
@@ -165,6 +193,8 @@ propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
 
   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;
@@ -179,17 +209,117 @@ propagate_for_debug (rtx insn, rtx last, rtx dest, rtx src,
          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_init (struct dead_debug *debug, bitmap used)
+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)
-    bitmap_clear (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 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
@@ -199,7 +329,8 @@ dead_debug_init (struct dead_debug *debug, bitmap used)
    will be removed, and only then rescanned.  */
 
 static void
-dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
+dead_debug_reset_uses (struct dead_debug_local *debug,
+                      struct dead_debug_use *head)
 {
   bool got_head = (debug->head == head);
   bitmap rescan;
@@ -216,7 +347,7 @@ dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
   while (head)
     {
       struct dead_debug_use *next = head->next;
-      rtx insn;
+      rtx_insn *insn;
 
       insn = DF_REF_INSN (head->use);
       if (!next || DF_REF_INSN (next->use) != insn)
@@ -258,12 +389,87 @@ dead_debug_reset_uses (struct dead_debug *debug, struct dead_debug_use *head)
   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_init.  */
+   dead_debug_local_init.  */
+
 void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+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);
 
@@ -284,11 +490,30 @@ dead_debug_finish (struct dead_debug *debug, bitmap used)
     }
 }
 
-/* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
-   insn.  Create a bitmap for DEBUG as needed.  */
+/* Release GLOBAL->used unless it is the same as USED.  Release the
+   mapping hash table if it was initialized.  */
+
 void
-dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
+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;
@@ -304,27 +529,51 @@ dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
   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 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.  */
+   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 *debug, unsigned int uregno,
-                       rtx insn, enum debug_temp_where where)
+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 reg = NULL_RTX;
   rtx breg;
-  rtx dval;
+  rtx dval = NULL_RTX;
   rtx bind;
+  bool global;
 
-  if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
+  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
@@ -359,10 +608,23 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
   if (reg == NULL)
     {
       gcc_checking_assert (!uses);
-      return 0;
+      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);
+  gcc_checking_assert (uses || global);
 
   breg = reg;
   /* Recover the expression INSN stores in REG.  */
@@ -404,16 +666,14 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
             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 (REGNO (reg) < FIRST_PSEUDO_REGISTER
-                  && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
-                      != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
+         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 = lowpart_subreg (GET_MODE (reg),
-                                  cleanup_auto_inc_dec (src, VOIDmode),
-                                  GET_MODE (dest));
+           breg = debug_lowpart_subreg (GET_MODE (reg),
+                                        cleanup_auto_inc_dec (src, VOIDmode),
+                                        GET_MODE (dest));
        }
       else if (GET_CODE (dest) == SUBREG)
        {
@@ -428,14 +688,14 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
             setting REG in its mode would, we won't know what to bind
             the debug temp to.  */
          else if (REGNO (reg) < FIRST_PSEUDO_REGISTER
-                  && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+                  && (REG_NREGS (reg)
                       != hard_regno_nregs[REGNO (reg)][GET_MODE (dest)]))
            breg = NULL;
          /* Yay, we can use SRC, just adjust its mode.  */
          else
-           breg = lowpart_subreg (GET_MODE (reg),
-                                  cleanup_auto_inc_dec (src, VOIDmode),
-                                  GET_MODE (dest));
+           breg = debug_lowpart_subreg (GET_MODE (reg),
+                                        cleanup_auto_inc_dec (src, VOIDmode),
+                                        GET_MODE (dest));
        }
       /* Oh well, we're out of luck.  */
       else
@@ -455,7 +715,7 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
      probably doesn't make sense to introduce a new debug temp.  */
   if (where == DEBUG_TEMP_AFTER_WITH_REG && !uses->next)
     {
-      rtx next = DF_REF_INSN (uses->use);
+      rtx_insn *next = DF_REF_INSN (uses->use);
 
       if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
        {
@@ -464,19 +724,23 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
        }
     }
 
-  /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL).  */
-  dval = make_debug_expr_from_rtl (reg);
+  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)
+  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);
-  df_insn_rescan (bind);
+  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))
@@ -485,10 +749,9 @@ dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
        *DF_REF_REAL_LOC (cur->use) = dval;
       else
        *DF_REF_REAL_LOC (cur->use)
-         = gen_lowpart_SUBREG (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval);
+         = debug_lowpart_subreg (GET_MODE (*DF_REF_REAL_LOC (cur->use)), dval,
+                                 GET_MODE (dval));
       /* ??? Should we simplify subreg of subreg?  */
-      if (debug->to_rescan == NULL)
-       debug->to_rescan = BITMAP_ALLOC (NULL);
       bitmap_set_bit (debug->to_rescan, INSN_UID (DF_REF_INSN (cur->use)));
       uses = cur->next;
       XDELETE (cur);