From cc4185b907e70a2065204482104eb60d771a5b1a Mon Sep 17 00:00:00 2001 From: Andreas Jonson Date: Sat, 14 Sep 2024 12:51:56 +0200 Subject: [PATCH] [Attributor] Swap range metadata to attribute for calls. --- llvm/lib/Transforms/IPO/Attributor.cpp | 7 ++ .../Transforms/IPO/AttributorAttributes.cpp | 81 ++++++++++++------- .../Attributor/IPConstantProp/PR16052.ll | 8 +- llvm/test/Transforms/Attributor/range.ll | 45 +++++++++-- .../Transforms/Attributor/value-simplify.ll | 4 +- 5 files changed, 100 insertions(+), 45 deletions(-) diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index a2548258ddaf0..448e1b4982b74 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -990,6 +990,13 @@ static bool addIfNotExistent(LLVMContext &Ctx, const Attribute &Attr, AB.addAttribute(Attr); return true; } + if (Attr.isConstantRangeAttribute()) { + Attribute::AttrKind Kind = Attr.getKindAsEnum(); + if (!ForceReplace && AttrSet.hasAttribute(Kind)) + return false; + AB.addAttribute(Attr); + return true; + } llvm_unreachable("Expected enum or string attribute!"); } diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index 22dde04745bec..79a7a2a95d1d2 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -9172,44 +9172,58 @@ struct AAValueConstantRangeImpl : AAValueConstantRange { return MDNode::get(Ctx, LowAndHigh); } - /// Return true if \p Assumed is included in \p KnownRanges. - static bool isBetterRange(const ConstantRange &Assumed, MDNode *KnownRanges) { - + /// Return true if \p Assumed is included in ranges from instruction \p I. + static bool isBetterRange(const ConstantRange &Assumed, + const Instruction &I) { if (Assumed.isFullSet()) return false; - if (!KnownRanges) - return true; - - // If multiple ranges are annotated in IR, we give up to annotate assumed - // range for now. + std::optional Known; - // TODO: If there exists a known range which containts assumed range, we - // can say assumed range is better. - if (KnownRanges->getNumOperands() > 2) - return false; + if (const auto *CB = dyn_cast(&I)) { + Known = CB->getRange(); + } else if (MDNode *KnownRanges = I.getMetadata(LLVMContext::MD_range)) { + // If multiple ranges are annotated in IR, we give up to annotate assumed + // range for now. + + // TODO: If there exists a known range which containts assumed range, we + // can say assumed range is better. + if (KnownRanges->getNumOperands() > 2) + return false; - ConstantInt *Lower = - mdconst::extract(KnownRanges->getOperand(0)); - ConstantInt *Upper = - mdconst::extract(KnownRanges->getOperand(1)); + ConstantInt *Lower = + mdconst::extract(KnownRanges->getOperand(0)); + ConstantInt *Upper = + mdconst::extract(KnownRanges->getOperand(1)); - ConstantRange Known(Lower->getValue(), Upper->getValue()); - return Known.contains(Assumed) && Known != Assumed; + Known.emplace(Lower->getValue(), Upper->getValue()); + } + return !Known || (*Known != Assumed && Known->contains(Assumed)); } /// Helper function to set range metadata. static bool setRangeMetadataIfisBetterRange(Instruction *I, const ConstantRange &AssumedConstantRange) { - auto *OldRangeMD = I->getMetadata(LLVMContext::MD_range); - if (isBetterRange(AssumedConstantRange, OldRangeMD)) { - if (!AssumedConstantRange.isEmptySet()) { - I->setMetadata(LLVMContext::MD_range, - getMDNodeForConstantRange(I->getType(), I->getContext(), - AssumedConstantRange)); - return true; - } + if (isBetterRange(AssumedConstantRange, *I)) { + I->setMetadata(LLVMContext::MD_range, + getMDNodeForConstantRange(I->getType(), I->getContext(), + AssumedConstantRange)); + return true; + } + return false; + } + /// Helper function to set range return attribute. + static bool + setRangeRetAttrIfisBetterRange(Attributor &A, const IRPosition &IRP, + Instruction *I, + const ConstantRange &AssumedConstantRange) { + if (isBetterRange(AssumedConstantRange, *I)) { + A.manifestAttrs(IRP, + Attribute::get(I->getContext(), Attribute::Range, + AssumedConstantRange), + /*ForceReplace*/ true); + return true; } return false; } @@ -9226,9 +9240,13 @@ struct AAValueConstantRangeImpl : AAValueConstantRange { if (Instruction *I = dyn_cast(&V)) { assert(I == getCtxI() && "Should not annotate an instruction which is " "not the context instruction"); - if (isa(I) || isa(I)) + if (isa(I)) if (setRangeMetadataIfisBetterRange(I, AssumedConstantRange)) Changed = ChangeStatus::CHANGED; + if (isa(I)) + if (setRangeRetAttrIfisBetterRange(A, getIRPosition(), I, + AssumedConstantRange)) + Changed = ChangeStatus::CHANGED; } } @@ -9624,10 +9642,11 @@ struct AAValueConstantRangeCallSiteReturned /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - // If it is a load instruction with range metadata, use the metadata. - if (CallInst *CI = dyn_cast(&getAssociatedValue())) - if (auto *RangeMD = CI->getMetadata(LLVMContext::MD_range)) - intersectKnown(getConstantRangeFromMetadata(*RangeMD)); + // If it is a call instruction with range attribute, use the range. + if (CallInst *CI = dyn_cast(&getAssociatedValue())) { + if (std::optional Range = CI->getRange()) + intersectKnown(*Range); + } AAValueConstantRangeImpl::initialize(A); } diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll index b0446479dac4b..6641fdb9b4ffe 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR16052.ll @@ -68,7 +68,7 @@ define i64 @fn2c() { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CONV:%.*]] = sext i32 undef to i64 ; CGSCC-NEXT: [[ADD:%.*]] = add i64 42, [[CONV]] -; CGSCC-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[ADD]]) #[[ATTR2]], !range [[RNG0:![0-9]+]] +; CGSCC-NEXT: [[CALL2:%.*]] = call range(i64 -2147483606, 2147483690) i64 @fn1(i64 [[ADD]]) #[[ATTR2]] ; CGSCC-NEXT: ret i64 [[CALL2]] ; entry: @@ -91,13 +91,11 @@ entry: ret i64 %cond } ;. +; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +;. ; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR2]] = { nofree nosync willreturn } ;. -; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } -;. -; CGSCC: [[RNG0]] = !{i64 -2147483606, i64 2147483690} -;. ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: ; CHECK: {{.*}} diff --git a/llvm/test/Transforms/Attributor/range.ll b/llvm/test/Transforms/Attributor/range.ll index 1b27fc5f16f56..38f8a829cf419 100644 --- a/llvm/test/Transforms/Attributor/range.ll +++ b/llvm/test/Transforms/Attributor/range.ll @@ -19,7 +19,7 @@ define i32 @test0-range-check(ptr %p) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; TUNIT-LABEL: define {{[^@]+}}@test0-range-check ; TUNIT-SAME: (ptr nofree readonly align 4 captures(none) [[P:%.*]]) #[[ATTR0]] { -; TUNIT-NEXT: [[A:%.*]] = tail call i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3:[0-9]+]], !range [[RNG0]] +; TUNIT-NEXT: [[A:%.*]] = tail call range(i32 0, 10) i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3:[0-9]+]] ; TUNIT-NEXT: ret i32 [[A]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: read) @@ -32,6 +32,40 @@ define i32 @test0-range-check(ptr %p) { ret i32 %a } +define i32 @test0-range-check-smaller-current-range-attr(ptr %p) { +; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; TUNIT-LABEL: define {{[^@]+}}@test0-range-check-smaller-current-range-attr +; TUNIT-SAME: (ptr nofree readonly align 4 captures(none) [[P:%.*]]) #[[ATTR0]] { +; TUNIT-NEXT: [[A:%.*]] = tail call range(i32 2, 5) i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]] +; TUNIT-NEXT: ret i32 [[A]] +; +; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: read) +; CGSCC-LABEL: define {{[^@]+}}@test0-range-check-smaller-current-range-attr +; CGSCC-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR1]] { +; CGSCC-NEXT: [[A:%.*]] = tail call range(i32 2, 5) i32 @test0(ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[P]]) #[[ATTR5]] +; CGSCC-NEXT: ret i32 [[A]] +; + %a = tail call range(i32 2, 5) i32 @test0(ptr %p) + ret i32 %a +} + +define i32 @test0-range-check-larger-current-range-attr(ptr %p) { +; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; TUNIT-LABEL: define {{[^@]+}}@test0-range-check-larger-current-range-attr +; TUNIT-SAME: (ptr nofree readonly align 4 captures(none) [[P:%.*]]) #[[ATTR0]] { +; TUNIT-NEXT: [[A:%.*]] = tail call range(i32 0, 10) i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]] +; TUNIT-NEXT: ret i32 [[A]] +; +; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: read) +; CGSCC-LABEL: define {{[^@]+}}@test0-range-check-larger-current-range-attr +; CGSCC-SAME: (ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR1]] { +; CGSCC-NEXT: [[A:%.*]] = tail call range(i32 0, 100) i32 @test0(ptr nofree noundef nonnull readonly align 4 captures(none) dereferenceable(4) [[P]]) #[[ATTR5]] +; CGSCC-NEXT: ret i32 [[A]] +; + %a = tail call range(i32 0, 100) i32 @test0(ptr %p) + ret i32 %a +} + declare void @use3-dummy(i1, i1, i1) define void @use3(i1, i1, i1) { ; CHECK-LABEL: define {{[^@]+}}@use3 @@ -48,7 +82,7 @@ define void @test0-icmp-check(ptr %p){ ; ret = [0, 10) ; TUNIT-LABEL: define {{[^@]+}}@test0-icmp-check ; TUNIT-SAME: (ptr nofree readonly align 4 captures(none) [[P:%.*]]) { -; TUNIT-NEXT: [[RET:%.*]] = tail call i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]], !range [[RNG0]] +; TUNIT-NEXT: [[RET:%.*]] = tail call range(i32 0, 10) i32 @test0(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]] ; TUNIT-NEXT: [[CMP_EQ_1:%.*]] = icmp eq i32 [[RET]], 10 ; TUNIT-NEXT: [[CMP_EQ_2:%.*]] = icmp eq i32 [[RET]], 9 ; TUNIT-NEXT: [[CMP_EQ_3:%.*]] = icmp eq i32 [[RET]], 8 @@ -284,7 +318,7 @@ define i1 @test1-check(ptr %p) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; TUNIT-LABEL: define {{[^@]+}}@test1-check ; TUNIT-SAME: (ptr nofree readonly align 4 captures(none) [[P:%.*]]) #[[ATTR0]] { -; TUNIT-NEXT: [[RES:%.*]] = tail call i32 @test1(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]], !range [[RNG2:![0-9]+]] +; TUNIT-NEXT: [[RES:%.*]] = tail call range(i32 200, 1091) i32 @test1(ptr nofree noundef readonly align 4 captures(none) [[P]]) #[[ATTR3]] ; TUNIT-NEXT: [[CMP:%.*]] = icmp eq i32 [[RES]], 500 ; TUNIT-NEXT: ret i1 [[CMP]] ; @@ -624,7 +658,7 @@ define dso_local i32 @test4-g2(i32 %u) { ; TUNIT-LABEL: define {{[^@]+}}@test4-g2 ; TUNIT-SAME: (i32 [[U:%.*]]) #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[CALL:%.*]] = tail call i32 @test4-f2(i32 [[U]]) #[[ATTR4]], !range [[RNG3:![0-9]+]] +; TUNIT-NEXT: [[CALL:%.*]] = tail call range(i32 1, -2147483648) i32 @test4-f2(i32 [[U]]) #[[ATTR4]] ; TUNIT-NEXT: ret i32 [[CALL]] ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) @@ -1760,6 +1794,7 @@ declare void @barney(i32 signext, i32 signext) !0 = !{i32 0, i32 10} !1 = !{i32 10, i32 100} +!2 = !{i32 2, i32 5} ;. ; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) } ; TUNIT: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } @@ -1778,8 +1813,6 @@ declare void @barney(i32 signext, i32 signext) ;. ; TUNIT: [[RNG0]] = !{i32 0, i32 10} ; TUNIT: [[RNG1]] = !{i32 10, i32 100} -; TUNIT: [[RNG2]] = !{i32 200, i32 1091} -; TUNIT: [[RNG3]] = !{i32 1, i32 -2147483648} ;. ; CGSCC: [[RNG0]] = !{i32 0, i32 10} ; CGSCC: [[RNG1]] = !{i32 10, i32 100} diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll index 87ad0e5be0231..beceab7ce9ed7 100644 --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -1114,7 +1114,7 @@ define i32 @test(i1 %c) { ; TUNIT-LABEL: define {{[^@]+}}@test ; TUNIT-SAME: (i1 [[C:%.*]]) { ; TUNIT-NEXT: [[R1:%.*]] = call i32 @ctx_test1(i1 noundef [[C]]) -; TUNIT-NEXT: [[R2:%.*]] = call i32 @ctx_test2(i1 noundef [[C]]), !range [[RNG0:![0-9]+]] +; TUNIT-NEXT: [[R2:%.*]] = call range(i32 0, -2147483648) i32 @ctx_test2(i1 noundef [[C]]) ; TUNIT-NEXT: [[ADD:%.*]] = add i32 [[R1]], [[R2]] ; TUNIT-NEXT: ret i32 [[ADD]] ; @@ -1689,8 +1689,6 @@ define i32 @readExtInitZeroInit() { ; TUNIT: attributes #[[ATTR15]] = { nosync nounwind memory(read) } ; TUNIT: attributes #[[ATTR16]] = { nounwind memory(write) } ;. -; TUNIT: [[RNG0]] = !{i32 0, i32 -2147483648} -;. ; CGSCC: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn } ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR2]] = { memory(readwrite, argmem: none) }