Files
swift-mirror/test/SILOptimizer/allocbox_to_stack.sil
John McCall 8d231d20c6 Rewrite StackNesting to be a non-iterative single-pass algorithm.
The previous algorithm was doing an iterative forward data flow analysis
followed by a reverse data flow analysis. I suspect the history here is that
it was a reverse analysis, and that didn't really work for infinite loops,
and so complexity accumulated.

The new algorithm is quite straightforward and relies on the allocations
being properly jointly post-dominated, just not nested. We simply walk
forward through the blocks in consistent-with-dominance order, maintaining
the stack of active allocations and deferring deallocations that are
improperly nested until we deallocate the allocations above it. The only
real subtlety is that we have to delay walking into dead-end regions until
we've seen all of the edges into them, so that we can know whether we have
a coherent stack state in them. If the state is incoherent, we need to
remove any deallocations of previous allocations because we cannot talk
correctly about what's on top of the stack.

The reason I'm doing this, besides it just being a simpler and hopefully
faster algorithm, is that modeling some of the uses of the async stack
allocator properly requires builtins that cannot just be semantically
reordered. That should be somewhat easier to handle with the new approach,
although really (1) we should not have runtime functions that need this and
(2) we're going to need a conservatively-correct solution that's different
from this anyway because hoisting allocations is *also* limited in its own
way.

I've attached a rather pedantic proof of the correctness of the algorithm.

The thing that concerns me most about the rewritten pass is that it isn't
actually validating joint post-dominance on input, so if you give it bad
input, it might be a little mystifying to debug the verifier failures.
2025-11-03 11:51:17 -08:00

