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 @@ -2082,14 +2082,33 @@ void initialize(Attributor &A) override { AANoRecurseImpl::initialize(A); if (const Function *F = getAnchorScope()) - if (A.getInfoCache().getSccSize(*F) == 1) - return; - indicatePessimisticFixpoint(); + if (A.getInfoCache().getSccSize(*F) != 1) + indicatePessimisticFixpoint(); } /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { + // If all live call sites are known to be no-recurse, we are as well. + auto CallSitePred = [&](AbstractCallSite ACS) { + const auto &NoRecurseAA = A.getAAFor( + *this, IRPosition::function(*ACS.getInstruction()->getFunction()), + /* TrackDependence */ false, DepClassTy::OPTIONAL); + return NoRecurseAA.isKnownNoRecurse(); + }; + bool AllCallSitesKnown; + if (A.checkForAllCallSites(CallSitePred, *this, true, AllCallSitesKnown)) { + // If we know all call sites and all are known no-recurse, we are done. + // If all known call sites, which might not be all that exist, are known + // to be no-recurse, we are not done but we can continue to assume + // no-recurse. If one of the call sites we have not visited will become + // live, another update is triggered. + if (AllCallSitesKnown) + indicateOptimisticFixpoint(); + return ChangeStatus::UNCHANGED; + } + + // If the above check does not hold anymore we look at the calls. auto CheckForNoRecurse = [&](Instruction &I) { ImmutableCallSite ICS(&I); if (ICS.hasFnAttr(Attribute::NoRecurse)) diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/dbg.ll @@ -30,7 +30,7 @@ ; CHECK-LABEL: define {{[^@]+}}@caller ; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readonly [[P:%.*]]) ; CHECK-NEXT: call void @test(i32** nocapture readonly align 8 [[Y]]), !dbg !4 -; CHECK-NEXT: call void @test_byval(), !dbg !5 +; CHECK-NEXT: call void @test_byval() #1, !dbg !5 ; CHECK-NEXT: ret void ; call void @test(i32** %Y), !dbg !1 diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/nonzero-address-spaces.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s ; ArgumentPromotion should preserve the default function address space ; from the data layout. diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/multiple_callbacks.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s ; ; ; /---------------------------------------| diff --git a/llvm/test/Transforms/Attributor/internal-noalias.ll b/llvm/test/Transforms/Attributor/internal-noalias.ll --- a/llvm/test/Transforms/Attributor/internal-noalias.ll +++ b/llvm/test/Transforms/Attributor/internal-noalias.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 < %s | FileCheck %s +; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 < %s | FileCheck %s define dso_local i32 @visible(i32* noalias %A, i32* noalias %B) #0 { entry: diff --git a/llvm/test/Transforms/Attributor/norecurse.ll b/llvm/test/Transforms/Attributor/norecurse.ll --- a/llvm/test/Transforms/Attributor/norecurse.ll +++ b/llvm/test/Transforms/Attributor/norecurse.ll @@ -1,4 +1,4 @@ -; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR ; Copied from Transforms/FunctoinAttrs/norecurse.ll ; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind readnone willreturn @@ -59,8 +59,7 @@ declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) ; ATTRIBUTOR: Function Attrs -; FIXME: missing "norecurse" -; ATTRIBUTOR-SAME: nosync readnone +; ATTRIBUTOR-SAME: norecurse nosync readnone define internal i32 @called_by_norecurse() { %a = call i32 @k() ret i32 %a @@ -73,19 +72,22 @@ } ; ATTRIBUTOR: Function Attrs -; FIXME: missing "norecurse" -; ATTRIBUTOR-SAME: nosync +; ATTRIBUTOR-SAME: norecurse nosync readnone +; ATTRIBUTOR-NEXT: @called_by_norecurse_indirectly define internal i32 @called_by_norecurse_indirectly() { %a = call i32 @k() ret i32 %a } -define internal void @o() { +; ATTRIBUTOR: Function Attrs +; ATTRIBUTOR-SAME: norecurse nosync readnone +; ATTRIBUTOR-NEXT: @o +define internal i32 @o() { %a = call i32 @called_by_norecurse_indirectly() - ret void + ret i32 %a } -define void @p() norecurse { - call void @o() - ret void +define i32 @p() norecurse { + %a = call i32 @o() + ret i32 %a } ; ATTRIBUTOR: Function Attrs: nofree nosync nounwind