From 19c19ca0d54a2458c8560489ceef2ba4d102f2e0 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Sun, 14 Sep 2025 23:11:30 +0800 Subject: [PATCH 1/8] [SCCP] Add pre-commit tests. NFC. --- .../Transforms/SCCP/relax-range-checks.ll | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 llvm/test/Transforms/SCCP/relax-range-checks.ll diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll new file mode 100644 index 0000000000000..ad9c476722174 --- /dev/null +++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -passes=sccp -S | FileCheck %s + +define i1 @relax_range_check(i8 range(i8 0, 5) %x) { +; CHECK-LABEL: define i1 @relax_range_check( +; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %add = add i8 %x, -3 + %ret = icmp ult i8 %add, 2 + ret i1 %ret +} + +define i1 @relax_range_check_highbits_check(i8 range(i8 2, 0) %x) { +; CHECK-LABEL: define i1 @relax_range_check_highbits_check( +; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) { +; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], -2 +; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[AND]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %and = and i8 %x, -2 + %ret = icmp eq i8 %and, 2 + ret i1 %ret +} + +; Negative tests. + +define i1 @relax_range_check_one_instruction(i8 range(i8 0, 5) %x) { +; CHECK-LABEL: define i1 @relax_range_check_one_instruction( +; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) { +; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[X]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %ret = icmp ult i8 %x, 2 + ret i1 %ret +} + +define i1 @relax_range_check_not_profitable(i8 range(i8 0, 6) %x) { +; CHECK-LABEL: define i1 @relax_range_check_not_profitable( +; CHECK-SAME: i8 range(i8 0, 6) [[X:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %add = add i8 %x, -3 + %ret = icmp ult i8 %add, 2 + ret i1 %ret +} From 3a89d80c31011205af132a4090949ab2df09f3e1 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Sun, 14 Sep 2025 23:13:03 +0800 Subject: [PATCH 2/8] [SCCP] Relax two-instruction range checks. --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 48 +++++++++++++++++++ .../Transforms/SCCP/relax-range-checks.ll | 4 +- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 84485176ad4ff..caaf069194b3b 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -19,8 +19,10 @@ #include "llvm/Analysis/ValueLattice.h" #include "llvm/Analysis/ValueLatticeUtils.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/ConstantRange.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Support/Casting.h" @@ -284,6 +286,52 @@ static Value *simplifyInstruction(SCCPSolver &Solver, return Sub; } + // Relax range checks. + if (auto *ICmp = dyn_cast(&Inst)) { + Value *X; + auto MatchTwoInstructionExactRangeCheck = + [&]() -> std::optional { + const APInt *RHSC; + if (!match(ICmp->getOperand(1), m_APInt(RHSC))) + return std::nullopt; + + Value *LHS = ICmp->getOperand(0); + ICmpInst::Predicate Pred = ICmp->getPredicate(); + unsigned BitWidth = RHSC->getBitWidth(); + if (Pred == ICmpInst::ICMP_ULT) { + const APInt *Offset; + if (match(LHS, m_AddLike(m_Value(X), m_APInt(Offset)))) + return ConstantRange(APInt::getZero(BitWidth), *RHSC).sub(*Offset); + return std::nullopt; + } + // Match icmp eq/ne X & NegPow2, C + if (ICmp->isEquality()) { + const APInt *Mask; + if (match(LHS, m_And(m_Value(X), m_NegatedPower2(Mask))) && + RHSC->countr_zero() >= Mask->countr_zero()) { + ConstantRange CR(*RHSC, *RHSC - *Mask); + return Pred == ICmpInst::ICMP_EQ ? CR : CR.inverse(); + } + } + return std::nullopt; + }; + + if (auto CR = MatchExactTwoInstructionRangeCheck()) { + ConstantRange LRange = GetRange(X); + if (auto NewCR = CR->exactUnionWith(LRange.inverse())) { + ICmpInst::Predicate Pred; + APInt RHS; + if (NewCR->getEquivalentICmp(Pred, RHS)) { + IRBuilder Builder(&Inst); + Value *NewICmp = + Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS)); + InsertedValues.insert(NewICmp); + return NewICmp; + } + } + } + } + return nullptr; } diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll index ad9c476722174..c5f90e419f710 100644 --- a/llvm/test/Transforms/SCCP/relax-range-checks.ll +++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll @@ -5,7 +5,7 @@ define i1 @relax_range_check(i8 range(i8 0, 5) %x) { ; CHECK-LABEL: define i1 @relax_range_check( ; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) { ; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3 -; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2 +; CHECK-NEXT: [[RET:%.*]] = icmp uge i8 [[X]], 3 ; CHECK-NEXT: ret i1 [[RET]] ; %add = add i8 %x, -3 @@ -17,7 +17,7 @@ define i1 @relax_range_check_highbits_check(i8 range(i8 2, 0) %x) { ; CHECK-LABEL: define i1 @relax_range_check_highbits_check( ; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) { ; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], -2 -; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[AND]], 2 +; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[X]], 4 ; CHECK-NEXT: ret i1 [[RET]] ; %and = and i8 %x, -2 From 99e2a89566dbb9fec1d76b84aa3a17e0044b0ca7 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Sun, 14 Sep 2025 23:33:15 +0800 Subject: [PATCH 3/8] [SCCP] Fix typo --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index caaf069194b3b..3dd45498e92a9 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -316,7 +316,7 @@ static Value *simplifyInstruction(SCCPSolver &Solver, return std::nullopt; }; - if (auto CR = MatchExactTwoInstructionRangeCheck()) { + if (auto CR = MatchTwoInstructionExactRangeCheck()) { ConstantRange LRange = GetRange(X); if (auto NewCR = CR->exactUnionWith(LRange.inverse())) { ICmpInst::Predicate Pred; From 6d1d9629aea42f6ab4bdb90c39d89ed8519aab3a Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 15 Sep 2025 00:14:34 +0800 Subject: [PATCH 4/8] [SCCP] Updates tests. NFC. --- llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll b/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll index b49a40abaaeb1..ef56d213d5db2 100644 --- a/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll +++ b/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll @@ -40,7 +40,7 @@ define void @val_undef_range() { ; CHECK-LABEL: @val_undef_range( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = add nuw nsw i32 undef, 0 -; CHECK-NEXT: [[BC_1:%.*]] = icmp ult i32 [[A]], 127 +; CHECK-NEXT: [[BC_1:%.*]] = icmp ult i32 undef, 127 ; CHECK-NEXT: br i1 [[BC_1]], label [[TRUE:%.*]], label [[FALSE:%.*]] ; CHECK: true: ; CHECK-NEXT: call void @use(i1 false) From 546f62765e4955675f0f5a53de3e25caf000a3a7 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 15 Sep 2025 00:26:01 +0800 Subject: [PATCH 5/8] [SCCP] Handle some negative cases. --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 4 ++- .../SCCP/conditions-ranges-with-undef.ll | 2 +- .../Transforms/SCCP/relax-range-checks.ll | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 3dd45498e92a9..516abfbe213f6 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -307,7 +307,7 @@ static Value *simplifyInstruction(SCCPSolver &Solver, // Match icmp eq/ne X & NegPow2, C if (ICmp->isEquality()) { const APInt *Mask; - if (match(LHS, m_And(m_Value(X), m_NegatedPower2(Mask))) && + if (match(LHS, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask)))) && RHSC->countr_zero() >= Mask->countr_zero()) { ConstantRange CR(*RHSC, *RHSC - *Mask); return Pred == ICmpInst::ICMP_EQ ? CR : CR.inverse(); @@ -318,6 +318,8 @@ static Value *simplifyInstruction(SCCPSolver &Solver, if (auto CR = MatchTwoInstructionExactRangeCheck()) { ConstantRange LRange = GetRange(X); + if (LRange.isFullSet()) + return nullptr; if (auto NewCR = CR->exactUnionWith(LRange.inverse())) { ICmpInst::Predicate Pred; APInt RHS; diff --git a/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll b/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll index ef56d213d5db2..b49a40abaaeb1 100644 --- a/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll +++ b/llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll @@ -40,7 +40,7 @@ define void @val_undef_range() { ; CHECK-LABEL: @val_undef_range( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = add nuw nsw i32 undef, 0 -; CHECK-NEXT: [[BC_1:%.*]] = icmp ult i32 undef, 127 +; CHECK-NEXT: [[BC_1:%.*]] = icmp ult i32 [[A]], 127 ; CHECK-NEXT: br i1 [[BC_1]], label [[TRUE:%.*]], label [[FALSE:%.*]] ; CHECK: true: ; CHECK-NEXT: call void @use(i1 false) diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll index c5f90e419f710..3b517bf0453c7 100644 --- a/llvm/test/Transforms/SCCP/relax-range-checks.ll +++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll @@ -48,3 +48,31 @@ define i1 @relax_range_check_not_profitable(i8 range(i8 0, 6) %x) { %ret = icmp ult i8 %add, 2 ret i1 %ret } + +define i1 @relax_range_check_unknown_range(i64 %x) { +; CHECK-LABEL: define i1 @relax_range_check_unknown_range( +; CHECK-SAME: i64 [[X:%.*]]) { +; CHECK-NEXT: [[AND:%.*]] = and i64 [[X]], -67108864 +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[AND]], 0 +; CHECK-NEXT: ret i1 [[TMP1]] +; + %and = and i64 %x, -67108864 + %test = icmp eq i64 %and, 0 + ret i1 %test +} + +define i1 @relax_range_check_highbits_check_multiuse(i8 range(i8 2, 0) %x) { +; CHECK-LABEL: define i1 @relax_range_check_highbits_check_multiuse( +; CHECK-SAME: i8 range(i8 2, 0) [[X:%.*]]) { +; CHECK-NEXT: [[AND:%.*]] = and i8 [[X]], -2 +; CHECK-NEXT: call void @use(i8 [[AND]]) +; CHECK-NEXT: [[RET:%.*]] = icmp eq i8 [[AND]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %and = and i8 %x, -2 + call void @use(i8 %and) + %ret = icmp eq i8 %and, 2 + ret i1 %ret +} + +declare void @use(i8) From fcd06a82a8e04120aa24cd521953740c9ca5d3d4 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 15 Sep 2025 01:14:40 +0800 Subject: [PATCH 6/8] [SCCP] Add one-use check. --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 34 +++++++++---------- .../Transforms/SCCP/relax-range-checks.ll | 14 ++++++++ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 516abfbe213f6..e239cad257141 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -297,13 +297,9 @@ static Value *simplifyInstruction(SCCPSolver &Solver, Value *LHS = ICmp->getOperand(0); ICmpInst::Predicate Pred = ICmp->getPredicate(); - unsigned BitWidth = RHSC->getBitWidth(); - if (Pred == ICmpInst::ICMP_ULT) { - const APInt *Offset; - if (match(LHS, m_AddLike(m_Value(X), m_APInt(Offset)))) - return ConstantRange(APInt::getZero(BitWidth), *RHSC).sub(*Offset); - return std::nullopt; - } + const APInt *Offset; + if (match(LHS, m_OneUse(m_AddLike(m_Value(X), m_APInt(Offset))))) + return ConstantRange::makeExactICmpRegion(Pred, *RHSC).sub(*Offset); // Match icmp eq/ne X & NegPow2, C if (ICmp->isEquality()) { const APInt *Mask; @@ -320,16 +316,20 @@ static Value *simplifyInstruction(SCCPSolver &Solver, ConstantRange LRange = GetRange(X); if (LRange.isFullSet()) return nullptr; - if (auto NewCR = CR->exactUnionWith(LRange.inverse())) { - ICmpInst::Predicate Pred; - APInt RHS; - if (NewCR->getEquivalentICmp(Pred, RHS)) { - IRBuilder Builder(&Inst); - Value *NewICmp = - Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS)); - InsertedValues.insert(NewICmp); - return NewICmp; - } + auto NewCR = CR->exactUnionWith(LRange.inverse()); + if (!NewCR) + return nullptr; + // Avoid transforming cases which do not relax the range. + if (NewCR.value() == *CR) + return nullptr; + ICmpInst::Predicate Pred; + APInt RHS; + if (NewCR->getEquivalentICmp(Pred, RHS)) { + IRBuilder Builder(&Inst); + Value *NewICmp = + Builder.CreateICmp(Pred, X, ConstantInt::get(X->getType(), RHS)); + InsertedValues.insert(NewICmp); + return NewICmp; } } } diff --git a/llvm/test/Transforms/SCCP/relax-range-checks.ll b/llvm/test/Transforms/SCCP/relax-range-checks.ll index 3b517bf0453c7..90722f350aa9e 100644 --- a/llvm/test/Transforms/SCCP/relax-range-checks.ll +++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll @@ -75,4 +75,18 @@ define i1 @relax_range_check_highbits_check_multiuse(i8 range(i8 2, 0) %x) { ret i1 %ret } +define i1 @relax_range_check_multiuse(i8 range(i8 0, 5) %x) { +; CHECK-LABEL: define i1 @relax_range_check_multiuse( +; CHECK-SAME: i8 range(i8 0, 5) [[X:%.*]]) { +; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], -3 +; CHECK-NEXT: call void @use(i8 [[ADD]]) +; CHECK-NEXT: [[RET:%.*]] = icmp ult i8 [[ADD]], 2 +; CHECK-NEXT: ret i1 [[RET]] +; + %add = add i8 %x, -3 + call void @use(i8 %add) + %ret = icmp ult i8 %add, 2 + ret i1 %ret +} + declare void @use(i8) From 5147912738bf57de944982c1f8e38af77fc202dc Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Tue, 16 Sep 2025 23:07:33 +0800 Subject: [PATCH 7/8] [SCCP] Add some comments. --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index e239cad257141..3c45046dc86bb 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -314,16 +314,25 @@ static Value *simplifyInstruction(SCCPSolver &Solver, if (auto CR = MatchTwoInstructionExactRangeCheck()) { ConstantRange LRange = GetRange(X); + // Early exit if we know nothing about X. if (LRange.isFullSet()) return nullptr; + // We are allowed to refine the comparison to either true or false for out + // of range inputs. Here we refine the comparison to true, i.e. we relax + // the range check. auto NewCR = CR->exactUnionWith(LRange.inverse()); + // TODO: Check if we can narrow the range check to an equality test. + // E.g, for X in [0, 4), X - 3 u< 2 -> X == 3 if (!NewCR) return nullptr; // Avoid transforming cases which do not relax the range. + // Without this we may revert the transform + // (X - C) < Pow2 -> (X & -Pow2) == C. if (NewCR.value() == *CR) return nullptr; ICmpInst::Predicate Pred; APInt RHS; + // Check if we can represent NewCR as an icmp predicate. if (NewCR->getEquivalentICmp(Pred, RHS)) { IRBuilder Builder(&Inst); Value *NewICmp = From bee3d902dd505cf9b11499ba4f230e4e8ae96b92 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Wed, 17 Sep 2025 00:12:03 +0800 Subject: [PATCH 8/8] [SCCP] Drop outdated check. --- llvm/lib/Transforms/Utils/SCCPSolver.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 3c45046dc86bb..af216cd9214bf 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -325,11 +325,6 @@ static Value *simplifyInstruction(SCCPSolver &Solver, // E.g, for X in [0, 4), X - 3 u< 2 -> X == 3 if (!NewCR) return nullptr; - // Avoid transforming cases which do not relax the range. - // Without this we may revert the transform - // (X - C) < Pow2 -> (X & -Pow2) == C. - if (NewCR.value() == *CR) - return nullptr; ICmpInst::Predicate Pred; APInt RHS; // Check if we can represent NewCR as an icmp predicate.