mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
8cda95bca4
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.
701 lines
23 KiB
Plaintext
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 : $()
|
|
}
|