Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1381,6 +1381,13 @@ On an argument, this attribute indicates that the function does not write through this pointer argument, even though it may write to the memory that the pointer points to. +``writeonly`` + On a function, this attribute indicates that the function writes to but + does not read from memory. + + On an argument, this attribute indicates that the function writes but does + not read through this pointer argument (even though it may read from the + memory that the pointer points to). ``argmemonly`` This attribute indicates that the only memory accesses inside function are loads and stores from objects pointed to by its pointer-typed arguments, Index: include/llvm/Analysis/AliasAnalysis.h =================================================================== --- include/llvm/Analysis/AliasAnalysis.h +++ include/llvm/Analysis/AliasAnalysis.h @@ -152,6 +152,13 @@ /// This property corresponds to the IntrReadMem LLVM intrinsic flag. FMRB_OnlyReadsMemory = FMRL_Anywhere | MRI_Ref, + // This function does not read from memory anywhere, but may write to any + // memory location. + // + // This property corresponds to the LLVM IR 'writeonly' attribute. + // This property corresponds to the IntrWriteMem LLVM intrinsic flag. + FMRB_OnlyWritesMemory = FMRL_Anywhere | MRI_Mod, + /// This indicates that the function could not be classified into one of the /// behaviors above. FMRB_UnknownModRefBehavior = FMRL_Anywhere | MRI_ModRef @@ -313,6 +320,12 @@ return !(MRB & MRI_Mod); } + /// Checks if functions with the specified behavior are known to only write + /// memory (or not access memory at all). + static bool onlyWritesMemory(FunctionModRefBehavior MRB) { + return !(MRB & MRI_Ref); + } + /// Checks if functions with the specified behavior are known to read and /// write at most from objects pointed to by their pointer-typed arguments /// (with arbitrary offsets). Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -510,7 +510,8 @@ ATTR_KIND_SWIFT_ERROR = 47, ATTR_KIND_NO_RECURSE = 48, ATTR_KIND_INACCESSIBLEMEM_ONLY = 49, - ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50 + ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50, + ATTR_KIND_WRITEONLY = 51 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.td =================================================================== --- include/llvm/IR/Attributes.td +++ include/llvm/IR/Attributes.td @@ -160,6 +160,9 @@ /// Function must be in a unwind table. def UWTable : EnumAttr<"uwtable">; +/// Function only writes to memory. +def WriteOnly : EnumAttr<"writeonly">; + /// Zero extended before/after call. def ZExt : EnumAttr<"zeroext">; Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -385,6 +385,14 @@ CALLSITE_DELEGATE_SETTER(setOnlyReadsMemory()); } + /// @brief Determine if the call does not access or only writes memory. + bool onlyWritesMemory() const { + CALLSITE_DELEGATE_GETTER(onlyWritesMemory()); + } + void setOnlyWritesMemory() { + CALLSITE_DELEGATE_SETTER(setOnlyWritesMemory()); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -288,6 +288,14 @@ addFnAttr(Attribute::ReadOnly); } + /// @brief Determine if the function does not access or only reads memory. + bool onlyWritesMemory() const { + return doesNotAccessMemory() || hasFnAttribute(Attribute::WriteOnly); + } + void setOnlyWritesMemory() { + addFnAttr(Attribute::WriteOnly); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { Index: include/llvm/IR/Instructions.h =================================================================== --- include/llvm/IR/Instructions.h +++ include/llvm/IR/Instructions.h @@ -1713,6 +1713,14 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call does not access or only writes memory. + bool onlyWritesMemory() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::WriteOnly); + } + void setOnlyWritesMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { @@ -3641,6 +3649,14 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call does not access or only writes memory. + bool onlyWritesMemory() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::WriteOnly); + } + void setOnlyWritesMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly); + } + /// @brief Determine if the call access memmory only using it's pointer /// arguments. bool onlyAccessesArgMemory() const { Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -73,6 +73,12 @@ int ArgNo = argNo; } +// WriteOnly - The specified argument pointer is not read from through the +// pointer by the intrinsic. +class WriteOnly : IntrinsicProperty { + int ArgNo = argNo; +} + // ReadNone - The specified argument pointer is not dereferenced by the // intrinsic. class ReadNone : IntrinsicProperty { @@ -360,16 +366,16 @@ [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty], [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>, - ReadOnly<1>]>; + WriteOnly<0>, ReadOnly<1>]>; def int_memmove : Intrinsic<[], [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty], [IntrReadWriteArgMem, NoCapture<0>, NoCapture<1>, - ReadOnly<1>]>; + WriteOnly<0>, ReadOnly<1>]>; def int_memset : Intrinsic<[], [llvm_anyptr_ty, llvm_i8_ty, llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty], - [IntrReadWriteArgMem, NoCapture<0>]>; + [IntrWriteArgMem, NoCapture<0>]>; let IntrProperties = [IntrNoMem] in { def int_fma : Intrinsic<[llvm_anyfloat_ty], Index: lib/Analysis/AliasAnalysis.cpp =================================================================== --- lib/Analysis/AliasAnalysis.cpp +++ lib/Analysis/AliasAnalysis.cpp @@ -143,6 +143,9 @@ if (onlyReadsMemory(MRB)) Result = ModRefInfo(Result & MRI_Ref); + if (onlyWritesMemory(MRB)) + Result = ModRefInfo(Result & MRI_Mod); + if (onlyAccessesArgPointees(MRB)) { bool DoesAlias = false; ModRefInfo AllArgsMask = MRI_NoModRef; @@ -208,6 +211,9 @@ if (onlyReadsMemory(CS1B)) Result = ModRefInfo(Result & MRI_Ref); + if (onlyWritesMemory(CS1B)) + Result = ModRefInfo(Result & MRI_Mod); + // If CS2 only access memory through arguments, accumulate the mod/ref // information from CS1's references to the memory referenced by // CS2's arguments. Index: lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- lib/Analysis/BasicAliasAnalysis.cpp +++ lib/Analysis/BasicAliasAnalysis.cpp @@ -570,6 +570,9 @@ if (CS.onlyReadsMemory()) Min = FMRB_OnlyReadsMemory; + if (CS.onlyWritesMemory()) + Min = FMRB_OnlyWritesMemory; + if (CS.onlyAccessesArgMemory()) Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees); @@ -597,38 +600,22 @@ if (F->onlyReadsMemory()) Min = FMRB_OnlyReadsMemory; + if (F->onlyWritesMemory()) + Min = FMRB_OnlyWritesMemory; + if (F->onlyAccessesArgMemory()) Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees); return Min; } -/// Returns true if this is a writeonly (i.e Mod only) parameter. Currently, -/// we don't have a writeonly attribute, so this only knows about builtin -/// intrinsics and target library functions. We could consider adding a -/// writeonly attribute in the future and moving all of these facts to either -/// Intrinsics.td or InferFunctionAttr.cpp +/// Returns true if this is a writeonly (i.e Mod only) parameter. static bool isWriteOnlyParam(ImmutableCallSite CS, unsigned ArgIdx, const TargetLibraryInfo &TLI) { - if (const IntrinsicInst *II = dyn_cast(CS.getInstruction())) - switch (II->getIntrinsicID()) { - default: - break; - case Intrinsic::memset: - case Intrinsic::memcpy: - case Intrinsic::memmove: - // We don't currently have a writeonly attribute. All other properties - // of these intrinsics are nicely described via attributes in - // Intrinsics.td and handled generically. - if (ArgIdx == 0) - return true; - } + if (CS.paramHasAttr(ArgIdx + 1, Attribute::WriteOnly)) + return true; - // We can bound the aliasing properties of memset_pattern16 just as we can - // for memcpy/memset. This is particularly important because the - // LoopIdiomRecognizer likes to turn loops into calls to memset_pattern16 - // whenever possible. Note that all but the missing writeonly attribute are - // handled via InferFunctionAttr. + // FIXME Consider handling this in InferFunctionAttr.cpp if (CS.getCalledFunction() && isMemsetPattern16(CS.getCalledFunction(), TLI)) if (ArgIdx == 0) return true; @@ -643,8 +630,7 @@ ModRefInfo BasicAAResult::getArgModRefInfo(ImmutableCallSite CS, unsigned ArgIdx) { - // Emulate the missing writeonly attribute by checking for known builtin - // intrinsics and target library functions. + // Checking for known target library functions. if (isWriteOnlyParam(CS, ArgIdx, TLI)) return MRI_Mod; Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -650,6 +650,7 @@ KEYWORD(sanitize_memory); KEYWORD(swiftself); KEYWORD(uwtable); + KEYWORD(writeonly); KEYWORD(zeroext); KEYWORD(type); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -1069,6 +1069,7 @@ case lltok::kw_sanitize_memory: B.addAttribute(Attribute::SanitizeMemory); break; case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; + case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break; // Error handling. case lltok::kw_inreg: @@ -1363,6 +1364,7 @@ case lltok::kw_signext: B.addAttribute(Attribute::SExt); break; case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break; case lltok::kw_swiftself: B.addAttribute(Attribute::SwiftSelf); break; + case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break; case lltok::kw_zeroext: B.addAttribute(Attribute::ZExt); break; case lltok::kw_alignstack: Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -154,6 +154,7 @@ kw_sanitize_memory, kw_swiftself, kw_uwtable, + kw_writeonly, kw_zeroext, kw_type, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1321,6 +1321,8 @@ return Attribute::SwiftSelf; case bitc::ATTR_KIND_UW_TABLE: return Attribute::UWTable; + case bitc::ATTR_KIND_WRITEONLY: + return Attribute::WriteOnly; case bitc::ATTR_KIND_Z_EXT: return Attribute::ZExt; } Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -265,6 +265,8 @@ return bitc::ATTR_KIND_SWIFT_SELF; case Attribute::UWTable: return bitc::ATTR_KIND_UW_TABLE; + case Attribute::WriteOnly: + return bitc::ATTR_KIND_WRITEONLY; case Attribute::ZExt: return bitc::ATTR_KIND_Z_EXT; case Attribute::EndAttrKinds: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -269,6 +269,8 @@ return "sanitize_memory"; if (hasAttribute(Attribute::UWTable)) return "uwtable"; + if (hasAttribute(Attribute::WriteOnly)) + return "writeonly"; if (hasAttribute(Attribute::ZExt)) return "zeroext"; if (hasAttribute(Attribute::Cold)) @@ -451,6 +453,7 @@ case Attribute::InaccessibleMemOnly: return 1ULL << 49; case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50; case Attribute::SwiftSelf: return 1ULL << 51; + case Attribute::WriteOnly: return 1ULL << 52; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1318,6 +1318,7 @@ return; } } else if (I->getKindAsEnum() == Attribute::ReadOnly || + I->getKindAsEnum() == Attribute::WriteOnly || I->getKindAsEnum() == Attribute::ReadNone) { if (Idx == 0) { CheckFailed("Attribute '" + I->getAsString() + @@ -1491,6 +1492,16 @@ Assert( !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone) && + Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly)), + "Attributes 'readnone and writeonly' are incompatible!", V); + + Assert( + !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly) && + Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly)), + "Attributes 'readonly and writeonly' are incompatible!", V); + + Assert( + !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone) && Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::InaccessibleMemOrArgMemOnly)), "Attributes 'readnone and inaccessiblemem_or_argmemonly' are incompatible!", V); Index: test/Analysis/BasicAA/cs-cs.ll =================================================================== --- test/Analysis/BasicAA/cs-cs.ll +++ test/Analysis/BasicAA/cs-cs.ll @@ -9,6 +9,7 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i32, i1) nounwind declare void @a_readonly_func(i8 *) noinline nounwind readonly +declare void @a_writeonly_func(i8 *) noinline nounwind writeonly define <8 x i16> @test1(i8* %p, <8 x i16> %y) { entry: @@ -22,18 +23,18 @@ ; CHECK-LABEL: Function: test1: ; CHECK: NoAlias: i8* %p, i8* %q -; CHECK: Just Ref: Ptr: i8* %p <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: Ptr: i8* %q <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 +; CHECK: Just Ref: Ptr: i8* %p <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: Ptr: i8* %q <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 ; CHECK: NoModRef: Ptr: i8* %p <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) ; CHECK: Both ModRef: Ptr: i8* %q <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) -; CHECK: Just Ref: Ptr: i8* %p <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: Ptr: i8* %q <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) -; CHECK: NoModRef: %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 -; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #4 <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) +; CHECK: Just Ref: Ptr: i8* %p <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: Ptr: i8* %q <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) +; CHECK: NoModRef: %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 +; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #6 <-> call void @llvm.arm.neon.vst1.p0i8.v8i16(i8* %q, <8 x i16> %y, i32 16) } define void @test2(i8* %P, i8* %Q) nounwind ssp { @@ -233,9 +234,24 @@ ; CHECK: Just Ref: call void @a_readonly_func(i8* %P) <-> call void @llvm.memset.p0i8.i64(i8* %P, i8 -51, i64 32, i32 8, i1 false) } -attributes #0 = { nounwind readonly argmemonly } -attributes #1 = { nounwind argmemonly } -attributes #2 = { noinline nounwind readonly } -attributes #3 = { nounwind ssp } -attributes #4 = { nounwind } +define void @test7(i8* %P) nounwind ssp { + call void @a_writeonly_func(i8* %P) + call void @a_readonly_func(i8* %P) + ret void + +; CHECK-LABEL: Function: test7: + +; CHECK: Just Mod: Ptr: i8* %P <-> call void @a_writeonly_func(i8* %P) +; CHECK: Just Ref: Ptr: i8* %P <-> call void @a_readonly_func(i8* %P) +; CHECK: Just Mod: call void @a_writeonly_func(i8* %P) <-> call void @a_readonly_func(i8* %P) +; CHECK: Just Ref: call void @a_readonly_func(i8* %P) <-> call void @a_writeonly_func(i8* %P) +} + +attributes #0 = { argmemonly nounwind readonly } +attributes #1 = { argmemonly nounwind } +attributes #2 = { argmemonly nounwind writeonly } +attributes #3 = { noinline nounwind readonly } +attributes #4 = { noinline nounwind writeonly } +attributes #5 = { nounwind ssp } +attributes #6 = { nounwind } Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #30 +; CHECK: call void @nobuiltin() #31 ret void; } @@ -293,6 +293,12 @@ ret void; } +define void @f51() writeonly +; CHECK: define void @f51() #30 +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -323,4 +329,5 @@ ; CHECK: attributes #27 = { norecurse } ; CHECK: attributes #28 = { inaccessiblememonly } ; CHECK: attributes #29 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #30 = { nobuiltin } +; CHECK: attributes #30 = { writeonly } +; CHECK: attributes #31 = { nobuiltin } Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -1173,7 +1173,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #39 + ; CHECK: call void @f.nobuiltin() #40 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1519,6 +1519,8 @@ ret void } +declare void @f.writeonly() writeonly +; CHECK: declare void @f.writeonly() #39 ; CHECK: attributes #0 = { alignstack=4 } ; CHECK: attributes #1 = { alignstack=8 } @@ -1559,7 +1561,8 @@ ; CHECK: attributes #36 = { argmemonly nounwind readonly } ; CHECK: attributes #37 = { argmemonly nounwind } ; CHECK: attributes #38 = { nounwind readonly } -; CHECK: attributes #39 = { builtin } +; CHECK: attributes #39 = { writeonly } +; CHECK: attributes #40 = { builtin } ;; Metadata Index: test/Transforms/ObjCARC/nested.ll =================================================================== --- test/Transforms/ObjCARC/nested.ll +++ test/Transforms/ObjCARC/nested.ll @@ -820,6 +820,6 @@ } -; CHECK: attributes #0 = { argmemonly nounwind } +; CHECK: attributes #0 = { argmemonly nounwind writeonly } ; CHECK: attributes #1 = { nonlazybind } ; CHECK: attributes [[NUW]] = { nounwind } Index: utils/TableGen/CodeGenIntrinsics.h =================================================================== --- utils/TableGen/CodeGenIntrinsics.h +++ utils/TableGen/CodeGenIntrinsics.h @@ -98,6 +98,7 @@ enum ArgAttribute { NoCapture, ReadOnly, + WriteOnly, ReadNone }; std::vector > ArgumentAttributes; Index: utils/TableGen/CodeGenTarget.cpp =================================================================== --- utils/TableGen/CodeGenTarget.cpp +++ utils/TableGen/CodeGenTarget.cpp @@ -599,6 +599,9 @@ } else if (Property->isSubClassOf("ReadOnly")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadOnly)); + } else if (Property->isSubClassOf("WriteOnly")) { + unsigned ArgNo = Property->getValueAsInt("ArgNo"); + ArgumentAttributes.push_back(std::make_pair(ArgNo, WriteOnly)); } else if (Property->isSubClassOf("ReadNone")) { unsigned ArgNo = Property->getValueAsInt("ArgNo"); ArgumentAttributes.push_back(std::make_pair(ArgNo, ReadNone)); Index: utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- utils/TableGen/IntrinsicEmitter.cpp +++ utils/TableGen/IntrinsicEmitter.cpp @@ -554,6 +554,12 @@ OS << "Attribute::ReadOnly"; addComma = true; break; + case CodeGenIntrinsic::WriteOnly: + if (addComma) + OS << ","; + OS << "Attribute::WriteOnly"; + addComma = true; + break; case CodeGenIntrinsic::ReadNone: if (addComma) OS << ","; @@ -618,12 +624,21 @@ OS << "Attribute::ReadOnly"; break; case CodeGenIntrinsic::WriteArgMem: - case CodeGenIntrinsic::ReadWriteArgMem: if (addComma) OS << ","; + OS << "Attribute::WriteOnly,"; OS << "Attribute::ArgMemOnly"; break; case CodeGenIntrinsic::WriteMem: + if (addComma) + OS << ","; + OS << "Attribute::WriteOnly"; + break; + case CodeGenIntrinsic::ReadWriteArgMem: + if (addComma) + OS << ","; + OS << "Attribute::ArgMemOnly"; + break; case CodeGenIntrinsic::ReadWriteMem: break; }