Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -1532,6 +1532,18 @@ If a function reads from a writeonly pointer argument, the behavior is undefined. +``writable`` + This attribute indicates that any memory location based on this pointer + argument that can be loaded from, can also be stored to without trapping. + + If the argument is also ``dereferenceable(N)``, the first ``N`` bytes of + the pointer are (non-atomically) loaded and stored back to the pointer on + entry to the function. + + The last rule is needed to ensure a well-defined interaction between + ``writable`` and ``noalias``, and to ensure that spurious stores can be + introduced without data races. + .. _gc: Garbage Collector Strategy Names Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -713,6 +713,7 @@ ATTR_KIND_SKIP_PROFILE = 85, ATTR_KIND_MEMORY = 86, ATTR_KIND_NOFPCLASS = 87, + ATTR_KIND_WRITABLE = 88, }; enum ComdatSelectionKindCodes { Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ llvm/include/llvm/IR/Attributes.td @@ -300,6 +300,9 @@ /// Function always comes back to callsite. def WillReturn : EnumAttr<"willreturn", [FnAttr]>; +/// Pointer argument is writable. +def Writable : EnumAttr<"writable", [ParamAttr]>; + /// Function only writes to memory. def WriteOnly : EnumAttr<"writeonly", [ParamAttr]>; Index: llvm/lib/Analysis/AliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/AliasAnalysis.cpp +++ llvm/lib/Analysis/AliasAnalysis.cpp @@ -917,9 +917,8 @@ if (isa(Object)) return true; - // TODO: Also handle sret. if (auto *A = dyn_cast(Object)) - return A->hasByValAttr(); + return A->hasByValAttr() || A->hasAttribute(Attribute::Writable); // TODO: Noalias shouldn't imply writability, this should check for an // allocator function instead. Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2060,6 +2060,8 @@ return Attribute::Hot; case bitc::ATTR_KIND_PRESPLIT_COROUTINE: return Attribute::PresplitCoroutine; + case bitc::ATTR_KIND_WRITABLE: + return Attribute::Writable; } } Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -809,6 +809,8 @@ return bitc::ATTR_KIND_MUSTPROGRESS; case Attribute::PresplitCoroutine: return bitc::ATTR_KIND_PRESPLIT_COROUTINE; + case Attribute::Writable: + return bitc::ATTR_KIND_WRITABLE; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ llvm/lib/IR/Attributes.cpp @@ -1961,7 +1961,8 @@ .addAttribute(Attribute::ReadNone) .addAttribute(Attribute::ReadOnly) .addAttribute(Attribute::Dereferenceable) - .addAttribute(Attribute::DereferenceableOrNull); + .addAttribute(Attribute::DereferenceableOrNull) + .addAttribute(Attribute::Writable); if (ASK & ASK_UNSAFE_TO_DROP) Incompatible.addAttribute(Attribute::Nest) .addAttribute(Attribute::SwiftError) Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -990,6 +990,7 @@ case Attribute::ImmArg: case Attribute::ByRef: case Attribute::WriteOnly: + case Attribute::Writable: // These are not really attributes. case Attribute::None: case Attribute::EndAttrKinds: Index: llvm/test/Bitcode/attributes.ll =================================================================== --- llvm/test/Bitcode/attributes.ll +++ llvm/test/Bitcode/attributes.ll @@ -511,6 +511,11 @@ ; CHECK: define void @f88() [[SKIPPROFILE:#[0-9]+]] define void @f88() skipprofile { ret void } +; CHECK: define void @f89(ptr writable %p) +define void @f89(ptr writable %p) { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { memory(none) } Index: llvm/test/Transforms/LICM/scalar-promote.ll =================================================================== --- llvm/test/Transforms/LICM/scalar-promote.ll +++ llvm/test/Transforms/LICM/scalar-promote.ll @@ -885,9 +885,8 @@ ret void } -; TODO: The store can be promoted, as sret memory is writable. -define void @sret_cond_store(ptr sret(i32) noalias %ptr) { -; CHECK-LABEL: @sret_cond_store( +define void @writable_cond_store(ptr sret(i32) noalias writable %ptr) { +; CHECK-LABEL: @writable_cond_store( ; CHECK-NEXT: [[PTR_PROMOTED:%.*]] = load i32, ptr [[PTR:%.*]], align 4 ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: @@ -896,9 +895,10 @@ ; CHECK-NEXT: br i1 [[C]], label [[LOOP_LATCH]], label [[EXIT:%.*]] ; CHECK: loop.latch: ; CHECK-NEXT: [[V_INC]] = add i32 [[V_INC1]], 1 -; CHECK-NEXT: store i32 [[V_INC]], ptr [[PTR]], align 4 ; CHECK-NEXT: br label [[LOOP]] ; CHECK: exit: +; CHECK-NEXT: [[V_INC1_LCSSA:%.*]] = phi i32 [ [[V_INC1]], [[LOOP]] ] +; CHECK-NEXT: store i32 [[V_INC1_LCSSA]], ptr [[PTR]], align 4 ; CHECK-NEXT: ret void ; br label %loop