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]]