From 6690dbffe0298cb7260fb9caf26743bd56d3c7e8 Mon Sep 17 00:00:00 2001 From: Igor Laevsky Date: Sat, 11 Jul 2015 10:30:36 +0000 Subject: [PATCH] Add argmemonly attribute. This change adds new attribute called "argmemonly". Function marked with this attribute can only access memory through it's argument pointers. This attribute directly corresponds to the "OnlyAccessesArgumentPointees" ModRef behaviour in alias analysis. Differential Revision: http://reviews.llvm.org/D10398 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@241979 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LangRef.rst | 8 ++++ include/llvm/Analysis/AliasAnalysis.h | 3 ++ include/llvm/Bitcode/LLVMBitCodes.h | 1 + include/llvm/IR/Attributes.h | 2 + include/llvm/IR/CallSite.h | 9 ++++ include/llvm/IR/Function.h | 10 ++++ include/llvm/IR/Instructions.h | 18 +++++++ lib/Analysis/BasicAliasAnalysis.cpp | 6 +++ lib/AsmParser/LLLexer.cpp | 1 + lib/AsmParser/LLParser.cpp | 67 +++++++++++++++------------ lib/AsmParser/LLToken.h | 1 + lib/Bitcode/Reader/BitcodeReader.cpp | 2 + lib/Bitcode/Writer/BitcodeWriter.cpp | 2 + lib/IR/Attributes.cpp | 8 +++- lib/IR/Verifier.cpp | 8 ++-- test/Analysis/BasicAA/modref.ll | 45 ++++++++++++++++++ test/Bitcode/attributes.ll | 11 ++++- 17 files changed, 167 insertions(+), 35 deletions(-) diff --git a/docs/LangRef.rst b/docs/LangRef.rst index a28ad104f13..48771c211c1 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -1326,6 +1326,14 @@ 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. +``argmemonly`` + This attribute indicates that the only memory accesses inside function are + loads and stores from objects pointed to by its pointer-typed arguments, + with arbitrary offsets. Or in other words, all memory operations in the + function can refer to memory only using pointers based on its function + arguments. + Note that ``argmemonly`` can be used together with ``readonly`` attribute + in order to specify that function reads only from its arguments. ``returns_twice`` This attribute indicates that this function can return twice. The C ``setjmp`` is an example of such a function. The compiler disables diff --git a/include/llvm/Analysis/AliasAnalysis.h b/include/llvm/Analysis/AliasAnalysis.h index 57e44876f5c..36f8199a032 100644 --- a/include/llvm/Analysis/AliasAnalysis.h +++ b/include/llvm/Analysis/AliasAnalysis.h @@ -211,6 +211,8 @@ public: /// (if it has any) are non-volatile loads from objects pointed to by its /// pointer-typed arguments, with arbitrary offsets. /// + /// This property corresponds to the LLVM IR 'argmemonly' attribute combined + /// with 'readonly' attribute. /// This property corresponds to the IntrReadArgMem LLVM intrinsic flag. OnlyReadsArgumentPointees = ArgumentPointees | Ref, @@ -218,6 +220,7 @@ public: /// function (if it has any) are non-volatile loads and stores from objects /// pointed to by its pointer-typed arguments, with arbitrary offsets. /// + /// This property corresponds to the LLVM IR 'argmemonly' attribute. /// This property corresponds to the IntrReadWriteArgMem LLVM intrinsic flag. OnlyAccessesArgumentPointees = ArgumentPointees | ModRef, diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index 605c4172dd8..7130ee75523 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -407,6 +407,7 @@ namespace bitc { ATTR_KIND_DEREFERENCEABLE_OR_NULL = 42, ATTR_KIND_CONVERGENT = 43, ATTR_KIND_SAFESTACK = 44, + ATTR_KIND_ARGMEMONLY = 45 }; enum ComdatSelectionKindCodes { diff --git a/include/llvm/IR/Attributes.h b/include/llvm/IR/Attributes.h index 366bf709ab1..4d6d7da1fa5 100644 --- a/include/llvm/IR/Attributes.h +++ b/include/llvm/IR/Attributes.h @@ -98,6 +98,8 @@ public: OptimizeNone, ///< Function must not be optimized. ReadNone, ///< Function does not access memory ReadOnly, ///< Function only reads from memory + ArgMemOnly, ///< Funciton can access memory only using pointers + ///< based on its arguments. Returned, ///< Return value is always equal to this argument ReturnsTwice, ///< Function can return twice SExt, ///< Sign extended before/after call diff --git a/include/llvm/IR/CallSite.h b/include/llvm/IR/CallSite.h index dd2903e807e..2841781e8a9 100644 --- a/include/llvm/IR/CallSite.h +++ b/include/llvm/IR/CallSite.h @@ -290,6 +290,15 @@ public: CALLSITE_DELEGATE_SETTER(setOnlyReadsMemory()); } + /// @brief Determine if the call can access memmory only using pointers based + /// on its arguments. + bool onlyAccessesArgMemory() const { + CALLSITE_DELEGATE_GETTER(onlyAccessesArgMemory()); + } + void setOnlyAccessesArgMemory() { + CALLSITE_DELEGATE_SETTER(setOnlyAccessesArgMemory()); + } + /// @brief Determine if the call cannot return. bool doesNotReturn() const { CALLSITE_DELEGATE_GETTER(doesNotReturn()); diff --git a/include/llvm/IR/Function.h b/include/llvm/IR/Function.h index 02ea056de39..ec9f4cad094 100644 --- a/include/llvm/IR/Function.h +++ b/include/llvm/IR/Function.h @@ -293,6 +293,16 @@ public: addFnAttr(Attribute::ReadOnly); } + /// @brief Determine if the call can access memmory only using pointers based + /// on its arguments. + bool onlyAccessesArgMemory() const { + return AttributeSets.hasAttribute(AttributeSet::FunctionIndex, + Attribute::ArgMemOnly); + } + void setOnlyAccessesArgMemory() { + addFnAttr(Attribute::ArgMemOnly); + } + /// @brief Determine if the function cannot return. bool doesNotReturn() const { return AttributeSets.hasAttribute(AttributeSet::FunctionIndex, diff --git a/include/llvm/IR/Instructions.h b/include/llvm/IR/Instructions.h index 62723e44c0c..07d5f111b9e 100644 --- a/include/llvm/IR/Instructions.h +++ b/include/llvm/IR/Instructions.h @@ -1595,6 +1595,15 @@ public: addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// @brief Determine if the call can access memmory only using pointers based + /// on its arguments. + bool onlyAccessesArgMemory() const { + return hasFnAttr(Attribute::ArgMemOnly); + } + void setOnlyAccessesArgMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::ArgMemOnly); + } + /// \brief Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { @@ -3364,6 +3373,15 @@ public: addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// @brief Determine if the call access memmory only using it's pointer + /// arguments. + bool onlyAccessesArgMemory() const { + return hasFnAttr(Attribute::ArgMemOnly); + } + void setOnlyAccessesArgMemory() { + addAttribute(AttributeSet::FunctionIndex, Attribute::ArgMemOnly); + } + /// \brief Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { diff --git a/lib/Analysis/BasicAliasAnalysis.cpp b/lib/Analysis/BasicAliasAnalysis.cpp index 8e812252fdf..68f766edb30 100644 --- a/lib/Analysis/BasicAliasAnalysis.cpp +++ b/lib/Analysis/BasicAliasAnalysis.cpp @@ -685,6 +685,9 @@ BasicAliasAnalysis::getModRefBehavior(ImmutableCallSite CS) { if (CS.onlyReadsMemory()) Min = OnlyReadsMemory; + if (CS.onlyAccessesArgMemory()) + Min = ModRefBehavior(Min & OnlyAccessesArgumentPointees); + // The AliasAnalysis base class has some smarts, lets use them. return ModRefBehavior(AliasAnalysis::getModRefBehavior(CS) & Min); } @@ -710,6 +713,9 @@ BasicAliasAnalysis::getModRefBehavior(const Function *F) { if (F->onlyReadsMemory()) Min = OnlyReadsMemory; + if (F->onlyAccessesArgMemory()) + Min = ModRefBehavior(Min & OnlyAccessesArgumentPointees); + const TargetLibraryInfo &TLI = getAnalysis().getTLI(); if (isMemsetPattern16(F, TLI)) diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 88f359d4fd5..5c4bab734b2 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -593,6 +593,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(attributes); KEYWORD(alwaysinline); + KEYWORD(argmemonly); KEYWORD(builtin); KEYWORD(byval); KEYWORD(inalloca); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index aa96d60ca0d..1c6e7bd18d0 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -946,35 +946,42 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B, B.addStackAlignmentAttr(Alignment); continue; } - case lltok::kw_alwaysinline: B.addAttribute(Attribute::AlwaysInline); break; - case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; - case lltok::kw_cold: B.addAttribute(Attribute::Cold); break; - case lltok::kw_convergent: B.addAttribute(Attribute::Convergent); break; - case lltok::kw_inlinehint: B.addAttribute(Attribute::InlineHint); break; - case lltok::kw_jumptable: B.addAttribute(Attribute::JumpTable); break; - case lltok::kw_minsize: B.addAttribute(Attribute::MinSize); break; - case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; - case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; - case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break; - case lltok::kw_noimplicitfloat: B.addAttribute(Attribute::NoImplicitFloat); break; - case lltok::kw_noinline: B.addAttribute(Attribute::NoInline); break; - case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break; - case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; - case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; - case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; - case lltok::kw_optnone: B.addAttribute(Attribute::OptimizeNone); break; - case lltok::kw_optsize: B.addAttribute(Attribute::OptimizeForSize); break; - case lltok::kw_readnone: B.addAttribute(Attribute::ReadNone); break; - case lltok::kw_readonly: B.addAttribute(Attribute::ReadOnly); break; - case lltok::kw_returns_twice: B.addAttribute(Attribute::ReturnsTwice); break; - case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break; - case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break; - case lltok::kw_sspstrong: B.addAttribute(Attribute::StackProtectStrong); break; - case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break; - case lltok::kw_sanitize_address: B.addAttribute(Attribute::SanitizeAddress); break; - case lltok::kw_sanitize_thread: B.addAttribute(Attribute::SanitizeThread); break; - case lltok::kw_sanitize_memory: B.addAttribute(Attribute::SanitizeMemory); break; - case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; + case lltok::kw_alwaysinline: B.addAttribute(Attribute::AlwaysInline); break; + case lltok::kw_argmemonly: B.addAttribute(Attribute::ArgMemOnly); break; + case lltok::kw_builtin: B.addAttribute(Attribute::Builtin); break; + case lltok::kw_cold: B.addAttribute(Attribute::Cold); break; + case lltok::kw_convergent: B.addAttribute(Attribute::Convergent); break; + case lltok::kw_inlinehint: B.addAttribute(Attribute::InlineHint); break; + case lltok::kw_jumptable: B.addAttribute(Attribute::JumpTable); break; + case lltok::kw_minsize: B.addAttribute(Attribute::MinSize); break; + case lltok::kw_naked: B.addAttribute(Attribute::Naked); break; + case lltok::kw_nobuiltin: B.addAttribute(Attribute::NoBuiltin); break; + case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break; + case lltok::kw_noimplicitfloat: + B.addAttribute(Attribute::NoImplicitFloat); break; + case lltok::kw_noinline: B.addAttribute(Attribute::NoInline); break; + case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break; + case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break; + case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; + case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; + case lltok::kw_optnone: B.addAttribute(Attribute::OptimizeNone); break; + case lltok::kw_optsize: B.addAttribute(Attribute::OptimizeForSize); break; + case lltok::kw_readnone: B.addAttribute(Attribute::ReadNone); break; + case lltok::kw_readonly: B.addAttribute(Attribute::ReadOnly); break; + case lltok::kw_returns_twice: + B.addAttribute(Attribute::ReturnsTwice); break; + case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break; + case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break; + case lltok::kw_sspstrong: + B.addAttribute(Attribute::StackProtectStrong); break; + case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break; + case lltok::kw_sanitize_address: + B.addAttribute(Attribute::SanitizeAddress); break; + case lltok::kw_sanitize_thread: + B.addAttribute(Attribute::SanitizeThread); break; + case lltok::kw_sanitize_memory: + B.addAttribute(Attribute::SanitizeMemory); break; + case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; // Error handling. case lltok::kw_inreg: @@ -1258,6 +1265,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_alignstack: case lltok::kw_alwaysinline: + case lltok::kw_argmemonly: case lltok::kw_builtin: case lltok::kw_inlinehint: case lltok::kw_jumptable: @@ -1334,6 +1342,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_alignstack: case lltok::kw_alwaysinline: + case lltok::kw_argmemonly: case lltok::kw_builtin: case lltok::kw_cold: case lltok::kw_inlinehint: diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index 2487d120813..691f085f0c9 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -100,6 +100,7 @@ namespace lltok { // Attributes: kw_attributes, kw_alwaysinline, + kw_argmemonly, kw_sanitize_address, kw_builtin, kw_byval, diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 4695078303a..c04e8b9f1f3 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1090,6 +1090,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::Alignment; case bitc::ATTR_KIND_ALWAYS_INLINE: return Attribute::AlwaysInline; + case bitc::ATTR_KIND_ARGMEMONLY: + return Attribute::ArgMemOnly; case bitc::ATTR_KIND_BUILTIN: return Attribute::Builtin; case bitc::ATTR_KIND_BY_VAL: diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index 8a3ed18ffeb..1a70ba5ac12 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -162,6 +162,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_ALIGNMENT; case Attribute::AlwaysInline: return bitc::ATTR_KIND_ALWAYS_INLINE; + case Attribute::ArgMemOnly: + return bitc::ATTR_KIND_ARGMEMONLY; case Attribute::Builtin: return bitc::ATTR_KIND_BUILTIN; case Attribute::ByVal: diff --git a/lib/IR/Attributes.cpp b/lib/IR/Attributes.cpp index c3032f4ffc7..546a98670a2 100644 --- a/lib/IR/Attributes.cpp +++ b/lib/IR/Attributes.cpp @@ -190,6 +190,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "sanitize_address"; if (hasAttribute(Attribute::AlwaysInline)) return "alwaysinline"; + if (hasAttribute(Attribute::ArgMemOnly)) + return "argmemonly"; if (hasAttribute(Attribute::Builtin)) return "builtin"; if (hasAttribute(Attribute::ByVal)) @@ -447,6 +449,9 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) { llvm_unreachable("dereferenceable_or_null attribute not supported in raw " "format"); break; + case Attribute::ArgMemOnly: + llvm_unreachable("argmemonly attribute not supported in raw format"); + break; } llvm_unreachable("Unsupported attribute type"); } @@ -1356,7 +1361,8 @@ AttrBuilder &AttrBuilder::addRawValue(uint64_t Val) { for (Attribute::AttrKind I = Attribute::None; I != Attribute::EndAttrKinds; I = Attribute::AttrKind(I + 1)) { if (I == Attribute::Dereferenceable || - I == Attribute::DereferenceableOrNull) + I == Attribute::DereferenceableOrNull || + I == Attribute::ArgMemOnly) continue; if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) { Attrs[I] = true; diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp index 647920f23da..2a0a4ff393e 100644 --- a/lib/IR/Verifier.cpp +++ b/lib/IR/Verifier.cpp @@ -1273,7 +1273,8 @@ void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx, I->getKindAsEnum() == Attribute::Cold || I->getKindAsEnum() == Attribute::OptimizeNone || I->getKindAsEnum() == Attribute::JumpTable || - I->getKindAsEnum() == Attribute::Convergent) { + I->getKindAsEnum() == Attribute::Convergent || + I->getKindAsEnum() == Attribute::ArgMemOnly) { if (!isFunction) { CheckFailed("Attribute '" + I->getAsString() + "' only applies to functions!", V); @@ -1531,8 +1532,9 @@ void Verifier::VerifyStatepoint(ImmutableCallSite CS) { const Instruction &CI = *CS.getInstruction(); - Assert(!CS.doesNotAccessMemory() && !CS.onlyReadsMemory(), - "gc.statepoint must read and write memory to preserve " + Assert(!CS.doesNotAccessMemory() && !CS.onlyReadsMemory() && + !CS.onlyAccessesArgMemory(), + "gc.statepoint must read and write all memory to preserve " "reordering restrictions required by safepoint semantics", &CI); diff --git a/test/Analysis/BasicAA/modref.ll b/test/Analysis/BasicAA/modref.ll index e124d6cbe20..3084f809c37 100644 --- a/test/Analysis/BasicAA/modref.ll +++ b/test/Analysis/BasicAA/modref.ll @@ -145,6 +145,51 @@ entry: ; CHECK: load i32, i32* } +;; Check that aa correctly handles functions marked with argmemonly +;; attribute. +declare i32 @func_argmemonly(i32 * %P) argmemonly + +;; Can not remove redundant load, function may write to it. +; CHECK-LABEL: @test8( +define i32 @test8(i32 *%P) { + %V1 = load i32, i32* %P + call i32 @func_argmemonly(i32* %P) + %V2 = load i32, i32* %P + %Diff = sub i32 %V1, %V2 + ret i32 %Diff + ; CHECK: load + ; CHECK: load + ; CHECK: sub + ; CHECK: ret i32 %Diff +} + +;; In this case load can be removed, function clobbers only %P2. +; CHECK-LABEL: @test9( +define i32 @test9(i32* %P, i32* noalias %P2) { + %V1 = load i32, i32* %P + call i32 @func_argmemonly(i32* %P2) + %V2 = load i32, i32* %P + %Diff = sub i32 %V1, %V2 + ret i32 %Diff + ; CHECK-NOT: load + ; CHECK: ret i32 0 +} + +;; In this case load can *not* be removed. Function clobers only %P2 but it may +;; alias with %P. +; CHECK-LABEL: @test10( +define i32 @test10(i32* %P, i32* %P2) { + %V1 = load i32, i32* %P + call i32 @func_argmemonly(i32* %P2) + %V2 = load i32, i32* %P + %Diff = sub i32 %V1, %V2 + ret i32 %Diff + ; CHECK: load + ; CHECK: load + ; CHECK: sub + ; CHECK: ret i32 %Diff +} + declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) nounwind declare void @llvm.memset.p0i8.i8(i8* nocapture, i8, i8, i32, i1) nounwind declare void @llvm.memcpy.p0i8.p0i8.i8(i8* nocapture, i8* nocapture, i8, i32, i1) nounwind diff --git a/test/Bitcode/attributes.ll b/test/Bitcode/attributes.ll index cae6a2e01e6..a0bc66642f7 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() #26 +; CHECK: call void @nobuiltin() #27 ret void; } @@ -256,6 +256,12 @@ define void @f43() convergent { ret void } +define void @f44() argmemonly +; CHECK: define void @f44() #26 +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -282,4 +288,5 @@ define void @f43() convergent { ; CHECK: attributes #23 = { noinline optnone } ; CHECK: attributes #24 = { jumptable } ; CHECK: attributes #25 = { convergent } -; CHECK: attributes #26 = { nobuiltin } +; CHECK: attributes #26 = { argmemonly } +; CHECK: attributes #27 = { nobuiltin } -- 2.34.1