mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
edges into dead-end regions. - Only treat edges *into* dead-end regions as special; edges internal the region must use the normal rules. - Conservatively merge information along those edges rather than just picking one at random. This requires us to not walk into the region until we've processed all of the edges to it. - Make sure we prevent *any* stack allocations from being deallocated if the stack is inconsistent entering a block. Additionally, fix a bug which was incorrectly treating all blocks that don't themselves exit the function as ultimately leading to unreachable, which had inadvertently largely turned off the consistency check.
334 lines
7.1 KiB
Plaintext
334 lines
7.1 KiB
Plaintext
// RUN: %target-sil-opt -verify-continue-on-failure -o /dev/null %s 2>&1 | %FileCheck %s
|
|
|
|
// REQUIRES: asserts
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
|
|
// Check that join points normally require the stack to match.
|
|
// CHECK-LABEL: Begin Error in function require_match_1
|
|
// CHECK-LABEL: Begin Error in function require_match_1
|
|
// CHECK: SIL verification failed: inconsistent stack states entering basic block
|
|
// CHECK-NEXT: Entering basic block bb3
|
|
// CHECK-NEXT: Current stack state: []
|
|
// CHECK-NEXT: Recorded stack state: [
|
|
// CHECK-NEXT: %0 = alloc_stack $Builtin.Int32
|
|
// CHECK-NEXT: ]
|
|
// CHECK-LABEL: End Error in function require_match_1
|
|
sil @require_match_1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%result = tuple ()
|
|
return %result : $()
|
|
}
|
|
|
|
// Same as above, just with the branches switched around.
|
|
// CHECK-LABEL: Begin Error in function require_match_2
|
|
// CHECK-LABEL: Begin Error in function require_match_2
|
|
// CHECK: SIL verification failed: inconsistent stack states entering basic block
|
|
// CHECK-NEXT: Entering basic block bb3
|
|
// CHECK-NEXT: Current stack state: [
|
|
// CHECK-NEXT: %0 = alloc_stack $Builtin.Int32
|
|
// CHECK-NEXT: ]
|
|
// CHECK-NEXT: Recorded stack state: []
|
|
// CHECK-LABEL: End Error in function require_match_2
|
|
sil @require_match_2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb3:
|
|
%result = tuple ()
|
|
return %result : $()
|
|
}
|
|
|
|
// Check that such a join point is okay if it's a branch into a dead-end region.
|
|
// CHECK-NOT: Begin Error in function merge_unreachable_okay_1
|
|
sil @merge_unreachable_okay_1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
unreachable
|
|
}
|
|
|
|
// Same as above, just with the branches switched around.
|
|
// CHECK-NOT: Begin Error in function merge_unreachable_okay_1
|
|
sil @merge_unreachable_okay_2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb3:
|
|
unreachable
|
|
}
|
|
|
|
// Check that it's not okay to subsequently dealloc the allocation.
|
|
// CHECK-LABEL: Begin Error in function merge_unreachable_then_dealloc_1
|
|
// CHECK: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
// CHECK-NEXT: Current stack state: []
|
|
// CHECK-NEXT: Stack allocation:
|
|
// CHECK-NEXT: %0 = alloc_stack $Builtin.Int32
|
|
// CHECK-LABEL: End Error in function merge_unreachable_then_dealloc_1
|
|
sil @merge_unreachable_then_dealloc_1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
dealloc_stack %0
|
|
unreachable
|
|
}
|
|
|
|
// Same as above, just with the branches switched around.
|
|
// CHECK-LABEL: Begin Error in function merge_unreachable_then_dealloc_2
|
|
// CHECK: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
// CHECK-NEXT: Current stack state: []
|
|
// CHECK-NEXT: Stack allocation:
|
|
// CHECK-NEXT: %0 = alloc_stack $Builtin.Int32
|
|
// CHECK-LABEL: End Error in function merge_unreachable_then_dealloc_2
|
|
sil @merge_unreachable_then_dealloc_2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb3:
|
|
dealloc_stack %0
|
|
unreachable
|
|
}
|
|
|
|
// Parallel branches with inconsistent stack state into a dead-end loop
|
|
// CHECK-NOT: Begin Error in function parallel_branches_into_dead_1
|
|
sil @parallel_branches_into_dead_1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
}
|
|
|
|
// Same as above, just with the dealloc switched around to trigger
|
|
// a different visitation pattern.
|
|
// CHECK-NOT: Begin Error in function parallel_branches_into_dead_2
|
|
sil @parallel_branches_into_dead_2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
}
|
|
|
|
// Add an unreachable block that also branches to the dead-end region to
|
|
// make sure we don't fail to visit anything.
|
|
// CHECK-NOT: Begin Error in function parallel_branches_into_dead_with_unreachable_block
|
|
sil @parallel_branches_into_dead_with_unreachable_block : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
|
|
bb5: // unreachable
|
|
br bb3
|
|
}
|
|
|
|
// Parallel branches with inconsistent stack state into a dead-end loop
|
|
// that contains a dealloc
|
|
// CHECK-LABEL: Begin Error in function parallel_branches_into_dead_dealloc_1
|
|
// CHECK-NEXT: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
sil @parallel_branches_into_dead_dealloc_1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
}
|
|
|
|
// Same as above, just with the dealloc switched around to trigger
|
|
// a different visitation pattern.
|
|
// CHECK-LABEL: Begin Error in function parallel_branches_into_dead_dealloc_2
|
|
// CHECK-NEXT: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
sil @parallel_branches_into_dead_dealloc_2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
dealloc_stack %0
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
dealloc_stack %0
|
|
br bb3
|
|
}
|
|
|
|
// Yet another visitation pattern.
|
|
// CHECK-LABEL: Begin Error in function parallel_branches_into_dead_dealloc_3
|
|
// CHECK-NEXT: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
sil @parallel_branches_into_dead_dealloc_3 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb3:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
}
|
|
|
|
// Yet another visitation pattern.
|
|
// CHECK-LABEL: Begin Error in function parallel_branches_into_dead_dealloc_4
|
|
// CHECK-NEXT: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
sil @parallel_branches_into_dead_dealloc_4 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
dealloc_stack %0
|
|
br bb3
|
|
}
|
|
|
|
// And again, add an unreachable block.
|
|
// CHECK-LABEL: Begin Error in function parallel_branches_into_dead_dealloc_with_unreachable_block
|
|
// CHECK-NEXT: SIL verification failed: deallocating allocation that is not the top of the stack
|
|
sil @parallel_branches_into_dead_dealloc_with_unreachable_block : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $Builtin.Int32
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3
|
|
|
|
bb2:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb3:
|
|
dealloc_stack %0
|
|
br bb4
|
|
|
|
bb4:
|
|
br bb3
|
|
|
|
bb5: // unreachable
|
|
br bb3
|
|
}
|