Skip to content

Commit 569a520

Browse files
committed
SILOptimizer: add a mandatory generic-specialization pass.
This pass is only used for functions with performance annotations (@_noLocks, @_noAllocation). It runs in the mandatory pipeline and specializes all function calls in performance-annotated functions and functions which are called from such functions. In addition, the pass also does some other related optimizations: devirtualization, constant-folding Builtin.canBeClass, inlining of transparent functions and memory access optimizations.
1 parent dc8d482 commit 569a520

File tree

5 files changed

+197
-35
lines changed

5 files changed

+197
-35
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

+2
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ PASS(RedundantLoadElimination, "redundant-load-elim",
237237
"Redundant Load Elimination")
238238
PASS(DeadStoreElimination, "dead-store-elim",
239239
"Dead Store Elimination")
240+
PASS(MandatoryGenericSpecializer, "mandatory-generic-specializer",
241+
"Mandatory Generic Function Specialization on Static Types")
240242
PASS(GenericSpecializer, "generic-specializer",
241243
"Generic Function Specialization on Static Types")
242244
PASS(ExistentialSpecializer, "existential-specializer",

include/swift/SILOptimizer/Utils/Generics.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ void trySpecializeApplyOfGeneric(
4545
SILOptFunctionBuilder &FunctionBuilder,
4646
ApplySite Apply, DeadInstructionSet &DeadApplies,
4747
llvm::SmallVectorImpl<SILFunction *> &NewFunctions,
48-
OptRemark::Emitter &ORE);
48+
OptRemark::Emitter &ORE,
49+
bool isMandatory);
4950

5051
/// Helper class to describe re-abstraction of function parameters done during
5152
/// specialization.
@@ -336,11 +337,14 @@ class GenericFuncSpecializer {
336337
SubstitutionMap ContextSubs;
337338
std::string ClonedName;
338339

340+
bool isMandatory;
341+
339342
public:
340343
GenericFuncSpecializer(SILOptFunctionBuilder &FuncBuilder,
341344
SILFunction *GenericFunc,
342345
SubstitutionMap ParamSubs,
343-
const ReabstractionInfo &ReInfo);
346+
const ReabstractionInfo &ReInfo,
347+
bool isMandatory = false);
344348

345349
/// If we already have this specialization, reuse it.
346350
SILFunction *lookupSpecialization();

lib/SILOptimizer/PassManager/PassPipeline.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
165165
P.addPredictableDeadAllocationElimination();
166166

167167
P.addOptimizeHopToExecutor();
168+
P.addMandatoryGenericSpecializer();
168169

169170
P.addDiagnoseUnreachable();
170171
P.addDiagnoseInfiniteRecursion();

lib/SILOptimizer/Transforms/GenericSpecializer.cpp

+181-28
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,23 @@
2121
#include "swift/SIL/SILFunction.h"
2222
#include "swift/SIL/SILInstruction.h"
2323
#include "swift/SILOptimizer/PassManager/Transforms.h"
24+
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
25+
#include "swift/SILOptimizer/Utils/ConstantFolding.h"
26+
#include "swift/SILOptimizer/Utils/Devirtualize.h"
2427
#include "swift/SILOptimizer/Utils/Generics.h"
2528
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2629
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
30+
#include "swift/SILOptimizer/Utils/SILInliner.h"
2731
#include "llvm/ADT/SmallVector.h"
2832

2933
using namespace swift;
3034

