[ownership] Update emitIndirectConditionalCastWithScalar for ownership.

This comes up because when we perform mandatory inlining, we perform the
transform as we inline. So the tests for this are in mandatory_inlining
naturally.
This commit is contained in:
Michael Gottesman
2019-02-18 15:13:33 -08:00
parent ab827a9821
commit 42d184995e
5 changed files with 439 additions and 28 deletions

View File

@@ -716,6 +716,27 @@ public:
BeginBorrowInst(getSILDebugLocation(Loc), LV)); BeginBorrowInst(getSILDebugLocation(Loc), LV));
} }
SILValue emitLoadBorrowOperation(SILLocation loc, SILValue v) {
if (!hasOwnership()) {
return emitLoadValueOperation(loc, v,
LoadOwnershipQualifier::Unqualified);
}
return createLoadBorrow(loc, v);
}
SILValue emitBeginBorrowOperation(SILLocation loc, SILValue v) {
if (!hasOwnership() ||
v.getOwnershipKind().isCompatibleWith(ValueOwnershipKind::Guaranteed))
return v;
return createBeginBorrow(loc, v);
}
void emitEndBorrowOperation(SILLocation loc, SILValue v) {
if (!hasOwnership())
return;
createEndBorrow(loc, v);
}
// Pass in an address or value, perform a begin_borrow/load_borrow and pass // Pass in an address or value, perform a begin_borrow/load_borrow and pass
// the value to the passed in closure. After the closure has finished // the value to the passed in closure. After the closure has finished
// executing, automatically insert the end_borrow. The closure can assume that // executing, automatically insert the end_borrow. The closure can assume that

View File

@@ -1182,51 +1182,103 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M,
/// using a scalar cast operation. /// using a scalar cast operation.
void swift::emitIndirectConditionalCastWithScalar( void swift::emitIndirectConditionalCastWithScalar(
SILBuilder &B, ModuleDecl *M, SILLocation loc, SILBuilder &B, ModuleDecl *M, SILLocation loc,
CastConsumptionKind consumption, SILValue src, CanType sourceType, CastConsumptionKind consumption, SILValue srcAddr, CanType sourceType,
SILValue dest, CanType targetType, SILBasicBlock *indirectSuccBB, SILValue destAddr, CanType targetType, SILBasicBlock *indirectSuccBB,
SILBasicBlock *indirectFailBB, ProfileCounter TrueCount, SILBasicBlock *indirectFailBB, ProfileCounter TrueCount,
ProfileCounter FalseCount) { ProfileCounter FalseCount) {
assert(canUseScalarCheckedCastInstructions(B.getModule(), assert(canUseScalarCheckedCastInstructions(B.getModule(),
sourceType, targetType)); sourceType, targetType));
// We only need a different failure block if the cast consumption // Create our successor and fail blocks.
// requires us to destroy the source value. SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough();
SILBasicBlock *scalarFailBB;
if (!shouldDestroyOnFailure(consumption)) {
scalarFailBB = indirectFailBB;
} else {
scalarFailBB = B.splitBlockForFallthrough();
}
// We always need a different success block.
SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough(); SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough();
auto &srcTL = B.getModule().Types.getTypeLowering(src->getType()); // Always take; this works under an assumption that retaining the result is
// equivalent to retaining the source. That means that these casts would not
// be appropriate for bridging-like conversions.
//
// Our plan is:
//
// 1. If the original cast was a take_always cast, then we take from our
// memory location in the caller, store the value into dest in the success
// block, and perform a destroy of our default argument in the failure block.
//
// 2. If the original cast was copy_on_success, then with ownership we borrow,
// copy in the success path and store back into the source slot after copying.
//
// 3. If the original cast was take_on_success, then on success we place the
// casted value into dest and on failure, store the original value back into
// src.
SILType targetValueType = destAddr->getType().getObjectType();
// Inline constructor
auto srcValue = ([&]() -> SILValue {
if (consumption == CastConsumptionKind::CopyOnSuccess)
return B.emitLoadBorrowOperation(loc, srcAddr);
return B.emitLoadValueOperation(loc, srcAddr, LoadOwnershipQualifier::Take);
})();
// Always take; this works under an assumption that retaining the
// result is equivalent to retaining the source. That means that
// these casts would not be appropriate for bridging-like conversions.
SILValue srcValue = srcTL.emitLoadOfCopy(B, loc, src, IsTake);
SILType targetValueType = dest->getType().getObjectType();
B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType, B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType,
scalarSuccBB, scalarFailBB, TrueCount, FalseCount); scalarSuccBB, scalarFailBB, TrueCount, FalseCount);
// Emit the success block. // Emit the success block.
B.setInsertionPoint(scalarSuccBB); { B.setInsertionPoint(scalarSuccBB); {
auto &targetTL = B.getModule().Types.getTypeLowering(targetValueType);
SILValue succValue = scalarSuccBB->createPhiArgument( SILValue succValue = scalarSuccBB->createPhiArgument(
targetValueType, ValueOwnershipKind::Owned); targetValueType, srcValue.getOwnershipKind());
if (!shouldTakeOnSuccess(consumption))
targetTL.emitCopyValue(B, loc, succValue); switch (consumption) {
targetTL.emitStoreOfCopy(B, loc, succValue, dest, IsInitialization); // On success, we take with both take_always and take_on_success.
case CastConsumptionKind::TakeAlways:
case CastConsumptionKind::TakeOnSuccess:
break;
case CastConsumptionKind::CopyOnSuccess: {
SILValue originalSuccValue = succValue;
succValue = B.emitCopyValueOperation(loc, succValue);
B.emitEndBorrowOperation(loc, originalSuccValue);
B.emitEndBorrowOperation(loc, srcValue);
break;
}
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("should never see a borrow_always here");
}
// And then store the succValue into dest.
B.emitStoreValueOperation(loc, succValue, destAddr,
StoreOwnershipQualifier::Init);
B.createBranch(loc, indirectSuccBB); B.createBranch(loc, indirectSuccBB);
} }
// Emit the failure block. // Emit the failure block.
if (shouldDestroyOnFailure(consumption)) {
B.setInsertionPoint(scalarFailBB); B.setInsertionPoint(scalarFailBB);
srcTL.emitDestroyValue(B, loc, srcValue); {
SILValue failValue = srcValue;
// If we have ownership, we need to create something for the default
// argument. Otherwise, we just use the input argument to the
// checked_cast_br.
if (B.hasOwnership()) {
failValue = scalarFailBB->createPhiArgument(srcValue->getType(),
srcValue.getOwnershipKind());
}
switch (consumption) {
case CastConsumptionKind::TakeAlways:
// We need to destroy the fail value if we have take_always.
B.emitDestroyValueOperation(loc, failValue);
break;
case CastConsumptionKind::TakeOnSuccess:
// If we have take_on_success, since we failed, just store the value back
// into the src location that we originally took from.
B.emitStoreValueOperation(loc, failValue, srcAddr,
StoreOwnershipQualifier::Init);
break;
case CastConsumptionKind::CopyOnSuccess:
B.emitEndBorrowOperation(loc, failValue);
B.emitEndBorrowOperation(loc, srcValue);
break;
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("borrow_on_success should never appear here");
}
B.createBranch(loc, indirectFailBB); B.createBranch(loc, indirectFailBB);
} }
} }

