[llvm] [IPSCCP] Add range attribute handling. (PR #86747)
Andreas Jonson via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 10 12:08:47 PDT 2024
https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/86747
>From 9f2fb2a8a51fee3c26ba735e3c64300578ad8c3e Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Thu, 4 Apr 2024 20:47:10 +0200
Subject: [PATCH 1/2] [IPSCCP] add test for range attribute handling. NFC.
---
llvm/test/Transforms/SCCP/range-attribute.ll | 163 +++++++++++++++++++
1 file changed, 163 insertions(+)
create mode 100644 llvm/test/Transforms/SCCP/range-attribute.ll
diff --git a/llvm/test/Transforms/SCCP/range-attribute.ll b/llvm/test/Transforms/SCCP/range-attribute.ll
new file mode 100644
index 00000000000000..ea1e88a7939b35
--- /dev/null
+++ b/llvm/test/Transforms/SCCP/range-attribute.ll
@@ -0,0 +1,163 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
+
+declare void @use(i1)
+declare i32 @get_i32()
+
+define void @range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @range_attribute(
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C2]])
+; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT: call void @use(i1 [[C4]])
+; CHECK-NEXT: ret void
+;
+ %c1 = icmp ult i32 %v, 10
+ call void @use(i1 %c1)
+ %c2 = icmp ult i32 %v, 9
+ call void @use(i1 %c2)
+ %c3 = icmp ugt i32 %v, 9
+ call void @use(i1 %c3)
+ %c4 = icmp ugt i32 %v, 8
+ call void @use(i1 %c4)
+ ret void
+}
+
+define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
+; CHECK-LABEL: @range_attribute_single(
+; CHECK-NEXT: ret i32 [[V:%.*]]
+;
+ ret i32 %v
+}
+
+define void @call_range_attribute() {
+; CHECK-LABEL: @call_range_attribute(
+; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V]], 10
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C2]])
+; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT: call void @use(i1 [[C4]])
+; CHECK-NEXT: ret void
+;
+ %v = call range(i32 0, 10) i32 @get_i32()
+ %c1 = icmp ult i32 %v, 10
+ call void @use(i1 %c1)
+ %c2 = icmp ult i32 %v, 9
+ call void @use(i1 %c2)
+ %c3 = icmp ugt i32 %v, 9
+ call void @use(i1 %c3)
+ %c4 = icmp ugt i32 %v, 8
+ call void @use(i1 %c4)
+ ret void
+}
+
+
+declare range(i32 0, 10) i32 @get_i32_in_range()
+
+define void @call_range_result() {
+; CHECK-LABEL: @call_range_result(
+; CHECK-NEXT: [[V:%.*]] = call i32 @get_i32_in_range()
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V]], 10
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C2]])
+; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
+; CHECK-NEXT: call void @use(i1 [[C4]])
+; CHECK-NEXT: ret void
+;
+ %v = call i32 @get_i32_in_range()
+ %c1 = icmp ult i32 %v, 10
+ call void @use(i1 %c1)
+ %c2 = icmp ult i32 %v, 9
+ call void @use(i1 %c2)
+ %c3 = icmp ugt i32 %v, 9
+ call void @use(i1 %c3)
+ %c4 = icmp ugt i32 %v, 8
+ call void @use(i1 %c4)
+ ret void
+}
+
+define internal i1 @ip_cmp_range_attribute(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_attribute(
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %c = icmp ult i32 %v, 10
+ ret i1 %c
+}
+
+define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @ip_range_attribute(
+; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]])
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %c = call i1 @ip_cmp_range_attribute(i32 %v)
+ ret i1 %c
+}
+
+define internal i1 @ip_cmp_range_call(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_call(
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %c = icmp ult i32 %v, 10
+ ret i1 %c
+}
+
+define i1 @ip_range_call() {
+; CHECK-LABEL: @ip_range_call(
+; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]])
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %v = call range(i32 0, 10) i32 @get_i32()
+ %c = call i1 @ip_cmp_range_call(i32 %v)
+ ret i1 %c
+}
+
+define internal i1 @ip_cmp_range_result(i32 %v) {
+; CHECK-LABEL: @ip_cmp_range_result(
+; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %c = icmp ult i32 %v, 10
+ ret i1 %c
+}
+
+define i1 @ip_range_result() {
+; CHECK-LABEL: @ip_range_result(
+; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
+; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]])
+; CHECK-NEXT: ret i1 [[C]]
+;
+ %v = call range(i32 0, 10) i32 @get_i32()
+ %c = call i1 @ip_cmp_range_result(i32 %v)
+ ret i1 %c
+}
+
+define internal i1 @ip_cmp_with_range_attribute(i32 range(i32 0, 10) %v) {
+; CHECK-LABEL: @ip_cmp_with_range_attribute(
+; CHECK-NEXT: ret i1 undef
+;
+ %c = icmp eq i32 %v, 5
+ ret i1 %c
+}
+
+define i1 @ip_range_attribute_constant() {
+; CHECK-LABEL: @ip_range_attribute_constant(
+; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_with_range_attribute(i32 5)
+; CHECK-NEXT: ret i1 true
+;
+ %c = call i1 @ip_cmp_with_range_attribute(i32 5)
+ ret i1 %c
+}
>From fa71860e1937b916758693e268d5a16021c8b5c3 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Wed, 10 Apr 2024 21:05:00 +0200
Subject: [PATCH 2/2] [IPSCCP] Add range attribute handling.
---
.../llvm/Transforms/Utils/SCCPSolver.h | 4 ++
llvm/lib/Transforms/IPO/SCCP.cpp | 3 +-
llvm/lib/Transforms/Utils/SCCPSolver.cpp | 40 ++++++++++++++++++-
llvm/test/Transforms/SCCP/range-attribute.ll | 37 +++++++----------
4 files changed, 57 insertions(+), 27 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
index 1a95f80812aabd..9f7ccd4a8a32cf 100644
--- a/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
+++ b/llvm/include/llvm/Transforms/Utils/SCCPSolver.h
@@ -151,6 +151,10 @@ class SCCPSolver {
/// works with both scalars and structs.
void markOverdefined(Value *V);
+ /// trackValueOfArgument - Mark the specified argument overdefined unless it
+ /// have range attribute. This works with both scalars and structs.
+ void trackValueOfArgument(Argument *V);
+
// isStructLatticeConstant - Return true if all the lattice values
// corresponding to elements of the structure are constants,
// false otherwise.
diff --git a/llvm/lib/Transforms/IPO/SCCP.cpp b/llvm/lib/Transforms/IPO/SCCP.cpp
index b1f9b827dcbaf3..f8920541e6fd64 100644
--- a/llvm/lib/Transforms/IPO/SCCP.cpp
+++ b/llvm/lib/Transforms/IPO/SCCP.cpp
@@ -144,9 +144,8 @@ static bool runIPSCCP(
// Assume the function is called.
Solver.markBlockExecutable(&F.front());
- // Assume nothing about the incoming arguments.
for (Argument &AI : F.args())
- Solver.markOverdefined(&AI);
+ Solver.trackValueOfArgument(&AI);
}
// Determine if we can track any of the module's global variables. If so, add
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 38fc7763c5b204..b82ed25a6d0cf5 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -428,6 +428,13 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
return markConstant(ValueState[V], V, C);
}
+ /// markConstantRange - Mark the object as constant range with \p CR. If the
+ /// object is not a constant range with the range \p CR, add it to the
+ /// instruction work list so that the users of the instruction are updated
+ /// later.
+ bool markConstantRange(ValueLatticeElement &IV, Value *V,
+ const ConstantRange &CR);
+
// markOverdefined - Make a value be marked as "overdefined". If the
// value is not already overdefined, add it to the overdefined instruction
// work list so that the users of the instruction are updated later.
@@ -788,6 +795,17 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
markOverdefined(ValueState[V], V);
}
+ void trackValueOfArgument(Argument *A) {
+ if (A->getType()->isIntegerTy()) {
+ if (std::optional<ConstantRange> Range = A->getRange()) {
+ markConstantRange(ValueState[A], A, *Range);
+ return;
+ }
+ }
+ // Assume nothing about the incoming arguments without range.
+ markOverdefined(A);
+ }
+
bool isStructLatticeConstant(Function *F, StructType *STy);
Constant *getConstant(const ValueLatticeElement &LV, Type *Ty) const;
@@ -873,6 +891,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
return true;
}
+bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
+ const ConstantRange &CR) {
+ if (!IV.markConstantRange(CR))
+ return false;
+ LLVM_DEBUG(dbgs() << "markConstantRange: " << CR << ": " << *V << '\n');
+ pushToWorkList(IV, V);
+ return true;
+}
+
bool SCCPInstVisitor::markOverdefined(ValueLatticeElement &IV, Value *V) {
if (!IV.markOverdefined())
return false;
@@ -1581,10 +1608,15 @@ void SCCPInstVisitor::visitStoreInst(StoreInst &SI) {
}
static ValueLatticeElement getValueFromMetadata(const Instruction *I) {
- if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
- if (I->getType()->isIntegerTy())
+ if (I->getType()->isIntegerTy()) {
+ if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
return ValueLatticeElement::getRange(
getConstantRangeFromMetadata(*Ranges));
+
+ if (const auto *CB = dyn_cast<CallBase>(I))
+ if (std::optional<ConstantRange> Range = CB->getRange())
+ return ValueLatticeElement::getRange(*Range);
+ }
if (I->hasMetadata(LLVMContext::MD_nonnull))
return ValueLatticeElement::getNot(
ConstantPointerNull::get(cast<PointerType>(I->getType())));
@@ -2090,6 +2122,10 @@ const SmallPtrSet<Function *, 16> SCCPSolver::getMRVFunctionsTracked() {
void SCCPSolver::markOverdefined(Value *V) { Visitor->markOverdefined(V); }
+void SCCPSolver::trackValueOfArgument(Argument *V) {
+ Visitor->trackValueOfArgument(V);
+}
+
bool SCCPSolver::isStructLatticeConstant(Function *F, StructType *STy) {
return Visitor->isStructLatticeConstant(F, STy);
}
diff --git a/llvm/test/Transforms/SCCP/range-attribute.ll b/llvm/test/Transforms/SCCP/range-attribute.ll
index ea1e88a7939b35..ae66af91bb8e35 100644
--- a/llvm/test/Transforms/SCCP/range-attribute.ll
+++ b/llvm/test/Transforms/SCCP/range-attribute.ll
@@ -6,12 +6,10 @@ declare i32 @get_i32()
define void @range_attribute(i32 range(i32 0, 10) %v) {
; CHECK-LABEL: @range_attribute(
-; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT: call void @use(i1 [[C1]])
-; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V:%.*]], 9
; CHECK-NEXT: call void @use(i1 [[C2]])
-; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
; CHECK-NEXT: call void @use(i1 [[C4]])
; CHECK-NEXT: ret void
@@ -29,7 +27,7 @@ define void @range_attribute(i32 range(i32 0, 10) %v) {
define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
; CHECK-LABEL: @range_attribute_single(
-; CHECK-NEXT: ret i32 [[V:%.*]]
+; CHECK-NEXT: ret i32 0
;
ret i32 %v
}
@@ -37,12 +35,10 @@ define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
define void @call_range_attribute() {
; CHECK-LABEL: @call_range_attribute(
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
-; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
; CHECK-NEXT: call void @use(i1 [[C2]])
-; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
; CHECK-NEXT: call void @use(i1 [[C4]])
; CHECK-NEXT: ret void
@@ -65,12 +61,10 @@ declare range(i32 0, 10) i32 @get_i32_in_range()
define void @call_range_result() {
; CHECK-LABEL: @call_range_result(
; CHECK-NEXT: [[V:%.*]] = call i32 @get_i32_in_range()
-; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[V]], 10
-; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
; CHECK-NEXT: call void @use(i1 [[C2]])
-; CHECK-NEXT: [[C3:%.*]] = icmp ugt i32 [[V]], 9
-; CHECK-NEXT: call void @use(i1 [[C3]])
+; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
; CHECK-NEXT: call void @use(i1 [[C4]])
; CHECK-NEXT: ret void
@@ -89,8 +83,7 @@ define void @call_range_result() {
define internal i1 @ip_cmp_range_attribute(i32 %v) {
; CHECK-LABEL: @ip_cmp_range_attribute(
-; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 undef
;
%c = icmp ult i32 %v, 10
ret i1 %c
@@ -99,7 +92,7 @@ define internal i1 @ip_cmp_range_attribute(i32 %v) {
define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
; CHECK-LABEL: @ip_range_attribute(
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]])
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%c = call i1 @ip_cmp_range_attribute(i32 %v)
ret i1 %c
@@ -107,8 +100,7 @@ define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
define internal i1 @ip_cmp_range_call(i32 %v) {
; CHECK-LABEL: @ip_cmp_range_call(
-; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 undef
;
%c = icmp ult i32 %v, 10
ret i1 %c
@@ -118,7 +110,7 @@ define i1 @ip_range_call() {
; CHECK-LABEL: @ip_range_call(
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]])
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%v = call range(i32 0, 10) i32 @get_i32()
%c = call i1 @ip_cmp_range_call(i32 %v)
@@ -127,8 +119,7 @@ define i1 @ip_range_call() {
define internal i1 @ip_cmp_range_result(i32 %v) {
; CHECK-LABEL: @ip_cmp_range_result(
-; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[V:%.*]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 undef
;
%c = icmp ult i32 %v, 10
ret i1 %c
@@ -138,7 +129,7 @@ define i1 @ip_range_result() {
; CHECK-LABEL: @ip_range_result(
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]])
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%v = call range(i32 0, 10) i32 @get_i32()
%c = call i1 @ip_cmp_range_result(i32 %v)
More information about the llvm-commits
mailing list