summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolai Hähnle <nicolai.haehnle@amd.com>2016-04-01 16:10:17 -0500
committerNicolai Hähnle <nicolai.haehnle@amd.com>2016-04-06 13:30:01 -0500
commit036a6e8ef008ddd8f9c5000442ce86116333fa35 (patch)
tree082d60e98b1e5cc2f3e8bb17cd5c08ce74b63505
parent47605d726a4bd75a2d64ec8c9e8853260c5b78a6 (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
-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.td12
-rw-r--r--lib/Analysis/AliasAnalysis.cpp6
-rw-r--r--lib/Analysis/BasicAliasAnalysis.cpp36
-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.cpp11
-rw-r--r--test/Analysis/BasicAA/cs-cs.ll46
-rw-r--r--test/Bitcode/attributes.ll11
-rw-r--r--test/Bitcode/compatibility.ll7
-rw-r--r--test/Transforms/ObjCARC/nested.ll2
-rw-r--r--utils/TableGen/CodeGenIntrinsics.h1
-rw-r--r--utils/TableGen/CodeGenTarget.cpp3
-rw-r--r--utils/TableGen/IntrinsicEmitter.cpp17
24 files changed, 171 insertions, 50 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index 6c09a24252c..c6c8b8a9f44 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -1404,6 +1404,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 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,
diff --git a/include/llvm/Analysis/AliasAnalysis.h b/include/llvm/Analysis/AliasAnalysis.h
index 2d06965b84e..78c741b21af 100644
--- a/include/llvm/Analysis/AliasAnalysis.h
+++ b/include/llvm/Analysis/AliasAnalysis.h
@@ -152,6 +152,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
@@ -313,6 +320,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 0c4cc854cdc..bcf43d28db6 100644
--- a/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/include/llvm/Bitcode/LLVMBitCodes.h
@@ -510,7 +510,8 @@ enum AttributeKindCodes {
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 {
diff --git a/include/llvm/IR/Attributes.td b/include/llvm/IR/Attributes.td
index 3442da2aec8..594a0620da2 100644
--- a/include/llvm/IR/Attributes.td
+++ b/include/llvm/IR/Attributes.td
@@ -163,6 +163,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 2bd836fd794..d8669db6619 100644
--- a/include/llvm/IR/CallSite.h
+++ b/include/llvm/IR/CallSite.h
@@ -385,6 +385,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 c5744c844d2..887c226f05b 100644
--- a/include/llvm/IR/Function.h
+++ b/include/llvm/IR/Function.h
@@ -291,6 +291,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 5b5834d341a..ca5c8efba37 100644
--- a/include/llvm/IR/Instructions.h
+++ b/include/llvm/IR/Instructions.h
@@ -1725,6 +1725,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 {
@@ -3653,6 +3661,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 9f06cca792d..405c9022958 100644
--- a/include/llvm/IR/Intrinsics.td
+++ b/include/llvm/IR/Intrinsics.td
@@ -73,6 +73,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 {
@@ -360,16 +366,16 @@ def int_memcpy : 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_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],
diff --git a/lib/Analysis/AliasAnalysis.cpp b/lib/Analysis/AliasAnalysis.cpp
index 453bc3dbe67..d29e79a4099 100644
--- a/lib/Analysis/AliasAnalysis.cpp
+++ b/lib/Analysis/AliasAnalysis.cpp
@@ -143,6 +143,9 @@ ModRefInfo AAResults::getModRefInfo(ImmutableCallSite CS,
if (onlyReadsMemory(MRB))
Result = ModRefInfo(Result & MRI_Ref);
+ if (doesNotReadMemory(MRB))
+ Result = ModRefInfo(Result & MRI_Mod);
+
if (onlyAccessesArgPointees(MRB)) {
bool DoesAlias = false;
ModRefInfo AllArgsMask = MRI_NoModRef;
@@ -208,6 +211,9 @@ ModRefInfo AAResults::getModRefInfo(ImmutableCallSite CS1,
if (onlyReadsMemory(CS1B))
Result = ModRefInfo(Result & MRI_Ref);
+ 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
// CS2's arguments.
diff --git a/lib/Analysis/BasicAliasAnalysis.cpp b/lib/Analysis/BasicAliasAnalysis.cpp
index d8f6f109815..3a64c12813b 100644
--- a/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/lib/Analysis/BasicAliasAnalysis.cpp
@@ -570,6 +570,9 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(ImmutableCallSite CS) {
if (CS.onlyReadsMemory())
Min = FMRB_OnlyReadsMemory;
+ if (CS.doesNotReadMemory())
+ Min = FMRB_DoesNotReadMemory;
+
if (CS.onlyAccessesArgMemory())
Min = FunctionModRefBehavior(Min & FMRB_OnlyAccessesArgumentPointees);
@@ -597,38 +600,22 @@ FunctionModRefBehavior BasicAAResult::getModRefBehavior(const Function *F) {
if (F->onlyReadsMemory())
Min = FMRB_OnlyReadsMemory;
+ if (F->doesNotReadMemory())
+ Min = FMRB_DoesNotReadMemory;
+
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<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.
+ // FIXME Consider handling this in InferFunctionAttr.cpp
if (CS.getCalledFunction() && isMemsetPattern16(CS.getCalledFunction(), TLI))
if (ArgIdx == 0)
return true;
@@ -643,8 +630,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 target library functions.
if (isWriteOnlyParam(CS, ArgIdx, TLI))
return MRI_Mod;
diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp
index 58558b99dfa..b6745a60983 100644
--- a/lib/AsmParser/LLLexer.cpp
+++ b/lib/AsmParser/LLLexer.cpp
@@ -656,6 +656,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 c8543206e21..b485d0a5885 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -1088,6 +1088,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:
@@ -1384,6 +1385,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 c6258046cf0..e60b032672b 100644
--- a/lib/AsmParser/LLToken.h
+++ b/lib/AsmParser/LLToken.h
@@ -160,6 +160,7 @@ namespace lltok {
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 85a331cc00d..845a2bdeadd 100644
--- a/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1332,6 +1332,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 99046d993a8..c309ed69693 100644
--- a/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -267,6 +267,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 19664f77eae..2606825a702 100644
--- a/lib/IR/Attributes.cpp
+++ b/lib/IR/Attributes.cpp
@@ -271,6 +271,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
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))
@@ -458,6 +460,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 fe3d6eb6cf4..84d50c9a02b 100644
--- a/lib/IR/Verifier.cpp
+++ b/lib/IR/Verifier.cpp
@@ -1320,6 +1320,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() +
@@ -1510,6 +1511,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..fe0672d2ea1 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) #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 @@ 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 }
-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 }
diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll
index dec3608fb2b..94c7fd49779 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() #30
+; CHECK: call void @nobuiltin() #31
ret void;
}
@@ -318,6 +318,12 @@ entry:
ret float 1.0
}
+define void @f54() writeonly
+; CHECK: define void @f54() #30
+{
+ ret void;
+}
+
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@@ -348,4 +354,5 @@ entry:
; 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 }
diff --git a/test/Bitcode/compatibility.ll b/test/Bitcode/compatibility.ll
index ae12a24ede4..5689e6a7bb2 100644
--- a/test/Bitcode/compatibility.ll
+++ b/test/Bitcode/compatibility.ll
@@ -1173,7 +1173,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
@@ -1519,6 +1519,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 }
@@ -1559,7 +1561,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/Transforms/ObjCARC/nested.ll b/test/Transforms/ObjCARC/nested.ll
index cf14a1f9a66..31884f1e5a0 100644
--- a/test/Transforms/ObjCARC/nested.ll
+++ b/test/Transforms/ObjCARC/nested.ll
@@ -820,6 +820,6 @@ entry:
}
-; CHECK: attributes #0 = { argmemonly nounwind }
+; CHECK: attributes #0 = { argmemonly nounwind writeonly }
; CHECK: attributes #1 = { nonlazybind }
; CHECK: attributes [[NUW]] = { nounwind }
diff --git a/utils/TableGen/CodeGenIntrinsics.h b/utils/TableGen/CodeGenIntrinsics.h
index fe1fd7fdc3f..3897879012d 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 45f0f6826ac..8d5bdd232e5 100644
--- a/utils/TableGen/CodeGenTarget.cpp
+++ b/utils/TableGen/CodeGenTarget.cpp
@@ -599,6 +599,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;
}