Skip to content

Commit eb44dd0

Browse files
haopliuAlexisPerry
authored andcommitted
Add ConstantRangeList::unionWith() and ::intersectWith() (llvm#96547)
Add ConstantRangeList::unionWith() and ::intersectWith(). These methods will be used in the "initializes" attribute inference. llvm@df11106
1 parent 1669921 commit eb44dd0

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

llvm/include/llvm/IR/ConstantRangeList.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ class [[nodiscard]] ConstantRangeList {
7272
APInt(64, Upper, /*isSigned=*/true)));
7373
}
7474

75+
/// Return the range list that results from the union of this
76+
/// ConstantRangeList with another ConstantRangeList, "CRL".
77+
ConstantRangeList unionWith(const ConstantRangeList &CRL) const;
78+
79+
/// Return the range list that results from the intersection of this
80+
/// ConstantRangeList with another ConstantRangeList, "CRL".
81+
ConstantRangeList intersectWith(const ConstantRangeList &CRL) const;
82+
7583
/// Return true if this range list is equal to another range list.
7684
bool operator==(const ConstantRangeList &CRL) const {
7785
return Ranges == CRL.Ranges;

llvm/lib/IR/ConstantRangeList.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,95 @@ void ConstantRangeList::insert(const ConstantRange &NewRange) {
8181
}
8282
}
8383

