[Attributor] AANoRecurse check all call sites for `norecurse`

If all call sites are in `norecurse` functions we can derive `norecurse`
as the ReversePostOrderFunctionAttrsPass does. This should make
ReversePostOrderFunctionAttrsLegacyPass obsolete once the Attributor is
enabled.

Reviewed By: uenoku

Differential Revision: https://reviews.llvm.org/D72017
This commit is contained in:
Johannes Doerfert 2019-12-30 16:15:38 -06:00
parent 368f7ee7a5
commit 26d02b0f28
6 changed files with 38 additions and 17 deletions

View File

@ -2082,14 +2082,33 @@ struct AANoRecurseFunction final : AANoRecurseImpl {
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<AANoRecurse>(
*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))

View File

@ -30,7 +30,7 @@ define void @caller(i32** %Y, %struct.pair* %P) {
; 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

View File

@ -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.

View File

@ -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
;
;
; /---------------------------------------|

View File

@ -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:

View File

@ -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 @@ define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
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 @@ define void @m() norecurse {
}
; 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