This is an archive of the discontinued LLVM Phabricator instance.

[PSCEV] Create AddRec for Phis in cases of possible integer overflow, using runtime checks
ClosedPublic

Authored by dorit on Feb 16 2017, 5:25 AM.

Details

Summary

Extend the SCEVPredicateRewriter to work a bit harder when it encounters an UnknownSCEV whose Value is a Phi node.
The goal it to build an AddRecurrence for Phi nodes whose update chain involves casts, that can be ignored under the proper runtime overflow test.
This is the first step in addressing PR30654.
Next steps will improve upon it, as detailed in the comment in the body of the patch (under "TODO").

(BTW, If some of these steps seem critical I am happy to include them with this first patch, if this patch looks in principle ok (I don't want to build much upon a wrong direction...)).

Diff Detail

Event Timeline

dorit created this revision.Feb 16 2017, 5:25 AM
dorit added a reviewer: mssimpso.
Ayal added a subscriber: Ayal.Feb 20 2017, 1:48 AM
Ayal added inline comments.
llvm/lib/Analysis/ScalarEvolution.cpp
10863–10864

dyn_cast >> isa

10863–10864

The attempt to createAddRecFromPHIWithCasts() involves introducing a new cast predicate, right? Worth guarding or at-least commenting.

11005

exits >> latches

11025

Alternatively, you can bailed out immediately and return nullptr when multiple distinct BackEdge/Start values are found. Then these checks should be asserts?

11046

This check seems redundant, as we're stopping on the first index found to be a phi or a simple casted phi, right? Simply break when found, and check if i == e afterwards, setting FoundIndex = i (if not).

delena added a subscriber: delena.Feb 20 2017, 1:53 AM
sbaranga added inline comments.Feb 20 2017, 10:35 AM
llvm/lib/Analysis/ScalarEvolution.cpp
10878

I know I said that this should be a NUSW on the bugzilla ticket, but I'm not that sure anymore.
Whatever the case this need a comment explaining the choice.

11000

I think it would be better to move this to Scalar Evolution itself, instead of having it in the rewriter. It would essentially be a lazy analysis, and would also take as an argument the loop and would return the analyzed expression and a set of predicates. That way we don't have to do the analysis again for every instantiation of a SCEVPredicateRewriter.

11072

Why is it correct to add the NSW flag here? I'm worried that it's somehow implied by the predicates that we're adding.

11091

Same as above (why can we add the NSW flag here?).

Hi Ayal,
I agree with all your comments, and will incorporate your suggestions to the next upload. I just want to clear out the caching and NoWrapFlags issues that Silviu had raised so I could include that in the next upload.
BTW, some of your comments refer to code that was copied over from getAddRecFromPHI, so I guess it would make sense to make the same changes there, where relevant. (I hope @sanjoy will have a chance to take a look to provide his input :-)).
Thanks!
Dorit

Hi Silviu,

About the NoWrap flags: I don't have very high confidence about this, I think it indeed needs to be corrected -- proposed fixes (and followup questions...) below.

I should mention that in getAddRecFromPHI (which this function is inspired by) the wrapping flags are determined by this piece of logic:

if (auto BO = MatchBinaryOp(BEValueV, DT)) {
  if (BO->Opcode == Instruction::Add && BO->LHS == PN) {
    if (BO->IsNUW)
      Flags = setFlags(Flags, SCEV::FlagNUW);
    if (BO->IsNSW)
      Flags = setFlags(Flags, SCEV::FlagNSW);
  }
} else if (GEPOperator *GEP = dyn_cast<GEPOperator>(BEValueV)) {
  // If the increment is an inbounds GEP, then we know the address
  // space cannot be wrapped around. We cannot make any guarantee
  // about signed or unsigned overflow because pointers are
  // unsigned but we may have a negative index from the base
  // pointer. We can guarantee that no unsigned wrap occurs if the
  // indices form a positive value.
  if (GEP->isInBounds() && GEP->getOperand(0) == PN) {
    Flags = setFlags(Flags, SCEV::FlagNW);

    const SCEV *Ptr = getSCEV(GEP->getPointerOperand());
    if (isKnownPositive(getMinusSCEV(getSCEV(GEP), Ptr)))
      Flags = setFlags(Flags, SCEV::FlagNUW);
  }

  // We cannot transfer nuw and nsw flags from subtraction
  // operations -- sub nuw X, Y is not the same as add nuw X, -Y
  // for instance.
}

I thought this is not needed/relevant in our scenario here because we determine the signess of the flags according to the whether we encountered a zext or sext; but I also realize now didn't think about the GEP scenario at all, I was thinking about integer inductions only. So in addition to the fixes below I will also add a check that we are dealing with integers, not pointers.

Many thanks,
Dorit

llvm/lib/Analysis/ScalarEvolution.cpp
10878

I actually don't see how we can tell whether to create an NUSW or NSSW assumption without further analysis, such as the analysis we do in getAddRecForPhiWithCasts…; So maybe in the general case we need to be conservative here and add both NSSW and NUSW to make sure that there will be no kind of overflow due to the truncation?

11000

So where/when would this analysis be triggered? And where would we cache the result of the ScalarEvolution analysis? could you please elaborate on what would be the flow of things you are suggesting / what exactly you mean by lazy here…?

I agree of course we should avoid repeating the analysis over and over again; the caching of the analysis that I was going to add, along with guarding the analysis with "if(NewPreds)", would guarantee that if the analysis succeeded once (when NewPreds was passed) then any time we are passed "Preds" we will not repeat the analysis (because I was going to add a new kind of Predicate, and cache things in Preds). But indeed if the analysis fails, this caching will not prevent us from repeating it (and failing) every time we are passed NewPreds…

So maybe what this means is that the caching should be done in a new data-structure to be added to PSCEV, separately of Preds, where we would cache both a failure -- simply associate the UnknownSCEV of the phi node with itself, and a success -- associate the UnknownSCEV with the respective AddRec (and of course this AddRec would itself have a Predicate that the analysis will have already added). This way we could check the results of the analysis regardless of whether we are passed Preds. If we do that, would your suggestion above still be relevant?

11072

Not sure about this, and looking at this again I can't justify SCEV:NSW here. Probably SCEV::FlagAnyWrap is all we can do here (as without the predicate we know nothing because of the truncate). Right?

11091

Again, not sure about this. I thought we can put here what the predicate guarantees. So if we added an NSSW assumption we could set the NoWrapFlags to SCEV:FlagNSW (right?). Originally I only looked at the Sext pattern and that's why I put the NSW Flag. Then I extended the analysis to also consider the Zext pattern, but didn't go back to fix the flag. So if we added an NUSW predicate, then would it be correct to set the flags to SCEV:FlagNUW ?? (NUSW and NUW don't have the same semantics…). Maybe SCEV:FlagNW makes most sense then in that case?

dorit added a comment.Feb 27 2017, 2:43 PM

Silviu, would you please clarify your comment about moving the analysis to Scalar Evolution (please see my question above)? and why is it better than just caching things in the rewriter (possibly via a new data-structure in PSCEV or via Preds)? I would like to address your comments and upload a revised patch... thanks a lot,
Dorit

sbaranga edited edge metadata.Feb 28 2017, 5:56 AM

Hi Dorit,

Sorry for the delay. I'm on holiday until next week so communication will be slow until I get back.

Thanks,
Silviu

llvm/lib/Analysis/ScalarEvolution.cpp
10878

Adding just NUSW would work, but the problem would be that the predicate would fail at runtime often if the number is used as signed. Ideally we should find a solution that works in most cases.

11000

I was thinking using the same mechanism that SCEV already has for caching SCEV expressions (and which also use to store SCEV predicates).

Essentially, PSCEV would call SCEV from here and SCEV would check to see if it has already analyzed the node or not. If not, it would do this analysis and store the result (using the loop + the SCEV Unknown as keys for further lookups). As you've said, in case of failure we can just return the SCEVUnknown expression without any additional predicates.

This would essentially be the same thing SCEV does for getSCEV().

11072

Sounds correct, we should drop the NSW flag.

11091

We can't add NSW/NUW on SCEV expressions if we infer them from SCEV predicates. The problem is doing so would essentially mean that the NUW/NSW are not predicated (which isn't true) and can technically lead us to false conclusions (we can even use the nsw/nuw flags to prove the original predicate, which is incorrect).

dorit added a comment.Feb 28 2017, 6:46 AM

Thanks for responding while on holiday!

llvm/lib/Analysis/ScalarEvolution.cpp
10878

So Truncate may need to call SE::getAddRecForPhiWithCasts analysis directly, so that it will have the signess knowledge/context; in fact the analysis results already include the predicate that needs to be added (NUSW/NSSW).

11000

So just making sure:
Currently SCEV caches things in the ValueExprMap.
Are you suggesting to add a new member to SCEV, to store the result of the analysis? (where the result of the analysis is: "the SCEVUnknown %x in loop L can be rewritten to the AddRec '(0,+,%step)' if the Predicate 'Flags=NSSW, AR=(0,+,trunc(%step)' is added" ? )

sbaranga added inline comments.Mar 9 2017, 2:35 AM
llvm/lib/Analysis/ScalarEvolution.cpp
11000

I think the result of the analysis should be "for loop L the loop-variant SCEVUnknown can be re-written to another SCEV, given a set of predicates", so the analysis gives you both the SCEV and a set of predicates. This should be general enough to use in more cases.

I would be happy with either reusing the same ValueExprMap for storage, or adding another ValueExprMap (they would probably both work). In general I'm not too picky about this as long as it is sensible (although @sanjoy will probably have something to say).

dorit added inline comments.Mar 16 2017, 10:05 AM
llvm/lib/Analysis/ScalarEvolution.cpp
11000

SCEV's ValueExprMap maps a Value to a SCEVExpr; and we want to map a <UnknownSCEV, Loop> pair to a <SCEVExpr, setOfPredicates> pair.
Are we talking about the same ValueExprMap?

Also, you commented earlier that SCEV uses the ValueExprMap to also cache SCEV predicates; Could you please point me to where? (I thought that predicates and predicate-based rewrites were stored only in PSCEV's Preds and RewriteMap…) ?

Thanks

sbaranga added inline comments.Mar 16 2017, 10:29 AM
llvm/lib/Analysis/ScalarEvolution.cpp
11000

Sorry, I should have looked at the code earlier. What I meant was adding a FoldingSet, like the ones used by ScalarEvolution to store SCEVs to and Preds (see UniqueSCEVs and UniquePreds). In fact what I have in mind is just adding another FoldingSet next to the two existing folding sets in ScalarEvolution.

It would probably be easier to use a SCEVUnknown instead of a Value as a key, since it already has the callback to handle RAUW for the underlying Value.

dorit added inline comments.Mar 17 2017, 5:57 AM
llvm/lib/Analysis/ScalarEvolution.cpp
11000

In fact what I have in mind is just adding another FoldingSet next to the two existing folding sets in ScalarEvolution.

Isn't it more natural to hold the mapping from the unknownSCEV+Loop to the Predicate+AddRecExpr in a map like this?:

DenseMap< std::pair<const Loop *, const SCEV *> , std::pair<const SCEV *, SmallVector<constSCEVPredicate *, 2>> >

It would probably be easier to use a SCEVUnknown instead of a Value as a key, since it already has the callback to handle RAUW for the underlying Value.

Wait, why would we be replacing uses here? We will be recording here just a tentative mapping, which will be valid only if the PSCEV caller will decide to actually add these predicates and SCEV rewrites in its Preds and RewriteMap… ?

I'll upload a new patch along these lines next week (but if something in the above sounds wrong please shout!)

sbaranga added inline comments.Mar 21 2017, 7:20 AM
llvm/lib/Analysis/ScalarEvolution.cpp
11000

Using DenseMap should be ok as well.

Regarding replacing uses: we need to handle this case in order to properly cache the result of the analysis (because passes that use SCEV can replace uses). This should be fine however if we use SCEVs instead of Values. So this shouldn't be a problem with the DenseMap that you want to use.

dorit updated this revision to Diff 92622.Mar 22 2017, 6:01 AM

Hi Silviu,

The new revision addresses your comments and implements two of the TODO items:

  1. it caches the results of the analysis in a new map in ScalarEvolution (as per your suggestion; thanks!).
  2. it provides a bit of context to visitTruncate so we'd know which overflow check to create (signed or unsigned).

About the cache: For now, I didn't define it to hold a set of predicates but just a single SCEVWrapPredicate per item; I was wondering if maybe we want a SCEVUnionPredicate instead of a Set of predicates? In any case, maybe better leave that generalization for when the need arises?

I have not yet implemented the code style comments by Ayal that relate to the code I copied from createAddRecFromPHI; These should be fixed in both functions, and I want to try to see if I can outline some common pieces between these functions to avoid duplication as much as possible.
I will go ahead and look at that next, and upload a new revision later on. Or in a separate patch if you prefer?
(only expected changes are NFC stuff around createAddRecFromPHI/ createAddRecFromPHIWithCasts; In terms of functionality - the patch is ready for review :-))

Ayal added a comment.Apr 9 2017, 3:56 AM

Hi Silviu,

The new revision addresses your comments and implements two of the TODO items:

  1. it caches the results of the analysis in a new map in ScalarEvolution (as per your suggestion; thanks!).
  2. it provides a bit of context to visitTruncate so we'd know which overflow check to create (signed or unsigned).

About the cache: For now, I didn't define it to hold a set of predicates but just a single SCEVWrapPredicate per item; I was wondering if maybe we want a SCEVUnionPredicate instead of a Set of predicates? In any case, maybe better leave that generalization for when the need arises?

I have not yet implemented the code style comments by Ayal that relate to the code I copied from createAddRecFromPHI; These should be fixed in both functions, and I want to try to see if I can outline some common pieces between these functions to avoid duplication as much as possible.
I will go ahead and look at that next, and upload a new revision later on. Or in a separate patch if you prefer?
(only expected changes are NFC stuff around createAddRecFromPHI/ createAddRecFromPHIWithCasts; In terms of functionality - the patch is ready for review :-))

