Skip to content

Commit b2870a8

Browse files
authored
Merge pull request swiftlang#22698 from gottesmm/pr-c1efb90f3a61302ee9028592a5ab5aa9a2f2e54e
2 parents 4b44fed + 42d1849 commit b2870a8

File tree

5 files changed

+439
-28
lines changed

5 files changed

+439
-28
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,27 @@ class SILBuilder {
716716
BeginBorrowInst(getSILDebugLocation(Loc), LV));
717717
}
718718

719+
SILValue emitLoadBorrowOperation(SILLocation loc, SILValue v) {
720+
if (!hasOwnership()) {
721+
return emitLoadValueOperation(loc, v,
722+
LoadOwnershipQualifier::Unqualified);
723+
}
724+
return createLoadBorrow(loc, v);
725+
}
726+
727+
SILValue emitBeginBorrowOperation(SILLocation loc, SILValue v) {
728+
if (!hasOwnership() ||
729+
v.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Guaranteed))
730+
return v;
731+
return createBeginBorrow(loc, v);
732+
}
733+
734+
void emitEndBorrowOperation(SILLocation loc, SILValue v) {
735+
if (!hasOwnership())
736+
return;
737+
createEndBorrow(loc, v);
738+
}
739+
719740
// Pass in an address or value, perform a begin_borrow/load_borrow and pass
720741
// the value to the passed in closure. After the closure has finished
721742
// executing, automatically insert the end_borrow. The closure can assume that

lib/SIL/DynamicCasts.cpp

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,51 +1182,103 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M,
11821182
/// using a scalar cast operation.
11831183
void swift::emitIndirectConditionalCastWithScalar(
11841184
SILBuilder &B, ModuleDecl *M, SILLocation loc,
1185-
CastConsumptionKind consumption, SILValue src, CanType sourceType,
1186-
SILValue dest, CanType targetType, SILBasicBlock *indirectSuccBB,
1185+
CastConsumptionKind consumption, SILValue srcAddr, CanType sourceType,
1186+
SILValue destAddr, CanType targetType, SILBasicBlock *indirectSuccBB,
11871187
SILBasicBlock *indirectFailBB, ProfileCounter TrueCount,
11881188
ProfileCounter FalseCount) {
11891189
assert(canUseScalarCheckedCastInstructions(B.getModule(),
11901190
sourceType, targetType));
11911191

1192-
// We only need a different failure block if the cast consumption
1193-
// requires us to destroy the source value.
1194-
SILBasicBlock *scalarFailBB;
1195-
if (!shouldDestroyOnFailure(consumption)) {
1196-
scalarFailBB = indirectFailBB;
1197-
} else {
1198-
scalarFailBB = B.splitBlockForFallthrough();
1199-
}
1200-
1201-
// We always need a different success block.
1192+
// Create our successor and fail blocks.
1193+
SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough();
12021194
SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough();
12031195

1204-
auto &srcTL = B.getModule().Types.getTypeLowering(src->getType());
1205-
1206-
// Always take; this works under an assumption that retaining the
1207-
// result is equivalent to retaining the source. That means that
1208-
// these casts would not be appropriate for bridging-like conversions.
1209-
SILValue srcValue = srcTL.emitLoadOfCopy(B, loc, src, IsTake);
1196+
// Always take; this works under an assumption that retaining the result is
1197+
// equivalent to retaining the source. That means that these casts would not
1198+
// be appropriate for bridging-like conversions.
1199+
//
1200+
// Our plan is:
1201+
//
1202+
// 1. If the original cast was a take_always cast, then we take from our
1203+
// memory location in the caller, store the value into dest in the success
1204+
// block, and perform a destroy of our default argument in the failure block.
1205+
//
1206+
// 2. If the original cast was copy_on_success, then with ownership we borrow,
1207+
// copy in the success path and store back into the source slot after copying.
1208+
//
1209+
// 3. If the original cast was take_on_success, then on success we place the
1210+
// casted value into dest and on failure, store the original value back into
1211+
// src.
1212+
SILType targetValueType = destAddr->getType().getObjectType();
1213+
// Inline constructor
1214+
auto srcValue = ([&]() -> SILValue {
1215+
if (consumption == CastConsumptionKind::CopyOnSuccess)
1216+
return B.emitLoadBorrowOperation(loc, srcAddr);
1217+
return B.emitLoadValueOperation(loc, srcAddr, LoadOwnershipQualifier::Take);
1218+
})();
12101219

1211-
SILType targetValueType = dest->getType().getObjectType();
12121220
B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType,
12131221
scalarSuccBB, scalarFailBB, TrueCount, FalseCount);
12141222

