Skip to content

Commit acf923b

Browse files
committed
MandatoryInlining: fix a memory lifetime bug related to partial_apply with in_guaranteed parameters.
Because partial_apply consumes it's arguments we need to copy them. This was done for "direct" parameters but not for address parameters. rdar://problem/64035105
1 parent 552134a commit acf923b

File tree

2 files changed

+75
-13
lines changed

2 files changed

+75
-13
lines changed

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ static SILValue stripCopiesAndBorrows(SILValue v) {
5959
/// It is important to note that, we can not assume that the partial apply, the
6060
/// apply site, or the callee value are control dependent in any way. This
6161
/// requires us to need to be very careful. See inline comments.
62-
static void fixupReferenceCounts(
62+
///
63+
/// Returns true if the stack nesting is invalidated and must be corrected
64+
/// afterwards.
65+
static bool fixupReferenceCounts(
6366
PartialApplyInst *pai, FullApplySite applySite, SILValue calleeValue,
6467
ArrayRef<ParameterConvention> captureArgConventions,
6568
MutableArrayRef<SILValue> capturedArgs, bool isCalleeGuaranteed) {
@@ -72,6 +75,7 @@ static void fixupReferenceCounts(
7275
// FIXME: Can we cache this in between inlining invocations?
7376
DeadEndBlocks deadEndBlocks(pai->getFunction());
7477
SmallVector<SILBasicBlock *, 4> leakingBlocks;
78+
bool invalidatedStackNesting = false;
7579

7680
// Add a copy of each non-address type capture argument to lifetime extend the
7781
// captured argument over at least the inlined function and till the end of a
@@ -83,14 +87,6 @@ static void fixupReferenceCounts(
8387
for (unsigned i : indices(captureArgConventions)) {
8488
auto convention = captureArgConventions[i];
8589
SILValue &v = capturedArgs[i];
86-
if (v->getType().isAddress()) {
87-
// FIXME: What about indirectly owned parameters? The invocation of the
88-
// closure would perform an indirect copy which we should mimick here.
89-
assert(convention != ParameterConvention::Indirect_In &&
90-
"Missing indirect copy");
91-
continue;
92-
}
93-
9490
auto *f = applySite.getFunction();
9591

9692
// See if we have a trivial value. In such a case, just continue. We do not
@@ -102,11 +98,40 @@ static void fixupReferenceCounts(
10298

10399
switch (convention) {
104100
case ParameterConvention::Indirect_In:
101+
llvm_unreachable("Missing indirect copy");
102+
105103
case ParameterConvention::Indirect_In_Constant:
106104
case ParameterConvention::Indirect_Inout:
107105
case ParameterConvention::Indirect_InoutAliasable:
108-
case ParameterConvention::Indirect_In_Guaranteed:
109-
llvm_unreachable("Should be handled above");
106+
break;
107+
108+
case ParameterConvention::Indirect_In_Guaranteed: {
109+
// Do the same as for Direct_Guaranteed, just the address version.
110+
// (See comment below).
111+
SILBuilderWithScope builder(pai);
112+
auto *stackLoc = builder.createAllocStack(loc, v->getType().getObjectType());
113+
builder.createCopyAddr(loc, v, stackLoc, IsNotTake, IsInitialization);
114+
visitedBlocks.clear();
115+
116+
LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks);
117+
bool consumedInLoop = checker.completeConsumingUseSet(
118+
pai, applySite.getCalleeOperand(),
119+
[&](SILBasicBlock::iterator insertPt) {
120+
SILBuilderWithScope builder(insertPt);
121+
builder.createDestroyAddr(loc, stackLoc);
122+
builder.createDeallocStack(loc, stackLoc);
123+
});
124+
125+
if (!consumedInLoop) {
126+
applySite.insertAfterInvocation([&](SILBasicBlock::iterator iter) {
127+
SILBuilderWithScope(iter).createDestroyAddr(loc, stackLoc);
128+
SILBuilderWithScope(iter).createDeallocStack(loc, stackLoc);
129+
});
130+
}
131+
v = stackLoc;
132+
invalidatedStackNesting = true;
133+
break;
134+
}
110135

111136
case ParameterConvention::Direct_Guaranteed: {
112137
// If we have a direct_guaranteed value, the value is being taken by the
@@ -242,6 +267,7 @@ static void fixupReferenceCounts(
242267
SILBuilderWithScope(iter).emitDestroyValueOperation(loc, calleeValue);
243268
});
244269
}
270+
return invalidatedStackNesting;
245271
}
246272

247273
static SILValue cleanupLoadedCalleeValue(SILValue calleeValue, LoadInst *li) {
@@ -917,8 +943,9 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder,
917943
// We need to insert the copies before the partial_apply since if we can
918944
// not remove the partial_apply the captured values will be dead by the
919945
// time we hit the call site.
920-
fixupReferenceCounts(PAI, InnerAI, CalleeValue, CapturedArgConventions,
921-
CapturedArgs, IsCalleeGuaranteed);
946+
invalidatedStackNesting |= fixupReferenceCounts(PAI, InnerAI,
947+
CalleeValue, CapturedArgConventions,
948+
CapturedArgs, IsCalleeGuaranteed);
922949
}
923950

924951
// Register a callback to record potentially unused function values after

test/SILOptimizer/mandatory_inlining.sil

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,3 +1428,38 @@ bb0(%0 : $@callee_guaranteed () -> ()):
14281428
%9999 = tuple()
14291429
return %9999 : $()
14301430
}
1431+
1432+
struct Mystruct {
1433+
var s: String
1434+
}
1435+
1436+
sil @use_string : $@convention(thin) (@guaranteed String) -> ()
1437+
1438+
sil private [transparent] [ossa] @callee_with_guaranteed : $@convention(thin) (Bool, @in_guaranteed Mystruct) -> () {
1439+
bb0(%0 : $Bool, %1 : $*Mystruct):
1440+
%4 = struct_element_addr %1 : $*Mystruct, #Mystruct.s
1441+
%5 = load [copy] %4 : $*String
1442+
%6 = function_ref @use_string : $@convention(thin) (@guaranteed String) -> ()
1443+
%7 = apply %6(%5) : $@convention(thin) (@guaranteed String) -> ()
1444+
destroy_value %5 : $String
1445+
%19 = tuple ()
1446+
return %19 : $()
1447+
}
1448+
1449+
// Make sure that this doesn't cause a memory lifetime failure.
1450+
//
1451+
// CHECK-LABEL: sil [ossa] @partial_apply_with_indirect_param
1452+
// CHECK: } // end sil function 'partial_apply_with_indirect_param'
1453+
sil [ossa] @partial_apply_with_indirect_param : $@convention(thin) (@in_guaranteed Mystruct, Bool) -> () {
1454+
bb0(%0 : $*Mystruct, %1 : $Bool):
1455+
%216 = function_ref @callee_with_guaranteed : $@convention(thin) (Bool, @in_guaranteed Mystruct) -> ()
1456+
%217 = alloc_stack $Mystruct
1457+
copy_addr %0 to [initialization] %217 : $*Mystruct
1458+
%219 = partial_apply [callee_guaranteed] %216(%217) : $@convention(thin) (Bool, @in_guaranteed Mystruct) -> ()
1459+
%220 = apply %219(%1) : $@callee_guaranteed (Bool) -> ()
1460+
destroy_value %219 : $@callee_guaranteed (Bool) -> ()
1461+
dealloc_stack %217 : $*Mystruct
1462+
%19 = tuple ()
1463+
return %19 : $()
1464+
}
1465+

0 commit comments

Comments
 (0)