diff options
author | Nicolai Haehnle <nhaehnle@gmail.com> | 2016-07-04 08:01:29 +0000 |
---|---|---|
committer | Nicolai Haehnle <nhaehnle@gmail.com> | 2016-07-04 08:01:29 +0000 |
commit | b07f5404567484379450db2135cdc56ba08ebb00 (patch) | |
tree | 85ada6277883420d7c9b8aa5b9fce14cd59359cd | |
parent | 061feda889306e89211087d63c4ebf4ef91a8446 (diff) |
Add writeonly IR attribute
Summary:
This complements the earlier addition of IntrWriteMem and IntrWriteArgMem
LLVM intrinsic properties, see D18291.
Also start using the attribute for memset, memcpy, and memmove intrinsics,
and remove their special-casing in BasicAliasAnalysis.
Reviewers: reames, joker.eph
Subscribers: joker.eph, llvm-commits
Differential Revision: http://reviews.llvm.org/D18714
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@274485 91177308-0d34-0410-b5e6-96231b3b80d8
30 files changed, 194 insertions, 48 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 4dbdb9d6b96..70c24050310 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -1474,6 +1474,13 @@ example: 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, diff --git a/include/llvm/Analysis/AliasAnalysis.h b/include/llvm/Analysis/AliasAnalysis.h index 417010116d3..d6308b7073a 100644 --- a/include/llvm/Analysis/AliasAnalysis.h +++ b/include/llvm/Analysis/AliasAnalysis.h @@ -151,6 +151,13 @@ enum FunctionModRefBehavior { /// 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 @@ public: 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). diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index d2a6a195411..52d4f01b798 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -521,7 +521,8 @@ enum AttributeKindCodes { 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 { diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td index ebad03bf6bd..7b63638a3f6 100644 --- a/include/llvm/IR/Attributes.td +++ b/include/llvm/IR/Attributes.td @@ -167,6 +167,9 @@ def SwiftSelf : EnumAttr<"swiftself">; /// 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">; diff --git a/include/llvm/IR/CallSite.h b/include/llvm/IR/CallSite.h index 23f04747889..3229d86651a 100644 --- a/include/llvm/IR/CallSite.h +++ b/include/llvm/IR/CallSite.h @@ -417,6 +417,14 @@ public: 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 { diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h index 867cfc4f140..d7d27e7585c 100644 --- a/include/llvm/IR/Function.h +++ b/include/llvm/IR/Function.h @@ -298,6 +298,14 @@ public: 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 { diff --git a/include/llvm/IR/Instructions.h b/include/llvm/IR/Instructions.h index e094afa33e2..15623ab0a71 100644 --- a/include/llvm/IR/Instructions.h +++ b/include/llvm/IR/Instructions.h @@ -1739,6 +1739,14 @@ public: 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 @@ public: 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 { diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index deaeb1d4456..e543ef56ef3 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -60,6 +60,12 @@ class ReadOnly<int argNo> : IntrinsicProperty { int ArgNo = argNo; } +// WriteOnly - The intrinsic does not read memory through the specified +// argument pointer. +class WriteOnly<int argNo> : IntrinsicProperty { + int ArgNo = argNo; +} + // ReadNone - The specified argument pointer is not dereferenced by the // intrinsic. class ReadNone<int argNo> : IntrinsicProperty { @@ -349,7 +355,7 @@ def int_memcpy : Intrinsic<[], [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_memmove : Intrinsic<[], 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], diff --git a/lib/Analysis/AliasAnalysis.cpp b/lib/Analysis/AliasAnalysis.cpp index 263dd48bfea..a9d4610ed67 100644 --- a/lib/Analysis/AliasAnalysis.cpp +++ b/lib/Analysis/AliasAnalysis.cpp @@ -142,6 +142,8 @@ ModRefInfo AAResults::getModRefInfo(ImmutableCallSite CS, 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 @@ ModRefInfo AAResults::getModRefInfo(ImmutableCallSite CS1, // 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 diff --git a/lib/Analysis/BasicAliasAnalysis.cpp b/lib/Analysis/BasicAliasAnalysis.cpp index cfb73541820..7efcd7243f3 100644 --- a/lib/Analysis/BasicAliasAnalysis.cpp +++ b/lib/Analysis/BasicAliasAnalysis.cpp @@ -563,6 +563,8 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(ImmutableCallSite CS) { // 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 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(const Function *F) { // 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 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(const Function *F) { 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<IntrinsicInst>(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 @@ static bool isWriteOnlyParam(ImmutableCallSite CS, unsigned ArgIdx, 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; diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index a3d1346e7d2..507e7e76ecd 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -660,6 +660,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(swifterror); KEYWORD(swiftself); KEYWORD(uwtable); + KEYWORD(writeonly); KEYWORD(zeroext); KEYWORD(type); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index c5216a2ad6d..a2fcbf41204 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -1098,6 +1098,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, 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 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { 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: diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index be3697aefdd..37998e87950 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -205,6 +205,7 @@ enum Kind { kw_swifterror, kw_swiftself, kw_uwtable, + kw_writeonly, kw_zeroext, kw_type, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 4689d37b1b8..9aaf3500cad 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1464,6 +1464,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { 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; } diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index d3174fd504c..1ee7b7b25e4 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -667,6 +667,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { 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: diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index bc49b930070..b0a4cb6be55 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -292,6 +292,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { 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 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) { 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; diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 47ded3cbd42..682e934d4b0 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -1309,6 +1309,7 @@ void Verifier::verifyAttributeTypes(AttributeSet Attrs, unsigned Idx, 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 @@ void Verifier::verifyParameterAttrs(AttributeSet Attrs, unsigned Idx, Type *Ty, "'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 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeSet Attrs, 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); diff --git a/test/Analysis/BasicAA/cs-cs.ll b/test/Analysis/BasicAA/cs-cs.ll index dc298f1668b..3bc4d72eab3 100644 --- a/test/Analysis/BasicAA/cs-cs.ll +++ b/test/Analysis/BasicAA/cs-cs.ll @@ -9,6 +9,7 @@ declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) nounwind 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 @@ entry: ; 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 @@ define void @test6(i8* %P) nounwind ssp { ; 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 } diff --git a/test/Bindings/llvm-c/Inputs/invalid.ll.bc b/test/Bindings/llvm-c/Inputs/invalid.ll.bc Binary files differindex 60c7afffbc2..c6753320ae3 100644 --- a/test/Bindings/llvm-c/Inputs/invalid.ll.bc +++ b/test/Bindings/llvm-c/Inputs/invalid.ll.bc diff --git a/test/Bindings/llvm-c/invalid-bitcode.test b/test/Bindings/llvm-c/invalid-bitcode.test index 262cb88fd3c..c6aaaeb71fe 100644 --- a/test/Bindings/llvm-c/invalid-bitcode.test +++ b/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 diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index d230aa7aa4c..9fdf54b7b30 100644 --- a/test/Bitcode/attributes.ll +++ b/test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ define void @f34() ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #32 +; CHECK: call void @nobuiltin() #33 ret void; } @@ -328,6 +328,12 @@ define i8* @f55(i32, i32) allocsize(0, 1) { 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 @@ define i8* @f55(i32, i32) allocsize(0, 1) { ; 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 } diff --git a/test/Bitcode/compatibility.ll b/test/Bitcode/compatibility.ll index 827bc6a56ca..18270607c3c 100644 --- a/test/Bitcode/compatibility.ll +++ b/test/Bitcode/compatibility.ll @@ -1244,7 +1244,7 @@ exit: ; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2> 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 @@ normal: 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 @@ normal: ; 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 diff --git a/test/Bitcode/invalid.ll b/test/Bitcode/invalid.ll index c56f0ffefa9..4bbb4570f7e 100644 --- a/test/Bitcode/invalid.ll +++ b/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 diff --git a/test/Bitcode/invalid.ll.bc b/test/Bitcode/invalid.ll.bc Binary files differindex 60c7afffbc2..a2b611e6c51 100644 --- a/test/Bitcode/invalid.ll.bc +++ b/test/Bitcode/invalid.ll.bc diff --git a/test/LTO/X86/Inputs/invalid.ll.bc b/test/LTO/X86/Inputs/invalid.ll.bc Binary files differindex 60c7afffbc2..c6753320ae3 100644 --- a/test/LTO/X86/Inputs/invalid.ll.bc +++ b/test/LTO/X86/Inputs/invalid.ll.bc diff --git a/test/LTO/X86/invalid.ll b/test/LTO/X86/invalid.ll index b9d0bca091d..cc3127be362 100644 --- a/test/LTO/X86/invalid.ll +++ b/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) diff --git a/test/Verifier/writeonly.ll b/test/Verifier/writeonly.ll new file mode 100644 index 00000000000..0eeaebbc3a8 --- /dev/null +++ b/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 diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h index 2b479537dfe..9d84d243cfd 100644 --- a/utils/TableGen/CodeGenIntrinsics.h +++ b/utils/TableGen/CodeGenIntrinsics.h @@ -112,6 +112,7 @@ namespace llvm { enum ArgAttribute { NoCapture, ReadOnly, + WriteOnly, ReadNone }; std::vector<std::pair<unsigned, ArgAttribute> > ArgumentAttributes; diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index fb9d29c7593..1367e24bb2d 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -595,6 +595,9 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { } 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)); diff --git a/utils/TableGen/IntrinsicEmitter.cpp b/utils/TableGen/IntrinsicEmitter.cpp index 2004eaeec48..bee7fbf715b 100644 --- a/utils/TableGen/IntrinsicEmitter.cpp +++ b/utils/TableGen/IntrinsicEmitter.cpp @@ -554,6 +554,12 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { 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 @@ EmitAttributes(const std::vector<CodeGenIntrinsic> &Ints, raw_ostream &OS) { 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; } |