#include "flags.h"
#include "df.h"
#include "hashtab.h"
+#include "except.h"
/* The data stored for the loop. */
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;
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);
}
}
/* 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)
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;
|| !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))
(*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);
}
}
-/* 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
{
EXECUTE_IF_SET_IN_BITMAP (inv->depends_on, 0, i, bi)
{
- move_invariant_reg (loop, i);
+ if (!move_invariant_reg (loop, i))
+ goto fail;
}
}
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));
}
{
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);
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