diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 0c2a02514ba0e..7dd8a329029a3 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -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; diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index cb87a44980321..d96d506a9b05d 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -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. diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 0e81d3b391a08..fed21b992e3d1 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -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; diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 8c48174b9f525..ce651783caf16 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -3729,6 +3729,26 @@ static Value *simplifyICmpWithIntrinsicOnLHS(CmpInst::Predicate Pred, } } +/// Helper method to get range from metadata or attribute. +static std::optional getRange(Value *V, + const InstrInfoQuery &IIQ) { + if (Instruction *I = dyn_cast(V)) + if (MDNode *MD = IIQ.getMetadata(I, LLVMContext::MD_range)) + return getConstantRangeFromMetadata(*MD); + + Attribute Range; + if (const Argument *A = dyn_cast(V)) { + Range = A->getAttribute(llvm::Attribute::Range); + } else if (const CallBase *CB = dyn_cast(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, @@ -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(RHS) && isa(LHS)) { - auto RHS_Instr = cast(RHS); - auto LHS_Instr = cast(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 RhsCr = getRange(RHS, Q.IIQ)) + if (std::optional 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(LHS) && (isa(RHS) || isa(RHS))) { diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 056e4f31981a7..d22e1c1231118 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -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); diff --git a/llvm/test/Transforms/InstCombine/icmp-range.ll b/llvm/test/Transforms/InstCombine/icmp-range.ll index 77bb5fdb6bfd4..9ed2f2a4860c6 100644 --- a/llvm/test/Transforms/InstCombine/icmp-range.ll +++ b/llvm/test/Transforms/InstCombine/icmp-range.ll @@ -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( @@ -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( @@ -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 @@ -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> ; %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 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> +; + %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