84+
ConstantRangeList
85+
ConstantRangeList::unionWith(const ConstantRangeList &CRL) const {
86+
assert(getBitWidth() == CRL.getBitWidth() &&
87+
"ConstantRangeList bitwidths don't agree!");
88+
// Handle common cases.
89+
if (empty())
90+
return CRL;
91+
if (CRL.empty())
92+
return *this;
93+
94+
ConstantRangeList Result;
95+
size_t i = 0, j = 0;
96+
// "PreviousRange" tracks the lowest unioned range that is being processed.
97+
// Its lower is fixed and the upper may be updated over iterations.
98+
ConstantRange PreviousRange(getBitWidth(), false);
99+
if (Ranges[i].getLower().slt(CRL.Ranges[j].getLower())) {
100+
PreviousRange = Ranges[i++];
101+
} else {
102+
PreviousRange = CRL.Ranges[j++];
103+
}
104+
105+
// Try to union "PreviousRange" and "CR". If they are disjoint, push
106+
// "PreviousRange" to the result and assign it to "CR", a new union range.
107+
// Otherwise, update the upper of "PreviousRange" to cover "CR". Note that,
108+
// the lower of "PreviousRange" is always less or equal the lower of "CR".
109+
auto UnionAndUpdateRange = [&PreviousRange,
110+
&Result](const ConstantRange &CR) {
111+
if (PreviousRange.getUpper().slt(CR.getLower())) {
112+
Result.Ranges.push_back(PreviousRange);
113+
PreviousRange = CR;
114+
} else {
115+
PreviousRange = ConstantRange(
116+
PreviousRange.getLower(),
117+
APIntOps::smax(PreviousRange.getUpper(), CR.getUpper()));
118+
}
119+
};
120+
while (i < size() || j < CRL.size()) {
121+
if (j == CRL.size() ||
122+
(i < size() && Ranges[i].getLower().slt(CRL.Ranges[j].getLower()))) {
123+
// Merge PreviousRange with this.
124+
UnionAndUpdateRange(Ranges[i++]);
125+
} else {
126+
// Merge PreviousRange with CRL.
127+
UnionAndUpdateRange(CRL.Ranges[j++]);
128+
}
129+
}
130+
Result.Ranges.push_back(PreviousRange);
131+
return Result;
132+
}
133+
134+
ConstantRangeList
135+
ConstantRangeList::intersectWith(const ConstantRangeList &CRL) const {
136+
assert(getBitWidth() == CRL.getBitWidth() &&
137+
"ConstantRangeList bitwidths don't agree!");
138+
139+
// Handle common cases.
140+
if (empty())
141+
return *this;
142+
if (CRL.empty())
143+
return CRL;
144+
145+
ConstantRangeList Result;
146+
size_t i = 0, j = 0;
147+
while (i < size() && j < CRL.size()) {
148+
auto &Range = this->Ranges[i];
149+
auto &OtherRange = CRL.Ranges[j];
150+
151+
// The intersection of two Ranges is (max(lowers), min(uppers)), and it's
152+
// possible that max(lowers) > min(uppers) if they don't have intersection.
153+
// Add the intersection to result only if it's non-empty.
154+
// To keep simple, we don't call ConstantRange::intersectWith() as it
155+
// considers the complex upper wrapped case and may result two ranges,
156+
// like (2, 8) && (6, 4) = {(2, 4), (6, 8)}.
157+
APInt Start = APIntOps::smax(Range.getLower(), OtherRange.getLower());
158+
APInt End = APIntOps::smin(Range.getUpper(), OtherRange.getUpper());
159+
if (Start.slt(End))
160+
Result.Ranges.push_back(ConstantRange(Start, End));
161+
162+
// Move to the next Range in one list determined by the uppers.
163+
// For example: A = {(0, 2), (4, 8)}; B = {(-2, 5), (6, 10)}
164+
// We need to intersect three pairs: A0 && B0; A1 && B0; A1 && B1.
165+
if (Range.getUpper().slt(OtherRange.getUpper()))
166+
i++;
167+
else
168+
j++;
169+
}
170+
return Result;
171+
}
172+
84173
void ConstantRangeList::print(raw_ostream &OS) const {
85174
interleaveComma(Ranges, OS, [&](ConstantRange CR) {
86175
OS << "(" << CR.getLower() << ", " << CR.getUpper() << ")";

llvm/unittests/IR/ConstantRangeListTest.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,116 @@ TEST_F(ConstantRangeListTest, Insert) {
9494
EXPECT_TRUE(CRL == Expected);
9595
}
9696

97+
ConstantRangeList GetCRL(ArrayRef<std::pair<APInt, APInt>> Pairs) {
98+
SmallVector<ConstantRange, 2> Ranges;
99+
for (auto &[Start, End] : Pairs)
100+
Ranges.push_back(ConstantRange(Start, End));
101+
return ConstantRangeList(Ranges);
102+
}
103+
104+
TEST_F(ConstantRangeListTest, Union) {
105+
APInt APN4 = APInt(64, -4, /*isSigned=*/true);
106+
APInt APN2 = APInt(64, -2, /*isSigned=*/true);
107+
APInt AP0 = APInt(64, 0, /*isSigned=*/true);
108+
APInt AP2 = APInt(64, 2, /*isSigned=*/true);
109+
APInt AP4 = APInt(64, 4, /*isSigned=*/true);
110+
APInt AP6 = APInt(64, 6, /*isSigned=*/true);
111+
APInt AP7 = APInt(64, 7, /*isSigned=*/true);
112+
APInt AP8 = APInt(64, 8, /*isSigned=*/true);
113+
APInt AP10 = APInt(64, 10, /*isSigned=*/true);
114+
APInt AP11 = APInt(64, 11, /*isSigned=*/true);
115+
APInt AP12 = APInt(64, 12, /*isSigned=*/true);
116+
APInt AP16 = APInt(64, 16, /*isSigned=*/true);
117+
APInt AP18 = APInt(64, 18, /*isSigned=*/true);
118+
ConstantRangeList CRL = GetCRL({{AP0, AP4}, {AP8, AP12}});
119+
120+
// Union with a subset.
121+
ConstantRangeList Empty;
122+
EXPECT_EQ(CRL.unionWith(Empty), CRL);
123+
EXPECT_EQ(Empty.unionWith(CRL), CRL);
124+
125+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP0, AP2}})), CRL);
126+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP10, AP12}})), CRL);
127+
128+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP0, AP2}, {AP8, AP10}})), CRL);
129+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP0, AP2}, {AP10, AP12}})), CRL);
130+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP2, AP4}, {AP8, AP10}})), CRL);
131+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP2, AP4}, {AP10, AP12}})), CRL);
132+
133+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP0, AP4}, {AP8, AP10}, {AP11, AP12}})),
134+
CRL);
135+
136+
EXPECT_EQ(CRL.unionWith(CRL), CRL);
137+
138+
// Union with new ranges.
139+
EXPECT_EQ(CRL.unionWith(GetCRL({{APN4, APN2}})),
140+
GetCRL({{APN4, APN2}, {AP0, AP4}, {AP8, AP12}}));
141+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP6, AP7}})),
142+
GetCRL({{AP0, AP4}, {AP6, AP7}, {AP8, AP12}}));
143+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP16, AP18}})),
144+
GetCRL({{AP0, AP4}, {AP8, AP12}, {AP16, AP18}}));
145+
146+
EXPECT_EQ(CRL.unionWith(GetCRL({{APN2, AP2}})),
147+
GetCRL({{APN2, AP4}, {AP8, AP12}}));
148+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP2, AP6}})),
149+
GetCRL({{AP0, AP6}, {AP8, AP12}}));
150+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP10, AP16}})),
151+
GetCRL({{AP0, AP4}, {AP8, AP16}}));
152+
153+
EXPECT_EQ(CRL.unionWith(GetCRL({{APN2, AP10}})), GetCRL({{APN2, AP12}}));
154+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP2, AP10}})), GetCRL({{AP0, AP12}}));
155+
EXPECT_EQ(CRL.unionWith(GetCRL({{AP4, AP16}})), GetCRL({{AP0, AP16}}));
156+
EXPECT_EQ(CRL.unionWith(GetCRL({{APN2, AP16}})), GetCRL({{APN2, AP16}}));
157+
}
158+
159+
TEST_F(ConstantRangeListTest, Intersect) {
160+
APInt APN2 = APInt(64, -2, /*isSigned=*/true);
161+
APInt AP0 = APInt(64, 0, /*isSigned=*/true);
162+
APInt AP2 = APInt(64, 2, /*isSigned=*/true);
163+
APInt AP4 = APInt(64, 4, /*isSigned=*/true);
164+
APInt AP6 = APInt(64, 6, /*isSigned=*/true);
165+
APInt AP7 = APInt(64, 7, /*isSigned=*/true);
166+
APInt AP8 = APInt(64, 8, /*isSigned=*/true);
167+
APInt AP10 = APInt(64, 10, /*isSigned=*/true);
168+
APInt AP11 = APInt(64, 11, /*isSigned=*/true);
169+
APInt AP12 = APInt(64, 12, /*isSigned=*/true);
170+
APInt AP16 = APInt(64, 16, /*isSigned=*/true);
171+
ConstantRangeList CRL = GetCRL({{AP0, AP4}, {AP8, AP12}});
172+
173+
// No intersection.
174+
ConstantRangeList Empty;
175+
EXPECT_EQ(CRL.intersectWith(Empty), Empty);
176+
EXPECT_EQ(Empty.intersectWith(CRL), Empty);
177+
178+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP0}})), Empty);
179+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP6, AP8}})), Empty);
180+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP12, AP16}})), Empty);
181+
182+
// Single intersect range.
183+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP2}})), GetCRL({{AP0, AP2}}));
184+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP6}})), GetCRL({{AP0, AP4}}));
185+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP2, AP4}})), GetCRL({{AP2, AP4}}));
186+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP2, AP6}})), GetCRL({{AP2, AP4}}));
187+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP6, AP10}})), GetCRL({{AP8, AP10}}));
188+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP6, AP16}})), GetCRL({{AP8, AP12}}));
189+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP10, AP12}})), GetCRL({{AP10, AP12}}));
190+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP10, AP16}})), GetCRL({{AP10, AP12}}));
191+
192+
// Multiple intersect ranges.
193+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP10}})),
194+
GetCRL({{AP0, AP4}, {AP8, AP10}}));
195+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP16}})), CRL);
196+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP2, AP10}})),
197+
GetCRL({{AP2, AP4}, {AP8, AP10}}));
198+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP2, AP16}})),
199+
GetCRL({{AP2, AP4}, {AP8, AP12}}));
200+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP2}, {AP6, AP10}})),
201+
GetCRL({{AP0, AP2}, {AP8, AP10}}));
202+
EXPECT_EQ(CRL.intersectWith(GetCRL({{AP2, AP6}, {AP10, AP16}})),
203+
GetCRL({{AP2, AP4}, {AP10, AP12}}));
204+
EXPECT_EQ(CRL.intersectWith(GetCRL({{APN2, AP2}, {AP7, AP10}, {AP11, AP16}})),
205+
GetCRL({{AP0, AP2}, {AP8, AP10}, {AP11, AP12}}));
206+
EXPECT_EQ(CRL.intersectWith(CRL), CRL);
207+
}
208+
97209
} // anonymous namespace

0 commit comments

Comments
 (0)