3135
namespace {
3236

33-
class GenericSpecializer : public SILFunctionTransform {
34-
35-
bool specializeAppliesInFunction(SILFunction &F);
36-
37-
/// The entry point to the transformation.
38-
void run() override {
39-
SILFunction &F = *getFunction();
40-
41-
LLVM_DEBUG(llvm::dbgs() << "***** GenericSpecializer on function:"
42-
<< F.getName() << " *****\n");
43-
44-
if (specializeAppliesInFunction(F))
45-
invalidateAnalysis(SILAnalysis::InvalidationKind::Everything);
46-
}
47-
48-
};
49-
50-
} // end anonymous namespace
51-
52-
bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) {
53-
SILOptFunctionBuilder FunctionBuilder(*this);
37+
static bool specializeAppliesInFunction(SILFunction &F,
38+
SILTransform *transform,
39+
bool isMandatory) {
40+
SILOptFunctionBuilder FunctionBuilder(*transform);
5441
DeadInstructionSet DeadApplies;
5542
llvm::SmallSetVector<SILInstruction *, 8> Applies;
5643
OptRemark::Emitter ORE(DEBUG_TYPE, F);
@@ -97,14 +84,17 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) {
9784
SILFunction *Callee = Apply.getReferencedFunctionOrNull();
9885
assert(Callee && "Expected to have a known callee");
9986

100-
if (!Apply.canOptimize() || !Callee->shouldOptimize())
87+
if (!Apply.canOptimize())
88+
continue;
89+
90+
if (!isMandatory && !Callee->shouldOptimize())
10191
continue;
10292

10393
// We have a call that can potentially be specialized, so
10494
// attempt to do so.
10595
llvm::SmallVector<SILFunction *, 2> NewFunctions;
10696
trySpecializeApplyOfGeneric(FunctionBuilder, Apply, DeadApplies,
107-
NewFunctions, ORE);
97+
NewFunctions, ORE, isMandatory);
10898

10999
// Remove all the now-dead applies. We must do this immediately
110100
// rather than defer it in order to avoid problems with cloning
@@ -120,18 +110,181 @@ bool GenericSpecializer::specializeAppliesInFunction(SILFunction &F) {
120110
Changed = true;
121111
}
122112

123-
// If calling the specialization utility resulted in new functions
124-
// (as opposed to returning a previous specialization), we need to notify
125-
// the pass manager so that the new functions get optimized.
126-
for (SILFunction *NewF : reverse(NewFunctions)) {
127-
addFunctionToPassManagerWorklist(NewF, Callee);
113+
if (auto *sft = dyn_cast<SILFunctionTransform>(transform)) {
114+
// If calling the specialization utility resulted in new functions
115+
// (as opposed to returning a previous specialization), we need to notify
116+
// the pass manager so that the new functions get optimized.
117+
for (SILFunction *NewF : reverse(NewFunctions)) {
118+
sft->addFunctionToPassManagerWorklist(NewF, Callee);
119+
}
128120
}
129121
}
130122
}
131123

132124
return Changed;
133125
}
134126