12151223
// Emit the success block.
12161224
B.setInsertionPoint(scalarSuccBB); {
1217-
auto &targetTL = B.getModule().Types.getTypeLowering(targetValueType);
12181225
SILValue succValue = scalarSuccBB->createPhiArgument(
1219-
targetValueType, ValueOwnershipKind::Owned);
1220-
if (!shouldTakeOnSuccess(consumption))
1221-
targetTL.emitCopyValue(B, loc, succValue);
1222-
targetTL.emitStoreOfCopy(B, loc, succValue, dest, IsInitialization);
1226+
targetValueType, srcValue.getOwnershipKind());
1227+
1228+
switch (consumption) {
1229+
// On success, we take with both take_always and take_on_success.
1230+
case CastConsumptionKind::TakeAlways:
1231+
case CastConsumptionKind::TakeOnSuccess:
1232+
break;
1233+
case CastConsumptionKind::CopyOnSuccess: {
1234+
SILValue originalSuccValue = succValue;
1235+
succValue = B.emitCopyValueOperation(loc, succValue);
1236+
B.emitEndBorrowOperation(loc, originalSuccValue);
1237+
B.emitEndBorrowOperation(loc, srcValue);
1238+
break;
1239+
}
1240+
case CastConsumptionKind::BorrowAlways:
1241+
llvm_unreachable("should never see a borrow_always here");
1242+
}
1243+
1244+
// And then store the succValue into dest.
1245+
B.emitStoreValueOperation(loc, succValue, destAddr,
1246+
StoreOwnershipQualifier::Init);
12231247
B.createBranch(loc, indirectSuccBB);
12241248
}
12251249

12261250
// Emit the failure block.
1227-
if (shouldDestroyOnFailure(consumption)) {
1228-
B.setInsertionPoint(scalarFailBB);
1229-
srcTL.emitDestroyValue(B, loc, srcValue);
1251+
B.setInsertionPoint(scalarFailBB);
1252+
{
1253+
SILValue failValue = srcValue;
1254+
1255+
// If we have ownership, we need to create something for the default
1256+
// argument. Otherwise, we just use the input argument to the
1257+
// checked_cast_br.
1258+
if (B.hasOwnership()) {
1259+
failValue = scalarFailBB->createPhiArgument(srcValue->getType(),
1260+
srcValue.getOwnershipKind());
1261+
}
1262+
1263+
switch (consumption) {
1264+
case CastConsumptionKind::TakeAlways:
1265+
// We need to destroy the fail value if we have take_always.
1266+
B.emitDestroyValueOperation(loc, failValue);
1267+
break;
1268+
case CastConsumptionKind::TakeOnSuccess:
1269+
// If we have take_on_success, since we failed, just store the value back
1270+
// into the src location that we originally took from.
1271+
B.emitStoreValueOperation(loc, failValue, srcAddr,
1272+
StoreOwnershipQualifier::Init);
1273+
break;
1274+
case CastConsumptionKind::CopyOnSuccess:
1275+
B.emitEndBorrowOperation(loc, failValue);
1276+
B.emitEndBorrowOperation(loc, srcValue);
1277+
break;
1278+
case CastConsumptionKind::BorrowAlways:
1279+
llvm_unreachable("borrow_on_success should never appear here");
1280+
}
1281+
12301282
B.createBranch(loc, indirectFailBB);
12311283
}
12321284
}

