diff --git a/clang/test/CodeGenObjC/os_log.m b/clang/test/CodeGenObjC/os_log.m --- a/clang/test/CodeGenObjC/os_log.m +++ b/clang/test/CodeGenObjC/os_log.m @@ -14,7 +14,7 @@ #ifdef __x86_64__ // CHECK-LABEL: define i8* @test_builtin_os_log // CHECK-O0-LABEL: define i8* @test_builtin_os_log -// CHECK: (i8* returned %[[BUF:.*]]) +// CHECK: (i8* returned "no-capture-maybe-returned" %[[BUF:.*]]) // CHECK-O0: (i8* %[[BUF:.*]]) void *test_builtin_os_log(void *buf) { return __builtin_os_log_format(buf, "capabilities: %@", GenString()); diff --git a/clang/test/CodeGenOpenCL/as_type.cl b/clang/test/CodeGenOpenCL/as_type.cl --- a/clang/test/CodeGenOpenCL/as_type.cl +++ b/clang/test/CodeGenOpenCL/as_type.cl @@ -67,7 +67,7 @@ return __builtin_astype(x, int3); } -//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned %[[x:.*]]) +//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned "no-capture-maybe-returned" %[[x:.*]]) //CHECK: %[[cast:.*]] = addrspacecast i32* %[[x]] to i32 addrspace(1)* //CHECK: ret i32 addrspace(1)* %[[cast]] global int* addr_cast(int *x) { diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -252,6 +252,35 @@ static constexpr Attribute::AttrKind ID = Attribute::Returned; }; +/// An abstract interface for all nocapture attributes. +struct AANoCapture : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AANoCapture(Value &V) : AbstractAttribute(V) {} + + /// Return true if we know that the underlying value is not captured in its + /// respective scope. + virtual bool isKnownNoCapture() const = 0; + + /// Return true if we assume that the underlying value is not captured in its + /// respective scope. + virtual bool isAssumedNoCapture() const = 0; + + /// Return true if we know that the underlying value is not captured in its + /// respective scope but we allow it to escape through a "return". + virtual bool isKnownNoCaptureMaybeReturned() const = 0; + + /// Return true if we assume that the underlying value is not captured in its + /// respective scope but we allow it to escape through a "return". + virtual bool isAssumedNoCaptureMaybeReturned() const = 0; + + /// See AbstractState::getAttrKind(). + Attribute::AttrKind getAttrKind() const override { return ID; } + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = Attribute::NoCapture; +}; + /// ---------------------------------------------------------------------------- /// Pass (Manager) Boilerplate /// ---------------------------------------------------------------------------- diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CallGraphSCCPass.h" +#include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" @@ -45,6 +46,9 @@ STATISTIC(NumFnNoReturn, "Number of functions marked noreturn"); +STATISTIC(NumFnArgumentNoCapture, + "Number of function arguments marked no-capture"); + // TODO: Determine a good default value. static cl::opt MaxFixpointIterations("attributor-max-iterations", cl::Hidden, @@ -108,10 +112,15 @@ /// Helper to adjust the statistics. static void bookkeeping(AbstractAttribute::ManifestPosition MP, const Attribute &Attr) { + if (!Attr.isEnumAttribute()) + return; switch (Attr.getKindAsEnum()) { case Attribute::Returned: NumFnArgumentReturned++; return; + case Attribute::NoCapture: + NumFnArgumentNoCapture++; + return; case Attribute::NoReturn: NumFnNoReturn++; return; @@ -192,7 +201,7 @@ case MP_ARGUMENT: { Argument &Arg = *cast(getAssociatedValue()); for (const Attribute &Attr : Attrs) { - if (Arg.hasAttribute(Attr.getKindAsEnum())) { + if (Attr.isEnumAttribute() && Arg.hasAttribute(Attr.getKindAsEnum())) { const Attribute &ExistingAttr = Arg.getParent()->getAttributes().getParamAttr(Arg.getArgNo(), Attr.getKindAsEnum()); @@ -622,6 +631,280 @@ } }; +/// ----------------------- Variable Capturing --------------------------------- + +/// A class to hold the state of for no-capture attributes. +struct AANoCaptureImpl : public AANoCapture, IntegerState { + + /// State encoding bits. A set bit in the state means the property holds. + /// BEST_STATE is the best possible state, 0 the worst possible state. + enum { + NOT_CAPTURED = 1 << 0, + NOT_RETURNED = 1 << 1, + + BEST_STATE = NOT_CAPTURED | NOT_RETURNED + }; + + /// Constructor that takes the value this attribute is associated with (\p V) + /// as well as the function this attribute is related to. + AANoCaptureImpl(Value &V, Function &F) : AANoCapture(V) { + assert(getAssumed() == BEST_STATE); + + // If the value in questions is unused it is not captured. + if (V.getNumUses() == 0) + indicateFixpoint(/* Optimistic */ true); + + // If the enclosing function cannot capture state, nothing is captured. + if (!canFunctionCaptureState(F)) + indicateFixpoint(/* Optimistic */ true); + } + + /// See AANoCapture::isKnownNoCapture(). + bool isKnownNoCapture() const override { return getKnown() == BEST_STATE; } + + /// See AANoCapture::isAssumedNoCapture(...). + bool isAssumedNoCapture() const override { + return getAssumed() == BEST_STATE; + } + + /// See AANoCapture::isKnownNoCaptureMaybeReturned(...). + bool isKnownNoCaptureMaybeReturned() const override { + return getKnown() & NOT_CAPTURED; + } + + /// See AANoCapture::isAssumedNoCaptureMaybeReturned(...). + bool isAssumedNoCaptureMaybeReturned() const override { + return getAssumed() & NOT_CAPTURED; + } + + /// see AbstractAttribute::isAssumedNoCaptureMaybeReturned(...). + virtual void + getDeducedAttributes(SmallVectorImpl &Attrs) const override { + assert(isAssumedNoCaptureMaybeReturned()); + + LLVMContext &Ctx = AnchoredVal.getContext(); + if (isAssumedNoCapture()) + Attrs.emplace_back(Attribute::get(Ctx, Attribute::NoCapture)); + else + Attrs.emplace_back(Attribute::get(Ctx, "no-capture-maybe-returned")); + } + + /// Return true if the function \p F is generally able to capture state. + static bool canFunctionCaptureState(Function &F) { + // A function cannot capture state if it only reads memory and has no other + // way of communication (exceptions and return values). + if (F.onlyReadsMemory() && F.doesNotThrow() && + F.getReturnType()->isVoidTy()) + return false; + + return true; + } + + /// See AbstractAttribute::getState() + ///{ + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + ///} + + /// See AbstractState::getAsStr(). + const std::string getAsStr() const override { + if (isKnownNoCapture()) + return "known not-captured"; + if (isAssumedNoCapture()) + return "assumed not-captured"; + if (isKnownNoCaptureMaybeReturned()) + return "known not-captured-maybe-returned"; + if (isAssumedNoCaptureMaybeReturned()) + return "assumed not-captured-maybe-returned"; + return "assumed-captured"; + } +}; + +/// Attributor-aware capture tracker. +struct AACaptureUseTracker final : public CaptureTracker { + + /// Create a capture tracker that can lookup in-flight abstract attributes + /// through the attributor \p A. If a use leads to a potential capture, + /// \p AssumedNoCapture is set to false and the search is stopped. If a use + /// leads to a return instruction, \p MaybeReturned is set to true and + /// \p AssumedNoCapture is not changed. If a use is found that is currently + /// assumed "no-capture-maybe-returned", the user is added to the + /// \p PotentialCopies set. All values in \p PotentialCopies are later tracked + /// as well. For every explored use we decrement \p RemainingUsesToExplore. + /// Once it reaches 0, the search is stopped and \p AssumedNoCapture is set to + /// false. + AACaptureUseTracker(Attributor &A, AbstractAttribute &QueryingAA, + bool &AssumedNoCapture, bool &MaybeReturned, + SmallVectorImpl &PotentialCopies, + unsigned &RemainingUsesToExplore) + : A(A), QueryingAA(QueryingAA), AssumedNoCapture(AssumedNoCapture), + MaybeReturned(MaybeReturned), PotentialCopies(PotentialCopies), + RemainingUsesToExplore(RemainingUsesToExplore) {} + + /// See CaptureTracker::tooManyUses(). + void tooManyUses() override { AssumedNoCapture = false; } + + /// See CaptureTracker::captured(...). + bool captured(const Use *U) override { + LLVM_DEBUG(errs() << "Check use: " << *U->get() << " in " << *U->getUser() + << "\n"); + + // Because we may reuse the tracker multiple times we keep track of the + // number of explored uses ourselves as well. + if (RemainingUsesToExplore-- == 0) { + LLVM_DEBUG(errs() << " - too many uses to explore\n"); + return shouldContinueTraversal(false); + } + + // Explicitly catch return instructions. + if (isa(U->getUser())) + return shouldContinueTraversal(true, /* Returns */ true); + + // Special case for comparisons agains null. We consider them as + // non-capturing. + // TODO: This should only be valid if 0 is not a valid address. + if (ICmpInst *ICmp = dyn_cast(U->getUser())) { + unsigned Idx = (ICmp->getOperand(0) == U->get()); + if (isa(ICmp->getOperand(Idx))) + if (cast(ICmp->getOperand(Idx))->isNullValue()) + return shouldContinueTraversal(true); + return shouldContinueTraversal(false); + } + + // For now we only use special logic for call sites. However, the tracker + // itself knows about a lot of other non-capturing cases already. + CallSite CS(U->getUser()); + if (!CS || !CS.isArgOperand(U)) + return shouldContinueTraversal(false); + + // TODO: This might be checked in the tracker already. + unsigned ArgNo = CS.getArgumentNo(U); + if (CS.paramHasAttr(ArgNo, Attribute::NoCapture)) + return shouldContinueTraversal(true); + + // If we do not know the called function we have to assume the use captures. + if (!CS.getCalledFunction()) + return shouldContinueTraversal(false); + + // If the called function cannot capture state, nothing is captured. + Function &F = *CS.getCalledFunction(); + if (!AANoCaptureImpl::canFunctionCaptureState(F)) + return shouldContinueTraversal(true); + + // Handle var-arg functions. + if (F.isVarArg() && F.arg_size() <= ArgNo) + return shouldContinueTraversal(false); + + // If we have a abstract no-capture attribute for the argument we can use it + // to justify a non-capture attribute here. This allows recursion! + auto *ArgNoCaptureAA = A.getAAFor(QueryingAA, F, ArgNo); + if (ArgNoCaptureAA) { + if (ArgNoCaptureAA->isAssumedNoCapture()) + return shouldContinueTraversal(true); + if (ArgNoCaptureAA->isAssumedNoCaptureMaybeReturned()) { + PotentialCopies.push_back(CS.getInstruction()); + return shouldContinueTraversal(true); + } + } + + // Check for an existing attribute to justify no-capture for this use. + if (F.getAttributes().hasParamAttr(ArgNo, "no-capture-maybe-returned")) { + PotentialCopies.push_back(CS.getInstruction()); + return shouldContinueTraversal(true); + } + + // Lastly, we could not find a reason no-capture can be assumed so we don't. + return shouldContinueTraversal(false); + } + + /// See CaptureTracker::shouldExplore(...). + bool shouldExplore(const Use *U) override { return true; } + + /// Update the state according to \p ShouldContinue and return the appropriate + /// value for use in the CaptureTracker::captured() interface. The \p Returns + /// flag indicate if the use allows the value to be "returned" out of the + /// current scope. + bool shouldContinueTraversal(bool ShouldContinue, bool Returns = false) { + LLVM_DEBUG(dbgs() << " - " << (ShouldContinue ? "will not" : "might") + << " capture" << (Returns ? ", but may return\n" : "\n")); + AssumedNoCapture = ShouldContinue; + MaybeReturned |= Returns; + return !AssumedNoCapture; + } + +private: + /// The attributor providing in-flight abstract attributes. + Attributor &A; + + /// The abstract attribute currently updated. + AbstractAttribute &QueryingAA; + + /// The current state tracking potential capturing. + bool &AssumedNoCapture; + + /// The current state tracking potential use as return value. + bool &MaybeReturned; + + /// Set of potential copies of the tracked value. + SmallVectorImpl &PotentialCopies; + + /// Global counter to limit the number of explored uses. + unsigned &RemainingUsesToExplore; +}; + +/// An AA to represent the no-capture argument attribute. +struct AANoCaptureArgument final : public AANoCaptureImpl { + + /// See AANoCaptureImpl::AANoCaptureImpl(...). + AANoCaptureArgument(Argument &Arg) : AANoCaptureImpl(Arg, *Arg.getParent()) { + if (Arg.hasAttribute(Attribute::NoCapture)) + indicateFixpoint(/* Optimistic */ true); + } + + /// See AbstractAttribute::updateImpl(Attributor &A). + virtual ChangeStatus updateImpl(Attributor &A) override; + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_ARGUMENT; + } +}; + +ChangeStatus AANoCaptureArgument::updateImpl(Attributor &A) { + // The current assumed state used to determine a change. + auto AssumedState = getAssumed(); + + bool MaybeReturned = false; + bool AssumedNoCapture = true; + SmallVector PotentialCopies; + unsigned RemainingUsesToExplore = DefaultMaxUsesToExplore; + + // Use the CaptureTracker interface and logic with the specialized tracker, + // defined in AACaptureUseTracker, that can look at in-flight abstract + // attributes and directly updates the assumed state. + AACaptureUseTracker Tracker(A, *this, AssumedNoCapture, MaybeReturned, + PotentialCopies, RemainingUsesToExplore); + + unsigned Idx = 0; + // Check all potential copies of the associated value until we can assume none + // will be captured or we have to assume at least one might be. + PotentialCopies.push_back(getAssociatedValue()); + while (AssumedNoCapture && Idx < PotentialCopies.size()) + PointerMayBeCaptured(PotentialCopies[Idx++], &Tracker); + + // Since we ignored "returns" of the associated value or any of the potential + // copies, we can give up if we cannot assume no-capture anymore. If however + // no-capture can be assumed we still have to track if the value, or any + // potential copy, might be returned. + if (!AssumedNoCapture) + indicateFixpoint(/* Optimistic */ false); + else if (MaybeReturned) + Assumed = Assumed & ~NOT_RETURNED; + + return (AssumedState == getAssumed()) ? ChangeStatus::UNCHANGED + : ChangeStatus::CHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -805,6 +1088,15 @@ // Every function might be "no-return". registerAA(*new AANoReturnFunction(F)); + // For each argument we check if we can derive attributes. + for (Argument &Arg : F.args()) { + + // So far only pointer arguments are interesting. However, "returned" + // is also derived but as a "function return attribute" (see above). + if (Arg.getType()->isPointerTy()) + registerAA(*new AANoCaptureArgument(Arg)); + } + // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be querried by abstract attributes // during their initialziation or update. diff --git a/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll b/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll --- a/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll +++ b/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll @@ -15,7 +15,7 @@ ret i32* %tmp } -; CHECK: define i32* @c(i32* readnone returned %r) +; CHECK: define i32* @c(i32* readnone returned "no-capture-maybe-returned" %r) @g = global i32 0 define i32* @c(i32 *%r) { %a = icmp eq i32* %r, null diff --git a/llvm/test/Transforms/FunctionAttrs/SCC1.ll b/llvm/test/Transforms/FunctionAttrs/SCC1.ll --- a/llvm/test/Transforms/FunctionAttrs/SCC1.ll +++ b/llvm/test/Transforms/FunctionAttrs/SCC1.ll @@ -35,7 +35,8 @@ ; 1 functionattrs - Number of functions marked as norecurse ; 6 functionattrs - Number of functions marked argmemonly ; 6 functionattrs - Number of functions marked as nounwind -; 16 functionattrs - Number of arguments marked nocapture +; 10 functionattrs - Number of arguments marked nocapture +; 6 functionattrs - Number of arguments marked nocapture-maybe-returned ; 4 functionattrs - Number of arguments marked readnone ; 6 functionattrs - Number of arguments marked writeonly ; 6 functionattrs - Number of arguments marked readonly @@ -45,7 +46,7 @@ ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND:[0-9]*]] +; CHECK: define dso_local i32* @external_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND:[0-9]*]] define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0) @@ -55,7 +56,7 @@ ret i32* %call3 } -; CHECK: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret0_nw(i32* returned "no-capture-maybe-returned" %n0, i32* nocapture %w0) #[[NOUNWIND]] define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: %r0 = alloca i32, align 4 @@ -83,7 +84,7 @@ ret i32* %retval.0 } -; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret1_rrw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %r1, i32* nocapture %w0) #[[NOUNWIND]] define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -114,7 +115,7 @@ ret i32* %retval.0 } -; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0) #[[NOREC_NOUNWIND:[0-9]*]] +; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOREC_NOUNWIND:[0-9]*]] define dso_local i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %tobool = icmp ne i32* %n0, null @@ -132,7 +133,7 @@ ret i32* %w0 } -; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0) #[[NOUNWIND]] +; CHECK: define internal i32* @internal_ret1_rw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]] define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -157,7 +158,7 @@ ret i32* %retval.0 } -; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND]] +; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]] define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) diff --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -27,8 +27,7 @@ ; return p == 0; ; } ; -; FIXME: no-capture missing for %p -; CHECK: define dso_local i32 @is_null_return(i32* readnone %p) +; CHECK: define dso_local i32 @is_null_return(i32* nocapture readnone %p) ; define dso_local i32 @is_null_return(i32* %p) #0 { entry: @@ -47,8 +46,7 @@ ; return 0; ; } ; -; FIXME: no-capture missing for %p -; CHECK: define dso_local i32 @is_null_control(i32* readnone %p) +; CHECK: define dso_local i32 @is_null_control(i32* nocapture readnone %p) ; define dso_local i32 @is_null_control(i32* %p) #0 { entry: @@ -98,8 +96,6 @@ ; return srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(a)))))))))))))))); ; } ; -; FIXME: no-capture missing for %a -; FIXME: returned missing for %a ; FIXME: We should *not* derive any attributes for the return value not present on the argument! ; CHECK: define dso_local noalias nonnull i32* @srec16(i32* nocapture readnone %a) ; define dso_local i32* @srec16(i32* nocapture readnone %a) @@ -127,15 +123,12 @@ ; TEST 5: ; -; FIXME: no-capture missing for %a -; CHECK: define dso_local float* @scc_A(i32* readnone returned %a) +; CHECK: define dso_local float* @scc_A(i32* readnone returned "no-capture-maybe-returned" %a) ; -; FIXME: no-capture missing for %a -; CHECK: define dso_local i64* @scc_B(double* readnone returned %a) +; CHECK: define dso_local i64* @scc_B(double* readnone returned "no-capture-maybe-returned" %a) ; ; FIXME: readnone missing for %s -; FIXME: no-capture missing for %a -; CHECK: define dso_local i8* @scc_C(i16* returned %a) +; CHECK: define dso_local i8* @scc_C(i16* returned "no-capture-maybe-returned" %a) ; ; float *scc_A(int *a) { ; return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a); @@ -264,7 +257,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned %a) +; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a) ; define dso_local i64* @not_captured_but_returned_0(i64* %a) #0 { entry: @@ -280,7 +273,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* %a) +; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* "no-capture-maybe-returned" %a) ; define dso_local i64* @not_captured_but_returned_1(i64* %a) #0 { entry: @@ -296,8 +289,7 @@ ; not_captured_but_returned_1(a); ; } ; -; FIXME: no-capture missing for %a -; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* %a) +; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* nocapture %a) ; define dso_local void @test_not_captured_but_returned_calls(i64* %a) #0 { entry: @@ -313,7 +305,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned %a) +; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned "no-capture-maybe-returned" %a) ; define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* %a) #0 { entry: @@ -345,7 +337,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) +; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* "no-capture-maybe-returned" %a) ; define dso_local i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 { entry: diff --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -168,7 +168,7 @@ ; TEST 2 ; -; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] ; BOTH: define dso_local double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] ; BOTH: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r) [[NoInlineNoUnwindReadnoneUwtable]] ; @@ -176,8 +176,8 @@ ; FNATTR: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] ; FNATTR: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]] ; -; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] -; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* %b) [[NoInlineNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b) [[NoInlineNoUnwindReadnoneUwtable]] ; ATTRIBUTOR: define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) [[NoInlineNoUnwindReadnoneUwtable]] ; ; double* ptr_scc_r1(double* a, double* b, double* r); @@ -262,9 +262,9 @@ ; return *a ? a : ret0(ret0(ret0(...ret0(a)...))); ; } ; -; FEW_IT: define dso_local i32* @ret0(i32* %a) +; FEW_IT: define dso_local i32* @ret0(i32* "no-capture-maybe-returned" %a) ; FNATTR: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindUwtable:#[0-9]*]] -; BOTH: define dso_local i32* @ret0(i32* readonly returned %a) [[NoInlineNoReturnNoUnwindReadonlyUwtable:#[0-9]*]] +; BOTH: define dso_local i32* @ret0(i32* readonly returned "no-capture-maybe-returned" %a) [[NoInlineNoReturnNoUnwindReadonlyUwtable:#[0-9]*]] define dso_local i32* @ret0(i32* %a) #0 { entry: %v = load i32, i32* %a, align 4 @@ -303,9 +303,9 @@ ; ; BOTH: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]] ; -; BOTH: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable]] ; FNATTR: define dso_local i32* @calls_unknown_fn(i32* readnone %r) [[NoInlineNoUnwindUwtable:#[0-9]*]] -; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]] +; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable:#[0-9]*]] ; declare void @unknown_fn(i32* (i32*)*) #0 @@ -413,8 +413,8 @@ ; } ; ; FNATTR: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] -; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned %b) [[NoInlineNoUnwindUwtable]] -; BOTH: define dso_local double* @bitcast(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] ; define dso_local double* @bitcast(i32* %b) #0 { entry: @@ -433,8 +433,8 @@ ; } ; ; FNATTR: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] -; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned %b) [[NoInlineNoUnwindUwtable]] -; BOTH: define dso_local double* @bitcasts_select_and_phi(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoUnwindUwtable]] +; BOTH: define dso_local double* @bitcasts_select_and_phi(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] ; define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 { entry: diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll --- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll @@ -6,21 +6,21 @@ ; Function Attrs: argmemonly define i32* @given_argmem_infer_readnone(i32* %p) #0 { -; CHECK: define i32* @given_argmem_infer_readnone(i32* readnone returned %p) #0 { +; CHECK: define i32* @given_argmem_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 { entry: ret i32* %p } ; Function Attrs: inaccessiblememonly define i32* @given_inaccessible_infer_readnone(i32* %p) #1 { -; CHECK: define i32* @given_inaccessible_infer_readnone(i32* readnone returned %p) #0 { +; CHECK: define i32* @given_inaccessible_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 { entry: ret i32* %p } ; Function Attrs: inaccessiblemem_or_argmemonly define i32* @given_inaccessible_or_argmem_infer_readnone(i32* %p) #2 { -; CHECK: define i32* @given_inaccessible_or_argmem_infer_readnone(i32* readnone returned %p) #0 { +; CHECK: define i32* @given_inaccessible_or_argmem_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 { entry: ret i32* %p } diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -3,7 +3,7 @@ @g = global i32* null ; [#uses=1] -; CHECK: define i32* @c1(i32* readnone returned %q) +; CHECK: define i32* @c1(i32* readnone returned "no-capture-maybe-returned" %q) define i32* @c1(i32* %q) { ret i32* %q } @@ -134,7 +134,7 @@ ret void } -; CHECK: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1) +; CHECK: define void @test1_1(i8* nocapture %x1_1, i8* nocapture %y1_1) ; It would be acceptable to add readnone to %y1_1 and %y1_2. define void @test1_1(i8* %x1_1, i8* %y1_1) { call i8* @test1_2(i8* %x1_1, i8* %y1_1) @@ -142,7 +142,7 @@ ret void } -; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2) +; CHECK: define i8* @test1_2(i8* nocapture %x1_2, i8* returned "no-capture-maybe-returned" %y1_2) define i8* @test1_2(i8* %x1_2, i8* %y1_2) { call void @test1_1(i8* %x1_2, i8* %y1_2) store i32* null, i32** @g @@ -156,21 +156,21 @@ ret void } -; CHECK: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3) +; CHECK: define void @test3(i8* nocapture %x3, i8* nocapture readnone %y3, i8* nocapture %z3) define void @test3(i8* %x3, i8* %y3, i8* %z3) { call void @test3(i8* %z3, i8* %y3, i8* %x3) store i32* null, i32** @g ret void } -; CHECK: define void @test4_1(i8* %x4_1) +; CHECK: define void @test4_1(i8* nocapture readnone %x4_1) define void @test4_1(i8* %x4_1) { call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1) store i32* null, i32** @g ret void } -; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2) +; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture readnone %z4_2) define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) { call void @test4_1(i8* null) store i32* null, i32** @g diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll --- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -12,7 +12,7 @@ ret void } -; CHECK: define i8* @test2(i8* readnone returned %p) +; CHECK: define i8* @test2(i8* readnone returned "no-capture-maybe-returned" %p) define i8* @test2(i8* %p) { store i32 0, i32* @x ret i8* %p @@ -54,13 +54,13 @@ ret void } -; CHECK: define i32* @test8_1(i32* readnone returned %p) +; CHECK: define i32* @test8_1(i32* readnone returned "no-capture-maybe-returned" %p) define i32* @test8_1(i32* %p) { entry: ret i32* %p } -; CHECK: define void @test8_2(i32* %p) +; CHECK: define void @test8_2(i32* nocapture %p) define void @test8_2(i32* %p) { entry: %call = call i32* @test8_1(i32* %p)