From 6c5c853339956b339026807347f7301ab8040511 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 31 Jan 2020 18:52:08 -0800 Subject: [PATCH] [ownership] Change guaranteed args from transformation terminators to not require end_borrows. For those who are unaware, a transformation terminator is a terminator like switch_enum/checked_cast_br that always dominate their successor blocks. Since they dominate their successor blocks by design and transform their input into the args form, we can validate that they obey guaranteed ownership semantics just like a forwarding instruction. Beyond removing unnecessary code bloat, this also makes it significantly more easier to optimize/work with transformation terminators when converting @owned -> @guaranteed since we do not need to find end_borrow points when the owned value is consumed. --- include/swift/SIL/SILBuilder.h | 6 ++ include/swift/SIL/SILInstruction.h | 24 ++++++ lib/SIL/DynamicCasts.cpp | 3 - lib/SIL/OwnershipUtils.cpp | 24 +++++- lib/SIL/SILOwnershipVerifier.cpp | 52 ++++++++++++- lib/SILGen/SILGenBuilder.cpp | 7 ++ lib/SILGen/SILGenBuilder.h | 9 +++ lib/SILGen/SILGenDynamicCast.cpp | 5 +- lib/SILGen/SILGenExpr.cpp | 10 ++- lib/SILGen/SILGenPattern.cpp | 4 +- test/SIL/ownership-verifier/over_consume.sil | 17 ++--- test/SIL/ownership-verifier/use_verifier.sil | 7 -- test/SILGen/switch.swift | 14 ---- test/SILGen/switch_isa.swift | 7 -- test/SILOptimizer/destroy_hoisting.sil | 1 - test/SILOptimizer/diagnose_unreachable.sil | 76 ++++++++++++++++--- .../mandatory_inlining_ownership.sil | 2 - 17 files changed, 200 insertions(+), 68 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index f7d8bf2f884df..f279acb259b18 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -802,6 +802,12 @@ class SILBuilder { } EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue) { + if (auto *arg = dyn_cast(borrowedValue)) { + if (auto *ti = arg->getSingleTerminator()) { + assert(!ti->isTransformationTerminator() && + "Transforming terminators do not have end_borrow"); + } + } return insert(new (getModule()) EndBorrowInst(getSILDebugLocation(loc), borrowedValue)); } diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4afcb22beb988..ec22b11ea77c6 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7042,6 +7042,30 @@ class TermInst : public NonValueInstruction { bool isProgramTerminating() const; TermKind getTermKind() const { return TermKind(getKind()); } + + /// Returns true if this is a transformation terminator. + bool isTransformationTerminator() const { + switch (getTermKind()) { + case TermKind::UnwindInst: + case TermKind::UnreachableInst: + case TermKind::ReturnInst: + case TermKind::ThrowInst: + case TermKind::YieldInst: + case TermKind::TryApplyInst: + case TermKind::BranchInst: + case TermKind::CondBranchInst: + case TermKind::SwitchValueInst: + case TermKind::SwitchEnumAddrInst: + case TermKind::DynamicMethodBranchInst: + case TermKind::CheckedCastAddrBranchInst: + case TermKind::CheckedCastValueBranchInst: + return false; + case TermKind::SwitchEnumInst: + case TermKind::CheckedCastBranchInst: + return true; + } + llvm_unreachable("Covered switch isn't covered."); + } }; /// UnreachableInst - Position in the code which would be undefined to reach. diff --git a/lib/SIL/DynamicCasts.cpp b/lib/SIL/DynamicCasts.cpp index fe5b9a8bf8c68..d6cfef83222a2 100644 --- a/lib/SIL/DynamicCasts.cpp +++ b/lib/SIL/DynamicCasts.cpp @@ -1254,9 +1254,7 @@ void swift::emitIndirectConditionalCastWithScalar( case CastConsumptionKind::TakeOnSuccess: break; case CastConsumptionKind::CopyOnSuccess: { - SILValue originalSuccValue = succValue; succValue = B.emitCopyValueOperation(loc, succValue); - B.emitEndBorrowOperation(loc, originalSuccValue); B.emitEndBorrowOperation(loc, srcValue); break; } @@ -1295,7 +1293,6 @@ void swift::emitIndirectConditionalCastWithScalar( StoreOwnershipQualifier::Init); break; case CastConsumptionKind::CopyOnSuccess: - B.emitEndBorrowOperation(loc, failValue); B.emitEndBorrowOperation(loc, srcValue); break; case CastConsumptionKind::BorrowAlways: diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index 8047342ad08e6..9f37d9c37bcab 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -66,6 +66,15 @@ bool swift::isGuaranteedForwardingValueKind(SILNodeKind kind) { } bool swift::isGuaranteedForwardingValue(SILValue value) { + // If we have an argument from a transforming terminator, we can forward + // guaranteed. + if (auto *arg = dyn_cast(value)) { + if (auto *ti = arg->getSingleTerminator()) { + if (ti->isTransformationTerminator()) { + return true; + } + } + } return isGuaranteedForwardingValueKind( value->getKindOfRepresentativeSILNodeInObject()); } @@ -173,9 +182,18 @@ bool swift::getUnderlyingBorrowIntroducingValues( // Otherwise if v is an ownership forwarding value, add its defining // instruction if (isGuaranteedForwardingValue(v)) { - auto *i = v->getDefiningInstruction(); - assert(i); - llvm::transform(i->getAllOperands(), std::back_inserter(worklist), + if (auto *i = v->getDefiningInstruction()) { + llvm::transform(i->getAllOperands(), std::back_inserter(worklist), + [](const Operand &op) -> SILValue { return op.get(); }); + continue; + } + + // Otherwise, we should have a block argument that is defined by a single + // predecessor terminator. + auto *arg = cast(v); + auto *termInst = arg->getSingleTerminator(); + assert(termInst && termInst->isTransformationTerminator()); + llvm::transform(termInst->getAllOperands(), std::back_inserter(worklist), [](const Operand &op) -> SILValue { return op.get(); }); continue; } diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index f05a70f435a82..5e9139683db6e 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -334,9 +334,55 @@ bool SILValueOwnershipChecker::gatherUsers( continue; } - // Otherwise if we have a terminator, add any as uses any end_borrow to - // ensure that the subscope is completely enclsed within the super scope. We - // require all of our arguments to be either trivial or guaranteed. + // See if our forwarding terminator is a transformation terminator. If so, + // just add all of the uses of its successors to the worklist to visit as + // users. + if (ti->isTransformationTerminator()) { + for (auto &succ : ti->getSuccessors()) { + auto *succBlock = succ.getBB(); + + // If we do not have any arguments, then continue. + if (succBlock->args_empty()) + continue; + + // Otherwise, make sure that all arguments are trivial or guaranteed. If + // we fail, emit an error. + // + // TODO: We could ignore this error and emit a more specific error on + // the actual terminator. + for (auto *succArg : succBlock->getSILPhiArguments()) { + // *NOTE* We do not emit an error here since we want to allow for more + // specific errors to be found during use_verification. + // + // TODO: Add a flag that associates the terminator instruction with + // needing to be verified. If it isn't verified appropriately, assert + // when the verifier is destroyed. + auto succArgOwnershipKind = succArg->getOwnershipKind(); + if (!succArgOwnershipKind.isCompatibleWith(ownershipKind)) { + // This is where the error would go. + continue; + } + + // If we have an any value, just continue. + if (succArgOwnershipKind == ValueOwnershipKind::None) + continue; + + // Otherwise add all users of this BBArg to the worklist to visit + // recursively. + llvm::copy(succArg->getUses(), std::back_inserter(users)); + } + } + continue; + } + + // Otherwise, we are dealing with the merging of true phis. To work with + // this we will eventually need to create a notion of forwarding borrow + // scopes. + + // But until then, we validate that the argument has an end_borrow that acts + // as a subscope that is compeltely enclosed within the scopes of all + // incoming values. We require all of our arguments to be either trivial or + // guaranteed. for (auto &succ : ti->getSuccessors()) { auto *succBlock = succ.getBB(); diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index 503e6d6d9b55c..4ba29af7d3abe 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -199,6 +199,13 @@ ManagedValue SILGenBuilder::createGuaranteedPhiArgument(SILType type) { return SGF.emitManagedBorrowedArgumentWithCleanup(arg); } +ManagedValue +SILGenBuilder::createGuaranteedTransformingTerminatorArgument(SILType type) { + SILPhiArgument *arg = + getInsertionBB()->createPhiArgument(type, ValueOwnershipKind::Guaranteed); + return ManagedValue::forUnmanaged(arg); +} + ManagedValue SILGenBuilder::createAllocRef( SILLocation loc, SILType refType, bool objc, bool canAllocOnStack, ArrayRef inputElementTypes, diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index c4b8cd3f1b42e..55d3dad837e00 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -127,6 +127,15 @@ class SILGenBuilder : public SILBuilder { ManagedValue createOwnedPhiArgument(SILType type); ManagedValue createGuaranteedPhiArgument(SILType type); + /// For arguments from terminators that are "transforming terminators". These + /// types of guaranteed arguments are validated as part of the operand of the + /// transforming terminator since transforming terminators are guaranteed to + /// be the only predecessor of our parent block. + /// + /// NOTE: Two examples of transforming terminators are switch_enum, + /// checked_cast_br. + ManagedValue createGuaranteedTransformingTerminatorArgument(SILType type); + using SILBuilder::createMarkUninitialized; ManagedValue createMarkUninitialized(ValueDecl *decl, ManagedValue operand, MarkUninitializedInst::Kind muKind); diff --git a/lib/SILGen/SILGenDynamicCast.cpp b/lib/SILGen/SILGenDynamicCast.cpp index 7b3ea94ceb66b..c63c446c3be58 100644 --- a/lib/SILGen/SILGenDynamicCast.cpp +++ b/lib/SILGen/SILGenDynamicCast.cpp @@ -185,7 +185,7 @@ namespace { // argument. ManagedValue argument; if (!shouldTakeOnSuccess(consumption)) { - argument = SGF.B.createGuaranteedPhiArgument( + argument = SGF.B.createGuaranteedTransformingTerminatorArgument( origTargetTL.getLoweredType()); } else { argument = @@ -236,7 +236,8 @@ namespace { switch (consumption) { case CastConsumptionKind::BorrowAlways: case CastConsumptionKind::CopyOnSuccess: - SGF.B.createGuaranteedPhiArgument(operandValue.getType()); + SGF.B.createGuaranteedTransformingTerminatorArgument( + operandValue.getType()); handleFalse(None); break; case CastConsumptionKind::TakeAlways: diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 879c9082c6272..0986ed1f33e46 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -165,8 +165,14 @@ namespace { struct EndBorrowCleanup : Cleanup { SILValue borrowedValue; - EndBorrowCleanup(SILValue borrowedValue) - : borrowedValue(borrowedValue) {} + EndBorrowCleanup(SILValue borrowedValue) : borrowedValue(borrowedValue) { + if (auto *arg = dyn_cast(borrowedValue)) { + if (auto *ti = arg->getSingleTerminator()) { + assert(!ti->isTransformationTerminator() && + "Transforming terminators do not have end_borrow"); + } + } + } void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index 711b70d101178..bc510cd09a56d 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -1921,7 +1921,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( SILValue eltValue; if (isPlusZero) { - origCMV = {SGF.B.createGuaranteedPhiArgument(eltTy), + origCMV = {SGF.B.createGuaranteedTransformingTerminatorArgument(eltTy), CastConsumptionKind::BorrowAlways}; } else { origCMV = {SGF.B.createOwnedPhiArgument(eltTy), @@ -1971,7 +1971,7 @@ void PatternMatchEmission::emitEnumElementObjectDispatch( if (SILBasicBlock *defaultBB = blocks.getDefaultBlock()) { SGF.B.setInsertionPoint(defaultBB); if (isPlusZero) { - SGF.B.createGuaranteedPhiArgument(src.getType()); + SGF.B.createGuaranteedTransformingTerminatorArgument(src.getType()); } else { SGF.B.createOwnedPhiArgument(src.getType()); } diff --git a/test/SIL/ownership-verifier/over_consume.sil b/test/SIL/ownership-verifier/over_consume.sil index e178c87e53d0b..26df8e67f0d8a 100644 --- a/test/SIL/ownership-verifier/over_consume.sil +++ b/test/SIL/ownership-verifier/over_consume.sil @@ -235,7 +235,6 @@ bb0(%0 : @owned $Optional): switch_enum %0 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%1 : @guaranteed $Builtin.NativeObject): - end_borrow %1 : $Builtin.NativeObject br bb3 bb2: @@ -251,7 +250,7 @@ bb3: // CHECK: Found use after free?! // CHECK: Value: %1 = begin_borrow %0 : $Optional // CHECK: Consuming User: end_borrow %1 : $Optional -// CHECK: Non Consuming User: end_borrow %3 : $Builtin.NativeObject +// CHECK: Non Consuming User: %5 = unchecked_ref_cast %3 : $Builtin.NativeObject to $Builtin.NativeObject // CHECK: Block: bb1 sil [ossa] @switch_enum_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Optional) -> () { bb0(%0 : @owned $Optional): @@ -260,7 +259,7 @@ bb0(%0 : @owned $Optional): bb1(%2 : @guaranteed $Builtin.NativeObject): end_borrow %1 : $Optional - end_borrow %2 : $Builtin.NativeObject + %2a = unchecked_ref_cast %2 : $Builtin.NativeObject to $Builtin.NativeObject br bb3 bb2: @@ -312,7 +311,6 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @owned $Builtin.NativeObject): @@ -339,7 +337,6 @@ bb1(%1 : @owned $SuperKlass): br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -363,11 +360,9 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -387,7 +382,6 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %0 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%1 : @guaranteed $SuperKlass): - end_borrow %1 : $SuperKlass br bb3 bb2(%2 : @owned $Builtin.NativeObject): @@ -414,7 +408,6 @@ bb1(%1 : @owned $SuperKlass): br bb3 bb2(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb3: @@ -426,7 +419,7 @@ bb3: // CHECK: Found use after free?! // CHECK: Value: %1 = begin_borrow %0 : $Builtin.NativeObject // CHECK: Consuming User: end_borrow %1 : $Builtin.NativeObject -// CHECK: Non Consuming User: end_borrow %7 : $Builtin.NativeObject +// CHECK: Non Consuming User: %9 = unchecked_ref_cast %7 : $Builtin.NativeObject to $Builtin.NativeObject // CHECK: Block: bb2 sil [ossa] @checked_cast_br_guaranteed_arg_outlives_original_value : $@convention(thin) (@owned Builtin.NativeObject) -> () { bb0(%0 : @owned $Builtin.NativeObject): @@ -434,13 +427,13 @@ bb0(%0 : @owned $Builtin.NativeObject): checked_cast_br %1 : $Builtin.NativeObject to SuperKlass, bb1, bb2 bb1(%2 : @guaranteed $SuperKlass): - end_borrow %2 : $SuperKlass end_borrow %1 : $Builtin.NativeObject + %2a = unchecked_ref_cast %2 : $SuperKlass to $SuperKlass br bb3 bb2(%3 : @guaranteed $Builtin.NativeObject): end_borrow %1 : $Builtin.NativeObject - end_borrow %3 : $Builtin.NativeObject + %3a = unchecked_ref_cast %3 : $Builtin.NativeObject to $Builtin.NativeObject br bb3 bb3: diff --git a/test/SIL/ownership-verifier/use_verifier.sil b/test/SIL/ownership-verifier/use_verifier.sil index 0c95b2b8351d9..2fe7809c7caea 100644 --- a/test/SIL/ownership-verifier/use_verifier.sil +++ b/test/SIL/ownership-verifier/use_verifier.sil @@ -518,7 +518,6 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): switch_enum %1 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%2 : @guaranteed $Builtin.NativeObject): - end_borrow %2 : $Builtin.NativeObject br bb3 bb2: @@ -536,7 +535,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject end_borrow %1 : $Builtin.NativeObject br bb3 @@ -557,7 +555,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject br bb3 bb2: @@ -577,7 +574,6 @@ bb0(%0 : @owned $Builtin.NativeObject): switch_enum %2 : $Optional, case #Optional.some!enumelt.1: bb1, case #Optional.none!enumelt: bb2 bb1(%3 : @guaranteed $Builtin.NativeObject): - end_borrow %3 : $Builtin.NativeObject br bb3 bb2: @@ -784,11 +780,9 @@ bb6: checked_cast_br %6 : $Builtin.NativeObject to SuperKlass, bb7, bb8 bb7(%7 : @guaranteed $SuperKlass): - end_borrow %7 : $SuperKlass br bb9 bb8(%8 : @guaranteed $Builtin.NativeObject): - end_borrow %8 : $Builtin.NativeObject br bb9 bb9: @@ -1195,4 +1189,3 @@ bb0(%0 : @guaranteed $Builtin.NativeObject, %1 : @owned $Builtin.NativeObject): %9999 = tuple() return %9999 : $() } - diff --git a/test/SILGen/switch.swift b/test/SILGen/switch.swift index eacc3dd1d08f4..ba883822e6610 100644 --- a/test/SILGen/switch.swift +++ b/test/SILGen/switch.swift @@ -468,17 +468,14 @@ func test_isa_class_1(x: B) { case is D1 where runced(): // CHECK: function_ref @$s6switch1ayyF // CHECK: destroy_value [[CAST_D1_COPY]] - // CHECK: end_borrow [[CAST_D1]] // CHECK: br [[CONT:bb[0-9]+]] a() // CHECK: [[NO_CASE1]]: // CHECK-NEXT: destroy_value [[CAST_D1_COPY]] - // CHECK-NEXT: end_borrow [[CAST_D1]] // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_D1]]([[CASTFAIL_D1:%.*]] : @guaranteed $B): - // CHECK-NEXT: end_borrow [[CASTFAIL_D1]] // CHECK-NEXT: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -492,7 +489,6 @@ func test_isa_class_1(x: B) { b() // CHECK: [[IS_NOT_D2]]([[CASTFAIL_D2:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[CASTFAIL_D2]] // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case is E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): @@ -508,11 +504,9 @@ func test_isa_class_1(x: B) { // CHECK: [[NO_CASE3]]: // CHECK-NEXT: destroy_value [[CAST_E_COPY]] - // CHECK-NEXT: end_borrow // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_E]]([[NOTCAST_E:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOTCAST_E]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -524,12 +518,10 @@ func test_isa_class_1(x: B) { // CHECK: function_ref @$s6switch1dyyF // CHECK-NEXT: apply // CHECK: destroy_value [[CAST_C_COPY]] - // CHECK: end_borrow [[CAST_C]] // CHECK: br [[CONT]] d() // CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_C]] default: // CHECK: function_ref @$s6switch1eyyF // CHECK: br [[CONT]] @@ -570,7 +562,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE:bb5]] // CHECK: [[IS_NOT_D1]]([[NOCAST_D1:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_D1]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]]: @@ -589,7 +580,6 @@ func test_isa_class_2(x: B) -> AnyObject { return y // CHECK: [[IS_NOT_D2]]([[NOCAST_D2:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_D2]] // CHECK: checked_cast_br [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]] case let y as E where funged(): // CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E): @@ -604,7 +594,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: [[RET:%.*]] = init_existential_ref [[CAST_E_COPY_COPY]] // CHECK: end_borrow [[BORROWED_CAST_E_COPY]] // CHECK: destroy_value [[CAST_E_COPY]] - // CHECK: end_borrow [[CAST_E]] // CHECK: br [[CONT]]([[RET]] : $AnyObject) c() return y @@ -614,7 +603,6 @@ func test_isa_class_2(x: B) -> AnyObject { // CHECK: br [[NEXT_CASE:bb[0-9]+]] // CHECK: [[IS_NOT_E]]([[NOCAST_E:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_E]] // CHECK: br [[NEXT_CASE]] // CHECK: [[NEXT_CASE]] @@ -633,7 +621,6 @@ func test_isa_class_2(x: B) -> AnyObject { return y // CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B): - // CHECK: end_borrow [[NOCAST_C]] default: // CHECK: function_ref @$s6switch1eyyF // CHECK: [[X_COPY_2:%.*]] = copy_value [[X]] @@ -1166,7 +1153,6 @@ func address_only_with_trivial_subtype(_ a: TrivialSingleCaseEnum, _ value: Any) // CHECK: switch_enum [[TUP_0_VAL]] // // CHECK: bb1([[CASE_VAL:%.*]] : -// CHECK-NEXT: end_borrow [[CASE_VAL]] // CHECK-NEXT: destroy_addr [[TUP_1]] // CHECK-NEXT: end_borrow [[TUP_0_VAL]] // CHECK-NEXT: destroy_addr [[TUP_0]] diff --git a/test/SILGen/switch_isa.swift b/test/SILGen/switch_isa.swift index 54a07371eee3c..b73abc34fd697 100644 --- a/test/SILGen/switch_isa.swift +++ b/test/SILGen/switch_isa.swift @@ -71,8 +71,6 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true } // CHECK: [[GUARD_YES]]: // CHECK-NEXT: destroy_value [[L2]] // CHECK-NEXT: destroy_value [[R2]] -// CHECK-NEXT: end_borrow [[L]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: destroy_value [[TUP]] // CHECK-NEXT: br [[EXIT:bb[0-9]+]] @@ -80,16 +78,11 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true } // CHECK: [[GUARD_NO]]: // CHECK-NEXT: destroy_value [[L2]] // CHECK-NEXT: destroy_value [[R2]] -// TODO: Infer end_borrow from the input begin_borrow. This should be eliminated. -// CHECK-NEXT: end_borrow [[L]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: br [[CONT:bb[0-9]+]] // // CHECK: [[L_CAST_NO]]([[LFAIL:%.*]] : @guaranteed $B): -// CHECK-NEXT: end_borrow [[LFAIL]] // CHECK-NEXT: destroy_value [[R2]] -// CHECK-NEXT: end_borrow [[R]] // CHECK-NEXT: end_borrow [[BORROWED_TUP]] // CHECK-NEXT: br [[CONT]] // diff --git a/test/SILOptimizer/destroy_hoisting.sil b/test/SILOptimizer/destroy_hoisting.sil index e97dc0e91aaac..f85bb5ffd2c3f 100644 --- a/test/SILOptimizer/destroy_hoisting.sil +++ b/test/SILOptimizer/destroy_hoisting.sil @@ -131,7 +131,6 @@ bb0(%0 : $*S, %1 : @owned $S, %2 : @guaranteed $TwoCases): bb1(%4 : @guaranteed $X): %5 = enum $TwoCases, #TwoCases.B!enumelt store %1 to [assign] %0 : $*S - end_borrow %4 : $X br bb3 bb2: diff --git a/test/SILOptimizer/diagnose_unreachable.sil b/test/SILOptimizer/diagnose_unreachable.sil index 7045ca9e368ab..be1a304fe1be8 100644 --- a/test/SILOptimizer/diagnose_unreachable.sil +++ b/test/SILOptimizer/diagnose_unreachable.sil @@ -783,23 +783,20 @@ bb2: unreachable } -// Test propagation of guaranteed phi arguments. The nested end_borrow -// must be removed, even with the outer borrow is *not* a function -// argument. - enum EnumWithB { case A(B) func testit() -> Int } -// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// Test propagation of guaranteed phi arguments. The nested end_borrow +// must be removed, even with the outer borrow is *not* a function +// argument. +// +// CHECK-LABEL: sil hidden [ossa] @testEliminatePropagateGuaranteedPhiAfterSwitch : $@convention(method) (@guaranteed EnumWithB) -> () { // CHECK: bb1([[PHI:%.*]] : @guaranteed $B): -// CHECK: br bb2 -// CHECK: bb2: -// CHECK: end_borrow [[PHI]] : $B // CHECK-NOT: end_borrow -// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' -sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK-LABEL: } // end sil function 'testEliminatePropagateGuaranteedPhiAfterSwitch' +sil hidden [ossa] @testEliminatePropagateGuaranteedPhiAfterSwitch : $@convention(method) (@guaranteed EnumWithB) -> () { bb0(%0 : @guaranteed $EnumWithB): switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 @@ -807,8 +804,67 @@ bb1(%2 : @guaranteed $B): br bb3(%2 : $B) bb3(%4 : @guaranteed $B): + %99 = tuple () end_borrow %4 : $B + return %99 : $() +} + +// CHECK-LABEL: sil hidden [ossa] @testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch : $@convention(method) (@owned B) -> () { +// CHECK: [[BORROW:%.*]] = begin_borrow +// CHECK: end_borrow [[BORROW]] +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch' +sil hidden [ossa] @testEliminatePropagateBeginBorrowGuaranteedPhiAfterSwitch : $@convention(method) (@owned B) -> () { +bb0(%0 : @owned $B): + %1 = begin_borrow %0 : $B + br bb1(%1 : $B) + +bb1(%2 : @guaranteed $B): + br bb3(%2 : $B) + +bb3(%3 : @guaranteed $B): + %99 = tuple () + end_borrow %3 : $B end_borrow %2 : $B + end_borrow %1 : $B + destroy_value %0 : $B + return %99 : $() +} + +// Make sure we do not look for end_borrow when looking at a switch_enum, switch_enum. +// +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhiSwitchEnum : $@convention(method) (@guaranteed EnumWithB) -> () { +// CHECK: bb1([[PHI:%.*]] : @guaranteed $B): +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhiSwitchEnum' +sil hidden [ossa] @testPropagateGuaranteedPhiSwitchEnum : $@convention(method) (@guaranteed EnumWithB) -> () { +bb0(%0 : @guaranteed $EnumWithB): + switch_enum %0 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb1 + +bb1(%2 : @guaranteed $B): + %3 = enum $EnumWithB, #EnumWithB.A!enumelt.1, %2 : $B + switch_enum %3 : $EnumWithB, case #EnumWithB.A!enumelt.1: bb3 + +bb3(%4 : @guaranteed $B): %99 = tuple () return %99 : $() } + +// Make sure that we can handle iterated end_borrow. +// +// CHECK-LABEL: sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed B) -> () { +// CHECK-NOT: end_borrow +// CHECK-LABEL: } // end sil function 'testPropagateGuaranteedPhi' +sil hidden [ossa] @testPropagateGuaranteedPhi : $@convention(method) (@guaranteed B) -> () { +bb0(%0 : @guaranteed $B): + br bb1(%0 : $B) + +bb1(%1 : @guaranteed $B): + br bb2(%1 : $B) + +bb2(%2 : @guaranteed $B): + %99 = tuple () + end_borrow %2 : $B + end_borrow %1 : $B + return %99 : $() +} diff --git a/test/SILOptimizer/mandatory_inlining_ownership.sil b/test/SILOptimizer/mandatory_inlining_ownership.sil index 8f5429b2869dc..2eceda9595f66 100644 --- a/test/SILOptimizer/mandatory_inlining_ownership.sil +++ b/test/SILOptimizer/mandatory_inlining_ownership.sil @@ -396,7 +396,6 @@ bb3: // // 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]] @@ -404,7 +403,6 @@ bb3: // 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]]