Skip to content

Commit a3b5250

Browse files
authored
[InstSimpliy] Use range attribute to simplify comparisons (#84627)
Use the new range attribute from #84617 to simplify comparisons where both sides have range information.
1 parent 368db56 commit a3b5250

File tree

6 files changed

+187
-19
lines changed

6 files changed

+187
-19
lines changed

llvm/include/llvm/IR/Attributes.h

+5
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,11 @@ class AttributeList {
848848
return getAttributeAtIndex(FunctionIndex, Kind);
849849
}
850850

851+
/// Return the attribute for the given attribute kind for the return value.
852+
Attribute getRetAttr(Attribute::AttrKind Kind) const {
853+
return getAttributeAtIndex(ReturnIndex, Kind);
854+
}
855+
851856
/// Return the alignment of the return value.
852857
MaybeAlign getRetAlignment() const;
853858

llvm/include/llvm/IR/Function.h

+3
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,9 @@ class LLVM_EXTERNAL_VISIBILITY Function : public GlobalObject,
430430
/// Return the attribute for the given attribute kind.
431431
Attribute getFnAttribute(StringRef Kind) const;
432432

433+
/// Return the attribute for the given attribute kind for the return value.
434+
Attribute getRetAttribute(Attribute::AttrKind Kind) const;
435+
433436
/// For a string attribute \p Kind, parse attribute as an integer.
434437
///
435438
/// \returns \p Default if attribute is not present.

llvm/include/llvm/IR/InstrTypes.h

+12
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,18 @@ class CallBase : public Instruction {
19091909
/// Determine whether the return value has the given attribute.
19101910
bool hasRetAttr(StringRef Kind) const { return hasRetAttrImpl(Kind); }
19111911

1912+
/// Return the attribute for the given attribute kind for the return value.
1913+
Attribute getRetAttr(Attribute::AttrKind Kind) const {
1914+
Attribute RetAttr = Attrs.getRetAttr(Kind);
1915+
if (RetAttr.isValid())
1916+
return RetAttr;
1917+
1918+
// Look at the callee, if available.
1919+
if (const Function *F = getCalledFunction())
1920+
return F->getAttributes().getRetAttr(Kind);
1921+
return Attribute();
1922+
}
1923+
19121924
/// Determine whether the argument or parameter has the given attribute.
19131925
bool paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const;
19141926

llvm/lib/Analysis/InstructionSimplify.cpp

+24-14
Original file line numberDiff line numberDiff line change
@@ -3729,6 +3729,26 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
37293729
}
37303730
}
37313731

