mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Optimizer: add simplifications for destroy_value
Attempt to optimize by forwarding the destroy to operands of forwarding instructions. ``` %3 = struct $S (%1, %2) destroy_value %3 // the only use of %3 ``` -> ``` destroy_value %1 destroy_value %2 ``` The benefit of this transformation is that the forwarding instruction can be removed. Also, handle `destroy_value` for phi arguments. This is a more complex case where the destroyed value comes from different predecessors via a phi argument. The optimization moves the `destroy_value` to each predecessor block. ``` bb1: br bb3(%0) bb2: br bb3(%1) bb3(%3 : @owned T): ... // no deinit-barriers destroy_value %3 // the only use of %3 ``` -> ``` bb1: destroy_value %0 br bb3 bb2: destroy_value %1 br bb3 bb3: ... ```
This commit is contained in:
@@ -14,8 +14,147 @@ import SIL
|
||||
|
||||
extension DestroyValueInst : OnoneSimplifiable, SILCombineSimplifiable {
|
||||
func simplify(_ context: SimplifyContext) {
|
||||
// If the value has `.none` ownership, the destroy is a no-op. Note that a value can have `.none`
|
||||
// ownership even if it's type is not trivial, e.g.
|
||||
//
|
||||
// ```
|
||||
// %1 = enum $NonTrivialEnum, #NonTrivialEnum.trivialCase!enumelt // ownership: none
|
||||
// %2 = destroy_value %1
|
||||
// ```
|
||||
//
|
||||
if destroyedValue.ownership == .none {
|
||||
context.erase(instruction: self)
|
||||
return
|
||||
}
|
||||
|
||||
tryRemoveForwardingOperandInstruction(context)
|
||||
}
|
||||
|
||||
/// Attempt to optimize by forwarding the destroy to operands of forwarding instructions.
|
||||
///
|
||||
/// ```
|
||||
/// %3 = struct $S (%1, %2)
|
||||
/// destroy_value %3 // the only use of %3
|
||||
/// ```
|
||||
/// ->
|
||||
/// ```
|
||||
/// destroy_value %1
|
||||
/// destroy_value %2
|
||||
/// ```
|
||||
///
|
||||
/// The benefit of this transformation is that the forwarding instruction can be removed.
|
||||
///
|
||||
private func tryRemoveForwardingOperandInstruction(_ context: SimplifyContext) {
|
||||
guard context.preserveDebugInfo ? destroyedValue.uses.isSingleUse
|
||||
: destroyedValue.uses.ignoreDebugUses.isSingleUse
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
let destroyedInst: Instruction
|
||||
switch destroyedValue {
|
||||
case is StructInst,
|
||||
is EnumInst:
|
||||
if destroyedValue.type.nominal!.valueTypeDestructor != nil {
|
||||
// Moving the destroy to a non-copyable struct/enum's operands would drop the deinit call!
|
||||
return
|
||||
}
|
||||
destroyedInst = destroyedValue as! SingleValueInstruction
|
||||
|
||||
// Handle various "forwarding" instructions that simply pass through values
|
||||
// without performing operations that would affect destruction semantics.
|
||||
//
|
||||
// We are intentionally _not_ handling `unchecked_enum_data`, because that would not necessarily be
|
||||
// a simplification, because destroying the whole enum is more effort than to destroy an enum payload.
|
||||
// We are also not handling `destructure_struct` and `destructure_tuple`. That would end up in
|
||||
// an infinite simplification loop in MandatoryPerformanceOptimizations because there we "split" such
|
||||
// destroys again when de-virtualizing deinits of non-copyable types.
|
||||
//
|
||||
case is TupleInst,
|
||||
is RefToBridgeObjectInst,
|
||||
is ConvertFunctionInst,
|
||||
is ThinToThickFunctionInst,
|
||||
is UpcastInst,
|
||||
is UncheckedRefCastInst,
|
||||
is UnconditionalCheckedCastInst,
|
||||
is BridgeObjectToRefInst,
|
||||
is InitExistentialRefInst,
|
||||
is OpenExistentialRefInst:
|
||||
destroyedInst = destroyedValue as! SingleValueInstruction
|
||||
|
||||
case let arg as Argument:
|
||||
tryRemovePhiArgument(arg, context)
|
||||
return
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
let builder = Builder(before: self, context)
|
||||
for op in destroyedInst.definedOperands where op.value.ownership == .owned {
|
||||
builder.createDestroyValue(operand: op.value)
|
||||
}
|
||||
|
||||
// Users include `debug_value` instructions and this `destroy_value`
|
||||
context.erase(instructionIncludingAllUsers: destroyedInst)
|
||||
}
|
||||
|
||||
/// Handles the optimization of `destroy_value` instructions for phi arguments.
|
||||
/// This is a more complex case where the destroyed value comes from different predecessors
|
||||
/// via a phi argument. The optimization moves the `destroy_value` to each predecessor block.
|
||||
///
|
||||
/// ```
|
||||
/// bb1:
|
||||
/// br bb3(%0)
|
||||
/// bb2:
|
||||
/// br bb3(%1)
|
||||
/// bb3(%3 : @owned T):
|
||||
/// ... // no deinit-barriers
|
||||
/// destroy_value %3 // the only use of %3
|
||||
/// ```
|
||||
/// ->
|
||||
/// ```
|
||||
/// bb1:
|
||||
/// destroy_value %0
|
||||
/// br bb3
|
||||
/// bb2:
|
||||
/// destroy_value %1
|
||||
/// br bb3
|
||||
/// bb3:
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
private func tryRemovePhiArgument(_ arg: Argument, _ context: SimplifyContext) {
|
||||
guard let phi = Phi(arg),
|
||||
arg.parentBlock == parentBlock,
|
||||
!isDeinitBarrierInBlock(before: self, context)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
for incomingOp in phi.incomingOperands {
|
||||
let oldBranch = incomingOp.instruction as! BranchInst
|
||||
let builder = Builder(before: oldBranch, context)
|
||||
builder.createDestroyValue(operand: incomingOp.value)
|
||||
builder.createBranch(to: parentBlock, arguments: oldBranch.arguments(excluding: incomingOp))
|
||||
context.erase(instruction: oldBranch)
|
||||
}
|
||||
|
||||
// Users of `arg` include `debug_value` instructions and this `destroy_value`
|
||||
context.erase(instructions: arg.uses.users)
|
||||
|
||||
arg.parentBlock.eraseArgument(at: arg.index, context)
|
||||
}
|
||||
}
|
||||
|
||||
private func isDeinitBarrierInBlock(before instruction: Instruction, _ context: SimplifyContext) -> Bool {
|
||||
return ReverseInstructionList(first: instruction.previous).contains(where: {
|
||||
$0.isDeinitBarrier(context.calleeAnalysis)
|
||||
})
|
||||
}
|
||||
|
||||
private extension BranchInst {
|
||||
func arguments(excluding excludeOp: Operand) -> [Value] {
|
||||
return Array(operands.filter{ $0 != excludeOp }.values)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,11 +140,8 @@ bb0(%0 : @owned $Klass):
|
||||
return %2 : $Klass
|
||||
}
|
||||
|
||||
// We get ARC traffic here today since we do not get rid of PhiArguments kept
|
||||
// alive only by destroys/end_borrows. We will eventually though.
|
||||
//
|
||||
// CHECK-LABEL: sil [ossa] @owned_to_owned_consuming : $@convention(thin) (@owned FakeOptional<Klass>) -> () {
|
||||
// CHECK: copy_value
|
||||
// CHECK-NOT: copy_value
|
||||
// CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt
|
||||
// CHECK: } // end sil function 'owned_to_owned_consuming'
|
||||
sil [ossa] @owned_to_owned_consuming : $@convention(thin) (@owned FakeOptional<Klass>) -> () {
|
||||
|
||||
@@ -36,9 +36,12 @@ public class MyGizmo {
|
||||
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvsToTembnn_
|
||||
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Gizmo) -> ()
|
||||
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Gizmo) -> ()
|
||||
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_
|
||||
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
|
||||
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
|
||||
|
||||
// TODO: check why this code is not outlined
|
||||
|
||||
// xCHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_
|
||||
// xCHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
|
||||
// xCHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
|
||||
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_
|
||||
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@guaranteed Array<String>, Gizmo) -> @owned Optional<AnyObject>
|
||||
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembnn_
|
||||
@@ -126,32 +129,32 @@ public func testOutlining() {
|
||||
// CHECK: return %7 : $()
|
||||
// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvsToTembnn_'
|
||||
|
||||
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
|
||||
// CHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional<AnyObject>, %3 : $Gizmo):
|
||||
// CHECK: %4 = objc_method %3 : $Gizmo, #Gizmo.modifyString!foreign : (Gizmo) -> (String?, Int, Any?) -> String?
|
||||
// CHECK: %5 = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
|
||||
// CHECK: %6 = apply %5(%0) : $@convention(method) (@guaranteed String) -> @owned NSString
|
||||
// CHECK: release_value %0 : $String
|
||||
// CHECK: %8 = enum $Optional<NSString>, #Optional.some!enumelt, %6 : $NSString
|
||||
// CHECK: %9 = apply %4(%8, %1, %2, %3) : $@convention(objc_method) (Optional<NSString>, Int, Optional<AnyObject>, Gizmo) -> @autoreleased Optional<NSString>
|
||||
// CHECK: strong_release %6 : $NSString
|
||||
// CHECK: switch_enum %9 : $Optional<NSString>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1
|
||||
// xCHECK-LABEL: sil shared [noinline] @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
|
||||
// xCHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional<AnyObject>, %3 : $Gizmo):
|
||||
// xCHECK: %4 = objc_method %3 : $Gizmo, #Gizmo.modifyString!foreign : (Gizmo) -> (String?, Int, Any?) -> String?
|
||||
// xCHECK: %5 = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
|
||||
// xCHECK: %6 = apply %5(%0) : $@convention(method) (@guaranteed String) -> @owned NSString
|
||||
// xCHECK: release_value %0 : $String
|
||||
// xCHECK: %8 = enum $Optional<NSString>, #Optional.some!enumelt, %6 : $NSString
|
||||
// xCHECK: %9 = apply %4(%8, %1, %2, %3) : $@convention(objc_method) (Optional<NSString>, Int, Optional<AnyObject>, Gizmo) -> @autoreleased Optional<NSString>
|
||||
// xCHECK: strong_release %6 : $NSString
|
||||
// xCHECK: switch_enum %9 : $Optional<NSString>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1
|
||||
//
|
||||
// CHECK: bb1:
|
||||
// CHECK: %12 = enum $Optional<String>, #Optional.none!enumelt
|
||||
// CHECK: br bb3(%12 : $Optional<String>)
|
||||
// xCHECK: bb1:
|
||||
// xCHECK: %14 = enum $Optional<String>, #Optional.none!enumelt
|
||||
// xCHECK: br bb3(%14 : $Optional<String>)
|
||||
//
|
||||
// CHECK: bb2(%14 : $NSString):
|
||||
// CHECK: %15 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
|
||||
// CHECK: %16 = metatype $@thin String.Type
|
||||
// CHECK: %17 = apply %15(%9, %16) : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
|
||||
// CHECK: release_value %9 : $Optional<NSString>
|
||||
// CHECK: %19 = enum $Optional<String>, #Optional.some!enumelt, %17 : $String
|
||||
// CHECK: br bb3(%19 : $Optional<String>)
|
||||
// xCHECK: bb2(%16 : $NSString):
|
||||
// xCHECK: %18 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
|
||||
// xCHECK: %19 = metatype $@thin String.Type
|
||||
// xCHECK: %20 = apply %18(%9, %19) : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
|
||||
// xCHECK: release_value %9 : $Optional<NSString>
|
||||
// xCHECK: %22 = enum $Optional<String>, #Optional.some!enumelt, %20 : $String
|
||||
// xCHECK: br bb3(%22 : $Optional<String>)
|
||||
//
|
||||
// CHECK: bb3(%21 : $Optional<String>):
|
||||
// CHECK: return %21 : $Optional<String>
|
||||
// CHECK: } // end sil function '$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_'
|
||||
// xCHECK: bb3(%24 : $Optional<String>):
|
||||
// xCHECK: return %24 : $Optional<String>
|
||||
// xCHECK: } // end sil function '$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_'
|
||||
|
||||
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_ : $@convention(thin) (@guaranteed Array<String>, Gizmo) -> @owned Optional<AnyObject> {
|
||||
// CHECK: bb0(%0 : $Array<String>, %1 : $Gizmo):
|
||||
|
||||
@@ -77,8 +77,7 @@ bb0(%0 : @guaranteed $Klass):
|
||||
// CHECK-LABEL: sil [ossa] @ref_to_raw_pointer_unchecked_ref_cast_composition_ossa_owned : $@convention(thin) (@owned Klass) -> Builtin.RawPointer
|
||||
// CHECK: bb0([[ARG:%.*]] : @owned
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = ref_to_raw_pointer [[ARG]]
|
||||
// CHECK-NEXT: [[CAST:%.*]] = unchecked_ref_cast [[ARG]]
|
||||
// CHECK-NEXT: destroy_value [[CAST]]
|
||||
// CHECK-NEXT: destroy_value [[ARG]]
|
||||
// CHECK-NEXT: return [[RESULT]]
|
||||
// CHECK: } // end sil function 'ref_to_raw_pointer_unchecked_ref_cast_composition_ossa_owned'
|
||||
sil [ossa] @ref_to_raw_pointer_unchecked_ref_cast_composition_ossa_owned : $@convention(thin) (@owned Klass) -> Builtin.RawPointer {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=destroy_value | %FileCheck %s
|
||||
// RUN: %target-sil-opt -enable-experimental-feature MoveOnlyEnumDeinits %s -onone-simplification -simplify-instruction=destroy_value | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ONONE
|
||||
// RUN: %target-sil-opt -enable-experimental-feature MoveOnlyEnumDeinits %s -simplification -simplify-instruction=destroy_value | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-O
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: swift_feature_MoveOnlyEnumDeinits
|
||||
|
||||
sil_stage canonical
|
||||
|
||||
@@ -8,20 +9,448 @@ import Builtin
|
||||
import Swift
|
||||
import SwiftShims
|
||||
|
||||
sil @cl : $@convention(thin) () -> Int
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_copy :
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: } // end sil function 'remove_copy'
|
||||
sil [ossa] @remove_copy : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = function_ref @cl : $@convention(thin) () -> Int
|
||||
%1 = thin_to_thick_function %0 : $@convention(thin) () -> Int to $@callee_guaranteed () -> Int
|
||||
destroy_value %1 : $@callee_guaranteed () -> Int
|
||||
%4 = tuple ()
|
||||
return %4 : $()
|
||||
struct S {
|
||||
var a: C
|
||||
var b: C
|
||||
var c: Int
|
||||
}
|
||||
|
||||
struct S2 {
|
||||
var a: C
|
||||
}
|
||||
|
||||
struct NCS: ~Copyable {
|
||||
var a: C
|
||||
deinit
|
||||
}
|
||||
|
||||
enum E {
|
||||
case A(Int)
|
||||
case B(C)
|
||||
}
|
||||
|
||||
enum NCE: ~Copyable {
|
||||
case A(Int)
|
||||
case B(C)
|
||||
deinit
|
||||
}
|
||||
|
||||
protocol P: C {}
|
||||
class C: P {}
|
||||
class D: C {}
|
||||
|
||||
sil @cl : $@convention(thin) () -> Int
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_destroy :
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: } // end sil function 'remove_destroy'
|
||||
sil [ossa] @remove_destroy : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = function_ref @cl : $@convention(thin) () -> Int
|
||||
%1 = thin_to_thick_function %0 to $@callee_guaranteed () -> Int
|
||||
destroy_value %1
|
||||
%4 = tuple ()
|
||||
return %4
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_struct :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK-NEXT: return %2
|
||||
// CHECK: } // end sil function 'remove_struct'
|
||||
sil [ossa] @remove_struct : $@convention(thin) (@owned C, @owned C, Int) -> Int {
|
||||
bb0(%0 : @owned $C, %1 : @owned $C, %2 : $Int):
|
||||
%3 = struct $S (%0, %1, %2)
|
||||
destroy_value %3
|
||||
return %2
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_tuple :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK-NEXT: return %2
|
||||
// CHECK: } // end sil function 'remove_tuple'
|
||||
sil [ossa] @remove_tuple : $@convention(thin) (@owned C, @owned C, Int) -> Int {
|
||||
bb0(%0 : @owned $C, %1 : @owned $C, %2 : $Int):
|
||||
%3 = tuple (%0, %1, %2)
|
||||
destroy_value %3
|
||||
return %2
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_enum_a :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_enum_a'
|
||||
sil [ossa] @remove_enum_a : $@convention(thin) (Int) -> () {
|
||||
bb0(%0 : $Int):
|
||||
%1 = enum $E, #E.A!enumelt, %0, forwarding: @owned
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_enum_b :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_enum_b'
|
||||
sil [ossa] @remove_enum_b : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = enum $E, #E.B!enumelt, %0
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_ref_to_bridged :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_ref_to_bridged'
|
||||
sil [ossa] @remove_ref_to_bridged : $@convention(thin) (@owned C, Builtin.Word) -> () {
|
||||
bb0(%0 : @owned $C, %1 : $Builtin.Word):
|
||||
%2 = ref_to_bridge_object %0, %1
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_bridged_to_ref :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_bridged_to_ref'
|
||||
sil [ossa] @remove_bridged_to_ref : $@convention(thin) (@owned Builtin.BridgeObject) -> () {
|
||||
bb0(%0 : @owned $Builtin.BridgeObject):
|
||||
%1 = bridge_object_to_ref %0 to $C
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_convert_function :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_convert_function'
|
||||
sil [ossa] @remove_convert_function : $@convention(thin) (@owned @convention(thick) (C) -> ()) -> () {
|
||||
bb0(%0 : @owned $@convention(thick) (C) -> ()):
|
||||
%1 = convert_function %0 to $@convention(thick) (D) -> ()
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_thin_to_thick_function :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_thin_to_thick_function'
|
||||
sil [ossa] @remove_thin_to_thick_function : $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = function_ref @cl : $@convention(thin) () -> Int
|
||||
%1 = thin_to_thick_function %0 to $@callee_guaranteed () -> Int, forwarding: @owned
|
||||
destroy_value %1
|
||||
%4 = tuple ()
|
||||
return %4
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_upcast :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_upcast'
|
||||
sil [ossa] @remove_upcast : $@convention(thin) (@owned D) -> () {
|
||||
bb0(%0 : @owned $D):
|
||||
%1 = upcast %0 to $C
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_unchecked_ref_cast :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_unchecked_ref_cast'
|
||||
sil [ossa] @remove_unchecked_ref_cast : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = unchecked_ref_cast %0 to $D
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_unconditional_checked_cast :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_unconditional_checked_cast'
|
||||
sil [ossa] @remove_unconditional_checked_cast : $@convention(thin) (@owned any P) -> () {
|
||||
bb0(%0 : @owned $any P):
|
||||
%1 = unconditional_checked_cast %0 to C
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_init_existential_ref :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_init_existential_ref'
|
||||
sil [ossa] @remove_init_existential_ref : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = init_existential_ref %0 : $C : $C, $P
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @remove_open_existential_ref :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'remove_open_existential_ref'
|
||||
sil [ossa] @remove_open_existential_ref : $@convention(thin) (@owned any P) -> () {
|
||||
bb0(%0 : @owned $any P):
|
||||
%1 = open_existential_ref %0 to $@opened("01234567-89ab-cdef-0123-111111111111", P) Self
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// Doing this wouldn't be a simplification
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_unchecked_enum_data :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: %1 = unchecked_enum_data
|
||||
// CHECK: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_unchecked_enum_data'
|
||||
sil [ossa] @dont_remove_unchecked_enum_data : $@convention(thin) (@owned E) -> () {
|
||||
bb0(%0 : @owned $E):
|
||||
%1 = unchecked_enum_data %0, #E.B!enumelt
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// Doing this might undo an opposite simplification
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_destructure_struct :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: %1 = destructure_struct
|
||||
// CHECK: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_destructure_struct'
|
||||
sil [ossa] @dont_remove_destructure_struct : $@convention(thin) (@owned S2) -> () {
|
||||
bb0(%0 : @owned $S2):
|
||||
%1 = destructure_struct %0
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// Doing this might undo an opposite simplification
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_destructure_tuple :
|
||||
// CHECK: bb0
|
||||
// CHECK: %2 = destructure_tuple
|
||||
// CHECK: destroy_value %2
|
||||
// CHECK: } // end sil function 'dont_remove_destructure_tuple'
|
||||
sil [ossa] @dont_remove_destructure_tuple : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = tuple (%0)
|
||||
%2 = destructure_tuple %1
|
||||
destroy_value %2
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_struct_with_deinit :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: %1 = struct $NCS
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_struct_with_deinit'
|
||||
sil [ossa] @dont_remove_struct_with_deinit : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = struct $NCS (%0)
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_enum_with_deinit :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: %1 = enum $NCE
|
||||
// CHECK-NEXT: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_enum_with_deinit'
|
||||
sil [ossa] @dont_remove_enum_with_deinit : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
%1 = enum $NCE, #NCE.B!enumelt, %0
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_with_additional_user :
|
||||
// CHECK: bb0
|
||||
// CHECK-NEXT: %1 = upcast
|
||||
// CHECK: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_with_additional_user'
|
||||
sil [ossa] @dont_remove_with_additional_user : $@convention(thin) (@owned D) -> () {
|
||||
bb0(%0 : @owned $D):
|
||||
%1 = upcast %0 to $C
|
||||
%2 = begin_borrow %1
|
||||
fix_lifetime %2
|
||||
end_borrow %2
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_remove_with_debug_user :
|
||||
// CHECK: bb0
|
||||
// CHECK-O-NEXT: destroy_value %0
|
||||
// CHECK-O-NEXT: tuple
|
||||
// CHECK-ONONE-NEXT: %1 = upcast
|
||||
// CHECK-ONONE-NEXT: debug_value %1
|
||||
// CHECK-ONONE-NEXT: destroy_value %1
|
||||
// CHECK: } // end sil function 'dont_remove_with_debug_user'
|
||||
sil [ossa] @dont_remove_with_debug_user : $@convention(thin) (@owned D) -> () {
|
||||
bb0(%0 : @owned $D):
|
||||
%1 = upcast %0 to $C
|
||||
debug_value %1, let, name "x"
|
||||
destroy_value %1
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @hoist_over_phi :
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: br bb3
|
||||
// CHECK: bb2:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: br bb3
|
||||
// CHECK: bb3:
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'hoist_over_phi'
|
||||
sil [ossa] @hoist_over_phi : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
cond_br undef, bb1, bb2
|
||||
|
||||
bb1:
|
||||
br bb3(%0)
|
||||
|
||||
bb2:
|
||||
br bb3(%0)
|
||||
|
||||
bb3(%4 : @owned $C):
|
||||
destroy_value %4
|
||||
%10 = tuple ()
|
||||
return %10
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @hoist_over_phi_with_additional_args :
|
||||
// CHECK: bb1:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: br bb3(%1, %2)
|
||||
// CHECK: bb2:
|
||||
// CHECK-NEXT: destroy_value %0
|
||||
// CHECK-NEXT: br bb3(%1, %2)
|
||||
// CHECK: bb3({{%[0-9]+}} : $Int, {{%[0-9]+}} : $Int):
|
||||
// CHECK-NEXT: debug_step
|
||||
// CHECK-NEXT: tuple
|
||||
// CHECK: } // end sil function 'hoist_over_phi_with_additional_args'
|
||||
sil [ossa] @hoist_over_phi_with_additional_args : $@convention(thin) (@owned C, Int, Int) -> () {
|
||||
bb0(%0 : @owned $C, %1 : $Int, %2 : $Int):
|
||||
cond_br undef, bb1, bb2
|
||||
|
||||
bb1:
|
||||
br bb3(%1, %0, %2)
|
||||
|
||||
bb2:
|
||||
br bb3(%1, %0, %2)
|
||||
|
||||
bb3(%6 : $Int, %7 : @owned $C, %8 : $Int):
|
||||
debug_step // not a deinit barrier
|
||||
destroy_value %7
|
||||
%10 = tuple ()
|
||||
return %10
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_hoist_over_non_phi_arg :
|
||||
// CHECK: bb2([[A:%.*]] : @owned $C):
|
||||
// CHECK-NEXT: destroy_value [[A]]
|
||||
// CHECK-NEXT: br bb3
|
||||
// CHECK: } // end sil function 'dont_hoist_over_non_phi_arg'
|
||||
sil [ossa] @dont_hoist_over_non_phi_arg : $@convention(thin) (@owned E) -> () {
|
||||
bb0(%0 : @owned $E):
|
||||
switch_enum %0, case #E.A!enumelt: bb1, case #E.B!enumelt: bb2
|
||||
|
||||
bb1(%2 : $Int):
|
||||
br bb3
|
||||
|
||||
bb2(%4 : @owned $C):
|
||||
destroy_value %4
|
||||
br bb3
|
||||
|
||||
bb3:
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_hoist_over_deinit_barrier :
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: bb3([[A:%.*]] : @owned $C):
|
||||
// CHECK-NEXT: apply
|
||||
// CHECK-NEXT: destroy_value [[A]]
|
||||
// CHECK: } // end sil function 'dont_hoist_over_deinit_barrier'
|
||||
sil [ossa] @dont_hoist_over_deinit_barrier : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
cond_br undef, bb1, bb2
|
||||
|
||||
bb1:
|
||||
br bb3(%0)
|
||||
|
||||
bb2:
|
||||
br bb3(%0)
|
||||
|
||||
bb3(%4 : @owned $C):
|
||||
apply undef() : $() -> ()
|
||||
destroy_value %4
|
||||
%10 = tuple ()
|
||||
return %10
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil [ossa] @dont_hoist_destroy_in_different_block :
|
||||
// CHECK-NOT: destroy_value
|
||||
// CHECK: bb4:
|
||||
// CHECK-NEXT: destroy_value
|
||||
// CHECK: bb5:
|
||||
// CHECK-NEXT: destroy_value
|
||||
// CHECK: } // end sil function 'dont_hoist_destroy_in_different_block'
|
||||
sil [ossa] @dont_hoist_destroy_in_different_block : $@convention(thin) (@owned C) -> () {
|
||||
bb0(%0 : @owned $C):
|
||||
cond_br undef, bb1, bb2
|
||||
|
||||
bb1:
|
||||
br bb3(%0)
|
||||
|
||||
bb2:
|
||||
br bb3(%0)
|
||||
|
||||
bb3(%4 : @owned $C):
|
||||
cond_br undef, bb4, bb5
|
||||
|
||||
bb4:
|
||||
destroy_value %4
|
||||
br bb6
|
||||
|
||||
bb5:
|
||||
destroy_value %4
|
||||
br bb6
|
||||
|
||||
bb6:
|
||||
%r = tuple ()
|
||||
return %r
|
||||
}
|
||||
|
||||
|
||||
@@ -19,47 +19,47 @@
|
||||
|
||||
/// Public import or non-resilient modules
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -swift-version 5 -enable-library-evolution -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-public.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-public.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-internal.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
|
||||
/// Foundation is imported from a different file
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-not-imported-fileA.swift \
|
||||
// RUN: %t/inlinable-not-imported-fileB.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: -swift-version 5 -enable-library-evolution -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-not-imported-fileA.swift \
|
||||
// RUN: %t/inlinable-not-imported-fileB.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
|
||||
/// Foundation is imported via a transitive dependency
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-imported-transitive.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
|
||||
/// Any non-inlinable uses
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/non-inlinable-public.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/non-inlinable-ioi.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/non-inlinable-internal.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/non-inlinable-not-imported-fileA.swift \
|
||||
// RUN: %t/non-inlinable-not-imported-fileB.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -module-name main -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: %t/non-inlinable-not-imported-fileA.swift \
|
||||
// RUN: %t/non-inlinable-not-imported-fileB.swift > %t/main.sil
|
||||
@@ -69,16 +69,16 @@
|
||||
// CHECK-NOT-OPTIMIZED-NOT: $NSError
|
||||
|
||||
/// Implementation-only import
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: %t/inlinable-ioi.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-NOT-OPTIMIZED --input-file %t/main.sil %s
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: %t/inlinable-ioi.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-NOT-OPTIMIZED --input-file %t/main.sil %s
|
||||
|
||||
/// Internal import from resilient module
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t \
|
||||
// RUN: %target-swift-frontend -emit-module -emit-sil -I%t -Xllvm -sil-disable-pass=simplify-destroy_value \
|
||||
// RUN: -swift-version 5 -enable-library-evolution \
|
||||
// RUN: %t/inlinable-internal.swift > %t/main.sil
|
||||
// RUN: %FileCheck --check-prefix CHECK-NOT-OPTIMIZED --input-file %t/main.sil %s
|
||||
|
||||
Reference in New Issue
Block a user