Skip to content

Commit

Permalink
[InstSimpliy] Use range attribute to simplify comparisons (#84627)
Browse files Browse the repository at this point in the history
Use the new range attribute from #84617
to simplify comparisons where both sides have range information.
  • Loading branch information
andjo403 authored Mar 12, 2024
1 parent 368db56 commit a3b5250
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 19 deletions.
5 changes: 5 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,11 @@ class AttributeList {
return getAttributeAtIndex(FunctionIndex, Kind);
}

/// Return the attribute for the given attribute kind for the return value.
Attribute getRetAttr(Attribute::AttrKind Kind) const {
return getAttributeAtIndex(ReturnIndex, Kind);
}

/// Return the alignment of the return value.
MaybeAlign getRetAlignment() const;

Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ class LLVM_EXTERNAL_VISIBILITY Function : public GlobalObject,
/// Return the attribute for the given attribute kind.
Attribute getFnAttribute(StringRef Kind) const;

/// Return the attribute for the given attribute kind for the return value.
Attribute getRetAttribute(Attribute::AttrKind Kind) const;

/// For a string attribute \p Kind, parse attribute as an integer.
///
/// \returns \p Default if attribute is not present.
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1909,6 +1909,18 @@ class CallBase : public Instruction {
/// Determine whether the return value has the given attribute.
bool hasRetAttr(StringRef Kind) const { return hasRetAttrImpl(Kind); }

/// Return the attribute for the given attribute kind for the return value.
Attribute getRetAttr(Attribute::AttrKind Kind) const {
Attribute RetAttr = Attrs.getRetAttr(Kind);
if (RetAttr.isValid())
return RetAttr;

// Look at the callee, if available.
if (const Function *F = getCalledFunction())
return F->getAttributes().getRetAttr(Kind);
return Attribute();
}

/// Determine whether the argument or parameter has the given attribute.
bool paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const;

Expand Down
38 changes: 24 additions & 14 deletions llvm/lib/Analysis/InstructionSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3729,6 +3729,26 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred,
}
}

/// Helper method to get range from metadata or attribute.
static std::optional<ConstantRange> getRange(Value *V,
const InstrInfoQuery &IIQ) {
if (Instruction *I = dyn_cast<Instruction>(V))
if (MDNode *MD = IIQ.getMetadata(I, LLVMContext::MD_range))
return getConstantRangeFromMetadata(*MD);

Attribute Range;
if (const Argument *A = dyn_cast<Argument>(V)) {
Range = A->getAttribute(llvm::Attribute::Range);
} else if (const CallBase *CB = dyn_cast<CallBase>(V)) {
Range = CB->getRetAttr(llvm::Attribute::Range);
}

if (Range.isValid())
return Range.getRange();

return std::nullopt;
}

