-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[SCCP] Relax two-instruction range checks #158495
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
✅ With the latest revision this PR passed the undef deprecator. |
@llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesIf we know x in R1, the range check Compile-time impact: https://llvm-compile-time-tracker.com/compare.php?from=ead4f3e271fdf6918aef2ede3a7134811147d276&to=fcd06a82a8e04120aa24cd521953740c9ca5d3d4&stat=instructions%3Au Full diff: https://github.com/llvm/llvm-project/pull/158495.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 84485176ad4ff..e239cad257141 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,54 @@ static Value *simplifyInstruction(SCCPSolver &Solver,
return Sub;
}
+ // Relax range checks.
+ if (auto *ICmp = dyn_cast<ICmpInst>(&Inst)) {
+ Value *X;
+ auto MatchTwoInstructionExactRangeCheck =
+ [&]() -> std::optional<ConstantRange> {
+ const APInt *RHSC;
+ if (!match(ICmp->getOperand(1), m_APInt(RHSC)))
+ return std::nullopt;
+
+ Value *LHS = ICmp->getOperand(0);
+ ICmpInst::Predicate Pred = ICmp->getPredicate();
+ 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;
+ 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();
+ }
+ }
+ return std::nullopt;
+ };
+
+ if (auto CR = MatchTwoInstructionExactRangeCheck()) {
+ ConstantRange LRange = GetRange(X);
+ if (LRange.isFullSet())
+ return nullptr;
+ 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<NoFolder> 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
new file mode 100644
index 0000000000000..90722f350aa9e
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll
@@ -0,0 +1,92 @@
+; 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 uge i8 [[X]], 3
+; 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 ult i8 [[X]], 4
+; 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
+}
+
+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
+}
+
+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)
|
@llvm/pr-subscribers-function-specialization Author: Yingwei Zheng (dtcxzyw) ChangesIf we know x in R1, the range check Compile-time impact: https://llvm-compile-time-tracker.com/compare.php?from=ead4f3e271fdf6918aef2ede3a7134811147d276&to=fcd06a82a8e04120aa24cd521953740c9ca5d3d4&stat=instructions%3Au Full diff: https://github.com/llvm/llvm-project/pull/158495.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 84485176ad4ff..e239cad257141 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,54 @@ static Value *simplifyInstruction(SCCPSolver &Solver,
return Sub;
}
+ // Relax range checks.
+ if (auto *ICmp = dyn_cast<ICmpInst>(&Inst)) {
+ Value *X;
+ auto MatchTwoInstructionExactRangeCheck =
+ [&]() -> std::optional<ConstantRange> {
+ const APInt *RHSC;
+ if (!match(ICmp->getOperand(1), m_APInt(RHSC)))
+ return std::nullopt;
+
+ Value *LHS = ICmp->getOperand(0);
+ ICmpInst::Predicate Pred = ICmp->getPredicate();
+ 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;
+ 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();
+ }
+ }
+ return std::nullopt;
+ };
+
+ if (auto CR = MatchTwoInstructionExactRangeCheck()) {
+ ConstantRange LRange = GetRange(X);
+ if (LRange.isFullSet())
+ return nullptr;
+ 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<NoFolder> 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
new file mode 100644
index 0000000000000..90722f350aa9e
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/relax-range-checks.ll
@@ -0,0 +1,92 @@
+; 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 uge i8 [[X]], 3
+; 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 ult i8 [[X]], 4
+; 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
+}
+
+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
+}
+
+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)
|
// Relax range checks. | ||
if (auto *ICmp = dyn_cast<ICmpInst>(&Inst)) { | ||
Value *X; | ||
auto MatchTwoInstructionExactRangeCheck = |
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.
almost the same as MatchRangeCheck in #158498 is it possible to have this common somewhere?
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.
I used to add a common helper in ValueTracking. But I finally decided to duplicate the code because they have different one-use constraints from each other.
Value *LHS = ICmp->getOperand(0); | ||
ICmpInst::Predicate Pred = ICmp->getPredicate(); | ||
const APInt *Offset; | ||
if (match(LHS, m_OneUse(m_AddLike(m_Value(X), m_APInt(Offset))))) |
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.
is it not good to remove the dependency to the "add" even if there is muli use?
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.
It is tuned based on the feedback from opt-bench. See the ir diff in previous runs.
ConstantRange LRange = GetRange(X); | ||
if (LRange.isFullSet()) | ||
return nullptr; | ||
auto NewCR = CR->exactUnionWith(LRange.inverse()); |
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.
maybe good to have a comment here that describe why union of the inverse relax the range
if (LRange.isFullSet()) | ||
return nullptr; | ||
auto NewCR = CR->exactUnionWith(LRange.inverse()); | ||
if (!NewCR) |
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.
do we also want to check the intersection with LRange as if there only is two values it is possible to have EQ/NE predicate instead? eg if relax_range_check had the argument range [0,4) instead of [0,5)
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.
I will add a todo here.
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.
LGTM
@zyw-bot csmith-quick-fuzz |
If we know x in R1, the range check
x in R2
can be relaxed intox in Union(R2, Inverse(R1))
. The latter one may be more efficient if we can represent it with one icmp.Fixes regressions introduced by #156497.
Proof for
(X & -Pow2) == C -> (X - C) < Pow2
: https://alive2.llvm.org/ce/z/HMgkuuCompile-time impact: https://llvm-compile-time-tracker.com/compare.php?from=ead4f3e271fdf6918aef2ede3a7134811147d276&to=bee3d902dd505cf9b11499ba4f230e4e8ae96b92&stat=instructions%3Au