OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / lower-subreg.c
index ce9cfc5..89f3044 100644 (file)
@@ -1,5 +1,6 @@
 /* Decompose multiword subregs.
 /* Decompose multiword subregs.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
+   Free Software Foundation, Inc.
    Contributed by Richard Henderson <rth@redhat.com>
                  Ian Lance Taylor <iant@google.com>
 
    Contributed by Richard Henderson <rth@redhat.com>
                  Ian Lance Taylor <iant@google.com>
 
@@ -7,7 +8,7 @@ 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
 
 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 2, or (at your option) any later
+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
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -16,9 +17,8 @@ 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
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
 
 #include "config.h"
 #include "system.h"
@@ -34,9 +34,12 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "basic-block.h"
 #include "recog.h"
 #include "bitmap.h"
 #include "basic-block.h"
 #include "recog.h"
 #include "bitmap.h"
+#include "dce.h"
 #include "expr.h"
 #include "expr.h"
+#include "except.h"
 #include "regs.h"
 #include "tree-pass.h"
 #include "regs.h"
 #include "tree-pass.h"
+#include "df.h"
 
 #ifdef STACK_GROWS_DOWNWARD
 # undef STACK_GROWS_DOWNWARD
 
 #ifdef STACK_GROWS_DOWNWARD
 # undef STACK_GROWS_DOWNWARD
@@ -62,10 +65,42 @@ static bitmap decomposable_context;
    which it can not be decomposed.  */
 static bitmap non_decomposable_context;
 
    which it can not be decomposed.  */
 static bitmap non_decomposable_context;
 
+/* Bit N in this bitmap is set if regno N is used in a subreg
+   which changes the mode but not the size.  This typically happens
+   when the register accessed as a floating-point value; we want to
+   avoid generating accesses to its subwords in integer modes.  */
+static bitmap subreg_context;
+
 /* Bit N in the bitmap in element M of this array is set if there is a
    copy from reg M to reg N.  */
 static VEC(bitmap,heap) *reg_copy_graph;
 
 /* Bit N in the bitmap in element M of this array is set if there is a
    copy from reg M to reg N.  */
 static VEC(bitmap,heap) *reg_copy_graph;
 
+/* Return whether X is a simple object which we can take a word_mode
+   subreg of.  */
+
+static bool
+simple_move_operand (rtx x)
+{
+  if (GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
+
+  if (!OBJECT_P (x))
+    return false;
+
+  if (GET_CODE (x) == LABEL_REF
+      || GET_CODE (x) == SYMBOL_REF
+      || GET_CODE (x) == HIGH
+      || GET_CODE (x) == CONST)
+    return false;
+
+  if (MEM_P (x)
+      && (MEM_VOLATILE_P (x)
+         || mode_dependent_address_p (XEXP (x, 0))))
+    return false;
+
+  return true;
+}
+
 /* If INSN is a single set between two objects, return the single set.
    Such an insn can always be decomposed.  INSN should have been
    passed to recog and extract_insn before this is called.  */
 /* If INSN is a single set between two objects, return the single set.
    Such an insn can always be decomposed.  INSN should have been
    passed to recog and extract_insn before this is called.  */
@@ -87,25 +122,16 @@ simple_move (rtx insn)
   x = SET_DEST (set);
   if (x != recog_data.operand[0] && x != recog_data.operand[1])
     return NULL_RTX;
   x = SET_DEST (set);
   if (x != recog_data.operand[0] && x != recog_data.operand[1])
     return NULL_RTX;
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!OBJECT_P (x))
-    return NULL_RTX;
-  if (MEM_P (x)
-      && (MEM_VOLATILE_P (x)
-         || mode_dependent_address_p (XEXP (x, 0))))
+  if (!simple_move_operand (x))
     return NULL_RTX;
 
   x = SET_SRC (set);
   if (x != recog_data.operand[0] && x != recog_data.operand[1])
     return NULL_RTX;
     return NULL_RTX;
 
   x = SET_SRC (set);
   if (x != recog_data.operand[0] && x != recog_data.operand[1])
     return NULL_RTX;
-  if (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-  if (!OBJECT_P (x) && GET_CODE (x) != ASM_OPERANDS)
-    return NULL_RTX;
-  if (MEM_P (x)
-      && (MEM_VOLATILE_P (x)
-         || mode_dependent_address_p (XEXP (x, 0))))
+  /* For the src we can handle ASM_OPERANDS, and it is beneficial for
+     things like x86 rdtsc which returns a DImode value.  */
+  if (GET_CODE (x) != ASM_OPERANDS
+      && !simple_move_operand (x))
     return NULL_RTX;
 
   /* We try to decompose in integer modes, to avoid generating
     return NULL_RTX;
 
   /* We try to decompose in integer modes, to avoid generating
@@ -119,6 +145,11 @@ simple_move (rtx insn)
          == BLKmode))
     return NULL_RTX;
 
          == BLKmode))
     return NULL_RTX;
 
+  /* Reject PARTIAL_INT modes.  They are used for processor specific
+     purposes and it's probably best not to tamper with them.  */
+  if (GET_MODE_CLASS (mode) == MODE_PARTIAL_INT)
+    return NULL_RTX;
+
   return set;
 }
 
   return set;
 }
 
