Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -1474,6 +1474,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 may write to but + does not read from memory. + + On an argument, this attribute indicates that the function may write to 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: llvm/trunk/include/llvm/Analysis/AliasAnalysis.h =================================================================== --- llvm/trunk/include/llvm/Analysis/AliasAnalysis.h +++ llvm/trunk/include/llvm/Analysis/AliasAnalysis.h @@ -151,6 +151,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_DoesNotReadMemory = 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 @@ -312,6 +319,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 doesNotReadMemory(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: llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h +++ llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h @@ -521,7 +521,8 @@ ATTR_KIND_NO_RECURSE = 48, ATTR_KIND_INACCESSIBLEMEM_ONLY = 49, ATTR_KIND_INACCESSIBLEMEM_OR_ARGMEMONLY = 50, - ATTR_KIND_ALLOC_SIZE = 51 + ATTR_KIND_ALLOC_SIZE = 51, + ATTR_KIND_WRITEONLY = 52 }; enum ComdatSelectionKindCodes { Index: llvm/trunk/include/llvm/IR/Attributes.td =================================================================== --- llvm/trunk/include/llvm/IR/Attributes.td +++ llvm/trunk/include/llvm/IR/Attributes.td @@ -167,6 +167,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: llvm/trunk/include/llvm/IR/CallSite.h =================================================================== --- llvm/trunk/include/llvm/IR/CallSite.h +++ llvm/trunk/include/llvm/IR/CallSite.h @@ -417,6 +417,14 @@ CALLSITE_DELEGATE_SETTER(setOnlyReadsMemory()); } + /// @brief Determine if the call does not access or only writes memory. + bool doesNotReadMemory() const { + CALLSITE_DELEGATE_GETTER(doesNotReadMemory()); + } + void setDoesNotReadMemory() { + CALLSITE_DELEGATE_SETTER(setDoesNotReadMemory()); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { Index: llvm/trunk/include/llvm/IR/Function.h =================================================================== --- llvm/trunk/include/llvm/IR/Function.h +++ llvm/trunk/include/llvm/IR/Function.h @@ -298,6 +298,14 @@ addFnAttr(Attribute::ReadOnly); } + /// @brief Determine if the function does not access or only writes memory. + bool doesNotReadMemory() const { + return doesNotAccessMemory() || hasFnAttribute(Attribute::WriteOnly); + } + void setDoesNotReadMemory() { + addFnAttr(Attribute::WriteOnly); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { Index: llvm/trunk/include/llvm/IR/Instructions.h =================================================================== --- llvm/trunk/include/llvm/IR/Instructions.h +++ llvm/trunk/include/llvm/IR/Instructions.h @@ -1739,6 +1739,14 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call does not access or only writes memory. + bool doesNotReadMemory() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::WriteOnly); + } + void setDoesNotReadMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly); + } + /// @brief Determine if the call can access memmory only using pointers based /// on its arguments. bool onlyAccessesArgMemory() const { @@ -3691,6 +3699,14 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call does not access or only writes memory. + bool doesNotReadMemory() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::WriteOnly); + } + void setDoesNotReadMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::WriteOnly); + } + /// @brief Determine if the call access memmory only using it's pointer /// arguments. bool onlyAccessesArgMemory() const { Index: llvm/trunk/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/trunk/include/llvm/IR/Intrinsics.td +++ llvm/trunk/include/llvm/IR/Intrinsics.td @@ -60,6 +60,12 @@ int ArgNo = argNo; } +// WriteOnly - The intrinsic does not read memory through the specified +// argument pointer. +class WriteOnly : IntrinsicProperty { + int ArgNo = argNo; +} + // ReadNone - The specified argument pointer is not dereferenced by the // intrinsic. class ReadNone : IntrinsicProperty { @@ -349,7 +355,7 @@ [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty], [IntrArgMemOnly, 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], @@ -358,7 +364,7 @@ def int_memset : Intrinsic<[], [llvm_anyptr_ty, llvm_i8_ty, llvm_anyint_ty, llvm_i32_ty, llvm_i1_ty], - [IntrArgMemOnly, NoCapture<0>]>; + [IntrArgMemOnly, NoCapture<0>, WriteOnly<0>]>; let IntrProperties = [IntrNoMem] in { def int_fma : Intrinsic<[llvm_anyfloat_ty], Index: llvm/trunk/lib/Analysis/AliasAnalysis.cpp =================================================================== --- llvm/trunk/lib/Analysis/AliasAnalysis.cpp +++ llvm/trunk/lib/Analysis/AliasAnalysis.cpp @@ -142,6 +142,8 @@ if (onlyReadsMemory(MRB)) Result = ModRefInfo(Result & MRI_Ref); + else if (doesNotReadMemory(MRB)) + Result = ModRefInfo(Result & MRI_Mod); if (onlyAccessesArgPointees(MRB)) { bool DoesAlias = false; @@ -207,6 +209,8 @@ // from CS1 reading memory written by CS2. if (onlyReadsMemory(CS1B)) Result = ModRefInfo(Result & MRI_Ref); + else if (doesNotReadMemory(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 Index: llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp +++ llvm/trunk/lib/Analysis/BasicAliasAnalysis.cpp @@ -563,6 +563,8 @@ // than that. if (CS.onlyReadsMemory()) Min = FMRB_OnlyReadsMemory; + else if (CS.doesNotReadMemory()) + Min = FMRB_DoesNotReadMemory; if (CS.onlyAccessesArgMemory()) Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees); @@ -590,6 +592,8 @@ // If the function declares it only reads memory, go with that. if (F->onlyReadsMemory()) Min = FMRB_OnlyReadsMemory; + else if (F->doesNotReadMemory()) + Min = FMRB_DoesNotReadMemory; if (F->onlyAccessesArgMemory()) Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees); @@ -597,32 +601,18 @@ 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. + // whenever possible. + // FIXME Consider handling this in InferFunctionAttr.cpp together with other + // attributes. LibFunc::Func F; if (CS.getCalledFunction() && TLI.getLibFunc(*CS.getCalledFunction(), F) && F == LibFunc::memset_pattern16 && TLI.has(F)) @@ -639,8 +629,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 builtin intrinsics and target library functions. if (isWriteOnlyParam(CS, ArgIdx, TLI)) return MRI_Mod; Index: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/lib/AsmParser/LLLexer.cpp @@ -660,6 +660,7 @@ KEYWORD(swifterror); KEYWORD(swiftself); KEYWORD(uwtable); + KEYWORD(writeonly); KEYWORD(zeroext); KEYWORD(type); Index: llvm/trunk/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.cpp +++ llvm/trunk/lib/AsmParser/LLParser.cpp @@ -1098,6 +1098,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: @@ -1394,6 +1395,7 @@ case lltok::kw_sret: B.addAttribute(Attribute::StructRet); break; case lltok::kw_swifterror: B.addAttribute(Attribute::SwiftError); 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: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -205,6 +205,7 @@ kw_swifterror, kw_swiftself, kw_uwtable, + kw_writeonly, kw_zeroext, kw_type, Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1464,6 +1464,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: llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -667,6 +667,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: llvm/trunk/lib/IR/Attributes.cpp =================================================================== --- llvm/trunk/lib/IR/Attributes.cpp +++ llvm/trunk/lib/IR/Attributes.cpp @@ -292,6 +292,8 @@ return "readnone"; if (hasAttribute(Attribute::ReadOnly)) return "readonly"; + if (hasAttribute(Attribute::WriteOnly)) + return "writeonly"; if (hasAttribute(Attribute::Returned)) return "returned"; if (hasAttribute(Attribute::ReturnsTwice)) @@ -516,6 +518,7 @@ case Attribute::InaccessibleMemOrArgMemOnly: return 1ULL << 50; case Attribute::SwiftSelf: return 1ULL << 51; case Attribute::SwiftError: return 1ULL << 52; + case Attribute::WriteOnly: return 1ULL << 53; case Attribute::Dereferenceable: llvm_unreachable("dereferenceable attribute not supported in raw format"); break; Index: llvm/trunk/lib/IR/Verifier.cpp =================================================================== --- llvm/trunk/lib/IR/Verifier.cpp +++ llvm/trunk/lib/IR/Verifier.cpp @@ -1309,6 +1309,7 @@ return; } } else if (I->getKindAsEnum() == Attribute::ReadOnly || + I->getKindAsEnum() == Attribute::WriteOnly || I->getKindAsEnum() == Attribute::ReadNone) { if (Idx == 0) { CheckFailed("Attribute '" + I->getAsString() + @@ -1382,6 +1383,18 @@ "'readnone and readonly' are incompatible!", V); + Assert(!(Attrs.hasAttribute(Idx, Attribute::ReadNone) && + Attrs.hasAttribute(Idx, Attribute::WriteOnly)), + "Attributes " + "'readnone and writeonly' are incompatible!", + V); + + Assert(!(Attrs.hasAttribute(Idx, Attribute::ReadOnly) && + Attrs.hasAttribute(Idx, Attribute::WriteOnly)), + "Attributes " + "'readonly and writeonly' are incompatible!", + V); + Assert(!(Attrs.hasAttribute(Idx, Attribute::NoInline) && Attrs.hasAttribute(Idx, Attribute::AlwaysInline)), "Attributes " @@ -1499,6 +1512,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: llvm/trunk/test/Analysis/BasicAA/cs-cs.ll =================================================================== --- llvm/trunk/test/Analysis/BasicAA/cs-cs.ll +++ llvm/trunk/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) #5 +; CHECK: NoModRef: Ptr: i8* %q <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 ; 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) #5 +; CHECK: NoModRef: Ptr: i8* %q <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 +; CHECK: NoModRef: %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 <-> 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) #5 <-> %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 +; 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) #5 +; 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) #5 +; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 <-> %a = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 +; CHECK: NoModRef: %b = call <8 x i16> @llvm.arm.neon.vld1.v8i16.p0i8(i8* %p, i32 16) #5 <-> 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,23 @@ ; 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 } +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 = { noinline nounwind readonly } -attributes #3 = { nounwind ssp } -attributes #4 = { nounwind } +attributes #3 = { noinline nounwind writeonly } +attributes #4 = { nounwind ssp } +attributes #5 = { nounwind } Index: llvm/trunk/test/Bindings/llvm-c/invalid-bitcode.test =================================================================== --- llvm/trunk/test/Bindings/llvm-c/invalid-bitcode.test +++ llvm/trunk/test/Bindings/llvm-c/invalid-bitcode.test @@ -1,13 +1,13 @@ ; RUN: not llvm-c-test --module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s ; RUN: not llvm-c-test --lazy-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s -CHECK: Error parsing bitcode: Unknown attribute kind (52) +CHECK: Error parsing bitcode: Unknown attribute kind (63) ; RUN: not llvm-c-test --new-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=NEW %s ; RUN: not llvm-c-test --lazy-new-module-dump < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=NEW %s -NEW: Error with new bitcode parser: Unknown attribute kind (52) +NEW: Error with new bitcode parser: Unknown attribute kind (63) ; RUN: llvm-c-test --test-diagnostic-handler < %S/Inputs/invalid.ll.bc 2>&1 | FileCheck --check-prefix=DIAGNOSTIC %s Index: llvm/trunk/test/Bitcode/attributes.ll =================================================================== --- llvm/trunk/test/Bitcode/attributes.ll +++ llvm/trunk/test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #32 +; CHECK: call void @nobuiltin() #33 ret void; } @@ -328,6 +328,12 @@ ret i8* null } +; CHECK: define void @f56() #32 +define void @f56() writeonly +{ + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -360,4 +366,5 @@ ; CHECK: attributes #29 = { inaccessiblemem_or_argmemonly } ; CHECK: attributes #30 = { allocsize(0) } ; CHECK: attributes #31 = { allocsize(0,1) } -; CHECK: attributes #32 = { nobuiltin } +; CHECK: attributes #32 = { writeonly } +; CHECK: attributes #33 = { nobuiltin } Index: llvm/trunk/test/Bitcode/compatibility.ll =================================================================== --- llvm/trunk/test/Bitcode/compatibility.ll +++ llvm/trunk/test/Bitcode/compatibility.ll @@ -1244,7 +1244,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 @@ -1590,6 +1590,8 @@ ret void } +declare void @f.writeonly() writeonly +; CHECK: declare void @f.writeonly() #39 ; CHECK: attributes #0 = { alignstack=4 } ; CHECK: attributes #1 = { alignstack=8 } @@ -1630,7 +1632,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: llvm/trunk/test/Bitcode/invalid.ll =================================================================== --- llvm/trunk/test/Bitcode/invalid.ll +++ llvm/trunk/test/Bitcode/invalid.ll @@ -1,6 +1,6 @@ ; RUN: not llvm-dis < %s.bc 2>&1 | FileCheck %s -; CHECK: llvm-dis{{(\.EXE|\.exe)?}}: error: Unknown attribute kind (52) +; CHECK: llvm-dis{{(\.EXE|\.exe)?}}: error: Unknown attribute kind (63) ; invalid.ll.bc has an invalid attribute number. ; The test checks that LLVM reports the error and doesn't access freed memory Index: llvm/trunk/test/LTO/X86/invalid.ll =================================================================== --- llvm/trunk/test/LTO/X86/invalid.ll +++ llvm/trunk/test/LTO/X86/invalid.ll @@ -1,4 +1,4 @@ ; RUN: not llvm-lto %S/Inputs/invalid.ll.bc 2>&1 | FileCheck %s -; CHECK: llvm-lto{{.*}}: error loading file '{{.*}}/Inputs/invalid.ll.bc': Unknown attribute kind (52) +; CHECK: llvm-lto{{.*}}: error loading file '{{.*}}/Inputs/invalid.ll.bc': Unknown attribute kind (63) Index: llvm/trunk/test/Verifier/writeonly.ll =================================================================== --- llvm/trunk/test/Verifier/writeonly.ll +++ llvm/trunk/test/Verifier/writeonly.ll @@ -0,0 +1,13 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s + +declare void @a() readnone writeonly +; CHECK: Attributes {{.*}} are incompatible + +declare void @b() readonly writeonly +; CHECK: Attributes {{.*}} are incompatible + +declare void @c(i32* readnone writeonly %p) +; CHECK: Attributes {{.*}} are incompatible + +declare void @d(i32* readonly writeonly %p) +; CHECK: Attributes {{.*}} are incompatible Index: llvm/trunk/utils/TableGen/CodeGenIntrinsics.h =================================================================== --- llvm/trunk/utils/TableGen/CodeGenIntrinsics.h +++ llvm/trunk/utils/TableGen/CodeGenIntrinsics.h @@ -112,6 +112,7 @@ enum ArgAttribute { NoCapture, ReadOnly, + WriteOnly, ReadNone }; std::vector > ArgumentAttributes; Index: llvm/trunk/utils/TableGen/CodeGenTarget.cpp =================================================================== --- llvm/trunk/utils/TableGen/CodeGenTarget.cpp +++ llvm/trunk/utils/TableGen/CodeGenTarget.cpp @@ -595,6 +595,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: llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- llvm/trunk/utils/TableGen/IntrinsicEmitter.cpp +++ llvm/trunk/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 << ","; @@ -617,12 +623,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; }