Add VEC_WIDEN_MULT_EVEN/ODD_EXPR
[platform/upstream/gcc.git] / gcc / df-problems.c
index 729cad5..7afbed9 100644 (file)
@@ -1,6 +1,6 @@
 /* Standard problems for dataflow support routines.
    Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-   2008, 2009, 2010 Free Software Foundation, Inc.
+   2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
    Originally contributed by Michael P. Hayes
              (m.hayes@elec.canterbury.ac.nz, mhayes@redhat.com)
    Major rewrite contributed by Danny Berlin (dberlin@dberlin.org)
@@ -32,7 +32,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "recog.h"
 #include "function.h"
 #include "regs.h"
-#include "output.h"
 #include "alloc-pool.h"
 #include "flags.h"
 #include "hard-reg-set.h"
@@ -906,6 +905,7 @@ df_lr_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED)
      blocks within infinite loops.  */
   if (!reload_completed)
     {
+      unsigned int pic_offset_table_regnum = PIC_OFFSET_TABLE_REGNUM;
       /* Any reference to any pseudo before reload is a potential
         reference of the frame pointer.  */
       bitmap_set_bit (&df->hardware_regs_used, FRAME_POINTER_REGNUM);
@@ -919,9 +919,9 @@ df_lr_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED)
 
       /* Any constant, or pseudo with constant equivalences, may
         require reloading from memory using the pic register.  */
-      if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM
-         && fixed_regs[PIC_OFFSET_TABLE_REGNUM])
-       bitmap_set_bit (&df->hardware_regs_used, PIC_OFFSET_TABLE_REGNUM);
+      if (pic_offset_table_regnum != INVALID_REGNUM
+         && fixed_regs[pic_offset_table_regnum])
+       bitmap_set_bit (&df->hardware_regs_used, pic_offset_table_regnum);
     }
 
   EXECUTE_IF_SET_IN_BITMAP (df_lr->out_of_date_transfer_functions, 0, bb_index, bi)
@@ -2747,10 +2747,12 @@ df_ignore_stack_reg (int regno ATTRIBUTE_UNUSED)
 
 
 /* Remove all of the REG_DEAD or REG_UNUSED notes from INSN and add
-   them to OLD_DEAD_NOTES and OLD_UNUSED_NOTES.  */
+   them to OLD_DEAD_NOTES and OLD_UNUSED_NOTES.  Remove also
+   REG_EQUAL/REG_EQUIV notes referring to dead pseudos using LIVE
+   as the bitmap of currently live registers.  */
 
 static void
-df_kill_notes (rtx insn)
+df_kill_notes (rtx insn, bitmap live)
 {
   rtx *pprev = &REG_NOTES (insn);
   rtx link = *pprev;
@@ -2797,6 +2799,47 @@ df_kill_notes (rtx insn)
            }
          break;
 