@@ -202,11 +233,9 @@ enum classify_move_insn
 {
   /* Not a simple move from one location to another.  */
   NOT_SIMPLE_MOVE,
 {
   /* Not a simple move from one location to another.  */
   NOT_SIMPLE_MOVE,
-  /* A simple move from one pseudo-register to another with no
-     REG_RETVAL note.  */
+  /* A simple move from one pseudo-register to another.  */
   SIMPLE_PSEUDO_REG_MOVE,
   SIMPLE_PSEUDO_REG_MOVE,
-  /* A simple move involving a non-pseudo-register, or from one
-     pseudo-register to another with a REG_RETVAL note.  */
+  /* A simple move involving a non-pseudo-register.  */
   SIMPLE_MOVE
 };
 
   SIMPLE_MOVE
 };
 
@@ -258,8 +287,21 @@ find_decomposable_subregs (rtx *px, void *data)
          bitmap_set_bit (decomposable_context, regno);
          return -1;
        }
          bitmap_set_bit (decomposable_context, regno);
          return -1;
        }
+
+      /* If this is a cast from one mode to another, where the modes
+        have the same size, and they are not tieable, then mark this
+        register as non-decomposable.  If we decompose it we are
+        likely to mess up whatever the backend is trying to do.  */
+      if (outer_words > 1
+         && outer_size == inner_size
+         && !MODES_TIEABLE_P (GET_MODE (x), GET_MODE (inner)))
+       {
+         bitmap_set_bit (non_decomposable_context, regno);
+         bitmap_set_bit (subreg_context, regno);
+         return -1;
+       }
     }
     }
-  else if (GET_CODE (x) == REG)
+  else if (REG_P (x))
     {
       unsigned int regno;
 
     {
       unsigned int regno;
 
@@ -269,10 +311,10 @@ find_decomposable_subregs (rtx *px, void *data)
 
         If this is not a simple copy from one location to another,
         then we can not decompose this register.  If this is a simple
 
         If this is not a simple copy from one location to another,
         then we can not decompose this register.  If this is a simple
-        copy from one pseudo-register to another, with no REG_RETVAL
-        note, and the mode is right, then we mark the register as
-        decomposable.  Otherwise we don't say anything about this
-        register--it could be decomposed, but whether that would be
+        copy from one pseudo-register to another, and the mode is right
+        then we mark the register as decomposable.
+        Otherwise we don't say anything about this register --
+        it could be decomposed, but whether that would be
         profitable depends upon how it is used elsewhere.
 
         We only set bits in the bitmap for multi-word
         profitable depends upon how it is used elsewhere.
 
         We only set bits in the bitmap for multi-word
@@ -299,6 +341,16 @@ find_decomposable_subregs (rtx *px, void *data)
            }
        }
     }
            }
        }
     }
+  else if (MEM_P (x))
+    {
+      enum classify_move_insn cmi_mem = NOT_SIMPLE_MOVE;
+
+      /* Any registers used in a MEM do not participate in a
+        SIMPLE_MOVE or SIMPLE_PSEUDO_REG_MOVE.  Do our own recursion
+        here, and return -1 to block the parent's recursion.  */
+      for_each_rtx (&XEXP (x, 0), find_decomposable_subregs, &cmi_mem);
+      return -1;
+    }
 
   return 0;
 }
 
   return 0;
 }
@@ -318,7 +370,6 @@ decompose_register (unsigned int regno)
   reg = regno_reg_rtx[regno];
 
   regno_reg_rtx[regno] = NULL_RTX;
   reg = regno_reg_rtx[regno];
 
   regno_reg_rtx[regno] = NULL_RTX;
-  clear_reg_info_regno (regno);
 
   words = GET_MODE_SIZE (GET_MODE (reg));
   words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
   words = GET_MODE_SIZE (GET_MODE (reg));
   words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
@@ -346,7 +397,7 @@ simplify_subreg_concatn (enum machine_mode outermode, rtx op,
                         unsigned int byte)
 {
   unsigned int inner_size;
                         unsigned int byte)
 {
   unsigned int inner_size;
-  enum machine_mode innermode;
+  enum machine_mode innermode, partmode;
   rtx part;
   unsigned int final_offset;
 
   rtx part;
   unsigned int final_offset;
 
@@ -359,11 +410,24 @@ simplify_subreg_concatn (enum machine_mode outermode, rtx op,
 
   inner_size = GET_MODE_SIZE (innermode) / XVECLEN (op, 0);
   part = XVECEXP (op, 0, byte / inner_size);
 
   inner_size = GET_MODE_SIZE (innermode) / XVECLEN (op, 0);
   part = XVECEXP (op, 0, byte / inner_size);
+  partmode = GET_MODE (part);
+
+  /* VECTOR_CSTs in debug expressions are expanded into CONCATN instead of
+     regular CONST_VECTORs.  They have vector or integer modes, depending
+     on the capabilities of the target.  Cope with them.  */
+  if (partmode == VOIDmode && VECTOR_MODE_P (innermode))
+    partmode = GET_MODE_INNER (innermode);
+  else if (partmode == VOIDmode)
+    {
+      enum mode_class mclass = GET_MODE_CLASS (innermode);
+      partmode = mode_for_size (inner_size * BITS_PER_UNIT, mclass, 0);
+    }
+
   final_offset = byte % inner_size;
   if (final_offset + GET_MODE_SIZE (outermode) > inner_size)
     return NULL_RTX;
 
   final_offset = byte % inner_size;
   if (final_offset + GET_MODE_SIZE (outermode) > inner_size)
     return NULL_RTX;
 
-  return simplify_gen_subreg (outermode, part, GET_MODE (part), final_offset);
+  return simplify_gen_subreg (outermode, part, partmode, final_offset);
 }
 
 /* Wrapper around simplify_gen_subreg which handles CONCATN.  */
 }
 
 /* Wrapper around simplify_gen_subreg which handles CONCATN.  */