Sure, this is fine with me. It's indeed best to address style comments separately.

Sure, this is fine with me. It's indeed best to address style comments separately.

Great.

In that case, I have no further updates to this revision at this point.

Any further comments anyone? Silviu, does this address your all your comments?

Hi Dorit,

Sorry for the delayed review. I have some more comments.

Regarding testing: it would be nicer to add some LAA tests, since the LAA analysis results will print the added predicates and the SCEV expressions for the bounds.

Thanks,
Silviu

llvm/include/llvm/Analysis/ScalarEvolution.h
1722

The data in this map should be also transferred in ScalarEvolution::ScalarEvolution(ScalarEvolution &&Arg).

We should also remove mappings in forgetLoop().

The point of the mapping would be to remove existing loop-variant SCEVUnknowns from the analysis result, so we should have a better name for this? Maybe PredicatedAnalyzableSCEVs?

llvm/lib/Analysis/ScalarEvolution.cpp
4086

Can this be a static function?

4225

It would be nice to have a comment here saying that this works because we're going to add a SCEV predicate to prove that SymbolicPHI == Add->getOperand(i).

4262

I think the contract should be to return a SCEV expression with the same type as what getSCEV would return for the phi node.
It's also probably better to not mention the vectorizer here (since more passes will end up running this code anyway).

