re PR rtl-optimization/64616 (Redundant ldr when accessing var inside and outside a loop)
2015-05-13 Thomas Preud'homme <thomas.preudhomme@arm.com> gcc/ PR rtl-optimization/64616 * loop-invariant.c (can_move_invariant_reg): New. (move_invariant_reg): Call above new function to decide whether instruction can just be moved, skipping creation of temporary register. gcc/testsuite/ PR rtl-optimization/64616 * gcc.dg/loop-8.c: New test. * gcc.dg/loop-9.c: New test. From-SVN: r223113
This commit is contained in:
parent
e66fc62389
commit
aa953e2fc5
|
@ -1,3 +1,11 @@
|
|||
2015-05-13 Thomas Preud'homme <thomas.preudhomme@arm.com>
|
||||
|
||||
PR rtl-optimization/64616
|
||||
* loop-invariant.c (can_move_invariant_reg): New.
|
||||
(move_invariant_reg): Call above new function to decide whether
|
||||
instruction can just be moved, skipping creation of temporary
|
||||
register.
|
||||
|
||||
2015-05-12 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
PR target/pr66047.c
|
||||
|
|
|
@ -1511,6 +1511,79 @@ replace_uses (struct invariant *inv, rtx reg, bool in_group)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Whether invariant INV setting REG can be moved out of LOOP, at the end of
|
||||
the block preceding its header. */
|
||||
|
||||
static bool
|
||||
can_move_invariant_reg (struct loop *loop, struct invariant *inv, rtx reg)
|
||||
{
|
||||
df_ref def, use;
|
||||
unsigned int dest_regno, defs_in_loop_count = 0;
|
||||
rtx_insn *insn = inv->insn;
|
||||
basic_block bb = BLOCK_FOR_INSN (inv->insn);
|
||||
|
||||
/* We ignore hard register and memory access for cost and complexity reasons.
|
||||
Hard register are few at this stage and expensive to consider as they
|
||||
require building a separate data flow. Memory access would require using
|
||||
df_simulate_* and can_move_insns_across functions and is more complex. */
|
||||
if (!REG_P (reg) || HARD_REGISTER_P (reg))
|
||||
return false;
|
||||
|
||||
/* Check whether the set is always executed. We could omit this condition if
|
||||
we know that the register is unused outside of the loop, but it does not
|
||||
seem worth finding out. */
|
||||
if (!inv->always_executed)
|
||||
return false;
|
||||
|
||||
/* Check that all uses that would be dominated by def are already dominated
|
||||
by it. */
|
||||
dest_regno = REGNO (reg);
|
||||
for (use = DF_REG_USE_CHAIN (dest_regno); use; use = DF_REF_NEXT_REG (use))
|
||||
{
|
||||
rtx_insn *use_insn;
|
||||
basic_block use_bb;
|
||||
|
||||
use_insn = DF_REF_INSN (use);
|
||||
use_bb = BLOCK_FOR_INSN (use_insn);
|
||||
|
||||
/* Ignore instruction considered for moving. */
|
||||
if (use_insn == insn)
|
||||
continue;
|
||||
|
||||
/* Don't consider uses outside loop. */
|
||||
if (!flow_bb_inside_loop_p (loop, use_bb))
|
||||
continue;
|
||||
|
||||
/* Don't move if a use is not dominated by def in insn. */
|
||||
if (use_bb == bb && DF_INSN_LUID (insn) >= DF_INSN_LUID (use_insn))
|
||||
return false;
|
||||
if (!dominated_by_p (CDI_DOMINATORS, use_bb, bb))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check for other defs. Any other def in the loop might reach a use
|
||||
currently reached by the def in insn. */
|
||||
for (def = DF_REG_DEF_CHAIN (dest_regno); def; def = DF_REF_NEXT_REG (def))
|
||||
{
|
||||
basic_block def_bb = DF_REF_BB (def);
|
||||
|
||||
/* Defs in exit block cannot reach a use they weren't already. */
|
||||
if (single_succ_p (def_bb))
|
||||
{
|
||||
basic_block def_bb_succ;
|
||||
|
||||
def_bb_succ = single_succ (def_bb);
|
||||
if (!flow_bb_inside_loop_p (loop, def_bb_succ))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (++defs_in_loop_count > 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Move invariant INVNO out of the LOOP. Returns true if this succeeds, false
|
||||
otherwise. */
|
||||
|
||||
|
@ -1544,11 +1617,8 @@ move_invariant_reg (struct loop *loop, unsigned invno)
|
|||
}
|
||||
}
|
||||
|
||||
/* Move the set out of the loop. If the set is always executed (we could
|
||||
omit this condition if we know that the register is unused outside of
|
||||
the loop, but it does not seem worth finding out) and it has no uses
|
||||
that would not be dominated by it, we may just move it (TODO).
|
||||
Otherwise we need to create a temporary register. */
|
||||
/* If possible, just move the set out of the loop. Otherwise, we
|
||||
need to create a temporary register. */
|
||||
set = single_set (inv->insn);
|
||||
reg = dest = SET_DEST (set);
|
||||
if (GET_CODE (reg) == SUBREG)
|
||||
|
@ -1556,19 +1626,25 @@ move_invariant_reg (struct loop *loop, unsigned invno)
|
|||
if (REG_P (reg))
|
||||
regno = REGNO (reg);
|
||||
|
||||
reg = gen_reg_rtx_and_attrs (dest);
|
||||
if (!can_move_invariant_reg (loop, inv, reg))
|
||||
{
|
||||
reg = gen_reg_rtx_and_attrs (dest);
|
||||
|
||||
/* Try replacing the destination by a new pseudoregister. */
|
||||
validate_change (inv->insn, &SET_DEST (set), reg, true);
|
||||
/* Try replacing the destination by a new pseudoregister. */
|
||||
validate_change (inv->insn, &SET_DEST (set), reg, true);
|
||||
|
||||
/* As well as all the dominated uses. */
|
||||
replace_uses (inv, reg, true);
|
||||
/* As well as all the dominated uses. */
|
||||
replace_uses (inv, reg, true);
|
||||
|
||||
/* And validate all the changes. */
|
||||
if (!apply_change_group ())
|
||||
goto fail;
|
||||
/* And validate all the changes. */
|
||||
if (!apply_change_group ())
|
||||
goto fail;
|
||||
|
||||
emit_insn_after (gen_move_insn (dest, reg), inv->insn);
|
||||
emit_insn_after (gen_move_insn (dest, reg), inv->insn);
|
||||
}
|
||||
else if (dump_file)
|
||||
fprintf (dump_file, "Invariant %d moved without introducing a new "
|
||||
"temporary register\n", invno);
|
||||
reorder_insns (inv->insn, inv->insn, BB_END (preheader));
|
||||
|
||||
/* If there is a REG_EQUAL note on the insn we just moved, and the
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2015-05-13 Thomas Preud'homme <thomas.preudhomme@arm.com>
|
||||
|
||||
PR rtl-optimization/64616
|
||||
* gcc.dg/loop-8.c: New test.
|
||||
* gcc.dg/loop-9.c: New test.
|
||||
|
||||
2015-05-12 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
PR target/pr66047.c
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O1 -fdump-rtl-loop2_invariant" } */
|
||||
|
||||
void
|
||||
f (int *a, int *b)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 100; i++)
|
||||
{
|
||||
int d = 42;
|
||||
|
||||
a[i] = d;
|
||||
if (i % 2)
|
||||
d = i;
|
||||
b[i] = d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Load of 42 is moved out of the loop, introducing a new pseudo register. */
|
||||
/* { dg-final { scan-rtl-dump-times "Decided" 1 "loop2_invariant" } } */
|
||||
/* { dg-final { scan-rtl-dump-not "without introducing a new temporary register" "loop2_invariant" } } */
|
||||
/* { dg-final { cleanup-rtl-dump "loop2_invariant" } } */
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O1 -fdump-rtl-loop2_invariant" } */
|
||||
|
||||
void
|
||||
f (double *a)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 100; i++)
|
||||
a[i] = 18.4242;
|
||||
}
|
||||
|
||||
/* Load of x is moved out of the loop. */
|
||||
/* { dg-final { scan-rtl-dump "Decided" "loop2_invariant" } } */
|
||||
/* { dg-final { scan-rtl-dump "without introducing a new temporary register" "loop2_invariant" } } */
|
||||
/* { dg-final { cleanup-rtl-dump "loop2_invariant" } } */
|
||||
|
Loading…
Reference in New Issue