@@ -469,7 +533,7 @@ resolve_subreg_use (rtx *px, void *data)
         that the note must be removed.  */
       if (!x)
        {
         that the note must be removed.  */
       if (!x)
        {
-         gcc_assert(!insn);
+         gcc_assert (!insn);
          return 1;
        }
 
          return 1;
        }
 
@@ -481,66 +545,40 @@ resolve_subreg_use (rtx *px, void *data)
     {
       /* Return 1 to the caller to indicate that we found a direct
         reference to a register which is being decomposed.  This can
     {
       /* Return 1 to the caller to indicate that we found a direct
         reference to a register which is being decomposed.  This can
-        happen inside notes.  */
-      gcc_assert (!insn);
+        happen inside notes, multiword shift or zero-extend
+        instructions.  */
       return 1;
     }
 
   return 0;
 }
 
       return 1;
     }
 
   return 0;
 }
 
-/* If there is a REG_LIBCALL note on OLD_START, move it to NEW_START,
-   and link the corresponding REG_RETVAL note to NEW_START.  */
-
-static void
-move_libcall_note (rtx old_start, rtx new_start)
-{
-  rtx note0, note1, end;
-
-  note0 = find_reg_note (old_start, REG_LIBCALL, NULL);
-  if (note0 == NULL_RTX)
-    return;
-
-  remove_note (old_start, note0);
-  end = XEXP (note0, 0);
-  note1 = find_reg_note (end, REG_RETVAL, NULL);
-
-  XEXP (note0, 1) = REG_NOTES (new_start);
-  REG_NOTES (new_start) = note0;
-  XEXP (note1, 0) = new_start;
-}
-
-/* Remove any REG_RETVAL note, the corresponding REG_LIBCALL note, and
-   any markers for a no-conflict block.  We have decomposed the
-   registers so the non-conflict is now obvious.  */
+/* This is called via for_each_rtx.  Look for SUBREGs which can be
+   decomposed and decomposed REGs that need copying.  */
 
 
-static void
-remove_retval_note (rtx insn1)
+static int
+adjust_decomposed_uses (rtx *px, void *data ATTRIBUTE_UNUSED)
 {
 {
-  rtx note0, insn0, note1, insn;
-
-  note1 = find_reg_note (insn1, REG_RETVAL, NULL);
-  if (note1 == NULL_RTX)
-    return;
-
-  insn0 = XEXP (note1, 0);
-  note0 = find_reg_note (insn0, REG_LIBCALL, NULL);
+  rtx x = *px;
 
 
-  remove_note (insn0, note0);
-  remove_note (insn1, note1);
+  if (x == NULL_RTX)
+    return 0;
 
 
-  for (insn = insn0; insn != insn1; insn = NEXT_INSN (insn))
+  if (resolve_subreg_p (x))
     {
     {
-      while (1)
-       {
-         rtx note;
+      x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
+                                  SUBREG_BYTE (x));
 
 
-         note = find_reg_note (insn, REG_NO_CONFLICT, NULL);
-         if (note == NULL_RTX)
-           break;
-         remove_note (insn, note);
-       }
+      if (x)
+       *px = x;
+      else
+       x = copy_rtx (*px);
     }
     }
+
+  if (resolve_reg_p (x))
+    *px = copy_rtx (x);
+
+  return 0;
 }
 
 /* Resolve any decomposed registers which appear in register notes on
 }
 
 /* Resolve any decomposed registers which appear in register notes on
@@ -554,53 +592,63 @@ resolve_reg_notes (rtx insn)
   note = find_reg_equal_equiv_note (insn);
   if (note)
     {
   note = find_reg_equal_equiv_note (insn);
   if (note)
     {
+      int old_count = num_validated_changes ();
       if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL))
       if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL))
-       {
-         remove_note (insn, note);
-         remove_retval_note (insn);
-       }
+       remove_note (insn, note);
+      else
+       if (old_count != num_validated_changes ())
+         df_notes_rescan (insn);
     }
 
   pnote = &REG_NOTES (insn);
   while (*pnote != NULL_RTX)
     {
     }
 
   pnote = &REG_NOTES (insn);
   while (*pnote != NULL_RTX)
     {
-      bool delete = false;
+      bool del = false;
 
       note = *pnote;
       switch (REG_NOTE_KIND (note))
        {
 
       note = *pnote;
       switch (REG_NOTE_KIND (note))
        {
-       case REG_NO_CONFLICT:
+       case REG_DEAD:
+       case REG_UNUSED:
          if (resolve_reg_p (XEXP (note, 0)))
          if (resolve_reg_p (XEXP (note, 0)))
-           delete = true;
+           del = true;
          break;
 
        default:
          break;
        }
 
          break;
 
        default:
          break;
        }
 
-      if (delete)
+      if (del)
        *pnote = XEXP (note, 1);
       else
        pnote = &XEXP (note, 1);
     }
 }
 
        *pnote = XEXP (note, 1);
       else
        pnote = &XEXP (note, 1);
     }
 }
 
-/* Return whether X can not be decomposed into subwords.  */
+/* Return whether X can be decomposed into subwords.  */
 
 static bool
 
 static bool