127+
/// The generic specializer, used in the optimization pipeline.
128+
class GenericSpecializer : public SILFunctionTransform {
129+
130+
/// The entry point to the transformation.
131+
void run() override {
132+
SILFunction &F = *getFunction();
133+
134+
LLVM_DEBUG(llvm::dbgs() << "***** GenericSpecializer on function:"
135+
<< F.getName() << " *****\n");
136+
137+
if (specializeAppliesInFunction(F, this, /*isMandatory*/ false)) {
138+
invalidateAnalysis(SILAnalysis::InvalidationKind::Everything);
139+
}
140+
}
141+
};
142+
143+
/// The mandatory specializer, which runs in the mandatory pipeline.
144+
///
145+
/// It specializes functions, called from performance-annotated functions
146+
/// (@_noLocks, @_noAllocation).
147+
class MandatoryGenericSpecializer : public SILModuleTransform {
148+
149+
void run() override;
150+
151+
bool optimize(SILFunction *func, ClassHierarchyAnalysis *cha);
152+
153+
bool optimizeInst(SILInstruction *inst, SILOptFunctionBuilder &funcBuilder,
154+
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha);
155+
};
156+
157+
158+
void MandatoryGenericSpecializer::run() {
159+
SILModule *module = getModule();
160+
161+
if (!module->getOptions().EnablePerformanceAnnotations)
162+
return;
163+
164+
ClassHierarchyAnalysis *cha = getAnalysis<ClassHierarchyAnalysis>();
165+
166+
llvm::SmallVector<SILFunction *, 8> workList;
167+
llvm::SmallPtrSet<SILFunction *, 16> visited;
168+
169+
// Look for performance-annotated functions.
170+
for (SILFunction &function : *module) {
171+
if (function.getPerfConstraints() != PerformanceConstraints::None) {
172+
workList.push_back(&function);
173+
visited.insert(&function);
174+
}
175+
}
176+
177+
while (!workList.empty()) {
178+
SILFunction *func = workList.pop_back_val();
179+
module->linkFunction(func, SILModule::LinkingMode::LinkAll);
180+
if (!func->isDefinition())
181+
continue;
182+
183+
// Perform generic specialization and other related optimzations.
184+
bool changed = optimize(func, cha);
185+
186+
if (changed)
187+
invalidateAnalysis(func, SILAnalysis::InvalidationKind::Everything);
188+
189+
// Continue specializing called functions.
190+
for (SILBasicBlock &block : *func) {
191+
for (SILInstruction &inst : block) {
192+
if (auto as = ApplySite::isa(&inst)) {
193+
if (SILFunction *callee = as.getReferencedFunctionOrNull()) {
194+
if (visited.insert(callee).second)
195+
workList.push_back(callee);
196+
}
197+
}
198+
}
199+
}
200+
}
201+
}
202+
203+
/// Specialize generic calls in \p func and do some other related optimizations:
204+
/// devirtualization and constant-folding of the Builtin.canBeClass.
205+
bool MandatoryGenericSpecializer::optimize(SILFunction *func,
206+
ClassHierarchyAnalysis *cha) {
207+
bool changed = false;
208+
SILOptFunctionBuilder funcBuilder(*this);
209+
InstructionDeleter deleter;
210+
ReachingReturnBlocks rrBlocks(func);
211+
NonErrorHandlingBlocks neBlocks(func);
212+
213+
// If this is a just specialized function, try to optimize copy_addr, etc.
214+
// instructions.
215+
if (optimizeMemoryAccesses(*func)) {
216+
eliminateDeadAllocations(*func);
217+
changed = true;
218+
}
219+
220+
// Visiting blocks in reverse order avoids revisiting instructions after block
221+
// splitting, which would be quadratic.
222+
for (SILBasicBlock &block : llvm::reverse(*func)) {
223+
// Only consider blocks which are not on a "throw" path.
224+
if (!rrBlocks.reachesReturn(&block) || !neBlocks.isNonErrorHandling(&block))
225+
continue;
226+
227+
for (SILInstruction *inst : deleter.updatingReverseRange(&block)) {
228+
changed |= optimizeInst(inst, funcBuilder, deleter, cha);
229+
}
230+
}
231+
deleter.cleanupDeadInstructions();
232+
233+
if (specializeAppliesInFunction(*func, this, /*isMandatory*/ true))
234+
changed = true;
235+
236+
return changed;
237+
}
238+
239+
bool MandatoryGenericSpecializer::
240+
optimizeInst(SILInstruction *inst, SILOptFunctionBuilder &funcBuilder,
241+
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha) {
242+
if (auto as = ApplySite::isa(inst)) {
243+
// Specialization opens opportunities to devirtualize method calls.
244+
ApplySite newAS = tryDevirtualizeApply(as, cha).first;
245+
if (!newAS)
246+
return false;
247+
deleter.forceDelete(as.getInstruction());
248+
auto newFAS = FullApplySite::isa(newAS.getInstruction());
249+
if (!newFAS)
250+
return true;
251+
252+
SILFunction *callee = newFAS.getReferencedFunctionOrNull();
253+
if (!callee || callee->isTransparent() == IsNotTransparent)
254+
return true;
255+
256+
// If the de-virtualized callee is a transparent function, inline it.
257+
SILInliner::inlineFullApply(newFAS, SILInliner::InlineKind::MandatoryInline,
258+
funcBuilder, deleter);
259+
return true;
260+
}
261+
if (auto *bi = dyn_cast<BuiltinInst>(inst)) {
262+
// Constant-fold the Builtin.canBeClass. This is essential for Array code.
263+
if (bi->getBuiltinInfo().ID != BuiltinValueKind::CanBeObjCClass)
264+
return false;
265+
266+
SILBuilderWithScope builder(bi);
267+
IntegerLiteralInst *lit = optimizeBuiltinCanBeObjCClass(bi, builder);
268+
if (!lit)
269+
return false;
270+
271+
bi->replaceAllUsesWith(lit);
272+
ConstantFolder constFolder(funcBuilder, getOptions().AssertConfig,
273+
/*EnableDiagnostics*/ false);
274+
constFolder.addToWorklist(lit);
275+
constFolder.processWorkList();
276+
deleter.forceDelete(bi);
277+
return true;
278+
}
279+
return false;
280+
}
281+
282+
} // end anonymous namespace
283+
135284
SILTransform *swift::createGenericSpecializer() {
136285
return new GenericSpecializer();
137286
}
287+
288+
SILTransform *swift::createMandatoryGenericSpecializer() {
289+
return new MandatoryGenericSpecializer();
290+
}

