[llvm] [SCEV] Handle more add/addrec mixes in computeConstantDifference() (PR #101999)
Philip Reames via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 12 09:19:07 PDT 2024
================
@@ -11931,61 +11931,88 @@ ScalarEvolution::computeConstantDifference(const SCEV *More, const SCEV *Less) {
// We avoid subtracting expressions here because this function is usually
// fairly deep in the call stack (i.e. is called many times).
- // X - X = 0.
unsigned BW = getTypeSizeInBits(More->getType());
- if (More == Less)
- return APInt(BW, 0);
-
- if (isa<SCEVAddRecExpr>(Less) && isa<SCEVAddRecExpr>(More)) {
- const auto *LAR = cast<SCEVAddRecExpr>(Less);
- const auto *MAR = cast<SCEVAddRecExpr>(More);
-
- if (LAR->getLoop() != MAR->getLoop())
- return std::nullopt;
-
- // We look at affine expressions only; not for correctness but to keep
- // getStepRecurrence cheap.
- if (!LAR->isAffine() || !MAR->isAffine())
- return std::nullopt;
+ APInt Diff(BW, 0);
+ // Try various simplifications to reduce the difference to a constant. Limit
+ // the number of allowed simplifications to keep compile-time low.
+ for (unsigned I = 0; I < 4; ++I) {
+ if (More == Less)
+ return Diff;
+
+ // Reduce addrecs with identical steps to their start value.
+ if (isa<SCEVAddRecExpr>(Less) && isa<SCEVAddRecExpr>(More)) {
+ const auto *LAR = cast<SCEVAddRecExpr>(Less);
+ const auto *MAR = cast<SCEVAddRecExpr>(More);
+
+ if (LAR->getLoop() != MAR->getLoop())
+ return std::nullopt;
+
+ // We look at affine expressions only; not for correctness but to keep
+ // getStepRecurrence cheap.
+ if (!LAR->isAffine() || !MAR->isAffine())
+ return std::nullopt;
+
+ if (LAR->getStepRecurrence(*this) != MAR->getStepRecurrence(*this))
+ return std::nullopt;
+
+ Less = LAR->getStart();
+ More = MAR->getStart();
+ continue;
+ }
- if (LAR->getStepRecurrence(*this) != MAR->getStepRecurrence(*this))
+ // Try to cancel out common factors in two add expressions.
+ SmallDenseMap<const SCEV *, int, 8> Multiplicity;
+ auto Add = [&](const SCEV *S, int Mul) {
+ if (auto *C = dyn_cast<SCEVConstant>(S)) {
+ if (Mul == 1) {
+ Diff += C->getAPInt();
+ } else {
+ assert(Mul == -1);
+ Diff -= C->getAPInt();
+ }
+ } else
+ Multiplicity[S] += Mul;
+ };
+ auto Decompose = [&](const SCEV *S, int Mul) {
+ if (isa<SCEVAddExpr>(S)) {
+ for (const SCEV *Op : S->operands())
+ Add(Op, Mul);
+ } else
+ Add(S, Mul);
+ };
+ Decompose(More, 1);
+ Decompose(Less, -1);
+
+ // Check whether all the non-constants cancel out, or reduce to new
+ // More/Less values.
+ const SCEV *NewMore = nullptr, *NewLess = nullptr;
+ for (const auto [S, Mul] : Multiplicity) {
+ if (Mul == 0)
+ continue;
+ if (Mul == 1) {
+ if (NewMore)
+ return std::nullopt;
+ NewMore = S;
+ } else if (Mul == -1) {
+ if (NewLess)
+ return std::nullopt;
+ NewLess = S;
+ } else
+ return std::nullopt;
+ }
+
+ // Values stayed the same, no point in trying further.
+ if (NewMore == More || NewLess == Less)
return std::nullopt;
- Less = LAR->getStart();
- More = MAR->getStart();
-
- // fall through
+ More = NewMore;
+ Less = NewLess;
+ if (!More || !Less)
----------------
preames wrote:
The stylistic choice here to break instead of just returning for the two sub-cases here seems a bit odd. It would seem to be a simpler invariant to always have More and Less valid values on the backedge.
https://github.com/llvm/llvm-project/pull/101999
More information about the llvm-commits
mailing list