Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -850,7 +850,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); /// Check \p Pred on all function call sites. /// Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -2298,14 +2298,43 @@ << " 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()); + if (CallSite CS = CallSite(UserI)) { + if (CS.isArgOperand(&U)) { + const IRPosition &IRP = getIRPosition(); + if (const Argument *Arg = IRP.getAssociatedArgument()) { + if (!Arg->hasNoCaptureAttr()) { + return true; + } + } + } + } + + if (isa(U) || isa(U) || isa(U) || + isa(U)) { + Follow = true; + return true; + } + + // Unknown user for which we can not track uses further + 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"); + // Check if V's uses reach getCtxI() i.e. callSite. + if (!A.checkForAllUses(UsePred, *this, V, getCtxI())) { + LLVM_DEBUG( + dbgs() << "[Attributor][AANoAliasCSArg] " << V + << " cannot be noalias as it is potentially captured\n"); + return indicatePessimisticFixpoint(); + } return indicatePessimisticFixpoint(); } @@ -5006,7 +5035,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; @@ -5027,12 +5057,18 @@ /* 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"); @@ -5040,6 +5076,12 @@ continue; } + // For uses which reachable call instruction. + if (ReachableFromI && ReachabilityAA && + !ReachabilityAA->isAssumedReachable(UserI, ReachableFromI)) + continue; + } + bool Follow = false; if (!Pred(*U, Follow)) return false; 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 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* nonnull 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 nonnull 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* nonnull 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 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* nonnull 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 nonnull 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 nonnull 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 @@ -24,7 +24,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* [[A:%.*]], i64 99, i32** nonnull 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* [[A:%.*]], i64 99, i32** nonnull align 64 dereferenceable(8) [[C]]) ; CHECK-NEXT: ret void ; entry: Index: llvm/test/Transforms/Attributor/heap_to_stack.ll =================================================================== --- llvm/test/Transforms/Attributor/heap_to_stack.ll +++ llvm/test/Transforms/Attributor/heap_to_stack.ll @@ -48,7 +48,7 @@ define void @test2() { %1 = tail call noalias i8* @malloc(i64 4) ; CHECK: @malloc(i64 4) - ; CHECK-NEXT: @sync_func(i8* %1) + ; CHECK-NEXT: @sync_func(i8* noalias %1) tail call void @sync_func(i8* %1) tail call void @free(i8* %1) ret void @@ -225,7 +225,7 @@ %1 = tail call noalias i8* @malloc(i64 4) ; CHECK: test11 ; CHECK-NEXT: alloc - ; CHECK-NEXT: @sync_will_return(i8* %1) + ; CHECK-NEXT: @sync_will_return(i8* noalias %1) tail call void @sync_will_return(i8* %1) tail call void @free(i8* %1) ret void Index: llvm/test/Transforms/Attributor/noalias.ll =================================================================== --- llvm/test/Transforms/Attributor/noalias.ll +++ llvm/test/Transforms/Attributor/noalias.ll @@ -168,14 +168,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) @@ -225,7 +225,7 @@ ; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) ; FIXME: This should be @use_nocapture(i8* noalias nocapture [[A]]) ; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) -; CHECK-NEXT: tail call void @use(i8* [[A]]) +; CHECK-NEXT: tail call void @use(i8* noalias [[A]]) ; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) ; CHECK-NEXT: ret void ;