1426 lines
48 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -sil-print-debuginfo -enable-sil-verify-all %s -allocbox-to-stack -enable-copy-propagation=requested-passes-only | %FileCheck %s
sil_stage raw
import Builtin
struct Int {
var _value: Builtin.Int64
}
struct Bool {
var _value: Builtin.Int1
}
protocol Error {}
class C {}
// CHECK-LABEL: sil @simple_promotion
sil @simple_promotion : $(Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
return %3 : $Int
// CHECK: alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: strong_release
// CHECK: return
}
// CHECK-LABEL: sil @double_project_box
sil @double_project_box : $(Int) -> (Int, Int) {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
%3 = load %1a : $*Int
%1b = project_box %1 : ${ var Int }, 0
%3b = load %1b : $*Int
strong_release %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: strong_release
// CHECK: return
}
// CHECK-LABEL: sil @init_var
sil @init_var : $() -> Int {
bb0:
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
return %3 : $Int
// CHECK: %0 = alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: strong_release
// CHECK-NOT: destroy_addr
// CHECK: dealloc_stack %0 : $*Int
// CHECK: return
}
// CHECK-LABEL: sil @multi_strong_release
sil @multi_strong_release : $() -> Int {
bb0:
%1 = alloc_box ${ var Int }
%1a = mark_uninitialized [rootself] %1 : ${ var Int }
%2 = project_box %1a : ${ var Int }, 0
%3 = load %2 : $*Int
strong_retain %1a : ${ var Int }
strong_release %1a : ${ var Int }
br bb1
bb1:
strong_release %1a : ${ 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 @struct_tuple_element_addr
sil @struct_tuple_element_addr : $(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 %2 : $*Int
%b = tuple_element_addr %aa : $*(Int, Int), 0
store %0 to %b : $*Int
%6 = struct_element_addr %1a : $*TestStruct, #TestStruct.Elt
%7 = load %6 : $*Int
strong_release %a : ${ var (Int, Int) }
strong_release %1 : ${ var TestStruct }
return %7 : $Int
// CHECK-DAG: dealloc_stack [[STRUCT]]
// CHECK-DAG: dealloc_stack [[TUPLE]]
// CHECK: return
}
sil @callee : $@convention(thin) (@inout Int) -> ()
// CHECK-LABEL: sil @escaping_pointer
sil @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 %1a : $*Int
strong_release %1 : ${ var Int }
%10 = address_to_pointer %1a : $*Int to $Builtin.RawPointer
%11 = pointer_to_address %10 : $Builtin.RawPointer to [strict] $*Int
%12 = load %11 : $*Int
return %8 : $Int
// CHECK: return
}
protocol P {
}
sil @returns_protocol : $@convention(thin) () -> @out P
// CHECK-LABEL: sil @test_indirect_return
sil @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
strong_release %2 : ${ var P }
%0 = tuple ()
return %0 : $()
// CHECK: return
}
class SomeClass {}
// CHECK-LABEL: sil @class_promotion
sil @class_promotion : $(SomeClass) -> SomeClass {
bb0(%0 : $SomeClass):
%1 = alloc_box ${ var SomeClass }
%1a = project_box %1 : ${ var SomeClass }, 0
store %0 to %1a : $*SomeClass
%3 = load %1a : $*SomeClass
strong_release %1 : ${ var SomeClass }
return %3 : $SomeClass
// CHECK: %1 = alloc_stack
// CHECK-NOT: alloc_box
// CHECK-NOT: strong_release
// CHECK: destroy_addr {{.*}} : $*SomeClass
// CHECK: return
}
protocol LogicValue {
func getLogicValue() -> Bool
}
// CHECK-LABEL: @protocols
sil @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
strong_release %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 @dealloc_box
sil @dealloc_box : $@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 @$s1t9SomeUnionO1yfMS0_FCS_9SomeClassS0_ : $@convention(thin) (@owned SomeClass, @thin SomeUnion.Type) -> @owned SomeUnion
sil @$s1t9SomeClassCCfMS0_FT_S0_ : $@convention(thin) (@thick SomeClass.Type) -> @owned SomeClass
// CHECK-LABEL: sil @union_test
sil @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 %1a : $*SomeUnion
strong_release %1 : ${ var SomeUnion }
%10 = tuple ()
return %10 : $()
// CHECK: dealloc_stack [[UNION]]
// CHECK: [[T0:%.*]] = tuple ()
// CHECK-NEXT: return [[T0]] : $()
}
// CHECK-LABEL: sil @multiple_release_test
sil @multiple_release_test : $@convention(thin) (Bool) -> Bool {
bb0(%0 : $Bool):
%1 = alloc_box ${ var Bool }
%1a = project_box %1 : ${ var Bool }, 0
store %0 to %1a : $*Bool
strong_retain %1 : ${ var Bool }
strong_retain %1 : ${ var Bool }
%5 = tuple ()
%6 = load %1a : $*Bool
strong_release %1 : ${ var Bool }
strong_release %1 : ${ var Bool }
strong_release %1 : ${ var Bool }
return %6 : $Bool
// CHECK: alloc_stack $Bool
// CHECK-NEXT: store {{%[0-9]+}}
// 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 @box_reachable_from_release_test
sil @box_reachable_from_release_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:
strong_release %1 : ${ var Bool }
br bb1
// CHECK: bb3:
// CHECK-NEXT: dealloc_stack
bb3:
strong_release %1 : ${ var Bool }
%2 = tuple ()
// CHECK: return
return %2 : $()
}
sil @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
// strong_releases 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 @test_mui
sil @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 %3 : $*SomeClass
strong_retain %12 : $SomeClass
%14 = apply %11(%12) : $@convention(thin) (@owned SomeClass) -> ()
strong_release %2a : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
// CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
// CHECK: bb2
bb2:
%17 = tuple ()
// CHECK: return
return %17 : $()
bb3:
strong_release %2a : ${ var SomeClass }
// CHECK: destroy_addr [[MUI]]
// CHECK-NEXT: dealloc_stack [[STACK]]
br bb2
}
// CHECK-LABEL: sil @$s6struct5apply1fS2iyc_tF
// struct.apply (f : () -> Swift.Int) -> Swift.Int
sil @$s6struct5apply1fS2iyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> Int {
bb0(%0 : $@callee_owned () -> Int):
debug_value %0 : $@callee_owned () -> Int, let, name "f" // id: %1
strong_retain %0 : $@callee_owned () -> Int // id: %2
%3 = apply %0() : $@callee_owned () -> Int // user: %5
strong_release %0 : $@callee_owned () -> Int // id: %4
return %3 : $Int // id: %5
}
// CHECK-LABEL: sil @$s6struct6escape1fSiycSiyc_tF
// struct.escape (f : () -> Swift.Int) -> () -> Swift.Int
sil @$s6struct6escape1fSiycSiyc_tF : $@convention(thin) (@owned @callee_owned () -> Int) -> @owned @callee_owned () -> Int {
bb0(%0 : $@callee_owned () -> Int):
debug_value %0 : $@callee_owned () -> Int, let, name "f" // id: %1
return %0 : $@callee_owned () -> Int // id: %2
}
// CHECK-LABEL: sil @$s6struct8useStack1tySi_tF
// struct.useStack (t : Swift.Int) -> ()
sil @$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 %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
strong_retain %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %6
// CHECK: [[PA:%[a-zA-Z0-9]+]] = partial_apply [[FUNC]]
%7 = partial_apply %5(%2) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int // user: %8
%8 = apply %4(%7) : $@convention(thin) (@owned @callee_owned () -> Int) -> Int
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Int>
%10 = tuple () // user: %11
return %10 : $() // id: %11
}
// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n
// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_
// struct.(useStack (t : Swift.Int) -> ()).(closure #1)
sil private @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
bb0(%0 : $<τ_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 %3 : $*Int // user: %8
dealloc_stack %3 : $*Int // id: %6
strong_release %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 @$s6struct6useBox1tySi_tF
// struct.useBox (t : Swift.Int) -> ()
sil @$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 %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
strong_retain %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %6
%7 = partial_apply %5(%2) : $@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
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Int> // id: %10
%11 = tuple () // user: %12
return %11 : $() // id: %12
}
// CHECK-LABEL: sil private @$s6struct6useBox1tySi_tFSiycfU_
// struct.(useBox (t : Swift.Int) -> ()).(closure #1)
sil private @$s6struct6useBox1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
bb0(%0 : $<τ_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 %3 : $*Int // user: %8
dealloc_stack %3 : $*Int // id: %6
strong_release %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 @no_final_release
sil @no_final_release : $@convention(thin) (Int) -> Bool {
bb0(%0 : $Int):
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int>, 0
store %0 to %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>) -> ()
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Int>
%5 = partial_apply %3(%1) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
strong_retain %5 : $@callee_owned () -> ()
%7 = apply %5() : $@callee_owned () -> ()
strong_release %5 : $@callee_owned () -> ()
unreachable
}
// CHECK-LABEL: sil [transparent] [serialized] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> ()
sil [transparent] [serialized] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> () {
// CHECK: bb0
bb0(%0 : $@callee_owned () -> @out U):
debug_value %0 : $@callee_owned () -> @out U
strong_retain %0 : $@callee_owned () -> @out U
%3 = alloc_stack $U
%4 = apply %0(%3) : $@callee_owned () -> @out U
destroy_addr %3 : $*U
dealloc_stack %3 : $*U
strong_release %0 : $@callee_owned () -> @out U
%8 = tuple ()
// CHECK: return
return %8 : $()
}
sil [transparent] [serialized] @mightApplyGuaranteed : $@convention(thin) <U where U : P> (@guaranteed @callee_guaranteed () -> @out U) -> () {
bb0(%0 : $@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 ()
return %8 : $()
}
// CHECK-LABEL: sil @callWithAutoclosure
sil @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 @callWithAutoclosure_2 : $@convention(thin) <T where T : P> (@in T) -> () {
sil @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
strong_retain %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>(%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
strong_release %4 : $<τ_0_0> { var τ_0_0 } <T>
%9 = tuple ()
// CHECK: return
return %9 : $()
}
// CHECK: } // end sil function 'callWithAutoclosure_2'
// CHECK-LABEL: sil private @$s21closure_to_specializeTf0ns_n : $@convention(thin) <T where T : P> (@inout_aliasable T) -> @out T
sil private @closure_to_specialize : $@convention(thin) <T where T : P> (@owned <τ_0_0> { var τ_0_0 } <T>) -> @out T {
// CHECK: bb0
bb0(%0 : $*T, %1 : $<τ_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: strong_release
strong_release %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] @inner
sil [noinline] @inner : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool {
// CHECK: bb0
bb0(%0 : $@callee_owned () -> Bool):
debug_value %0 : $@callee_owned () -> Bool
strong_retain %0 : $@callee_owned () -> Bool
%3 = apply %0() : $@callee_owned () -> Bool
strong_release %0 : $@callee_owned () -> Bool
// CHECK: return
return %3 : $Bool
}
// CHECK-LABEL: sil [noinline] @outer
sil [noinline] @outer : $@convention(thin) (@owned @callee_owned () -> Bool) -> Bool {
// CHECK: bb0
bb0(%0 : $@callee_owned () -> Bool):
debug_value %0 : $@callee_owned () -> Bool
strong_retain %0 : $@callee_owned () -> Bool
%3 = apply %0() : $@callee_owned () -> Bool
strong_release %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 @specialized
sil shared @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 @unspecialized
sil @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
}
// CHECK-LABEL: sil shared @closure1
sil shared @closure1 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
// CHECK: bb0
bb0(%0 : $Int, %1 : $<τ_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
strong_release %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
// CHECK: return
return %8 : $Bool
}
// CHECK-LABEL: sil shared @$s8closure2Tf0ns_n
// CHECK: bb0
// CHECK: return
// CHECK-NOT: bb1
// CHECK-LABEL: sil shared @closure2
sil shared @closure2 : $@convention(thin) <T where T : Count> (Int, @owned <τ_0_0 : Count> { var S<τ_0_0> } <T>) -> Bool {
// CHECK: bb0
bb0(%0 : $Int, %1 : $<τ_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
dealloc_stack %4 : $*S<T>
strong_release %1 : $<τ_0_0 where τ_0_0 : Count> { var S<τ_0_0> } <T>
// CHECK: return
return %8 : $Bool
}
// CHECK-LABEL: sil [transparent] [serialized] @binary
sil [transparent] [serialized] @binary : $@convention(thin) (Int, Int) -> Bool
// CHECK-LABEL: sil @destroy_stack_value_after_closure
sil @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: strong_retain [[PARTIAL]]
strong_retain %6 : $@callee_owned () -> ()
// CHECK: apply [[PARTIAL]]
%9 = apply %6() : $@callee_owned () -> ()
copy_addr %1 to [init] %0 : $*T
// CHECK: strong_release [[PARTIAL]]
strong_release %6 : $@callee_owned () -> ()
// CHECK: destroy_addr [[STACK]]
// CHECK: destroy_addr %1
destroy_addr %1 : $*T
%13 = tuple ()
return %13 : $()
}
sil @applied : $@convention(thin) <T> (@owned <τ_0_0> { var τ_0_0 } <T>) -> () {
bb0(%0 : $<τ_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
strong_release %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 @throwing_unwrap : $@convention(thin) (Int) -> (Int, @error any Error)
sil @fake_throwing_init : $@convention(method) (Int, @owned ThrowDerivedClass) -> (@owned ThrowDerivedClass, @error any Error)
sil hidden @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 : $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 %4 : $*ThrowDerivedClass
%8 = load %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 : $ThrowDerivedClass): // Preds: bb1
store %14 to %4 : $*ThrowDerivedClass
%16 = load %4 : $*ThrowDerivedClass
strong_retain %16 : $ThrowDerivedClass
strong_release %3 : ${ var ThrowDerivedClass }
return %16 : $ThrowDerivedClass
// %20 // user: %22
bb3(%20 : $Error): // Preds: bb0
strong_release %8 : $ThrowDerivedClass
br bb5(%20 : $Error)
// %23 // user: %24
bb4(%23 : $Error): // Preds: bb1
br bb5(%23 : $Error)
// %25 // user: %27
bb5(%25 : $Error): // Preds: bb4 bb3
strong_release %3 : ${ var ThrowDerivedClass }
throw %25 : $Error
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: [[STACK2:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: dealloc_stack [[STACK2]] : $*Bool, loc "testloc":27:27
// CHECK-NEXT: dealloc_stack [[BOX]] : $*Int, loc "testloc":27:27
// CHECK-NEXT: dealloc_stack [[STACK1]] : $*Bool, loc "testloc":27:27
// CHECK: bb3:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @deal_with_wrong_nesting : $(Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
cond_br undef, bb1, bb2
bb1:
strong_release %1 : ${ var Int }
dealloc_stack %as1 : $*Bool
br bb3
bb2:
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load %1a : $*Int
%as2 = alloc_stack $Bool
strong_release %1 : ${ var Int }
dealloc_stack %as2 : $*Bool, loc "testloc":27:27
br bb3
bb3:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack_ref [[REF]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @wrong_nesting_with_alloc_ref : $(Int) -> () {
bb0(%0 : $Int):
%as1 = alloc_ref [stack] $SomeClass
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
dealloc_stack_ref %as1 : $SomeClass
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @nesting_and_unreachable1 : $(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 %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @nesting_and_unreachable2 : $(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
strong_release %1 : ${ var Int }
dealloc_stack %as2 : $*Bool
unreachable
bb2:
store %0 to %1a : $*Int
dealloc_stack %as1 : $*Bool
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: dealloc_stack [[STACK]]
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @nesting_and_unreachable3 : $(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:
strong_release %1 : ${ var Int }
unreachable
bb2:
store %0 to %1a : $*Int
%3 = load %1a : $*Int
dealloc_stack %as1 : $*Bool
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @nesting_and_unreachable4
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK: store {{%[0-9]+}}
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @nesting_and_unreachable4 : $(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 %1a : $*Int
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @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 {{%[0-9]+}}
// CHECK: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @nesting_and_unreachable5 : $(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 %1a : $*Int
%3 = load %1a : $*Int
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @nesting_and_unreachable6
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK: bb1:
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: unreachable
// CHECK: bb3:
// CHECK-NEXT: return
sil @nesting_and_unreachable6 : $@convention(thin) (Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
%3 = load %1a : $*Int
cond_br undef, bb1, bb2
bb1:
strong_release %1 : ${ var Int }
br bb3
bb2:
%241 = alloc_stack $Int
strong_release %1 : ${ var Int }
unreachable
bb3:
return %3 : $Int
}
// CHECK-LABEL: sil @nesting_and_unreachable7
// CHECK: bb0(%0 : $Bool):
// CHECK-NEXT: [[AB:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: [[AI:%[0-9]+]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: store {{%[0-9]+}}
// CHECK-NEXT: load
// CHECK-NEXT: unreachable
sil @nesting_and_unreachable7 : $@convention(thin) (Bool) -> Bool {
bb0(%0 : $Bool):
%as1 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb2
bb1:
strong_release %1 : ${ var Int }
br bb3
bb2:
strong_release %1 : ${ var Int }
br bb3
bb3:
store %0 to %as1 : $*Bool
%3 = load %as1 : $*Bool
unreachable
}
// StackNesting used to split the edge to bb3, but that's not really
// necessary. Also, this input very arguably should not be allowed
// because there isn't a consistent stack depth on entry to bb3.
// CHECK-LABEL: sil @nesting_and_unreachable_critical_edge
// CHECK: bb0(%0 : $Int):
// CHECK-NEXT: [[BOX:%[0-9]+]] = alloc_stack $Int
// CHECK-NEXT: [[STACK1:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: [[STACK2:%[0-9]+]] = alloc_stack $Bool
// CHECK-NEXT: cond_br
// CHECK: bb2:
// CHECK: store {{%[0-9]+}}
// CHECK: dealloc_stack [[STACK2]]
// CHECK-NEXT: dealloc_stack [[STACK1]]
// CHECK-NEXT: dealloc_stack [[BOX]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK: bb3:
// CHECK-NEXT: unreachable
// CHECK: // end sil function 'nesting_and_unreachable_critical_edge'
sil @nesting_and_unreachable_critical_edge : $(Int) -> () {
bb0(%0 : $Int):
%1 = alloc_box ${ var Int }
%as1 = alloc_stack $Bool
%1a = project_box %1 : ${ var Int }, 0
cond_br undef, bb1, bb3
bb1:
%as2 = alloc_stack $Bool
cond_br undef, bb2, bb3
bb2:
store %0 to %1a : $*Int
%3 = load %1a : $*Int
dealloc_stack %as2 : $*Bool
dealloc_stack %as1 : $*Bool
strong_release %1 : ${ var Int }
%r = tuple ()
return %r : $()
bb3:
strong_release %1 : ${ var Int }
unreachable
}
// Verify that we don't crash on this one:
//
// CHECK-LABEL: sil @unreachable_with_two_predecessors
sil @unreachable_with_two_predecessors : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
cond_br undef, bb1, bb4
bb1:
%4 = alloc_stack $Bool
%1 = alloc_box ${ var Int }
%1a = project_box %1 : ${ var Int }, 0
store %0 to %1a : $*Int
cond_br undef, bb3, bb2
bb2:
dealloc_stack %4 : $*Bool
br bb4
bb3:
%9 = load %1a : $*Int
strong_release %1 : ${ var Int }
dealloc_stack %4 : $*Bool
%13 = tuple ()
return %13 : $()
bb4:
unreachable
}
// Verify the case in which a box used by a partial apply which is then used by a try_apply.
//
// <rdar://problem/42815224> Swift CI: 2. Swift Source Compatibility Suite (master): Kitura project fails with compiler crash
// CHECK-LABEL: sil @testPAUsedByTryApplyClosure : $@convention(thin) (@guaranteed { var Int }) -> () {
// CHECK-LABEL: } // end sil function 'testPAUsedByTryApplyClosure'
sil @testPAUsedByTryApplyClosure : $@convention(thin) (@guaranteed { var Int }) -> () {
bb0(%0: ${ var Int }):
%v = tuple ()
return %v : $()
}
// CHECK-LABEL: sil private @testPAUsedByTryApply : $@convention(method) (@callee_guaranteed (@guaranteed @callee_guaranteed () -> ()) -> @error any Error) -> () {
// CHECK-LABEL: } // end sil function 'testPAUsedByTryApply'
sil private @testPAUsedByTryApply : $@convention(method) (@callee_guaranteed (@guaranteed @callee_guaranteed () -> ()) -> @error any Error) -> () {
bb0(%0: $@callee_guaranteed (@guaranteed @callee_guaranteed () -> ()) -> @error any Error):
%box = alloc_box ${ var Int }, var
%closure = function_ref @testPAUsedByTryApplyClosure : $@convention(thin) (@guaranteed { var Int }) -> ()
%pa = partial_apply [callee_guaranteed] %closure(%box) : $@convention(thin) (@guaranteed { var Int }) -> ()
try_apply %0(%pa) : $@callee_guaranteed (@guaranteed @callee_guaranteed () -> ()) -> @error any Error, normal bb2, error bb3
bb2(%result : $()):
br bb4
bb3(%error : $Error):
br bb4
bb4:
%v = tuple ()
return %v : $()
}
sil [ossa] @move_closure_callee : $@convention(thin) (@guaranteed { var Builtin.Int32 }) -> () {
bb0(%box : @closureCapture @guaranteed ${ var Builtin.Int32 }):
%13 = integer_literal $Builtin.Int32, 13
%addr = project_box %box : ${ var Builtin.Int32 }, 0
store %13 to [trivial] %addr : $*Builtin.Int32
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @move_closure : {{.*}} {
// CHECK: [[SPECIALIZED_CLOSURE:%[^,]+]] = function_ref @$s19move_closure_calleeTf0s_n : $@convention(thin) (@inout_aliasable Builtin.Int32) -> ()
// CHECK: partial_apply [callee_guaranteed] [[SPECIALIZED_CLOSURE]]
// CHECK-LABEL: } // end sil function 'move_closure'
sil [ossa] @move_closure : $@convention(thin) () -> Builtin.Int32 {
bb0:
%box = alloc_box ${ var Builtin.Int32 }
%addr = project_box %box : ${ var Builtin.Int32 }, 0
%42 = integer_literal $Builtin.Int32, 42
store %42 to [trivial] %addr : $*Builtin.Int32
%callee = function_ref @move_closure_callee : $@convention(thin) (@guaranteed { var Builtin.Int32 }) -> ()
%box_copy = copy_value %box : ${ var Builtin.Int32 }
mark_function_escape %addr : $*Builtin.Int32
%closure = partial_apply [callee_guaranteed] %callee(%box_copy) : $@convention(thin) (@guaranteed { var Builtin.Int32 }) -> ()
%closure_move = move_value %closure : $@callee_guaranteed () -> ()
apply %closure_move() : $@callee_guaranteed () -> ()
destroy_value %closure_move : $@callee_guaranteed () -> ()
%value = load [trivial] %addr : $*Builtin.Int32
destroy_value %box : ${ var Builtin.Int32 }
return %value : $Builtin.Int32
}
sil @getC : $@convention(thin) () -> (@owned C)
// CHECK-LABEL: sil @leak_to_inf_loop_1 : {{.*}} {
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'leak_to_inf_loop_1'
sil @leak_to_inf_loop_1 : $@convention(thin) (@owned C) -> () {
entry(%c : $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to %c_addr
strong_retain %box
strong_release %box
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load %c_addr
store %c2 to %c_addr
release_value %c_old
br loop
}
// CHECK-LABEL: sil @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 @leak_to_inf_loop_2 : $@convention(thin) (@owned C) -> () {
entry(%c : $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to %c_addr
strong_retain %box
strong_release %box
cond_br undef, exit, to_loop
exit:
strong_release %box
return undef : $()
to_loop:
strong_retain %box
strong_release %box
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load %c_addr
store %c2 to %c_addr
release_value %c_old
br loop
}
// CHECK-LABEL: sil @leak_to_inf_loop_3 : {{.*}} {
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK-LABEL: } // end sil function 'leak_to_inf_loop_3'
sil @leak_to_inf_loop_3 : $@convention(thin) (@owned C) -> () {
entry(%c : $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to %c_addr
br to_loop
to_loop:
strong_retain %box
strong_release %box
br loop
loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
%c_old = load %c_addr
store %c2 to %c_addr
release_value %c_old
br loop
}
// CHECK-LABEL: sil @dealloc_box_before_loop
// CHECK: dealloc_stack
// CHECK-LABEL: } // end sil function 'dealloc_box_before_loop'
sil @dealloc_box_before_loop : $@convention(thin) (@owned C) -> () {
entry(%c : $C):
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
store %c to %c_addr
cond_br undef, exit, to_loop
exit:
strong_release %box
return undef : $()
to_loop:
dealloc_box %box
br loop
loop:
br loop
}
// CHECK-LABEL: sil @alloc_box_in_deadend_loop
// CHECK: alloc_stack
// CHECK: dealloc_stack
// CHECK-LABEL: } // end sil function 'alloc_box_in_deadend_loop'
sil @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 %c_addr
strong_release %box
br loop
}
// CHECK-LABEL: sil @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 @alloc_box_in_exiting_loop : $@convention(thin) () -> () {
entry:
br exiting_loop
exiting_loop:
%box = alloc_box ${ var C }
%c_addr = project_box %box, 0
cond_br undef, exit, latch
latch:
cond_br undef, backedge, infinite_loop
backedge:
strong_release %box
br exiting_loop
exit:
strong_release %box
return undef : $()
infinite_loop:
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
%c = apply %getC() : $@convention(thin) () -> (@owned C)
store %c to %c_addr
strong_retain %box
strong_release %box
br infinite_loop
}
// CHECK-LABEL: sil @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 @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:
strong_release %box
br loop
die:
strong_retain %box
strong_release %box
unreachable
}