Skip to content

Commit d80d7dd

Browse files
committed
MandatoryGenericSpecializer: add the partial_apply -> apply peephole optimization
In performance-annotated functions optimize the pattern where a partial_apply is immediately applied. This remove the partial apply and thus avoids an allocation. Fixes an unnecessary performance violation error. rdar://95155145
1 parent d866158 commit d80d7dd

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

lib/SILOptimizer/Transforms/GenericSpecializer.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
2929
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
3030
#include "swift/SILOptimizer/Utils/SILInliner.h"
31+
#include "swift/SILOptimizer/Utils/StackNesting.h"
3132
#include "llvm/ADT/SmallVector.h"
3233

3334
using namespace swift;
@@ -195,10 +196,12 @@ class MandatoryGenericSpecializer : public SILModuleTransform {
195196

196197
void run() override;
197198

198-
bool optimize(SILFunction *func, ClassHierarchyAnalysis *cha);
199+
bool optimize(SILFunction *func, ClassHierarchyAnalysis *cha,
200+
bool &invalidatedStackNesting);
199201

200202
bool optimizeInst(SILInstruction *inst, SILOptFunctionBuilder &funcBuilder,
201-
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha);
203+
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha,
204+
bool &invalidatedStackNesting);
202205
};
203206

204207

@@ -220,7 +223,7 @@ void MandatoryGenericSpecializer::run() {
220223
visited.insert(&function);
221224
}
222225
}
223-
226+
224227
while (!workList.empty()) {
225228
SILFunction *func = workList.pop_back_val();
226229
module->linkFunction(func, SILModule::LinkingMode::LinkAll);
@@ -229,20 +232,26 @@ void MandatoryGenericSpecializer::run() {
229232

230233
// Perform generic specialization and other related optimization.
231234

235+
bool invalidatedStackNesting = false;
236+
232237
// To avoid phase ordering problems of the involved optimizations, iterate
233238
// until we reach a fixed point.
234239
// This should always happen, but to be on the safe side, limit the number
235240
// of iterations to 10 (which is more than enough - usually the loop runs
236241
// 1 to 3 times).
237242
for (int i = 0; i < 10; i++) {
238-
bool changed = optimize(func, cha);
243+
bool changed = optimize(func, cha, invalidatedStackNesting);
239244
if (changed) {
240245
invalidateAnalysis(func, SILAnalysis::InvalidationKind::FunctionBody);
241246
} else {
242247
break;
243248
}
244249
}
245250

251+
if (invalidatedStackNesting) {
252+
StackNesting::fixNesting(func);
253+
}
254+
246255
// Continue specializing called functions.
247256
for (SILBasicBlock &block : *func) {
248257
for (SILInstruction &inst : block) {
@@ -260,7 +269,8 @@ void MandatoryGenericSpecializer::run() {
260269
/// Specialize generic calls in \p func and do some other related optimizations:
261270
/// devirtualization and constant-folding of the Builtin.canBeClass.
262271
bool MandatoryGenericSpecializer::optimize(SILFunction *func,
263-
ClassHierarchyAnalysis *cha) {
272+
ClassHierarchyAnalysis *cha,
273+
bool &invalidatedStackNesting) {
264274
bool changed = false;
265275
SILOptFunctionBuilder funcBuilder(*this);
266276
InstructionDeleter deleter;
@@ -282,7 +292,7 @@ bool MandatoryGenericSpecializer::optimize(SILFunction *func,
282292
continue;
283293

284294
for (SILInstruction *inst : deleter.updatingReverseRange(&block)) {
285-
changed |= optimizeInst(inst, funcBuilder, deleter, cha);
295+
changed |= optimizeInst(inst, funcBuilder, deleter, cha, invalidatedStackNesting);
286296
}
287297
}
288298
deleter.cleanupDeadInstructions();
@@ -295,7 +305,8 @@ bool MandatoryGenericSpecializer::optimize(SILFunction *func,
295305

296306
bool MandatoryGenericSpecializer::
297307
optimizeInst(SILInstruction *inst, SILOptFunctionBuilder &funcBuilder,
298-
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha) {
308+
InstructionDeleter &deleter, ClassHierarchyAnalysis *cha,
309+
bool &invalidatedStackNesting) {
299310
if (auto as = ApplySite::isa(inst)) {
300311

301312
bool changed = false;
@@ -307,10 +318,22 @@ optimizeInst(SILInstruction *inst, SILOptFunctionBuilder &funcBuilder,
307318
as = newAS;
308319
}
309320

310-
auto fas = FullApplySite::isa(as.getInstruction());
311-
if (!fas)
321+
if (auto *pai = dyn_cast<PartialApplyInst>(as)) {
322+
SILBuilderContext builderCtxt(funcBuilder.getModule());
323+
if (tryOptimizeApplyOfPartialApply(pai, builderCtxt, deleter.getCallbacks())) {
324+
// Try to delete the partial_apply.
325+
// We don't need to copy all arguments again (to extend their lifetimes),
326+
// because it was already done in tryOptimizeApplyOfPartialApply.
327+
tryDeleteDeadClosure(pai, deleter.getCallbacks(), /*needKeepArgsAlive=*/ false);
328+
invalidatedStackNesting = true;
329+
return true;
330+
}
312331
return changed;
313-
332+
}
333+
334+
auto fas = FullApplySite::isa(as.getInstruction());
335+
assert(fas);
336+
314337
SILFunction *callee = fas.getReferencedFunctionOrNull();
315338
if (!callee)
316339
return changed;

test/SILOptimizer/performance-annotations.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,13 @@ func goodClosure2() {
213213
})
214214
}
215215

216+
@_noAllocation
217+
func closueWhichModifiesLocalVar() -> Int {
218+
var x = 42
219+
let localNonEscapingClosure = {
220+
x += 1
221+
}
222+
localNonEscapingClosure()
223+
return x
224+
}
225+

0 commit comments

Comments
 (0)