Skip to content

Commit 63f6066

Browse files
committed
[Attributor] Implement "norecurse" function attribute deduction
Summary: This patch introduces `norecurse` function attribute deduction. `norecurse` will be deduced if the following conditions hold: * The size of SCC in which the function belongs equals to 1. * The function doesn't have self-recursion. * We have `norecurse` for all call site. To avoid a large change, SCC is calculated using scc_iterator in InfoCache initialization for now. Reviewers: jdoerfert, sstefan1 Reviewed By: jdoerfert Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D67751 llvm-svn: 372475
1 parent 9ec7117 commit 63f6066

File tree

4 files changed

+179
-62
lines changed

4 files changed

+179
-62
lines changed

llvm/include/llvm/Transforms/IPO/Attributor.h

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,12 @@
9696
#ifndef LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H
9797
#define LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H
9898

99+
#include "llvm/ADT/MapVector.h"
100+
#include "llvm/ADT/SCCIterator.h"
99101
#include "llvm/ADT/SetVector.h"
100102
#include "llvm/Analysis/AliasAnalysis.h"
103+
#include "llvm/Analysis/CallGraph.h"
101104
#include "llvm/Analysis/TargetLibraryInfo.h"
102-
#include "llvm/ADT/MapVector.h"
103105
#include "llvm/IR/CallSite.h"
104106
#include "llvm/IR/PassManager.h"
105107

