Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -887,7 +887,8 @@ /// This method will evaluate \p Pred on all (transitive) uses of the /// associated value and return true if \p Pred holds every time. bool checkForAllUses(const function_ref &Pred, - const AbstractAttribute &QueryingAA, const Value &V); + const AbstractAttribute &QueryingAA, const Value &V, + const Instruction *ReachableFromI = nullptr); /// Helper struct used in the communication between an abstract attribute (AA) /// that wants to change the signature of a function and the Attributor which Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -2474,15 +2474,53 @@ << " is assumed NoAlias in the definition\n"); // (ii) Check whether the value is captured in the scope using AANoCapture. - // FIXME: This is conservative though, it is better to look at CFG and - // check only uses possibly executed before this callsite. + // Look at CFG and check only uses possibly executed before this + // callsite. + + auto UsePred = [&](const Use &U, bool &Follow) -> bool { + Instruction *UserI = cast(U.getUser()); + const Function *ScopeFn = IRP.getAnchorScope(); + if (ScopeFn) { + const auto &ReachabilityAA = + A.getAAFor(*this, IRPosition::function(*ScopeFn), + /* TrackDependence */ false); + if (auto *CB = dyn_cast(UserI)) { + if (CB->isArgOperand(&U)) { + unsigned ArgNo = CB->getArgOperandNo(&U); + + const auto &NoCaptureAA = A.getAAFor( + *this, IRPosition::callsite_argument(*CB, ArgNo)); + + if (!NoCaptureAA.isAssumedNoCapture()) + return true; + + if (!ReachabilityAA.isAssumedReachable(UserI, getCtxI())) + return true; + } + } + } + + if (isa(U) || isa(U) || isa(U) || + isa(U)) { + Follow = true; + return true; + } + + LLVM_DEBUG(dbgs() << "[Attributor][AANoAliasCSArg] Unknown user: " << *U + << "\n"); + return false; + }; auto &NoCaptureAA = A.getAAFor(*this, IRP); if (!NoCaptureAA.isAssumedNoCaptureMaybeReturned()) { - LLVM_DEBUG( - dbgs() << "[Attributor][AANoAliasCSArg] " << V - << " cannot be noalias as it is potentially captured\n"); - return indicatePessimisticFixpoint(); + // Check all of V's uses. + // UsePred checks: getCtxI() i.e. callSite, is reachable from V's user. + if (!A.checkForAllUses(UsePred, *this, V, getCtxI())) { + LLVM_DEBUG( + dbgs() << "[Attributor][AANoAliasCSArg] " << V + << " cannot be noalias as it is potentially captured\n"); + return indicatePessimisticFixpoint(); + } } // (iii) Check there is no other pointer argument which could alias with the @@ -5679,7 +5717,8 @@ bool Attributor::checkForAllUses( const function_ref &Pred, - const AbstractAttribute &QueryingAA, const Value &V) { + const AbstractAttribute &QueryingAA, const Value &V, + const Instruction *ReachableFromI) { const IRPosition &IRP = QueryingAA.getIRPosition(); SmallVector Worklist; SmallPtrSet Visited; @@ -5694,18 +5733,25 @@ return true; bool AnyDead = false; + bool AnyUnreachable = false; const Function *ScopeFn = IRP.getAnchorScope(); const auto *LivenessAA = ScopeFn ? &getAAFor(QueryingAA, IRPosition::function(*ScopeFn), /* TrackDependence */ false) : nullptr; + const auto *ReachabilityAA = + ScopeFn ? &getAAFor(QueryingAA, + IRPosition::function(*ScopeFn), + /* TrackDependence */ false) + : nullptr; + while (!Worklist.empty()) { const Use *U = Worklist.pop_back_val(); if (!Visited.insert(U).second) continue; LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << "\n"); - if (Instruction *UserI = dyn_cast(U->getUser())) + if (Instruction *UserI = dyn_cast(U->getUser())) { if (LivenessAA && LivenessAA->isAssumedDead(UserI)) { LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": " << *LivenessAA << "\n"); @@ -5713,6 +5759,15 @@ continue; } + // Track if we miss any unreachable user. + if (ReachableFromI && ReachabilityAA && + !ReachabilityAA->isAssumedReachable(UserI, ReachableFromI)) { + LLVM_DEBUG(dbgs() << "[Attributor] Unreachable user: " << *UserI << ": " + << *ReachabilityAA << "\n"); + AnyUnreachable = true; + } + } + bool Follow = false; if (!Pred(*U, Follow)) return false; @@ -5725,6 +5780,10 @@ if (AnyDead) recordDependence(*LivenessAA, QueryingAA, DepClassTy::OPTIONAL); + // If we exclude any use due to reachability + if (AnyUnreachable) + recordDependence(*ReachabilityAA, QueryingAA, DepClassTy::OPTIONAL); + return true; } Index: llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll =================================================================== --- llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll +++ llvm/test/Transforms/Attributor/IPConstantProp/pthreads.ll @@ -33,10 +33,10 @@ ; CHECK-NEXT: [[ALLOC1:%.*]] = alloca i8, align 8 ; CHECK-NEXT: [[ALLOC2:%.*]] = alloca i8, align 8 ; CHECK-NEXT: [[THREAD:%.*]] = alloca i64, align 8 -; CHECK-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @foo, i8* noalias nofree readnone null) -; CHECK-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @bar, i8* nofree nonnull readnone align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) -; CHECK-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @baz, i8* noalias nocapture nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC1]]) -; CHECK-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @buz, i8* nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC2]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @pthread_create(i64* noalias nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @foo, i8* noalias nofree readnone null) +; CHECK-NEXT: [[CALL1:%.*]] = call i32 @pthread_create(i64* noalias nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @bar, i8* nofree nonnull readnone align 8 dereferenceable(8) bitcast (i8** @GlobalVPtr to i8*)) +; CHECK-NEXT: [[CALL2:%.*]] = call i32 @pthread_create(i64* noalias nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @baz, i8* noalias nocapture nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC1]]) +; CHECK-NEXT: [[CALL3:%.*]] = call i32 @pthread_create(i64* noalias nonnull align 8 dereferenceable(8) [[THREAD]], %union.pthread_attr_t* noalias null, i8* (i8*)* nonnull @buz, i8* noalias nofree nonnull readnone align 8 dereferenceable(1) [[ALLOC2]]) ; CHECK-NEXT: ret i32 0 ; entry: @@ -84,7 +84,7 @@ define internal i8* @buz(i8* %arg) { ; CHECK-LABEL: define {{[^@]+}}@buz -; CHECK-SAME: (i8* nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]]) +; CHECK-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: ret i8* [[ARG]] ; Index: llvm/test/Transforms/Attributor/callbacks.ll =================================================================== --- llvm/test/Transforms/Attributor/callbacks.ll +++ llvm/test/Transforms/Attributor/callbacks.ll @@ -23,7 +23,7 @@ ; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[B]] to i8* ; CHECK-NEXT: store i32 42, i32* [[B]], align 32 ; CHECK-NEXT: store i32* [[B]], i32** [[C]], align 64 -; CHECK-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t0_callback_broker(i32* noalias null, i32* nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nonnull bitcast (void (i32*, i32*, i32*, i64, i32**)* @t0_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A:%.*]], i64 99, i32** noalias nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) +; CHECK-NEXT: call void (i32*, i32*, void (i32*, i32*, ...)*, ...) @t0_callback_broker(i32* noalias null, i32* noalias nonnull align 128 dereferenceable(4) [[PTR]], void (i32*, i32*, ...)* nonnull bitcast (void (i32*, i32*, i32*, i64, i32**)* @t0_callback_callee to void (i32*, i32*, ...)*), i32* align 256 [[A:%.*]], i64 99, i32** noalias nocapture nonnull readonly align 64 dereferenceable(8) [[C]]) ; CHECK-NEXT: ret void ; Index: llvm/test/Transforms/Attributor/noalias.ll =================================================================== --- llvm/test/Transforms/Attributor/noalias.ll +++ llvm/test/Transforms/Attributor/noalias.ll @@ -182,14 +182,14 @@ declare void @test10_helper_1(i8* %a) define void @test10_helper_2(i8* noalias %a) { -; CHECK: tail call void @test10_helper_1(i8* %a) +; CHECK: tail call void @test10_helper_1(i8* noalias %a) tail call void @test10_helper_1(i8* %a) ret void } define void @test10(i8* noalias %a) { ; CHECK: define void @test10(i8* noalias %a) ; FIXME: missing noalias -; CHECK-NEXT: tail call void @test10_helper_1(i8* %a) +; CHECK-NEXT: tail call void @test10_helper_1(i8* noalias %a) tail call void @test10_helper_1(i8* %a) ; CHECK-NEXT: tail call void @test10_helper_2(i8* noalias %a)