Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -844,7 +844,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. /// @@ -2076,6 +2077,18 @@ return isAssumed(NO_CAPTURE_MAYBE_RETURNED); } + /// Return true if we know that the underlying value is not captured in its + /// respective scope but before specified instruction. + bool isKnownNoCaptureMaybeBeforeInstruction() const { + return true; + } + + /// Return true if we assume that the underlying value is not captured in its + /// respective scope but before specified instruction. + bool isAssumedNoCaptureMaybeBeforeInstruction() const { + return true; + } + /// Create an abstract attribute view for the position \p IRP. static AANoCapture &createForPosition(const IRPosition &IRP, Attributor &A); Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -2298,15 +2298,33 @@ << " 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 { + 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"); - return indicatePessimisticFixpoint(); + if (V.getType()->isPointerTy()) { + ImmutableCallSite ICSCaller(&getAnchorValue()); + const Instruction *CallSiteI = ICSCaller.getInstruction(); + if (!A.checkForAllUses(UsePred, *this, V, CallSiteI)) { + 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 @@ -5006,7 +5024,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; @@ -5032,7 +5051,7 @@ 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 +5059,17 @@ continue; } + // For uses which reachable call instruction. + if (ReachableFromI) { + if (UserI->isSameOperationAs(ReachableFromI)) { + const auto &NoCapture = getAAFor( + QueryingAA, IRP); + LLVM_DEBUG(dbgs() << "[Attributor] Use reachable. \n"); + return NoCapture.isAssumedNoCaptureMaybeBeforeInstruction(); + } + } + } + bool Follow = false; if (!Pred(*U, Follow)) return false; Index: llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll =================================================================== --- llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll +++ llvm/test/Transforms/Attributor/ArgumentPromotion/variadic.ll @@ -29,7 +29,7 @@ ; Function Attrs: nounwind uwtable define internal void @callee_t0f(i8* nocapture readnone %tp13, i8* nocapture readnone %tp14, i8* nocapture readnone %tp15, i8* nocapture readnone %tp16, i8* nocapture readnone %tp17, ...) { ; CHECK-LABEL: define {{[^@]+}}@callee_t0f -; CHECK-SAME: (i8* nocapture nofree nonnull readnone [[TP13:%.*]], i8* nocapture nofree nonnull readnone [[TP14:%.*]], i8* nocapture nofree nonnull readnone [[TP15:%.*]], i8* nocapture nofree nonnull readnone [[TP16:%.*]], i8* nocapture nofree nonnull readnone [[TP17:%.*]], ...) +; CHECK-SAME: (i8* noalias nocapture nofree nonnull readnone [[TP13:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP14:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP15:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP16:%.*]], i8* noalias nocapture nofree nonnull readnone [[TP17:%.*]], ...) ; CHECK-NEXT: entry: ; CHECK-NEXT: ret void ; 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: [[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* 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: [[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 @@ -164,13 +164,13 @@ define void @test8() { %1 = tail call noalias i8* @malloc(i64 4) ; CHECK: %1 = tail call noalias i8* @malloc(i64 4) - ; CHECK-NEXT: @no_sync_func(i8* nocapture nofree %1) + ; CHECK-NEXT: @no_sync_func(i8* noalias nocapture nofree %1) tail call void @no_sync_func(i8* %1) %2 = bitcast i8* %1 to i32* store i32 10, i32* %2 %3 = load i32, i32* %2 tail call void @foo(i32* %2) - ; CHECK: @free(i8* %1) + ; CHECK: @free(i8* noalias nocapture %1) tail call void @free(i8* %1) ret void } @@ -179,13 +179,13 @@ define void @test9() { %1 = tail call noalias i8* @malloc(i64 4) ; CHECK: %1 = tail call noalias i8* @malloc(i64 4) - ; CHECK-NEXT: @no_sync_func(i8* nocapture nofree %1) + ; CHECK-NEXT: @no_sync_func(i8* noalias nocapture nofree %1) tail call void @no_sync_func(i8* %1) %2 = bitcast i8* %1 to i32* store i32 10, i32* %2 %3 = load i32, i32* %2 tail call void @foo_nounw(i32* %2) - ; CHECK: @free(i8* %1) + ; CHECK: @free(i8* noalias nocapture %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) @@ -222,11 +222,11 @@ ; CHECK-LABEL: @test12_2( ; CHECK-NEXT: [[A:%.*]] = tail call noalias i8* @malloc(i64 4) ; FIXME: This should be @use_nocapture(i8* noalias [[A]]) -; CHECK-NEXT: tail call void @use_nocapture(i8* nocapture [[A]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* noalias 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_nocapture(i8* nocapture [[A]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nocapture [[A]]) +; CHECK-NEXT: tail call void @use(i8* noalias [[A]]) +; CHECK-NEXT: tail call void @use_nocapture(i8* noalias nocapture [[A]]) ; CHECK-NEXT: ret void ; %A = tail call noalias i8* @malloc(i64 4)