From 5b3776842f8e14b70199c0f76773b2af1cf32d34 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 12 Oct 2022 17:22:11 +0200 Subject: [PATCH] [FunctionAttrs] Account for memory effects of inalloca/preallocated The code for inferring memory attributes on arguments claims that inalloca/preallocated arguments are always clobbered: https://github.com/llvm/llvm-project/blob/d71ad4108056d685f48407447095d8d92fd7685d/llvm/lib/Transforms/IPO/FunctionAttrs.cpp#L640-L642 However, we would still infer memory attributes for the whole function without taking this into account, so we could still end up inferring readnone for the function. This adds an argument clobber if there are any inalloca/preallocated arguments. Differential Revision: https://reviews.llvm.org/D135783 --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 7 ++++++- llvm/test/Transforms/FunctionAttrs/readattrs.ll | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 22ff83b..055cb14 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -133,13 +133,18 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR, if (!ThisBody) return OrigMRB; - // Scan the function body for instructions that may read or write memory. FunctionModRefBehavior MRB = FunctionModRefBehavior::none(); + // Inalloca and preallocated arguments are always clobbered by the call. + if (F.getAttributes().hasAttrSomewhere(Attribute::InAlloca) || + F.getAttributes().hasAttrSomewhere(Attribute::Preallocated)) + MRB |= FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef); + // Returns true if Ptr is not based on a function argument. auto IsArgumentOrAlloca = [](const Value *Ptr) { const Value *UO = getUnderlyingObject(Ptr); return isa(UO) || isa(UO); }; + // Scan the function body for instructions that may read or write memory. for (Instruction &I : instructions(F)) { // Some instructions can be ignored even if they read or write memory. // Detect these now, skipping to the next instruction if one is found. diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll index 798378e..63c2783 100644 --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -81,9 +81,9 @@ define void @test6_2(i8** %p, i8* %q) { ; inalloca parameters are always considered written define void @test7_1(i32* inalloca(i32) %a) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn ; CHECK-LABEL: define {{[^@]+}}@test7_1 -; CHECK-SAME: (i32* nocapture inalloca(i32) [[A:%.*]]) #[[ATTR1]] { +; CHECK-SAME: (i32* nocapture inalloca(i32) [[A:%.*]]) #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: ret void ; ret void @@ -91,9 +91,9 @@ define void @test7_1(i32* inalloca(i32) %a) { ; preallocated parameters are always considered written define void @test7_2(i32* preallocated(i32) %a) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn ; CHECK-LABEL: define {{[^@]+}}@test7_2 -; CHECK-SAME: (i32* nocapture preallocated(i32) [[A:%.*]]) #[[ATTR1]] { +; CHECK-SAME: (i32* nocapture preallocated(i32) [[A:%.*]]) #[[ATTR5]] { ; CHECK-NEXT: ret void ; ret void @@ -130,7 +130,7 @@ declare void @llvm.masked.scatter.v4i32.v4p0i32(<4 x i32>%val, <4 x i32*>, i32, define void @test9(<4 x i32*> %ptrs, <4 x i32>%val) { ; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn writeonly ; CHECK-LABEL: define {{[^@]+}}@test9 -; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR6:[0-9]+]] { +; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]], <4 x i32> [[VAL:%.*]]) #[[ATTR7:[0-9]+]] { ; CHECK-NEXT: call void @llvm.masked.scatter.v4i32.v4p0i32(<4 x i32> [[VAL]], <4 x i32*> [[PTRS]], i32 4, <4 x i1> ) ; CHECK-NEXT: ret void ; @@ -142,7 +142,7 @@ declare <4 x i32> @llvm.masked.gather.v4i32.v4p0i32(<4 x i32*>, i32, <4 x i1>, < define <4 x i32> @test10(<4 x i32*> %ptrs) { ; CHECK: Function Attrs: mustprogress nofree nosync nounwind readonly willreturn ; CHECK-LABEL: define {{[^@]+}}@test10 -; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR8:[0-9]+]] { +; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR9:[0-9]+]] { ; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @llvm.masked.gather.v4i32.v4p0i32(<4 x i32*> [[PTRS]], i32 4, <4 x i1> , <4 x i32> undef) ; CHECK-NEXT: ret <4 x i32> [[RES]] ; @@ -154,7 +154,7 @@ declare <4 x i32> @test11_1(<4 x i32*>) argmemonly nounwind readonly define <4 x i32> @test11_2(<4 x i32*> %ptrs) { ; CHECK: Function Attrs: argmemonly nofree nounwind readonly ; CHECK-LABEL: define {{[^@]+}}@test11_2 -; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR10:[0-9]+]] { +; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] { ; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @test11_1(<4 x i32*> [[PTRS]]) ; CHECK-NEXT: ret <4 x i32> [[RES]] ; @@ -166,7 +166,7 @@ declare <4 x i32> @test12_1(<4 x i32*>) argmemonly nounwind define <4 x i32> @test12_2(<4 x i32*> %ptrs) { ; CHECK: Function Attrs: argmemonly nounwind ; CHECK-LABEL: define {{[^@]+}}@test12_2 -; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR11:[0-9]+]] { +; CHECK-SAME: (<4 x i32*> [[PTRS:%.*]]) #[[ATTR12:[0-9]+]] { ; CHECK-NEXT: [[RES:%.*]] = call <4 x i32> @test12_1(<4 x i32*> [[PTRS]]) ; CHECK-NEXT: ret <4 x i32> [[RES]] ; @@ -177,7 +177,7 @@ define <4 x i32> @test12_2(<4 x i32*> %ptrs) { define i32 @volatile_load(i32* %p) { ; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nounwind willreturn ; CHECK-LABEL: define {{[^@]+}}@volatile_load -; CHECK-SAME: (i32* [[P:%.*]]) #[[ATTR12:[0-9]+]] { +; CHECK-SAME: (i32* [[P:%.*]]) #[[ATTR13:[0-9]+]] { ; CHECK-NEXT: [[LOAD:%.*]] = load volatile i32, i32* [[P]], align 4 ; CHECK-NEXT: ret i32 [[LOAD]] ; -- 2.7.4