test/SILOptimizer/mandatory_inlining.sil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ sil @partial_apply_user : $@convention(thin) (@owned @callee_owned (@owned Built
2727
sil @fromLiteral : $@convention(thin) (Builtin.Int128, @thin Int64.Type) -> Int64
2828
sil @use_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
2929

30+
class Klass {}
31+
3032
//////////////////////////////////////
3133
// Multiple Inline in a Block Tests //
3234
//////////////////////////////////////
@@ -578,7 +580,7 @@ bb2:
578580
br bb3(%14 : $Bool)
579581

580582
bb3(%16 : $Bool):
581-
strong_release %3 : ${ var @callee_owned () -> Bool }
583+
strong_release %3 : ${ var @callee_owned () -> Bool }
582584
strong_release %2 : ${ var Bool }
583585
return %16 : $Bool
584586
}

test/SILOptimizer/mandatory_inlining_ossa_to_non_ossa.sil

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,167 @@ bb0(%0 : $Builtin.NativeObject, %1 : $FakeOptional<Klass>):
293293
%9999 = tuple()
294294
return %9999 : $()
295295
}
296+
297+
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
298+
bb0(%0 : @owned $Builtin.NativeObject):
299+
%1 = alloc_stack $Builtin.NativeObject
300+
%2 = alloc_stack $Klass
301+
store %0 to [init] %1 : $*Builtin.NativeObject
302+
checked_cast_addr_br take_always Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
303+
304+
bb1:
305+
destroy_addr %2 : $*Klass
306+
br bb3
307+
308+
bb2:
309+
br bb3
310+
311+
bb3:
312+
dealloc_stack %2 : $*Klass
313+
dealloc_stack %1 : $*Builtin.NativeObject
314+
%9999 = tuple()
315+
return %9999 : $()
316+
}
317+
318+
319+
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
320+
// CHECK: bb0([[ARG:%.*]] :
321+
// CHECK-NEXT: strong_retain [[ARG]]
322+
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
323+
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
324+
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
325+
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
326+
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
327+
//
328+
// ==> On success, we store the value into dest. The destroy is not from the
329+
// ==> optimizer, but from the code.
330+
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
331+
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
332+
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
333+
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
334+
//
335+
// ==> take_always implies we destroy in failure
336+
// CHECK: [[FAILURE_BB]]:
337+
// CHECK-NEXT: strong_release [[RELOADED_ARG]]
338+
// CHECK-NEXT: br [[CONT_BB]]
339+
//
340+
// CHECK: [[CONT_BB]]:
341+
// CHECK: strong_release [[ARG]]
342+
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_takealways_caller'
343+
sil @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
344+
bb0(%0 : $Builtin.NativeObject):
345+
%3 = function_ref @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
346+
strong_retain %0 : $Builtin.NativeObject
347+
apply %3(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
348+
strong_release %0 : $Builtin.NativeObject
349+
%9999 = tuple()
350+
return %9999 : $()
351+
}
352+
353+
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
354+
bb0(%0 : @owned $Builtin.NativeObject):
355+
%1 = alloc_stack $Builtin.NativeObject
356+
%2 = alloc_stack $Klass
357+
store %0 to [init] %1 : $*Builtin.NativeObject
358+
checked_cast_addr_br take_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
359+
360+
bb1:
361+
destroy_addr %2 : $*Klass
362+
br bb3
363+
364+
bb2:
365+
destroy_addr %1 : $*Builtin.NativeObject
366+
br bb3
367+
368+
bb3:
369+
dealloc_stack %2 : $*Klass
370+
dealloc_stack %1 : $*Builtin.NativeObject
371+
%9999 = tuple()
372+
return %9999 : $()
373+
}
374+
375+
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
376+
// CHECK: bb0([[ARG:%.*]] :
377+
// CHECK-NEXT: strong_retain [[ARG]]
378+
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
379+
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
380+
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
381+
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
382+
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
383+
//
384+
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
385+
// ==> On success, we store into dest and destroy dest.
386+
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
387+
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
388+
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
389+
//
390+
// ==> Since we are doing a take on success and we failed... store the original
391+
// ==> value back into the memory slot.
392+
// CHECK: [[FAILURE_BB]]:
393+
// CHECK-NEXT: store [[RELOADED_ARG]] to [[SRC_ADDR]]
394+
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
395+
// CHECK-NEXT: br [[CONT_BB]]
396+
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_takeonsuccess_caller'
397+
sil @term_nonossa_checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
398+
bb0(%0 : $Builtin.NativeObject):
399+
%2 = function_ref @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
400+
strong_retain %0 : $Builtin.NativeObject
401+
apply %2(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
402+
strong_release %0 : $Builtin.NativeObject
403+
%9999 = tuple()
404+
return %9999 : $()
405+
}
406+
407+
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
408+
bb0(%0 : @owned $Builtin.NativeObject):
409+
%1 = alloc_stack $Builtin.NativeObject
410+
%2 = alloc_stack $Klass
411+
store %0 to [init] %1 : $*Builtin.NativeObject
412+
checked_cast_addr_br copy_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
413+
414+
bb1:
415+
destroy_addr %2 : $*Klass
416+
destroy_addr %1 : $*Builtin.NativeObject
417+
br bb3
418+
419+
bb2:
420+
destroy_addr %1 : $*Builtin.NativeObject
421+
br bb3
422+
423+
bb3:
424+
dealloc_stack %2 : $*Klass
425+
dealloc_stack %1 : $*Builtin.NativeObject
426+
%9999 = tuple()
427+
return %9999 : $()
428+
}
429+
430+
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
431+
// CHECK: bb0([[ARG:%.*]] :
432+
// CHECK-NEXT: strong_retain [[ARG]]
433+
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
434+
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
435+
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
436+
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
437+
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
438+
//
439+
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
440+
// CHECK-NEXT: strong_retain [[CAST_VALUE]]
441+
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
442+
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
443+
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
444+
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
445+
//
446+
// CHECK: [[FAILURE_BB]]:
447+
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
448+
// CHECK-NEXT: br [[CONT_BB]]
449+
//
450+
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_copyonsuccess_caller'
451+
sil @term_nonossa_checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
452+
bb0(%0 : $Builtin.NativeObject):
453+
%1 = function_ref @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
454+
strong_retain %0 : $Builtin.NativeObject
455+
apply %1(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
456+
strong_release %0 : $Builtin.NativeObject
457+
%9999 = tuple()
458+
return %9999 : $()
459+
}

0 commit comments

Comments
 (0)