-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[SCEVExpander] Expand UDiv avoiding UB when in seq_min/max. #92177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
bff4e5a
16a4b00
32833a7
b0d098c
d6d8655
4ace524
70f2ef7
fa12ea4
5b1a789
c2dc23d
1468d8b
c12b25c
5f31e85
9c4b76d
b699ddd
5469289
d5c8931
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,6 +125,11 @@ class SCEVExpander : public SCEVVisitor<SCEVExpander, Value *> { | |
| /// "expanded" form. | ||
| bool LSRMode; | ||
|
|
||
| /// When true, rewrite any divisors of UDiv expressions that may be 0 to | ||
| /// umax(Divisor, 1) to avoid introducing UB. If the divisor may be poison, | ||
| /// freeze it first. | ||
| bool SafeUDivMode = false; | ||
|
|
||
| typedef IRBuilder<InstSimplifyFolder, IRBuilderCallbackInserter> BuilderType; | ||
| BuilderType Builder; | ||
|
|
||
|
|
@@ -419,6 +424,9 @@ class SCEVExpander : public SCEVVisitor<SCEVExpander, Value *> { | |
| BasicBlock::iterator findInsertPointAfter(Instruction *I, | ||
| Instruction *MustDominate) const; | ||
|
|
||
| static const SCEV *rewriteExpressionToRemoveUB(const SCEV *BTC, Loop *L, | ||
| ScalarEvolution &SE); | ||
|
||
|
|
||
| private: | ||
| LLVMContext &getContext() const { return SE.getContext(); } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4304,6 +4304,16 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind, | |
| } | ||
|
|
||
| for (unsigned i = 1, e = Ops.size(); i != e; ++i) { | ||
| bool MayBeUB = SCEVExprContains(Ops[i], [this](const SCEV *S) { | ||
| auto *UDiv = dyn_cast<SCEVUDivExpr>(S); | ||
| // The UDiv may be UB if the divisor is poison or zero. Unless the divisor | ||
| // is a non-zero constant, we have to assume the UDiv may be UB. | ||
| return UDiv && (!isa<SCEVConstant>(UDiv->getOperand(1)) || | ||
| !isKnownNonZero(UDiv->getOperand(1))); | ||
| }); | ||
|
|
||
| if (MayBeUB) | ||
| continue; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should also change the doc comment on SCEVSequentialMinMaxExpr for the new semantics introduced here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added thanks! |
||
| // We can replace %x umin_seq %y with %x umin %y if either: | ||
| // * %y being poison implies %x is also poison. | ||
| // * %x cannot be the saturating value (e.g. zero for umin). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -676,7 +676,15 @@ Value *SCEVExpander::visitUDivExpr(const SCEVUDivExpr *S) { | |
| SCEV::FlagAnyWrap, /*IsSafeToHoist*/ true); | ||
| } | ||
|
|
||
| Value *RHS = expand(S->getRHS()); | ||
| const SCEV *RHSExpr = S->getRHS(); | ||
| Value *RHS = expand(RHSExpr); | ||
| if (SafeUDivMode && | ||
| (!isa<SCEVConstant>(RHSExpr) || SE.isKnownNonZero(RHSExpr))) { | ||
| if (!isa<SCEVConstant>(S->getRHS())) | ||
|
||
| RHS = Builder.CreateFreeze(RHS); | ||
| RHS = Builder.CreateIntrinsic(RHS->getType(), Intrinsic::umax, | ||
| {RHS, ConstantInt::get(RHS->getType(), 1)}); | ||
| } | ||
| return InsertBinop(Instruction::UDiv, LHS, RHS, SCEV::FlagAnyWrap, | ||
| /*IsSafeToHoist*/ SE.isKnownNonZero(S->getRHS())); | ||
| } | ||
|
|
@@ -1371,11 +1379,14 @@ Value *SCEVExpander::visitSignExtendExpr(const SCEVSignExtendExpr *S) { | |
| Value *SCEVExpander::expandMinMaxExpr(const SCEVNAryExpr *S, | ||
| Intrinsic::ID IntrinID, Twine Name, | ||
| bool IsSequential) { | ||
| bool PrevSafeMode = SafeUDivMode; | ||
| SafeUDivMode |= IsSequential; | ||
| Value *LHS = expand(S->getOperand(S->getNumOperands() - 1)); | ||
| Type *Ty = LHS->getType(); | ||
| if (IsSequential) | ||
| LHS = Builder.CreateFreeze(LHS); | ||
| for (int i = S->getNumOperands() - 2; i >= 0; --i) { | ||
| SafeUDivMode = (IsSequential && i != 0) || PrevSafeMode; | ||
| Value *RHS = expand(S->getOperand(i)); | ||
| if (IsSequential && i != 0) | ||
| RHS = Builder.CreateFreeze(RHS); | ||
|
|
@@ -1390,6 +1401,7 @@ Value *SCEVExpander::expandMinMaxExpr(const SCEVNAryExpr *S, | |
| } | ||
| LHS = Sel; | ||
| } | ||
| SafeUDivMode = PrevSafeMode; | ||
| return LHS; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't really two separate cases. The "early return" happens "upon reaching the saturation point" in both cases. The clarification here is just that "early return" is in the sense that the RHS will not be executed at all (even if it has UB), rather than only that its value will not be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, should be updated in #110824