Skip to content

Commit e7bc537

Browse files
authored
[IPSCCP] Add range attribute handling (#86747)
Support the new range attribute to infer ConstantRanges in IPSCCP.
1 parent 82ae646 commit e7bc537

File tree

4 files changed

+197
-4
lines changed

4 files changed

+197
-4
lines changed

llvm/include/llvm/Transforms/Utils/SCCPSolver.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ class SCCPSolver {
151151
/// works with both scalars and structs.
152152
void markOverdefined(Value *V);
153153

154+
/// trackValueOfArgument - Mark the specified argument overdefined unless it
155+
/// have range attribute. This works with both scalars and structs.
156+
void trackValueOfArgument(Argument *V);
157+
154158
// isStructLatticeConstant - Return true if all the lattice values
155159
// corresponding to elements of the structure are constants,
156160
// false otherwise.

llvm/lib/Transforms/IPO/SCCP.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,8 @@ static bool runIPSCCP(
144144
// Assume the function is called.
145145
Solver.markBlockExecutable(&F.front());
146146

147-
// Assume nothing about the incoming arguments.
148147
for (Argument &AI : F.args())
149-
Solver.markOverdefined(&AI);
148+
Solver.trackValueOfArgument(&AI);
150149
}
151150

152151
// Determine if we can track any of the module's global variables. If so, add

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,13 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
428428
return markConstant(ValueState[V], V, C);
429429
}
430430

431+
/// markConstantRange - Mark the object as constant range with \p CR. If the
432+
/// object is not a constant range with the range \p CR, add it to the
433+
/// instruction work list so that the users of the instruction are updated
434+
/// later.
435+
bool markConstantRange(ValueLatticeElement &IV, Value *V,
436+
const ConstantRange &CR);
437+
431438
// markOverdefined - Make a value be marked as "overdefined". If the
432439
// value is not already overdefined, add it to the overdefined instruction
433440
// work list so that the users of the instruction are updated later.
@@ -788,6 +795,17 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
788795
markOverdefined(ValueState[V], V);
789796
}
790797

798+
void trackValueOfArgument(Argument *A) {
799+
if (A->getType()->isIntegerTy()) {
800+
if (std::optional<ConstantRange> Range = A->getRange()) {
801+
markConstantRange(ValueState[A], A, *Range);
802+
return;
803+
}
804+
}
805+
// Assume nothing about the incoming arguments without range.
806+
markOverdefined(A);
807+
}
808+
791809
bool isStructLatticeConstant(Function *F, StructType *STy);
792810

793811
Constant *getConstant(const ValueLatticeElement &LV, Type *Ty) const;
@@ -873,6 +891,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
873891
return true;
874892
}
875893