-cannot_decompose_p (rtx x)
+can_decompose_p (rtx x)
 {
   if (REG_P (x))
     {
       unsigned int regno = REGNO (x);
 
       if (HARD_REGISTER_NUM_P (regno))
 {
   if (REG_P (x))
     {
       unsigned int regno = REGNO (x);
 
       if (HARD_REGISTER_NUM_P (regno))
-       return !validate_subreg (word_mode, GET_MODE (x), x, UNITS_PER_WORD);
+       {
+         unsigned int byte, num_bytes;
+
+         num_bytes = GET_MODE_SIZE (GET_MODE (x));
+         for (byte = 0; byte < num_bytes; byte += UNITS_PER_WORD)
+           if (simplify_subreg_regno (regno, GET_MODE (x), byte, word_mode) < 0)
+             return false;
+         return true;
+       }
       else
       else
-       return bitmap_bit_p (non_decomposable_context, regno);
+       return !bitmap_bit_p (subreg_context, regno);
     }
 
     }
 
-  return false;
+  return true;
 }
 
 /* Decompose the registers used in a simple move SET within INSN.  If
 }
 
 /* Decompose the registers used in a simple move SET within INSN.  If
@@ -678,10 +726,27 @@ resolve_simple_move (rtx set, rtx insn)
       return insn;
     }
 
       return insn;
     }
 
+  /* It's possible for the code to use a subreg of a decomposed
+     register while forming an address.  We need to handle that before
+     passing the address to emit_move_insn.  We pass NULL_RTX as the
+     insn parameter to resolve_subreg_use because we can not validate
+     the insn yet.  */
+  if (MEM_P (src) || MEM_P (dest))
+    {
+      int acg;
+
+      if (MEM_P (src))
+       for_each_rtx (&XEXP (src, 0), resolve_subreg_use, NULL_RTX);
+      if (MEM_P (dest))
+       for_each_rtx (&XEXP (dest, 0), resolve_subreg_use, NULL_RTX);
+      acg = apply_change_group ();
+      gcc_assert (acg);
+    }
+
   /* If SRC is a register which we can't decompose, or has side
      effects, we need to move via a temporary register.  */
 
   /* If SRC is a register which we can't decompose, or has side
      effects, we need to move via a temporary register.  */
 
-  if (cannot_decompose_p (src)
+  if (!can_decompose_p (src)
       || side_effects_p (src)
       || GET_CODE (src) == ASM_OPERANDS)
     {
       || side_effects_p (src)
       || GET_CODE (src) == ASM_OPERANDS)
     {
@@ -701,7 +766,7 @@ resolve_simple_move (rtx set, rtx insn)
 
   dest_mode = orig_mode;
   pushing = push_operand (dest, dest_mode);
 
   dest_mode = orig_mode;
   pushing = push_operand (dest, dest_mode);
-  if (cannot_decompose_p (dest)
+  if (!can_decompose_p (dest)
       || (side_effects_p (dest) && !pushing)
       || (!SCALAR_INT_MODE_P (dest_mode)
          && !resolve_reg_p (dest)
       || (side_effects_p (dest) && !pushing)
       || (!SCALAR_INT_MODE_P (dest_mode)
          && !resolve_reg_p (dest)
@@ -757,7 +822,7 @@ resolve_simple_move (rtx set, rtx insn)
       unsigned int i;
 
       if (REG_P (dest) && !HARD_REGISTER_NUM_P (REGNO (dest)))
       unsigned int i;
 
       if (REG_P (dest) && !HARD_REGISTER_NUM_P (REGNO (dest)))
-       emit_insn (gen_rtx_CLOBBER (VOIDmode, dest));
+       emit_clobber (dest);
 
       for (i = 0; i < words; ++i)
        emit_move_insn (simplify_gen_subreg_concatn (word_mode, dest,
 
       for (i = 0; i < words; ++i)
        emit_move_insn (simplify_gen_subreg_concatn (word_mode, dest,
@@ -787,10 +852,10 @@ resolve_simple_move (rtx set, rtx insn)
   insns = get_insns ();
   end_sequence ();
 
   insns = get_insns ();
   end_sequence ();
 
+  copy_reg_eh_region_note_forward (insn, insns, NULL_RTX);
+
   emit_insn_before (insns, insn);
 
   emit_insn_before (insns, insn);
 
-  move_libcall_note (insn, insns);
-  remove_retval_note (insn);
   delete_insn (insn);
 
   return insns;
   delete_insn (insn);
 
   return insns;
@@ -805,25 +870,35 @@ resolve_clobber (rtx pat, rtx insn)
   rtx reg;
   enum machine_mode orig_mode;
   unsigned int words, i;
   rtx reg;
   enum machine_mode orig_mode;
   unsigned int words, i;
+  int ret;
 
   reg = XEXP (pat, 0);
 
   reg = XEXP (pat, 0);
-  if (!resolve_reg_p (reg))
+  if (!resolve_reg_p (reg) && !resolve_subreg_p (reg))
     return false;
 
   orig_mode = GET_MODE (reg);
   words = GET_MODE_SIZE (orig_mode);
   words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
     return false;
 
   orig_mode = GET_MODE (reg);
   words = GET_MODE_SIZE (orig_mode);
   words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
-  XEXP (pat, 0) = simplify_subreg_concatn (word_mode, reg, 0);
+  ret = validate_change (NULL_RTX, &XEXP (pat, 0),
+                        simplify_gen_subreg_concatn (word_mode, reg,
+                                                     orig_mode, 0),
+                        0);
+  df_insn_rescan (insn);
+  gcc_assert (ret != 0);
+
   for (i = words - 1; i > 0; --i)
     {
       rtx x;
 
   for (i = words - 1; i > 0; --i)
     {
       rtx x;
 
-      x = simplify_subreg_concatn (word_mode, reg, i * UNITS_PER_WORD);
+      x = simplify_gen_subreg_concatn (word_mode, reg, orig_mode,
+                                      i * UNITS_PER_WORD);
       x = gen_rtx_CLOBBER (VOIDmode, x);
       emit_insn_after (x, insn);
     }
 
       x = gen_rtx_CLOBBER (VOIDmode, x);
       emit_insn_after (x, insn);
     }
 
+  resolve_reg_notes (insn);
+
   return true;
 }
 
   return true;
 }
 
@@ -838,19 +913,179 @@ resolve_use (rtx pat, rtx insn)
       delete_insn (insn);
       return true;
     }
       delete_insn (insn);
       return true;
     }
+
+  resolve_reg_notes (insn);
+
   return false;
 }
 
   return false;
 }
 
+/* A VAR_LOCATION can be simplified.  */
+
+static void
+resolve_debug (rtx insn)
+{
+  for_each_rtx (&PATTERN (insn), adjust_decomposed_uses, NULL_RTX);
+
+  df_insn_rescan (insn);
+
+  resolve_reg_notes (insn);
+}
+
+/* Checks if INSN is a decomposable multiword-shift or zero-extend and
+   sets the decomposable_context bitmap accordingly.  A non-zero value
+   is returned if a decomposable insn has been found.  */
+
+static int
+find_decomposable_shift_zext (rtx insn)
+{
+  rtx set;
+  rtx op;
+  rtx op_operand;
+
+  set = single_set (insn);
+  if (!set)
+    return 0;
+
+  op = SET_SRC (set);
+  if (GET_CODE (op) != ASHIFT
+      && GET_CODE (op) != LSHIFTRT
+      && GET_CODE (op) != ZERO_EXTEND)
+    return 0;
+
+  op_operand = XEXP (op, 0);
+  if (!REG_P (SET_DEST (set)) || !REG_P (op_operand)
+      || HARD_REGISTER_NUM_P (REGNO (SET_DEST (set)))
+      || HARD_REGISTER_NUM_P (REGNO (op_operand))
+      || !SCALAR_INT_MODE_P (GET_MODE (op)))
+    return 0;
+
+  if (GET_CODE (op) == ZERO_EXTEND)
+    {
+      if (GET_MODE (op_operand) != word_mode
+         || GET_MODE_BITSIZE (GET_MODE (op)) != 2 * BITS_PER_WORD)
+       return 0;
+    }
+  else /* left or right shift */
+    {
+      if (!CONST_INT_P (XEXP (op, 1))
+         || INTVAL (XEXP (op, 1)) < BITS_PER_WORD
+         || GET_MODE_BITSIZE (GET_MODE (op_operand)) != 2 * BITS_PER_WORD)
+       return 0;
+    }
+
+  bitmap_set_bit (decomposable_context, REGNO (SET_DEST (set)));
+
+  if (GET_CODE (op) != ZERO_EXTEND)
+    bitmap_set_bit (decomposable_context, REGNO (op_operand));
+
+  return 1;
+}
+
+/* Decompose a more than word wide shift (in INSN) of a multiword
+   pseudo or a multiword zero-extend of a wordmode pseudo into a move
+   and 'set to zero' insn.  Return a pointer to the new insn when a
+   replacement was done.  */
+
+static rtx
+resolve_shift_zext (rtx insn)
+{
+  rtx set;
+  rtx op;
+  rtx op_operand;
+  rtx insns;
+  rtx src_reg, dest_reg, dest_zero;
+  int src_reg_num, dest_reg_num, offset1, offset2, src_offset;
+
+  set = single_set (insn);
+  if (!set)
+    return NULL_RTX;
+
+  op = SET_SRC (set);
+  if (GET_CODE (op) != ASHIFT
+      && GET_CODE (op) != LSHIFTRT
+      && GET_CODE (op) != ZERO_EXTEND)
+    return NULL_RTX;
+
+  op_operand = XEXP (op, 0);
+
+  if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (op_operand))
+    return NULL_RTX;
+
+  /* src_reg_num is the number of the word mode register which we
+     are operating on.  For a left shift and a zero_extend on little
+     endian machines this is register 0.  */
+  src_reg_num = GET_CODE (op) == LSHIFTRT ? 1 : 0;
+
+  if (WORDS_BIG_ENDIAN
+      && GET_MODE_SIZE (GET_MODE (op_operand)) > UNITS_PER_WORD)
+    src_reg_num = 1 - src_reg_num;
+
+  if (GET_CODE (op) == ZERO_EXTEND)
+    dest_reg_num = WORDS_BIG_ENDIAN ? 1 : 0;
+  else
+    dest_reg_num = 1 - src_reg_num;
+
+  offset1 = UNITS_PER_WORD * dest_reg_num;
+  offset2 = UNITS_PER_WORD * (1 - dest_reg_num);
+  src_offset = UNITS_PER_WORD * src_reg_num;
+
+  start_sequence ();
+
+  dest_reg = simplify_gen_subreg_concatn (word_mode, SET_DEST (set),
+                                          GET_MODE (SET_DEST (set)),
+                                          offset1);
+  dest_zero = simplify_gen_subreg_concatn (word_mode, SET_DEST (set),
+                                           GET_MODE (SET_DEST (set)),
+                                           offset2);
+  src_reg = simplify_gen_subreg_concatn (word_mode, op_operand,
+                                         GET_MODE (op_operand),
+                                         src_offset);
+  if (GET_CODE (op) != ZERO_EXTEND)
+    {
+      int shift_count = INTVAL (XEXP (op, 1));
+      if (shift_count > BITS_PER_WORD)
+       src_reg = expand_shift (GET_CODE (op) == ASHIFT ?
+                               LSHIFT_EXPR : RSHIFT_EXPR,
+                               word_mode, src_reg,
+                               shift_count - BITS_PER_WORD,
+                               dest_reg, 1);
+    }
+
+  if (dest_reg != src_reg)
+    emit_move_insn (dest_reg, src_reg);
+  emit_move_insn (dest_zero, CONST0_RTX (word_mode));
+  insns = get_insns ();
+
+  end_sequence ();
+
+  emit_insn_before (insns, insn);
+
+  if (dump_file)
+    {
+      rtx in;
+      fprintf (dump_file, "; Replacing insn: %d with insns: ", INSN_UID (insn));
+      for (in = insns; in != insn; in = NEXT_INSN (in))
+       fprintf (dump_file, "%d ", INSN_UID (in));
+      fprintf (dump_file, "\n");
+    }
+
+  delete_insn (insn);
+  return insns;
+}
+
 /* Look for registers which are always accessed via word-sized SUBREGs
    or via copies.  Decompose these registers into several word-sized
    pseudo-registers.  */
 
 static void
 /* Look for registers which are always accessed via word-sized SUBREGs
    or via copies.  Decompose these registers into several word-sized
    pseudo-registers.  */
 
 static void
-decompose_multiword_subregs (bool update_life)
+decompose_multiword_subregs (void)
 {
   unsigned int max;
   basic_block bb;
 
 {
   unsigned int max;
   basic_block bb;
 
+  if (df)
+    df_set_flags (DF_DEFER_INSN_RESCAN);
+
   max = max_reg_num ();
 
   /* First see if there are any multi-word pseudo-registers.  If there
   max = max_reg_num ();
 
   /* First see if there are any multi-word pseudo-registers.  If there
@@ -870,6 +1105,9 @@ decompose_multiword_subregs (bool update_life)
       return;
   }
 
       return;
   }
 
+  if (df)
+    run_word_dce ();
+
   /* FIXME: When the dataflow branch is merged, we can change this
      code to look for each multi-word pseudo-register and to find each
      insn which sets or uses that register.  That should be faster
   /* FIXME: When the dataflow branch is merged, we can change this
      code to look for each multi-word pseudo-register and to find each
      insn which sets or uses that register.  That should be faster
@@ -877,6 +1115,7 @@ decompose_multiword_subregs (bool update_life)
 
   decomposable_context = BITMAP_ALLOC (NULL);
   non_decomposable_context = BITMAP_ALLOC (NULL);
 
   decomposable_context = BITMAP_ALLOC (NULL);
   non_decomposable_context = BITMAP_ALLOC (NULL);
+  subreg_context = BITMAP_ALLOC (NULL);
 
   reg_copy_graph = VEC_alloc (bitmap, heap, max);
   VEC_safe_grow (bitmap, heap, reg_copy_graph, max);
 
   reg_copy_graph = VEC_alloc (bitmap, heap, max);
   VEC_safe_grow (bitmap, heap, reg_copy_graph, max);
@@ -898,6 +1137,10 @@ decompose_multiword_subregs (bool update_life)
            continue;
 
          recog_memoized (insn);
            continue;
 
          recog_memoized (insn);
+
+         if (find_decomposable_shift_zext (insn))
+           continue;
+
          extract_insn (insn);
 
          set = simple_move (insn);
          extract_insn (insn);
 
          set = simple_move (insn);
@@ -906,34 +1149,8 @@ decompose_multiword_subregs (bool update_life)
            cmi = NOT_SIMPLE_MOVE;
          else
            {
            cmi = NOT_SIMPLE_MOVE;
          else
            {
-             bool retval;
-
-             retval = find_reg_note (insn, REG_RETVAL, NULL_RTX) != NULL_RTX;
-
-             if (find_pseudo_copy (set) && !retval)
+             if (find_pseudo_copy (set))
                cmi = SIMPLE_PSEUDO_REG_MOVE;
                cmi = SIMPLE_PSEUDO_REG_MOVE;
-             else if (retval
-                      && REG_P (SET_SRC (set))
-                      && HARD_REGISTER_P (SET_SRC (set)))
-               {
-                 rtx note;
-
-                 /* We don't want to decompose an assignment which
-                    copies the value returned by a libcall to a
-                    pseudo-register.  Doing that will lose the RETVAL
-                    note with no real gain.  */
-                 cmi = NOT_SIMPLE_MOVE;
-
-                 /* If we have a RETVAL note, there should be an
-                    EQUAL note.  We don't want to decompose any
-                    registers which that EQUAL note refers to
-                    directly.  If we do, we will no longer know the
-                    value of the libcall.  */
-                 note = find_reg_equal_equiv_note (insn);
-                 if (note != NULL_RTX)
-                   for_each_rtx (&XEXP (note, 0), find_decomposable_subregs,
-                                 &cmi);
-               }
              else
                cmi = SIMPLE_MOVE;
            }
              else
                cmi = SIMPLE_MOVE;
            }
@@ -963,17 +1180,16 @@ decompose_multiword_subregs (bool update_life)
   bitmap_and_compl_into (decomposable_context, non_decomposable_context);
   if (!bitmap_empty_p (decomposable_context))
     {
   bitmap_and_compl_into (decomposable_context, non_decomposable_context);
   if (!bitmap_empty_p (decomposable_context))
     {
-      int hold_no_new_pseudos = no_new_pseudos;
-      int max_regno = max_reg_num ();
-      sbitmap blocks;
+      sbitmap sub_blocks;
+      unsigned int i;
+      sbitmap_iterator sbi;
       bitmap_iterator iter;
       unsigned int regno;
 
       propagate_pseudo_copies ();
 
       bitmap_iterator iter;
       unsigned int regno;
 
       propagate_pseudo_copies ();
 
-      no_new_pseudos = 0;
-      blocks = sbitmap_alloc (last_basic_block);
-      sbitmap_zero (blocks);
+      sub_blocks = sbitmap_alloc (last_basic_block);
+      sbitmap_zero (sub_blocks);
 
       EXECUTE_IF_SET_IN_BITMAP (decomposable_context, 0, regno, iter)
        decompose_register (regno);
 
       EXECUTE_IF_SET_IN_BITMAP (decomposable_context, 0, regno, iter)
        decompose_register (regno);
@@ -984,26 +1200,18 @@ decompose_multiword_subregs (bool update_life)
 
          FOR_BB_INSNS (bb, insn)
            {
 
          FOR_BB_INSNS (bb, insn)
            {
-             rtx next, pat;
-             bool changed;
+             rtx pat;
 
              if (!INSN_P (insn))
                continue;
 
 
              if (!INSN_P (insn))
                continue;
 
-             next = NEXT_INSN (insn);
-             changed = false;
-
              pat = PATTERN (insn);
              if (GET_CODE (pat) == CLOBBER)
              pat = PATTERN (insn);
              if (GET_CODE (pat) == CLOBBER)
-               {
-                 if (resolve_clobber (pat, insn))
-                   changed = true;
-               }
+               resolve_clobber (pat, insn);
              else if (GET_CODE (pat) == USE)
              else if (GET_CODE (pat) == USE)
-               {
-                 if (resolve_use (pat, insn))
-                   changed = true;
-               }
+               resolve_use (pat, insn);
+             else if (DEBUG_INSN_P (insn))
+               resolve_debug (insn);
              else
                {
                  rtx set;
              else
                {
                  rtx set;
@@ -1016,12 +1224,41 @@ decompose_multiword_subregs (bool update_life)
                  if (set)
                    {
                      rtx orig_insn = insn;
                  if (set)
                    {
                      rtx orig_insn = insn;
+                     bool cfi = control_flow_insn_p (insn);
+
+                     /* We can end up splitting loads to multi-word pseudos
+                        into separate loads to machine word size pseudos.
+                        When this happens, we first had one load that can
+                        throw, and after resolve_simple_move we'll have a
+                        bunch of loads (at least two).  All those loads may
+                        trap if we can have non-call exceptions, so they
+                        all will end the current basic block.  We split the
+                        block after the outer loop over all insns, but we
+                        make sure here that we will be able to split the
+                        basic block and still produce the correct control
+                        flow graph for it.  */
+                     gcc_assert (!cfi
+                                 || (cfun->can_throw_non_call_exceptions
+                                     && can_throw_internal (insn)));
 
                      insn = resolve_simple_move (set, insn);
                      if (insn != orig_insn)
                        {
 
                      insn = resolve_simple_move (set, insn);
                      if (insn != orig_insn)
                        {
-                         changed = true;
+                         recog_memoized (insn);
+                         extract_insn (insn);
+
+                         if (cfi)
+                           SET_BIT (sub_blocks, bb->index);
+                       }
+                   }
+                 else
+                   {
+                     rtx decomposed_shift;
 
 
+                     decomposed_shift = resolve_shift_zext (insn);
+                     if (decomposed_shift != NULL_RTX)
+                       {
+                         insn = decomposed_shift;
                          recog_memoized (insn);
                          extract_insn (insn);
                        }
                          recog_memoized (insn);
                          extract_insn (insn);
                        }
@@ -1042,46 +1279,63 @@ decompose_multiword_subregs (bool update_life)
                          int dup_num = recog_data.dup_num[i];
                          rtx *px = recog_data.operand_loc[dup_num];
 
                          int dup_num = recog_data.dup_num[i];
                          rtx *px = recog_data.operand_loc[dup_num];
 
-                         validate_change (insn, pl, *px, 1);
+                         validate_unshare_change (insn, pl, *px, 1);
                        }
 
                      i = apply_change_group ();
                      gcc_assert (i);
                        }
 
                      i = apply_change_group ();
                      gcc_assert (i);
-
-                     changed = true;
                    }
                }
                    }
                }
+           }
+       }
+
+      /* If we had insns to split that caused control flow insns in the middle
+        of a basic block, split those blocks now.  Note that we only handle
+        the case where splitting a load has caused multiple possibly trapping
+        loads to appear.  */
+      EXECUTE_IF_SET_IN_SBITMAP (sub_blocks, 0, i, sbi)
+       {
+         rtx insn, end;
+         edge fallthru;
 
 
-             if (changed)
+         bb = BASIC_BLOCK (i);
+         insn = BB_HEAD (bb);
+         end = BB_END (bb);
+
+         while (insn != end)
+           {
+             if (control_flow_insn_p (insn))
                {
                {
-                 SET_BIT (blocks, bb->index);
-                 reg_scan_update (insn, next, max_regno);
+                 /* Split the block after insn.  There will be a fallthru
+                    edge, which is OK so we keep it.  We have to create the
+                    exception edges ourselves.  */
+                 fallthru = split_block (bb, insn);
+                 rtl_make_eh_edge (NULL, bb, BB_END (bb));
+                 bb = fallthru->dest;
+                 insn = BB_HEAD (bb);
                }
                }
+             else
+               insn = NEXT_INSN (insn);
            }
        }
 
            }
        }
 
-      no_new_pseudos = hold_no_new_pseudos;
-
-      if (update_life)
-       update_life_info (blocks, UPDATE_LIFE_GLOBAL_RM_NOTES,
-                         PROP_DEATH_NOTES);
-
-      sbitmap_free (blocks);
+      sbitmap_free (sub_blocks);
     }
 
   {
     unsigned int i;
     bitmap b;
 
     }
 
   {
     unsigned int i;
     bitmap b;
 
-    for (i = 0; VEC_iterate (bitmap, reg_copy_graph, i, b); ++i)
+    FOR_EACH_VEC_ELT (bitmap, reg_copy_graph, i, b)
       if (b)
        BITMAP_FREE (b);
   }
 
       if (b)
        BITMAP_FREE (b);
   }
 