10868

Now that I'm having a fresh look, I think we need to revisit the conditions for the transformation here.

If we want to do trunc({x,+,y}) -> {trunc(x), +, trunc(y)}, is this not always true?

dorit updated this revision to Diff 95137.Apr 13 2017, 8:44 AM
dorit added a comment.Apr 13 2017, 9:00 AM

Regarding testing: it would be nicer to add some LAA tests, since the LAA analysis results will print the added predicates and the SCEV expressions for the bounds.

Until your patch in D17080 is committed my patch does not have an effect on loop-access-analysis PSE (only to loop-vectorizer's PSE)… So for now nothing is printed. I'm happy to add a loop-accesses analysis test as soon as your patch is committed.

Thanks very much for your comments,
Dorit

llvm/include/llvm/Analysis/ScalarEvolution.h
1722

The point of the mapping would be to remove existing loop-variant SCEVUnknowns from the analysis result, so we should have a better name for this? Maybe PredicatedAnalyzableSCEVs?

Changed to PredicatedSCEVRewrites (since we can rewrite the SCEVUnknowns into the SCEV on the RHS if we add the predicate). But if your suggestion is more intuitive to you I'll change to that.

llvm/lib/Analysis/ScalarEvolution.cpp
10868

Yes, I think you're right… This simplifies a few things… :-) so no need for a visitTruncateExpr case at all in this rewriter... the base class rewriter can handle the Truncate (without any predicates) (right?)

Thanks!!

dorit added inline comments.Apr 14 2017, 10:07 PM
llvm/include/llvm/Analysis/ScalarEvolution.h
1722

We should also remove mappings in forgetLoop().

Just noticed there's also a forgetMemoizedResults(S). Looks like we should be removing the mapping for S there too, right?

dorit updated this revision to Diff 95409.Apr 16 2017, 8:56 AM

Remove mappings from PredicatedSCEVRewrites also in forgetMemoizedResults().

dorit added a comment.Apr 23 2017, 1:09 AM

ping :-)
thanks,
Dorit

