OSDN Git Service

2006-10-09 Brooks Moses <bmoses@stanford.edu>
[pf3gnuchains/gcc-fork.git] / gcc / loop-invariant.c
index a57857d..cf22949 100644 (file)
@@ -52,6 +52,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "flags.h"
 #include "df.h"
 #include "hashtab.h"
+#include "except.h"
 
 /* The data stored for the loop.  */
 
@@ -232,6 +233,9 @@ invariant_for_use (struct df_ref *use)
   struct df_ref *def;
   basic_block bb = BLOCK_FOR_INSN (use->insn), def_bb;
 
+  if (use->flags & DF_REF_READ_WRITE)
+    return NULL;
+
   defs = DF_REF_CHAIN (use);
   if (!defs || defs->next)
     return NULL;
@@ -475,7 +479,7 @@ find_identical_invariants (htab_t eq, struct invariant *inv)
 
   if (dump_file && inv->eqto != inv->invno)
     fprintf (dump_file,
-            "Invariant %d is equivalent to invariant %d.\n ",
+            "Invariant %d is equivalent to invariant %d.\n",
             inv->invno, inv->eqto);
 }
 
@@ -681,7 +685,8 @@ record_use (struct def *def, rtx *use, rtx insn)
 }
 
 /* Finds the invariants INSN depends on and store them to the DEPENDS_ON
-   bitmap.  */
+   bitmap.  Returns true if all dependencies of INSN are known to be
+   loop invariants, false otherwise.  */
 
 static bool
 check_dependencies (rtx insn, bitmap depends_on)
@@ -694,6 +699,9 @@ check_dependencies (rtx insn, bitmap depends_on)
 
   for (use = DF_INSN_GET (df, insn)->uses; use; use = use->next_ref)
     {
+      if (use->flags & DF_REF_READ_WRITE)
+       return false;
+
       defs = DF_REF_CHAIN (use);
       if (!defs)
        continue;
@@ -761,16 +769,14 @@ find_invariant_insn (rtx insn, bool always_reached, bool always_executed)
       || !check_maybe_invariant (SET_SRC (set)))
     return;
 
-  if (may_trap_p (PATTERN (insn)))
-    {
-      if (!always_reached)
-       return;
+  /* If the insn can throw exception, we cannot move it at all without changing
+     cfg.  */
+  if (can_throw_internal (insn))
+    return;
 
-      /* Unless the exceptions are handled, the behavior is undefined
-        if the trap occurs.  */
-      if (flag_non_call_exceptions)
-       return;
-    }
+  /* We cannot make trapping insn executed, unless it was executed before.  */
+  if (may_trap_after_code_motion_p (PATTERN (insn)) && !always_reached)
+    return;
 
   depends_on = BITMAP_ALLOC (NULL);
   if (!check_dependencies (insn, depends_on))
@@ -926,6 +932,32 @@ get_inv_cost (struct invariant *inv, int *comp_cost, unsigned *regs_needed)
   (*regs_needed)++;
   (*comp_cost) += inv->cost;
 
+#ifdef STACK_REGS
+  {
+    /* Hoisting constant pool constants into stack regs may cost more than
+       just single register.  On x87, the balance is affected both by the
+       small number of FP registers, and by its register stack organization,
+       that forces us to add compensation code in and around the loop to
+       shuffle the operands to the top of stack before use, and pop them
+       from the stack after the loop finishes.
+
+       To model this effect, we increase the number of registers needed for
+       stack registers by two: one register push, and one register pop.
+       This usually has the effect that FP constant loads from the constant
+       pool are not moved out of the loop.
+
+       Note that this also means that dependent invariants can not be moved.
+       However, the primary purpose of this pass is to move loop invariant
+       address arithmetic out of loops, and address arithmetic that depends
+       on floating point constants is unlikely to ever occur.  */
+    rtx set = single_set (inv->insn);
+    if (set
+       && IS_STACK_MODE (GET_MODE (SET_SRC (set)))
+       && constant_pool_constant_p (SET_SRC (set)))
+      (*regs_needed) += 2;
+  }
+#endif
+
   EXECUTE_IF_SET_IN_BITMAP (inv->depends_on, 0, depno, bi)
     {
       dep = VEC_index (invariant_p, invariants, depno);
@@ -1077,22 +1109,38 @@ find_invariants_to_move (void)
     }
 }
 
