summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolai Haehnle <nhaehnle@gmail.com>2016-07-04 08:01:29 +0000
committerNicolai Haehnle <nhaehnle@gmail.com>2016-07-04 08:01:29 +0000
commitb07f5404567484379450db2135cdc56ba08ebb00 (patch)
tree85ada6277883420d7c9b8aa5b9fce14cd59359cd
parent061feda889306e89211087d63c4ebf4ef91a8446 (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
-rw-r--r--docs/LangRef.rst7
-rw-r--r--include/llvm/Analysis/AliasAnalysis.h13
-rw-r--r--include/llvm/Bitcode/LLVMBitCodes.h3
-rw-r--r--include/llvm/IR/Attributes.td3
-rw-r--r--include/llvm/IR/CallSite.h8
-rw-r--r--include/llvm/IR/Function.h8
-rw-r--r--include/llvm/IR/Instructions.h16
-rw-r--r--include/llvm/IR/Intrinsics.td10
-rw-r--r--lib/Analysis/AliasAnalysis.cpp4
-rw-r--r--lib/Analysis/BasicAliasAnalysis.cpp33
-rw-r--r--lib/AsmParser/LLLexer.cpp1
-rw-r--r--lib/AsmParser/LLParser.cpp2
-rw-r--r--lib/AsmParser/LLToken.h1
-rw-r--r--lib/Bitcode/Reader/BitcodeReader.cpp2
-rw-r--r--lib/Bitcode/Writer/BitcodeWriter.cpp2
-rw-r--r--lib/IR/Attributes.cpp3
-rw-r--r--lib/IR/Verifier.cpp23
-rw-r--r--test/Analysis/BasicAA/cs-cs.ll43
-rw-r--r--test/Bindings/llvm-c/Inputs/invalid.ll.bcbin688 -> 688 bytes
-rw-r--r--test/Bindings/llvm-c/invalid-bitcode.test4
-rw-r--r--test/Bitcode/attributes.ll11
-rw-r--r--test/Bitcode/compatibility.ll7
-rw-r--r--test/Bitcode/invalid.ll2
-rw-r--r--test/Bitcode/invalid.ll.bcbin688 -> 688 bytes
-rw-r--r--test/LTO/X86/Inputs/invalid.ll.bcbin688 -> 688 bytes
-rw-r--r--test/LTO/X86/invalid.ll2
-rw-r--r--test/Verifier/writeonly.ll13
-rw-r--r--utils/TableGen/CodeGenIntrinsics.h1
-rw-r--r--utils/TableGen/CodeGenTarget.cpp3
-rw-r--r--utils/TableGen/IntrinsicEmitter.cpp17
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
index 60c7afffbc2..c6753320ae3 100644
--- a/test/Bindings/llvm-c/Inputs/invalid.ll.bc
+++ b/test/Bindings/llvm-c/Inputs/invalid.ll.bc
Binary files differ
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
index 60c7afffbc2..a2b611e6c51 100644
--- a/test/Bitcode/invalid.ll.bc
+++ b/test/Bitcode/invalid.ll.bc
Binary files differ
diff --git a/test/LTO/X86/Inputs/invalid.ll.bc b/test/LTO/X86/Inputs/invalid.ll.bc
index 60c7afffbc2..c6753320ae3 100644
--- a/test/LTO/X86/Inputs/invalid.ll.bc
+++ b/test/LTO/X86/Inputs/invalid.ll.bc
Binary files differ
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;
}