Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -1314,6 +1314,10 @@ 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. +``read_write_arg_mem`` + This attribute indicates that the only memory acesses inside function are + non-volatile loads and stores from objects pointed to by its pointer-typed + arguments, with arbitrary offsets. ``returns_twice`` This attribute indicates that this function can return twice. The C ``setjmp`` is an example of such a function. The compiler disables Index: include/llvm/Analysis/AliasAnalysis.h =================================================================== --- include/llvm/Analysis/AliasAnalysis.h +++ include/llvm/Analysis/AliasAnalysis.h @@ -212,6 +212,7 @@ /// 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 'read_write_arg_mem' attribute. /// This property corresponds to the IntrReadWriteArgMem LLVM intrinsic flag. OnlyAccessesArgumentPointees = ArgumentPointees | ModRef, Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -403,7 +403,8 @@ ATTR_KIND_JUMP_TABLE = 40, ATTR_KIND_DEREFERENCEABLE = 41, ATTR_KIND_DEREFERENCEABLE_OR_NULL = 42, - ATTR_KIND_CONVERGENT = 43 + ATTR_KIND_CONVERGENT = 43, + ATTR_KIND_READ_WRITE_ARG_MEM = 44 }; enum ComdatSelectionKindCodes { Index: include/llvm/IR/Attributes.h =================================================================== --- include/llvm/IR/Attributes.h +++ include/llvm/IR/Attributes.h @@ -98,6 +98,9 @@ OptimizeNone, ///< Function must not be optimized. ReadNone, ///< Function does not access memory ReadOnly, ///< Function only reads from memory + ReadWriteArgMem, ///< Function reads and writes only to/from memory + ///< pointed to by it's arguments with arbitrary + ///< offsets. Returned, ///< Return value is always equal to this argument ReturnsTwice, ///< Function can return twice SExt, ///< Sign extended before/after call Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -268,6 +268,16 @@ CALLSITE_DELEGATE_SETTER(setOnlyReadsMemory()); } + /// @brief Determine if the call only reads and writes to/from memory + /// locations pointed by it's pointer arguments with arbitrary offsets or + /// does not access memory at all. + bool readsWritesOnlyArgMem() const { + CALLSITE_DELEGATE_GETTER(readsWritesOnlyArgMem()); + } + void setReadWriteOnlyArgMem() { + CALLSITE_DELEGATE_SETTER(setReadWriteOnlyArgMem()); + } + /// @brief Determine if the call cannot return. bool doesNotReturn() const { CALLSITE_DELEGATE_GETTER(doesNotReturn()); Index: include/llvm/IR/Function.h =================================================================== --- include/llvm/IR/Function.h +++ include/llvm/IR/Function.h @@ -281,6 +281,18 @@ addFnAttr(Attribute::ReadOnly); } + /// @brief Determine if function only reads and writes to/from memory + /// locations pointed by it's pointer arguments with arbitrary offsets or + /// does not access memory at all. + bool readsWritesOnlyArgMem() const { + return doesNotAccessMemory() || + AttributeSets.hasAttribute(AttributeSet::FunctionIndex, + Attribute::ReadWriteArgMem); + } + void setReadWriteOnlyArgMem() { + addFnAttr(Attribute::ReadWriteArgMem); + } + /// @brief Determine if the function cannot return. bool doesNotReturn() const { return AttributeSets.hasAttribute(AttributeSet::FunctionIndex, Index: include/llvm/IR/Instructions.h =================================================================== --- include/llvm/IR/Instructions.h +++ include/llvm/IR/Instructions.h @@ -1552,6 +1552,16 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call only reads and writes to/from memory + /// locations pointed by it's pointer arguments with arbitrary offsets or + /// does not access memory at all. + bool readsWritesOnlyArgMem() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::ReadWriteArgMem); + } + void setReadWriteOnlyArgMem() { + addAttribute(AttributeSet::FunctionIndex, Attribute::ReadWriteArgMem); + } + /// \brief Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { @@ -3287,6 +3297,16 @@ addAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly); } + /// \brief Determine if the call only reads and writes to/from memory + /// locations pointed by it's pointer arguments with arbitrary offsets or + /// does not access memory at all. + bool readsWritesOnlyArgMem() const { + return doesNotAccessMemory() || hasFnAttr(Attribute::ReadWriteArgMem); + } + void setReadWriteOnlyArgMem() { + addAttribute(AttributeSet::FunctionIndex, Attribute::ReadWriteArgMem); + } + /// \brief Determine if the call cannot return. bool doesNotReturn() const { return hasFnAttr(Attribute::NoReturn); } void setDoesNotReturn() { Index: lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- lib/Analysis/BasicAliasAnalysis.cpp +++ lib/Analysis/BasicAliasAnalysis.cpp @@ -681,6 +681,9 @@ if (CS.onlyReadsMemory()) Min = OnlyReadsMemory; + if (CS.readsWritesOnlyArgMem()) + Min = OnlyAccessesArgumentPointees; + // The AliasAnalysis base class has some smarts, lets use them. return ModRefBehavior(AliasAnalysis::getModRefBehavior(CS) & Min); } @@ -706,6 +709,10 @@ if (F->onlyReadsMemory()) Min = OnlyReadsMemory; + if (F->readsWritesOnlyArgMem()) + Min = OnlyAccessesArgumentPointees; + + // TODO: Mark memset with read_write_arg_mem attribute const TargetLibraryInfo &TLI = getAnalysis().getTLI(); if (isMemsetPattern16(F, TLI)) Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -621,6 +621,7 @@ KEYWORD(optsize); KEYWORD(readnone); KEYWORD(readonly); + KEYWORD(read_write_arg_mem); KEYWORD(returned); KEYWORD(returns_twice); KEYWORD(signext); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -934,34 +934,35 @@ 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_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_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_read_write_arg_mem: B.addAttribute(Attribute::ReadWriteArgMem); 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_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: @@ -1268,6 +1269,7 @@ case lltok::kw_sspreq: case lltok::kw_sspstrong: case lltok::kw_uwtable: + case lltok::kw_read_write_arg_mem: HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute"); break; } @@ -1344,6 +1346,7 @@ case lltok::kw_sspreq: case lltok::kw_sspstrong: case lltok::kw_uwtable: + case lltok::kw_read_write_arg_mem: HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute"); break; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -129,6 +129,7 @@ kw_optsize, kw_readnone, kw_readonly, + kw_read_write_arg_mem, kw_returned, kw_returns_twice, kw_signext, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -1142,6 +1142,8 @@ return Attribute::ReadNone; case bitc::ATTR_KIND_READ_ONLY: return Attribute::ReadOnly; + case bitc::ATTR_KIND_READ_WRITE_ARG_MEM: + return Attribute::ReadWriteArgMem; case bitc::ATTR_KIND_RETURNED: return Attribute::Returned; case bitc::ATTR_KIND_RETURNS_TWICE: Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -218,6 +218,8 @@ return bitc::ATTR_KIND_READ_NONE; case Attribute::ReadOnly: return bitc::ATTR_KIND_READ_ONLY; + case Attribute::ReadWriteArgMem: + return bitc::ATTR_KIND_READ_WRITE_ARG_MEM; case Attribute::Returned: return bitc::ATTR_KIND_RETURNED; case Attribute::ReturnsTwice: Index: lib/IR/Attributes.cpp =================================================================== --- lib/IR/Attributes.cpp +++ lib/IR/Attributes.cpp @@ -240,6 +240,8 @@ return "readnone"; if (hasAttribute(Attribute::ReadOnly)) return "readonly"; + if (hasAttribute(Attribute::ReadWriteArgMem)) + return "read_write_arg_mem"; if (hasAttribute(Attribute::Returned)) return "returned"; if (hasAttribute(Attribute::ReturnsTwice)) @@ -444,6 +446,9 @@ llvm_unreachable("dereferenceable_or_null attribute not supported in raw " "format"); break; + case Attribute::ReadWriteArgMem: + llvm_unreachable("read_write_arg_mem attribute not supported in raw format"); + break; } llvm_unreachable("Unsupported attribute type"); } @@ -1353,7 +1358,8 @@ 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::ReadWriteArgMem) continue; if (uint64_t A = (Val & AttributeImpl::getAttrMask(I))) { Attrs[I] = true; Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -1269,7 +1269,8 @@ I->getKindAsEnum() == Attribute::Cold || I->getKindAsEnum() == Attribute::OptimizeNone || I->getKindAsEnum() == Attribute::JumpTable || - I->getKindAsEnum() == Attribute::Convergent) { + I->getKindAsEnum() == Attribute::Convergent || + I->getKindAsEnum() == Attribute::ReadWriteArgMem) { if (!isFunction) { CheckFailed("Attribute '" + I->getAsString() + "' only applies to functions!", V); @@ -1440,6 +1441,16 @@ "Attributes 'readnone and readonly' are incompatible!", V); Assert( + !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadNone) && + Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadWriteArgMem)), + "Attributes 'readnone and read_write_arg_mem' are incompatible!", V); + + Assert( + !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadOnly) && + Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::ReadWriteArgMem)), + "Attributes 'readonly and read_write_arg_mem' are incompatible!", V); + + Assert( !(Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::NoInline) && Attrs.hasAttribute(AttributeSet::FunctionIndex, Attribute::AlwaysInline)), @@ -1527,7 +1538,8 @@ const Instruction &CI = *CS.getInstruction(); - Assert(!CS.doesNotAccessMemory() && !CS.onlyReadsMemory(), + Assert(!CS.doesNotAccessMemory() && !CS.onlyReadsMemory() && + !CS.readsWritesOnlyArgMem(), "gc.statepoint must read and write memory to preserve " "reordering restrictions required by safepoint semantics", &CI); Index: test/Analysis/BasicAA/modref.ll =================================================================== --- test/Analysis/BasicAA/modref.ll +++ test/Analysis/BasicAA/modref.ll @@ -145,6 +145,51 @@ ; CHECK: load i32, i32* } +;; Check that aa correctly handles functions marked with read_write_arg_mem +;; attribute. +declare i32 @func_read_write_arg_mem(i32 *byval %P) + +;; 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_read_write_arg_mem(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 *byval %P, i32 *byval %P2) { + %V1 = load i32, i32* %P + call i32 @func_read_write_arg_mem(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_read_write_arg_mem(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 Index: test/Bitcode/attributes.ll =================================================================== --- test/Bitcode/attributes.ll +++ test/Bitcode/attributes.ll @@ -204,7 +204,7 @@ ; CHECK: define void @f34() { call void @nobuiltin() nobuiltin -; CHECK: call void @nobuiltin() #26 +; CHECK: call void @nobuiltin() #27 ret void; } @@ -256,6 +256,12 @@ ret void } +define void @f44() read_write_arg_mem +; CHECK: define void @f44() #26 +{ + ret void; +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -282,4 +288,5 @@ ; CHECK: attributes #23 = { noinline optnone } ; CHECK: attributes #24 = { jumptable } ; CHECK: attributes #25 = { convergent } -; CHECK: attributes #26 = { nobuiltin } +; CHECK: attributes #26 = { read_write_arg_mem } +; CHECK: attributes #27 = { nobuiltin } Index: test/Verifier/invalid-statepoint3.ll =================================================================== --- /dev/null +++ test/Verifier/invalid-statepoint3.ll @@ -0,0 +1,32 @@ +; RUN: not opt -S < %s -verify 2>&1 | FileCheck %s + +; CHECK: gc.statepoint must read and write memory to preserve reordering restrictions required by safepoint semantics + +declare void @use(...) +declare i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(i32, i32, i32) +declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) + +define i8 addrspace(1)* @test1(i8 addrspace(1)* %arg) gc "statepoint-example" { +entry: + %safepoint_token = call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %arg) readnone + %reloc = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(i32 %safepoint_token, i32 7, i32 7) + ret i8 addrspace(1)* %reloc +} + +; CHECK: gc.statepoint must read and write memory to preserve reordering restrictions required by safepoint semantics + +define i8 addrspace(1)* @test2(i8 addrspace(1)* %arg) gc "statepoint-example" { +entry: + %safepoint_token = call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %arg) readnone + %reloc = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(i32 %safepoint_token, i32 7, i32 7) + ret i8 addrspace(1)* %reloc +} + +; CHECK: gc.statepoint must read and write memory to preserve reordering restrictions required by safepoint semantics + +define i8 addrspace(1)* @test3(i8 addrspace(1)* %arg) gc "statepoint-example" { +entry: + %safepoint_token = call i32 (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %arg) readnone + %reloc = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(i32 %safepoint_token, i32 7, i32 7) + ret i8 addrspace(1)* %reloc +} Index: test/Verifier/read_write_arg_mem.ll =================================================================== --- /dev/null +++ test/Verifier/read_write_arg_mem.ll @@ -0,0 +1,8 @@ +; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s +; Sanity checks for read_write_arg_mem attribute + +declare void @a(i64* %p) readnone read_write_arg_mem +; CHECK: Attributes {{.*}} are incompatible + +declare void @b(i64* %p) readonly read_write_arg_mem +; CHECK: Attributes {{.*}} are incompatible