Skip to content

Commit fb3eb0b

Browse files
committed
Simplify function signature optimzation.
Several functionalities have been added to FSO over time and the logic has become muddled. We were always looking at a static image of the SIL and try to reason about what kind of function signature related optimizations we can do. This can easily lead to muddled logic. e.g. we need to consider 2 different function signature optimizations together instead of independently. Split 1 single function to do all sorts of different analyses in FSO into several small transformations, each of which does a specific job. After every analysis, we produce a new function and eventually we collapse all intermediate thunks to in a single thunk. With this change, it will be easier to implement function signature optimization as now we can do them independently now. Small modifications to the test cases.
1 parent b2aaa61 commit fb3eb0b

17 files changed

+761
-904
lines changed

include/swift/SIL/Mangle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ template <typename SubType>
104104
class SpecializationMangler : public SpecializationManglerBase {
105105
SubType *asImpl() { return static_cast<SubType *>(this); }
106106
public:
107+
Mangle::Mangler &getMangler() const { return M; }
107108

108109
~SpecializationMangler() = default;
109110

include/swift/SIL/Projection.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -730,11 +730,6 @@ class ProjectionTreeNode {
730730
Initialized(false), IsLive(false) {}
731731

732732
public:
733-
enum LivenessKind : unsigned {
734-
NormalUseLiveness = 0,
735-
IgnoreEpilogueReleases = 1,
736-
};
737-
738733
class NewAggregateBuilder;
739734

740735
~ProjectionTreeNode() = default;
@@ -799,8 +794,7 @@ class ProjectionTreeNode {
799794

800795
void processUsersOfValue(ProjectionTree &Tree,
801796
llvm::SmallVectorImpl<ValueNodePair> &Worklist,
802-
SILValue Value, ProjectionTreeNode::LivenessKind Kind,
803-
llvm::DenseSet<SILInstruction *> &Releases);
797+
SILValue Value);
804798

805799
void createNextLevelChildren(ProjectionTree &Tree);
806800

@@ -816,11 +810,6 @@ class ProjectionTree {
816810

817811
llvm::BumpPtrAllocator &Allocator;
818812

819-
/// The way we compute what is live and what is dead.
820-
ProjectionTreeNode::LivenessKind Kind;
821-
822-
llvm::DenseSet<SILInstruction *> EpilogueReleases;
823-
824813
// A common pattern is a 3 field struct.
825814
llvm::SmallVector<ProjectionTreeNode *, 4> ProjectionTreeNodes;
826815
llvm::SmallVector<unsigned, 3> LiveLeafIndices;
@@ -831,9 +820,6 @@ class ProjectionTree {
831820
/// Construct a projection tree from BaseTy.
832821
ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &Allocator,
833822
SILType BaseTy);
834-
ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &Allocator,
835-
SILType BaseTy, ProjectionTreeNode::LivenessKind Kind,
836-
llvm::DenseSet<SILInstruction*> Insts);
837823
/// Construct an uninitialized projection tree, which can then be
838824
/// initialized by initializeWithExistingTree.
839825
ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &Allocator)

include/swift/SILOptimizer/Analysis/ARCAnalysis.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ class ConsumedArgToEpilogueReleaseMatcher {
189189
ExitKind Kind;
190190
llvm::SmallMapVector<SILArgument *, ReleaseList, 8> ArgInstMap;
191191

192+
/// Set to true if we found some releases but not all for the argument.
193+
llvm::DenseSet<SILArgument *> FoundSomeReleases;
194+
192195
/// Eventually this will be used in place of HasBlock.
193196
SILBasicBlock *ProcessedBlock;
194197

@@ -240,6 +243,12 @@ class ConsumedArgToEpilogueReleaseMatcher {
240243
return false;
241244
}
242245

246+
/// Return true if we've found some epilgoue releases for the argument
247+
/// but not all.
248+
bool hasSomeReleasesForArgument(SILArgument *Arg) {
249+
return FoundSomeReleases.find(Arg) != FoundSomeReleases.end();
250+
}
251+
243252
bool isSingleRelease(SILArgument *Arg) const {
244253
auto Iter = ArgInstMap.find(Arg);
245254
assert(Iter != ArgInstMap.end() && "Failed to get release list for argument");

include/swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h

Lines changed: 21 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@
2828

2929
namespace swift {
3030

31-
using ReleaseSet = llvm::DenseSet<SILInstruction *>;
32-
3331
/// A structure that maintains all of the information about a specific
3432
/// SILArgument that we are tracking.
3533
struct ArgumentDescriptor {
36-
3734
/// The argument that we are tracking original data for.
3835
SILArgument *Arg;
3936

37+
/// Parameter Info.
38+
SILParameterInfo PInfo;
39+
4040
/// The original index of this argument.
4141
unsigned Index;
4242

@@ -48,6 +48,9 @@ struct ArgumentDescriptor {
4848

4949
/// Should the argument be exploded ?
5050
bool Explode;
51+
52+
/// This parameter is owned to guaranteed.
53+
bool OwnedToGuaranteed;
5154

5255
/// Is this parameter an indirect result?
5356
bool IsIndirectResult;
@@ -73,17 +76,13 @@ struct ArgumentDescriptor {
7376
/// to the original argument. The reason why we do this is to make sure we
7477
/// have access to the original argument's state if we modify the argument
7578
/// when optimizing.
76-
ArgumentDescriptor(llvm::BumpPtrAllocator &BPA, SILArgument *A,
77-
ReleaseSet Releases)
78-
: Arg(A), Index(A->getIndex()),
79+
ArgumentDescriptor(llvm::BumpPtrAllocator &BPA, SILArgument *A)
80+
: Arg(A), PInfo(Arg->getKnownParameterInfo()), Index(A->getIndex()),
7981
Decl(A->getDecl()), IsEntirelyDead(false), Explode(false),
82+
OwnedToGuaranteed(false),
8083
IsIndirectResult(A->isIndirectResult()),
8184
CalleeRelease(), CalleeReleaseInThrowBlock(),
82-
ProjTree(A->getModule(), BPA, A->getType(),
83-
ProjectionTreeNode::LivenessKind::IgnoreEpilogueReleases,
84-
Releases) {
85-
ProjTree.computeUsesAndLiveness(A);
86-
}
85+
ProjTree(A->getModule(), BPA, A->getType()) {}
8786

8887
ArgumentDescriptor(const ArgumentDescriptor &) = delete;
8988
ArgumentDescriptor(ArgumentDescriptor &&) = default;
@@ -95,35 +94,12 @@ struct ArgumentDescriptor {
9594
return Arg->hasConvention(P);
9695
}
9796

98-
/// Convert the potentially multiple interface params associated with this
99-
/// argument.
100-
void
101-
computeOptimizedInterfaceParams(SmallVectorImpl<SILParameterInfo> &Out) const;
102-
103-
/// Add potentially multiple new arguments to NewArgs from the caller's apply
104-
/// or try_apply inst.
105-
void addCallerArgs(SILBuilder &Builder, FullApplySite FAS,
106-
SmallVectorImpl<SILValue> &NewArgs) const;
107-
108-
/// Add potentially multiple new arguments to NewArgs from the thunk's
109-
/// function arguments.
110-
void addThunkArgs(SILBuilder &Builder, SILBasicBlock *BB,
111-
SmallVectorImpl<SILValue> &NewArgs) const;
112-
113-
/// Optimize the argument at ArgOffset and return the index of the next
114-
/// argument to be optimized.
115-
///
116-
/// The return value makes it easy to SROA arguments since we can return the
117-
/// amount of SROAed arguments we created.
118-
unsigned updateOptimizedBBArgs(SILBuilder &Builder, SILBasicBlock *BB,
119-
unsigned ArgOffset);
120-
12197
bool canOptimizeLiveArg() const {
12298
return Arg->getType().isObject();
12399
}
124100

125101
/// Return true if it's both legal and a good idea to explode this argument.
126-
bool shouldExplode() const {
102+
bool shouldExplode(ConsumedArgToEpilogueReleaseMatcher &ERM) const {
127103
// We cannot optimize the argument.
128104
if (!canOptimizeLiveArg())
129105
return false;
@@ -150,15 +126,19 @@ struct ResultDescriptor {
150126
/// @owned or we could not find such a release in the callee, this is null.
151127
RetainList CalleeRetain;
152128

129+
/// This is owned to guaranteed.
130+
bool OwnedToGuaranteed;
131+
153132
/// Initialize this argument descriptor with all information from A that we
154133
/// use in our optimization.
155134
///
156135
/// *NOTE* We cache a lot of data from the argument and maintain a reference
157136
/// to the original argument. The reason why we do this is to make sure we
158137
/// have access to the original argument's state if we modify the argument
159138
/// when optimizing.
160-
ResultDescriptor() {};
161-
ResultDescriptor(SILResultInfo RI) : ResultInfo(RI), CalleeRetain() {}
139+
ResultDescriptor() {}
140+
ResultDescriptor(SILResultInfo RI)
141+
: ResultInfo(RI), CalleeRetain(), OwnedToGuaranteed(false) {}
162142

163143
ResultDescriptor(const ResultDescriptor &) = delete;
164144
ResultDescriptor(ResultDescriptor &&) = default;
@@ -171,100 +151,12 @@ struct ResultDescriptor {
171151
}
172152
};
173153

174-
class FunctionSignatureInfo {
175-
/// Should this function be optimized.
176-
bool ShouldOptimize;
177-
178-
/// Optimizing this function may lead to good performance potential.
179-
bool HighlyProfitable;
180-
181-
/// Function currently analyzing.
182-
SILFunction *F;
183-
184-
/// The allocator we are using.
185-
llvm::BumpPtrAllocator &Allocator;
186-
187-
/// The alias analysis currently using.
188-
AliasAnalysis *AA;
189-
190-
/// The rc-identity analysis currently using.
191-
RCIdentityFunctionInfo *RCFI;
192-
193-
/// Does any call inside the given function may bind dynamic 'Self' to a
194-
/// generic argument of the callee.
195-
bool MayBindDynamicSelf;
196-
197-
/// Did we decide to change the self argument? If so we need to
198-
/// change the calling convention 'method' to 'freestanding'.
199-
bool ShouldModifySelfArgument = false;
200-
201-
/// A list of structures which present a "view" of precompiled information on
202-
/// an argument that we will use during our optimization.
203-
llvm::SmallVector<ArgumentDescriptor, 8> ArgDescList;
204-
205-
/// Keep a "view" of precompiled information on the direct results
206-
/// which we will use during our optimization.
207-
llvm::SmallVector<ResultDescriptor, 4> ResultDescList;
208-
209-
210-
public:
211-
FunctionSignatureInfo(SILFunction *F, llvm::BumpPtrAllocator &BPA,
212-
AliasAnalysis *AA, RCIdentityFunctionInfo *RCFI) :
213-
ShouldOptimize(false), HighlyProfitable(false), F(F), Allocator(BPA),
214-
AA(AA), RCFI(RCFI), MayBindDynamicSelf(computeMayBindDynamicSelf(F)) {
215-
analyze();
216-
}
217-
218-
bool shouldOptimize() const { return ShouldOptimize; }
219-
bool profitableOptimize() const { return HighlyProfitable; }
220-
221-
void analyze();
222-
bool analyzeParameters();
223-
bool analyzeResult();
224-
225-
/// Returns the mangled name of the function that should be generated from
226-
/// this function analyzer.
227-
std::string getOptimizedName() const;
228-
229-
bool shouldModifySelfArgument() const { return ShouldModifySelfArgument; }
230-
ArrayRef<ArgumentDescriptor> getArgDescList() const { return ArgDescList; }
231-
ArrayRef<ResultDescriptor> getResultDescList() {return ResultDescList;}
232-
SILFunction *getAnalyzedFunction() const { return F; }
233-
234-
private:
235-
/// Is the given argument required by the ABI?
236-
///
237-
/// Metadata arguments may be required if dynamic Self is bound to any generic
238-
/// parameters within this function's call sites.
239-
bool isArgumentABIRequired(SILArgument *Arg) {
240-
// This implicitly asserts that a function binding dynamic self has a self
241-
// metadata argument or object from which self metadata can be obtained.
242-
return MayBindDynamicSelf && (F->getSelfMetadataArgument() == Arg);
243-
}
244-
};
245-
246-
247-
154+
/// Returns true if F is a function which the pass know show to specialize
155+
/// function signatures for.
248156
bool canSpecializeFunction(SILFunction *F);
249157

250-
void
251-
addReleasesForConvertedOwnedParameter(SILBuilder &Builder,
252-
SILLocation Loc,
253-
OperandValueArrayRef Parameters,
254-
ArrayRef<ArgumentDescriptor> &ArgDescs);
255-
256-
void
257-
addReleasesForConvertedOwnedParameter(SILBuilder &Builder,
258-
SILLocation Loc,
259-
ArrayRef<SILArgument*> Parameters,
260-
ArrayRef<ArgumentDescriptor> &ArgDescs);
261-
void
262-
addRetainsForConvertedDirectResults(SILBuilder &Builder,
263-
SILLocation Loc,
264-
SILValue ReturnValue,
265-
SILInstruction *AI,
266-
ArrayRef<ResultDescriptor> DirectResults);
267-
158+
/// Return true if this argument is used in a non-trivial way.
159+
bool hasNonTrivialNonDebugUse(SILArgument *Arg);
268160

269161
} // end namespace swift
270162

lib/SIL/Mangle.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ FunctionSignatureSpecializationMangler(SpecializationPass P, Mangler &M,
8181
void
8282
FunctionSignatureSpecializationMangler::
8383
setArgumentDead(unsigned ArgNo) {
84-
Args[ArgNo].first = ArgumentModifierIntBase(ArgumentModifier::Dead);
84+
Args[ArgNo].first |= ArgumentModifierIntBase(ArgumentModifier::Dead);
8585
}
8686

8787
void

lib/SIL/Projection.cpp

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -892,8 +892,7 @@ void
892892
ProjectionTreeNode::
893893
processUsersOfValue(ProjectionTree &Tree,
894894
llvm::SmallVectorImpl<ValueNodePair> &Worklist,
895-
SILValue Value, LivenessKind Kind,
896-
llvm::DenseSet<SILInstruction *> &Releases) {
895+
SILValue Value) {
897896
DEBUG(llvm::dbgs() << " Looking at Users:\n");
898897

899898
// For all uses of V...
@@ -911,13 +910,6 @@ processUsersOfValue(ProjectionTree &Tree,
911910
if (!P.isValid()) {
912911
DEBUG(llvm::dbgs() << " Failed to create projection. Adding "
913912
"to non projection user!\n");
914-
// Is the user an epilogue release ?
915-
if (Kind == IgnoreEpilogueReleases) {
916-
bool EpilogueReleaseUser = !Releases.empty();
917-
EpilogueReleaseUser &= Releases.find(User) != Releases.end();
918-
if (EpilogueReleaseUser)
919-
continue;
920-
}
921913
addNonProjectionUser(Op);
922914
continue;
923915
}
@@ -949,12 +941,6 @@ processUsersOfValue(ProjectionTree &Tree,
949941
// The only projection which we do not currently handle are enums since we
950942
// may not know the correct case. This can be extended in the future.
951943
// Is the user an epilogue release ?
952-
if (Kind == IgnoreEpilogueReleases) {
953-
bool EpilogueReleaseUser = !Releases.empty();
954-
EpilogueReleaseUser &= Releases.find(User) != Releases.end();
955-
if (EpilogueReleaseUser)
956-
continue;
957-
}
958944
addNonProjectionUser(Op);
959945
}
960946
}
@@ -1157,21 +1143,7 @@ class NewAggregateBuilderMap {
11571143

11581144
ProjectionTree::
11591145
ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &BPA, SILType BaseTy)
1160-
: Mod(Mod), Allocator(BPA),
1161-
Kind(ProjectionTreeNode::LivenessKind::NormalUseLiveness) {
1162-
DEBUG(llvm::dbgs() << "Constructing Projection Tree For : " << BaseTy);
1163-
1164-
// Create the root node of the tree with our base type.
1165-
createRoot(BaseTy);
1166-
1167-
// Create the rest of the type tree lazily based on uses.
1168-
}
1169-
1170-
ProjectionTree::
1171-
ProjectionTree(SILModule &Mod, llvm::BumpPtrAllocator &BPA, SILType BaseTy,
1172-
ProjectionTreeNode::LivenessKind Kind,
1173-
llvm::DenseSet<SILInstruction *> Insts)
1174-
: Mod(Mod), Allocator(BPA), Kind(Kind), EpilogueReleases(Insts) {
1146+
: Mod(Mod), Allocator(BPA) {
11751147
DEBUG(llvm::dbgs() << "Constructing Projection Tree For : " << BaseTy);
11761148

11771149
// Create the root node of the tree with our base type.
@@ -1187,8 +1159,6 @@ ProjectionTree::~ProjectionTree() {
11871159

11881160
void
11891161
ProjectionTree::initializeWithExistingTree(const ProjectionTree &PT) {
1190-
Kind = PT.Kind;
1191-
EpilogueReleases = PT.EpilogueReleases;
11921162
LiveLeafIndices = PT.LiveLeafIndices;
11931163
for (const auto &N : PT.ProjectionTreeNodes) {
11941164
ProjectionTreeNodes.push_back(new (Allocator) ProjectionTreeNode(*N));
@@ -1285,7 +1255,7 @@ computeUsesAndLiveness(SILValue Base) {
12851255
// If Value is not null, collate all users of Value the appropriate child
12861256
// nodes and add such items to the worklist.
12871257
if (Value) {
1288-
Node->processUsersOfValue(*this, UseWorklist, Value, Kind, EpilogueReleases);
1258+
Node->processUsersOfValue(*this, UseWorklist, Value);
12891259
}
12901260

12911261
// If this node is live due to a non projection user, propagate down its
@@ -1449,11 +1419,9 @@ replaceValueUsesWithLeafUses(SILBuilder &Builder, SILLocation Loc,
14491419

14501420
// Grab the parent of this node.
14511421
ProjectionTreeNode *Parent = Node->getParent(*this);
1452-
DEBUG(llvm::dbgs() << " Visiting parent of leaf: " <<
1453-
Parent->getType() << "\n");
14541422

14551423
// If the parent is dead, continue.
1456-
if (!Parent->IsLive) {
1424+
if (!Parent || !Parent->IsLive) {
14571425
DEBUG(llvm::dbgs() << " Parent is dead... continuing.\n");
14581426
continue;
14591427
}

0 commit comments

Comments
 (0)