Skip to content

Commit 2f814dd

Browse files
author
Raj Barik
committed
ProtocolDevirtualizer Part 2: Transformation Phase
1 parent 873d982 commit 2f814dd

File tree

8 files changed

+555
-0
lines changed

8 files changed

+555
-0
lines changed

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class SILOptions {
6464
/// Remove all runtime assertions during optimizations.
6565
bool RemoveRuntimeAsserts = false;
6666

67+
/// Enable protocol devirtualization optimization.
68+
bool ProtocolDevirtualizer = false;
69+
6770
/// Controls whether the SIL ARC optimizations are run.
6871
bool EnableARCOptimizations = true;
6972

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,9 @@ def sil_unroll_threshold : Separate<["-"], "sil-unroll-threshold">,
390390
MetaVarName<"<250>">,
391391
HelpText<"Controls the aggressiveness of loop unrolling">;
392392

393+
def sil_protocol_devirtualizer : Flag<["-"], "sil-protocol-devirtualizer">,
394+
HelpText<"Enable SIL protocol devirtualizer optimization">;
395+
393396
def sil_merge_partial_modules : Flag<["-"], "sil-merge-partial-modules">,
394397
HelpText<"Merge SIL from all partial swiftmodules into the final module">;
395398

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ PASS(DeadStoreElimination, "dead-store-elim",
150150
"Dead Store Elimination")
151151
PASS(GenericSpecializer, "generic-specializer",
152152
"Generic Function Specialization on Static Types")
153+
PASS(ProtocolDevirtualizer, "protocol-devirtualizer",
154+
"Protocol Devirtualizer")
153155
PASS(GlobalOpt, "global-opt",
154156
"SIL Global Optimization")
155157
PASS(GlobalPropertyOpt, "global-property-opt",

include/swift/SILOptimizer/Utils/Generics.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
#include "swift/AST/SubstitutionMap.h"
2121
#include "swift/SIL/SILFunction.h"
2222
#include "swift/SIL/SILInstruction.h"
23+
#include "swift/SILOptimizer/Analysis/ProtocolDevirtualizerAnalysis.h"
24+
#include "swift/SILOptimizer/Utils/FunctionSignatureOptUtils.h"
2325
#include "swift/SILOptimizer/Utils/Local.h"
26+
#include "swift/SILOptimizer/Utils/SpecializationMangler.h"
2427
#include "llvm/ADT/SmallBitVector.h"
2528
#include "llvm/Support/CommandLine.h"
2629
#include "llvm/Support/Debug.h"
@@ -299,6 +302,61 @@ class GenericFuncSpecializer {
299302
}
300303
};
301304

305+
/// ProtocolDevirtualizer transformation class that creates a protocol
306+
/// constrained generic and a thunk.
307+
class ProtocolDevirtualizerTransform {
308+
/// The original function to analyze and transform.
309+
SILFunction *F;
310+
311+
/// The newly created inner function.
312+
SILFunction *NewF;
313+
314+
/// The function signature mangler we are using.
315+
Mangle::FunctionSignatureSpecializationMangler &Mangler;
316+
317+
/// Map Arguments to their ProtocolTypes.
318+
llvm::SmallDenseMap<int, std::pair<ProtocolDecl *, ClassDecl *>> &Arg2DeclMap;
319+
320+
/// Argument to Generic Type Map for NewF.
321+
llvm::SmallDenseMap<int, GenericTypeParamType *> Arg2GenericTypeMap;
322+
323+
// Allocate the argument descriptors.
324+
llvm::SmallVector<ArgumentDescriptor, 4> &ArgumentDescList;
325+
326+
/// Create the Devirtualized Inner Function.
327+
void createDevirtualizedProtocolFunction();
328+
329+
/// Create a name for the inner function.
330+
std::string createDevirtualizedFunctionName();
331+
332+
/// Create the new devirtualized protocol function signature.
333+
CanSILFunctionType createDevirtualizedFunctionType();
334+
335+
/// Populate the body of NewF.
336+
void populateProtocolConstrainedGenericFunction(const SILDebugScope *DS);
337+
338+
/// Create the thunk.
339+
void populateThunkBody();
340+
341+
public:
342+
/// Constructor.
343+
ProtocolDevirtualizerTransform(
344+
SILFunction *F,
345+
Mangle::FunctionSignatureSpecializationMangler &Mangler,
346+
llvm::SmallVector<ArgumentDescriptor, 4> &ADL,
347+
llvm::SmallDenseMap<int, std::pair<ProtocolDecl *, ClassDecl *>> &Arg2DeclMap
348+
) : F(F), NewF(nullptr), Mangler(Mangler), Arg2DeclMap(Arg2DeclMap) , ArgumentDescList(ADL) {}
349+
350+
/// Return the optimized iner function.
351+
SILFunction *getDevirtualizedProtocolFunction() { return NewF; }
352+
353+
/// Run the optimization.
354+
bool run() {
355+
createDevirtualizedProtocolFunction();
356+
return true;
357+
}
358+
};
359+
302360
// =============================================================================
303361
// Prespecialized symbol lookup.
304362
// =============================================================================

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,9 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
568568
return true;
569569
}
570570
}
571+
if (Args.hasArg(OPT_sil_protocol_devirtualizer)) {
572+
Opts.ProtocolDevirtualizer = true;
573+
}
571574
if (const Arg *A = Args.getLastArg(OPT_num_threads)) {
572575
if (StringRef(A->getValue()).getAsInteger(10, Opts.NumThreads)) {
573576
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
331331

332332
// Add the outliner pass (Osize).
333333
P.addOutliner();
334+
335+
// Protocol Devirtualizer Pass.
336+
P.addProtocolDevirtualizer();
334337
}
335338

336339
static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) {

lib/SILOptimizer/Transforms/GenericSpecializer.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,13 @@
2424
#include "swift/SILOptimizer/Utils/Local.h"
2525
#include "swift/SILOptimizer/PassManager/Transforms.h"
2626
#include "llvm/ADT/SmallVector.h"
27+
#include "llvm/ADT/Statistic.h"
2728

2829
using namespace swift;
2930

31+
STATISTIC(NumFunctionsWithProtocolArgsDevirtualized, "Number of functions with protocol args devirtualized");
32+
using ArgIndexList = llvm::SmallVector<unsigned, 8>;
33+
3034
namespace {
3135

3236
class GenericSpecializer : public SILFunctionTransform {
@@ -45,6 +49,93 @@ class GenericSpecializer : public SILFunctionTransform {
4549

4650
};
4751

52+
/// ProtocolDevirtualizer class.
53+
class ProtocolDevirtualizer : public SILFunctionTransform {
54+
55+
/// determine if the current function is a target for protocol devirtualizer.
56+
bool canDevirtualizeProtocolInFunction(ProtocolDevirtualizerAnalysis *PDA,
57+
llvm::SmallDenseMap<int, std::pair<ProtocolDecl*, ClassDecl *>> &Arg2DeclMap);
58+
59+
public:
60+
61+
void run() override {
62+
auto *F = getFunction();
63+
64+
/// Don't run protocol devirtualizer at -Os.
65+
if (F->optimizeForSize())
66+
return;
67+
68+
/// Don't optimize functions that should not be optimized.
69+
if (F->empty() || (!F->shouldOptimize())) {
70+
return;
71+
}
72+
73+
/// This is the function to optimize for protocol devirtualize.
74+
DEBUG(llvm::dbgs() << "*** ProtocolDevirtualization Pass on function: " << F->getName() << " ***\n");
75+
76+
/// Use FunctionSignature Specialization to determine if this function
77+
/// can be specialized, without call graph information.
78+
if ( !canSpecializeFunction(F, nullptr, false)) {
79+
DEBUG(llvm::dbgs() << " cannot specialize function -> abort\n");
80+
return;
81+
}
82+
83+
CallerAnalysis *CA = PM->getAnalysis<CallerAnalysis>();
84+
const CallerAnalysis::FunctionInfo &FuncInfo = CA->getCallerInfo(F);
85+
86+
/// Use FunctionSignature Specialization to determine if this function
87+
/// can be specialized, based on call graph.
88+
/// canSpecializeFunction does not consider generic methods -- TODO in future.
89+
if (!canSpecializeFunction(F, &FuncInfo, true)) {
90+
DEBUG(llvm::dbgs() << " cannot specialize function -> abort\n");
91+
return;
92+
}
93+
94+
/// Get the protocol devirtualizer pass that contains which protocols can be devirtualized.
95+
ProtocolDevirtualizerAnalysis *PDA = getAnalysis<ProtocolDevirtualizerAnalysis>();
96+
97+
/// Determine the arguments that can be devirtualized.
98+
llvm::SmallDenseMap<int, std::pair<ProtocolDecl *, ClassDecl *>> Arg2DeclMap;
99+
if(!canDevirtualizeProtocolInFunction(PDA, Arg2DeclMap)) {
100+
DEBUG(llvm::dbgs() << " cannot devirtualize function -> abort\n");
101+
return;
102+
}
103+
104+
DEBUG(llvm::dbgs() << "Function::" << F->getName() << " has a Protocol Argument and can be optimized via PDA\n");
105+
106+
/// Name Mangler for naming the protocol constrained generic method.
107+
auto P = Demangle::SpecializationPass::GenericSpecializer;
108+
Mangle::FunctionSignatureSpecializationMangler Mangler(P, F->isSerialized(), F);
109+
110+
/// Save the arguments in a descriptor.
111+
llvm::SmallVector<ArgumentDescriptor, 4> ArgumentDescList;
112+
auto Args = F->begin()->getFunctionArguments();
113+
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
114+
ArgumentDescList.emplace_back(Args[i]);
115+
}
116+
117+
/// Instantiate the ProtocolDevirtualizerTransform pass.
118+
ProtocolDevirtualizerTransform PDT(F, Mangler, ArgumentDescList, Arg2DeclMap);
119+
120+
/// Run the protocol devirtualizer.
121+
bool Changed = PDT.run();
122+
123+
if (Changed) {
124+
/// Update statistics on the number of functions devirtualized.
125+
++ NumFunctionsWithProtocolArgsDevirtualized;
126+
127+
/// Invalidate Analysis as we introduce a new function.
128+
invalidateAnalysis(SILAnalysis::InvalidationKind::Everything);
129+
130+
/// Make sure the PM knows about the new devirtualized inner function.
131+
notifyAddFunction(PDT.getDevirtualizedProtocolFunction(), F);
132+
133+
/// should we restart pipeline? be conservative.
134+
restartPassPipeline();
135+
}
136+
}
137+
};
138+
48139
} // end anonymous namespace
49140

50141
bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) {
@@ -129,6 +220,39 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) {
129220
return Changed;
130221
}
131222

