Skip to content

Commit 75dcfa4

Browse files
committed
Fix capture promotion for partial_apply [stack]
1 parent be31b70 commit 75dcfa4

File tree

2 files changed

+129
-3
lines changed

2 files changed

+129
-3
lines changed

lib/SILOptimizer/IPO/CapturePromotion.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,17 @@ static bool scanUsesForEscapesAndMutations(Operand *Op,
10341034
return isPartialApplyNonEscapingUser(Op, PAI, State);
10351035
}
10361036

1037+
// A mark_dependence user on a partial_apply is safe.
1038+
if (auto *MD = dyn_cast<MarkDependenceInst>(User)) {
1039+
if (MD->getBase() == Op->get()) {
1040+
auto parent = MD->getValue();
1041+
while ((MD = dyn_cast<MarkDependenceInst>(parent))) {
1042+
parent = MD->getValue();
1043+
}
1044+
return isa<PartialApplyInst>(parent);
1045+
}
1046+
}
1047+
10371048
if (auto *PBI = dyn_cast<ProjectBoxInst>(User)) {
10381049
// It is assumed in later code that we will only have 1 project_box. This
10391050
// can be seen since there is no code for reasoning about multiple
@@ -1198,6 +1209,26 @@ static SILValue getOrCreateProjectBoxHelper(SILValue PartialOperand) {
11981209
return B.createProjectBox(Box->getLoc(), Box, 0);
11991210
}
12001211

1212+
/// Change the base in mark_dependence.
1213+
static void
1214+
mapMarkDependenceArguments(SingleValueInstruction *root,
1215+
llvm::DenseMap<SILValue, SILValue> &map) {
1216+
for (auto *Use : root->getUses()) {
1217+
if (auto *MD = dyn_cast<MarkDependenceInst>(Use->getUser())) {
1218+
mapMarkDependenceArguments(MD, map);
1219+
auto iter = map.find(MD->getBase());
1220+
if (iter != map.end()) {
1221+
MD->setBase(iter->second);
1222+
}
1223+
// Remove mark_dependence on trivial values.
1224+
if (MD->getBase()->getType().isTrivial(MD->getModule())) {
1225+
MD->replaceAllUsesWith(MD->getValue());
1226+
MD->eraseFromParent();
1227+
}
1228+
}
1229+
}
1230+
}
1231+
12011232
/// Given a partial_apply instruction and a set of promotable indices,
12021233
/// clone the closure with the promoted captures and replace the partial_apply
12031234
/// with a partial_apply of the new closure, fixing up reference counting as
@@ -1236,6 +1267,8 @@ processPartialApplyInst(SILOptFunctionBuilder &FuncBuilder,
12361267
unsigned OpCount = PAI->getNumOperands() - PAI->getNumTypeDependentOperands();
12371268
SmallVector<SILValue, 16> Args;
12381269
auto NumIndirectResults = calleeConv.getNumIndirectSILResults();
1270+
llvm::DenseMap<SILValue, SILValue> capturedMap;
1271+
llvm::SmallSet<SILValue, 16> newCaptures;
12391272
for (; OpNo != OpCount; ++OpNo) {
12401273
unsigned Index = OpNo - 1 + FirstIndex;
12411274
if (!PromotableIndices.count(Index)) {
@@ -1250,8 +1283,18 @@ processPartialApplyInst(SILOptFunctionBuilder &FuncBuilder,
12501283
SILValue Addr = getOrCreateProjectBoxHelper(Box);
12511284

12521285
auto &typeLowering = M.getTypeLowering(Addr->getType());
1253-
Args.push_back(
1254-
typeLowering.emitLoadOfCopy(B, PAI->getLoc(), Addr, IsNotTake));
1286+
auto newCaptured =
1287+
typeLowering.emitLoadOfCopy(B, PAI->getLoc(), Addr, IsNotTake);
1288+
Args.push_back(newCaptured);
1289+
1290+
capturedMap[Box] = newCaptured;
1291+
newCaptures.insert(newCaptured);
1292+
1293+
// A partial_apply [stack] does not own the captured argument but we must
1294+
// destroy the projected object. We will do so after having created the new
1295+
// partial_apply below.
1296+
if (PAI->isOnStack())
1297+
continue;
12551298

12561299
// Cleanup the captured argument.
12571300
//
@@ -1268,14 +1311,30 @@ processPartialApplyInst(SILOptFunctionBuilder &FuncBuilder,
12681311
// Create a new partial apply with the new arguments.
12691312
auto *NewPAI = B.createPartialApply(
12701313
PAI->getLoc(), FnVal, PAI->getSubstitutionMap(), Args,
1271-
PAI->getType().getAs<SILFunctionType>()->getCalleeConvention());
1314+
PAI->getType().getAs<SILFunctionType>()->getCalleeConvention(),
1315+
PAI->isOnStack());
12721316
PAI->replaceAllUsesWith(NewPAI);
12731317
PAI->eraseFromParent();
12741318
if (FRI->use_empty()) {
12751319
FRI->eraseFromParent();
12761320
// TODO: If this is the last use of the closure, and if it has internal
12771321
// linkage, we should remove it from the SILModule now.
12781322
}
1323+
1324+
if (NewPAI->isOnStack()) {
1325+
// Insert destroy's of new captured arguments.
1326+
for (auto *Use : NewPAI->getUses()) {
1327+
if (auto *DS = dyn_cast<DeallocStackInst>(Use->getUser())) {
1328+
B.setInsertionPoint(std::next(SILBasicBlock::iterator(DS)));
1329+
insertDestroyOfCapturedArguments(NewPAI, B, [&](SILValue arg) -> bool {
1330+
return newCaptures.count(arg);
1331+
});
1332+
}
1333+
}
1334+
// Map the mark dependence arguments.
1335+
mapMarkDependenceArguments(NewPAI, capturedMap);
1336+
}
1337+
12791338
return ClonedFn;
12801339
}
12811340

test/SILOptimizer/capture_promotion.sil

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,70 @@ bb0:
407407

408408
return %21 : $@callee_guaranteed () -> Int
409409
}
410+
411+
// CHECK-LABEL: sil @test_capture_promotion_on_stack
412+
sil @test_capture_promotion_on_stack : $@convention(thin) () -> () {
413+
bb0:
414+
%0 = tuple ()
415+
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Foo>
416+
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
417+
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
418+
%3 = metatype $@thick Foo.Type
419+
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
420+
store %4 to %1a : $*Foo
421+
%6 = alloc_box $<τ_0_0> { var τ_0_0 } <Baz>
422+
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
423+
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
424+
%8 = metatype $@thin Baz.Type
425+
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
426+
store %9 to %6a : $*Baz
427+
%11 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
428+
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
429+
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
430+
%13 = metatype $@thin Int.Type
431+
%14 = integer_literal $Builtin.Word, 3
432+
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
433+
store %15 to %11a : $*Int
434+
435+
// CHECK-NOT: function_ref @closure0_guaranteed
436+
// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @$s19closure0_guaranteedTf2iii_n
437+
// CHECK-NOT: function_ref @closure0_guaranteed
438+
439+
// The Foo variable is loaded from and retained, because it is a reference type
440+
// CHECK-NEXT: [[LOADFOO:%.*]] = load {{.*}} : $*Foo
441+
// CHECK-NEXT: strong_retain [[LOADFOO]] : $Foo
442+
443+
// The Baz variable is loaded and retain_value'd, because it is a non-trivial
444+
// aggregate type
445+
// CHECK-NEXT: [[LOADBAZ:%.*]] = load {{.*}} : $*Baz
446+
// CHECK-NEXT: retain_value [[LOADBAZ:%.*]] : $Baz
447+
448+
// The Int variable is loaded only, because it is trivial
449+
// CHECK-NEXT: [[LOADINT:%.*]] = load {{.*}} : $*Int
450+
451+
// The partial apply has one value argument for each pair of arguments that was
452+
// previously used to capture and pass the variable by reference
453+
// CHECK-NEXT: [[PA:%.*]] = partial_apply [callee_guaranteed] [on_stack] [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]])
454+
// CHECK-NEXT: [[MD1:%.*]] = mark_dependence [[PA]] : $@noescape @callee_guaranteed () -> Int on [[LOADFOO]] : $Foo
455+
// CHECK-NEXT: [[MD2:%.*]] = mark_dependence [[MD1]] : $@noescape @callee_guaranteed () -> Int on [[LOADBAZ]] : $Baz
456+
// CHECK-NEXT: apply [[MD2]]() : $@noescape @callee_guaranteed () -> Int
457+
// CHECK-NEXT: dealloc_stack [[PA]] : $@noescape @callee_guaranteed () -> Int
458+
// CHECK-NEXT: strong_release [[LOADFOO]] : $Foo
459+
// CHECK-NEXT: release_value [[LOADBAZ]] : $Baz
460+
// CHECK-NEXT: strong_release {{.*}} : $<τ_0_0> { var τ_0_0 } <Int>
461+
// CHECK-NEXT: strong_release {{.*}} : $<τ_0_0> { var τ_0_0 } <Baz>
462+
// CHECK-NEXT: strong_release {{.*}} : $<τ_0_0> { var τ_0_0 } <Foo>
463+
464+
%17 = function_ref @closure0_guaranteed : $@convention(thin) (@guaranteed <τ_0_0> { var τ_0_0 } <Foo>, @guaranteed <τ_0_0> { var τ_0_0 } <Baz>, @guaranteed <τ_0_0> { var τ_0_0 } <Int>) -> Int
465+
%21 = partial_apply [callee_guaranteed] [on_stack] %17(%1, %6, %11) : $@convention(thin) (@guaranteed <τ_0_0> { var τ_0_0 } <Foo>, @guaranteed <τ_0_0> { var τ_0_0 } <Baz>, @guaranteed <τ_0_0> { var τ_0_0 } <Int>) -> Int
466+
%md = mark_dependence %21 :$@noescape @callee_guaranteed () -> Int on %1 : $<τ_0_0> { var τ_0_0 } <Foo>
467+
%md2 = mark_dependence %md :$@noescape @callee_guaranteed () -> Int on %6 : $<τ_0_0> { var τ_0_0 } <Baz>
468+
%md3 = mark_dependence %md2 :$@noescape @callee_guaranteed () -> Int on %11 : $<τ_0_0> { var τ_0_0 } <Int>
469+
apply %md3() : $@noescape @callee_guaranteed () -> Int
470+
dealloc_stack %21 : $@noescape @callee_guaranteed () -> Int
471+
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
472+
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
473+
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
474+
%tuple = tuple()
475+
return %tuple : $()
476+
}

0 commit comments

Comments
 (0)