-
Notifications
You must be signed in to change notification settings - Fork 14.3k
Remove value cache in SCEV comparator. #100721
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
Conversation
@llvm/pr-subscribers-llvm-analysis Author: Johannes Reifferscheid (jreiffers) ChangesThe cache triggers almost never, and seems unlikely to help with performance. However, when it does, it is likely to cause the comparator to become inconsistent due to a bad interaction of the depth limit and cache hits. This leads to crashes in debug builds. See the new unit test for a reproducer. Full diff: https://github.com/llvm/llvm-project/pull/100721.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 51cffac808768..a69fe43b848b8 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -598,10 +598,9 @@ void SCEVUnknown::allUsesReplacedWith(Value *New) {
/// Since we do not continue running this routine on expression trees once we
/// have seen unequal values, there is no need to track them in the cache.
static int
-CompareValueComplexity(EquivalenceClasses<const Value *> &EqCacheValue,
- const LoopInfo *const LI, Value *LV, Value *RV,
+CompareValueComplexity(const LoopInfo *const LI, Value *LV, Value *RV,
unsigned Depth) {
- if (Depth > MaxValueCompareDepth || EqCacheValue.isEquivalent(LV, RV))
+ if (Depth > MaxValueCompareDepth)
return 0;
// Order pointer values after integer values. This helps SCEVExpander form
@@ -660,15 +659,13 @@ CompareValueComplexity(EquivalenceClasses<const Value *> &EqCacheValue,
return (int)LNumOps - (int)RNumOps;
for (unsigned Idx : seq(LNumOps)) {
- int Result =
- CompareValueComplexity(EqCacheValue, LI, LInst->getOperand(Idx),
- RInst->getOperand(Idx), Depth + 1);
+ int Result = CompareValueComplexity(LI, LInst->getOperand(Idx),
+ RInst->getOperand(Idx), Depth + 1);
if (Result != 0)
return Result;
}
}
- EqCacheValue.unionSets(LV, RV);
return 0;
}
@@ -679,7 +676,6 @@ CompareValueComplexity(EquivalenceClasses<const Value *> &EqCacheValue,
// not know if they are equivalent for sure.
static std::optional<int>
CompareSCEVComplexity(EquivalenceClasses<const SCEV *> &EqCacheSCEV,
- EquivalenceClasses<const Value *> &EqCacheValue,
const LoopInfo *const LI, const SCEV *LHS,
const SCEV *RHS, DominatorTree &DT, unsigned Depth = 0) {
// Fast-path: SCEVs are uniqued so we can do a quick equality check.
@@ -705,8 +701,8 @@ CompareSCEVComplexity(EquivalenceClasses<const SCEV *> &EqCacheSCEV,
const SCEVUnknown *LU = cast<SCEVUnknown>(LHS);
const SCEVUnknown *RU = cast<SCEVUnknown>(RHS);
- int X = CompareValueComplexity(EqCacheValue, LI, LU->getValue(),
- RU->getValue(), Depth + 1);
+ int X = CompareValueComplexity(LI, LU->getValue(), RU->getValue(),
+ Depth + 1);
if (X == 0)
EqCacheSCEV.unionSets(LHS, RHS);
return X;
@@ -773,8 +769,8 @@ CompareSCEVComplexity(EquivalenceClasses<const SCEV *> &EqCacheSCEV,
return (int)LNumOps - (int)RNumOps;
for (unsigned i = 0; i != LNumOps; ++i) {
- auto X = CompareSCEVComplexity(EqCacheSCEV, EqCacheValue, LI, LOps[i],
- ROps[i], DT, Depth + 1);
+ auto X = CompareSCEVComplexity(EqCacheSCEV, LI, LOps[i], ROps[i], DT,
+ Depth + 1);
if (X != 0)
return X;
}
@@ -802,12 +798,10 @@ static void GroupByComplexity(SmallVectorImpl<const SCEV *> &Ops,
if (Ops.size() < 2) return; // Noop
EquivalenceClasses<const SCEV *> EqCacheSCEV;
- EquivalenceClasses<const Value *> EqCacheValue;
// Whether LHS has provably less complexity than RHS.
auto IsLessComplex = [&](const SCEV *LHS, const SCEV *RHS) {
- auto Complexity =
- CompareSCEVComplexity(EqCacheSCEV, EqCacheValue, LI, LHS, RHS, DT);
+ auto Complexity = CompareSCEVComplexity(EqCacheSCEV, LI, LHS, RHS, DT);
return Complexity && *Complexity < 0;
};
if (Ops.size() == 2) {
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index a6a5ffda3cb70..b923c920b4d75 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1625,4 +1625,40 @@ TEST_F(ScalarEvolutionsTest, ForgetValueWithOverflowInst) {
});
}
+EST_F(ScalarEvolutionsTest, ComplexityComparatorIsStrictWeakOrdering) {
+ // Regression test for a case where caching of equivalent values caused the
+ // comparator to get inconsistent.
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(R"(
+ define i32 @foo(i32 %arg0) {
+ %1 = add i32 %arg0, 1
+ %2 = add i32 %arg0, 1
+ %3 = xor i32 %2, %1
+ %4 = add i32 %3, %2
+ %5 = add i32 %arg0, 1
+ %6 = xor i32 %5, %arg0
+ %7 = add i32 %arg0, %6
+ %8 = add i32 %5, %7
+ %9 = xor i32 %8, %7
+ %10 = add i32 %9, %8
+ %11 = xor i32 %10, %9
+ %12 = add i32 %11, %10
+ %13 = xor i32 %12, %11
+ %14 = add i32 %12, %13
+ %15 = add i32 %14, %4
+ ret i32 %15
+ })",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ // When _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG, this will
+ // crash if the comparator has the specific caching bug.
+ SE.getSCEV(F.getEntryBlock().getTerminator()->getOperand(0));
+ });
+}
+
} // end namespace llvm
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
The cache triggers almost never. When it does, it is likely to cause the comparator to become inconsistent due to a bad interaction of the depth limit and cache hits. This leads to crashes in debug builds.
…ached Similarly to CompareSCEVComplexity() itself, we should return nullopt instead of 0 from CompareValueComplexity(), because we do not know how the values related to each other. This means that the values will not be added to the equality cache, and it can not become inconsistent due to different results at different depths. The test case is adopted from llvm#100721.
I've been looking into this, and think that this is not quite the right fix for the issue. I've put up an alternative at #101022. CompareSCEVComplexity was already returning nullopt when hitting the depth limit, but CompareValueComplexity wasn't. I think it we just drop the EqCache at the Value level, we might still run into a similar issue with the higher level cache at the SCEV level. I do think that removing the cache is still a good idea independent of the strict weak ordering issue though. Based on my measurements, removing it improves compile-time. And given the very low depth limit involved here, I don't think we need to worry about regressing any degenerate cases either. |
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.
Actually, let's go ahead and merge this one first. The situation in CompareValueComplexity is pretty silly right now because MaxValueCompareDepth is much lower than MaxSCEVCompareDepth, but they reuse the same Depth parameter, which means that we're almost always going to hit the depth limit in this function. The implementation here warrants a larger change, so let's just drop the cache for now...
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/2464 Here is the relevant piece of the build log for the reference:
|
This was added in https://reviews.llvm.org/D26389 to help with extremely deep SCEV expressions. However, this is wrong since we may cache sub-SCEVs to be equivalent that CompareValueComplexity returned 0 due to hitting the max comparison depth. This doesn't fully fix the issue, since we may still consider deeply nested SCEVs that only differ in the innermost expression as equivalent due to hitting depth limits, but this helps with part of the problem. This also improves compile time in some compiles: https://llvm-compile-time-tracker.com/compare.php?from=34fa037c4fd7f38faada5beedc63ad234e904247&to=e241ecf999f4dd42d4b951d4a5d4f8eabeafcff0&stat=instructions:u Similar to llvm#100721. Partially fixes llvm#130688 and a miscompile we're seeing.
This was added in https://reviews.llvm.org/D26389 to help with extremely deep SCEV expressions. However, this is wrong since we may cache sub-SCEVs to be equivalent that CompareValueComplexity returned 0 due to hitting the max comparison depth. This also improves compile time in some compiles: https://llvm-compile-time-tracker.com/compare.php?from=34fa037c4fd7f38faada5beedc63ad234e904247&to=e241ecf999f4dd42d4b951d4a5d4f8eabeafcff0&stat=instructions:u Similar to #100721. Fixes #130688.
This was added in https://reviews.llvm.org/D26389 to help with extremely deep SCEV expressions. However, this is wrong since we may cache sub-SCEVs to be equivalent that CompareValueComplexity returned 0 due to hitting the max comparison depth. This also improves compile time in some compiles: https://llvm-compile-time-tracker.com/compare.php?from=34fa037c4fd7f38faada5beedc63ad234e904247&to=e241ecf999f4dd42d4b951d4a5d4f8eabeafcff0&stat=instructions:u Similar to llvm#100721. Fixes llvm#130688.
The cache triggers almost never, and seems unlikely to help with performance. However, when it does, it is likely to cause the comparator to become inconsistent due to a bad interaction of the depth limit and cache hits. This leads to crashes in debug builds. See the new unit test for a reproducer.