SimplifyCFG: insert compensating end_lifetime when replacing a switch_enum

This is a follow-up of https://github.com/swiftlang/swift/pull/84905, which handles non-copyable enums with a deinit correctly.
Also, for copyable enums it's more efficient to use `end_lifetime` than `destroy_value`, because we already know that the enum contains a trivial case.
Therefore no destroy operation is needed.
This commit is contained in:
Erik Eckstein
2025-10-20 15:56:20 +02:00
parent ffc71e95e8
commit c4c1c50e73
2 changed files with 35 additions and 6 deletions

View File

@@ -1793,7 +1793,8 @@ bool SimplifyCFG::simplifySwitchEnumUnreachableBlocks(SwitchEnumInst *SEI) {
if (Dest->args_empty()) {
SILBuilderWithScope builder(SEI);
if (SEI->getOperand()->getOwnershipKind() == OwnershipKind::Owned) {
builder.createDestroyValue(SEI->getLoc(), SEI->getOperand());
// Note that a `destroy_value` would be wrong for non-copyable enums with deinits.
builder.createEndLifetime(SEI->getLoc(), SEI->getOperand());
}
builder.createBranch(SEI->getLoc(), Dest);

View File

@@ -1,4 +1,6 @@
// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s
// RUN: %target-sil-opt -sil-print-types -enable-objc-interop -enable-experimental-feature MoveOnlyEnumDeinits -enable-sil-verify-all %s -simplify-cfg | %FileCheck %s
// REQUIRES: swift_feature_MoveOnlyEnumDeinits
// OSSA form of tests from simplify_cfg.sil and simplify_cfg_simple.sil.
//
@@ -64,6 +66,12 @@ struct S {
var b: NonTrivial
}
enum NCE: ~Copyable {
case a
case b(AnyObject)
deinit
}
///////////
// Tests //
///////////
@@ -1944,12 +1952,12 @@ bb3:
return %t : $()
}
// CHECK-LABEL: sil [ossa] @insert_compensating_destroy_in_switch_enum_destination_block :
// CHECK-LABEL: sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block :
// CHECK: bb0(%0 : @owned $Optional<AnyObject>):
// CHECK-NEXT: destroy_value %0
// CHECK-NEXT: end_lifetime %0
// CHECK-NEXT: tuple
// CHECK: } // end sil function 'insert_compensating_destroy_in_switch_enum_destination_block'
sil [ossa] @insert_compensating_destroy_in_switch_enum_destination_block : $@convention(thin) (@owned Optional<AnyObject>) -> () {
// CHECK: } // end sil function 'insert_compensating_endlifetime_in_switch_enum_destination_block'
sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block : $@convention(thin) (@owned Optional<AnyObject>) -> () {
bb0(%0 : @owned $Optional<AnyObject>):
switch_enum %0, case #Optional.none!enumelt: bb1, case #Optional.some!enumelt: bb2
@@ -1962,6 +1970,26 @@ bb2(%4 : @owned $AnyObject):
unreachable
}
// CHECK-LABEL: sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block2 :
// CHECK: bb0(%0 : @owned $NCE):
// CHECK-NEXT: %1 = drop_deinit %0
// CHECK-NEXT: end_lifetime %1
// CHECK-NEXT: tuple
// CHECK: } // end sil function 'insert_compensating_endlifetime_in_switch_enum_destination_block2'
sil [ossa] @insert_compensating_endlifetime_in_switch_enum_destination_block2 : $@convention(thin) (@owned NCE) -> () {
bb0(%0 : @owned $NCE):
%1 = drop_deinit %0
switch_enum %1, case #NCE.a!enumelt: bb1, case #NCE.b!enumelt: bb2
bb1:
%15 = tuple ()
return %15
bb2(%4 : @owned $AnyObject):
destroy_value %4
unreachable
}
// CHECK-LABEL: sil [ossa] @replace_phi_arg_with_borrowed_from_use :
// CHECK: bb3([[R:%.*]] : @reborrow $B):
// CHECK: bb6([[G:%.*]] : @guaranteed $E):