/// Given operands for an ICmpInst, see if we can fold the result.
/// If not, this returns null.
static Value *simplifyICmpInst(unsigned Predicate, Value *LHS, Value *RHS,
Expand Down Expand Up @@ -3776,24 +3796,14 @@ static Value *simplifyICmpInst(unsigned Predicate, Value *LHS, Value *RHS,

// If both operands have range metadata, use the metadata
// to simplify the comparison.
if (isa<Instruction>(RHS) && isa<Instruction>(LHS)) {
auto RHS_Instr = cast<Instruction>(RHS);
auto LHS_Instr = cast<Instruction>(LHS);

if (Q.IIQ.getMetadata(RHS_Instr, LLVMContext::MD_range) &&
Q.IIQ.getMetadata(LHS_Instr, LLVMContext::MD_range)) {
auto RHS_CR = getConstantRangeFromMetadata(
*RHS_Instr->getMetadata(LLVMContext::MD_range));
auto LHS_CR = getConstantRangeFromMetadata(
*LHS_Instr->getMetadata(LLVMContext::MD_range));

if (LHS_CR.icmp(Pred, RHS_CR))
if (std::optional<ConstantRange> RhsCr = getRange(RHS, Q.IIQ))
if (std::optional<ConstantRange> LhsCr = getRange(LHS, Q.IIQ)) {
if (LhsCr->icmp(Pred, *RhsCr))
return ConstantInt::getTrue(ITy);

if (LHS_CR.icmp(CmpInst::getInversePredicate(Pred), RHS_CR))
if (LhsCr->icmp(CmpInst::getInversePredicate(Pred), *RhsCr))
return ConstantInt::getFalse(ITy);
}
}

// Compare of cast, for example (zext X) != 0 -> X != 0
if (isa<CastInst>(LHS) && (isa<Constant>(RHS) || isa<CastInst>(RHS))) {
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/IR/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,10 @@ Attribute Function::getFnAttribute(StringRef Kind) const {
return AttributeSets.getFnAttr(Kind);
}

Attribute Function::getRetAttribute(Attribute::AttrKind Kind) const {
return AttributeSets.getRetAttr(Kind);
}

uint64_t Function::getFnAttributeAsParsedInteger(StringRef Name,
uint64_t Default) const {
Attribute A = getFnAttribute(Name);
Expand Down
144 changes: 139 additions & 5 deletions llvm/test/Transforms/InstCombine/icmp-range.ll
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ define i1 @test_two_ranges(ptr nocapture readonly %arg1, ptr nocapture readonly
ret i1 %rval
}

; Values' ranges overlap each other, so it can not be simplified.
define i1 @test_two_attribute_ranges(i32 range(i32 5, 10) %arg1, i32 range(i32 8, 16) %arg2) {
; CHECK-LABEL: @test_two_attribute_ranges(
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult i32 [[ARG1:%.*]], [[ARG2:%.*]]
; CHECK-NEXT: ret i1 [[RVAL]]
;
%rval = icmp ult i32 %arg2, %arg1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_two_ranges2(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
; CHECK-LABEL: @test_two_ranges2(
Expand All @@ -160,6 +170,35 @@ define i1 @test_two_ranges2(ptr nocapture readonly %arg1, ptr nocapture readonly
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_two_argument_ranges(i32 range(i32 1, 6) %arg1, i32 range(i32 8, 16) %arg2) {
; CHECK-LABEL: @test_two_argument_ranges(
; CHECK-NEXT: ret i1 false
;
%rval = icmp ult i32 %arg2, %arg1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_one_range_and_one_argument_range(ptr nocapture readonly %arg1, i32 range(i32 8, 16) %arg2) {
; CHECK-LABEL: @test_one_range_and_one_argument_range(
; CHECK-NEXT: ret i1 false
;
%val1 = load i32, ptr %arg1, !range !0
%rval = icmp ult i32 %arg2, %val1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_one_argument_range_and_one_range(i32 range(i32 1, 6) %arg1, ptr nocapture readonly %arg2) {
; CHECK-LABEL: @test_one_argument_range_and_one_range(
; CHECK-NEXT: ret i1 false
;
%val1 = load i32, ptr %arg2, !range !6
%rval = icmp ult i32 %val1, %arg1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to true.
define i1 @test_two_ranges3(ptr nocapture readonly %arg1, ptr nocapture readonly %arg2) {
; CHECK-LABEL: @test_two_ranges3(
Expand All @@ -186,8 +225,8 @@ define <2 x i1> @test_two_ranges_vec(ptr nocapture readonly %arg1, ptr nocapture
}

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

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

; Values' ranges overlap each other, so it can not be simplified.
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) {
; CHECK-LABEL: @test_two_argument_ranges_vec(
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult <2 x i32> [[VAL2:%.*]], [[VAL1:%.*]]
; CHECK-NEXT: ret <2 x i1> [[RVAL]]
;
%rval = icmp ult <2 x i32> %arg2, %arg1
ret <2 x i1> %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
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) {
; CHECK-LABEL: @test_two_argument_ranges_vec_false(
; CHECK-NEXT: ret <2 x i1> zeroinitializer
;
%rval = icmp ult <2 x i32> %arg2, %arg1
ret <2 x i1> %rval
}

; Values' ranges do not overlap each other, so it can simplified to true.
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) {
; CHECK-LABEL: @test_two_argument_ranges_vec_true(
; CHECK-NEXT: ret <2 x i1> <i1 true, i1 true>
;
%rval = icmp ugt <2 x i32> %arg2, %arg1
ret <2 x i1> %rval
}

declare i32 @create_range1()
declare range(i32 8, 16) i32 @create_range2()
declare range(i32 1, 6) i32 @create_range3()

; Values' ranges overlap each other, so it can not be simplified.
define i1 @test_two_return_attribute_ranges_not_simplified() {
; CHECK-LABEL: @test_two_return_attribute_ranges_not_simplified(
; CHECK-NEXT: [[ARG2:%.*]] = call range(i32 5, 10) i32 @create_range1()
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
; CHECK-NEXT: [[RVAL:%.*]] = icmp ult i32 [[ARG1]], [[ARG2]]
; CHECK-NEXT: ret i1 [[RVAL]]
;
%val1 = call range(i32 5, 10) i32 @create_range1()
%val2 = call i32 @create_range2()
%rval = icmp ult i32 %val2, %val1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_two_return_attribute_ranges_one_in_call() {
; CHECK-LABEL: @test_two_return_attribute_ranges_one_in_call(
; CHECK-NEXT: [[VAL1:%.*]] = call range(i32 1, 6) i32 @create_range1()
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
; CHECK-NEXT: ret i1 false
;
%val1 = call range(i32 1, 6) i32 @create_range1()
%val2 = call i32 @create_range2()
%rval = icmp ult i32 %val2, %val1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_two_return_attribute_ranges() {
; CHECK-LABEL: @test_two_return_attribute_ranges(
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
; CHECK-NEXT: [[ARG1:%.*]] = call i32 @create_range2()
; CHECK-NEXT: ret i1 false
;
%val1 = call i32 @create_range3()
%val2 = call i32 @create_range2()
%rval = icmp ult i32 %val2, %val1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_one_return_argument_and_one_argument_range(i32 range(i32 8, 16) %arg1) {
; CHECK-LABEL: @test_one_return_argument_and_one_argument_range(
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
; CHECK-NEXT: ret i1 false
;
%val1 = call i32 @create_range3()
%rval = icmp ult i32 %arg1, %val1
ret i1 %rval
}

; Values' ranges do not overlap each other, so it can simplified to false.
define i1 @test_one_range_and_one_return_argument(ptr nocapture readonly %arg1) {
; CHECK-LABEL: @test_one_range_and_one_return_argument(
; CHECK-NEXT: [[VAL1:%.*]] = call i32 @create_range3()
; CHECK-NEXT: ret i1 false
;
%val1 = call i32 @create_range3()
%val2 = load i32, ptr %arg1, !range !6
%rval = icmp ult i32 %val2, %val1
ret i1 %rval
}

define i1 @ugt_zext(i1 %b, i8 %x) {
; CHECK-LABEL: @ugt_zext(
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[X:%.*]], 0
Expand Down

0 comments on commit a3b5250

Please sign in to comment.