894+
bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
895+
const ConstantRange &CR) {
896+
if (!IV.markConstantRange(CR))
897+
return false;
898+
LLVM_DEBUG(dbgs() << "markConstantRange: " << CR << ": " << *V << '\n');
899+
pushToWorkList(IV, V);
900+
return true;
901+
}
902+
876903
bool SCCPInstVisitor::markOverdefined(ValueLatticeElement &IV, Value *V) {
877904
if (!IV.markOverdefined())
878905
return false;
@@ -1581,10 +1608,15 @@ void SCCPInstVisitor::visitStoreInst(StoreInst &SI) {
15811608
}
15821609

15831610
static ValueLatticeElement getValueFromMetadata(const Instruction *I) {
1584-
if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
1585-
if (I->getType()->isIntegerTy())
1611+
if (I->getType()->isIntegerTy()) {
1612+
if (MDNode *Ranges = I->getMetadata(LLVMContext::MD_range))
15861613
return ValueLatticeElement::getRange(
15871614
getConstantRangeFromMetadata(*Ranges));
1615+
1616+
if (const auto *CB = dyn_cast<CallBase>(I))
1617+
if (std::optional<ConstantRange> Range = CB->getRange())
1618+
return ValueLatticeElement::getRange(*Range);
1619+
}
15881620
if (I->hasMetadata(LLVMContext::MD_nonnull))
15891621
return ValueLatticeElement::getNot(
15901622
ConstantPointerNull::get(cast<PointerType>(I->getType())));
@@ -2090,6 +2122,10 @@ const SmallPtrSet<Function *, 16> SCCPSolver::getMRVFunctionsTracked() {
20902122

20912123
void SCCPSolver::markOverdefined(Value *V) { Visitor->markOverdefined(V); }
20922124

2125+
void SCCPSolver::trackValueOfArgument(Argument *V) {
2126+
Visitor->trackValueOfArgument(V);
2127+
}
2128+
20932129
bool SCCPSolver::isStructLatticeConstant(Function *F, StructType *STy) {
20942130
return Visitor->isStructLatticeConstant(F, STy);
20952131
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt < %s -passes=ipsccp -S | FileCheck %s
3+
4+
declare void @use(i1)
5+
declare i32 @get_i32()
6+
7+
define void @range_attribute(i32 range(i32 0, 10) %v) {
8+
; CHECK-LABEL: @range_attribute(
9+
; CHECK-NEXT: call void @use(i1 true)
10+
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V:%.*]], 9
11+
; CHECK-NEXT: call void @use(i1 [[C2]])
12+
; CHECK-NEXT: call void @use(i1 false)
13+
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
14+
; CHECK-NEXT: call void @use(i1 [[C4]])
15+
; CHECK-NEXT: ret void
16+
;
17+
%c1 = icmp ult i32 %v, 10
18+
call void @use(i1 %c1)
19+
%c2 = icmp ult i32 %v, 9
20+
call void @use(i1 %c2)
21+
%c3 = icmp ugt i32 %v, 9
22+
call void @use(i1 %c3)
23+
%c4 = icmp ugt i32 %v, 8
24+
call void @use(i1 %c4)
25+
ret void
26+
}
27+
28+
define i32 @range_attribute_single(i32 range(i32 0, 1) %v) {
29+
; CHECK-LABEL: @range_attribute_single(
30+
; CHECK-NEXT: ret i32 0
31+
;
32+
ret i32 %v
33+
}
34+
35+
define void @call_range_attribute() {
36+
; CHECK-LABEL: @call_range_attribute(
37+
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
38+
; CHECK-NEXT: call void @use(i1 true)
39+
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
40+
; CHECK-NEXT: call void @use(i1 [[C2]])
41+
; CHECK-NEXT: call void @use(i1 false)
42+
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
43+
; CHECK-NEXT: call void @use(i1 [[C4]])
44+
; CHECK-NEXT: ret void
45+
;
46+
%v = call range(i32 0, 10) i32 @get_i32()
47+
%c1 = icmp ult i32 %v, 10
48+
call void @use(i1 %c1)
49+
%c2 = icmp ult i32 %v, 9
50+
call void @use(i1 %c2)
51+
%c3 = icmp ugt i32 %v, 9
52+
call void @use(i1 %c3)
53+
%c4 = icmp ugt i32 %v, 8
54+
call void @use(i1 %c4)
55+
ret void
56+
}
57+
58+
59+
declare range(i32 0, 10) i32 @get_i32_in_range()
60+
61+
define void @call_range_result() {
62+
; CHECK-LABEL: @call_range_result(
63+
; CHECK-NEXT: [[V:%.*]] = call i32 @get_i32_in_range()
64+
; CHECK-NEXT: call void @use(i1 true)
65+
; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[V]], 9
66+
; CHECK-NEXT: call void @use(i1 [[C2]])
67+
; CHECK-NEXT: call void @use(i1 false)
68+
; CHECK-NEXT: [[C4:%.*]] = icmp ugt i32 [[V]], 8
69+
; CHECK-NEXT: call void @use(i1 [[C4]])
70+
; CHECK-NEXT: ret void
71+
;
72+
%v = call i32 @get_i32_in_range()
73+
%c1 = icmp ult i32 %v, 10
74+
call void @use(i1 %c1)
75+
%c2 = icmp ult i32 %v, 9
76+
call void @use(i1 %c2)
77+
%c3 = icmp ugt i32 %v, 9
78+
call void @use(i1 %c3)
79+
%c4 = icmp ugt i32 %v, 8
80+
call void @use(i1 %c4)
81+
ret void
82+
}
83+
84+
define internal i1 @ip_cmp_range_attribute(i32 %v) {
85+
; CHECK-LABEL: @ip_cmp_range_attribute(
86+
; CHECK-NEXT: ret i1 undef
87+
;
88+
%c = icmp ult i32 %v, 10
89+
ret i1 %c
90+
}
91+
92+
define i1 @ip_range_attribute(i32 range(i32 0, 10) %v) {
93+
; CHECK-LABEL: @ip_range_attribute(
94+
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_attribute(i32 [[V:%.*]])
95+
; CHECK-NEXT: ret i1 true
96+
;
97+
%c = call i1 @ip_cmp_range_attribute(i32 %v)
98+
ret i1 %c
99+
}
100+
101+
define internal i1 @ip_cmp_range_call(i32 %v) {
102+
; CHECK-LABEL: @ip_cmp_range_call(
103+
; CHECK-NEXT: ret i1 undef
104+
;
105+
%c = icmp ult i32 %v, 10
106+
ret i1 %c
107+
}
108+
109+
define i1 @ip_range_call() {
110+
; CHECK-LABEL: @ip_range_call(
111+
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
112+
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_call(i32 [[V]])
113+
; CHECK-NEXT: ret i1 true
114+
;
115+
%v = call range(i32 0, 10) i32 @get_i32()
116+
%c = call i1 @ip_cmp_range_call(i32 %v)
117+
ret i1 %c
118+
}
119+
120+
define internal i1 @ip_cmp_range_result(i32 %v) {
121+
; CHECK-LABEL: @ip_cmp_range_result(
122+
; CHECK-NEXT: ret i1 undef
123+
;
124+
%c = icmp ult i32 %v, 10
125+
ret i1 %c
126+
}
127+
128+
define i1 @ip_range_result() {
129+
; CHECK-LABEL: @ip_range_result(
130+
; CHECK-NEXT: [[V:%.*]] = call range(i32 0, 10) i32 @get_i32()
131+
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_range_result(i32 [[V]])
132+
; CHECK-NEXT: ret i1 true
133+
;
134+
%v = call range(i32 0, 10) i32 @get_i32()
135+
%c = call i1 @ip_cmp_range_result(i32 %v)
136+
ret i1 %c
137+
}
138+
139+
define internal i1 @ip_cmp_with_range_attribute(i32 range(i32 0, 10) %v) {
140+
; CHECK-LABEL: @ip_cmp_with_range_attribute(
141+
; CHECK-NEXT: ret i1 undef
142+
;
143+
%c = icmp eq i32 %v, 5
144+
ret i1 %c
145+
}
146+
147+
define i1 @ip_range_attribute_constant() {
148+
; CHECK-LABEL: @ip_range_attribute_constant(
149+
; CHECK-NEXT: [[C:%.*]] = call i1 @ip_cmp_with_range_attribute(i32 5)
150+
; CHECK-NEXT: ret i1 true
151+
;
152+
%c = call i1 @ip_cmp_with_range_attribute(i32 5)
153+
ret i1 %c
154+
}

0 commit comments

Comments
 (0)