OSDN Git Service

* lower-subreg.c: Include except.h.
[pf3gnuchains/gcc-fork.git] / gcc / lower-subreg.c
index ce9cfc5..e911205 100644 (file)
@@ -35,6 +35,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "recog.h"
 #include "bitmap.h"
 #include "expr.h"
+#include "except.h"
 #include "regs.h"
 #include "tree-pass.h"
 
@@ -66,6 +67,32 @@ static bitmap non_decomposable_context;
    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.  */
@@ -87,25 +114,16 @@ simple_move (rtx insn)
   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;
-  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
@@ -119,6 +137,11 @@ simple_move (rtx insn)
          == 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;
 }
 
@@ -259,7 +282,7 @@ find_decomposable_subregs (rtx *px, void *data)
          return -1;
        }
     }
-  else if (GET_CODE (x) == REG)
+  else if (REG_P (x))
     {
       unsigned int regno;
 
@@ -299,6 +322,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;
 }
@@ -469,7 +502,7 @@ resolve_subreg_use (rtx *px, void *data)
         that the note must be removed.  */
       if (!x)
        {
-         gcc_assert(!insn);
+         gcc_assert (!insn);
          return 1;
        }
 
@@ -489,6 +522,31 @@ resolve_subreg_use (rtx *px, void *data)
   return 0;
 }
 
+/* We are deleting INSN.  Move any EH_REGION notes to INSNS.  */
+
+static void
+move_eh_region_note (rtx insn, rtx insns)
+{
+  rtx note, p;
+
+  note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+  if (note == NULL_RTX)
+    return;
+
+  gcc_assert (CALL_P (insn)
+             || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))));
+
+  for (p = insns; p != NULL_RTX; p = NEXT_INSN (p))
+    {
+      if (CALL_P (p)
+         || (flag_non_call_exceptions
+             && INSN_P (p)
+             && may_trap_p (PATTERN (p))))
+       REG_NOTES (p) = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (note, 0),
+                                          REG_NOTES (p));
+    }
+}
+
 /* 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.  */
 
@@ -585,22 +643,23 @@ resolve_reg_notes (rtx insn)
     }
 }
 
-/* Return whether X can not be decomposed into subwords.  */
+/* Return whether X can be decomposed into subwords.  */
 
 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))
-       return !validate_subreg (word_mode, GET_MODE (x), x, UNITS_PER_WORD);
+       return (validate_subreg (word_mode, GET_MODE (x), x, UNITS_PER_WORD)
+               && HARD_REGNO_MODE_OK (regno, word_mode));
       else
-       return bitmap_bit_p (non_decomposable_context, regno);
+       return !bitmap_bit_p (non_decomposable_context, regno);
     }
 
-  return false;
+  return true;
 }
 
 /* Decompose the registers used in a simple move SET within INSN.  If
@@ -678,10 +737,27 @@ resolve_simple_move (rtx set, rtx 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 (cannot_decompose_p (src)
+  if (!can_decompose_p (src)
       || side_effects_p (src)
       || GET_CODE (src) == ASM_OPERANDS)
     {
@@ -701,7 +777,7 @@ resolve_simple_move (rtx set, rtx insn)
 
   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)
@@ -787,6 +863,8 @@ resolve_simple_move (rtx set, rtx insn)
   insns = get_insns ();
   end_sequence ();
 
+  move_eh_region_note (insn, insns);
+
   emit_insn_before (insns, insn);
 
   move_libcall_note (insn, insns);
@@ -805,21 +883,28 @@ resolve_clobber (rtx pat, rtx insn)
   rtx reg;
   enum machine_mode orig_mode;
   unsigned int words, i;
+  int ret;
 
   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;
 
-  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);
+  gcc_assert (ret != 0);
+
   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);
     }
@@ -965,15 +1050,20 @@ decompose_multiword_subregs (bool update_life)
     {
       int hold_no_new_pseudos = no_new_pseudos;
       int max_regno = max_reg_num ();
-      sbitmap blocks;
+      sbitmap life_blocks;
+      sbitmap sub_blocks;
+      unsigned int i;
+      sbitmap_iterator sbi;
       bitmap_iterator iter;
       unsigned int regno;
 
       propagate_pseudo_copies ();
 
       no_new_pseudos = 0;
-      blocks = sbitmap_alloc (last_basic_block);
-      sbitmap_zero (blocks);
+      life_blocks = sbitmap_alloc (last_basic_block);
+      sbitmap_zero (life_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);
@@ -1016,6 +1106,22 @@ decompose_multiword_subregs (bool update_life)
                  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
+                                 || (flag_non_call_exceptions
+                                     && can_throw_internal (insn)));
 
                      insn = resolve_simple_move (set, insn);
                      if (insn != orig_insn)
@@ -1024,6 +1130,9 @@ decompose_multiword_subregs (bool update_life)
 
                          recog_memoized (insn);
                          extract_insn (insn);
+
+                         if (cfi)
+                           SET_BIT (sub_blocks, bb->index);
                        }
                    }
 
@@ -1054,7 +1163,7 @@ decompose_multiword_subregs (bool update_life)
 
              if (changed)
                {
-                 SET_BIT (blocks, bb->index);
+                 SET_BIT (life_blocks, bb->index);
                  reg_scan_update (insn, next, max_regno);
                }
            }
@@ -1063,10 +1172,41 @@ decompose_multiword_subregs (bool update_life)
       no_new_pseudos = hold_no_new_pseudos;
 
       if (update_life)
-       update_life_info (blocks, UPDATE_LIFE_GLOBAL_RM_NOTES,
+       update_life_info (life_blocks, UPDATE_LIFE_GLOBAL_RM_NOTES,
                          PROP_DEATH_NOTES);
 
-      sbitmap_free (blocks);
+      /* 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;
+
+         bb = BASIC_BLOCK (i);
+         insn = BB_HEAD (bb);
+         end = BB_END (bb);
+
+         while (insn != end)
+           {
+             if (control_flow_insn_p (insn))
+               {
+                 /* 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);
+           }
+       }
+
+      sbitmap_free (life_blocks);
+      sbitmap_free (sub_blocks);
     }
 
   {
@@ -1124,7 +1264,8 @@ struct tree_opt_pass pass_lower_subreg =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_dump_func |
-  TODO_ggc_collect,                     /* todo_flags_finish */
+  TODO_ggc_collect |
+  TODO_verify_flow,                     /* todo_flags_finish */
   'u'                                   /* letter */
 };
 
@@ -1142,6 +1283,7 @@ struct tree_opt_pass pass_lower_subreg2 =
   0,                                    /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_dump_func |
-  TODO_ggc_collect,                     /* todo_flags_finish */
+  TODO_ggc_collect |
+  TODO_verify_flow,                     /* todo_flags_finish */
   'U'                                   /* letter */
 };