-  VEC_free (bitmap, heap, reg_copy_graph);  
+  VEC_free (bitmap, heap, reg_copy_graph);
 
   BITMAP_FREE (decomposable_context);
   BITMAP_FREE (non_decomposable_context);
 
   BITMAP_FREE (decomposable_context);
   BITMAP_FREE (non_decomposable_context);
+  BITMAP_FREE (subreg_context);
 }
 \f
 /* Gate function for lower subreg pass.  */
 }
 \f
 /* Gate function for lower subreg pass.  */
@@ -1097,7 +1351,7 @@ gate_handle_lower_subreg (void)
 static unsigned int
 rest_of_handle_lower_subreg (void)
 {
 static unsigned int
 rest_of_handle_lower_subreg (void)
 {
-  decompose_multiword_subregs (false);
+  decompose_multiword_subregs ();
   return 0;
 }
 
   return 0;
 }
 
@@ -1106,13 +1360,15 @@ rest_of_handle_lower_subreg (void)
 static unsigned int
 rest_of_handle_lower_subreg2 (void)
 {
 static unsigned int
 rest_of_handle_lower_subreg2 (void)
 {
-  decompose_multiword_subregs (true);
+  decompose_multiword_subregs ();
   return 0;
 }
 
   return 0;
 }
 