+       case REG_EQUAL:
+       case REG_EQUIV:
+         {
+           /* Remove the notes that refer to dead registers.  As we have at most
+              one REG_EQUAL/EQUIV note, all of EQ_USES will refer to this note
+              so we need to purge the complete EQ_USES vector when removing
+              the note using df_notes_rescan.  */
+           df_ref *use_rec;
+           bool deleted = false;
+
+           for (use_rec = DF_INSN_EQ_USES (insn); *use_rec; use_rec++)
+             {
+               df_ref use = *use_rec;
+               if (DF_REF_REGNO (use) > FIRST_PSEUDO_REGISTER
+                   && DF_REF_LOC (use)
+                   && (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
+                   && ! bitmap_bit_p (live, DF_REF_REGNO (use))
+                   && loc_mentioned_in_p (DF_REF_LOC (use), XEXP (link, 0)))
+                 {
+                   deleted = true;
+                   break;
+                 }
+             }
+           if (deleted)
+             {
+               rtx next;
+#ifdef REG_DEAD_DEBUGGING
+               df_print_note ("deleting: ", insn, link);
+#endif
+               next = XEXP (link, 1);
+               free_EXPR_LIST_node (link);
+               *pprev = link = next;
+               df_notes_rescan (insn);
+             }
+           else
+             {
+               pprev = &XEXP (link, 1);
+               link = *pprev;
+             }
+           break;
+         }
        default:
          pprev = &XEXP (link, 1);
          link = *pprev;
@@ -2842,25 +2885,6 @@ df_whole_mw_reg_unused_p (struct df_mw_hardreg *mws,
 }
 
 
-/* Node of a linked list of uses of dead REGs in debug insns.  */
-struct dead_debug_use
-{
-  df_ref use;
-  struct dead_debug_use *next;
-};
-
-/* Linked list of the above, with a bitmap of the REGs in the
-   list.  */
-struct dead_debug
-{
-  struct dead_debug_use *head;
-  bitmap used;
-  bitmap to_rescan;
-};
-
-static void dead_debug_reset (struct dead_debug *, unsigned int);
-
-
 /* Set the REG_UNUSED notes for the multiword hardreg defs in INSN
    based on the bits in LIVE.  Do not generate notes for registers in
    artificial uses.  DO_NOT_GEN is updated so that REG_DEAD notes are
@@ -2886,7 +2910,7 @@ df_set_unused_notes_for_mw (rtx insn, struct df_mw_hardreg *mws,
     {
       unsigned int regno = mws->start_regno;
       df_set_note (REG_UNUSED, insn, mws->mw_reg);
-      dead_debug_reset (debug, regno);
+      dead_debug_insert_temp (debug, regno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -2901,7 +2925,7 @@ df_set_unused_notes_for_mw (rtx insn, struct df_mw_hardreg *mws,
            && !bitmap_bit_p (artificial_uses, r))
          {
            df_set_note (REG_UNUSED, insn, regno_reg_rtx[r]);
-           dead_debug_reset (debug, r);
+           dead_debug_insert_temp (debug, r, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
            df_print_note ("adding 2: ", insn, REG_NOTES (insn));
 #endif
@@ -2969,12 +2993,12 @@ df_set_dead_notes_for_mw (rtx insn, struct df_mw_hardreg *mws,
 
   if (df_whole_mw_reg_dead_p (mws, live, artificial_uses, do_not_gen))
     {
-      /* Add a dead note for the entire multi word register.  */
       if (is_debug)
        {
          *added_notes_p = true;
          return;
        }
+      /* Add a dead note for the entire multi word register.  */
       df_set_note (REG_DEAD, insn, mws->mw_reg);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 1: ", insn, REG_NOTES (insn));
@@ -3028,7 +3052,7 @@ df_create_unused_note (rtx insn, df_ref def,
       rtx reg = (DF_REF_LOC (def))
                 ? *DF_REF_REAL_LOC (def): DF_REF_REG (def);
       df_set_note (REG_UNUSED, insn, reg);
-      dead_debug_reset (debug, dregno);
+      dead_debug_insert_temp (debug, dregno, insn, DEBUG_TEMP_AFTER_WITH_REG);
 #ifdef REG_DEAD_DEBUGGING
       df_print_note ("adding 3: ", insn, REG_NOTES (insn));
 #endif
@@ -3039,7 +3063,7 @@ df_create_unused_note (rtx insn, df_ref def,
 
 
 /* Initialize DEBUG to an empty list, and clear USED, if given.  */
-static inline void
+void
 dead_debug_init (struct dead_debug *debug, bitmap used)
 {
   debug->head = NULL;
@@ -3049,32 +3073,83 @@ dead_debug_init (struct dead_debug *debug, bitmap used)
     bitmap_clear (used);
 }
 
-/* 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.  */
-static inline void
-dead_debug_finish (struct dead_debug *debug, bitmap used)
+/* 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 *debug, struct dead_debug_use *head)
 {
-  struct dead_debug_use *head;
-  rtx insn = NULL;
+  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 (debug->used != used)
-    BITMAP_FREE (debug->used);
+  if (got_head)
+    rescan = NULL;
+  else
+    rescan = BITMAP_ALLOC (NULL);
 
-  while ((head = debug->head))
+  while (head)
     {
+      struct dead_debug_use *next = head->next;
+      rtx insn;
+
       insn = DF_REF_INSN (head->use);
-      if (!head->next || DF_REF_INSN (head->next->use) != insn)
+      if (!next || DF_REF_INSN (next->use) != insn)
        {
          INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-         df_insn_rescan_debug_internal (insn);
+         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));
        }
-      debug->head = head->next;
       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);
+}
+
+/* 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.  */
+void
+dead_debug_finish (struct dead_debug *debug, bitmap used)
+{
+  if (debug->used != used)
+    BITMAP_FREE (debug->used);
+
+  dead_debug_reset_uses (debug, debug->head);
+
   if (debug->to_rescan)
     {
       bitmap_iterator bi;
@@ -3090,37 +3165,9 @@ dead_debug_finish (struct dead_debug *debug, bitmap used)
     }
 }
 
-/* Reset DEBUG_INSNs with pending uses of DREGNO.  */
-static void
-dead_debug_reset (struct dead_debug *debug, unsigned int dregno)
-{
-  struct dead_debug_use **tailp = &debug->head;
-  struct dead_debug_use *cur;
-  rtx insn;
-
-  if (!debug->used || !bitmap_clear_bit (debug->used, dregno))
-    return;
-
-  while ((cur = *tailp))
-    {
-      if (DF_REF_REGNO (cur->use) == dregno)
-       {
-         *tailp = cur->next;
-         insn = DF_REF_INSN (cur->use);
-         INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC ();
-         if (debug->to_rescan == NULL)
-           debug->to_rescan = BITMAP_ALLOC (NULL);
-         bitmap_set_bit (debug->to_rescan, INSN_UID (insn));
-         XDELETE (cur);
-       }
-      else
-       tailp = &(*tailp)->next;
-    }
-}
-
 /* Add USE to DEBUG.  It must be a dead reference to UREGNO in a debug
    insn.  Create a bitmap for DEBUG as needed.  */
-static inline void
+void
 dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
 {
   struct dead_debug_use *newddu = XNEW (struct dead_debug_use);
@@ -3132,27 +3179,34 @@ dead_debug_add (struct dead_debug *debug, df_ref use, unsigned int uregno)
   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);
 }
 
 /* If UREGNO is referenced by any entry in DEBUG, emit a debug insn
-   before INSN that binds the REG to a debug temp, and replace all
-   uses of UREGNO in DEBUG with uses of the debug temp.  INSN must be
-   the insn where UREGNO dies.  */
-static inline void
-dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
-                         rtx 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.  */
+int
+dead_debug_insert_temp (struct dead_debug *debug, unsigned int uregno,
+                       rtx 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 breg;
   rtx dval;
   rtx bind;
 
   if (!debug->used || !bitmap_clear_bit (debug->used, uregno))
-    return;
+    return 0;
 
   /* Move all uses of uregno from debug->head to uses, setting mode to
      the widest referenced mode.  */
@@ -3173,17 +3227,126 @@ dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
        tailp = &(*tailp)->next;
     }
 
-  gcc_assert (reg);
+  /* 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);
+      return 0;
+    }
+
+  gcc_checking_assert (uses);
+
+  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 = copy_rtx (src);
+      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 (REGNO (reg) < FIRST_PSEUDO_REGISTER
+                  && (hard_regno_nregs[REGNO (reg)][GET_MODE (reg)]
+                      != hard_regno_nregs[REGNO (reg)][GET_MODE (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), copy_rtx (src),
+                                  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
+                  && (hard_regno_nregs[REGNO (reg)][GET_MODE (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), copy_rtx (src),
+                                  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 next = DF_REF_INSN (uses->use);
+
+      if (DEBUG_INSN_P (next) && reg == INSN_VAR_LOCATION_LOC (next))
+       {
+         XDELETE (uses);
+         return 0;
+       }
+    }
 
   /* 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), reg,
+                              DEBUG_EXPR_TREE_DECL (dval), breg,
                               VAR_INIT_STATUS_INITIALIZED);
 
-  bind = emit_debug_insn_before (bind, insn);
+  if (where == DEBUG_TEMP_AFTER_WITH_REG)
+    bind = emit_debug_insn_after (bind, insn);
+  else
+    bind = emit_debug_insn_before (bind, insn);
   df_insn_rescan (bind);
 
   /* Adjust all uses.  */
@@ -3201,6 +3364,8 @@ dead_debug_insert_before (struct dead_debug *debug, unsigned int uregno,
       uses = cur->next;
       XDELETE (cur);
     }
+
+  return 1;
 }
 
 /* Recompute the REG_DEAD and REG_UNUSED notes and compute register
@@ -3278,7 +3443,7 @@ df_note_bb_compute (unsigned int bb_index,
       debug_insn = DEBUG_INSN_P (insn);
 
       bitmap_clear (do_not_gen);
-      df_kill_notes (insn);
+      df_kill_notes (insn, live);
 
       /* Process the defs.  */
       if (CALL_P (insn))
@@ -3355,7 +3520,7 @@ df_note_bb_compute (unsigned int bb_index,
       while (*mws_rec)
        {
          struct df_mw_hardreg *mws = *mws_rec;
-         if ((DF_MWS_REG_DEF_P (mws))
+         if (DF_MWS_REG_USE_P (mws)
              && !df_ignore_stack_reg (mws->start_regno))
            {
              bool really_add_notes = debug_insn != 0;
@@ -3389,13 +3554,19 @@ df_note_bb_compute (unsigned int bb_index,
                {
                  if (debug_insn > 0)
                    {
-                     dead_debug_add (&debug, use, uregno);
+                     /* We won't add REG_UNUSED or REG_DEAD notes for
+                        these, so we don't have to mess with them in
+                        debug insns either.  */
+                     if (!bitmap_bit_p (artificial_uses, uregno)
+                         && !df_ignore_stack_reg (uregno))
+                       dead_debug_add (&debug, use, uregno);
                      continue;
                    }
                  break;
                }
              else
-               dead_debug_insert_before (&debug, uregno, insn);
+               dead_debug_insert_temp (&debug, uregno, insn,
+                                       DEBUG_TEMP_BEFORE_WITH_REG);
 
              if ( (!(DF_REF_FLAGS (use)
                      & (DF_REF_MW_HARDREG | DF_REF_READ_WRITE)))
@@ -3774,12 +3945,9 @@ df_simulate_one_insn_forwards (basic_block bb, rtx insn, bitmap live)
          {
            rtx reg = XEXP (link, 0);
            int regno = REGNO (reg);
-           if (regno < FIRST_PSEUDO_REGISTER)
-             {
-               int n = hard_regno_nregs[regno][GET_MODE (reg)];
-               while (--n >= 0)
-                 bitmap_clear_bit (live, regno + n);
-             }
+           if (HARD_REGISTER_NUM_P (regno))
+             bitmap_clear_range (live, regno,
+                                 hard_regno_nregs[regno][GET_MODE (reg)]);
            else
              bitmap_clear_bit (live, regno);
          }
@@ -4004,7 +4172,10 @@ can_move_insns_across (rtx from, rtx to, rtx across_from, rtx across_to,
          if (bitmap_intersect_p (merge_set, test_use)
              || bitmap_intersect_p (merge_use, test_set))
            break;
-         max_to = insn;
+#ifdef HAVE_cc0
+         if (!sets_cc0_p (insn))
+#endif
+           max_to = insn;
        }
       next = NEXT_INSN (insn);
       if (insn == to)
@@ -4041,7 +4212,11 @@ can_move_insns_across (rtx from, rtx to, rtx across_from, rtx across_to,
     {
       if (NONDEBUG_INSN_P (insn))
        {
-         if (!bitmap_intersect_p (test_set, local_merge_live))
+         if (!bitmap_intersect_p (test_set, local_merge_live)
+#ifdef HAVE_cc0
+             && !sets_cc0_p (insn)
+#endif
+             )
            {
              max_to = insn;
              break;