lib/SILOptimizer/Utils/Generics.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -1829,11 +1829,12 @@ ReabstractionInfo::ReabstractionInfo(ModuleDecl *targetModule,
18291829
GenericFuncSpecializer::GenericFuncSpecializer(
18301830
SILOptFunctionBuilder &FuncBuilder, SILFunction *GenericFunc,
18311831
SubstitutionMap ParamSubs,
1832-
const ReabstractionInfo &ReInfo)
1832+
const ReabstractionInfo &ReInfo,
1833+
bool isMandatory)
18331834
: FuncBuilder(FuncBuilder), M(GenericFunc->getModule()),
18341835
GenericFunc(GenericFunc),
18351836
ParamSubs(ParamSubs),
1836-
ReInfo(ReInfo) {
1837+
ReInfo(ReInfo), isMandatory(isMandatory) {
18371838

18381839
assert((GenericFunc->isDefinition() || ReInfo.isPrespecialized()) &&
18391840
"Expected definition or pre-specialized entry-point to specialize!");
@@ -1888,7 +1889,7 @@ void ReabstractionInfo::verify() const {
18881889
SILFunction *
18891890
GenericFuncSpecializer::tryCreateSpecialization(bool forcePrespecialization) {
18901891
// Do not create any new specializations at Onone.
1891-
if (!GenericFunc->shouldOptimize() && !forcePrespecialization)
1892+
if (!GenericFunc->shouldOptimize() && !forcePrespecialization && !isMandatory)
18921893
return nullptr;
18931894

18941895
LLVM_DEBUG(llvm::dbgs() << "Creating a specialization: "
@@ -2564,7 +2565,8 @@ void swift::trySpecializeApplyOfGeneric(
25642565
SILOptFunctionBuilder &FuncBuilder,
25652566
ApplySite Apply, DeadInstructionSet &DeadApplies,
25662567
SmallVectorImpl<SILFunction *> &NewFunctions,
2567-
OptRemark::Emitter &ORE) {
2568+
OptRemark::Emitter &ORE,
2569+
bool isMandatory) {
25682570
assert(Apply.hasSubstitutions() && "Expected an apply with substitutions!");
25692571
auto *F = Apply.getFunction();
25702572
auto *RefF =
@@ -2683,7 +2685,7 @@ void swift::trySpecializeApplyOfGeneric(
26832685

26842686
GenericFuncSpecializer FuncSpecializer(FuncBuilder,
26852687
RefF, Apply.getSubstitutionMap(),
2686-
ReInfo);
2688+
ReInfo, isMandatory);
26872689
SILFunction *SpecializedF = prespecializedF
26882690
? prespecializedF
26892691
: FuncSpecializer.lookupSpecialization();

0 commit comments

Comments
 (0)