[Attributor] Swap range metadata to attribute for calls. (#108835)
This commit is contained in:
parent
9d994d1c08
commit
0a067dc107
@ -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!");
|
||||
}
|
||||
|
||||
@ -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;
|
||||
std::optional<ConstantRange> Known;
|
||||
|
||||
// If multiple ranges are annotated in IR, we give up to annotate assumed
|
||||
// range for now.
|
||||
if (const auto *CB = dyn_cast<CallBase>(&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;
|
||||
// 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<ConstantInt>(KnownRanges->getOperand(0));
|
||||
ConstantInt *Upper =
|
||||
mdconst::extract<ConstantInt>(KnownRanges->getOperand(1));
|
||||
ConstantInt *Lower =
|
||||
mdconst::extract<ConstantInt>(KnownRanges->getOperand(0));
|
||||
ConstantInt *Upper =
|
||||
mdconst::extract<ConstantInt>(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<Instruction>(&V)) {
|
||||
assert(I == getCtxI() && "Should not annotate an instruction which is "
|
||||
"not the context instruction");
|
||||
if (isa<CallInst>(I) || isa<LoadInst>(I))
|
||||
if (isa<LoadInst>(I))
|
||||
if (setRangeMetadataIfisBetterRange(I, AssumedConstantRange))
|
||||
Changed = ChangeStatus::CHANGED;
|
||||
if (isa<CallInst>(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<CallInst>(&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<CallInst>(&getAssociatedValue())) {
|
||||
if (std::optional<ConstantRange> Range = CI->getRange())
|
||||
intersectKnown(*Range);
|
||||
}
|
||||
|
||||
AAValueConstantRangeImpl::initialize(A);
|
||||
}
|
||||
|
||||
@ -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: {{.*}}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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) }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user