Two patches in a week about dbr_schedule and redundant insns. This one
fixes a miscompilation of libgcov-driver.c for n32 and n64 MIPS.
It's independent of (and predates) the 59137 patch.
We had:
barrier
L1:
C: $r1 = ...
D: $r2 = ...
...
A: if ... goto L1
B: if ... goto L1
where these are the only two uses of L1. First we filled A's delay slot
with C:
barrier
L1:
C: $r1 = ...
L2:
D: $r2 = ...
...
A: if ... goto L2
C': $r1 = ...
B: if ... goto L1
Now B is the only user of L1 so it "owns" the target thread. When
looking for a delay slot, we realise that C is redundant with C',
so delete it and go on to D. We call update_block to record the
old C as a (use (insn)):
barrier
L1:
C'': (use ($r1 = ...))
L2:
D: $r2 = ...
L3:
...
A: if ... goto L2
C': $r1 = ...
B: if ... goto L3
D': $r2 = ...
So far so good. The problem is that redirecting B to L3 makes L1 unreachable,
so reorg_redirect_jump=>redirect_jump (..., 1)=>delete_related_insns deletes
it. Then C'' is unreachable and d_r_i deletes that too:
barrier
L2:
D: $r2 = ...
L3:
...
A: if ... goto L2
C': $r1 = ...
B: if ... goto L3
D': $r2 = ...
So now, when we try to calculate the liveness beyond D, we go back to the
barrier but have no indication that $r1 is live.
I wondered about several ways of fixing this.
* Back in the day, we just deleted the label and not related instructions.
I don't think that's something we should return to though. If we delete
all uses of:
barrier
L1:
insn
goto L2
to get:
barrier
L1:
(use (insn))
goto L2
then we should definitely remove the branch to L2.
* Deferring deleting labels until the end of dbr_schedule. On its own
this would pessimise the rest of dbr_schedule, e.g. a jump only "owns"
a thread until the next label. We would need to spinkle in a few checks
for the usage count being nonzero.
* Deferring deleting labels that fit the problem pattern. Having two
forms of redirect would make things even more complicated though.
And I don't think it would be robust in cases like the "goto L2" above.
If we delete a "goto L2" that is the last use of L2, we delete L2 too,
which in turn could be a label-after-barrier. So reorg would have to
predict what the recursive calls to delete_related_insns would do.
* Add the liveness information to the basic block info and let the
(use (insn))s be deleted. I started down that path but it soon got
very convoluted. Also:
We used to try to update the live status of registers if WHERE is at
the start of a basic block, but that can't work since we may remove a
BARRIER in relax_delay_slots. */
suggests that this has already been tried and it wasn't robust.
So in the end I just taught delete_related_insns to keep (use (insn)),
which AFAIK is a pattern that only dbr_schedule could generate.
Tested on mips64-linux-gnu. OK to install?
Thanks,
Richard
gcc/
* jump.c (delete_related_insns): Keep (use (insn))s.
* reorg.c (redundant_insn): Check for barriers too.
Index: gcc/jump.c
===================================================================
--- gcc/jump.c 2014-01-14 14:40:05.179783568 +0000
+++ gcc/jump.c 2014-01-14 19:10:18.392192242 +0000
@@ -1355,6 +1355,13 @@ delete_related_insns (rtx insn)
/* Keep going past other deleted labels to delete what follows. */
else if (code == CODE_LABEL && INSN_DELETED_P (next))
next = NEXT_INSN (next);
+ /* Keep the (use (insn))s created by dbr_schedule, which needs
+ them in order to track liveness relative to a previous
+ barrier. */
+ else if (INSN_P (next)
+ && GET_CODE (PATTERN (next)) == USE
+ && INSN_P (XEXP (PATTERN (next), 0)))
+ next = NEXT_INSN (next);
else if (code == BARRIER || INSN_P (next))
/* Note: if this deletes a jump, it can cause more
deletion of unreachable code, after a different label.
Index: gcc/reorg.c
===================================================================
--- gcc/reorg.c 2014-01-14 14:40:05.179783568 +0000
+++ gcc/reorg.c 2014-01-14 19:11:49.487961664 +0000
@@ -1512,7 +1512,10 @@ redundant_insn (rtx insn, rtx target, rt
trial && insns_to_search > 0;
trial = PREV_INSN (trial))
{
- if (LABEL_P (trial))
+ /* (use (insn))s can come immediately after a barrier if the
+ label that used to precede them has been deleted as dead.
+ See delete_related_insns. */
+ if (LABEL_P (trial) || BARRIER_P (trial))
return 0;
if (!INSN_P (trial))