Files
swift-mirror/test/SILOptimizer/stack-nesting.sil
John McCall 8cda95bca4 Don't delay stack deallocation of non-nested allocations
Credit to @eeckstein for noticing this possibility

I considered some much more complicated solutions to this problem, like
tracking all the deallocations we add and remove and then undoing them
for the allocations we end up marking non_nested, but ultimately I
figured out that this would work and was much less invasive, even if it
potentially yields slightly worse results in some cases.
2026-04-02 19:30:57 -04:00

701 lines
23 KiB
Plaintext

// RUN: %target-sil-opt -sil-disable-input-verify -test-runner %s -o /dev/null 2>&1 | %FileCheck %s
import Builtin
import Swift
class MyClass {}
// CHECK-LABEL: sil [ossa] @test_simple
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-LABEL: // end sil function 'test_simple'
sil [ossa] @test_simple : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
integer_literal $Builtin.Int32, 2
dealloc_stack %a
dealloc_stack %b
integer_literal $Builtin.Int32, 3
%ret = tuple ()
return %ret : $()
}
// CHECK-LABEL: sil [ossa] @test_flow
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-LABEL: // end sil function 'test_flow'
sil [ossa] @test_flow : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
cond_br %cond, bb1, bb2
bb1:
dealloc_stack %a
br bb3
bb2:
%c = alloc_stack $Int
dealloc_stack %a
dealloc_stack %c
br bb3
bb3:
dealloc_stack %b
integer_literal $Builtin.Int32, 2
%ret = tuple ()
return %ret : $()
}
// The dealloc_stack of %a is out of order and needs to be sunk past
// the dealloc_stack of %b, which doesn't exist on this dead-end path.
// CHECK-LABEL: sil [ossa] @test_simple_dead
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: unreachable
// CHECK-LABEL: // end sil function 'test_simple_dead'
sil [ossa] @test_simple_dead : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
integer_literal $Builtin.Int32, 2
dealloc_stack %a
integer_literal $Builtin.Int32, 3
unreachable
}
// The dealloc_stack of %a is out of order and needs to be sunk.
// Sinking it on the dead-end path actually means removing it.
// CHECK-LABEL: sil [ossa] @test_flow_dead
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-LABEL: // end sil function 'test_flow_dead'
sil [ossa] @test_flow_dead : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
dealloc_stack %a
cond_br %cond, bb1, bb2
bb1:
unreachable
bb2:
dealloc_stack %b
integer_literal $Builtin.Int32, 2
%ret = tuple ()
return %ret : $()
}
// The dealloc_stack of %a is out of order and needs to be sunk.
// In this case, there's a dealloc on both paths, despite one of them being
// dead.
// CHECK-LABEL: sil [ossa] @test_flow_dead_2
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: unreachable
// CHECK: bb2:
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-LABEL: // end sil function 'test_flow_dead_2'
sil [ossa] @test_flow_dead_2 : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
dealloc_stack %a
cond_br %cond, bb1, bb2
bb1:
dealloc_stack %b
unreachable
bb2:
dealloc_stack %b
integer_literal $Builtin.Int32, 2
%ret = tuple ()
return %ret : $()
}
// We need to delete the dealloc_stacks in bb3 because the stack is in
// an incoherent state entering that block.
// CHECK-LABEL: sil [ossa] @test_merge_alloc
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: unreachable
// CHECK-LABEL: // end sil function 'test_merge_alloc'
sil [ossa] @test_merge_alloc : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
cond_br %cond, bb1, bb2
bb1:
%c = alloc_stack $Int
br bb3
bb2:
br bb3
bb3:
dealloc_stack %a
dealloc_stack %b
unreachable
}
// We need to delete the dealloc_stack in bb3 because the stack is in
// an incoherent state entering bb3.
// CHECK-LABEL: sil [ossa] @test_merge_dealloc
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: unreachable
// CHECK-LABEL: // end sil function 'test_merge_dealloc'
sil [ossa] @test_merge_dealloc : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
cond_br %cond, bb1, bb2
bb1:
dealloc_stack %b
br bb3
bb2:
br bb3
bb3:
dealloc_stack %a
unreachable
}
// We need to delete the dealloc_stacks in bb3 because the stack is in
// an incoherent state entering bb3. The allocation and deallocation in
// the loop is required, however.
// CHECK-LABEL: sil @test_merge_alloc_dead_end_loop
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: br bb4
// CHECK: bb4:
// CHECK-NEXT: [[D:%.*]] = alloc_stack $Int
// CHECK-NEXT: br bb5
// CHECK: bb5:
// CHECK-NEXT: dealloc_stack [[D]]
// CHECK-NEXT: br bb4
// CHECK-LABEL: // end sil function 'test_merge_alloc_dead_end_loop'
sil @test_merge_alloc_dead_end_loop : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%b = alloc_stack $Int
cond_br %cond, bb1, bb2
bb1:
%c = alloc_stack $Int
br bb3
bb2:
br bb3
bb3:
dealloc_stack %b
dealloc_stack %a
br bb4
bb4:
%d = alloc_stack $Int
br bb5
bb5:
dealloc_stack %d
br bb4
}
// We only re-order b and c. We leave a alone.
// CHECK-LABEL: sil [ossa] @test_simple_non_nested
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-LABEL: // end sil function 'test_simple_non_nested'
sil [ossa] @test_simple_non_nested : $@convention(thin) (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack [non_nested] $Int
%b = alloc_stack $Int
%c = alloc_stack $Int
integer_literal $Builtin.Int32, 2
dealloc_stack %a
dealloc_stack %b
dealloc_stack %c
integer_literal $Builtin.Int32, 3
%ret = tuple ()
return %ret : $()
}
sil private @async_let_helper : $@convention(thin) @Sendable @async () -> (@out Int, @error any Error) {
bb0(%out : $*Int):
unreachable
}
// b and c must be marked as non-nested
// CHECK-LABEL: sil [ossa] @test_async_let_non_nested
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK: [[PTR:%.*]] = address_to_pointer [[A]] to $Builtin.RawPointer
// CHECK-NEXT: [[LET:%.*]] = builtin "startAsyncLetWithLocalBuffer"<Int>({{%.*}}, {{%.*}}, [[PTR]])
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: builtin "finishAsyncLet"([[LET]], [[PTR]])
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_async_let_non_nested'
sil [ossa] @test_async_let_non_nested : $@convention(thin) @async (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%fn = function_ref @async_let_helper : $@convention(thin) @Sendable @async () -> (@out Int, @error any Error)
%async_let_fn = thin_to_thick_function %fn to $@Sendable @async () -> (@out Int, @error any Error)
%scratch = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
%resultPtr = address_to_pointer %a to $Builtin.RawPointer
%al = builtin "startAsyncLetWithLocalBuffer"<Int>(%scratch, %fn, %resultPtr) : $Builtin.RawPointer
integer_literal $Builtin.Int32, 2
%b = alloc_stack $Int
%c = alloc_stack $Int
dealloc_stack %b
integer_literal $Builtin.Int32, 3
%out = builtin "finishAsyncLet"(%al, %resultPtr) : $()
dealloc_stack %a
dealloc_stack %c
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}
// Make sure we don't panic when there's still an already-deallocated
// async let on the state stack.
// CHECK-LABEL: sil [ossa] @test_two_async_lets :
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK: [[PTR:%.*]] = address_to_pointer [[A]] to $Builtin.RawPointer
// CHECK-NEXT: [[LET1:%.*]] = builtin "startAsyncLetWithLocalBuffer"<Int>({{%.*}}, {{%.*}}, [[PTR]])
// CHECK-NEXT: [[LET2:%.*]] = builtin "startAsyncLetWithLocalBuffer"<Int>({{%.*}}, {{%.*}}, [[PTR]])
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: [[B:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack $Int
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: builtin "finishAsyncLet"([[LET2]], [[PTR]])
// CHECK-NEXT: builtin "finishAsyncLet"([[LET1]], [[PTR]])
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_two_async_lets'
sil [ossa] @test_two_async_lets : $@convention(thin) @async (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
// Reusing result pointers and passing nil as the scratch space are not
// actually legitimate at runtime, but it's convenient and doesn't affect
// what we're testing.
%fn = function_ref @async_let_helper : $@convention(thin) @Sendable @async () -> (@out Int, @error any Error)
%async_let_fn = thin_to_thick_function %fn to $@Sendable @async () -> (@out Int, @error any Error)
%scratch = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
%resultPtr = address_to_pointer %a to $Builtin.RawPointer
%al1 = builtin "startAsyncLetWithLocalBuffer"<Int>(%scratch, %fn, %resultPtr) : $Builtin.RawPointer
%al2 = builtin "startAsyncLetWithLocalBuffer"<Int>(%scratch, %fn, %resultPtr) : $Builtin.RawPointer
integer_literal $Builtin.Int32, 2
%b = alloc_stack $Int
%c = alloc_stack $Int
dealloc_stack %c
integer_literal $Builtin.Int32, 3
%out2 = builtin "finishAsyncLet"(%al2, %resultPtr) : $()
%out1 = builtin "finishAsyncLet"(%al1, %resultPtr) : $()
dealloc_stack %b
dealloc_stack %a
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}
sil private @cancellation_helper : $@convention(thin) () -> () {
bb0:
unreachable
}
// The cancellation handler builtins cannot be reordered.
// CHECK-LABEL: sil [ossa] @test_cancellation_non_nested :
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK: [[HANDLER:%.*]] = builtin "taskAddCancellationHandler"({{%.*}})
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[D:%.*]] = alloc_stack $Int
// CHECK-NEXT: dealloc_stack [[D]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: builtin "taskRemoveCancellationHandler"([[HANDLER]])
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_cancellation_non_nested'
sil [ossa] @test_cancellation_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%fn = function_ref @cancellation_helper : $@convention(thin) () -> ()
%thick_fn = thin_to_thick_function %fn to $@noescape @callee_guaranteed () -> ()
%handler = builtin "taskAddCancellationHandler"(%thick_fn : $@noescape @callee_guaranteed () -> ()) : $UnsafeRawPointer
integer_literal $Builtin.Int32, 2
%b = alloc_stack $Int
%c = alloc_stack $Int
%d = alloc_stack $Int
dealloc_stack %d
dealloc_stack %b
integer_literal $Builtin.Int32, 3
%out = builtin "taskRemoveCancellationHandler"(%handler) : $()
dealloc_stack %a
dealloc_stack %c
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}
sil private @priority_helper : $@convention(thin) (UInt8, UInt8) -> () {
bb0(%0 : $UInt8, %1 : $UInt8):
unreachable
}
// The priority escalation handler builtins cannot be reordered.
// CHECK-LABEL: sil [ossa] @test_priority_non_nested :
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK: [[HANDLER:%.*]] = builtin "taskAddPriorityEscalationHandler"({{%.*}})
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[D:%.*]] = alloc_stack $Int
// CHECK-NEXT: dealloc_stack [[D]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: builtin "taskRemovePriorityEscalationHandler"([[HANDLER]])
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_priority_non_nested'
sil [ossa] @test_priority_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%fn = function_ref @priority_helper : $@convention(thin) (UInt8, UInt8) -> ()
%thick_fn = thin_to_thick_function %fn to $@noescape @callee_guaranteed (UInt8, UInt8) -> ()
%handler = builtin "taskAddPriorityEscalationHandler"(%thick_fn) : $UnsafeRawPointer
integer_literal $Builtin.Int32, 2
%b = alloc_stack $Int
%c = alloc_stack $Int
%d = alloc_stack $Int
dealloc_stack %d
dealloc_stack %b
integer_literal $Builtin.Int32, 3
%out = builtin "taskRemovePriorityEscalationHandler"(%handler) : $()
dealloc_stack %a
dealloc_stack %c
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}
sil [ossa] @partial_apply_helper : $@convention(thin) (@in_guaranteed Int) -> () {
bb0(%0: $*Int):
unreachable
}
// partial_apply [on_stack] must be markable as non-nested
// CHECK-LABEL: sil @test_mark_partial_apply_non_nested :
// CHECK: [[FN:%.*]] = function_ref @partial_apply_helper
// CHECK: partial_apply [callee_guaranteed] [on_stack] [non_nested] [[FN]](
// CHECK-LABEL: // end sil function 'test_mark_partial_apply_non_nested'
sil @test_mark_partial_apply_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
%a = alloc_stack $Int
%fn = function_ref @priority_helper : $@convention(thin) (UInt8, UInt8) -> ()
%thick_fn = thin_to_thick_function %fn to $@noescape @callee_guaranteed (UInt8, UInt8) -> ()
%handler = builtin "taskAddPriorityEscalationHandler"(%thick_fn) : $UnsafeRawPointer
%pafn = function_ref @partial_apply_helper : $@convention(thin) (@in_guaranteed Int) -> ()
%b = partial_apply [callee_guaranteed] [on_stack] %pafn(%a) : $@convention(thin) (@in_guaranteed Int) -> ()
%out = builtin "taskRemovePriorityEscalationHandler"(%handler) : $()
dealloc_stack %b
dealloc_stack %a
%ret = tuple ()
return %ret : $()
}
// alloc_ref [stack] must be markable as non-nested
// CHECK-LABEL: sil [ossa] @test_mark_alloc_ref_non_nested :
// CHECK: alloc_ref [stack] [non_nested] $MyClass
// CHECK-LABEL: // end sil function 'test_mark_alloc_ref_non_nested'
sil [ossa] @test_mark_alloc_ref_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
%fn = function_ref @priority_helper : $@convention(thin) (UInt8, UInt8) -> ()
%thick_fn = thin_to_thick_function %fn to $@noescape @callee_guaranteed (UInt8, UInt8) -> ()
%handler = builtin "taskAddPriorityEscalationHandler"(%thick_fn) : $UnsafeRawPointer
%b = alloc_ref [stack] $MyClass
%out = builtin "taskRemovePriorityEscalationHandler"(%handler) : $()
destroy_value %b
dealloc_stack_ref %b
%ret = tuple ()
return %ret : $()
}
// alloc_ref_dynamic [stack] must be markable as non-nested
// CHECK-LABEL: sil [ossa] @test_mark_alloc_ref_dynamic_non_nested :
// CHECK: [[METATYPE:%.*]] = metatype $@thick MyClass.Type
// CHECK: alloc_ref_dynamic [stack] [non_nested] [[METATYPE]]
// CHECK-LABEL: // end sil function 'test_mark_alloc_ref_dynamic_non_nested'
sil [ossa] @test_mark_alloc_ref_dynamic_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
%fn = function_ref @priority_helper : $@convention(thin) (UInt8, UInt8) -> ()
%thick_fn = thin_to_thick_function %fn to $@noescape @callee_guaranteed (UInt8, UInt8) -> ()
%handler = builtin "taskAddPriorityEscalationHandler"(%thick_fn) : $UnsafeRawPointer
%metatype = metatype $@thick MyClass.Type
%b = alloc_ref_dynamic [stack] %metatype, $MyClass
%out = builtin "taskRemovePriorityEscalationHandler"(%handler) : $()
destroy_value %b
dealloc_stack_ref %b
%ret = tuple ()
return %ret : $()
}
// The task local value builtins cannot be reordered.
// CHECK-LABEL: sil [ossa] @test_task_local_non_nested :
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = alloc_stack $Int
// CHECK: [[BINDING:%.*]] = builtin "addTaskLocalValue"<Int>({{.*}}, [[A]])
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: [[B:%.*]] = alloc_stack $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[D:%.*]] = alloc_stack $Int
// CHECK-NEXT: dealloc_stack [[D]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: builtin "removeTaskLocalValue"([[BINDING]])
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: dealloc_stack [[A]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_task_local_non_nested'
sil [ossa] @test_task_local_non_nested : $@convention(thin) @async () -> () {
bb0:
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%a = alloc_stack $Int
%key = address_to_pointer %a to $Builtin.RawPointer
%binding = builtin "addTaskLocalValue"<Int>(%key, %a) : $Builtin.RawPointer
integer_literal $Builtin.Int32, 2
%b = alloc_stack $Int
%c = alloc_stack $Int
%d = alloc_stack $Int
dealloc_stack %d
dealloc_stack %b
integer_literal $Builtin.Int32, 3
%out = builtin "removeTaskLocalValue"(%binding) : $()
dealloc_stack %a
dealloc_stack %c
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}
// We must not defer deallocations after deciding that they need to be
// [non_nested], or else we can get inconsistencies after join points.
// CHECK-LABEL: sil [ossa] @test_pending_async_let :
// CHECK: integer_literal $Builtin.Int32, 1
// CHECK-NEXT: [[A:%.*]] = builtin "startAsyncLetWithLocalBuffer"<Int>(
// CHECK-NEXT: [[B:%.*]] = alloc_stack [non_nested] $Int
// CHECK-NEXT: [[C:%.*]] = alloc_stack [non_nested] $Int
// CHECK: bb1:
// Note that we've deferred the deallocation of %b (by a single instruction).
// CHECK-NEXT: integer_literal $Builtin.Int32, 2
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: builtin "finishAsyncLet"([[A]], {{.*}})
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: builtin "finishAsyncLet"([[A]], {{.*}})
// CHECK-NEXT: dealloc_stack [[B]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 3
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: dealloc_stack [[C]]
// CHECK-NEXT: integer_literal $Builtin.Int32, 4
// CHECK-LABEL: // end sil function 'test_pending_async_let'
sil [ossa] @test_pending_async_let : $@convention(thin) @async (Builtin.Int1) -> () {
bb0(%cond: $Builtin.Int1):
specify_test "stack_nesting_fixup"
integer_literal $Builtin.Int32, 1
%al = builtin "startAsyncLetWithLocalBuffer"<Int>(undef: $Optional<Builtin.RawPointer>, undef: $@convention(thick) @Sendable @async () -> (@out Int, @error any Error), undef: $Builtin.RawPointer) : $Builtin.RawPointer
%b = alloc_stack $Int
%c = alloc_stack $Int
cond_br undef, bb1, bb2
bb1:
dealloc_stack %b
integer_literal $Builtin.Int32, 2
%out = builtin "finishAsyncLet"(%al, undef: $Builtin.RawPointer) : $()
br bb3
bb2:
%out2 = builtin "finishAsyncLet"(%al, undef: $Builtin.RawPointer) : $()
dealloc_stack %b
integer_literal $Builtin.Int32, 3
br bb3
bb3:
dealloc_stack %c
integer_literal $Builtin.Int32, 4
%ret = tuple ()
return %ret : $()
}