Files
swift-mirror/test/SILOptimizer/allocbox_to_stack_ownership.sil
Erik Eckstein c790052590 AllocBoxToStack: convert access checks from "dynamic" to "static"
Once we have promoted the box to stack, access violations can be detected statically by the DiagnoseStaticExclusivity pass (which runs after MandatoryAllocBoxToStack).
Therefore we can convert dynamic accesses to static accesses.

rdar://157458037
2025-08-30 07:29:15 +02:00

1454 lines
53 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types %s -allocbox-to-stack | %FileCheck %s --check-prefix=CHECK --check-prefix=OPT
// RUN: %target-sil-opt -sil-print-types %s -mandatory-allocbox-to-stack | %FileCheck %s --check-prefix=CHECK --check-prefix=MANDATORY
sil_stage raw
import Builtin
struct Int {
var _value: Builtin.Int64
}
struct Bool {
var _value: Builtin.Int1
}
protocol Error {}
class C {}
// CHECK-LABEL: sil [ossa] @simple_promotion
sil [ossa] @simple_promotion : $@convention(thin) (Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
return %3 : $Int
// CHECK: alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: destroy_value
// CHECK: return
}
// CHECK-LABEL: sil [ossa] @double_project_box
sil [ossa] @double_project_box : $@convention(thin) (Int) -> (Int, Int) {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
%1b = project_box %1 : ${ var Int }, 0
%3b = load [trivial] %1b : $*Int
destroy_value %1 : ${ var Int }
%r = tuple (%3 : $Int, %3b : $Int)
return %r : $(Int, Int)
// CHECK: alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: project_box
// CHECK-NOT: destroy_value
// CHECK: return
}
// CHECK-LABEL: sil [ossa] @init_var
sil [ossa] @init_var : $@convention(thin) (Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
return %3 : $Int
// CHECK: %1 = alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: destroy_value
// CHECK-NOT: destroy_addr
// CHECK: dealloc_stack %1 : $*Int
// CHECK: return
}
// CHECK-LABEL: sil [ossa] @multi_destroy_value
sil [ossa] @multi_destroy_value : $@convention(thin) () -> Int {
bb0:
%1 = alloc_box ${ var Int }
%1a = mark_uninitialized [rootself] %1 : ${ var Int }
%2 = project_box %1a : ${ var Int }, 0
%3 = load [trivial] %2 : $*Int
%x = copy_value %1a : ${ var Int }
destroy_value %1a : ${ var Int }
br bb1
bb1:
destroy_value %x : ${ var Int }
return %3 : $Int
// CHECK: %0 = alloc_stack
// CHECK: bb1:
// CHECK: dealloc_stack %0 : $*Int
// CHECK: return
}
struct TestStruct {
var Elt : Int
}
// CHECK-LABEL: sil [ossa] @struct_tuple_element_addr
sil [ossa] @struct_tuple_element_addr : $@convention(thin) (Int) -> Int {
bb1(%0 : $Int):
// CHECK-DAG: [[STRUCT:%.*]] = alloc_stack $TestStruct
// CHECK-DAG: [[TUPLE:%.*]] = alloc_stack $(Int, Int)
%1 = alloc_box ${ var TestStruct }
%1a = project_box %1 : ${ var TestStruct }, 0
%a = alloc_box ${ var (Int, Int) }
%aa = project_box %a : ${ var (Int, Int) }, 0
%2 = struct_element_addr %1a : $*TestStruct, #TestStruct.Elt
store %0 to [trivial] %2 : $*Int
%b = tuple_element_addr %aa : $*(Int, Int), 0
store %0 to [trivial] %b : $*Int
%6 = struct_element_addr %1a : $*TestStruct, #TestStruct.Elt
%7 = load [trivial] %6 : $*Int
destroy_value %a : ${ var (Int, Int) }
destroy_value %1 : ${ var TestStruct }
return %7 : $Int
// CHECK-DAG: dealloc_stack [[STRUCT]]
// CHECK-DAG: dealloc_stack [[TUPLE]]
// CHECK: return
}
sil [ossa] @callee : $@convention(thin) (@inout Int) -> ()
// CHECK-LABEL: sil [ossa] @escaping_pointer
sil [ossa] @escaping_pointer : $@convention(thin) () -> Int {
bb0:
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
%6 = function_ref @callee : $@convention(thin) (@inout Int) -> ()
%7 = apply %6(%1a) : $@convention(thin) (@inout Int) -> ()
%8 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%10 = address_to_pointer %1a : $*Int to $Builtin.RawPointer
%11 = pointer_to_address %10 : $Builtin.RawPointer to [strict] $*Int
%12 = load [trivial] %11 : $*Int
return %8 : $Int
// CHECK: return
}
protocol P {
}
sil [ossa] @returns_protocol : $@convention(thin) () -> @out P
// CHECK-LABEL: sil [ossa] @test_indirect_return
sil [ossa] @test_indirect_return : $@convention(thin) () -> () {
bb0:
// CHECK: alloc_stack
%1 = function_ref @returns_protocol : $@convention(thin) () -> @out P
%2 = alloc_box ${ var P }
%2a = project_box %2 : ${ var P }, 0
%3 = apply %1(%2a) : $@convention(thin) () -> @out P
destroy_value %2 : ${ var P }
%0 = tuple ()
return %0 : $()
// CHECK: return
}
class SomeClass {}
// CHECK-LABEL: sil [ossa] @class_promotion
sil [ossa] @class_promotion : $@convention(thin) (@owned SomeClass) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass):
%1 = alloc_box ${ var SomeClass }
%1a = project_box %1 : ${ var SomeClass }, 0
store %0 to [init] %1a : $*SomeClass
%3 = load [copy] %1a : $*SomeClass
destroy_value %1 : ${ var SomeClass }
return %3 : $SomeClass
// CHECK: %1 = alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: destroy_value
// CHECK: destroy_addr {{.*}} : $*SomeClass
// CHECK: return
}
protocol LogicValue {
func getLogicValue() -> Bool
}
// CHECK-LABEL: @protocols
sil [ossa] @protocols : $@convention(thin) (@in LogicValue, @thin Bool.Type) -> Bool {
bb0(%0 : $*LogicValue, %1 : $@thin Bool.Type):
%2 = alloc_box ${ var LogicValue }
%2a = project_box %2 : ${ var LogicValue }, 0
// CHECK: %2 = alloc_stack $any LogicValue
copy_addr [take] %0 to [init] %2a : $*LogicValue
%6 = open_existential_addr mutable_access %2a : $*LogicValue to $*@opened("01234567-89ab-cdef-0123-000000000000", LogicValue) Self
%7 = witness_method $@opened("01234567-89ab-cdef-0123-000000000000", LogicValue) Self, #LogicValue.getLogicValue, %6 : $*@opened("01234567-89ab-cdef-0123-000000000000", LogicValue) Self : $@convention(witness_method: LogicValue) <T: LogicValue> (@in_guaranteed T) -> Bool
%8 = apply %7<@opened("01234567-89ab-cdef-0123-000000000000", LogicValue) Self>(%6) : $@convention(witness_method: LogicValue) <T: LogicValue> (@in_guaranteed T) -> Bool
destroy_value %2 : ${ var LogicValue }
// CHECK: destroy_addr %2 : $*any LogicValue
// CHECK-NEXT: dealloc_stack %2 : $*any LogicValue
// CHECK-NEXT: return
return %8 : $Bool
}
// Generics test, which is address-only.
class Generic<T> {}
// CHECK-LABEL: sil [ossa] @dealloc_box_test
sil [ossa] @dealloc_box_test : $@convention(thin) <T> () -> () {
bb0: // CHECK-NEXT: bb0:
// CHECK-NEXT: alloc_stack
%1 = alloc_box $<τ_0_0> { var Generic<τ_0_0> } <T>
dealloc_box %1 : $<τ_0_0> { var Generic<τ_0_0> } <T>
%0 = tuple () // CHECK: tuple ()
return %0 : $()
// CHECK: return
}
enum SomeUnion {
case x(Int)
case y(SomeClass)
}
sil [ossa] @$s1t9SomeUnionO1yfMS0_FCS_9SomeClassS0_ : $@convention(thin) (@owned SomeClass, @thin SomeUnion.Type) -> @owned SomeUnion
sil [ossa] @$s1t9SomeClassCCfMS0_FT_S0_ : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass
// CHECK-LABEL: sil [ossa] @union_test
sil [ossa] @union_test : $@convention(thin) () -> () {
bb0:
// CHECK: [[UNION:%.*]] = alloc_stack
%1 = alloc_box ${ var SomeUnion }
%1a = project_box %1 : ${ var SomeUnion }, 0
%2 = function_ref @$s1t9SomeUnionO1yfMS0_FCS_9SomeClassS0_ : $@convention(thin) (@owned SomeClass, @thin SomeUnion.Type) -> @owned SomeUnion // user: %7
%3 = metatype $@thin SomeUnion.Type
%4 = function_ref @$s1t9SomeClassCCfMS0_FT_S0_ : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass // user: %6
%5 = metatype $@thick SomeClass.Type
%6 = apply %4(%5) : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass
%7 = apply %2(%6, %3) : $@convention(thin) (@owned SomeClass, @thin SomeUnion.Type) -> @owned SomeUnion
store %7 to [init] %1a : $*SomeUnion
destroy_value %1 : ${ var SomeUnion }
%10 = tuple ()
return %10 : $()
// CHECK: dealloc_stack [[UNION]]
// CHECK: [[T0:%.*]] = tuple ()
// CHECK-NEXT: return [[T0]] : $()
}
// CHECK-LABEL: sil [ossa] @multiple_destroy_test
sil [ossa] @multiple_destroy_test : $@convention(thin) (Bool) -> Bool {
bb0(%0 : $Bool):
%1 = alloc_box ${ var Bool }
%1a = project_box %1 : ${ var Bool }, 0
store %0 to [trivial] %1a : $*Bool
%2 = copy_value %1 : ${ var Bool }
%3 = copy_value %1 : ${ var Bool }
%5 = tuple ()
%6 = load [trivial] %1a : $*Bool
destroy_value %3 : ${ var Bool }
destroy_value %2 : ${ var Bool }
destroy_value %1 : ${ var Bool }
return %6 : $Bool
// CHECK: alloc_stack $Bool
// CHECK-NEXT: store
// CHECK-NEXT: tuple ()
// CHECK-NEXT: load
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: return
}
// Make sure that we can promote this box and dealloc_stack
// on each path.
//
// CHECK-LABEL: sil [ossa] @box_reachable_from_destroy_test
sil [ossa] @box_reachable_from_destroy_test : $@convention(thin) () -> () {
bb0:
br bb1
// CHECK: bb1:
// CHECK-NEXT: alloc_stack
bb1:
%1 = alloc_box ${ var Bool }
cond_br undef, bb2, bb3
// CHECK: bb2:
// CHECK-NEXT: dealloc_stack
bb2:
destroy_value %1 : ${ var Bool }
br bb1
// CHECK: bb3:
// CHECK-NEXT: dealloc_stack
bb3:
destroy_value %1 : ${ var Bool }
%2 = tuple ()
// CHECK: return
return %2 : $()
}
sil [ossa] @useSomeClass : $@convention(thin) (@owned SomeClass) -> ()
// <rdar://problem/16382973> DI misses destroy_addr because allocbox_to_stack isn't preserving mark_uninitialized invariants
// When allocbox_to_stack promotes the box, it should rewrite the multiple
// destroy_values into destroy_addr/dealloc_stack pairs. However, it needs to
// make sure to use the MUI result for the destroy addr.
public protocol My_Incrementable { }
extension Int : My_Incrementable { }
// CHECK-LABEL: sil [ossa] @test_mui
sil [ossa] @test_mui : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
%2 = alloc_box ${ var SomeClass }
%2a = mark_uninitialized [var] %2 : ${ var SomeClass }
%3 = project_box %2a : ${ var SomeClass }, 0
// CHECK: [[STACK:%[0-9]+]] = alloc_stack
// CHECK: [[MUI:%[0-9]+]] = mark_uninitialized
cond_br %0, bb1, bb3
bb1:
%7 = function_ref @$s1t9SomeClassCCfMS0_FT_S0_ : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass // user: %6
%8 = metatype $@thick SomeClass.Type
%9 = apply %7(%8) : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass
assign %9 to %3 : $*SomeClass
%11 = function_ref @useSomeClass : $@convention(thin) (@owned SomeClass) -> ()
%12 = load [copy] %3 : $*SomeClass
%14 = apply %11(%12) : $@convention(thin) (@owned SomeClass) -> ()
destroy_value %2a : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
// CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
// CHECK: bb2
bb2:
%17 = tuple ()
// CHECK: return
return %17 : $()
bb3:
destroy_value %2a : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
// CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
}
// CHECK-LABEL: sil [ossa] @$s6struct5apply1fS2iyc_tF
// struct.apply (f : () -> Swift.Int) -> Swift.Int
sil [ossa] @$s6struct5apply1fS2iyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> Int {
bb0(%0 : @owned $@callee_owned () -> Int):
debug_value %0 : $@callee_owned () -> Int, let, name "f" // id: %1
%1 = copy_value %0 : $@callee_owned () -> Int // id: %2
%3 = apply %1() : $@callee_owned () -> Int // user: %5
destroy_value %0 : $@callee_owned () -> Int // id: %4
return %3 : $Int // id: %5
}
// CHECK-LABEL: sil [ossa] @$s6struct6escape1fSiycSiyc_tF
// struct.escape (f : () -> Swift.Int) -> () -> Swift.Int
sil [ossa] @$s6struct6escape1fSiycSiyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> @owned @callee_owned () -> Int {
bb0(%0 : @owned $@callee_owned () -> Int):
debug_value %0 : $@callee_owned () -> Int, let, name "f" // id: %1
return %0 : $@callee_owned () -> Int // id: %2
}
// CHECK-LABEL: sil [ossa] @$s6struct8useStack1tySi_tF
// struct.useStack (t : Swift.Int) -> ()
sil [ossa] @$s6struct8useStack1tySi_tF : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
debug_value %0 : $Int, let, name "t" // id: %1
// CHECK: alloc_stack
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>, var, name "s" // users: %3, %6, %7, %7, %9
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
store %0 to [trivial] %2a : $*Int // id: %3
// function_ref struct.apply (f : () -> Swift.Int) -> Swift.Int
%4 = function_ref @$s6struct5apply1fS2iyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> Int // user: %8
// CHECK: [[FUNC:%[a-zA-Z0-9]+]] = function_ref @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
// function_ref struct.(useStack (t : Swift.Int) -> ()).(closure #1)
%5 = function_ref @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int // user: %7
%6 = copy_value %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %6
// CHECK: [[PA:%[a-zA-Z0-9]+]] = partial_apply [[FUNC]]
%7 = partial_apply %5(%6) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int // user: %8
%8 = apply %4(%7) : $@convention(thin) (@owned @callee_owned () -> Int) -> Int
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <Int>
%10 = tuple () // user: %11
return %10 : $() // id: %11
}
// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
// OPT-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ :
// MANDATORY-NOT: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ :
sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } <Int>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
// function_ref Swift.++ @postfix <A : Swift._Incrementable>(x : @inout A) -> A
%2 = function_ref @_TFsoP2ppUs14_Incrementable__FT1xRQ__Q_ : $@convention(thin) <τ_0_0 where τ_0_0 : My_Incrementable> (@inout τ_0_0) -> @out τ_0_0 // user: %4
%3 = alloc_stack $Int // users: %4, %5, %6
%4 = apply %2<Int>(%3, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : My_Incrementable> (@inout τ_0_0) -> @out τ_0_0
%5 = load [trivial] %3 : $*Int // user: %8
dealloc_stack %3 : $*Int // id: %6
destroy_value %0 : $<τ_0_0> { var τ_0_0 } <Int> // id: %7
return %5 : $Int // id: %8
}
// Swift.++ @postfix <A : Swift._Incrementable>(x : @inout A) -> A
sil [transparent] @_TFsoP2ppUs14_Incrementable__FT1xRQ__Q_ : $@convention(thin) <τ_0_0 where τ_0_0 : My_Incrementable> (@inout τ_0_0) -> @out τ_0_0
// CHECK-LABEL: sil [ossa] @$s6struct6useBox1tySi_tF
// struct.useBox (t : Swift.Int) -> ()
sil [ossa] @$s6struct6useBox1tySi_tF : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
debug_value %0 : $Int, let, name "t" // id: %1
// CHECK: alloc_box
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>, var, name "s" // users: %3, %6, %7, %7, %10
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
store %0 to [trivial] %2a : $*Int // id: %3
// function_ref struct.escape (f : () -> Swift.Int) -> () -> Swift.Int
%4 = function_ref @$s6struct6escape1fSiycSiyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> @owned @callee_owned () -> Int // user: %8
// function_ref struct.(useBox (t : Swift.Int) -> ()).(closure #1)
%5 = function_ref @$s6struct6useBox1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int // user: %7
%6 = copy_value %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %6
%7 = partial_apply %5(%6) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int // user: %8
%8 = apply %4(%7) : $@convention(thin) (@owned @callee_owned () -> Int) -> @owned @callee_owned () -> Int // user: %9
%9 = apply %8() : $@callee_owned () -> Int
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %10
%11 = tuple () // user: %12
return %11 : $() // id: %12
}
// CHECK-LABEL: sil private [ossa] @$s6struct6useBox1tySi_tFSiycfU_
// struct.(useBox (t : Swift.Int) -> ()).(closure #1)
sil private [ossa] @$s6struct6useBox1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } <Int>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
// function_ref Swift.++ @postfix <A : Swift._Incrementable>(x : @inout A) -> A
%2 = function_ref @_TFsoP2ppUs14_Incrementable__FT1xRQ__Q_ : $@convention(thin) <τ_0_0 where τ_0_0 : My_Incrementable> (@inout τ_0_0) -> @out τ_0_0 // user: %4
%3 = alloc_stack $Int // users: %4, %5, %6
%4 = apply %2<Int>(%3, %1) : $@convention(thin) <τ_0_0 where τ_0_0 : My_Incrementable> (@inout τ_0_0) -> @out τ_0_0
%5 = load [trivial] %3 : $*Int // user: %8
dealloc_stack %3 : $*Int // id: %6
destroy_value %0 : $<τ_0_0> { var τ_0_0 } <Int> // id: %7
return %5 : $Int // id: %8
}
// CHECK-LABEL: sil @closure
sil @closure : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
// CHECK-LABEL: sil [ossa] @no_final_destroy
sil [ossa] @no_final_destroy : $@convention(thin) (Int) -> Bool {
bb0(%0 : $Int):
// This is not destroyed, but the unreachable makes the verifier not trip.
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
store %0 to [trivial] %1a : $*Int
// function_ref main.(newFoo (Swift.Int) -> Swift.Bool).(modify #1) (())()
%3 = function_ref @closure : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
%4 = copy_value %1 : $<τ_0_0> { var τ_0_0 } <Int>
%5 = partial_apply %3(%4) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
%6 = copy_value %5 : $@callee_owned () -> ()
apply %6() : $@callee_owned () -> ()
destroy_value %5 : $@callee_owned () -> ()
unreachable
}
// CHECK-LABEL: sil [ossa] @test_copy_and_borrow_of_closure
// CHECK-NOT: alloc_box
// CHECK: alloc_stack
// CHECK-NOT: alloc_box
// CHECK: } // end sil function 'test_copy_and_borrow_of_closure'
sil [ossa] @test_copy_and_borrow_of_closure : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
// This is not destroyed, but the unreachable makes the verifier not trip.
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
store %0 to [trivial] %1a : $*Int
%3 = function_ref @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
%4 = copy_value %1 : $<τ_0_0> { var τ_0_0 } <Int>
%5 = partial_apply %3(%4) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
%6 = begin_borrow %5 : $@callee_owned () -> ()
%7 = copy_value %6 : $@callee_owned () -> ()
apply %7() : $@callee_owned () -> ()
end_borrow %6 : $@callee_owned () -> ()
destroy_value %5 : $@callee_owned () -> ()
destroy_value %1 : $<τ_0_0> { var τ_0_0 } <Int>
%r = tuple ()
return %r : $()
}
sil [ossa] @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> () {
bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } <Int>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
destroy_value %0 : $<τ_0_0> { var τ_0_0 } <Int> // id: %7
%r = tuple ()
return %r : $()
}
sil [ossa] @closureWithBoxArg : $@convention(thin) (@guaranteed { var SomeClass }) -> () {
bb0(%0 : @guaranteed ${ var SomeClass }):
%r = tuple ()
return %r : $()
}
sil [ossa] @useClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () {
bb0(%0 : @guaranteed $@callee_guaranteed () -> ()):
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @finalReleaseWithBorrow
// CHECK: [[S:%[0-9]+]] = alloc_stack {{.*}}$SomeClass
// CHECK-NOT: destroy_addr
// CHECK: [[F:%[0-9]+]] = function_ref @useClosure
// CHECK: apply [[F]]
// CHECK: destroy_addr [[S]]
// CHECK: } // end sil function 'finalReleaseWithBorrow'
sil [ossa] @finalReleaseWithBorrow : $@convention(thin) (@owned SomeClass) -> () {
bb0(%0 : @owned $SomeClass):
%1 = alloc_box $ { var SomeClass }
%2 = begin_borrow %1 : ${ var SomeClass }
%1a = project_box %2 : ${ var SomeClass }, 0
store %0 to [init] %1a : $*SomeClass
%3 = function_ref @closureWithBoxArg : $@convention(thin) (@guaranteed { var SomeClass }) -> ()
%4 = copy_value %2 : ${ var SomeClass }
%5 = partial_apply [callee_guaranteed] %3(%4) : $@convention(thin) (@guaranteed { var SomeClass }) -> ()
end_borrow %2 : ${ var SomeClass }
destroy_value %1 : ${ var SomeClass }
%6 = function_ref @useClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
apply %6(%5) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
destroy_value %5 : $@callee_guaranteed () -> ()
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> ()
sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> () {
// CHECK: bb0
bb0(%0 : @owned $@callee_owned () -> @out U):
debug_value %0 : $@callee_owned () -> @out U
%1 = copy_value %0 : $@callee_owned () -> @out U
%3 = alloc_stack $U
%4 = apply %1(%3) : $@callee_owned () -> @out U
destroy_addr %3 : $*U
dealloc_stack %3 : $*U
destroy_value %0 : $@callee_owned () -> @out U
%8 = tuple ()
// CHECK: return
return %8 : $()
}
sil [transparent] [serialized] [ossa] @mightApplyGuaranteed : $@convention(thin) <U where U : P> (@guaranteed @callee_guaranteed () -> @out U) -> () {
// CHECK: bb0
bb0(%0 : @guaranteed $@callee_guaranteed () -> @out U):
debug_value %0 : $@callee_guaranteed () -> @out U
%3 = alloc_stack $U
%4 = apply %0(%3) : $@callee_guaranteed () -> @out U
destroy_addr %3 : $*U
dealloc_stack %3 : $*U
%8 = tuple ()
// CHECK: return
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @callWithAutoclosure
sil [ossa] @callWithAutoclosure : $@convention(thin) <T where T : P> (@in T) -> () {
// CHECK: bb0
bb0(%0 : $*T):
// CHECK: debug_value {{.*}} expr op_deref
debug_value %0 : $*T, expr op_deref
// CHECK: function_ref @mightApply
%2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
%3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
// CHECK-NOT: alloc_box
// CHECK: [[STACK:%[0-9a-zA-Z_]+]] = alloc_stack $T
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
// CHECK: copy_addr %0 to [init] [[STACK]] : $*T
copy_addr %0 to [init] %4a : $*T
// CHECK: [[CLOSURE:%[0-9a-zA-Z]+]] = function_ref @$s21closure_to_specializeTf0ns_n
// CHECK: partial_apply [[CLOSURE]]<T>([[STACK]])
%6 = partial_apply %3<T>(%4) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
%7 = apply %2<T>(%6) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
// CHECK: destroy_addr [[STACK]] : $*T
// CHECK: dealloc_stack [[STACK]] : $*T
destroy_addr %0 : $*T
%9 = tuple ()
// CHECK: return
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @callWithAutoclosure_2 : $@convention(thin) <T where T : P> (@in T) -> () {
sil [ossa] @callWithAutoclosure_2 : $@convention(thin) <T where T : P> (@in T) -> () {
// CHECK: bb0
bb0(%0 : $*T):
// CHECK: debug_value {{.*}} expr op_deref
debug_value %0 : $*T, expr op_deref
// CHECK: function_ref @mightApply
%2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
%3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
// CHECK-NOT: alloc_box
// CHECK: [[STACK:%[0-9a-zA-Z_]+]] = alloc_stack $T
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
%4copy = copy_value %4 : $<τ_0_0> { var τ_0_0 } <T>
// CHECK: copy_addr %0 to [init] [[STACK]] : $*T
copy_addr %0 to [init] %4a : $*T
// CHECK: [[CLOSURE:%[0-9a-zA-Z]+]] = function_ref @$s21closure_to_specializeTf0ns_n
// CHECK: partial_apply [[CLOSURE]]<T>([[STACK]])
%6 = partial_apply %3<T>(%4copy) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
%7 = apply %2<T>(%6) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
// CHECK: destroy_addr [[STACK]] : $*T
// CHECK: dealloc_stack [[STACK]] : $*T
destroy_addr %0 : $*T
destroy_value %4 : $<τ_0_0> { var τ_0_0 } <T>
%9 = tuple ()
// CHECK: return
return %9 : $()
}
// CHECK: } // end sil function 'callWithAutoclosure_2'
// CHECK-LABEL: sil [ossa] @callWithAutoclosure_3 : $@convention(thin) <T where T : P> (@in T) -> () {
sil [ossa] @callWithAutoclosure_3 : $@convention(thin) <T where T : P> (@in T) -> () {
// CHECK: bb0
bb0(%0 : $*T):
// CHECK: debug_value {{.*}} expr op_deref
debug_value %0 : $*T, expr op_deref
// CHECK: function_ref @mightApply
%2 = function_ref @mightApply : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
%3 = function_ref @closure_to_specialize : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
// CHECK-NOT: alloc_box
// CHECK: [[STACK_UNMARKED:%[0-9a-zA-Z_]+]] = alloc_stack $T
// CHECK: [[STACK:%.*]] = mark_uninitialized [var] [[STACK_UNMARKED]]
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>
%4c = mark_uninitialized [var] %4 : $<τ_0_0> { var τ_0_0 } <T>
%4a = project_box %4c : $<τ_0_0> { var τ_0_0 } <T>, 0
%4copy = copy_value %4c : $<τ_0_0> { var τ_0_0 } <T>
// CHECK: copy_addr %0 to [init] [[STACK]] : $*T
copy_addr %0 to [init] %4a : $*T
// CHECK: [[CLOSURE:%[0-9a-zA-Z]+]] = function_ref @$s21closure_to_specializeTf0ns_n
// CHECK: partial_apply [[CLOSURE]]<T>([[STACK]])
%6 = partial_apply %3<T>(%4copy) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> @out τ_0_0
%7 = apply %2<T>(%6) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@owned @callee_owned () -> @out τ_0_0) -> ()
// CHECK: destroy_addr [[STACK]] : $*T
// CHECK: dealloc_stack [[STACK_UNMARKED]] : $*T
destroy_addr %0 : $*T
destroy_value %4c : $<τ_0_0> { var τ_0_0 } <T>
%9 = tuple ()
// CHECK: return
return %9 : $()
}
// CHECK: } // end sil function 'callWithAutoclosure_3'
// CHECK-LABEL: sil shared [ossa] @$s21closure_to_specializeTf0ns_n : $@convention(thin) <T where T : P> (@inout_aliasable T) -> @out T
sil shared [ossa] @closure_to_specialize : $@convention(thin) <T where T : P> (@owned <τ_0_0> { var τ_0_0 } <T>) -> @out T {
// CHECK: bb0
bb0(%0 : $*T, %1 : @owned $<τ_0_0> { var τ_0_0 } <T>):
%2 = project_box %1 : $<τ_0_0> { var τ_0_0 } <T>, 0
// CHECK-NEXT: copy_addr
copy_addr %2 to [init] %0 : $*T
// CHECK-NOT: destroy_value
destroy_value %1 : $<τ_0_0> { var τ_0_0 } <T>
%5 = tuple ()
// CHECK: return
return %5 : $()
}
protocol Count {
var count: Int { get }
}
struct Q : Count {
var count: Int { get }
init()
}
struct S<T : Count> {
@_hasStorage var t: T
var count: Int { get }
func test(i: Int) -> Bool
init(t: T)
}
// CHECK-LABEL: sil [noinline] [ossa] @inner
sil [noinline] [ossa] @inner : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool {
// CHECK: bb0
bb0(%0 : @owned $@callee_owned () -> Bool):
debug_value %0 : $@callee_owned () -> Bool
%1 = copy_value %0 : $@callee_owned () -> Bool
%3 = apply %1() : $@callee_owned () -> Bool
destroy_value %0 : $@callee_owned () -> Bool
// CHECK: return
return %3 : $Bool
}
// CHECK-LABEL: sil [noinline] [ossa] @outer
sil [noinline] [ossa] @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool {
// CHECK: bb0
bb0(%0 : @owned $@callee_owned () -> Bool):
debug_value %0 : $@callee_owned () -> Bool
%1 = copy_value %0 : $@callee_owned () -> Bool
%3 = apply %1() : $@callee_owned () -> Bool
destroy_value %0 : $@callee_owned () -> Bool
// CHECK: return
return %3 : $Bool
}
// CHECK-LABEL: sil @get
sil @get : $@convention(method) <T where T : Count> (@in S<T>) -> Int
// CHECK-LABEL: sil shared [ossa] @specialized
sil shared [ossa] @specialized : $@convention(method) (Int, @in S<Q>) -> Bool {
// CHECK: bb0
bb0(%0 : $Int, %1 : $*S<Q>):
debug_value %0 : $Int
debug_value %1 : $*S<Q>, expr op_deref
%4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
%5 = function_ref @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool
%6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <Q>
%6a = project_box %6 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <Q>, 0
copy_addr %1 to [init] %6a : $*S<Q>
%8 = partial_apply %5<Q>(%0, %6) : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool
%9 = apply %4(%8) : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
destroy_addr %1 : $*S<Q>
// CHECK: return
return %9 : $Bool
}
// CHECK-LABEL: sil [ossa] @unspecialized
sil [ossa] @unspecialized : $@convention(method) <T where T : Count> (Int, @in S<T>) -> Bool {
// CHECK: bb0
bb0(%0 : $Int, %1 : $*S<T>):
debug_value %0 : $Int
debug_value %1 : $*S<T>, expr op_deref
%4 = function_ref @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
%5 = function_ref @closure1 : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool
%6 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
%6a = project_box %6 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
copy_addr %1 to [init] %6a : $*S<T>
%8 = partial_apply %5<T>(%0, %6) : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool
%9 = apply %4(%8) : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
destroy_addr %1 : $*S<T>
// CHECK: return
return %9 : $Bool
}
// OPT-LABEL: sil shared [ossa] @closure1 :
// MANDATORY-NOT: sil shared [ossa] @closure1 :
sil shared [ossa] @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
// CHECK: bb0
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
%2 = project_box %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
%3 = function_ref @inner : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
%4 = function_ref @closure2 : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool
%5 = alloc_box $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
%5a = project_box %5 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
copy_addr %2 to [init] %5a : $*S<T>
%7 = partial_apply %4<T>(%0, %5) : $@convention(thin) <τ_0_0 where τ_0_0 : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <τ_0_0>) -> Bool
%8 = apply %3(%7) : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool
destroy_value %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
// CHECK: return
return %8 : $Bool
}
// CHECK-LABEL: sil shared [ossa] @$s8closure2Tf0ns_n
// CHECK: bb0
// CHECK: return
// CHECK-NOT: bb1
// OPT-LABEL: sil shared [ossa] @closure2 :
// MANDATORY-NOT: sil shared [ossa] @closure2 :
sil shared [ossa] @closure2 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
bb0(%0 : $Int, %1 : @owned $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>):
%2 = project_box %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
%3 = function_ref @binary : $@convention(thin) (Int, Int) -> Bool
%4 = alloc_stack $S<T>
copy_addr %2 to [init] %4 : $*S<T>
%6 = function_ref @get : $@convention(method) <τ_0_0 where τ_0_0 : Count> (@in S<τ_0_0>) -> Int
%7 = apply %6<T>(%4) : $@convention(method) <τ_0_0 where τ_0_0 : Count> (@in S<τ_0_0>) -> Int
%8 = apply %3(%0, %7) : $@convention(thin) (Int, Int) -> Bool
%9 = copy_value %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
%10 = project_box %9 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>, 0
copy_addr %10 to [init] %4 : $*S<T>
destroy_value %9 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
destroy_addr %4 : $*S<T>
dealloc_stack %4 : $*S<T>
destroy_value %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
return %8 : $Bool
}
// CHECK-LABEL: sil [transparent] [serialized] @binary
sil [transparent] [serialized] @binary : $@convention(thin) (Int, Int) -> Bool
// CHECK-LABEL: sil [ossa] @destroy_stack_value_after_closure
sil [ossa] @destroy_stack_value_after_closure : $@convention(thin) <T> (@in T) -> @out T {
// CHECK: bb0
bb0(%0 : $*T, %1 : $*T):
// CHECK: [[STACK:%.*]] = alloc_stack
// CHECK: copy_addr %1 to [init] [[STACK]]
// CHECK: [[APPLIED:%.*]] = function_ref
%3 = function_ref @applied : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> ()
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
copy_addr %1 to [init] %4a : $*T
// CHECK: [[PARTIAL:%.*]] = partial_apply [[APPLIED]]<T>([[STACK]])
%6 = partial_apply %3<T>(%4) : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <τ_0_0>) -> ()
// CHECK: debug_value [[PARTIAL]]
debug_value %6 : $@callee_owned () -> ()
// CHECK: [[COPIED_PARTIAL:%.*]] = copy_value [[PARTIAL]]
%7 = copy_value %6 : $@callee_owned () -> ()
// CHECK: apply [[COPIED_PARTIAL]]() : $@callee_owned () -> ()
%9 = apply %7() : $@callee_owned () -> ()
copy_addr %1 to [init] %0 : $*T
// CHECK: destroy_value [[PARTIAL]]
destroy_value %6 : $@callee_owned () -> ()
// CHECK: destroy_addr [[STACK]]
// CHECK: destroy_addr %1
destroy_addr %1 : $*T
%13 = tuple ()
return %13 : $()
}
sil [ossa] @applied : $@convention(thin) <T> (@owned <τ_0_0> { var τ_0_0 } <T>) -> () {
bb0(%0 : @owned $<τ_0_0> { var τ_0_0 } <T>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <T>, 0
%2 = function_ref @consume : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
%3 = alloc_stack $T
copy_addr %1 to [init] %3 : $*T
%5 = apply %2<T>(%3) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> ()
dealloc_stack %3 : $*T
destroy_value %0 : $<τ_0_0> { var τ_0_0 } <T>
%8 = tuple ()
return %8 : $()
}
sil hidden [noinline] @consume : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : $*T):
debug_value %0 : $*T, expr op_deref
destroy_addr %0 : $*T
%3 = tuple ()
return %3 : $()
}
class ThrowBaseClass {
required init() throws
init(noFail: ())
}
class ThrowDerivedClass : ThrowBaseClass {
//final init(failBeforeFullInitialization: Int) throws
}
sil [ossa] @throwing_unwrap : $@convention(thin) (Int) -> (Int, @error any Error)
sil [ossa] @fake_throwing_init : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error any Error)
sil hidden [ossa] @try_apply_during_chaining_init : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error any Error) {
// %0 // users: %11, %5
// %1 // user: %7
bb0(%0 : $Int, %1 : @owned $ThrowDerivedClass):
%2 = alloc_box ${ var ThrowDerivedClass }, let, name "self"
%3 = mark_uninitialized [delegatingself] %2 : ${ var ThrowDerivedClass }
%4 = project_box %3 : ${ var ThrowDerivedClass }, 0
store %1 to [init] %4 : $*ThrowDerivedClass
%8 = load [take] %4 : $*ThrowDerivedClass
%9 = function_ref @fake_throwing_init : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error any Error)
%10 = function_ref @throwing_unwrap : $@convention(thin) (Int) -> (Int, @error any Error)
try_apply %10(%0) : $@convention(thin) (Int) -> (Int, @error any Error), normal bb1, error bb3
// %12 // user: %13
bb1(%12 : $Int): // Preds: bb0
try_apply %9(%12, %8) : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error any Error), normal bb2, error bb4
// %14 // user: %15
bb2(%14 : @owned $ThrowDerivedClass): // Preds: bb1
store %14 to [init] %4 : $*ThrowDerivedClass
%16 = load [copy] %4 : $*ThrowDerivedClass
destroy_value %3 : ${ var ThrowDerivedClass }
return %16 : $ThrowDerivedClass
// %20 // user: %22
bb3(%20 : @owned $Error): // Preds: bb0
destroy_value %8 : $ThrowDerivedClass
br bb5(%20 : $Error)
// %23 // user: %24
bb4(%23 : @owned $Error): // Preds: bb1
br bb5(%23 : $Error)
// %25 // user: %27
bb5(%25 : @owned $Error): // Preds: bb4 bb3
destroy_value %3 : ${ var ThrowDerivedClass }
throw %25 : $Error
}
// CHECK-LABEL: sil [ossa] @deal_with_wrong_nesting
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK: bb2:
// CHECK: store
// CHECK: [[STACK2:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: dealloc_stack [[STACK2]]
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK: bb3:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @deal_with_wrong_nesting : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
cond_br undef, bb1, bb2
bb1:
destroy_value %1 : ${ var Int }
dealloc_stack %as1 : $*Bool
br bb3
bb2:
%1a = project_box %1 : ${ var Int }, 0
store %0 to [trivial] %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load [trivial] %1a : $*Int
%as2 = alloc_stack $Bool
destroy_value %1 : ${ var Int }
dealloc_stack %as2 : $*Bool
br bb3
bb3:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @wrong_nesting_with_alloc_ref
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[REF:%[0-9]+]] = alloc_ref [stack] $SomeClass
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: store
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack_ref [[REF]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @wrong_nesting_with_alloc_ref : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_ref [stack] $SomeClass
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to [trivial] %1a : $*Int
dealloc_ref %as1 : $SomeClass
dealloc_stack_ref %as1 : $SomeClass
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @nesting_and_unreachable1
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: dealloc_stack [[STACK2]]
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @nesting_and_unreachable1 : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
%as2 = alloc_stack $Bool
dealloc_stack %as2 : $*Bool
unreachable
bb2:
store %0 to [trivial] %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @nesting_and_unreachable2
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: dealloc_stack [[STACK2]]
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @nesting_and_unreachable2 : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
%as2 = alloc_stack $Bool
destroy_value %1 : ${ var Int }
dealloc_stack %as2 : $*Bool
unreachable
bb2:
store %0 to [trivial] %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @nesting_and_unreachable3
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK-NEXT: [[STACK:%[0-9]+]] = alloc_stack $Bool
// CHECK: bb1:
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store
// CHECK: dealloc_stack [[STACK]]
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @nesting_and_unreachable3 : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%as1 = alloc_stack $Bool
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
destroy_value %1 : ${ var Int }
unreachable
bb2:
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
dealloc_stack %as1 : $*Bool
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @nesting_and_unreachable4
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @nesting_and_unreachable4 : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
unreachable
bb2:
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @nesting_and_unreachable5
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: {{.*}} = alloc_stack $Bool
// CHECK-NEXT: {{.*}} = alloc_stack $Bool
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @nesting_and_unreachable5 : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
%as1 = alloc_stack $Bool
%as2 = alloc_stack $Bool
unreachable
bb2:
store %0 to [trivial] %1a : $*Int
%3 = load [trivial] %1a : $*Int
destroy_value %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @deinit_access : $@convention(thin) <T> (@in T) -> (@out T, @out T) {
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T):
// CHECK: [[STK:%.*]] = alloc_stack $T, var, name "x"
// CHECK: copy_addr %2 to [init] [[STK]] : $*T
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
// CHECK: copy_addr [[READ]] to [init] %0 : $*T
// CHECK: end_access [[READ]] : $*T
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
// CHECK: copy_addr [[READ]] to [init] %1 : $*T
// CHECK: end_access [[READ]] : $*T
// CHECK: destroy_addr [[STK]]
// CHECK: dealloc_stack [[STK]] : $*T
// CHECK: destroy_addr %2 : $*T
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function 'deinit_access'
sil [ossa] @deinit_access : $@convention(thin) <T> (@in T) -> (@out T, @out T) {
bb0(%0 : $*T, %1 : $*T, %2 : $*T):
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>, var, name "x"
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
copy_addr %2 to [init] %5 : $*T
%7 = begin_access [read] [static] %5 : $*T
copy_addr %7 to [init] %0 : $*T
end_access %7 : $*T
%10 = begin_access [read] [static] %5 : $*T
copy_addr %10 to [init] %1 : $*T
end_access %10 : $*T
destroy_value %4 : $<τ_0_0> { var τ_0_0 } <T>
destroy_addr %2 : $*T
%15 = tuple ()
return %15 : $()
}
// Test that
// begin_access [read], <other access>, copy_addr, end_access, destroy_addr
// is *not* folded into
// begin_access [deinit], copy_addr [take], end_access
//
// CHECK-LABEL: sil [ossa] @nodeinit_access : $@convention(thin) <T> (@in T) -> (@out T, @out T) {
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T):
// CHECK: [[STK:%.*]] = alloc_stack $T, var, name "x"
// CHECK: copy_addr %2 to [init] [[STK]] : $*T
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
// CHECK: copy_addr [[READ]] to [init] %0 : $*T
// CHECK: end_access [[READ]] : $*T
// CHECK: [[READ:%.*]] = begin_access [read] [static] [[STK]] : $*T
// CHECK: [[INNER:%.*]] = begin_access [read] [static] [[READ]] : $*T
// CHECK: end_access [[INNER]] : $*T
// CHECK: copy_addr [[READ]] to [init] %1 : $*T
// CHECK: end_access [[READ]] : $*T
// CHECK: destroy_addr [[STK]] : $*T
// CHECK: dealloc_stack [[STK]] : $*T
// CHECK: destroy_addr %2 : $*T
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function 'nodeinit_access'
sil [ossa] @nodeinit_access : $@convention(thin) <T> (@in T) -> (@out T, @out T) {
bb0(%0 : $*T, %1 : $*T, %2 : $*T):
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <T>, var, name "x"
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <T>, 0
copy_addr %2 to [init] %5 : $*T
%7 = begin_access [read] [static] %5 : $*T
copy_addr %7 to [init] %0 : $*T
end_access %7 : $*T
%10 = begin_access [read] [static] %5 : $*T
%innerAccess = begin_access [read] [static] %10 : $*T
end_access %innerAccess : $*T
copy_addr %10 to [init] %1 : $*T
end_access %10 : $*T
destroy_value %4 : $<τ_0_0> { var τ_0_0 } <T>
destroy_addr %2 : $*T
%15 = tuple ()
return %15 : $()
}
// CHECK-LABEL: sil [ossa] @alloc_box_in_specialized_callee :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'alloc_box_in_specialized_callee'
sil [ossa] @alloc_box_in_specialized_callee : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%2 = project_box %1, 0
store %0 to [trivial] %2
%4 = function_ref @callee_with_allocbox : $@convention(thin) (@guaranteed { var Int }) -> ()
%5 = apply %4(%1) : $@convention(thin) (@guaranteed { var Int }) -> ()
destroy_value %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil shared [ossa] @$s20callee_with_allocboxTf0s_n :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function '$s20callee_with_allocboxTf0s_n'
// CHECK-LABEL: sil [ossa] @callee_with_allocbox :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'callee_with_allocbox'
sil [ossa] @callee_with_allocbox : $@convention(thin) (@guaranteed { var Int }) -> () {
bb0(%0 : @guaranteed ${ var Int }):
%1 = alloc_box ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
%3 = project_box %0 : ${ var Int }, 0
copy_addr %3 to %2
destroy_value %1
%r = tuple ()
return %r
}
sil @getC : $@convention(thin) () -> (@owned C)
// CHECK-LABEL: sil [ossa] @leak_to_inf_loop_1 : {{.*}} {
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'leak_to_inf_loop_1'
sil [ossa] @leak_to_inf_loop_1 : $@convention(thin) (@owned C) -> () {
entry(%c : @owned $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to [init] %c_addr
%copy = copy_value %box
destroy_value %box
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load [take] %c_addr
store %c2 to [init] %c_addr
destroy_value %c_old
br loop
}
// CHECK-LABEL: sil [ossa] @leak_to_inf_loop_2 : {{.*}} {
// CHECK: cond_br undef, [[EXIT:bb[0-9]+]], [[TO_LOOP:bb[0-9]+]]
// CHECK: [[EXIT]]
// CHECK: destroy_addr
// CHECK: dealloc_stack
// CHECK: [[TO_LOOP]]
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'leak_to_inf_loop_2'
sil [ossa] @leak_to_inf_loop_2 : $@convention(thin) (@owned C) -> () {
entry(%c : @owned $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to [init] %c_addr
%copy = copy_value %box
destroy_value %box
cond_br undef, exit, to_loop
exit:
destroy_value %copy
return undef : $()
to_loop:
%copy2 = copy_value %copy
destroy_value %copy
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load [take] %c_addr
store %c2 to [init] %c_addr
destroy_value %c_old
br loop
}
// CHECK-LABEL: sil [ossa] @leak_to_inf_loop_3 : {{.*}} {
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'leak_to_inf_loop_3'
sil [ossa] @leak_to_inf_loop_3 : $@convention(thin) (@owned C) -> () {
entry(%c : @owned $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to [init] %c_addr
br to_loop
to_loop:
%copy = copy_value %box
destroy_value %box
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load [take] %c_addr
store %c2 to [init] %c_addr
destroy_value %c_old
br loop
}
// CHECK-LABEL: sil [ossa] @dealloc_box_before_loop
// CHECK: dealloc_stack
// CHECK-LABEL: } // end sil function 'dealloc_box_before_loop'
sil [ossa] @dealloc_box_before_loop : $@convention(thin) (@owned C) -> () {
entry(%c : @owned $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to [init] %c_addr
cond_br undef, exit, to_loop
exit:
destroy_value %box
return undef : $()
to_loop:
dealloc_box %box
br loop
loop:
br loop
}
// CHECK-LABEL: sil [ossa] @alloc_box_in_deadend_loop
// CHECK: alloc_stack
// CHECK: dealloc_stack
// CHECK-LABEL: } // end sil function 'alloc_box_in_deadend_loop'
sil [ossa] @alloc_box_in_deadend_loop : $@convention(thin) () -> () {
entry:
br loop
loop:
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c = apply %getC() : $@convention(thin) () -> (@owned C)
store %c to [init] %c_addr
destroy_value %box
br loop
}
// CHECK-LABEL: sil [ossa] @alloc_box_in_exiting_loop
// CHECK: br [[EXITING_LOOP:bb[0-9]+]]
// CHECK: [[EXITING_LOOP]]:
// CHECK: alloc_stack
// CHECK: cond_br undef, [[EXIT:bb[0-9]+]], [[LATCH:bb[0-9]+]]
// CHECK: [[LATCH]]:
// CHECK: cond_br undef, [[BACKEDGE:bb[0-9]+]], [[INFINITE_LOOP:bb[0-9]+]]
// CHECK: [[BACKEDGE]]:
// CHECK: dealloc_stack
// CHECK: br [[EXITING_LOOP]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack
// CHECK: [[INFINITE_LOOP]]:
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'alloc_box_in_exiting_loop'
sil [ossa] @alloc_box_in_exiting_loop : $@convention(thin) () -> () {
entry:
br exiting_loop
exiting_loop:
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c = apply %getC() : $@convention(thin) () -> (@owned C)
store %c to [init] %c_addr
cond_br undef, exit, latch
latch:
cond_br undef, backedge, to_infinite_loop
backedge:
destroy_value %box
br exiting_loop
exit:
destroy_value %box
return undef : $()
to_infinite_loop:
br infinite_loop
infinite_loop:
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
store %c2 to [assign] %c_addr
%copy = copy_value %box
destroy_value %copy
br infinite_loop
}
// CHECK-LABEL: sil [ossa] @alloc_box_in_deadend_loop_with_another_deadend
// CHECK: br [[LOOP:bb[0-9]+]]
// CHECK: [[LOOP]]:
// CHECK: alloc_stack
// CHECK: cond_br undef, [[BACKEDGE:bb[0-9]+]], [[DIE:bb[0-9]+]]
// CHECK: [[BACKEDGE]]:
// CHECK: dealloc_stack
// CHECK: br [[LOOP]]
// CHECK: [[DIE]]:
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'alloc_box_in_deadend_loop_with_another_deadend'
sil [ossa] @alloc_box_in_deadend_loop_with_another_deadend : $@convention(thin) () -> () {
entry:
br loop
loop:
%box = alloc_box ${ var C }
cond_br undef, backedge, die
backedge:
destroy_value %box
br loop
die:
%copy = copy_value %box
destroy_value %copy
unreachable
}
// CHECK-LABEL: sil [ossa] @test_access_enforcement :
// OPT: begin_access [modify] [dynamic]
// MANDATORY: begin_access [modify] [static]
// CHECK-LABEL: } // end sil function 'test_access_enforcement'
sil [ossa] @test_access_enforcement : $(Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%2 = project_box %1 : ${ var Int }, 0
%3 = begin_access [modify] [dynamic] %2
store %0 to [trivial] %3
end_access %3
%4 = load [trivial] %2
destroy_value %1
return %4 : $Int
}