-/* Move invariant INVNO out of the LOOP.  */
+/* Returns true if all insns in SEQ are valid.  */
 
-static void
+static bool
+seq_insns_valid_p (rtx seq)
+{
+  rtx x;
+
+  for (x = seq; x; x = NEXT_INSN (x))
+    if (insn_invalid_p (x))
+      return false;
+
+  return true;
+}
+
+/* Move invariant INVNO out of the LOOP.  Returns true if this succeeds, false
+   otherwise.  */
+
+static bool
 move_invariant_reg (struct loop *loop, unsigned invno)
 {
   struct invariant *inv = VEC_index (invariant_p, invariants, invno);
   struct invariant *repr = VEC_index (invariant_p, invariants, inv->eqto);
   unsigned i;
   basic_block preheader = loop_preheader_edge (loop)->src;
-  rtx reg, set, seq, op;
+  rtx reg, set, dest, seq, op;
   struct use *use;
   bitmap_iterator bi;
 
-  if (inv->reg
-      || !repr->move)
-    return;
+  if (inv->reg)
+    return true;
+  if (!repr->move)
+    return false;
 
   /* If this is a representative of the class of equivalent invariants,
      really move the invariant.  Otherwise just replace its use with
@@ -1103,7 +1151,8 @@ move_invariant_reg (struct loop *loop, unsigned invno)
        {
          EXECUTE_IF_SET_IN_BITMAP (inv->depends_on, 0, i, bi)
            {
-             move_invariant_reg (loop, i);
+             if (!move_invariant_reg (loop, i))
+               goto fail;
            }
        }
 
@@ -1113,14 +1162,15 @@ move_invariant_reg (struct loop *loop, unsigned invno)
         would not be dominated by it, we may just move it (TODO).  Otherwise we
         need to create a temporary register.  */
       set = single_set (inv->insn);
-      reg = gen_reg_rtx (GET_MODE (SET_DEST (set)));
-      emit_insn_after (gen_move_insn (SET_DEST (set), reg), inv->insn);
+      dest = SET_DEST (set);
+      reg = gen_reg_rtx (GET_MODE (dest));
 
-      /* If the SET_DEST of the invariant insn is a reg, we can just move
+      /* If the SET_DEST of the invariant insn is a pseudo, we can just move
         the insn out of the loop.  Otherwise, we have to use gen_move_insn
         to let emit_move_insn produce a valid instruction stream.  */
-      if (REG_P (SET_DEST (set)))
+      if (REG_P (dest) && !HARD_REGISTER_P (dest))
        {
+         emit_insn_after (gen_move_insn (dest, reg), inv->insn);
          SET_DEST (set) = reg;
          reorder_insns (inv->insn, inv->insn, BB_END (preheader));
        }
@@ -1128,18 +1178,28 @@ move_invariant_reg (struct loop *loop, unsigned invno)
        {
          start_sequence ();
          op = force_operand (SET_SRC (set), reg);
+         if (!op)
+           {
+             end_sequence ();
+             goto fail;
+           }
          if (op != reg)
            emit_move_insn (reg, op);
          seq = get_insns ();
          end_sequence ();
 
+         if (!seq_insns_valid_p (seq))
+           goto fail;
          emit_insn_after (seq, BB_END (preheader));
+      
+         emit_insn_after (gen_move_insn (dest, reg), inv->insn);
          delete_insn (inv->insn);
        }
     }
   else
     {
-      move_invariant_reg (loop, repr->invno);
+      if (!move_invariant_reg (loop, repr->invno))
+       goto fail;
       reg = repr->reg;
       set = single_set (inv->insn);
       emit_insn_after (gen_move_insn (SET_DEST (set), reg), inv->insn);
@@ -1156,6 +1216,17 @@ move_invariant_reg (struct loop *loop, unsigned invno)
       for (use = inv->def->uses; use; use = use->next)
        *use->pos = reg;
     }
+
+  return true;
+
+fail:
+  /* If we failed, clear move flag, so that we do not try to move inv
+     again.  */
+  if (dump_file)
+    fprintf (dump_file, "Failed to move invariant %d\n", invno);
+  inv->move = false;
+  inv->reg = NULL_RTX;
+  return false;
 }
 
 /* Move selected invariant out of the LOOP.  Newly created regs are marked