Files
swift-mirror/test/SIL/verifier-fail-stack.sil
John McCall 90995b82f4 Teach the SIL verifier to enforce stronger rules around
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.
2025-10-11 02:12:19 -04:00

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
}