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<Argument>(UO) || isa<AllocaInst>(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.
; 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
; 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
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> <i1 true, i1 false, i1 true, i1 false>)
; CHECK-NEXT: ret void
;
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> <i1 true, i1 false, i1 true, i1 false>, <4 x i32> undef)
; CHECK-NEXT: ret <4 x i32> [[RES]]
;
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]]
;
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]]
;
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]]
;