-struct tree_opt_pass pass_lower_subreg =
+struct rtl_opt_pass pass_lower_subreg =
 {
 {
-  "subreg",                            /* name */
+ {
+  RTL_PASS,
+  "subreg1",                           /* name */
   gate_handle_lower_subreg,             /* gate */
   rest_of_handle_lower_subreg,          /* execute */
   NULL,                                 /* sub */
   gate_handle_lower_subreg,             /* gate */
   rest_of_handle_lower_subreg,          /* execute */
   NULL,                                 /* sub */
@@ -1123,13 +1379,15 @@ struct tree_opt_pass pass_lower_subreg =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func |
-  TODO_ggc_collect,                     /* todo_flags_finish */
-  'u'                                   /* letter */
+  TODO_ggc_collect |
+  TODO_verify_flow                      /* todo_flags_finish */
+ }
 };
 
 };
 
-struct tree_opt_pass pass_lower_subreg2 =
+struct rtl_opt_pass pass_lower_subreg2 =
 {
 {
+ {
+  RTL_PASS,
   "subreg2",                           /* name */
   gate_handle_lower_subreg,             /* gate */
   rest_of_handle_lower_subreg2,          /* execute */
   "subreg2",                           /* name */
   gate_handle_lower_subreg,             /* gate */
   rest_of_handle_lower_subreg2,          /* execute */
@@ -1141,7 +1399,8 @@ struct tree_opt_pass pass_lower_subreg2 =
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   0,                                    /* properties_provided */
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
-  TODO_dump_func |
-  TODO_ggc_collect,                     /* todo_flags_finish */
-  'U'                                   /* letter */
+  TODO_df_finish | TODO_verify_rtl_sharing |
+  TODO_ggc_collect |
+  TODO_verify_flow                      /* todo_flags_finish */
+ }
 };
 };