3732+
/// Helper method to get range from metadata or attribute.
3733+
static std::optional<ConstantRange> getRange(Value *V,
3734+
const InstrInfoQuery &IIQ) {
3735+
if (Instruction *I = dyn_cast<Instruction>(V))
3736+
if (MDNode *MD = IIQ.getMetadata(I, LLVMContext::MD_range))
3737+
return getConstantRangeFromMetadata(*MD);
3738+
3739+
Attribute Range;
3740+
if (const Argument *A = dyn_cast<Argument>(V)) {
3741+
Range = A->getAttribute(llvm::Attribute::Range);
3742+
} else if (const CallBase *CB = dyn_cast<CallBase>(V)) {
3743+
Range = CB->getRetAttr(llvm::Attribute::Range);
3744+
}
3745+
3746+
if (Range.isValid())
3747+
return Range.getRange();
3748+
3749+
return std::nullopt;
3750+
}
3751+
37323752
/// Given operands for an ICmpInst, see if we can fold the result.
37333753
/// If not, this returns null.
37343754
static Value *simplifyICmpInst(unsigned Predicate, Value *LHS, Value *RHS,
@@ -3776,24 +3796,14 @@ static Value *simplifyICmpInst(unsigned Predicate, Value *LHS, Value *RHS,
37763796

37773797
// If both operands have range metadata, use the metadata
37783798
// to simplify the comparison.
3779-
if (isa<Instruction>(RHS) && isa<Instruction>(LHS)) {
3780-
auto RHS_Instr = cast<Instruction>(RHS);
3781-
auto LHS_Instr = cast<Instruction>(LHS);
3782-
3783-
if (Q.IIQ.getMetadata(RHS_Instr, LLVMContext::MD_range) &&
3784-
Q.IIQ.getMetadata(LHS_Instr, LLVMContext::MD_range)) {
3785-
auto RHS_CR = getConstantRangeFromMetadata(
3786-
*RHS_Instr->getMetadata(LLVMContext::MD_range));
3787-
auto LHS_CR = getConstantRangeFromMetadata(
3788-
*LHS_Instr->getMetadata(LLVMContext::MD_range));
3789-
3790-
if (LHS_CR.icmp(Pred, RHS_CR))
3799+
if (std::optional<ConstantRange> RhsCr = getRange(RHS, Q.IIQ))
3800+
if (std::optional<ConstantRange> LhsCr = getRange(LHS, Q.IIQ)) {
3801+
if (LhsCr->icmp(Pred, *RhsCr))
37913802
return ConstantInt::getTrue(ITy);
37923803

3793-
if (LHS_CR.icmp(CmpInst::getInversePredicate(Pred), RHS_CR))
3804+
if (LhsCr->icmp(CmpInst::getInversePredicate(Pred), *RhsCr))
37943805
return ConstantInt::getFalse(ITy);
37953806
}
3796-
}
37973807

37983808
// Compare of cast, for example (zext X) != 0 -> X != 0
37993809
if (isa<CastInst>(LHS) && (isa<Constant>(RHS) || isa<CastInst>(RHS))) {

llvm/lib/IR/Function.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,10 @@ Attribute Function::getFnAttribute(StringRef Kind) const {
700700
return AttributeSets.getFnAttr(Kind);
701701
}
702702

703+
Attribute Function::getRetAttribute(Attribute::AttrKind Kind) const {
704+
return AttributeSets.getRetAttr(Kind);
705+
}
706+
703707
uint64_t Function::getFnAttributeAsParsedInteger(StringRef Name,
704708
uint64_t Default) const {
705709
Attribute A = getFnAttribute(Name);

llvm/test/Transforms/InstCombine/icmp-range.ll

+139-5
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ define i1 @test_two_ranges(ptr nocapture readonly %arg1, ptr nocapture readonly
149149
ret i1 %rval
150150
}
151151

152+
; Values' ranges overlap each other, so it can not be simplified.
153+
define i1 @test_two_attribute_ranges(i32 range(i32 5, 10) %arg1, i32 range(i32 8, 16) %arg2) {
154+
; CHECK-LABEL: @test_two_attribute_ranges(
155+
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult i32 [[ARG1:%.*]], [[ARG2:%.*]]
156+
; CHECK-NEXT: ret i1 [[RVAL]]
157+
;
158+
%rval = icmp ult i32 %arg2, %arg1
159+
ret i1 %rval
160+
}
161+
152162
; Values' ranges do not overlap each other, so it can simplified to false.
153163
define i1 @test_two_ranges2(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
154164
; CHECK-LABEL: @test_two_ranges2(
@@ -160,6 +170,35 @@ define i1 @test_two_ranges2(ptr nocapture readonly %arg1, ptr nocapture readonly
160170
ret i1 %rval
161171
}
162172

173+
; Values' ranges do not overlap each other, so it can simplified to false.
174+
define i1 @test_two_argument_ranges(i32 range(i32 1, 6) %arg1, i32 range(i32 8, 16) %arg2) {
175+
; CHECK-LABEL: @test_two_argument_ranges(
176+
; CHECK-NEXT: ret i1 false
177+
;
178+
%rval = icmp ult i32 %arg2, %arg1
179+
ret i1 %rval
180+
}
181+
182+
; Values' ranges do not overlap each other, so it can simplified to false.
183+
define i1 @test_one_range_and_one_argument_range(ptr nocapture readonly %arg1, i32 range(i32 8, 16) %arg2) {
184+
; CHECK-LABEL: @test_one_range_and_one_argument_range(
185+
; CHECK-NEXT: ret i1 false
186+
;
187+
%val1 = load i32, ptr %arg1, !range !0
188+
%rval = icmp ult i32 %arg2, %val1
189+
ret i1 %rval
190+
}
191+
192+
; Values' ranges do not overlap each other, so it can simplified to false.
193+
define i1 @test_one_argument_range_and_one_range(i32 range(i32 1, 6) %arg1, ptr nocapture readonly %arg2) {
194+
; CHECK-LABEL: @test_one_argument_range_and_one_range(
195+
; CHECK-NEXT: ret i1 false
196+
;
197+
%val1 = load i32, ptr %arg2, !range !6
198+
%rval = icmp ult i32 %val1, %arg1
199+
ret i1 %rval
200+
}
201+
163202
; Values' ranges do not overlap each other, so it can simplified to true.
164203
define i1 @test_two_ranges3(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
165204
; CHECK-LABEL: @test_two_ranges3(
@@ -186,8 +225,8 @@ define <2 x i1> @test_two_ranges_vec(ptr nocapture readonly %arg1, ptr nocapture
186225
}
187226

188227
; Values' ranges do not overlap each other, so it can simplified to false.
189-
define <2 x i1> @test_two_ranges_vec_true(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
190-
; CHECK-LABEL: @test_two_ranges_vec_true(
228+
define <2 x i1> @test_two_ranges_vec_false(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
229+
; CHECK-LABEL: @test_two_ranges_vec_false(
191230
; CHECK-NEXT: ret <2 x i1> zeroinitializer
192231
;
193232
%val1 = load <2 x i32>, ptr %arg1, !range !0
@@ -196,9 +235,9 @@ define <2 x i1> @test_two_ranges_vec_true(ptr nocapture readonly %arg1, ptr noca
196235
ret <2 x i1> %rval
197236
}
198237

199-
; Values' ranges do not overlap each other, so it can simplified to false.
200-
define <2 x i1> @test_two_ranges_vec_false(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
201-
; CHECK-LABEL: @test_two_ranges_vec_false(
238+
; Values' ranges do not overlap each other, so it can simplified to true.
239+
define <2 x i1> @test_two_ranges_vec_true(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
240+
; CHECK-LABEL: @test_two_ranges_vec_true(
202241
; CHECK-NEXT: ret <2 x i1> <i1 true, i1 true>
203242
;
204243
%val1 = load <2 x i32>, ptr %arg1, !range !0
@@ -207,6 +246,101 @@ define <2 x i1> @test_two_ranges_vec_false(ptr nocapture readonly %arg1, ptr noc
207246
ret <2 x i1> %rval
208247
}
209248

249+
; Values' ranges overlap each other, so it can not be simplified.
250+
define <2 x i1> @test_two_argument_ranges_vec(<2 x i32> range(i32 5, 10) %arg1, <2 x i32> range(i32 8, 16) %arg2) {
251+
; CHECK-LABEL: @test_two_argument_ranges_vec(
252+
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult <2 x i32> [[VAL2:%.*]], [[VAL1:%.*]]
253+
; CHECK-NEXT: ret <2 x i1> [[RVAL]]
254+
;
255+
%rval = icmp ult <2 x i32> %arg2, %arg1
256+
ret <2 x i1> %rval
257+
}
258+
259+
; Values' ranges do not overlap each other, so it can simplified to false.
260+
define <2 x i1> @test_two_argument_ranges_vec_false(<2 x i32> range(i32 1, 6) %arg1, <2 x i32> range(i32 8, 16) %arg2) {
261+
; CHECK-LABEL: @test_two_argument_ranges_vec_false(
262+
; CHECK-NEXT: ret <2 x i1> zeroinitializer
263+
;
264+
%rval = icmp ult <2 x i32> %arg2, %arg1
265+
ret <2 x i1> %rval
266+
}
267+
268+
; Values' ranges do not overlap each other, so it can simplified to true.
269+
define <2 x i1> @test_two_argument_ranges_vec_true(<2 x i32> range(i32 1, 6) %arg1, <2 x i32> range(i32 8, 16) %arg2) {
270+
; CHECK-LABEL: @test_two_argument_ranges_vec_true(
271+
; CHECK-NEXT: ret <2 x i1> <i1 true, i1 true>
272+
;
273+
%rval = icmp ugt <2 x i32> %arg2, %arg1
274+
ret <2 x i1> %rval
275+
}
276+
277+
declare i32 @create_range1()
278+
declare range(i32 8, 16) i32 @create_range2()
279+
declare range(i32 1, 6) i32 @create_range3()
280+
281+
; Values' ranges overlap each other, so it can not be simplified.
282+
define i1 @test_two_return_attribute_ranges_not_simplified() {
283+
; CHECK-LABEL: @test_two_return_attribute_ranges_not_simplified(
284+
; CHECK-NEXT: [[ARG2:%.*]] = call range(i32 5, 10) i32 @create_range1()
285+
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
286+
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult i32 [[ARG1]], [[ARG2]]
287+
; CHECK-NEXT: ret i1 [[RVAL]]
288+
;
289+
%val1 = call range(i32 5, 10) i32 @create_range1()
290+
%val2 = call i32 @create_range2()
291+
%rval = icmp ult i32 %val2, %val1
292+
ret i1 %rval
293+
}
294+
295+
; Values' ranges do not overlap each other, so it can simplified to false.
296+
define i1 @test_two_return_attribute_ranges_one_in_call() {
297+
; CHECK-LABEL: @test_two_return_attribute_ranges_one_in_call(
298+
; CHECK-NEXT: [[VAL1:%.*]] = call range(i32 1, 6) i32 @create_range1()
299+
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
300+
; CHECK-NEXT: ret i1 false
301+
;
302+
%val1 = call range(i32 1, 6) i32 @create_range1()
303+
%val2 = call i32 @create_range2()
304+
%rval = icmp ult i32 %val2, %val1
305+
ret i1 %rval
306+
}
307+
308+
; Values' ranges do not overlap each other, so it can simplified to false.
309+
define i1 @test_two_return_attribute_ranges() {
310+
; CHECK-LABEL: @test_two_return_attribute_ranges(
311+
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
312+
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
313+
; CHECK-NEXT: ret i1 false
314+
;
315+
%val1 = call i32 @create_range3()
316+
%val2 = call i32 @create_range2()
317+
%rval = icmp ult i32 %val2, %val1
318+
ret i1 %rval
319+
}
320+
321+
; Values' ranges do not overlap each other, so it can simplified to false.
322+
define i1 @test_one_return_argument_and_one_argument_range(i32 range(i32 8, 16) %arg1) {
323+
; CHECK-LABEL: @test_one_return_argument_and_one_argument_range(
324+
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
325+
; CHECK-NEXT: ret i1 false
326+
;
327+
%val1 = call i32 @create_range3()
328+
%rval = icmp ult i32 %arg1, %val1
329+
ret i1 %rval
330+
}
331+
332+
; Values' ranges do not overlap each other, so it can simplified to false.
333+
define i1 @test_one_range_and_one_return_argument(ptr nocapture readonly %arg1) {
334+
; CHECK-LABEL: @test_one_range_and_one_return_argument(
335+
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
336+
; CHECK-NEXT: ret i1 false
337+
;
338+
%val1 = call i32 @create_range3()
339+
%val2 = load i32, ptr %arg1, !range !6
340+
%rval = icmp ult i32 %val2, %val1
341+
ret i1 %rval
342+
}
343+
210344
define i1 @ugt_zext(i1 %b, i8 %x) {
211345
; CHECK-LABEL: @ugt_zext(
212346
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[X:%.*]], 0

0 commit comments

Comments
 (0)