Merge pull request #84811 from rjmccall/verify-dead-end-edges

Strengthen the SIL verifier's rules for edges into dead-end regions
This commit is contained in:
John McCall
2025-10-11 18:16:56 -04:00
committed by GitHub
11 changed files with 1592 additions and 154 deletions

View File

@@ -55,11 +55,16 @@ bb3:
return %1 : $()
}
// CHECK: Begin Error in function test_missing_end_borrow
// CHECK: SIL verification failed: inconsistent active-operations sets entering basic block: state.ActiveOps == foundState.ActiveOps || isUnreachable()
// CHECK: Verifying instruction:
// CHECK: -> br bb3 // id: %5
// CHECK: End Error in function test_missing_end_borrow
// CHECK: Begin Error in function test_missing_end_borrow
// CHECK-NEXT: SIL verification failed: inconsistent active-operations sets entering basic block
// CHECK-NEXT: Entering basic block bb3
// CHECK-NEXT: Current active operations: []
// CHECK-NEXT: Recorded active operations: [
// CHECK-NEXT: %2 = store_borrow %0 to %1 : $*Klass
// CHECK-NEXT: ]
// CHECK-NEXT: Verifying instruction:
// CHECK-NEXT: -> br bb3 // id: %5
// CHECK-NEXT: End Error in function test_missing_end_borrow
sil [ossa] @test_missing_end_borrow_dead : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%stk = alloc_stack $Klass

View File

@@ -0,0 +1,333 @@
// 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
}

View File

@@ -19,7 +19,10 @@ sil @alloc_pack_metadata_before_tuple : $@convention(thin) () -> () {
}
// CHECK-LABEL: Begin Error in function dealloc_pack_metadata_with_bad_operand
// CHECK: SIL verification failed: stack dealloc does not match most recent stack alloc:
// CHECK: SIL verification failed: deallocating allocation that is not the top of the stack
// CHECK-LABEL: End Error in function dealloc_pack_metadata_with_bad_operand
// CHECK-LABEL: Begin Error in function dealloc_pack_metadata_with_bad_operand
// CHECK: SIL verification failed: return with stack allocs that haven't been deallocated
// CHECK-LABEL: End Error in function dealloc_pack_metadata_with_bad_operand
// CHECK-LABEL: Begin Error in function dealloc_pack_metadata_with_bad_operand
// CHECK: SIL verification failed: Must have alloc_pack_metadata operand