nitpick: you should run a spell checker over the patch.

This generally looks ok to me but since this is a complex change @sanjoy should approve it before being committed.

-Silviu

llvm/lib/Analysis/ScalarEvolution.cpp
4277

I think we need a more formal explanation here on why this predicate guarantees that this is an AddRecExpr.

We're trying to prove that if we have:

SymbolicPHI = phi({Start, LoopHead}, {NextValue, LoopLatch})
NextValue = (Sext ix (Trunc iy (%SymbolicPHI) to ix) to iy) + InvariantAccum

then SymolicPHI = {Start, +, InvariantAccum}.

At iteration 0 both values are equal to Start, so it's enough to prove that

SymbolicPhi + Invariant == (Sext (trunc (SymbolicPHI)) + Invariant, which should be true from the SCEV predicate.

10868

Correct, we shouldn't need to do anything here.

sanjoy requested changes to this revision.Apr 24 2017, 11:02 PM

Some comments inline.

llvm/include/llvm/Analysis/ScalarEvolution.h
1263

s/SumbolicPHI/SymbolicPHI/

1266

s/cahced/cached/

llvm/lib/Analysis/ScalarEvolution.cpp
4099

This is very minor, but we usually spell these as SExt and ZExt, in keeping with camel case.

4138

s/rewritew/rewrite/

4169

Usually for out parameters like this, the type is const SCEVPredicate *&. But I'd prefer just returning an std::pair.

4181

Can you do find({SymbolicPHI, L})?

4194

Pair isn't clear. Please either name it something more specific, or (IMO better) use {SymbolicPHI, L} instead of Pair.

4195

Can you do {SymbolicPHI, nullptr} instead of make_pair?

4259

Do you also need to check that the recurrence is affine?

4273

s/specificed/specified/

4278

I'm not sure that this is correct. Say we had a loop with 4 iterations, StartVal was i16 257, Accum was i16 1, TruncTy was i8 and the PHI was being zero extended. In that case, the value of the PHI node on the second iteration (i.e. after taking the backedge once) would be (trunc 257) + 1 = 2. However, despite {(trunc 257),+,1} = {1,+,1} not unsigned-overflowing in 4 iterations, {257,+,1} would not produce the correct values for the PHI node.

10634

Doesn't PredicatedSCEVRewrites.erase(I++) invalidate E?

10921

Use auto * for pointers.

This revision now requires changes to proceed.Apr 24 2017, 11:02 PM
dorit added a comment.Apr 25 2017, 5:30 AM

Hi Sanjoy, I will upload a new fixed version soon; just have two quick followup questions below. Thanks a lot!
Dorit

llvm/lib/Analysis/ScalarEvolution.cpp
4259

Oh, probably so…

Do you think the isAffine check is also required in createAddRecFromPHI()? createAddRecFromPHIWithCasts() is basically a subset of createAddRecFromPHI, modified to consider also the sext-trunc cast pattern (the intention is later to try to factor out common parts as much as possible). I copied this check from there without thinking much…

4278

I see.

So I guess we should be returning a {(sext (trunc Start)),+,{(sext (trunc Accum))} as the newAR, right...?

sanjoy added inline comments.Apr 25 2017, 10:11 AM
llvm/lib/Analysis/ScalarEvolution.cpp
4259

I was suggesting the isAffine since IIRC (but please check) you cannot create a no wrap predicate on a non-affine add recurrence.

4278

Not sure how that will work -- won't {(sext (trunc Start)),+,{(sext (trunc Accum))} be {1,+,1} and thus be 1 instead of 257 in the first iteration? I haven't thought this through, but I suspect you'll have to check that the starte value fits in the narrower type or something like that.

In any case, I agree with Silviu here -- whatever you go with, please justify why that is correct with a proof here.

10915

I'm also not a big fan of the name analyzeUnknownSCEVPHI here -- analyze does not mean anything specific, and the UnknownSCEV part is redundant. How about convertToAddRecWithPreds?

sbaranga added inline comments.Apr 25 2017, 10:40 AM
llvm/lib/Analysis/ScalarEvolution.cpp
4278

Maybe have two extra SCEVEqualPredicates to test that (sext (trunc Start)) == Start and {(sext (trunc Accum))} == Accum and return {Start, + Accum}?

Before adding the extra predicates it would be worth testing if SCEV can prove these properties statically first (for example for cases where either Start or Acum are constants).

dorit added inline comments.Apr 26 2017, 4:16 AM
llvm/lib/Analysis/ScalarEvolution.cpp
4278

Good idea! Sanjoy, Silviu: Are you ok with extending the SCEVEqualPredicate to a non constant on the RHS? (I think currently only SCEV == constant is supported).

sbaranga added inline comments.Apr 26 2017, 9:10 AM
llvm/lib/Analysis/ScalarEvolution.cpp
4278

That's ok with me.

dorit updated this revision to Diff 98422.May 10 2017, 3:29 AM
dorit edited edge metadata.

Main changes:

  1. SCEVEqualPredicate can take any SCEV on LHS/RHS (instead of only SCEVUnknown/SCEVConstant )
  2. We add three predicates instead of only one; so we now have pairs of <SCEV, SmallVector of predicates> (instead of <SCEV, single Predicate>)
  3. Added a proof for why these predicates guarantee the correctness of the proposed rewrite
  4. Addressed Style and Spelling comments

Thanks,
Dorit

dorit updated this revision to Diff 98427.May 10 2017, 4:55 AM

(discovered typo in the documentation)

sbaranga added inline comments.May 22 2017, 3:20 PM
llvm/lib/Analysis/ScalarEvolution.cpp
4355

I originally had in mind testing the SCEV expressions for equality (Expr == ExtendedExpr) and checking that both are loop invariant.

I had a look at the implementation of isKnownPredicate, and I'm not convinced this would work as expected. Can we add a regression test for this?

I think we need to check anyway that both expressions are loop invariant before adding the predicate.

dorit updated this revision to Diff 100147.May 24 2017, 12:42 PM
  • We now require that Accum is loop invariant.
  • Added a simple Expr == ExtendedExpr check, and only if it fails we call isKnownPredicate().
  • Extended the testcase:

• Check that we have both the overflow runtime check and the equality runtime check in doit1 and doit2
• Added doit3: where step is not invariant (not very interesting - we do nothing)
• Added doit4: where we can figure out at compile time that step == sext(trunc(step)). Here we check that we only have the overflow runtime check (without the equality runtime check).

Thanks,
Dorit

dorit added inline comments.May 24 2017, 12:43 PM
llvm/lib/Analysis/ScalarEvolution.cpp
4355

Right. I was assuming Accum is invariant in the loop, forgot I was allowing it to not be invariant. Thanks.

dorit added a comment.Jun 5 2017, 3:53 AM

ping?
thanks,
Dorit

Sorry for the delay, I missed the last update. I have a few minor suggestions, but otherwise I think it generally looks good.
I think Sanjoy still needs to approve this before it can go in.

Thanks
-Silviu

llvm/include/llvm/Analysis/ScalarEvolution.h
244

Ideally we would have something (not necessarily in SCEVEqualPredicate) to verify that these predicates can be checked.

We should also be making the LHS->RHS substitution in the rewriter at some point (not necessarily in this change).

llvm/lib/Analysis/ScalarEvolution.cpp
4093

s/confirms/conforms

4279

This is a bit hard to follow with NewExpr, Expr, etc and the initial statement problem should be simplified.

I guess we just want to prove that Expr(i) = Start + i *Accum, given that

(1) Expr(0) = Start
(2) Expr(i+1) = (Ext ix (Trunc iy (Expr(i)) to ix) to iy) + Accum

4303

No text should be required for iteration 2, proving the induction step should be enough.

4325

It would be better to just use Ext and Trunc as separate operators instead SExtTrunc.

llvm/test/Transforms/LoopVectorize/pr30654-phiscev-sext-trunc.ll
5

Could you add some text for each of these saying what predicates get added?

Thanks Silviu. I'll iron the comments following your suggestions.

llvm/include/llvm/Analysis/ScalarEvolution.h
244

Can you please clarify what check is missing?

And where would be an appropriate place add a TODO comment about the substitution?

llvm/lib/Analysis/ScalarEvolution.cpp
4303

I thought it's helpful to have an example step before the formal step. I'll drop it.

sbaranga added inline comments.Jun 14 2017, 9:03 AM
llvm/include/llvm/Analysis/ScalarEvolution.h
244

If we would have a Loop * as a member of SCEVEqualPredicate, and we could check that both LHS and RHS are invariant inside the constructor.

However, we don't need the Loop for anything else so I'm not sure that would be the right solution.

I guess since we already have an assert in AppendPredicate that should be enough for now.

dorit updated this revision to Diff 102963.Jun 18 2017, 12:34 AM

Addressed Silviu's last comments on the documentation:

  • Added the predicates that are added for each of the loops in the testcase
  • In the proof:
    • dropped all the introductory text and jump directly to the formal proof.
    • expanded the short notation I was using (SExTrunc) into the explicit notation (Ext ix (Trunc iy () to ix ) to iy)

@sbaranga, @sanjoy, ping :-)
thanks.
dorit

dorit added a comment.Jul 2 2017, 1:24 AM

ping^2

thanks,
dorit

It looks ok to me, thanks!

-Silviu

dorit added a comment.Jul 9 2017, 12:12 AM

@sanjoy, is the patch ok with you?

thanks,
Dorit

sanjoy requested changes to this revision.Jul 11 2017, 4:04 PM

Mostly minor stuff.

llvm/include/llvm/Analysis/ScalarEvolution.h
245

Now that the type system does not guarantee this, how about adding an assert that LHS != RHS?

1721

Why not have the key type be std::pair<const SCEVUnknown *, const Loop *>?

llvm/lib/Analysis/ScalarEvolution.cpp
4097

s/const SCEV *SymbolicPHI/const SCEVUnknown *SymbolicPHI/

4114

How about doing this check as the very first thing (to avoid doing this extra work when it would have failed anyway)?

4190

IMO a slightly more idiomatic pattern (which would obviate the need for the *** Part1 comment) is to have a createAddRecFromPHIWithCasts and a createAddRecFromPHIWithCastsImpl, where the first function checks PredicatedSCEVRewrites for an existing solution, and delegates to createAddRecFromPHIWithCastsImpl if we don't have a cached solution.

4204

Is this semantically important? That is, if you remove this and instead ensure that we always populate PredicatedSCEVRewrites[{SymbolicPHI, L}] before leaving this function, will we enter an infinite loop somewhere?

4246

Should we be getting here in the Add->getOperand(i) == SymbolicPHI case? Shouldn't the regular add recurrence creating logic have triggered?

4327

You should be able to cast<> here.

This revision now requires changes to proceed.Jul 11 2017, 4:04 PM
dorit updated this revision to Diff 106616.Jul 14 2017, 4:16 AM
dorit edited edge metadata.

Addressing Sanjoy's comments.

dorit marked 7 inline comments as done.Jul 14 2017, 4:30 AM

Hi Sanjoy,
Thanks! Comments addressed,
Dorit

llvm/include/llvm/Analysis/ScalarEvolution.h
245

Added in the constructor.

1721

Absolutely right (that was the intention, but somehow it made it only to the comment... )

llvm/lib/Analysis/ScalarEvolution.cpp
4204

I added a clarification on the motivation for this early initialization. There is nothing semantic behind it. It is just to avoid having to initialize it upon every exit from the function (keep things a bit more compact, avoid the slight code duplication, make sure we don't forget it…). Is that ok with you with the clarification?

4246

yes, I would indeed expect it would have been caught already; added a comment here, and an assert in isSimpleCastedPHI.

sanjoy accepted this revision.Jul 15 2017, 12:33 PM

lgtm with one nit

llvm/lib/Analysis/ScalarEvolution.cpp
4204

Given that you now have the createAddRecFromPHIWithCastsImpl split out, a cleaner way to achieve the same property would be to insert the result into the cache in createAddRecFromPHIWithCasts.

This revision is now accepted and ready to land.Jul 15 2017, 12:33 PM
dorit updated this revision to Diff 106819.Jul 16 2017, 2:35 PM
dorit marked 4 inline comments as done.

Hi Sanjoy,

Thanks!

I addressed your last comment + made another small change:

Should we be getting here in the Add->getOperand(i) == SymbolicPHI case? Shouldn't the regular add recurrence creating logic have triggered?

I realized that we may be getting here with Op == SymbolicPHI because we haven't yet processed the rest of the operands of the Add... (so createAddRecFromPHI may have failed because one of them is not invariant). So I changed the assert back to an if, with a detailed comment (in isSimpleCastedPHI).

Will wait a bit before I commit.

Many thanks Sanjoy and Silviu for all your help with this patch,

Dorit

This revision was automatically updated to reflect the committed changes.