223+
/// Check if any argument to a function meet the criteria for devirtualization.
224+
bool ProtocolDevirtualizer::canDevirtualizeProtocolInFunction(ProtocolDevirtualizerAnalysis *PDA,
225+
llvm::SmallDenseMap<int, std::pair<ProtocolDecl*, ClassDecl *>> &Arg2DeclMap) {
226+
auto *F = getFunction();
227+
auto Args = F->begin()->getFunctionArguments();
228+
bool returnFlag = false;
229+
230+
/// Analyze the argument for protocol conformance.
231+
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
232+
auto ArgType = Args[i]->getType();
233+
/// Keep it simple for now. Do not handle Protocol constrained methods or Optionals.
234+
auto SwiftArgType = ArgType.getSwiftRValueType();
235+
if (ArgType && ArgType.isAnyExistentialType()) {
236+
auto SwiftProtoDecl = SwiftArgType.getAnyNominal();
237+
if (SwiftProtoDecl) {
238+
auto ProtoDecl = dyn_cast<ProtocolDecl>(SwiftProtoDecl);
239+
auto CD = PDA->getSoleClassImplementingProtocol(ProtoDecl);
240+
if (CD) {
241+
/// Save the mapping for transformation pass.
242+
Arg2DeclMap[i] = std::make_pair(ProtoDecl, CD);
243+
DEBUG(llvm::dbgs() << "Function: " << F->getName() << " has a singel class decl\n");
244+
returnFlag |= true;
245+
}
246+
}
247+
}
248+
}
249+
return returnFlag;
250+
}
251+
132252
SILTransform *swift::createGenericSpecializer() {
133253
return new GenericSpecializer();
134254
}
255+
256+
SILTransform *swift::createProtocolDevirtualizer() {
257+
return new ProtocolDevirtualizer();
258+
}

0 commit comments

Comments
 (0)