@@ -524,15 +526,25 @@ class SubsumingPositionIterator {
524526
struct AnalysisGetter {
525527
template <typename Analysis>
526528
typename Analysis::Result *getAnalysis(const Function &F) {
527-
if (!FAM)
529+
if (!MAM || !F.getParent())
530+
return nullptr;
531+
auto &FAM = MAM->getResult<FunctionAnalysisManagerModuleProxy>(
532+
const_cast<Module &>(*F.getParent()))
533+
.getManager();
534+
return &FAM.getResult<Analysis>(const_cast<Function &>(F));
535+
}
536+
537+
template <typename Analysis>
538+
typename Analysis::Result *getAnalysis(const Module &M) {
539+
if (!MAM)
528540
return nullptr;
529-
return &FAM->getResult<Analysis>(const_cast<Function &>(F));
541+
return &MAM->getResult<Analysis>(const_cast<Module &>(M));
530542
}
531-
AnalysisGetter(FunctionAnalysisManager &FAM) : FAM(&FAM) {}
543+
AnalysisGetter(ModuleAnalysisManager &MAM) : MAM(&MAM) {}
532544
AnalysisGetter() {}
533545

534546
private:
535-
FunctionAnalysisManager *FAM = nullptr;
547+
ModuleAnalysisManager *MAM = nullptr;
536548
};
537549

538550
/// Data structure to hold cached (LLVM-IR) information.
@@ -548,7 +560,20 @@ struct AnalysisGetter {
548560
/// reusable, it is advised to inherit from the InformationCache and cast the
549561
/// instance down in the abstract attributes.
550562
struct InformationCache {
551-
InformationCache(const DataLayout &DL, AnalysisGetter &AG) : DL(DL), AG(AG) {}
563+
InformationCache(const Module &M, AnalysisGetter &AG)
564+
: DL(M.getDataLayout()), AG(AG) {
565+
566+
CallGraph *CG = AG.getAnalysis<CallGraphAnalysis>(M);
567+
if (!CG)
568+
return;
569+
570+
DenseMap<const Function *, unsigned> SccSize;
571+
for (scc_iterator<CallGraph *> I = scc_begin(CG); !I.isAtEnd(); ++I) {
572+
for (CallGraphNode *Node : *I)
573+
SccSize[Node->getFunction()] = I->size();
574+
}
575+
SccSizeOpt = std::move(SccSize);
576+
}
552577

553578
/// A map type from opcodes to instructions with this opcode.
554579
using OpcodeInstMapTy = DenseMap<unsigned, SmallVector<Instruction *, 32>>;
@@ -577,6 +602,13 @@ struct InformationCache {
577602
return AG.getAnalysis<AAManager>(F);
578603
}
579604

605+
/// Return SCC size on call graph for function \p F.
606+
unsigned getSccSize(const Function &F) {
607+
if (!SccSizeOpt.hasValue())
608+
return 0;
609+
return (SccSizeOpt.getValue())[&F];
610+
}
611+
580612
/// Return datalayout used in the module.
581613
const DataLayout &getDL() { return DL; }
582614

@@ -594,14 +626,15 @@ struct InformationCache {
594626
/// A map from functions to their instructions that may read or write memory.
595627
FuncRWInstsMapTy FuncRWInstsMap;
596628

597-
/// A map from functions to their TLI.
598-
599629
/// The datalayout used in the module.
600630
const DataLayout &DL;
601631

602632
/// Getters for analysis.
603633
AnalysisGetter &AG;
604634

635+
/// Cache result for scc size in the call graph
636+
Optional<DenseMap<const Function *, unsigned>> SccSizeOpt;
637+
605638
/// Give the Attributor access to the members so
606639
/// Attributor::identifyDefaultAbstractAttributes(...) can initialize them.
607640
friend struct Attributor;

llvm/lib/Transforms/IPO/Attributor.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,10 +1530,38 @@ struct AANoRecurseImpl : public AANoRecurse {
15301530
struct AANoRecurseFunction final : AANoRecurseImpl {
15311531
AANoRecurseFunction(const IRPosition &IRP) : AANoRecurseImpl(IRP) {}
15321532

1533+
/// See AbstractAttribute::initialize(...).
1534+
void initialize(Attributor &A) override {
1535+
AANoRecurseImpl::initialize(A);
1536+
if (const Function *F = getAnchorScope())
1537+
if (A.getInfoCache().getSccSize(*F) == 1)
1538+
return;
1539+
indicatePessimisticFixpoint();
1540+
}
1541+
15331542
/// See AbstractAttribute::updateImpl(...).
15341543
ChangeStatus updateImpl(Attributor &A) override {
1535-
// TODO: Implement this.
1536-
return indicatePessimisticFixpoint();
1544+
1545+
auto CheckForNoRecurse = [&](Instruction &I) {
1546+
ImmutableCallSite ICS(&I);
1547+
if (ICS.hasFnAttr(Attribute::NoRecurse))
1548+
return true;
1549+
1550+
const auto &NoRecurseAA =
1551+
A.getAAFor<AANoRecurse>(*this, IRPosition::callsite_function(ICS));
1552+
if (!NoRecurseAA.isAssumedNoRecurse())
1553+
return false;
1554+
1555+
// Recursion to the same function
1556+
if (ICS.getCalledFunction() == getAnchorScope())
1557+
return false;
1558+
1559+
return true;
1560+
};
1561+
1562+
if (!A.checkForAllCallLikeInstructions(CheckForNoRecurse, *this))
1563+
return indicatePessimisticFixpoint();
1564+
return ChangeStatus::UNCHANGED;
15371565
}
15381566

15391567
void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(norecurse) }
@@ -3934,6 +3962,9 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
39343962
// Every function might be "no-return".
39353963
getOrCreateAAFor<AANoReturn>(FPos);
39363964

3965+
// Every function might be "no-recurse".
3966+
getOrCreateAAFor<AANoRecurse>(FPos);
3967+
39373968
// Every function might be applicable for Heap-To-Stack conversion.
39383969
if (EnableHeapToStack)
39393970
getOrCreateAAFor<AAHeapToStack>(FPos);
@@ -4113,7 +4144,7 @@ static bool runAttributorOnModule(Module &M, AnalysisGetter &AG) {
41134144

41144145
// Create an Attributor and initially empty information cache that is filled
41154146
// while we identify default attribute opportunities.
4116-
InformationCache InfoCache(M.getDataLayout(), AG);
4147+
InformationCache InfoCache(M, AG);
41174148
Attributor A(InfoCache, DepRecInterval);
41184149

41194150
for (Function &F : M)
@@ -4149,9 +4180,7 @@ static bool runAttributorOnModule(Module &M, AnalysisGetter &AG) {
41494180
}
41504181

41514182
PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
4152-
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
4153-
4154-
AnalysisGetter AG(FAM);
4183+
AnalysisGetter AG(AM);
41554184
if (runAttributorOnModule(M, AG)) {
41564185
// FIXME: Think about passes we will preserve and add them here.
41574186
return PreservedAnalyses::none();
Lines changed: 83 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,88 @@
1-
; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s
2-
; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-functionattrs' -S | FileCheck %s
1+
; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s --check-prefixes=CHECK,BOTH
2+
; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-functionattrs' -S | FileCheck %s --check-prefixes=CHECK,BOTH
3+
; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,BOTH
34

45
; CHECK: Function Attrs
56
; CHECK-SAME: norecurse nounwind readnone
6-
; CHECK-NEXT: define i32 @leaf()
7+
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind willreturn
8+
; BOTH-NEXT: define i32 @leaf()
79
define i32 @leaf() {
810
ret i32 1
911
}
1012

11-
; CHECK: Function Attrs
13+
; BOTH: Function Attrs
1214
; CHECK-SAME: readnone
13-
; CHECK-NOT: norecurse
14-
; CHECK-NEXT: define i32 @self_rec()
15+
; BOTH-NOT: norecurse
16+
; BOTH-NEXT: define i32 @self_rec()
1517
define i32 @self_rec() {
1618
%a = call i32 @self_rec()
1719
ret i32 4
1820
}
1921

20-
; CHECK: Function Attrs
22+
; BOTH: Function Attrs
2123
; CHECK-SAME: readnone
22-
; CHECK-NOT: norecurse
23-
; CHECK-NEXT: define i32 @indirect_rec()
24+
; BOTH-NOT: norecurse
25+
; BOTH-NEXT: define i32 @indirect_rec()
2426
define i32 @indirect_rec() {
2527
%a = call i32 @indirect_rec2()
2628
ret i32 %a
2729
}
28-
; CHECK: Function Attrs
30+
; BOTH: Function Attrs
2931
; CHECK-SAME: readnone
30-
; CHECK-NOT: norecurse
31-
; CHECK-NEXT: define i32 @indirect_rec2()
32+
; BOTH-NOT: norecurse
33+
; BOTH-NEXT: define i32 @indirect_rec2()
3234
define i32 @indirect_rec2() {
3335
%a = call i32 @indirect_rec()
3436
ret i32 %a
3537
}
3638

37-
; CHECK: Function Attrs
39+
; BOTH: Function Attrs
3840
; CHECK-SAME: readnone
39-
; CHECK-NOT: norecurse
40-
; CHECK-NEXT: define i32 @extern()
41+
; BOTH-NOT: norecurse
42+
; BOTH-NEXT: define i32 @extern()
4143
define i32 @extern() {
4244
%a = call i32 @k()
4345
ret i32 %a
4446
}
4547

46-
; CHECK: Function Attrs
47-
; CHECK-NEXT: declare i32 @k()
48+
; BOTH: Function Attrs
49+
; BOTH-NEXT: declare i32 @k()
4850
declare i32 @k() readnone
4951

50-
; CHECK: Function Attrs
52+
; BOTH: Function Attrs
5153
; CHECK-SAME: nounwind
52-
; CHECK-NOT: norecurse
54+
; BOTH-NOT: norecurse
5355
; CHECK-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len)
56+
; ATTRIBUTOR-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture %src, i32 %len)
5457
define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
5558
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false)
5659
ret void
5760
}
5861

59-
; CHECK: Function Attrs
60-
; CHECK-NEXT: declare void @llvm.memcpy.p0i8.p0i8.i32
62+
; BOTH: Function Attrs
63+
; BOTH-NEXT: declare void @llvm.memcpy.p0i8.p0i8.i32
6164
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
6265

63-
; CHECK: Function Attrs
66+
; BOTH: Function Attrs
6467
; CHECK-SAME: norecurse readnone
68+
; FIXME: missing "norecurse"
69+
; ATTRIBUTOR-SAME: nosync
6570
; CHECK-NEXT: define internal i32 @called_by_norecurse()
6671
define internal i32 @called_by_norecurse() {
6772
%a = call i32 @k()
6873
ret i32 %a
6974
}
70-
; CHECK: Function Attrs
71-
; CHECK-NEXT: define void @m()
75+
; BOTH: Function Attrs
76+
; BOTH-NEXT: define void @m()
7277
define void @m() norecurse {
7378
%a = call i32 @called_by_norecurse()
7479
ret void
7580
}
7681

77-
; CHECK: Function Attrs
82+
; BOTH: Function Attrs
7883
; CHECK-SAME: norecurse readnone
84+
; FIXME: missing "norecurse"
85+
; ATTRIBUTOR-SAME: nosync
7986
; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly()
8087
define internal i32 @called_by_norecurse_indirectly() {
8188
%a = call i32 @k()
@@ -89,3 +96,54 @@ define void @p() norecurse {
8996
call void @o()
9097
ret void
9198
}
99+
100+
; ATTRIBUTOR: Function Attrs: nofree nosync nounwind
101+
; ATTRIBUTOR-NEXT: define void @f(i32 %x)
102+
define void @f(i32 %x) {
103+
entry:
104+
%x.addr = alloca i32, align 4
105+
store i32 %x, i32* %x.addr, align 4
106+
%0 = load i32, i32* %x.addr, align 4
107+
%tobool = icmp ne i32 %0, 0
108+
br i1 %tobool, label %if.then, label %if.end
109+
110+
if.then:
111+
call void @g() norecurse
112+
br label %if.end
113+
114+
if.end:
115+
ret void
116+
}
117+
118+
; BOTH: define void @g()
119+
define void @g() norecurse {
120+
entry:
121+
call void @f(i32 0)
122+
ret void
123+
}
124+
125+
; ATTRIBUTOR-NOT: Function Attrs
126+
; ATTRIBUTOR: define linkonce_odr i32 @leaf_redefinable()
127+
define linkonce_odr i32 @leaf_redefinable() {
128+
ret i32 1
129+
}
130+
131+
; Call through a function pointer
132+
; ATTRIBUTOR-NOT: Function Attrs
133+
; ATTRIBUTOR: define i32 @eval_func(i32 (i32)* nocapture %0, i32 %1)
134+
define i32 @eval_func(i32 (i32)* , i32) local_unnamed_addr {
135+
%3 = tail call i32 %0(i32 %1) #2
136+
ret i32 %3
137+
}
138+
139+
declare void @unknown()
140+
; Call an unknown function in a dead block.
141+
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind willreturn
142+
; ATTRIBUTOR: define i32 @call_unknown_in_dead_block()
143+
define i32 @call_unknown_in_dead_block() local_unnamed_addr {
144+
ret i32 0
145+
Dead:
146+
tail call void @unknown()
147+
ret i32 1
148+
}
149+

0 commit comments

Comments
 (0)