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 @@ -217,6 +217,24 @@ bool isAssumedReadNone(Attributor &A, const IRPosition &IRP, const AbstractAttribute &QueryingAA, bool &IsKnown); +/// Return true if \p ToI is potentially reachable from \p FromI. The two +/// instructions do not need to be in the same function. \p GoBackwardsCB +/// can be provided to convey domain knowledge about the "lifespan" the user is +/// interested in. By default, the callers of \p FromI are checked as well to +/// determine if \p ToI can be reached. If the query is not interested in +/// callers beyond a certain point, e.g., a GPU kernel entry or the function +/// containing an alloca, the \p GoBackwardsCB should return false. +bool isPotentiallyReachable( + Attributor &A, const Instruction &FromI, const Instruction &ToI, + const AbstractAttribute &QueryingAA, + std::function GoBackwardsCB = nullptr); + +/// Same as above but it is sufficient to reach any instruction in \p ToFn. +bool isPotentiallyReachable( + Attributor &A, const Instruction &FromI, const Function &ToFn, + const AbstractAttribute &QueryingAA, + std::function GoBackwardsCB); + } // namespace AA /// The value passed to the line option that defines the maximal initialization @@ -4635,11 +4653,12 @@ /// If the function represented by this possition can reach \p Fn. virtual bool canReach(Attributor &A, const Function &Fn) const = 0; - /// Can \p CB reach \p Fn + /// Can \p CB reach \p Fn. virtual bool canReach(Attributor &A, CallBase &CB, const Function &Fn) const = 0; - /// Can \p Inst reach \p Fn + /// Can \p Inst reach \p Fn. + /// See also AA::isPotentiallyReachable. virtual bool instructionCanReach(Attributor &A, const Instruction &Inst, const Function &Fn, bool UseBackwards = true) const = 0; 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 @@ -436,6 +436,121 @@ /* RequireReadNone */ true, IsKnown); } +static bool +isPotentiallyReachable(Attributor &A, const Instruction &FromI, + const Instruction *ToI, const Function &ToFn, + const AbstractAttribute &QueryingAA, + std::function GoBackwardsCB) { + LLVM_DEBUG(dbgs() << "[AA] isPotentiallyReachable @" << ToFn.getName() + << " from " << FromI << " [GBCB: " << bool(GoBackwardsCB) + << "]\n"); + + SmallPtrSet Visited; + SmallVector Worklist; + Worklist.push_back(&FromI); + + while (!Worklist.empty()) { + const Instruction *CurFromI = Worklist.pop_back_val(); + if (!Visited.insert(CurFromI).second) + continue; + + const Function *FromFn = CurFromI->getFunction(); + if (FromFn == &ToFn) { + if (!ToI) + return true; + LLVM_DEBUG(dbgs() << "[AA] check " << *ToI << " from " << *CurFromI + << " intraprocedurally\n"); + const auto &ReachabilityAA = A.getAAFor( + QueryingAA, IRPosition::function(ToFn), DepClassTy::OPTIONAL); + bool Result = ReachabilityAA.isAssumedReachable(A, *CurFromI, *ToI); + LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " " + << (Result ? "can potentially " : "cannot ") << "reach " + << *ToI << " [Intra]\n"); + if (Result) + return true; + continue; + } + + // TODO: If we can go arbitrarily backwards we will eventually reach an + // entry point that can reach ToI. Only once this takes a set of blocks + // through which we cannot go, or once we track internal functions not + // accessible from the outside, it makes sense to perform backwards analysis + // in the absence of a GoBackwardsCB. + if (!GoBackwardsCB) { + LLVM_DEBUG(dbgs() << "[AA] check @" << ToFn.getName() << " from " + << *CurFromI << " is not checked backwards, abort\n"); + return true; + } + + // Check if the current instruction is already known to reach the ToFn. + const auto &FnReachabilityAA = A.getAAFor( + QueryingAA, IRPosition::function(*FromFn), DepClassTy::OPTIONAL); + bool Result = FnReachabilityAA.instructionCanReach( + A, *CurFromI, ToFn, /* UseBackwards */ false); + LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " in @" << FromFn->getName() + << " " << (Result ? "can potentially " : "cannot ") + << "reach @" << ToFn.getName() << " [FromFn]\n"); + if (Result) + return true; + + // If we do not go backwards from the FromFn we are done here and so far we + // could not find a way to reach ToFn/ToI. + if (!GoBackwardsCB(*FromFn)) + continue; + + LLVM_DEBUG(dbgs() << "Stepping backwards to the call sites of @" + << FromFn->getName() << "\n"); + + auto CheckCallSite = [&](AbstractCallSite ACS) { + CallBase *CB = ACS.getInstruction(); + if (!CB) + return false; + + if (isa(CB)) + return false; + + Instruction *Inst = CB->getNextNonDebugInstruction(); + Worklist.push_back(Inst); + return true; + }; + + bool AllCallSitesKnown; + Result = !A.checkForAllCallSites(CheckCallSite, *FromFn, + /* RequireAllCallSites */ true, + &QueryingAA, AllCallSitesKnown); + if (Result) { + LLVM_DEBUG(dbgs() << "[AA] stepping back to call sites from " << *CurFromI + << " in @" << FromFn->getName() + << " failed, give up\n"); + return true; + } + + LLVM_DEBUG(dbgs() << "[AA] stepped back to call sites from " << *CurFromI + << " in @" << FromFn->getName() + << " worklist size is: " << Worklist.size() << "\n"); + } + return false; +} + +bool AA::isPotentiallyReachable( + Attributor &A, const Instruction &FromI, const Instruction &ToI, + const AbstractAttribute &QueryingAA, + std::function GoBackwardsCB) { + LLVM_DEBUG(dbgs() << "[AA] isPotentiallyReachable " << ToI << " from " + << FromI << " [GBCB: " << bool(GoBackwardsCB) << "]\n"); + const Function *ToFn = ToI.getFunction(); + return ::isPotentiallyReachable(A, FromI, &ToI, *ToFn, QueryingAA, + GoBackwardsCB); +} + +bool AA::isPotentiallyReachable( + Attributor &A, const Instruction &FromI, const Function &ToFn, + const AbstractAttribute &QueryingAA, + std::function GoBackwardsCB) { + return ::isPotentiallyReachable(A, FromI, /* ToI */ nullptr, ToFn, QueryingAA, + GoBackwardsCB); +} + /// Return true if \p New is equal or worse than \p Old. static bool isEqualOrWorse(const Attribute &New, const Attribute &Old) { if (!Old.isIntAttribute()) @@ -1466,9 +1581,11 @@ InvalidAA->Deps.pop_back(); AbstractAttribute *DepAA = cast(Dep.getPointer()); if (Dep.getInt() == unsigned(DepClassTy::OPTIONAL)) { + LLVM_DEBUG(dbgs() << " - recompute: " << *DepAA); Worklist.insert(DepAA); continue; } + LLVM_DEBUG(dbgs() << " - invalidate: " << *DepAA); DepAA->getState().indicatePessimisticFixpoint(); assert(DepAA->getState().isAtFixpoint() && "Expected fixpoint state!"); if (!DepAA->getState().isValidState()) diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -1130,19 +1130,63 @@ QueryingAA, IRPosition::function(*LI.getFunction()), DepClassTy::OPTIONAL); - // Helper to determine if the instruction may reach the load. - auto IsReachableFrom = [&](const Instruction &I) { - const auto &ReachabilityAA = A.getAAFor( - QueryingAA, IRPosition::function(*I.getFunction()), - DepClassTy::OPTIONAL); - return ReachabilityAA.isAssumedReachable(A, I, LI); - }; - - const bool CanUseCFGResoning = - NoRecurseAA.isKnownNoRecurse() && CanIgnoreThreading(LI); + const bool CanUseCFGResoning = CanIgnoreThreading(LI); InformationCache &InfoCache = A.getInfoCache(); const DominatorTree *DT = - InfoCache.getAnalysisResultForFunction(Scope); + NoRecurseAA.isKnownNoRecurse() + ? InfoCache.getAnalysisResultForFunction( + Scope) + : nullptr; + + enum GPUAddressSpace : unsigned { + Generic = 0, + Global = 1, + Shared = 3, + Constant = 4, + Local = 5, + }; + + // Helper to check if a value has "kernel lifetime", that is it will not + // outlive a GPU kernel. This is true for shared, constant, and local + // globals on AMD and NVIDIA GPUs. + auto HasKernelLifetime = [&](Value *V, Module &M) { + Triple T(M.getTargetTriple()); + if (!(T.isAMDGPU() || T.isNVPTX())) + return false; + switch (V->getType()->getPointerAddressSpace()) { + case GPUAddressSpace::Shared: + case GPUAddressSpace::Constant: + case GPUAddressSpace::Local: + return true; + default: + return false; + }; + }; + + // The IsLiveInCalleeCB will be used by the AA::isPotentiallyReachable query + // to determine if we should look at reachability from the callee. For + // certain pointers we know the lifetime and we do not have to step into the + // callee to determine reachability as the pointer would be dead in the + // callee. See the conditional initialization below. + std::function IsLiveInCalleeCB; + + if (auto *AI = dyn_cast(&getAssociatedValue())) { + // If the alloca containing function is not recursive the alloca + // must be dead in the callee. + const Function *AIFn = AI->getFunction(); + const auto &NoRecurseAA = A.getAAFor( + *this, IRPosition::function(*AIFn), DepClassTy::OPTIONAL); + if (NoRecurseAA.isAssumedNoRecurse()) { + IsLiveInCalleeCB = [AIFn](const Function &Fn) { return AIFn != &Fn; }; + } + } else if (auto *GV = dyn_cast(&getAssociatedValue())) { + // If the global has kernel lifetime we can stop if we reach a kernel + // as it is "dead" in the (unknown) callees. + if (HasKernelLifetime(GV, *GV->getParent())) + IsLiveInCalleeCB = [](const Function &Fn) { + return !Fn.hasFnAttribute("kernel"); + }; + } auto AccessCB = [&](const Access &Acc, bool Exact) { if (!Acc.isWrite()) @@ -1151,7 +1195,8 @@ // For now we only filter accesses based on CFG reasoning which does not // work yet if we have threading effects, or the access is complicated. if (CanUseCFGResoning) { - if (!IsReachableFrom(*Acc.getLocalInst())) + if (!AA::isPotentiallyReachable(A, *Acc.getLocalInst(), LI, QueryingAA, + IsLiveInCalleeCB)) return true; if (DT && Exact && (Acc.getLocalInst()->getFunction() == LI.getFunction()) && @@ -9680,7 +9725,7 @@ if (!Reachability.isAssumedReachable(A, Inst, CBInst)) return true; - const auto &CB = cast(CBInst); + auto &CB = cast(CBInst); const AACallEdges &AAEdges = A.getAAFor( *this, IRPosition::callsite_function(CB), DepClassTy::REQUIRED); @@ -9690,47 +9735,8 @@ bool UsedAssumedInformation = false; return A.checkForAllCallLikeInstructions(CheckCallBase, *this, - UsedAssumedInformation); - } - - ChangeStatus checkReachableBackwards(Attributor &A, QuerySet &Set) { - ChangeStatus Change = ChangeStatus::UNCHANGED; - - // For all remaining instruction queries, check - // callers. A call inside that function might satisfy the query. - auto CheckCallSite = [&](AbstractCallSite CallSite) { - CallBase *CB = CallSite.getInstruction(); - if (!CB) - return false; - - if (isa(CB)) - return false; - - Instruction *Inst = CB->getNextNonDebugInstruction(); - const AAFunctionReachability &AA = A.getAAFor( - *this, IRPosition::function(*Inst->getFunction()), - DepClassTy::REQUIRED); - for (const Function *Fn : make_early_inc_range(Set.Unreachable)) { - if (AA.instructionCanReach(A, *Inst, *Fn, /* UseBackwards */ false)) { - Set.markReachable(*Fn); - Change = ChangeStatus::CHANGED; - } - } - return true; - }; - - bool NoUnknownCall = true; - if (A.checkForAllCallSites(CheckCallSite, *this, true, NoUnknownCall)) - return Change; - - // If we don't know all callsites we have to assume that we can reach fn. - for (auto &QSet : InstQueriesBackwards) { - if (!QSet.second.CanReachUnknownCallee) - Change = ChangeStatus::CHANGED; - QSet.second.CanReachUnknownCallee = true; - } - - return Change; + UsedAssumedInformation, + /* CheckBBLivenessOnly */ true); } public: @@ -9783,12 +9789,15 @@ bool instructionCanReach(Attributor &A, const Instruction &Inst, const Function &Fn, bool UseBackwards) const override { - const auto &Reachability = &A.getAAFor( + if (UseBackwards) + return AA::isPotentiallyReachable(A, Inst, Fn, *this, nullptr); + + const auto &Reachability = A.getAAFor( *this, IRPosition::function(*getAssociatedFunction()), DepClassTy::REQUIRED); SmallVector CallEdges; - bool AllKnown = getReachableCallEdges(A, *Reachability, Inst, CallEdges); + bool AllKnown = getReachableCallEdges(A, Reachability, Inst, CallEdges); // Attributor returns attributes as const, so this function has to be // const for users of this attribute to use it without having to do // a const_cast. @@ -9798,25 +9807,7 @@ if (!AllKnown) InstQSet.CanReachUnknownCallee = true; - bool ForwardsResult = InstQSet.isReachable(A, *this, CallEdges, Fn); - if (ForwardsResult) - return true; - // We are done. - if (!UseBackwards) - return false; - - QuerySet &InstBackwardsQSet = NonConstThis->InstQueriesBackwards[&Inst]; - - Optional BackwardsCached = InstBackwardsQSet.isCachedReachable(Fn); - if (BackwardsCached.hasValue()) - return BackwardsCached.getValue(); - - // Assume unreachable, to prevent problems. - InstBackwardsQSet.Unreachable.insert(&Fn); - - // Check backwards reachability. - NonConstThis->checkReachableBackwards(A, InstBackwardsQSet); - return InstBackwardsQSet.isCachedReachable(Fn).getValue(); + return InstQSet.isReachable(A, *this, CallEdges, Fn); } /// See AbstractAttribute::updateImpl(...). @@ -9854,10 +9845,6 @@ Change |= InstPair.second.update(A, *this, CallEdges); } - // Update backwards queries. - for (auto &QueryPair : InstQueriesBackwards) - Change |= checkReachableBackwards(A, QueryPair.second); - return Change; } @@ -9886,9 +9873,6 @@ /// This is for instruction queries than scan "forward". DenseMap InstQueries; - - /// This is for instruction queries than scan "backward". - DenseMap InstQueriesBackwards; }; /// ---------------------- Assumption Propagation ------------------------------ diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/alignment.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/alignment.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/alignment.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/alignment.ll @@ -78,7 +78,7 @@ ; CHECK-LABEL: define {{[^@]+}}@callercaller ; CHECK-SAME: () #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 -; CHECK-NEXT: ret i32 3 +; CHECK-NEXT: ret i32 undef ; %B = alloca i32 store i32 2, i32* %B diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/basictest.ll @@ -35,7 +35,7 @@ ; CHECK-LABEL: define {{[^@]+}}@callercaller ; CHECK-SAME: () #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 -; CHECK-NEXT: ret i32 3 +; CHECK-NEXT: ret i32 undef ; %B = alloca i32 store i32 2, i32* %B diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll @@ -134,15 +134,15 @@ ; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0 ; IS__TUNIT_NPM-NEXT: store i32 1, i32* [[TMP1]], align 8 ; IS__TUNIT_NPM-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1 -; IS__TUNIT_NPM-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* -; IS__TUNIT_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST]], align 8 -; IS__TUNIT_NPM-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i64 0, i32 1 -; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_1]], align 8 -; IS__TUNIT_NPM-NEXT: [[C0:%.*]] = call i32 @f(i32 [[TMP0]], i64 [[TMP1]]) #[[ATTR2:[0-9]+]] ; IS__TUNIT_NPM-NEXT: [[S_CAST1:%.*]] = bitcast %struct.ss* [[S]] to i32* -; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST1]], align 32 +; IS__TUNIT_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[S_CAST1]], align 8 ; IS__TUNIT_NPM-NEXT: [[S_0_12:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i64 0, i32 1 -; IS__TUNIT_NPM-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_12]], align 32 +; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i64, i64* [[S_0_12]], align 8 +; IS__TUNIT_NPM-NEXT: [[C0:%.*]] = call i32 @f(i32 [[TMP0]], i64 [[TMP1]]) #[[ATTR2:[0-9]+]] +; IS__TUNIT_NPM-NEXT: [[S_CAST:%.*]] = bitcast %struct.ss* [[S]] to i32* +; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[S_CAST]], align 32 +; IS__TUNIT_NPM-NEXT: [[S_0_1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i64 0, i32 1 +; IS__TUNIT_NPM-NEXT: [[TMP3:%.*]] = load i64, i64* [[S_0_1]], align 32 ; IS__TUNIT_NPM-NEXT: [[C1:%.*]] = call i32 @g(i32 [[TMP2]], i64 [[TMP3]]) #[[ATTR2]] ; IS__TUNIT_NPM-NEXT: [[A:%.*]] = add i32 [[C0]], [[C1]] ; IS__TUNIT_NPM-NEXT: ret i32 [[A]] diff --git a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll --- a/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll +++ b/llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll @@ -55,9 +55,9 @@ ; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR0:[0-9]+]] { ; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret0_nw(i32* nofree [[N0]], i32* nofree [[W0]]) #[[ATTR3:[0-9]+]] -; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rrw(i32* nofree align 4 [[R0]], i32* nofree align 4 [[R0]], i32* nofree [[W0]]) #[[ATTR3]] -; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly align 4 [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) #[[ATTR4:[0-9]+]] -; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @internal_ret1_rw(i32* nofree align 4 [[R0]], i32* nofree [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rrw(i32* nofree align 4 [[R0]], i32* nofree align 4 [[R0]], i32* nofree [[W0]]) #[[ATTR4:[0-9]+]] +; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly align 4 [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) #[[ATTR5:[0-9]+]] +; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @internal_ret1_rw(i32* nofree align 4 [[R0]], i32* nofree [[W0]]) #[[ATTR4]] ; IS__CGSCC____-NEXT: ret i32* [[CALL3]] ; entry: @@ -108,10 +108,10 @@ ; IS__CGSCC____-NEXT: store i32 3, i32* [[R0]], align 4 ; IS__CGSCC____-NEXT: store i32 5, i32* [[R1]], align 4 ; IS__CGSCC____-NEXT: store i32 1, i32* [[W0]], align 4 -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR4]] ; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] ; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] -; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R0]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR5:[0-9]+]] +; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R0]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR5]] ; IS__CGSCC____-NEXT: [[CALL4:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R1]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR5]] ; IS__CGSCC____-NEXT: [[CALL5:%.*]] = call i32* @internal_ret0_nw(i32* nofree [[N0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] ; IS__CGSCC____-NEXT: br label [[RETURN]] @@ -184,12 +184,12 @@ ; IS__CGSCC____: if.then: ; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] ; IS__CGSCC____: if.end: -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) #[[ATTR4]] ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 ; IS__CGSCC____-NEXT: [[TMP2:%.*]] = load i32, i32* [[R1]], align 4 ; IS__CGSCC____-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]] ; IS__CGSCC____-NEXT: store i32 [[ADD]], i32* [[W0]], align 4 -; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR6:[0-9]+]] +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR4]] ; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32* @internal_ret0_nw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] ; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[W0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] ; IS__CGSCC____-NEXT: [[CALL4:%.*]] = call i32* @external_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[R1]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] @@ -264,27 +264,49 @@ } define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { -; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind -; CHECK-LABEL: define {{[^@]+}}@internal_ret1_rw -; CHECK-SAME: (i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[R0]], align 4 -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 -; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] -; CHECK: if.then: -; CHECK-NEXT: br label [[RETURN:%.*]] -; CHECK: if.end: -; CHECK-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) #[[ATTR3:[0-9]+]] -; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 -; CHECK-NEXT: store i32 [[TMP1]], i32* [[W0]], align 4 -; CHECK-NEXT: [[CALL1:%.*]] = call i32* @internal_ret0_nw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] -; CHECK-NEXT: [[CALL2:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[W0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] -; CHECK-NEXT: [[CALL3:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R0]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR4:[0-9]+]] -; CHECK-NEXT: [[CALL4:%.*]] = call i32* @external_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] -; CHECK-NEXT: br label [[RETURN]] -; CHECK: return: -; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32* [ [[CALL4]], [[IF_END]] ], [ [[W0]], [[IF_THEN]] ] -; CHECK-NEXT: ret i32* [[RETVAL_0]] +; IS__TUNIT____: Function Attrs: argmemonly nofree norecurse nosync nounwind +; IS__TUNIT____-LABEL: define {{[^@]+}}@internal_ret1_rw +; IS__TUNIT____-SAME: (i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR1]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[TMP0:%.*]] = load i32, i32* [[R0]], align 4 +; IS__TUNIT____-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 +; IS__TUNIT____-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] +; IS__TUNIT____: if.then: +; IS__TUNIT____-NEXT: br label [[RETURN:%.*]] +; IS__TUNIT____: if.end: +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 +; IS__TUNIT____-NEXT: store i32 [[TMP1]], i32* [[W0]], align 4 +; IS__TUNIT____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret0_nw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: [[CALL2:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[W0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: [[CALL3:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R0]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR4]] +; IS__TUNIT____-NEXT: [[CALL4:%.*]] = call i32* @external_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__TUNIT____-NEXT: br label [[RETURN]] +; IS__TUNIT____: return: +; IS__TUNIT____-NEXT: [[RETVAL_0:%.*]] = phi i32* [ [[CALL4]], [[IF_END]] ], [ [[W0]], [[IF_THEN]] ] +; IS__TUNIT____-NEXT: ret i32* [[RETVAL_0]] +; +; IS__CGSCC____: Function Attrs: argmemonly nofree norecurse nosync nounwind +; IS__CGSCC____-LABEL: define {{[^@]+}}@internal_ret1_rw +; IS__CGSCC____-SAME: (i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR1]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: [[TMP0:%.*]] = load i32, i32* [[R0]], align 4 +; IS__CGSCC____-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 +; IS__CGSCC____-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]] +; IS__CGSCC____: if.then: +; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] +; IS__CGSCC____: if.end: +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) #[[ATTR4]] +; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 +; IS__CGSCC____-NEXT: store i32 [[TMP1]], i32* [[W0]], align 4 +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret0_nw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: [[CALL2:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[W0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: [[CALL3:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[R0]], i32* nofree nonnull writeonly align 4 dereferenceable(4) "no-capture-maybe-returned" [[W0]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: [[CALL4:%.*]] = call i32* @external_ret2_nrw(i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree noundef nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: br label [[RETURN]] +; IS__CGSCC____: return: +; IS__CGSCC____-NEXT: [[RETVAL_0:%.*]] = phi i32* [ [[CALL4]], [[IF_END]] ], [ [[W0]], [[IF_THEN]] ] +; IS__CGSCC____-NEXT: ret i32* [[RETVAL_0]] ; entry: %0 = load i32, i32* %r0, align 4 @@ -310,9 +332,9 @@ } define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { -; IS__TUNIT____: Function Attrs: argmemonly nofree norecurse nosync nounwind +; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind ; IS__TUNIT____-LABEL: define {{[^@]+}}@external_source_ret2_nrw -; IS__TUNIT____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR1]] { +; IS__TUNIT____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR0]] { ; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: [[CALL:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) #[[ATTR4]] ; IS__TUNIT____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree [[R0]], i32* nofree [[W0]]) #[[ATTR3]] @@ -322,8 +344,8 @@ ; IS__CGSCC____-LABEL: define {{[^@]+}}@external_source_ret2_nrw ; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree [[W0:%.*]]) #[[ATTR1]] { ; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) #[[ATTR4]] -; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree [[R0]], i32* nofree [[W0]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) #[[ATTR5]] +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree [[R0]], i32* nofree [[W0]]) #[[ATTR6:[0-9]+]] ; IS__CGSCC____-NEXT: ret i32* [[CALL1]] ; entry: @@ -346,7 +368,7 @@ ; IS__CGSCC____: attributes #[[ATTR1]] = { argmemonly nofree norecurse nosync nounwind } ; IS__CGSCC____: attributes #[[ATTR2]] = { argmemonly nofree norecurse nosync nounwind willreturn } ; IS__CGSCC____: attributes #[[ATTR3]] = { nofree nosync nounwind } -; IS__CGSCC____: attributes #[[ATTR4]] = { nounwind willreturn } -; IS__CGSCC____: attributes #[[ATTR5]] = { nounwind } -; IS__CGSCC____: attributes #[[ATTR6]] = { nofree norecurse nosync nounwind } +; IS__CGSCC____: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind } +; IS__CGSCC____: attributes #[[ATTR5]] = { nounwind willreturn } +; IS__CGSCC____: attributes #[[ATTR6]] = { nounwind } ;. diff --git a/llvm/test/Transforms/Attributor/value-simplify-gpu.ll b/llvm/test/Transforms/Attributor/value-simplify-gpu.ll --- a/llvm/test/Transforms/Attributor/value-simplify-gpu.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-gpu.ll @@ -1,6 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals -; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM -; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM +; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM ; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM @@ -22,14 +22,14 @@ ; IS__TUNIT____-LABEL: define {{[^@]+}}@kernel ; IS__TUNIT____-SAME: (i32 [[C:%.*]]) #[[ATTR0:[0-9]+]] { ; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: call void @level1Kernel(i32 [[C]]) #[[ATTR1:[0-9]+]] +; IS__TUNIT____-NEXT: call void @level1Kernel(i32 [[C]]) #[[ATTR3:[0-9]+]] ; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC____: Function Attrs: norecurse nosync nounwind ; IS__CGSCC____-LABEL: define {{[^@]+}}@kernel ; IS__CGSCC____-SAME: (i32 [[C:%.*]]) #[[ATTR0:[0-9]+]] { ; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: call void @level1Kernel(i32 [[C]]) #[[ATTR3:[0-9]+]] +; IS__CGSCC____-NEXT: call void @level1Kernel(i32 [[C]]) #[[ATTR4:[0-9]+]] ; IS__CGSCC____-NEXT: ret void ; entry: @@ -40,16 +40,16 @@ define internal void @level1Kernel(i32 %C) { ; IS__TUNIT____: Function Attrs: norecurse nosync nounwind ; IS__TUNIT____-LABEL: define {{[^@]+}}@level1Kernel -; IS__TUNIT____-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { +; IS__TUNIT____-SAME: (i32 [[C:%.*]]) #[[ATTR1:[0-9]+]] { ; IS__TUNIT____-NEXT: entry: -; IS__TUNIT____-NEXT: call void @level2Kernelall_early() #[[ATTR3:[0-9]+]] +; IS__TUNIT____-NEXT: call void @level2Kernelall_early() #[[ATTR4:[0-9]+]] ; IS__TUNIT____-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 ; IS__TUNIT____-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] ; IS__TUNIT____: if.then: -; IS__TUNIT____-NEXT: call void @level2Kernela() #[[ATTR4:[0-9]+]] +; IS__TUNIT____-NEXT: call void @level2Kernela() #[[ATTR3]] ; IS__TUNIT____-NEXT: br label [[IF_END:%.*]] ; IS__TUNIT____: if.else: -; IS__TUNIT____-NEXT: call void @level2Kernelb() #[[ATTR4]] +; IS__TUNIT____-NEXT: call void @level2Kernelb() #[[ATTR3]] ; IS__TUNIT____-NEXT: br label [[IF_END]] ; IS__TUNIT____: if.end: ; IS__TUNIT____-NEXT: call void @level2Kernelall_late() #[[ATTR5:[0-9]+]] @@ -59,17 +59,16 @@ ; IS__CGSCC____-LABEL: define {{[^@]+}}@level1Kernel ; IS__CGSCC____-SAME: (i32 [[C:%.*]]) #[[ATTR1:[0-9]+]] { ; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: call void @level2Kernelall_early() #[[ATTR4:[0-9]+]] +; IS__CGSCC____-NEXT: call void @level2Kernelall_early() #[[ATTR5:[0-9]+]] ; IS__CGSCC____-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 ; IS__CGSCC____-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] ; IS__CGSCC____: if.then: -; IS__CGSCC____-NEXT: call void @level2Kernela() #[[ATTR3]] +; IS__CGSCC____-NEXT: call void @level2Kernela() #[[ATTR4]] ; IS__CGSCC____-NEXT: br label [[IF_END:%.*]] ; IS__CGSCC____: if.else: -; IS__CGSCC____-NEXT: call void @level2Kernelb() #[[ATTR3]] +; IS__CGSCC____-NEXT: call void @level2Kernelb() #[[ATTR4]] ; IS__CGSCC____-NEXT: br label [[IF_END]] ; IS__CGSCC____: if.end: -; IS__CGSCC____-NEXT: call void @level2Kernelall_late() #[[ATTR5:[0-9]+]] ; IS__CGSCC____-NEXT: ret void ; entry: @@ -112,8 +111,7 @@ ; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableKernel to i32*), align 4 ; IS__TUNIT____-NEXT: [[TMP1:%.*]] = load i32, i32* @ReachableKernelAS0, align 4 -; IS__TUNIT____-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 -; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR6:[0-9]+]] +; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 42) #[[ATTR6:[0-9]+]] ; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC____: Function Attrs: norecurse nosync nounwind @@ -122,8 +120,7 @@ ; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableKernel to i32*), align 4 ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* @ReachableKernelAS0, align 4 -; IS__CGSCC____-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 -; IS__CGSCC____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 42) #[[ATTR4]] ; IS__CGSCC____-NEXT: ret void ; entry: @@ -141,8 +138,7 @@ ; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableKernel to i32*), align 4 ; IS__TUNIT____-NEXT: [[TMP1:%.*]] = load i32, i32* @ReachableKernelAS0, align 4 -; IS__TUNIT____-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 -; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR6]] +; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 42) #[[ATTR6]] ; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC____: Function Attrs: norecurse nosync nounwind @@ -151,8 +147,7 @@ ; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableKernel to i32*), align 4 ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* @ReachableKernelAS0, align 4 -; IS__CGSCC____-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 -; IS__CGSCC____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 42) #[[ATTR4]] ; IS__CGSCC____-NEXT: ret void ; entry: @@ -164,12 +159,17 @@ } define internal void @level2Kernelall_late() { -; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly -; CHECK-LABEL: define {{[^@]+}}@level2Kernelall_late -; CHECK-SAME: () #[[ATTR2]] { -; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 -; CHECK-NEXT: ret void +; IS__TUNIT____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; IS__TUNIT____-LABEL: define {{[^@]+}}@level2Kernelall_late +; IS__TUNIT____-SAME: () #[[ATTR2]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: ret void +; +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn +; IS__CGSCC____-LABEL: define {{[^@]+}}@level2Kernelall_late +; IS__CGSCC____-SAME: () #[[ATTR3:[0-9]+]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: ret void ; entry: store i32 1, i32 *addrspacecast (i32 addrspace(3)* @UnreachableKernel to i32*), align 4 @@ -191,7 +191,7 @@ ; IS__CGSCC____-LABEL: define {{[^@]+}}@non_kernel ; IS__CGSCC____-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { ; IS__CGSCC____-NEXT: entry: -; IS__CGSCC____-NEXT: call void @level1(i32 [[C]]) #[[ATTR3]] +; IS__CGSCC____-NEXT: call void @level1(i32 [[C]]) #[[ATTR4]] ; IS__CGSCC____-NEXT: ret void ; entry: @@ -200,60 +200,40 @@ } define internal void @level1(i32 %C) { -; IS__TUNIT_OPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@level1 -; IS__TUNIT_OPM-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { -; IS__TUNIT_OPM-NEXT: entry: -; IS__TUNIT_OPM-NEXT: [[LOCAL:%.*]] = alloca i32, align 4 -; IS__TUNIT_OPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR3]] -; IS__TUNIT_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 -; IS__TUNIT_OPM-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] -; IS__TUNIT_OPM: if.then: -; IS__TUNIT_OPM-NEXT: call void @level2a(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR4]] -; IS__TUNIT_OPM-NEXT: br label [[IF_END:%.*]] -; IS__TUNIT_OPM: if.else: -; IS__TUNIT_OPM-NEXT: call void @level2b(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR4]] -; IS__TUNIT_OPM-NEXT: br label [[IF_END]] -; IS__TUNIT_OPM: if.end: -; IS__TUNIT_OPM-NEXT: call void @level2all_late(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR5]] -; IS__TUNIT_OPM-NEXT: ret void -; -; IS__TUNIT_NPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@level1 -; IS__TUNIT_NPM-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { -; IS__TUNIT_NPM-NEXT: entry: -; IS__TUNIT_NPM-NEXT: [[LOCAL:%.*]] = alloca i32, align 4 -; IS__TUNIT_NPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR3]] -; IS__TUNIT_NPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 -; IS__TUNIT_NPM-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] -; IS__TUNIT_NPM: if.then: -; IS__TUNIT_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[LOCAL]], align 4 -; IS__TUNIT_NPM-NEXT: call void @level2a(i32 [[TMP0]]) #[[ATTR4]] -; IS__TUNIT_NPM-NEXT: br label [[IF_END:%.*]] -; IS__TUNIT_NPM: if.else: -; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* [[LOCAL]], align 4 -; IS__TUNIT_NPM-NEXT: call void @level2b(i32 [[TMP1]]) #[[ATTR4]] -; IS__TUNIT_NPM-NEXT: br label [[IF_END]] -; IS__TUNIT_NPM: if.end: -; IS__TUNIT_NPM-NEXT: call void @level2all_late(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR5]] -; IS__TUNIT_NPM-NEXT: ret void +; IS__TUNIT____: Function Attrs: norecurse nosync nounwind +; IS__TUNIT____-LABEL: define {{[^@]+}}@level1 +; IS__TUNIT____-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[LOCAL:%.*]] = alloca i32, align 4 +; IS__TUNIT____-NEXT: call void @level2all_early(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR4]] +; IS__TUNIT____-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 +; IS__TUNIT____-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; IS__TUNIT____: if.then: +; IS__TUNIT____-NEXT: call void @level2a() #[[ATTR3]] +; IS__TUNIT____-NEXT: br label [[IF_END:%.*]] +; IS__TUNIT____: if.else: +; IS__TUNIT____-NEXT: call void @level2b() #[[ATTR3]] +; IS__TUNIT____-NEXT: br label [[IF_END]] +; IS__TUNIT____: if.end: +; IS__TUNIT____-NEXT: call void @level2all_late(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR5]] +; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC_OPM: Function Attrs: norecurse nosync nounwind ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@level1 ; IS__CGSCC_OPM-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[LOCAL:%.*]] = alloca i32, align 4 -; IS__CGSCC_OPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR4]] +; IS__CGSCC_OPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR5]] ; IS__CGSCC_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 ; IS__CGSCC_OPM-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] ; IS__CGSCC_OPM: if.then: -; IS__CGSCC_OPM-NEXT: call void @level2a(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR3]] +; IS__CGSCC_OPM-NEXT: call void @level2a(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR4]] ; IS__CGSCC_OPM-NEXT: br label [[IF_END:%.*]] ; IS__CGSCC_OPM: if.else: -; IS__CGSCC_OPM-NEXT: call void @level2b(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR3]] +; IS__CGSCC_OPM-NEXT: call void @level2b(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR4]] ; IS__CGSCC_OPM-NEXT: br label [[IF_END]] ; IS__CGSCC_OPM: if.end: -; IS__CGSCC_OPM-NEXT: call void @level2all_late(i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[LOCAL]]) #[[ATTR5]] +; IS__CGSCC_OPM-NEXT: call void @level2all_late(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR6:[0-9]+]] ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM: Function Attrs: norecurse nosync nounwind @@ -261,11 +241,11 @@ ; IS__CGSCC_NPM-SAME: (i32 [[C:%.*]]) #[[ATTR1]] { ; IS__CGSCC_NPM-NEXT: entry: ; IS__CGSCC_NPM-NEXT: [[LOCAL:%.*]] = alloca i32, align 4 -; IS__CGSCC_NPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR4]] +; IS__CGSCC_NPM-NEXT: call void @level2all_early(i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) undef) #[[ATTR5]] ; IS__CGSCC_NPM-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[C]], 0 ; IS__CGSCC_NPM-NEXT: br i1 [[TOBOOL]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] ; IS__CGSCC_NPM: if.then: -; IS__CGSCC_NPM-NEXT: call void @level2a(i32 undef) #[[ATTR3]] +; IS__CGSCC_NPM-NEXT: call void @level2a(i32 undef) #[[ATTR4]] ; IS__CGSCC_NPM-NEXT: br label [[IF_END:%.*]] ; IS__CGSCC_NPM: if.else: ; IS__CGSCC_NPM-NEXT: call void @level2b(i32 undef) #[[ATTR6:[0-9]+]] @@ -294,20 +274,19 @@ } define internal void @level2all_early(i32* %addr) { -; NOT_CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly -; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@level2all_early -; NOT_CGSCC_NPM-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { -; NOT_CGSCC_NPM-NEXT: entry: -; NOT_CGSCC_NPM-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; NOT_CGSCC_NPM-NEXT: store i32 17, i32* [[ADDR]], align 4 -; NOT_CGSCC_NPM-NEXT: ret void +; IS__TUNIT____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; IS__TUNIT____-LABEL: define {{[^@]+}}@level2all_early +; IS__TUNIT____-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: ret void ; -; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@level2all_early -; IS__CGSCC_NPM-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { -; IS__CGSCC_NPM-NEXT: entry: -; IS__CGSCC_NPM-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; IS__CGSCC_NPM-NEXT: ret void +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@level2all_early +; IS__CGSCC____-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 +; IS__CGSCC____-NEXT: ret void ; entry: store i32 1, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 @@ -316,36 +295,23 @@ } define internal void @level2a(i32* %addr) { -; IS__TUNIT_OPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@level2a -; IS__TUNIT_OPM-SAME: (i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { -; IS__TUNIT_OPM-NEXT: entry: -; IS__TUNIT_OPM-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; IS__TUNIT_OPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__TUNIT_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ADDR]], align 4 -; IS__TUNIT_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR6]] -; IS__TUNIT_OPM-NEXT: ret void -; -; IS__TUNIT_NPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@level2a -; IS__TUNIT_NPM-SAME: (i32 [[TMP0:%.*]]) #[[ATTR1]] { -; IS__TUNIT_NPM-NEXT: entry: -; IS__TUNIT_NPM-NEXT: [[ADDR_PRIV:%.*]] = alloca i32, align 4 -; IS__TUNIT_NPM-NEXT: store i32 [[TMP0]], i32* [[ADDR_PRIV]], align 4 -; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__TUNIT_NPM-NEXT: [[TMP3:%.*]] = load i32, i32* [[ADDR_PRIV]], align 4 -; IS__TUNIT_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 [[TMP3]]) #[[ATTR6]] -; IS__TUNIT_NPM-NEXT: ret void +; IS__TUNIT____: Function Attrs: norecurse nosync nounwind +; IS__TUNIT____-LABEL: define {{[^@]+}}@level2a +; IS__TUNIT____-SAME: () #[[ATTR1]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 17) #[[ATTR6]] +; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC_OPM: Function Attrs: norecurse nosync nounwind ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@level2a -; IS__CGSCC_OPM-SAME: (i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 ; IS__CGSCC_OPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__CGSCC_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ADDR]], align 4 -; IS__CGSCC_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR3]] +; IS__CGSCC_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* undef, align 4 +; IS__CGSCC_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 17) #[[ATTR4]] ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM: Function Attrs: norecurse nosync nounwind @@ -356,7 +322,7 @@ ; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 ; IS__CGSCC_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 ; IS__CGSCC_NPM-NEXT: [[TMP3:%.*]] = load i32, i32* [[ADDR_PRIV]], align 4 -; IS__CGSCC_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 17) #[[ATTR3]] +; IS__CGSCC_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 17) #[[ATTR4]] ; IS__CGSCC_NPM-NEXT: ret void ; entry: @@ -368,36 +334,23 @@ } define internal void @level2b(i32* %addr) { -; IS__TUNIT_OPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_OPM-LABEL: define {{[^@]+}}@level2b -; IS__TUNIT_OPM-SAME: (i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { -; IS__TUNIT_OPM-NEXT: entry: -; IS__TUNIT_OPM-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; IS__TUNIT_OPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__TUNIT_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ADDR]], align 4 -; IS__TUNIT_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR6]] -; IS__TUNIT_OPM-NEXT: ret void -; -; IS__TUNIT_NPM: Function Attrs: norecurse nosync nounwind -; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@level2b -; IS__TUNIT_NPM-SAME: (i32 [[TMP0:%.*]]) #[[ATTR1]] { -; IS__TUNIT_NPM-NEXT: entry: -; IS__TUNIT_NPM-NEXT: [[ADDR_PRIV:%.*]] = alloca i32, align 4 -; IS__TUNIT_NPM-NEXT: store i32 [[TMP0]], i32* [[ADDR_PRIV]], align 4 -; IS__TUNIT_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 -; IS__TUNIT_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__TUNIT_NPM-NEXT: [[TMP3:%.*]] = load i32, i32* [[ADDR_PRIV]], align 4 -; IS__TUNIT_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 [[TMP3]]) #[[ATTR6]] -; IS__TUNIT_NPM-NEXT: ret void +; IS__TUNIT____: Function Attrs: norecurse nosync nounwind +; IS__TUNIT____-LABEL: define {{[^@]+}}@level2b +; IS__TUNIT____-SAME: () #[[ATTR1]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 noundef 17) #[[ATTR6]] +; IS__TUNIT____-NEXT: ret void ; ; IS__CGSCC_OPM: Function Attrs: norecurse nosync nounwind ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@level2b -; IS__CGSCC_OPM-SAME: (i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { +; IS__CGSCC_OPM-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR1]] { ; IS__CGSCC_OPM-NEXT: entry: ; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 ; IS__CGSCC_OPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__CGSCC_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* [[ADDR]], align 4 -; IS__CGSCC_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 [[TMP2]]) #[[ATTR3]] +; IS__CGSCC_OPM-NEXT: [[TMP2:%.*]] = load i32, i32* undef, align 4 +; IS__CGSCC_OPM-NEXT: call void @use(i32 [[TMP0]], i32 [[TMP1]], i32 17) #[[ATTR4]] ; IS__CGSCC_OPM-NEXT: ret void ; ; IS__CGSCC_NPM: Function Attrs: norecurse nosync nounwind @@ -408,7 +361,7 @@ ; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @ReachableNonKernel to i32*), align 4 ; IS__CGSCC_NPM-NEXT: [[TMP2:%.*]] = load i32, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 ; IS__CGSCC_NPM-NEXT: [[TMP3:%.*]] = load i32, i32* [[ADDR_PRIV]], align 4 -; IS__CGSCC_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 17) #[[ATTR3]] +; IS__CGSCC_NPM-NEXT: call void @use(i32 [[TMP1]], i32 [[TMP2]], i32 17) #[[ATTR4]] ; IS__CGSCC_NPM-NEXT: ret void ; entry: @@ -420,20 +373,19 @@ } define internal void @level2all_late(i32* %addr) { -; NOT_CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly -; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@level2all_late -; NOT_CGSCC_NPM-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { -; NOT_CGSCC_NPM-NEXT: entry: -; NOT_CGSCC_NPM-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; NOT_CGSCC_NPM-NEXT: store i32 5, i32* [[ADDR]], align 4 -; NOT_CGSCC_NPM-NEXT: ret void +; IS__TUNIT____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; IS__TUNIT____-LABEL: define {{[^@]+}}@level2all_late +; IS__TUNIT____-SAME: (i32* noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 +; IS__TUNIT____-NEXT: ret void ; -; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly -; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@level2all_late -; IS__CGSCC_NPM-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { -; IS__CGSCC_NPM-NEXT: entry: -; IS__CGSCC_NPM-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 -; IS__CGSCC_NPM-NEXT: ret void +; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@level2all_late +; IS__CGSCC____-SAME: (i32* noalias nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 +; IS__CGSCC____-NEXT: ret void ; entry: store i32 1, i32* addrspacecast (i32 addrspace(3)* @UnreachableNonKernel to i32*), align 4 @@ -447,24 +399,25 @@ ; IS__TUNIT____: attributes #[[ATTR0]] = { norecurse nosync nounwind "kernel" } ; IS__TUNIT____: attributes #[[ATTR1]] = { norecurse nosync nounwind } ; IS__TUNIT____: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn writeonly } -; IS__TUNIT____: attributes #[[ATTR3]] = { nofree nosync nounwind willreturn writeonly } -; IS__TUNIT____: attributes #[[ATTR4]] = { nosync nounwind } +; IS__TUNIT____: attributes #[[ATTR3]] = { nosync nounwind } +; IS__TUNIT____: attributes #[[ATTR4]] = { nofree nosync nounwind willreturn writeonly } ; IS__TUNIT____: attributes #[[ATTR5]] = { nosync nounwind writeonly } ; IS__TUNIT____: attributes #[[ATTR6]] = { nounwind } ;. ; IS__CGSCC_OPM: attributes #[[ATTR0]] = { norecurse nosync nounwind "kernel" } ; IS__CGSCC_OPM: attributes #[[ATTR1]] = { norecurse nosync nounwind } ; IS__CGSCC_OPM: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn writeonly } -; IS__CGSCC_OPM: attributes #[[ATTR3]] = { nounwind } -; IS__CGSCC_OPM: attributes #[[ATTR4]] = { nounwind willreturn writeonly } -; IS__CGSCC_OPM: attributes #[[ATTR5]] = { nounwind writeonly } +; IS__CGSCC_OPM: attributes #[[ATTR3]] = { nofree norecurse nosync nounwind readnone willreturn } +; IS__CGSCC_OPM: attributes #[[ATTR4]] = { nounwind } +; IS__CGSCC_OPM: attributes #[[ATTR5]] = { nounwind willreturn writeonly } +; IS__CGSCC_OPM: attributes #[[ATTR6]] = { nounwind writeonly } ;. ; IS__CGSCC_NPM: attributes #[[ATTR0]] = { norecurse nosync nounwind "kernel" } ; IS__CGSCC_NPM: attributes #[[ATTR1]] = { norecurse nosync nounwind } ; IS__CGSCC_NPM: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn writeonly } -; IS__CGSCC_NPM: attributes #[[ATTR3]] = { nounwind } -; IS__CGSCC_NPM: attributes #[[ATTR4]] = { nounwind willreturn writeonly } -; IS__CGSCC_NPM: attributes #[[ATTR5]] = { nounwind writeonly } +; IS__CGSCC_NPM: attributes #[[ATTR3]] = { nofree norecurse nosync nounwind readnone willreturn } +; IS__CGSCC_NPM: attributes #[[ATTR4]] = { nounwind } +; IS__CGSCC_NPM: attributes #[[ATTR5]] = { nounwind willreturn writeonly } ; IS__CGSCC_NPM: attributes #[[ATTR6]] = { nosync nounwind } ; IS__CGSCC_NPM: attributes #[[ATTR7]] = { nosync nounwind writeonly } ;.