aboutsummaryrefslogtreecommitdiff
path: root/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
diff options
context:
space:
mode:
authorPaul Walker <paul.walker@arm.com>2025-09-19 12:57:13 +0100
committerGitHub <noreply@github.com>2025-09-19 12:57:13 +0100
commit7b8fd8f31bc6d65a59b6e09ebbeb77fdfb95360f (patch)
tree280e311dc968465597a387cf4716811125a5f725 /llvm/unittests/Analysis/ScalarEvolutionTest.cpp
parent3c862b4ba39aa53a2dfcffb2fe7862e02f7bd746 (diff)
downloadllvm-7b8fd8f31bc6d65a59b6e09ebbeb77fdfb95360f.zip
llvm-7b8fd8f31bc6d65a59b6e09ebbeb77fdfb95360f.tar.gz
llvm-7b8fd8f31bc6d65a59b6e09ebbeb77fdfb95360f.tar.bz2
[LLVM][SCEV] Look through common vscale multiplicand when simplifying compares. (#141798)
My usecase is simplifying the control flow generated by LoopVectorize when vectorising loops whose tripcount is a function of the runtime vector length. This can be problematic because: * CSE is a pre-LoopVectorize transform and so it's common for an IR function to include several calls to llvm.vscale(). (NOTE: Code generation will typically remove the duplicates) * Pre-LoopVectorize instcombines will rewrite some multiplies as shifts. This leads to a mismatch between VL based maths of the scalar loop and that created for the vector loop, which prevents some obvious simplifications. SCEV does not suffer these issues because it effectively does CSE during construction and shifts are represented as multiplies.
Diffstat (limited to 'llvm/unittests/Analysis/ScalarEvolutionTest.cpp')
-rw-r--r--llvm/unittests/Analysis/ScalarEvolutionTest.cpp137
1 files changed, 137 insertions, 0 deletions
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 6789604..1a68823 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1768,4 +1768,141 @@ TEST_F(ScalarEvolutionsTest, ComplexityComparatorIsStrictWeakOrdering3) {
SE.getSCEV(Or1);
}
+TEST_F(ScalarEvolutionsTest, SimplifyICmpOperands) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M =
+ parseAssemblyString("define i32 @foo(ptr %loc, i32 %a, i32 %b) {"
+ "entry: "
+ " ret i32 %a "
+ "} ",
+ Err, C);
+
+ ASSERT_TRUE(M && "Could not parse module?");
+ ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+ // Remove common factor when there's no signed wrapping.
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *B = SE.getSCEV(getArgByName(F, "b"));
+ const SCEV *VS = SE.getVScale(A->getType());
+ const SCEV *VSxA = SE.getMulExpr(VS, A, SCEV::FlagNSW);
+ const SCEV *VSxB = SE.getMulExpr(VS, B, SCEV::FlagNSW);
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_SLT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_SLT);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, B);
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_ULT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_ULT);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, B);
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_EQ;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_EQ);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, B);
+ }
+
+ // Verify the common factor's position doesn't impede simplification.
+ {
+ const SCEV *C = SE.getConstant(A->getType(), 100);
+ const SCEV *CxVS = SE.getMulExpr(C, VS, SCEV::FlagNSW);
+
+ // Verify common factor is available at different indices.
+ ASSERT_TRUE(isa<SCEVVScale>(cast<SCEVMulExpr>(VSxA)->getOperand(0)) !=
+ isa<SCEVVScale>(cast<SCEVMulExpr>(CxVS)->getOperand(0)));
+
+ CmpPredicate NewPred = ICmpInst::ICMP_SLT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = CxVS;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_SLT);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, C);
+ }
+ });
+
+ // Remove common factor when there's no unsigned wrapping.
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *B = SE.getSCEV(getArgByName(F, "b"));
+ const SCEV *VS = SE.getVScale(A->getType());
+ const SCEV *VSxA = SE.getMulExpr(VS, A, SCEV::FlagNUW);
+ const SCEV *VSxB = SE.getMulExpr(VS, B, SCEV::FlagNUW);
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_SLT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_FALSE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_ULT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_ULT);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, B);
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_EQ;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_TRUE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ EXPECT_EQ(NewPred, ICmpInst::ICMP_EQ);
+ EXPECT_EQ(NewLHS, A);
+ EXPECT_EQ(NewRHS, B);
+ }
+ });
+
+ // Do not remove common factor due to wrap flag mismatch.
+ runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ const SCEV *A = SE.getSCEV(getArgByName(F, "a"));
+ const SCEV *B = SE.getSCEV(getArgByName(F, "b"));
+ const SCEV *VS = SE.getVScale(A->getType());
+ const SCEV *VSxA = SE.getMulExpr(VS, A, SCEV::FlagNSW);
+ const SCEV *VSxB = SE.getMulExpr(VS, B, SCEV::FlagNUW);
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_SLT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_FALSE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_ULT;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_FALSE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ }
+
+ {
+ CmpPredicate NewPred = ICmpInst::ICMP_EQ;
+ const SCEV *NewLHS = VSxA;
+ const SCEV *NewRHS = VSxB;
+ EXPECT_FALSE(SE.SimplifyICmpOperands(NewPred, NewLHS, NewRHS));
+ }
+ });
+}
+
} // end namespace llvm