Skip to content

Commit 1db75d6

Browse files
authored
Merge pull request #24920 from gottesmm/pr-1ce3bba234b68150bc0675d364f95465bb26344d
[mandatory-inlining] When using the linear lifetime checker to insert…
2 parents ad79bba + 3b02901 commit 1db75d6

File tree

4 files changed

+195
-9
lines changed

4 files changed

+195
-9
lines changed

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ static void fixupReferenceCounts(
8888
DeadEndBlocks deadEndBlocks(pai->getFunction());
8989
SmallVector<SILBasicBlock *, 4> leakingBlocks;
9090

91-
auto errorBehavior =
92-
ownership::ErrorBehaviorKind::ReturnFalseOnLeakAssertOtherwise;
91+
auto errorBehavior = ownership::ErrorBehaviorKind::ReturnFalse;
9392

9493
// Add a copy of each non-address type capture argument to lifetime extend the
9594
// captured argument over at least the inlined function and till the end of a
@@ -142,21 +141,33 @@ static void fixupReferenceCounts(
142141
auto error =
143142
valueHasLinearLifetime(copy, {applySite}, {}, visitedBlocks,
144143
deadEndBlocks, errorBehavior, &leakingBlocks);
145-
if (error.getFoundError()) {
144+
if (error.getFoundLeak()) {
146145
while (!leakingBlocks.empty()) {
147146
auto *leakingBlock = leakingBlocks.pop_back_val();
148147
auto loc = RegularLocation::getAutoGeneratedLocation();
149148
SILBuilderWithScope builder(leakingBlock->begin());
149+
if (hasOwnership) {
150+
builder.createEndBorrow(loc, argument);
151+
}
150152
builder.emitDestroyValueOperation(loc, copy);
151153
}
152154
}
153155

154-
insertAfterApply(applySite, [&](SILBasicBlock::iterator iter) {
155-
if (hasOwnership) {
156-
SILBuilderWithScope(iter).createEndBorrow(loc, argument);
157-
}
158-
SILBuilderWithScope(iter).emitDestroyValueOperation(loc, copy);
159-
});
156+
// If we found an over consume it means that our value is consumed within
157+
// the loop. That means our leak code will have lifetime extended the
158+
// value over the loop. So we should /not/ insert a destroy after the
159+
// apply site. In contrast, if we do not have an over consume, we must
160+
// have been compensating for uses in the top of a diamond and need to
161+
// insert a destroy after the apply since the leak will just cover the
162+
// other path.
163+
if (!error.getFoundOverConsume()) {
164+
insertAfterApply(applySite, [&](SILBasicBlock::iterator iter) {
165+
if (hasOwnership) {
166+
SILBuilderWithScope(iter).createEndBorrow(loc, argument);
167+
}
168+
SILBuilderWithScope(iter).emitDestroyValueOperation(loc, copy);
169+
});
170+
}
160171
v = argument;
161172
break;
162173
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-run-simple-swift
2+
3+
func test(reportError: ((String) -> (Void))? = nil) {
4+
let reportError = reportError ?? { error in
5+
print(error)
6+
}
7+
8+
for _ in 0..<1 {
9+
reportError("foo bar baz")
10+
}
11+
}
12+
13+
func main() {
14+
test { error in
15+
print(error)
16+
}
17+
}
18+
19+
main()

test/SILOptimizer/mandatory_inlining.sil

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,3 +1200,143 @@ bb0(%0: $C):
12001200
%tuple = tuple()
12011201
return %tuple : $()
12021202
}
1203+
1204+
// CHECK-LABEL: sil @test_apply_pai_in_loop : $@convention(thin) (@guaranteed C) -> () {
1205+
// CHECK: bb0([[ARG:%.*]] : $C):
1206+
// CHECK-NEXT: strong_retain [[ARG]]
1207+
// CHECK-NEXT: strong_retain [[ARG]]
1208+
// CHECK-NEXT: br bb1
1209+
//
1210+
// CHECK: bb1:
1211+
// CHECK-NEXT: cond_br undef, bb2, bb3
1212+
//
1213+
// CHECK: bb2:
1214+
// CHECK-NEXT: function_ref use_c
1215+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @use_c
1216+
// CHECK-NEXT: apply [[FUNC]]([[ARG]])
1217+
// CHECK-NEXT: tuple
1218+
// CHECK-NEXT: br bb1
1219+
//
1220+
// CHECK: bb3:
1221+
// CHECK-NEXT: strong_release [[ARG]]
1222+
// CHECK-NEXT: strong_release [[ARG]]
1223+
// CHECK-NEXT: tuple
1224+
// CHECK-NEXT: return
1225+
// CHECK-NEXT: } // end sil function 'test_apply_pai_in_loop'
1226+
sil @test_apply_pai_in_loop : $@convention(thin) (@guaranteed C) -> () {
1227+
bb0(%0: $C):
1228+
strong_retain %0 : $C
1229+
%closure_fun = function_ref @guaranteed_closure_func : $@convention(thin) (@guaranteed C) -> ()
1230+
%closure = partial_apply [callee_guaranteed] %closure_fun(%0) : $@convention(thin) (@guaranteed C) -> ()
1231+
br bb1
1232+
1233+
bb1:
1234+
cond_br undef, bb2, bb3
1235+
1236+
bb2:
1237+
apply %closure() : $@callee_guaranteed () -> ()
1238+
br bb1
1239+
1240+
bb3:
1241+
strong_release %0: $C
1242+
%tuple = tuple()
1243+
return %tuple : $()
1244+
}
1245+
1246+
// CHECK-LABEL: sil @test_apply_pai_in_loop_with_diamond : $@convention(thin) (@guaranteed C) -> () {
1247+
// CHECK: bb0([[ARG:%.*]] : $C):
1248+
// CHECK-NEXT: strong_retain [[ARG]]
1249+
// CHECK-NEXT: strong_retain [[ARG]]
1250+
// CHECK-NEXT: strong_retain [[ARG]]
1251+
// CHECK-NEXT: cond_br undef, bb1, bb4
1252+
//
1253+
// CHECK: bb1:
1254+
// CHECK-NEXT: function_ref use_c
1255+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @use_c
1256+
// CHECK-NEXT: apply [[FUNC]]([[ARG]])
1257+
// CHECK-NEXT: tuple
1258+
// CHECK-NEXT: cond_br undef, bb2, bb3
1259+
//
1260+
// CHECK: bb2:
1261+
// CHECK-NEXT: function_ref use_c
1262+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @use_c
1263+
// CHECK-NEXT: apply [[FUNC]]([[ARG]])
1264+
// CHECK-NEXT: tuple
1265+
// CHECK-NEXT: br bb1
1266+
//
1267+
// CHECK: bb3:
1268+
// CHECK-NEXT: br bb1
1269+
//
1270+
// CHECK: bb4:
1271+
// CHECK-NEXT: strong_release [[ARG]]
1272+
// CHECK-NEXT: strong_release [[ARG]]
1273+
// CHECK-NEXT: strong_release [[ARG]]
1274+
// CHECK-NEXT: tuple
1275+
// CHECK-NEXT: return
1276+
// CHECK-NEXT: } // end sil function 'test_apply_pai_in_loop_with_diamond'
1277+
sil @test_apply_pai_in_loop_with_diamond : $@convention(thin) (@guaranteed C) -> () {
1278+
bb0(%0: $C):
1279+
strong_retain %0 : $C
1280+
%closure_fun = function_ref @guaranteed_closure_func : $@convention(thin) (@guaranteed C) -> ()
1281+
%closure = partial_apply [callee_guaranteed] %closure_fun(%0) : $@convention(thin) (@guaranteed C) -> ()
1282+
cond_br undef, bb2, bb5
1283+
1284+
bb2:
1285+
apply %closure() : $@callee_guaranteed () -> ()
1286+
cond_br undef, bb3, bb4
1287+
1288+
bb3:
1289+
apply %closure() : $@callee_guaranteed () -> ()
1290+
br bb2
1291+
1292+
bb4:
1293+
br bb2
1294+
1295+
bb5:
1296+
strong_release %0: $C
1297+
%tuple = tuple()
1298+
return %tuple : $()
1299+
}
1300+
1301+
// CHECK-LABEL: sil @test_apply_pai_in_diamond : $@convention(thin) (@guaranteed C) -> () {
1302+
// CHECK: bb0([[ARG:%.*]] : $C):
1303+
// CHECK-NEXT: strong_retain [[ARG]]
1304+
// CHECK-NEXT: strong_retain [[ARG]]
1305+
// CHECK-NEXT: cond_br undef, bb1, bb2
1306+
//
1307+
// CHECK: bb1:
1308+
// CHECK-NEXT: strong_release [[ARG]]
1309+
// CHECK-NEXT: br bb3
1310+
//
1311+
// CHECK: bb2:
1312+
// CHECK-NEXT: function_ref use_c
1313+
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @use_c
1314+
// CHECK-NEXT: apply [[FUNC]]([[ARG]])
1315+
// CHECK-NEXT: tuple
1316+
// CHECK-NEXT: strong_release [[ARG]]
1317+
// CHECK-NEXT: br bb3
1318+
//
1319+
// CHECK: bb3:
1320+
// CHECK-NEXT: strong_release [[ARG]]
1321+
// CHECK-NEXT: tuple
1322+
// CHECK-NEXT: return
1323+
// CHECK-NEXT: } // end sil function 'test_apply_pai_in_diamond'
1324+
sil @test_apply_pai_in_diamond : $@convention(thin) (@guaranteed C) -> () {
1325+
bb0(%0: $C):
1326+
strong_retain %0 : $C
1327+
%closure_fun = function_ref @guaranteed_closure_func : $@convention(thin) (@guaranteed C) -> ()
1328+
%closure = partial_apply [callee_guaranteed] %closure_fun(%0) : $@convention(thin) (@guaranteed C) -> ()
1329+
cond_br undef, bb1, bb2
1330+
1331+
bb1:
1332+
br bb3
1333+
1334+
bb2:
1335+
apply %closure() : $@callee_guaranteed () -> ()
1336+
br bb3
1337+
1338+
bb3:
1339+
strong_release %0: $C
1340+
%tuple = tuple()
1341+
return %tuple : $()
1342+
}

test/SILOptimizer/mandatory_inlining.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,19 @@ func dontCrash() {
187187
fatalError("baz \(k)")
188188
}
189189
}
190+
191+
func switchLoopWithPartialApplyCallee(reportError: ((String) -> (Void))?) {
192+
let reportError = reportError ?? { error in
193+
print(error)
194+
}
195+
196+
for _ in 0..<1 {
197+
reportError("foo bar baz")
198+
}
199+
}
200+
201+
func switchLoopWithPartialApplyCaller() {
202+
switchLoopWithPartialApplyCallee { error in
203+
print(error)
204+
}
205+
}

0 commit comments

Comments
 (0)