Updating dominators for exit-blocks of the unrolled loops is not enough,
as shown in PR27157. The proper way is to update dominators for all
dominance-children of exiting blocks. That set is a superset of
exit-blocks set.
Details
Diff Detail
Event Timeline
Could anyone take a look at this please? It's a fix for a stability issue, and I'd appreciate if we can land it fast (not having correct DT information might lead to weird bugs later in the pipeline).
Michael
Hi Michael,
I don't quite grok this statement "That set is a superset of
exit-blocks set.". What if we have
maybe_goto exit0; for (...) if (cond) { // block a maybe_goto exit0; } else { maybe_goto exit1; } } exit0: return exit1: return
The set of dominance children of exiting blocks of the above loop is
{exit1}, but the set of exit blocks is {exit0, exit1} (so the set of
dominance children of exiting blocks is not a superset of the exit
blocks of the loop). Is there some additional invariant that applies
to unrolled loops that I'm missing here?
lib/Transforms/Utils/LoopUnroll.cpp | ||
---|---|---|
558–568 | Use auto * here and below (to make it obvious that you're binding to a pointer). |
Generally, I think this change will be somewhat easier to follow if
you update the commit message to "this is the pre-unroll loop A, which
gets unrolled to this loop B, and these are the blocks X and Y we need
to fixup out of which the current technique leaves out X".
I realize that you have a test case, but reviewers are lazy people. :)
Hi Sanjoy,
Thank you for taking a look. You're right, my statement "That set is a superset of exit-blocks set" is incorrect (as shown by your example). But I believe the proposed way to update DT is still correct though.
Let me elaborate on the issue I'm trying to solve here: the attached test illustrates it.
The set of exiting blocks in this test is {loop_latch, loop_exiting_bb1, loop_exiting_bb2}.
The set of exit blocks is {exit2, exit1.loopexit, bb}.
The set of dominance children of exiting blocks is {exit2, exit1.loopexit, exit1, loop_exiting_bb2, bb}.
The loop after unrolling looks like this:
; ITERATION 1 loop_header: ; preds = %entry br i1 undef, label %loop_latch, label %loop_exiting_bb1 loop_exiting_bb1: ; preds = %loop_header br i1 false, label %loop_exiting_bb2, label %exit1.loopexit loop_exiting_bb2: ; preds = %loop_exiting_bb1 br i1 false, label %loop_latch, label %bb loop_latch: ; preds = %loop_exiting_bb2, %loop_header %iv_next = add nuw nsw i64 0, 1 %cmp = icmp ne i64 %iv_next, 2 br label %loop_header.1 ; ITERATION 2 loop_header.1: ; preds = %loop_latch br i1 undef, label %loop_latch.1, label %loop_exiting_bb1.1 loop_exiting_bb1.1: ; preds = %loop_header.1 br i1 false, label %loop_exiting_bb2.1, label %exit1.loopexit loop_exiting_bb2.1: ; preds = %loop_exiting_bb1.1 br i1 false, label %loop_latch.1, label %bb loop_latch.1: ; preds = %loop_exiting_bb2.1, %loop_header.1 %iv_next.1 = add nuw nsw i64 %iv_next, 1 %cmp.1 = icmp ne i64 %iv_next.1, 2 br label %exit2 ; EXIT BLOCKS bb: ; preds = %loop_exiting_bb2.1, %loop_exiting_bb2 br label %exit1 exit1.loopexit: ; preds = %loop_exiting_bb1.1, %loop_exiting_bb1 br label %exit1 exit1: ; preds = %exit1.loopexit, %bb ret void exit2: ; preds = %loop_latch.1 ret void
The issue is that currently we don't update dom-info for exit1, because it's not an exit block. Before the unrolling its dominator was loop_exiting_bb1, but now we have another incoming edge from loop_exiting_bb1.1, so its new dominator is loop_header. If, instead of looking at exit-blocks, we start looking at dominance children of exiting blocks, as proposed in this patch, that should be solved.
Now, if one of the exits has an incoming edge from outside the loop, then its dominator shouldn't change, since it's also outside of the loop. So, we should be fine in this case too.
Does it make sense?
Thanks,
Michael
Hi Michael,
How about loops like these:
define void @f() { entry: br label %loop.header loop.header: %iv = phi i32 [ 0, %entry ], [ %iv.inc, %latch ] %iv.inc = add i32 %iv, 1 br i1 undef, label %diamond, label %latch diamond: br i1 undef, label %left, label %right left: br i1 undef, label %exit, label %merge right: br i1 undef, label %exit, label %merge merge: br label %latch latch: %end.cond = icmp eq i32 %iv, 1 br i1 %end.cond, label %exit1, label %loop.header exit: ret void exit1: ret void }
?
exit is dominated by diamond (not a loop exit [edit, should be : "not a loop exiting block"]), but after unrolling exit becomes dominated by loop.header.
Hmm, that's a good catch. I think we need to check dominance children of all loop blocks then. That should be sufficient, right?
Thanks,
Michael
lgtm with a minor nit inline
lib/Transforms/Utils/LoopUnroll.cpp | ||
---|---|---|
564 | Isn't PrevIDom the same as BB? |
Thanks, committed in r265605.
Michael
lib/Transforms/Utils/LoopUnroll.cpp | ||
---|---|---|
564 | It is, thanks! |
Isn't PrevIDom the same as BB?