This is an archive of the discontinued LLVM Phabricator instance.

[LoopUnroll] Fix the way we update DT after complete unrolling.
ClosedPublic

Authored by mzolotukhin on Apr 1 2016, 1:44 PM.

Details

Summary

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.

Diff Detail

Event Timeline

mzolotukhin updated this revision to Diff 52419.Apr 1 2016, 1:44 PM
mzolotukhin retitled this revision from to [LoopUnroll] Fix the way we update DT after complete unrolling..
mzolotukhin updated this object.
mzolotukhin added a subscriber: llvm-commits.

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

sanjoy edited edge metadata.Apr 5 2016, 2:37 PM

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).

sanjoy added a comment.Apr 5 2016, 2:49 PM

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

sanjoy added a comment.EditedApr 5 2016, 4:45 PM

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

sanjoy added a comment.Apr 5 2016, 5:07 PM

Hmm, that's a good catch. I think we need to check dominance children of all loop blocks then. That should be sufficient, right?

Sounds right, but I didn't try to prove it.

mzolotukhin updated this revision to Diff 52845.Apr 6 2016, 1:18 PM
mzolotukhin edited edge metadata.
  • Update dom-info for all dom-children of original loop blocks.
mzolotukhin updated this revision to Diff 52846.Apr 6 2016, 1:20 PM
  • Use 'auto *' instead of 'auto'.
sanjoy accepted this revision.Apr 6 2016, 2:00 PM
sanjoy edited edge metadata.

lgtm with a minor nit inline

lib/Transforms/Utils/LoopUnroll.cpp
564

Isn't PrevIDom the same as BB?

This revision is now accepted and ready to land.Apr 6 2016, 2:00 PM
mzolotukhin closed this revision.Apr 6 2016, 2:54 PM

Thanks, committed in r265605.

Michael

lib/Transforms/Utils/LoopUnroll.cpp
564

It is, thanks!