View File

@@ -27,6 +27,8 @@ sil @partial_apply_user : $@convention(thin) (@owned @callee_owned (@owned Built
sil @fromLiteral : $@convention(thin) (Builtin.Int128, @thin Int64.Type) -> Int64 sil @fromLiteral : $@convention(thin) (Builtin.Int128, @thin Int64.Type) -> Int64
sil @use_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () sil @use_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
class Klass {}
////////////////////////////////////// //////////////////////////////////////
// Multiple Inline in a Block Tests // // Multiple Inline in a Block Tests //
////////////////////////////////////// //////////////////////////////////////

View File

@@ -293,3 +293,167 @@ bb0(%0 : $Builtin.NativeObject, %1 : $FakeOptional<Klass>):
%9999 = tuple() %9999 = tuple()
return %9999 : $() return %9999 : $()
} }
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br take_always Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
br bb3
bb2:
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: strong_retain [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// ==> On success, we store the value into dest. The destroy is not from the
// ==> optimizer, but from the code.
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// ==> take_always implies we destroy in failure
// CHECK: [[FAILURE_BB]]:
// CHECK-NEXT: strong_release [[RELOADED_ARG]]
// CHECK-NEXT: br [[CONT_BB]]
//
// CHECK: [[CONT_BB]]:
// CHECK: strong_release [[ARG]]
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_takealways_caller'
sil @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%3 = function_ref @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_retain %0 : $Builtin.NativeObject
apply %3(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_release %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br take_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
br bb3
bb2:
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: strong_retain [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
// ==> On success, we store into dest and destroy dest.
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// ==> Since we are doing a take on success and we failed... store the original
// ==> value back into the memory slot.
// CHECK: [[FAILURE_BB]]:
// CHECK-NEXT: store [[RELOADED_ARG]] to [[SRC_ADDR]]
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB]]
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_takeonsuccess_caller'
sil @term_nonossa_checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%2 = function_ref @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_retain %0 : $Builtin.NativeObject
apply %2(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_release %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br copy_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb2:
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil @term_nonossa_checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: strong_retain [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG]] to [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
// CHECK-NEXT: strong_retain [[CAST_VALUE]]
// CHECK-NEXT: store [[CAST_VALUE]] to [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// CHECK: [[FAILURE_BB]]:
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB]]
//
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_copyonsuccess_caller'
sil @term_nonossa_checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%1 = function_ref @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_retain %0 : $Builtin.NativeObject
apply %1(%0) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
strong_release %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

View File

