Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -2117,6 +2117,16 @@ // The NoBuiltinAttr attached to the target FunctionDecl. const NoBuiltinAttr *NBA = nullptr; + // Some ABIs may result in additional accesses to arguments that may + // otherwise not be present. + auto AddPotentialArgAccess = [&]() { + llvm::Attribute A = FuncAttrs.getAttribute(llvm::Attribute::Memory); + if (A.isValid()) + FuncAttrs.addMemoryAttr( + A.getModRefBehavior() | + llvm::FunctionModRefBehavior::argMemOnly(llvm::ModRefInfo::ModRef)); + }; + // Collect function IR attributes based on declaration-specific // information. // FIXME: handle sseregparm someday... @@ -2163,18 +2173,19 @@ // 'const', 'pure' and 'noalias' attributed functions are also nounwind. if (TargetDecl->hasAttr()) { - FuncAttrs.addAttribute(llvm::Attribute::ReadNone); + FuncAttrs.addMemoryAttr(llvm::FunctionModRefBehavior::none()); FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); // gcc specifies that 'const' functions have greater restrictions than // 'pure' functions, so they also cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { - FuncAttrs.addAttribute(llvm::Attribute::ReadOnly); + FuncAttrs.addMemoryAttr(llvm::FunctionModRefBehavior::readOnly()); FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); // gcc specifies that 'pure' functions cannot have infinite loops. FuncAttrs.addAttribute(llvm::Attribute::WillReturn); } else if (TargetDecl->hasAttr()) { - FuncAttrs.addAttribute(llvm::Attribute::ArgMemOnly); + FuncAttrs.addMemoryAttr( + llvm::FunctionModRefBehavior::argMemOnly(llvm::ModRefInfo::ModRef)); FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); } if (TargetDecl->hasAttr()) @@ -2353,8 +2364,7 @@ case ABIArgInfo::InAlloca: case ABIArgInfo::Indirect: { // inalloca and sret disable readnone and readonly - FuncAttrs.removeAttribute(llvm::Attribute::ReadOnly) - .removeAttribute(llvm::Attribute::ReadNone); + AddPotentialArgAccess(); break; } @@ -2524,9 +2534,7 @@ Attrs.addAlignmentAttr(Align.getQuantity()); // byval disables readnone and readonly. - FuncAttrs.removeAttribute(llvm::Attribute::ReadOnly) - .removeAttribute(llvm::Attribute::ReadNone); - + AddPotentialArgAccess(); break; } case ABIArgInfo::IndirectAliased: { @@ -2542,8 +2550,7 @@ case ABIArgInfo::InAlloca: // inalloca disables readnone and readonly. - FuncAttrs.removeAttribute(llvm::Attribute::ReadOnly) - .removeAttribute(llvm::Attribute::ReadNone); + AddPotentialArgAccess(); continue; } Index: clang/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- clang/lib/CodeGen/ItaniumCXXABI.cpp +++ clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1325,8 +1325,9 @@ llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false); // Mark the function as nounwind readonly. - llvm::Attribute::AttrKind FuncAttrs[] = { llvm::Attribute::NoUnwind, - llvm::Attribute::ReadOnly }; + llvm::AttrBuilder FuncAttrs(CGF.getLLVMContext()); + FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); + FuncAttrs.addMemoryAttr(llvm::FunctionModRefBehavior::readOnly()); llvm::AttributeList Attrs = llvm::AttributeList::get( CGF.getLLVMContext(), llvm::AttributeList::FunctionIndex, FuncAttrs); Index: clang/test/CodeGen/asm-attrs.c =================================================================== --- clang/test/CodeGen/asm-attrs.c +++ clang/test/CodeGen/asm-attrs.c @@ -10,9 +10,9 @@ // CHECK: call void asm sideeffect "foo7", {{.*}} [[NOATTRS]] // CHECK: call i32 asm "foo8", {{.*}} [[READNONE]] -// CHECK: attributes [[READNONE]] = { nounwind readnone } +// CHECK: attributes [[READNONE]] = { nounwind memory(none) } // CHECK: attributes [[NOATTRS]] = { nounwind } -// CHECK: attributes [[READONLY]] = { nounwind readonly } +// CHECK: attributes [[READONLY]] = { nounwind memory(read) } int g0, g1; Index: clang/test/CodeGen/builtin-sqrt.c =================================================================== --- clang/test/CodeGen/builtin-sqrt.c +++ clang/test/CodeGen/builtin-sqrt.c @@ -8,8 +8,8 @@ } // HAS_ERRNO: declare float @sqrtf(float noundef) [[ATTR:#[0-9]+]] -// HAS_ERRNO-NOT: attributes [[ATTR]] = {{{.*}} readnone +// HAS_ERRNO-NOT: attributes [[ATTR]] = {{{.*}} memory(none) // NO_ERRNO: declare float @llvm.sqrt.f32(float) [[ATTR:#[0-9]+]] -// NO_ERRNO: attributes [[ATTR]] = { nocallback nofree nosync nounwind readnone {{.*}}} +// NO_ERRNO: attributes [[ATTR]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } Index: clang/test/CodeGen/complex-builtins.c =================================================================== --- clang/test/CodeGen/complex-builtins.c +++ clang/test/CodeGen/complex-builtins.c @@ -197,9 +197,9 @@ // HAS_ERRNO: declare { x86_fp80, x86_fp80 } @ctanhl(ptr noundef byval({ x86_fp80, x86_fp80 }) align 16) [[NOT_READNONE]] }; -// NO__ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// NO__ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } // NO__ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } // HAS_ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } // HAS_ERRNO: attributes [[WILLRETURN_NOT_READNONE]] = { nounwind willreturn {{.*}} } Index: clang/test/CodeGen/complex-libcalls.c =================================================================== --- clang/test/CodeGen/complex-libcalls.c +++ clang/test/CodeGen/complex-libcalls.c @@ -197,9 +197,9 @@ // HAS_ERRNO: declare { x86_fp80, x86_fp80 } @ctanhl(ptr noundef byval({ x86_fp80, x86_fp80 }) align 16) [[NOT_READNONE]] }; -// NO__ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// NO__ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } // NO__ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } // HAS_ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } // HAS_ERRNO: attributes [[WILLRETURN_NOT_READNONE]] = { nounwind willreturn {{.*}} } Index: clang/test/CodeGen/function-attributes.c =================================================================== --- clang/test/CodeGen/function-attributes.c +++ clang/test/CodeGen/function-attributes.c @@ -111,9 +111,9 @@ // CHECK: attributes [[NUW]] = { nounwind optsize{{.*}} } // CHECK: attributes [[AI]] = { alwaysinline nounwind optsize{{.*}} } -// CHECK: attributes [[NUW_OS_RN]] = { nounwind optsize readnone{{.*}} } +// CHECK: attributes [[NUW_OS_RN]] = { nounwind optsize willreturn memory(none){{.*}} } // CHECK: attributes [[SR]] = { nounwind optsize{{.*}} "stackrealign"{{.*}} } // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} } // CHECK: attributes [[NR]] = { noreturn optsize } -// CHECK: attributes [[NUW_RN]] = { nounwind optsize readnone willreturn } +// CHECK: attributes [[NUW_RN]] = { nounwind optsize willreturn memory(none) } // CHECK: attributes [[RT_CALL]] = { optsize returns_twice } Index: clang/test/CodeGen/libcall-declarations.c =================================================================== --- clang/test/CodeGen/libcall-declarations.c +++ clang/test/CodeGen/libcall-declarations.c @@ -614,8 +614,8 @@ // CHECK-ERRNO: declare { double, double } @ctanh(double noundef, double noundef) [[NONCONST]] // CHECK-ERRNO: declare <2 x float> @ctanhf(<2 x float> noundef) [[NONCONST]] -// CHECK-NOERRNO: attributes [[NUWRN]] = { nounwind readnone{{.*}} } -// CHECK-NOERRNO: attributes [[NUWRO]] = { nounwind readonly{{.*}} } +// CHECK-NOERRNO: attributes [[NUWRN]] = { nounwind willreturn memory(none){{.*}} } +// CHECK-NOERRNO: attributes [[NUWRO]] = { nounwind willreturn memory(read){{.*}} } -// CHECK-ERRNO: attributes [[NUWRN]] = { nounwind readnone{{.*}} } -// CHECK-ERRNO: attributes [[NUWRO]] = { nounwind readonly{{.*}} } +// CHECK-ERRNO: attributes [[NUWRN]] = { nounwind willreturn memory(none){{.*}} } +// CHECK-ERRNO: attributes [[NUWRO]] = { nounwind willreturn memory(read){{.*}} } Index: clang/test/CodeGen/libcalls.c =================================================================== --- clang/test/CodeGen/libcalls.c +++ clang/test/CodeGen/libcalls.c @@ -124,5 +124,5 @@ } // CHECK-YES: attributes [[NUW]] = { nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+x87" } -// CHECK-NO-DAG: attributes [[NUW_RN]] = { nounwind readnone{{.*}} } -// CHECK-NO-DAG: attributes [[NUW_RNI]] = { nocallback nofree nosync nounwind readnone speculatable willreturn } +// CHECK-NO-DAG: attributes [[NUW_RN]] = { nounwind willreturn memory(none){{.*}} } +// CHECK-NO-DAG: attributes [[NUW_RNI]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } Index: clang/test/CodeGen/math-builtins.c =================================================================== --- clang/test/CodeGen/math-builtins.c +++ clang/test/CodeGen/math-builtins.c @@ -680,16 +680,16 @@ // HAS_ERRNO: declare fp128 @llvm.trunc.f128(fp128) [[READNONE_INTRINSIC]] }; -// NO__ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } -// NO__ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } +// NO__ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } +// NO__ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } // NO__ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// NO__ERRNO: attributes [[PURE]] = { {{.*}}readonly{{.*}} } +// NO__ERRNO: attributes [[PURE]] = { {{.*}}memory(read){{.*}} } // HAS_ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// HAS_ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } -// HAS_ERRNO: attributes [[PURE]] = { {{.*}}readonly{{.*}} } -// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } +// HAS_ERRNO: attributes [[PURE]] = { {{.*}}memory(read){{.*}} } +// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } -// HAS_ERRNO_GNU: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } -// HAS_ERRNO_WIN: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO_GNU: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } +// HAS_ERRNO_WIN: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } Index: clang/test/CodeGen/math-libcalls.c =================================================================== --- clang/test/CodeGen/math-libcalls.c +++ clang/test/CodeGen/math-libcalls.c @@ -704,18 +704,18 @@ // HAS_ERRNO: declare x86_fp80 @llvm.trunc.f80(x86_fp80) [[READNONE_INTRINSIC]] }; -// NO__ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } -// NO__ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } +// NO__ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } +// NO__ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } // NO__ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// NO__ERRNO: attributes [[READONLY]] = { {{.*}}readonly{{.*}} } +// NO__ERRNO: attributes [[READONLY]] = { {{.*}}memory(read){{.*}} } // HAS_ERRNO: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// HAS_ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } -// HAS_ERRNO: attributes [[READONLY]] = { {{.*}}readonly{{.*}} } -// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } +// HAS_ERRNO: attributes [[READONLY]] = { {{.*}}memory(read){{.*}} } +// HAS_ERRNO: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } // HAS_MAYTRAP: attributes [[NOT_READNONE]] = { nounwind {{.*}} } -// HAS_MAYTRAP: attributes [[READNONE]] = { {{.*}}readnone{{.*}} } +// HAS_MAYTRAP: attributes [[READNONE]] = { {{.*}}memory(none){{.*}} } -// HAS_ERRNO_GNU: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } -// HAS_ERRNO_WIN: attributes [[READNONE_INTRINSIC]] = { {{.*}}readnone{{.*}} } +// HAS_ERRNO_GNU: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } +// HAS_ERRNO_WIN: attributes [[READNONE_INTRINSIC]] = { {{.*}}memory(none){{.*}} } Index: clang/test/CodeGen/ms-declspecs.c =================================================================== --- clang/test/CodeGen/ms-declspecs.c +++ clang/test/CodeGen/ms-declspecs.c @@ -41,4 +41,4 @@ // CHECK: attributes [[NUW]] = { nounwind{{.*}} } // CHECK: attributes [[NI]] = { noinline nounwind{{.*}} } // CHECK: attributes [[NR]] = { noreturn } -// CHECK: attributes [[NA]] = { argmemonly nounwind{{.*}} } +// CHECK: attributes [[NA]] = { nounwind memory(argmem: readwrite){{.*}} } Index: clang/test/CodeGen/pragma-weak.c =================================================================== --- clang/test/CodeGen/pragma-weak.c +++ clang/test/CodeGen/pragma-weak.c @@ -202,4 +202,4 @@ int correct_linkage; // CHECK: attributes [[NI]] = { noinline nounwind{{.*}} } -// CHECK: attributes [[RN]] = { noinline nounwind optnone readnone{{.*}} } +// CHECK: attributes [[RN]] = { noinline nounwind optnone willreturn memory(none){{.*}} } Index: clang/test/CodeGen/struct-passing.c =================================================================== --- clang/test/CodeGen/struct-passing.c +++ clang/test/CodeGen/struct-passing.c @@ -23,5 +23,5 @@ // CHECK: declare void @f4({{.*}} byval({{.*}}) align 4) // CHECK: declare void @f5({{.*}} byval({{.*}}) align 4) -// CHECK: attributes [[RN]] = { nounwind readnone{{.*}} } -// CHECK: attributes [[RO]] = { nounwind readonly{{.*}} } +// CHECK: attributes [[RN]] = { nounwind willreturn memory(none){{.*}} } +// CHECK: attributes [[RO]] = { nounwind willreturn memory(read){{.*}} } Index: clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp =================================================================== --- clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp +++ clang/test/CodeGenCXX/2009-05-04-PureConstNounwind.cpp @@ -15,8 +15,8 @@ // CHECK: declare noundef i32 @_Z1tv() [[TF2:#[0-9]+]] // CHECK: attributes [[TF]] = { {{.*}} } -// CHECK: attributes [[NUW_RN]] = { nounwind readnone willreturn{{.*}} } -// CHECK: attributes [[NUW_RO]] = { nounwind readonly willreturn{{.*}} } +// CHECK: attributes [[NUW_RN]] = { nounwind willreturn memory(none){{.*}} } +// CHECK: attributes [[NUW_RO]] = { nounwind willreturn memory(read){{.*}} } // CHECK: attributes [[TF2]] = { {{.*}} } -// CHECK: attributes [[NUW_RN_CALL]] = { nounwind readnone willreturn } -// CHECK: attributes [[NUW_RO_CALL]] = { nounwind readonly willreturn } +// CHECK: attributes [[NUW_RN_CALL]] = { nounwind willreturn memory(none) } +// CHECK: attributes [[NUW_RO_CALL]] = { nounwind willreturn memory(read) } Index: clang/test/CodeGenCXX/dynamic-cast.cpp =================================================================== --- clang/test/CodeGenCXX/dynamic-cast.cpp +++ clang/test/CodeGenCXX/dynamic-cast.cpp @@ -20,5 +20,5 @@ // CHECK: declare ptr @__dynamic_cast(ptr, ptr, ptr, i64) [[NUW_RO:#[0-9]+]] -// CHECK: attributes [[NUW_RO]] = { nounwind readonly } +// CHECK: attributes [[NUW_RO]] = { nounwind memory(read) } // CHECK: attributes [[NR]] = { noreturn } Index: clang/test/CodeGenCXX/threadlocal_address.cpp =================================================================== --- clang/test/CodeGenCXX/threadlocal_address.cpp +++ clang/test/CodeGenCXX/threadlocal_address.cpp @@ -51,4 +51,4 @@ // CHECK-O1-NEXT: store i32 %[[INC]], ptr %[[J_ADDR]] // CHECK-O1-NEXT: ret i32 %[[INC]] // -// CHECK: attributes #[[ATTR_NUM]] = { nocallback nofree nosync nounwind readnone speculatable willreturn } +// CHECK: attributes #[[ATTR_NUM]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } Index: clang/test/CodeGenOpenCL/builtins-amdgcn.cl =================================================================== --- clang/test/CodeGenOpenCL/builtins-amdgcn.cl +++ clang/test/CodeGenOpenCL/builtins-amdgcn.cl @@ -796,7 +796,7 @@ // CHECK-DAG: [[$WI_RANGE]] = !{i32 0, i32 1024} // CHECK-DAG: [[$WS_RANGE]] = !{i16 1, i16 1025} -// CHECK-DAG: attributes #[[$NOUNWIND_READONLY:[0-9]+]] = { nofree nounwind readonly } +// CHECK-DAG: attributes #[[$NOUNWIND_READONLY:[0-9]+]] = { nofree nounwind memory(read) } // CHECK-DAG: attributes #[[$READ_EXEC_ATTRS]] = { convergent } // CHECK-DAG: ![[$EXEC]] = !{!"exec"} // CHECK-DAG: ![[$EXEC_LO]] = !{!"exec_lo"} Index: clang/test/CodeGenOpenCL/convergent.cl =================================================================== --- clang/test/CodeGenOpenCL/convergent.cl +++ clang/test/CodeGenOpenCL/convergent.cl @@ -133,7 +133,7 @@ __asm__ volatile("s_barrier"); } -// CHECK: attributes #0 = { nofree noinline norecurse nounwind " +// CHECK: attributes #0 = { nofree noinline norecurse nounwind memory({{.*}}) " // CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} } Index: clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl =================================================================== --- clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl +++ clang/test/CodeGenOpenCL/fdeclare-opencl-builtins.cl @@ -49,6 +49,6 @@ } // CHECK: attributes [[ATTR_CONST]] = -// CHECK-SAME: readnone +// CHECK-SAME: memory(none) // CHECK: attributes [[ATTR_PURE]] = -// CHECK-SAME: readonly +// CHECK-SAME: memory(read) Index: clang/test/Sema/libbuiltins-ctype-powerpc64.c =================================================================== --- clang/test/Sema/libbuiltins-ctype-powerpc64.c +++ clang/test/Sema/libbuiltins-ctype-powerpc64.c @@ -61,5 +61,5 @@ // CHECK: declare signext i32 @tolower(i32 noundef signext) [[NUW_RO:#[0-9]+]] // CHECK: declare signext i32 @toupper(i32 noundef signext) [[NUW_RO:#[0-9]+]] -// CHECK: attributes [[NUW_RO]] = { nounwind readonly{{.*}} } -// CHECK: attributes [[NUW_RO_CALL]] = { nounwind readonly willreturn } +// CHECK: attributes [[NUW_RO]] = { nounwind willreturn memory(read){{.*}} } +// CHECK: attributes [[NUW_RO_CALL]] = { nounwind willreturn memory(read) } Index: clang/test/Sema/libbuiltins-ctype-x86_64.c =================================================================== --- clang/test/Sema/libbuiltins-ctype-x86_64.c +++ clang/test/Sema/libbuiltins-ctype-x86_64.c @@ -61,5 +61,5 @@ // CHECK: declare i32 @tolower(i32 noundef) [[NUW_RO:#[0-9]+]] // CHECK: declare i32 @toupper(i32 noundef) [[NUW_RO:#[0-9]+]] -// CHECK: attributes [[NUW_RO]] = { nounwind readonly{{.*}} } -// CHECK: attributes [[NUW_RO_CALL]] = { nounwind readonly willreturn } +// CHECK: attributes [[NUW_RO]] = { nounwind willreturn memory(read){{.*}} } +// CHECK: attributes [[NUW_RO_CALL]] = { nounwind willreturn memory(read) } Index: llvm/include/llvm/IR/Attributes.h =================================================================== --- llvm/include/llvm/IR/Attributes.h +++ llvm/include/llvm/IR/Attributes.h @@ -146,6 +146,8 @@ static Attribute getWithPreallocatedType(LLVMContext &Context, Type *Ty); static Attribute getWithInAllocaType(LLVMContext &Context, Type *Ty); static Attribute getWithUWTableKind(LLVMContext &Context, UWTableKind Kind); + static Attribute getWithModRefBehavior(LLVMContext &Context, + FunctionModRefBehavior FMRB); /// For a typed attribute, return the equivalent attribute with the type /// changed to \p ReplacementTy. @@ -379,6 +381,7 @@ Optional getVScaleRangeMax() const; UWTableKind getUWTableKind() const; AllocFnKind getAllocKind() const; + FunctionModRefBehavior getModRefBehavior() const; std::string getAsString(bool InAttrGrp = false) const; /// Return true if this attribute set belongs to the LLVMContext. Index: llvm/include/llvm/IR/Function.h =================================================================== --- llvm/include/llvm/IR/Function.h +++ llvm/include/llvm/IR/Function.h @@ -491,54 +491,35 @@ void setPresplitCoroutine() { addFnAttr(Attribute::PresplitCoroutine); } void setSplittedCoroutine() { removeFnAttr(Attribute::PresplitCoroutine); } + FunctionModRefBehavior getModRefBehavior() const; + void setModRefBehavior(FunctionModRefBehavior FMRB); + /// Determine if the function does not access memory. - bool doesNotAccessMemory() const { - return hasFnAttribute(Attribute::ReadNone); - } - void setDoesNotAccessMemory() { - addFnAttr(Attribute::ReadNone); - } + bool doesNotAccessMemory() const; + void setDoesNotAccessMemory(); /// Determine if the function does not access or only reads memory. - bool onlyReadsMemory() const { - return doesNotAccessMemory() || hasFnAttribute(Attribute::ReadOnly); - } - void setOnlyReadsMemory() { - addFnAttr(Attribute::ReadOnly); - } + bool onlyReadsMemory() const; + void setOnlyReadsMemory(); /// Determine if the function does not access or only writes memory. - bool onlyWritesMemory() const { - return doesNotAccessMemory() || hasFnAttribute(Attribute::WriteOnly); - } - void setOnlyWritesMemory() { - addFnAttr(Attribute::WriteOnly); - } + bool onlyWritesMemory() const; + void setOnlyWritesMemory(); /// Determine if the call can access memmory only using pointers based /// on its arguments. - bool onlyAccessesArgMemory() const { - return hasFnAttribute(Attribute::ArgMemOnly); - } - void setOnlyAccessesArgMemory() { addFnAttr(Attribute::ArgMemOnly); } + bool onlyAccessesArgMemory() const; + void setOnlyAccessesArgMemory(); /// Determine if the function may only access memory that is /// inaccessible from the IR. - bool onlyAccessesInaccessibleMemory() const { - return hasFnAttribute(Attribute::InaccessibleMemOnly); - } - void setOnlyAccessesInaccessibleMemory() { - addFnAttr(Attribute::InaccessibleMemOnly); - } + bool onlyAccessesInaccessibleMemory() const; + void setOnlyAccessesInaccessibleMemory(); /// Determine if the function may only access memory that is /// either inaccessible from the IR or pointed to by its arguments. - bool onlyAccessesInaccessibleMemOrArgMem() const { - return hasFnAttribute(Attribute::InaccessibleMemOrArgMemOnly); - } - void setOnlyAccessesInaccessibleMemOrArgMem() { - addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } + bool onlyAccessesInaccessibleMemOrArgMem() const; + void setOnlyAccessesInaccessibleMemOrArgMem(); /// Determine if the function cannot return. bool doesNotReturn() const { Index: llvm/include/llvm/IR/InstrTypes.h =================================================================== --- llvm/include/llvm/IR/InstrTypes.h +++ llvm/include/llvm/IR/InstrTypes.h @@ -1847,47 +1847,37 @@ /// Return true if the call should not be inlined. bool isNoInline() const { return hasFnAttr(Attribute::NoInline); } void setIsNoInline() { addFnAttr(Attribute::NoInline); } + + FunctionModRefBehavior getModRefBehavior() const; + void setModRefBehavior(FunctionModRefBehavior FMRB); + /// Determine if the call does not access memory. - bool doesNotAccessMemory() const { return hasFnAttr(Attribute::ReadNone); } - void setDoesNotAccessMemory() { addFnAttr(Attribute::ReadNone); } + bool doesNotAccessMemory() const; + void setDoesNotAccessMemory(); /// Determine if the call does not access or only reads memory. - bool onlyReadsMemory() const { - return hasImpliedFnAttr(Attribute::ReadOnly); - } - - void setOnlyReadsMemory() { addFnAttr(Attribute::ReadOnly); } + bool onlyReadsMemory() const; + void setOnlyReadsMemory(); /// Determine if the call does not access or only writes memory. - bool onlyWritesMemory() const { - return hasImpliedFnAttr(Attribute::WriteOnly); - } - void setOnlyWritesMemory() { addFnAttr(Attribute::WriteOnly); } + bool onlyWritesMemory() const; + void setOnlyWritesMemory(); /// Determine if the call can access memmory only using pointers based /// on its arguments. - bool onlyAccessesArgMemory() const { - return hasFnAttr(Attribute::ArgMemOnly); - } - void setOnlyAccessesArgMemory() { addFnAttr(Attribute::ArgMemOnly); } + bool onlyAccessesArgMemory() const; + void setOnlyAccessesArgMemory(); /// Determine if the function may only access memory that is /// inaccessible from the IR. - bool onlyAccessesInaccessibleMemory() const { - return hasFnAttr(Attribute::InaccessibleMemOnly); - } - void setOnlyAccessesInaccessibleMemory() { - addFnAttr(Attribute::InaccessibleMemOnly); - } + bool onlyAccessesInaccessibleMemory() const; + void setOnlyAccessesInaccessibleMemory(); /// Determine if the function may only access memory that is /// either inaccessible from the IR or pointed to by its arguments. - bool onlyAccessesInaccessibleMemOrArgMem() const { - return hasFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } - void setOnlyAccessesInaccessibleMemOrArgMem() { - addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); - } + bool onlyAccessesInaccessibleMemOrArgMem() const; + void setOnlyAccessesInaccessibleMemOrArgMem(); + /// Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { addFnAttr(Attribute::NoReturn); } Index: llvm/lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -740,30 +740,11 @@ return II && II->getIntrinsicID() == IID; } -static FunctionModRefBehavior getModRefBehaviorFromAttrs(AttributeSet Attrs) { - if (Attrs.hasAttribute(Attribute::ReadNone)) - return FunctionModRefBehavior::none(); - - ModRefInfo MR = ModRefInfo::ModRef; - if (Attrs.hasAttribute(Attribute::ReadOnly)) - MR = ModRefInfo::Ref; - else if (Attrs.hasAttribute(Attribute::WriteOnly)) - MR = ModRefInfo::Mod; - - if (Attrs.hasAttribute(Attribute::ArgMemOnly)) - return FunctionModRefBehavior::argMemOnly(MR); - if (Attrs.hasAttribute(Attribute::InaccessibleMemOnly)) - return FunctionModRefBehavior::inaccessibleMemOnly(MR); - if (Attrs.hasAttribute(Attribute::InaccessibleMemOrArgMemOnly)) - return FunctionModRefBehavior::inaccessibleOrArgMemOnly(MR); - return FunctionModRefBehavior(MR); -} - /// Returns the behavior when calling the given call site. FunctionModRefBehavior BasicAAResult::getModRefBehavior(const CallBase *Call, AAQueryInfo &AAQI) { FunctionModRefBehavior Min = - getModRefBehaviorFromAttrs(Call->getAttributes().getFnAttrs()); + Call->getAttributes().getFnAttrs().getModRefBehavior(); if (const Function *F = dyn_cast(Call->getCalledOperand())) { FunctionModRefBehavior FMRB = AAQI.AAR.getModRefBehavior(F); @@ -791,7 +772,7 @@ FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); } - return getModRefBehaviorFromAttrs(F->getAttributes().getFnAttrs()); + return F->getModRefBehavior(); } /// Returns true if this is a writeonly (i.e Mod only) parameter. Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -148,6 +148,44 @@ std::make_pair(I.first, std::make_pair(I.second, LocTy()))); } +// TODO: This is only a temporary upgrade path, drop it once tests are updated. +static void upgradeMemoryAttrs(AttrBuilder &B) { + if (B.contains(Attribute::Memory)) + return; + + if (B.contains(Attribute::ReadNone)) { + B.removeAttribute(Attribute::ReadNone); + B.addMemoryAttr(FunctionModRefBehavior::none()); + return; + } + + ModRefInfo MR = ModRefInfo::ModRef; + if (B.contains(Attribute::ReadOnly)) { + MR = ModRefInfo::Ref; + B.removeAttribute(Attribute::ReadOnly); + } else if (B.contains(Attribute::WriteOnly)) { + MR = ModRefInfo::Mod; + B.removeAttribute(Attribute::WriteOnly); + } + + FunctionModRefBehavior FMRB(MR); + if (B.contains(Attribute::ArgMemOnly)) { + FMRB = FunctionModRefBehavior::argMemOnly(MR); + B.removeAttribute(Attribute::ArgMemOnly); + B.removeAttribute(Attribute::InaccessibleMemOrArgMemOnly); + } else if (B.contains(Attribute::InaccessibleMemOnly)) { + FMRB = FunctionModRefBehavior::inaccessibleMemOnly(MR); + B.removeAttribute(Attribute::InaccessibleMemOnly); + } else if (B.contains(Attribute::InaccessibleMemOrArgMemOnly)) { + FMRB = FunctionModRefBehavior::inaccessibleMemOnly(MR) | + FunctionModRefBehavior::argMemOnly(MR); + B.removeAttribute(Attribute::InaccessibleMemOrArgMemOnly); + } + + if (FMRB != FunctionModRefBehavior::unknown()) + B.addMemoryAttr(FMRB); +} + /// validateEndOfModule - Do final validity and basic correctness checks at the /// end of the module. bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) { @@ -165,6 +203,8 @@ B.merge(R->second); } + upgradeMemoryAttrs(B); + if (Function *Fn = dyn_cast(V)) { AttributeList AS = Fn->getAttributes(); AttrBuilder FnAttrs(M->getContext(), AS.getFnAttrs()); @@ -5834,6 +5874,8 @@ FuncAttrs.removeAttribute(Attribute::Alignment); } + upgradeMemoryAttrs(FuncAttrs); + // Okay, if we got here, the function is syntactically valid. Convert types // and do semantic checks. std::vector ParamTypeList; @@ -6604,6 +6646,8 @@ if (I != E) return error(CallLoc, "not enough parameters specified for call"); + upgradeMemoryAttrs(FnAttrs); + // Finish off the Attribute and check them AttributeList PAL = AttributeList::get(Context, AttributeSet::get(Context, FnAttrs), @@ -7312,6 +7356,8 @@ if (I != E) return error(CallLoc, "not enough parameters specified for call"); + upgradeMemoryAttrs(FnAttrs); + // Finish off the Attribute and check them AttributeList PAL = AttributeList::get(Context, AttributeSet::get(Context, FnAttrs), Index: llvm/lib/IR/AttributeImpl.h =================================================================== --- llvm/lib/IR/AttributeImpl.h +++ llvm/lib/IR/AttributeImpl.h @@ -263,6 +263,7 @@ Optional getVScaleRangeMax() const; UWTableKind getUWTableKind() const; AllocFnKind getAllocKind() const; + FunctionModRefBehavior getModRefBehavior() const; std::string getAsString(bool InAttrGrp) const; Type *getAttributeType(Attribute::AttrKind Kind) const; Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -211,6 +211,11 @@ return get(Context, UWTable, uint64_t(Kind)); } +Attribute Attribute::getWithModRefBehavior(LLVMContext &Context, + FunctionModRefBehavior FMRB) { + return get(Context, Memory, FMRB.toIntValue()); +} + Attribute Attribute::getWithAllocSizeArgs(LLVMContext &Context, unsigned ElemSizeArg, const Optional &NumElemsArg) { @@ -833,6 +838,11 @@ return SetNode ? SetNode->getAllocKind() : AllocFnKind::Unknown; } +FunctionModRefBehavior AttributeSet::getModRefBehavior() const { + return SetNode ? SetNode->getModRefBehavior() + : FunctionModRefBehavior::unknown(); +} + std::string AttributeSet::getAsString(bool InAttrGrp) const { return SetNode ? SetNode->getAsString(InAttrGrp) : ""; } @@ -1011,6 +1021,12 @@ return AllocFnKind::Unknown; } +FunctionModRefBehavior AttributeSetNode::getModRefBehavior() const { + if (auto A = findEnumAttribute(Attribute::Memory)) + return A->getModRefBehavior(); + return FunctionModRefBehavior::unknown(); +} + std::string AttributeSetNode::getAsString(bool InAttrGrp) const { std::string Str; for (iterator I = begin(), E = end(); I != E; ++I) { Index: llvm/lib/IR/Function.cpp =================================================================== --- llvm/lib/IR/Function.cpp +++ llvm/lib/IR/Function.cpp @@ -51,6 +51,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/SymbolTableListTraits.h" @@ -727,6 +728,70 @@ setPrologueData(Src->getPrologueData()); } +FunctionModRefBehavior Function::getModRefBehavior() const { + return getAttributes().getFnAttrs().getModRefBehavior(); +} +void Function::setModRefBehavior(FunctionModRefBehavior FMRB) { + addFnAttr(Attribute::getWithModRefBehavior(getContext(), FMRB)); +} + +/// Determine if the function does not access memory. +bool Function::doesNotAccessMemory() const { + return getModRefBehavior().doesNotAccessMemory(); +} +void Function::setDoesNotAccessMemory() { + setModRefBehavior(FunctionModRefBehavior::none()); +} + +/// Determine if the function does not access or only reads memory. +bool Function::onlyReadsMemory() const { + return getModRefBehavior().onlyReadsMemory(); +} +void Function::setOnlyReadsMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::readOnly()); +} + +/// Determine if the function does not access or only writes memory. +bool Function::onlyWritesMemory() const { + return getModRefBehavior().onlyWritesMemory(); +} +void Function::setOnlyWritesMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::writeOnly()); +} + +/// Determine if the call can access memmory only using pointers based +/// on its arguments. +bool Function::onlyAccessesArgMemory() const { + return getModRefBehavior().onlyAccessesArgPointees(); +} +void Function::setOnlyAccessesArgMemory() { + setModRefBehavior(getModRefBehavior() & + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// inaccessible from the IR. +bool Function::onlyAccessesInaccessibleMemory() const { + return getModRefBehavior().onlyAccessesInaccessibleMem(); +} +void Function::setOnlyAccessesInaccessibleMemory() { + setModRefBehavior( + getModRefBehavior() & + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// either inaccessible from the IR or pointed to by its arguments. +bool Function::onlyAccessesInaccessibleMemOrArgMem() const { + return getModRefBehavior().onlyAccessesInaccessibleOrArgMem(); +} +void Function::setOnlyAccessesInaccessibleMemOrArgMem() { + FunctionModRefBehavior FMRB = + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef) | + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); + setModRefBehavior(getModRefBehavior() & FMRB); +} + /// Table of string intrinsic names indexed by enum value. static const char * const IntrinsicNameTable[] = { "not_intrinsic", Index: llvm/lib/IR/Instructions.cpp =================================================================== --- llvm/lib/IR/Instructions.cpp +++ llvm/lib/IR/Instructions.cpp @@ -30,6 +30,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/ModRef.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" @@ -518,6 +519,83 @@ getIntrinsicID() != Intrinsic::assume; } +FunctionModRefBehavior CallBase::getModRefBehavior() const { + FunctionModRefBehavior FMRB = + getAttributes().getFnAttrs().getModRefBehavior(); + if (auto *Fn = dyn_cast(getCalledOperand())) { + FunctionModRefBehavior FnFMRB = Fn->getModRefBehavior(); + if (hasOperandBundles()) { + // TODO: Add a method to get FMRB for operand bundles instead. + if (hasReadingOperandBundles()) + FnFMRB |= FunctionModRefBehavior::readOnly(); + if (hasClobberingOperandBundles()) + FnFMRB |= FunctionModRefBehavior::writeOnly(); + } + FMRB &= FnFMRB; + } + return FMRB; +} +void CallBase::setModRefBehavior(FunctionModRefBehavior FMRB) { + addFnAttr(Attribute::getWithModRefBehavior(getContext(), FMRB)); +} + +/// Determine if the function does not access memory. +bool CallBase::doesNotAccessMemory() const { + return getModRefBehavior().doesNotAccessMemory(); +} +void CallBase::setDoesNotAccessMemory() { + setModRefBehavior(FunctionModRefBehavior::none()); +} + +/// Determine if the function does not access or only reads memory. +bool CallBase::onlyReadsMemory() const { + return getModRefBehavior().onlyReadsMemory(); +} +void CallBase::setOnlyReadsMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::readOnly()); +} + +/// Determine if the function does not access or only writes memory. +bool CallBase::onlyWritesMemory() const { + return getModRefBehavior().onlyWritesMemory(); +} +void CallBase::setOnlyWritesMemory() { + setModRefBehavior(getModRefBehavior() & FunctionModRefBehavior::writeOnly()); +} + +/// Determine if the call can access memmory only using pointers based +/// on its arguments. +bool CallBase::onlyAccessesArgMemory() const { + return getModRefBehavior().onlyAccessesArgPointees(); +} +void CallBase::setOnlyAccessesArgMemory() { + setModRefBehavior(getModRefBehavior() & + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// inaccessible from the IR. +bool CallBase::onlyAccessesInaccessibleMemory() const { + return getModRefBehavior().onlyAccessesInaccessibleMem(); +} +void CallBase::setOnlyAccessesInaccessibleMemory() { + setModRefBehavior( + getModRefBehavior() & + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef)); +} + +/// Determine if the function may only access memory that is +/// either inaccessible from the IR or pointed to by its arguments. +bool CallBase::onlyAccessesInaccessibleMemOrArgMem() const { + return getModRefBehavior().onlyAccessesInaccessibleOrArgMem(); +} +void CallBase::setOnlyAccessesInaccessibleMemOrArgMem() { + FunctionModRefBehavior FMRB = + FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef) | + FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::ModRef); + setModRefBehavior(getModRefBehavior() & FMRB); +} + //===----------------------------------------------------------------------===// // CallInst Implementation //===----------------------------------------------------------------------===// Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -69,10 +69,7 @@ #define DEBUG_TYPE "function-attrs" -STATISTIC(NumArgMemOnly, "Number of functions marked argmemonly"); -STATISTIC(NumReadNone, "Number of functions marked readnone"); -STATISTIC(NumReadOnly, "Number of functions marked readonly"); -STATISTIC(NumWriteOnly, "Number of functions marked writeonly"); +STATISTIC(NumMemoryAttr, "Number of functions with improved memory attribute"); STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); STATISTIC(NumReturned, "Number of arguments marked returned"); STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); @@ -243,79 +240,14 @@ return; } - ModRefInfo MR = FMRB.getModRef(); - for (Function *F : SCCNodes) { - if (F->doesNotAccessMemory()) - // Already perfect! - continue; - - if (FMRB.doesNotAccessMemory()) { - // For readnone, remove all other memory attributes. - AttributeMask AttrsToRemove; - AttrsToRemove.addAttribute(Attribute::ReadOnly); - AttrsToRemove.addAttribute(Attribute::WriteOnly); - AttrsToRemove.addAttribute(Attribute::ArgMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - - ++NumReadNone; - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::ReadNone); - Changed.insert(F); - continue; - } - - // Add argmemonly, inaccessiblememonly, or inaccessible_or_argmemonly - // attributes if possible. - AttributeMask AttrsToRemove; - AttrsToRemove.addAttribute(Attribute::ArgMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOnly); - AttrsToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - if (FMRB.onlyAccessesArgPointees()) { - if (!F->onlyAccessesArgMemory()) { - NumArgMemOnly++; - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::ArgMemOnly); - Changed.insert(F); - } - } else if (FMRB.onlyAccessesInaccessibleMem()) { - if (!F->onlyAccessesInaccessibleMemory()) { - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::InaccessibleMemOnly); - Changed.insert(F); - } - } else if (FMRB.onlyAccessesInaccessibleOrArgMem() && - !F->onlyAccessesInaccessibleMemOrArgMem()) { - F->removeFnAttrs(AttrsToRemove); - F->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly); + FunctionModRefBehavior OldFMRB = F->getModRefBehavior(); + FunctionModRefBehavior NewFMRB = FMRB & OldFMRB; + if (NewFMRB != OldFMRB) { + ++NumMemoryAttr; + F->addFnAttr(Attribute::getWithModRefBehavior(F->getContext(), NewFMRB)); Changed.insert(F); } - - // The SCC contains functions both writing and reading from memory. We - // cannot add readonly or writeonline attributes. - if (MR == ModRefInfo::ModRef) - continue; - - if (F->onlyReadsMemory() && MR == ModRefInfo::Ref) - continue; - - if (F->onlyWritesMemory() && MR == ModRefInfo::Mod) - continue; - - Changed.insert(F); - - // Add in the new attribute. - if (MR == ModRefInfo::Mod) { - ++NumWriteOnly; - F->removeFnAttr(Attribute::ReadOnly); - F->addFnAttr(Attribute::WriteOnly); - } else { - ++NumReadOnly; - assert(MR == ModRefInfo::Ref); - F->removeFnAttr(Attribute::WriteOnly); - F->addFnAttr(Attribute::ReadOnly); - } } } Index: llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp =================================================================== --- llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp +++ llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp @@ -1430,10 +1430,7 @@ // machine model for purposes of optimization. We have to strip these on // both function declarations and call sites. static constexpr Attribute::AttrKind FnAttrsToStrip[] = - {Attribute::ReadNone, Attribute::ReadOnly, Attribute::WriteOnly, - Attribute::ArgMemOnly, Attribute::InaccessibleMemOnly, - Attribute::InaccessibleMemOrArgMemOnly, - Attribute::NoSync, Attribute::NoFree}; + {Attribute::Memory, Attribute::NoSync, Attribute::NoFree}; // Create new attribute set containing only attributes which can be transferred // from original call to the safepoint. Index: llvm/lib/Transforms/Scalar/SCCP.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SCCP.cpp +++ llvm/lib/Transforms/Scalar/SCCP.cpp @@ -590,21 +590,28 @@ } } - // If we replaced an argument, the argmemonly and - // inaccessiblemem_or_argmemonly attributes do not hold any longer. Remove - // them from both the function and callsites. + // If we replaced an argument, we may not also access a global (currently + // classified as "other" memory). Update memory attribute to reflect this. if (ReplacedPointerArg) { - AttributeMask AttributesToRemove; - AttributesToRemove.addAttribute(Attribute::ArgMemOnly); - AttributesToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); - F.removeFnAttrs(AttributesToRemove); - + auto UpdateAttrs = [&](AttributeList AL) { + FunctionModRefBehavior FMRB = AL.getFnAttrs().getModRefBehavior(); + if (FMRB == FunctionModRefBehavior::unknown()) + return AL; + + FMRB |= FunctionModRefBehavior(FunctionModRefBehavior::Other, + ModRefInfo::ModRef); + return AL.addFnAttribute( + F.getContext(), + Attribute::getWithModRefBehavior(F.getContext(), FMRB)); + }; + + F.setAttributes(UpdateAttrs(F.getAttributes())); for (User *U : F.users()) { auto *CB = dyn_cast(U); if (!CB || CB->getCalledFunction() != &F) continue; - CB->removeFnAttrs(AttributesToRemove); + CB->setAttributes(UpdateAttrs(CB->getAttributes())); } } MadeChanges |= ReplacedPointerArg; Index: llvm/test/Analysis/BasicAA/cs-cs.ll =================================================================== --- llvm/test/Analysis/BasicAA/cs-cs.ll +++ llvm/test/Analysis/BasicAA/cs-cs.ll @@ -429,19 +429,19 @@ } -; CHECK: attributes #0 = { argmemonly nocallback nofree nounwind willreturn writeonly } -; CHECK-NEXT: attributes #1 = { argmemonly nocallback nofree nounwind willreturn } -; CHECK-NEXT: attributes #2 = { argmemonly nosync nounwind willreturn } -; CHECK-NEXT: attributes #3 = { noinline nounwind readonly } -; CHECK-NEXT: attributes #4 = { noinline nounwind writeonly } +; CHECK: attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) } +; CHECK-NEXT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +; CHECK-NEXT: attributes #2 = { nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK-NEXT: attributes #3 = { noinline nounwind memory(read) } +; CHECK-NEXT: attributes #4 = { noinline nounwind memory(write) } ; CHECK-NEXT: attributes #5 = { nounwind ssp } -; CHECK-NEXT: attributes #6 = { inaccessiblememonly nounwind } -; CHECK-NEXT: attributes #7 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK-NEXT: attributes #8 = { argmemonly nounwind } -; CHECK-NEXT: attributes #9 = { readonly } -; CHECK-NEXT: attributes #10 = { inaccessiblememonly } -; CHECK-NEXT: attributes #11 = { inaccessiblemem_or_argmemonly } -; CHECK-NEXT: attributes #12 = { argmemonly } +; CHECK-NEXT: attributes #6 = { nounwind memory(inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #7 = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #8 = { nounwind memory(argmem: readwrite) } +; CHECK-NEXT: attributes #9 = { memory(read) } +; CHECK-NEXT: attributes #10 = { memory(inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #11 = { memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK-NEXT: attributes #12 = { memory(argmem: readwrite) } attributes #0 = { argmemonly nounwind } attributes #1 = { noinline nounwind readonly } Index: llvm/test/Analysis/BasicAA/intrinsics.ll =================================================================== --- llvm/test/Analysis/BasicAA/intrinsics.ll +++ llvm/test/Analysis/BasicAA/intrinsics.ll @@ -22,6 +22,6 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nocallback nofree nosync nounwind readonly willreturn } -; CHECK: attributes #1 = { argmemonly nocallback nofree nosync nounwind willreturn writeonly } +; CHECK: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: write) } ; CHECK: attributes [[ATTR]] = { nounwind } Index: llvm/test/Analysis/BasicAA/pure-const-dce.ll =================================================================== --- llvm/test/Analysis/BasicAA/pure-const-dce.ll +++ llvm/test/Analysis/BasicAA/pure-const-dce.ll @@ -50,5 +50,5 @@ declare i32 @TestNone(i32) -; CHECK: attributes [[READNONE]] = { readnone } -; CHECK: attributes [[READONLY]] = { readonly } +; CHECK: attributes [[READNONE]] = { memory(none) } +; CHECK: attributes [[READONLY]] = { memory(read) } Index: llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -72,14 +72,14 @@ declare void @callee(i32* %p) nounwind declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind -; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn } -; CHECK: attributes #1 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly } -; CHECK: attributes #2 = { nofree nounwind readonly } +; CHECK: attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } +; CHECK: attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } +; CHECK: attributes #2 = { nofree nounwind memory(read, argmem: none) } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { mustprogress nofree nosync nounwind readnone willreturn } -; CHECK: attributes #5 = { argmemonly mustprogress nofree nosync nounwind willreturn } -; CHECK: attributes #6 = { argmemonly mustprogress nofree norecurse nosync nounwind willreturn } -; CHECK: attributes #7 = { argmemonly nocallback nofree nounwind willreturn } +; CHECK: attributes #4 = { mustprogress nofree nosync nounwind willreturn memory(none) } +; CHECK: attributes #5 = { mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK: attributes #6 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK: attributes #7 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } ; Root note. !0 = !{ } Index: llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll @@ -22,8 +22,8 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nocallback nofree nosync nounwind readonly willreturn } -; CHECK: attributes #1 = { argmemonly nocallback nofree nosync nounwind willreturn writeonly } +; CHECK: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: write) } ; CHECK: attributes [[NUW]] = { nounwind } !0 = !{!"tbaa root"} Index: llvm/test/Assembler/aarch64-intrinsics-attributes.ll =================================================================== --- llvm/test/Assembler/aarch64-intrinsics-attributes.ll +++ llvm/test/Assembler/aarch64-intrinsics-attributes.ll @@ -23,4 +23,4 @@ declare @llvm.aarch64.sve.dup.nxv4i32(, , i32) ; CHECK: attributes [[NOFREE_NOUNWIND_WILLRETURN]] = { nofree nounwind willreturn } -; CHECK: attributes [[NO_CALLBACK_NOFREE_NOSYNC_NOUNWIND_READNONE_WILLRETURN]] = { nocallback nofree nosync nounwind readnone willreturn } +; CHECK: attributes [[NO_CALLBACK_NOFREE_NOSYNC_NOUNWIND_READNONE_WILLRETURN]] = { nocallback nofree nosync nounwind willreturn memory(none) } Index: llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll =================================================================== --- llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll +++ llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll @@ -45,7 +45,7 @@ declare void @llvm.dbg.value(metadata, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { nounwind ssp } -; CHECK: attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ; CHECK: attributes #2 = { noinline nounwind ssp } ; CHECK: attributes [[NUW]] = { nounwind } Index: llvm/test/Transforms/FunctionAttrs/argmemonly.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/argmemonly.ll +++ llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -4,7 +4,7 @@ @g = global i32 20 define void @test_no_read_or_write() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: @test_no_read_or_write( ; CHECK-NEXT: entry: ; CHECK-NEXT: ret void @@ -14,7 +14,7 @@ } define i32 @test_only_read_arg(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_only_read_arg( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 @@ -26,7 +26,7 @@ } define i32 @test_only_read_arg_already_has_argmemonly(i32* %ptr) argmemonly { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_only_read_arg_already_has_argmemonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 @@ -38,7 +38,7 @@ } define i32 @test_read_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) ; CHECK-LABEL: @test_read_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32, i32* @g, align 4 @@ -50,7 +50,7 @@ } define i32 @test_read_loaded_ptr(i32** %ptr) { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none) ; CHECK-LABEL: @test_read_loaded_ptr( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[L:%.*]] = load i32*, i32** [[PTR:%.*]], align 8 @@ -64,7 +64,7 @@ } define void @test_only_write_arg(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind willreturn writeonly +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) ; CHECK-LABEL: @test_only_write_arg( ; CHECK-NEXT: entry: ; CHECK-NEXT: store i32 0, i32* [[PTR:%.*]], align 4 @@ -76,7 +76,7 @@ } define void @test_write_global() { -; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write, inaccessiblemem: none) ; CHECK-LABEL: @test_write_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: store i32 0, i32* @g, align 4 @@ -90,6 +90,7 @@ declare void @fn_may_access_memory() define void @test_call_may_access_memory() { +; CHECK: Function Attrs: memory(readwrite, argmem: none) ; CHECK-LABEL: @test_call_may_access_memory( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @fn_may_access_memory() @@ -103,7 +104,7 @@ declare i32 @fn_readnone() readnone define void @test_call_readnone(i32* %ptr) { -; CHECK: Function Attrs: argmemonly writeonly +; CHECK: Function Attrs: memory(argmem: write) ; CHECK-LABEL: @test_call_readnone( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @fn_readnone() @@ -119,7 +120,7 @@ declare i32 @fn_argmemonly(i32*) argmemonly define i32 @test_call_argmemonly(i32* %ptr) { -; CHECK: Function Attrs: argmemonly +; CHECK: Function Attrs: memory(argmem: readwrite) ; CHECK-LABEL: @test_call_argmemonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(i32* [[PTR:%.*]]) @@ -131,7 +132,7 @@ } define i32 @test_call_fn_where_argmemonly_can_be_inferred(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_call_fn_where_argmemonly_can_be_inferred( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(i32* [[PTR:%.*]]) @@ -143,7 +144,7 @@ } define void @test_memcpy_argonly(i8* %dst, i8* %src) { -; CHECK: Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(argmem: readwrite) ; CHECK-LABEL: @test_memcpy_argonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DST:%.*]], i8* [[SRC:%.*]], i64 32, i1 false) @@ -159,7 +160,7 @@ @arr = global [32 x i8] zeroinitializer define void @test_memcpy_src_global(i8* %dst) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @test_memcpy_src_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* @@ -173,7 +174,7 @@ } define void @test_memcpy_dst_global(i8* %src) { -; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @test_memcpy_dst_global( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* @@ -187,7 +188,7 @@ } define i32 @test_read_arg_access_alloca(i32* %ptr) { -; CHECK: Function Attrs: argmemonly mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) ; CHECK-LABEL: @test_read_arg_access_alloca( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 @@ -207,7 +208,7 @@ declare void @fn_inaccessiblememonly() inaccessiblememonly define void @test_inaccessiblememonly() { -; CHECK: Function Attrs: inaccessiblememonly +; CHECK: Function Attrs: memory(inaccessiblemem: readwrite) ; CHECK-LABEL: @test_inaccessiblememonly( ; CHECK-NEXT: call void @fn_inaccessiblememonly() ; CHECK-NEXT: ret void @@ -217,9 +218,9 @@ } define void @test_inaccessiblememonly_readonly() { -; CHECK: Function Attrs: inaccessiblememonly nofree readonly +; CHECK: Function Attrs: nofree memory(inaccessiblemem: read) ; CHECK-LABEL: @test_inaccessiblememonly_readonly( -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15:[0-9]+]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16:[0-9]+]] ; CHECK-NEXT: ret void ; call void @fn_inaccessiblememonly() readonly @@ -227,10 +228,10 @@ } define void @test_inaccessibleorargmemonly_readonly(i32* %arg) { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly nofree readonly +; CHECK: Function Attrs: nofree memory(argmem: read, inaccessiblemem: read) ; CHECK-LABEL: @test_inaccessibleorargmemonly_readonly( ; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] ; CHECK-NEXT: ret void ; load i32, i32* %arg @@ -239,10 +240,10 @@ } define void @test_inaccessibleorargmemonly_readwrite(i32* %arg) { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly +; CHECK: Function Attrs: memory(argmem: write, inaccessiblemem: read) ; CHECK-LABEL: @test_inaccessibleorargmemonly_readwrite( ; CHECK-NEXT: store i32 0, i32* [[ARG:%.*]], align 4 -; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR15]] +; CHECK-NEXT: call void @fn_inaccessiblememonly() #[[ATTR16]] ; CHECK-NEXT: ret void ; store i32 0, i32* %arg Index: llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll =================================================================== --- llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll +++ llvm/test/Transforms/InstCombine/AArch64/2012-04-23-Neon-Intrinsics.ll @@ -65,6 +65,6 @@ declare <4 x i32> @llvm.aarch64.neon.smull.v4i32(<4 x i16>, <4 x i16>) nounwind readnone declare <4 x i32> @llvm.aarch64.neon.umull.v4i32(<4 x i16>, <4 x i16>) nounwind readnone -; CHECK: attributes #0 = { nounwind readnone ssp } -; CHECK: attributes #1 = { nocallback nofree nosync nounwind readnone willreturn } +; CHECK: attributes #0 = { nounwind ssp memory() } +; CHECK: attributes #1 = { nocallback nofree nosync nounwind willreturn memory() } ; CHECK: attributes [[NUW]] = { nounwind } Index: llvm/test/Transforms/InstCombine/stpncpy-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/stpncpy-1.ll +++ llvm/test/Transforms/InstCombine/stpncpy-1.ll @@ -463,6 +463,6 @@ ret void } ;. -; ANY: attributes #[[ATTR0:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn writeonly } -; ANY: attributes #[[ATTR1:[0-9]+]] = { argmemonly nocallback nofree nounwind willreturn } +; ANY: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: write) } +; ANY: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } ;. Index: llvm/test/Transforms/LICM/scalar-promote.ll =================================================================== --- llvm/test/Transforms/LICM/scalar-promote.ll +++ llvm/test/Transforms/LICM/scalar-promote.ll @@ -600,7 +600,7 @@ } define i8 @test_hoistable_existing_load_sinkable_store_writeonly(ptr dereferenceable(8) %ptr, i8 %start) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_hoistable_existing_load_sinkable_store_writeonly( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[PTR_PROMOTED:%.*]] = load i8, ptr [[PTR:%.*]], align 1 @@ -641,7 +641,7 @@ ; Test case for PR51248. define void @test_sink_store_only() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_only( ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LOOP_HEADER:%.*]] @@ -678,7 +678,7 @@ } define void @test_sink_store_to_local_object_only_loop_must_execute() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_local_object_only_loop_must_execute( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 @@ -719,7 +719,7 @@ ; The store in the loop may not execute, so we need to introduce a load in the ; pre-header. Make sure the writeonly attribute is dropped. define void @test_sink_store_to_local_object_only_loop_may_not_execute(i8 %n) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_local_object_only_loop_may_not_execute( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 @@ -761,7 +761,7 @@ declare dereferenceable(8) noalias ptr @alloc_writeonly() writeonly define void @test_sink_store_to_noalias_call_object_only_loop_may_not_execute1(i8 %n) writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_to_noalias_call_object_only_loop_may_not_execute1( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[A:%.*]] = call noalias dereferenceable(8) ptr @alloc_writeonly() @@ -801,7 +801,7 @@ } define void @test_sink_store_only_no_phi_needed() writeonly { -; CHECK: Function Attrs: writeonly +; CHECK: Function Attrs: memory(write) ; CHECK-LABEL: @test_sink_store_only_no_phi_needed( ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LOOP:%.*]] Index: llvm/test/Transforms/LICM/strlen.ll =================================================================== --- llvm/test/Transforms/LICM/strlen.ll +++ llvm/test/Transforms/LICM/strlen.ll @@ -13,7 +13,7 @@ } ; CHECK: declare i64 @strlen(ptr nocapture) #0 -; CHECK: attributes #0 = { argmemonly mustprogress nofree nounwind readonly willreturn } +; CHECK: attributes #0 = { mustprogress nofree nounwind willreturn memory(argmem: read) } declare i64 @strlen(ptr) Index: llvm/test/Transforms/RewriteStatepointsForGC/X86/intrinsic-attributes.ll =================================================================== --- llvm/test/Transforms/RewriteStatepointsForGC/X86/intrinsic-attributes.ll +++ llvm/test/Transforms/RewriteStatepointsForGC/X86/intrinsic-attributes.ll @@ -1,6 +1,6 @@ ; RUN: opt < %s -S -rewrite-statepoints-for-gc | FileCheck %s -; CHECK: Function Attrs: nounwind readnone +; CHECK: Function Attrs: nounwind memory(none) ; CHECK: declare i64 @llvm.x86.sse2.cvttsd2si64(<2 x double>) declare i64 @llvm.x86.sse2.cvttsd2si64(<2 x double>) Index: llvm/test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll =================================================================== --- llvm/test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll +++ llvm/test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll @@ -20,6 +20,6 @@ attributes #1 = { norecurse noimplicitfloat } ;. -; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind readnone } +; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind memory(none) } ; CHECK: attributes #[[ATTR1]] = { noimplicitfloat norecurse } ;. Index: llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll =================================================================== --- llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll +++ llvm/test/Transforms/SCCP/ipscp-drop-argmemonly.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals ; RUN: opt -passes=ipsccp -S %s | FileCheck %s ; Test cases to ensure argmemonly/inaccessiblemem_or_argmemonly attributes are @@ -10,8 +10,11 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop argmemonly. +;. +; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = internal global i32 0 +;. define internal void @ptrarg.1(i32* %arg, i32 %val) argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @ptrarg.1( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -37,7 +40,7 @@ ; Here only the non-pointer argument %val is replaced, no need ; to drop the argmemonly attribute. define internal void @ptrarg.2(i32* %arg, i32 %val) argmemonly nounwind { -; CHECK: Function Attrs: argmemonly nounwind +; CHECK: Function Attrs: nounwind memory(argmem: readwrite) ; CHECK-LABEL: @ptrarg.2( ; CHECK-NEXT: store i32 10, i32* [[ARG:%.*]], align 4 ; CHECK-NEXT: ret void @@ -59,7 +62,7 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.3(i32* %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite) ; CHECK-LABEL: @ptrarg.3( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -85,7 +88,7 @@ ; Here only the non-pointer argument %val is replaced, no need ; to drop the inaccessiblemem_or_argmemonly attribute. define internal void @ptrarg.4(i32* %arg, i32 %val) inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: inaccessiblemem_or_argmemonly nounwind +; CHECK: Function Attrs: nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) ; CHECK-LABEL: @ptrarg.4( ; CHECK-NEXT: store i32 10, i32* [[ARG:%.*]], align 4 ; CHECK-NEXT: ret void @@ -107,7 +110,7 @@ ; Here the pointer argument %arg will be replaced by a constant. We need to ; drop inaccessiblemem_or_argmemonly. define internal void @ptrarg.5(i32* %arg, i32 %val) argmemonly inaccessiblemem_or_argmemonly nounwind { -; CHECK: Function Attrs: nounwind +; CHECK: Function Attrs: nounwind memory(readwrite, inaccessiblemem: none) ; CHECK-LABEL: @ptrarg.5( ; CHECK-NEXT: store i32 10, i32* @g, align 4 ; CHECK-NEXT: ret void @@ -143,10 +146,10 @@ define i32 @caller.6.cs.attributes(i32 %n) { ; CHECK-LABEL: @caller.6.cs.attributes( ; CHECK-NEXT: store i32 1, i32* @g, align 4 -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] -; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) [[NOUNWIND:#[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR0]] +; CHECK-NEXT: tail call void @ptrarg.5(i32* @g, i32 10) #[[ATTR4:[0-9]+]] ; CHECK-NEXT: [[G_VAL:%.*]] = load i32, i32* @g, align 4 ; CHECK-NEXT: ret i32 [[G_VAL]] ; @@ -159,4 +162,10 @@ ret i32 %g.val } -; CHECK: [[NOUNWIND]] = { nounwind } +;. +; CHECK: attributes #[[ATTR0]] = { nounwind memory(readwrite, inaccessiblemem: none) } +; CHECK: attributes #[[ATTR1:[0-9]+]] = { nounwind memory(argmem: readwrite) } +; CHECK: attributes #[[ATTR2]] = { nounwind memory(readwrite) } +; CHECK: attributes #[[ATTR3:[0-9]+]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) } +; CHECK: attributes #[[ATTR4]] = { nounwind } +;. Index: llvm/test/Transforms/SCCP/remove-call-inst.ll =================================================================== --- llvm/test/Transforms/SCCP/remove-call-inst.ll +++ llvm/test/Transforms/SCCP/remove-call-inst.ll @@ -36,4 +36,4 @@ } ; CHECK: attributes #0 = { noreturn nounwind } -; CHECK: attributes #1 = { nounwind readnone willreturn } +; CHECK: attributes #1 = { nounwind willreturn memory(none) } Index: llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad-debuginfo.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad-debuginfo.ll +++ llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad-debuginfo.ll @@ -68,7 +68,7 @@ declare dso_local i32 @__gxx_personality_v0(...) ;. ; CHECK: attributes #[[ATTR0:[0-9]+]] = { noreturn } -; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind readnone speculatable willreturn } +; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } ;. ; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) ; CHECK: [[META1:![0-9]+]] = !DIFile(filename: "", directory: "/") Index: llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad.ll +++ llvm/test/Transforms/SimplifyCFG/X86/merge-compatible-invokes-of-landingpad.ll @@ -2475,5 +2475,5 @@ ;. ; CHECK: attributes #[[ATTR0:[0-9]+]] = { noreturn } ; CHECK: attributes #[[ATTR1]] = { nomerge } -; CHECK: attributes #[[ATTR2]] = { readnone } +; CHECK: attributes #[[ATTR2]] = { memory(none) } ;. Index: llvm/test/Transforms/SimplifyCFG/preserve-branchweights.ll =================================================================== --- llvm/test/Transforms/SimplifyCFG/preserve-branchweights.ll +++ llvm/test/Transforms/SimplifyCFG/preserve-branchweights.ll @@ -1133,7 +1133,7 @@ ;. ; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind uwtable } ; CHECK: attributes #[[ATTR1]] = { nounwind } -; CHECK: attributes #[[ATTR2:[0-9]+]] = { noredzone nounwind readnone ssp } +; CHECK: attributes #[[ATTR2:[0-9]+]] = { noredzone nounwind ssp memory(none) } ;. ; CHECK: [[PROF0]] = !{!"branch_weights", i32 5, i32 11} ; CHECK: [[PROF1]] = !{!"branch_weights", i32 1, i32 3} Index: llvm/utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- llvm/utils/TableGen/IntrinsicEmitter.cpp +++ llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -776,49 +776,58 @@ case CodeGenIntrinsic::NoMem: if (Intrinsic.hasSideEffects) break; - OS << " Attribute::get(C, Attribute::ReadNone),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::none()),\n"; break; case CodeGenIntrinsic::ReadArgMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Ref)),\n"; break; case CodeGenIntrinsic::ReadMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::readOnly()),\n"; break; case CodeGenIntrinsic::ReadInaccessibleMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Ref)),\n"; break; case CodeGenIntrinsic::ReadInaccessibleMemOrArgMem: - OS << " Attribute::get(C, Attribute::ReadOnly),\n"; - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Ref) | " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Ref)),\n"; + break; break; case CodeGenIntrinsic::WriteArgMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::WriteMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::writeOnly()),\n"; break; case CodeGenIntrinsic::WriteInaccessibleMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::WriteInaccessibleMemOrArgMem: - OS << " Attribute::get(C, Attribute::WriteOnly),\n"; - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(ModRefInfo::Mod) | " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::Mod)),\n"; break; case CodeGenIntrinsic::ReadWriteArgMem: - OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::argMemOnly(ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteInaccessibleMem: - OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(" + << "ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteInaccessibleMemOrArgMem: - OS << " Attribute::get(C, " - << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + OS << " Attribute::getWithModRefBehavior(C, " + << "FunctionModRefBehavior::inaccessibleMemOnly(" + << "ModRefInfo::ModRef) | FunctionModRefBehavior::argMemOnly(" + << "ModRefInfo::ModRef)),\n"; break; case CodeGenIntrinsic::ReadWriteMem: break; Index: llvm/utils/UpdateTestChecks/common.py =================================================================== --- llvm/utils/UpdateTestChecks/common.py +++ llvm/utils/UpdateTestChecks/common.py @@ -327,7 +327,7 @@ UNUSED_NOTE = 'NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:' OPT_FUNCTION_RE = re.compile( - r'^(\s*;\s*Function\sAttrs:\s(?P[\w\s]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.$-]+?)\s*' + r'^(\s*;\s*Function\sAttrs:\s(?P[\w\s():,]+?))?\s*define\s+(?:internal\s+)?[^@]*@(?P[\w.$-]+?)\s*' r'(?P\((\)|(.*?[\w.-]+?)\))[^{]*\{)\n(?P.*?)^\}$', flags=(re.M | re.S))