This is an archive of the discontinued LLVM Phabricator instance.

[SCEV][IndVarSimplify] insert point should not be block front if the front instruction is a PHI
ClosedPublic

Authored by shchenz on Jun 1 2020, 9:24 PM.

Details

Summary

Problem:

for.cond2106:                                     ; preds = %for.cond2106, %entry
  %gid.0 = phi i8* [ null, %entry ], [ %incdec.ptr, %for.cond2106 ]
  %i.0 = phi i32 [ 0, %entry ], [ %inc2117, %for.cond2106 ]
  %incdec.ptr = getelementptr inbounds i8, i8* %gid.0, i64 1
  %idxprom2114 = zext i32 %i.0 to i64
  %inc2117 = add nuw nsw i32 %i.0, 1
  br label %for.cond2106

IndVarSimplify wants to eliminate %idxprom2114 = zext i32 %i.0 to i64, so it tries to widen PHI node for %I.0 from i32 to i64.

When IndVarSimplify tries to expand code for above phi node’s SCEV {0, +, 1} in createWideIV, scevexpander finds an existing instruction with same SCEV, the first PHI node %gid.0.

Then when scevexpander adds a cast from first PHI node type i8 * to i64, we get assert inside ReuseOrCreateCast.

Root Cause:
Reason for the assert is the cast instruction is generated at insert point which ignores all PHIs, but when IndVarSimplify starts to expand the SCEV for the PHI, it sets the insert point at the loop header’s very beginning.

So the new created cast(ptrtoint) does not dominates the expected use place. (insert point set by IndVarSimplify, which is the loop header’s very beginning).

Solution:
This is the second issue exposed by the insert point setting by IndVarSimplify:


// The rewriter provides a value for the desired IV expression. This may
// either find an existing phi or materialize a new one. Either way, we
// expect a well-formed cyclic phi-with-increments. i.e. any operand not part
// of the phi-SCC dominates the loop entry.
Instruction *InsertPt = &L->getHeader()->front();
WidePhi = cast<PHINode>(Rewriter.expandCodeFor(AddRec, WideType, InsertPt));

The first one was fixed in https://reviews.llvm.org/D57428, it corrects the insert point for IndVarSimplify specially in Value *SCEVExpander::expand as:

// IndVarSimplify sometimes sets the insertion point at the block start, even
// when there are PHIs at that point.  We must correct for this.
if (isa<PHINode>(*InsertPt)) 
  InsertPt = &*InsertPt->getParent()->getFirstInsertionPt();

This patch also fixes the insert point issue like above.

Improved solution?:
But should we fix it in IndVarSimplify instead of SCEVExpander? The author for https://reviews.llvm.org/D57428 also tried this way, but he found there are other places also set the insert point at the loop header’s very beginning, like the following statements in SCEVExpander::getAddRecExprPHILiterally:

// Expand the step somewhere that dominates the loop header.
Value *StepV = expandCodeFor(Step, IntTy, &L->getHeader()->front());

I went through llvm code base, there are other places calling expandCodeFor with insert point L->getHeader()->front(), I guess they are all not safe if the first instruction at the header front is PHI and the instruction we expand is not a PHI node?

Do we need to fix all these places? and one thing is there are many places whose insert points are parameters from their caller, so for this case we can not avoid the risk?

May be we need to add some check in Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty, Instruction *IP) like below, so we don’t need to go through all expandCodeFor callings and also can remove the fix for the insert point deep inside SCEVExpander?

Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty,
                                   Instruction *IP) {
   if (isa<PHINode>(*IP))
      IP = InsertPt = &*InsertPt->getParent()->getFirstInsertionPt();
  setInsertPoint(IP);
  return expandCodeFor(SH, Ty);
}

This should also resolve https://bugs.llvm.org/show_bug.cgi?id=37004
Thanks in advance for your comments.

Diff Detail

Event Timeline

shchenz created this revision.Jun 1 2020, 9:24 PM
lebedev.ri added a subscriber: lebedev.ri.

Please attach diffs with full context (-U99999)

shchenz updated this revision to Diff 267792.Jun 1 2020, 11:43 PM

add full context

Naively i would think that ScalarEvolutionExpander should not second-guess/re-guess specified insertion point,
and the callers (possibly ScalarEvolutionExpander itself too) should be adjusted to specify getFirstInsertionPt() as the insertion point.

shchenz updated this revision to Diff 268063.Jun 2 2020, 11:19 PM

fix the insert point at the caller site

Naively i would think that ScalarEvolutionExpander should not second-guess/re-guess specified insertion point,
and the callers (possibly ScalarEvolutionExpander itself too) should be adjusted to specify getFirstInsertionPt() as the insertion point.

Thanks for your comments. Update accordingly.

Except the changes in the patch, I also went through all ExpandCodeFor calling in llvm code base, below one is what I am not sure of:

LoopUtils.cpp, Line 1402:

for (RewritePhi &Phi : RewritePhiSet) {
  Phi.Expansion = Rewriter.expandCodeFor(Phi.ExpansionSCEV, Phi.PN->getType(),
                                         Phi.ExpansionPoint);

Phi.ExpansionPoint is set as phi node's incoming value. I can not find a doc indicates that PHI’s incoming can not be another PHI, if so I think if Phi.ExpansionPoint can be a PHI, then we also have the same issue?

shchenz updated this revision to Diff 268070.Jun 3 2020, 12:00 AM

Remove the callee site fix in https://reviews.llvm.org/D57428

@lebedev.ri Hi Roman, could you please help to have another look at this issue? This patch is to fix an insert point bug. And per your suggestion, make the fix at the caller side, like IndVarSimplify and SCEVExpander itself. Thanks

lebedev.ri accepted this revision.Jun 9 2020, 7:46 AM

LGTM, but i defer (as in: please wait for one more accept) to code owners of indvarsimplify/SCEV (@reames? @mkazantsev?)

This revision is now accepted and ready to land.Jun 9 2020, 7:46 AM

@reames @mkazantsev It will be much appreciated if you could take a look at this issue. This patch fixes all the block front insert points at the call site.

gentle ping.

gentle ping

This revision was automatically updated to reflect the committed changes.

I will let this patch commit first since it was already approved by @lebedev.ri . Welcome your post commit comments @reames @mkazantsev

serge-sans-paille added inline comments.
llvm/lib/Transforms/Scalar/IndVarSimplify.cpp
1443

the expandCodeFor always modifies current function, but returning nullptr is interpreted as « no change ». This makes pass return status invalid (the pass return false while a change actually happened).

This breaks validation http://lab.llvm.org:8011/builders/llvm-clang-x86_64-expensive-checks-debian/builds/9441/, and there wasn't a simple patch.