@@ -10,6 +10,8 @@ class C {
init(i: Builtin.Int64) init(i: Builtin.Int64)
} }
class Klass {}
sil [transparent] [ossa] @calleeWithGuaranteed : $@convention(thin) (@guaranteed C) -> Builtin.Int64 { sil [transparent] [ossa] @calleeWithGuaranteed : $@convention(thin) (@guaranteed C) -> Builtin.Int64 {
bb(%0 : @guaranteed $C): bb(%0 : @guaranteed $C):
%1 = ref_element_addr %0 : $C, #C.i %1 = ref_element_addr %0 : $C, #C.i
@@ -142,3 +144,173 @@ bb3:
destroy_value %0 : $Builtin.NativeObject destroy_value %0 : $Builtin.NativeObject
return %13 : $Builtin.NativeObject return %13 : $Builtin.NativeObject
} }
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br take_always Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
br bb3
bb2:
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [take] [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// ==> On success, we store the value into dest. The destroy is not from the
// ==> optimizer, but from the code.
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
// CHECK-NEXT: store [[CAST_VALUE]] to [init] [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// ==> take_always implies we destroy in failure
// CHECK: [[FAILURE_BB]]([[FAILURE_ARG:%.*]] :
// CHECK-NEXT: destroy_value [[FAILURE_ARG]]
// CHECK-NEXT: br [[CONT_BB]]
//
// CHECK: [[CONT_BB]]:
// CHECK: destroy_value [[ARG]]
// CHECK: } // end sil function 'term_nonossa_checked_cast_addr_br_takealways_caller'
sil [ossa] @term_nonossa_checked_cast_addr_br_takealways_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%3 = function_ref @term_ossa_checked_cast_addr_br_takealways_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%1 = copy_value %0 : $Builtin.NativeObject
apply %3(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
destroy_value %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br take_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
br bb3
bb2:
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load [take] [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] :
// ==> On success, we store into dest and destroy dest.
// CHECK-NEXT: store [[CAST_VALUE]] to [init] [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// ==> Since we are doing a take on success and we failed... store the original
// ==> value back into the memory slot.
// CHECK: [[FAILURE_BB]]([[FAIL_ARG:%.*]] :
// CHECK-NEXT: store [[FAIL_ARG]] to [init] [[SRC_ADDR]]
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB]]
//
// CHECK: } // end sil function 'checked_cast_addr_br_takeonsuccess_caller'
sil [ossa] @checked_cast_addr_br_takeonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%2 = function_ref @term_ossa_checked_cast_addr_br_takeonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%1 = copy_value %0 : $Builtin.NativeObject
apply %2(%1) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
destroy_value %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
sil [transparent] [ossa] @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
%2 = alloc_stack $Klass
store %0 to [init] %1 : $*Builtin.NativeObject
checked_cast_addr_br copy_on_success Builtin.NativeObject in %1 : $*Builtin.NativeObject to Klass in %2 : $*Klass, bb1, bb2
bb1:
destroy_addr %2 : $*Klass
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb2:
destroy_addr %1 : $*Builtin.NativeObject
br bb3
bb3:
dealloc_stack %2 : $*Klass
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]]
// CHECK-NEXT: [[SRC_ADDR:%.*]] = alloc_stack $Builtin.NativeObject
// CHECK-NEXT: [[DEST_ADDR:%.*]] = alloc_stack $Klass
// CHECK-NEXT: store [[ARG_COPY]] to [init] [[SRC_ADDR]]
// CHECK-NEXT: [[RELOADED_ARG:%.*]] = load_borrow [[SRC_ADDR]]
// CHECK-NEXT: checked_cast_br [[RELOADED_ARG]] : $Builtin.NativeObject to $Klass, [[SUCCESS_BB:bb[0-9]+]], [[FAILURE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_BB]]([[CAST_VALUE:%.*]] : @guaranteed
// CHECK-NEXT: [[CAST_VALUE_COPY:%.*]] = copy_value [[CAST_VALUE]]
// CHECK-NEXT: end_borrow [[CAST_VALUE]]
// CHECK-NEXT: end_borrow [[RELOADED_ARG]]
// CHECK-NEXT: store [[CAST_VALUE_COPY]] to [init] [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[DEST_ADDR]]
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB:bb[0-9]+]]
//
// CHECK: [[FAILURE_BB]]([[FAILURE_BORROWED_ARG:%.*]] :
// CHECK-NEXT: end_borrow [[FAILURE_BORROWED_ARG]]
// CHECK-NEXT: end_borrow [[RELOADED_ARG]]
// CHECK-NEXT: destroy_addr [[SRC_ADDR]]
// CHECK-NEXT: br [[CONT_BB]]
//
// CHECK: } // end sil function 'checked_cast_addr_br_copyonsuccess_caller'
sil [ossa] @checked_cast_addr_br_copyonsuccess_caller : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = function_ref @term_ossa_checked_cast_addr_br_copyonsuccess_callee : $@convention(thin) (@owned Builtin.NativeObject) -> ()
%2 = copy_value %0 : $Builtin.NativeObject
apply %1(%2) : $@convention(thin) (@owned Builtin.NativeObject) -> ()
destroy_value %0 : $Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}