Files
swift-mirror/test/SILOptimizer/mem2reg_lifetime_borrows.sil
Meghana Gupta 0b835f0080 Fix ownership verification error due to multi block mem2reg with store_borrows
@guaranteed stored values will never be added to a phi in mem2reg because
any uses of store borrowed locations have to be accessed via the store borrow return address
and since we ban address phis altogether we will never have input sil which necessitates this.

But, the DJ-edges based algorithm for inserting phi blocks in mem2reg can insert unnecessary phis
which are removed later.

Ensure fixPhiPredBlock handles both owned and guaranteed stored values correctly for this reason.
2023-04-21 12:29:18 -07:00

564 lines
17 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all -enable-lexical-lifetimes %s -mem2reg | %FileCheck %s
import Builtin
class Klass {}
struct WrapperStruct {
var val:Klass
}
public enum FakeOptional {
case none
case some(Klass)
}
sil [ossa] @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
sil [ossa] @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
sil [ossa] @use_owned : $@convention(thin) (@owned Klass) -> ()
sil [ossa] @get_owned : $@convention(thin) () -> @owned Klass
// CHECK-LABEL: sil [ossa] @test_no_storeborrow1 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_no_storeborrow1'
sil [ossa] @test_no_storeborrow1 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%stk = alloc_stack [lexical] $Klass
store %0 to [init] %stk : $*Klass
%ld = load_borrow %stk : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%ld) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %ld : $Klass
destroy_addr %stk : $*Klass
dealloc_stack %stk : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test_no_storeborrow2 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_no_storeborrow2'
sil [ossa] @test_no_storeborrow2 : $@convention(method) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%stk = alloc_stack [lexical] $Klass
store %0 to [init] %stk : $*Klass
%ld1 = load_borrow %stk : $*Klass
%f = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%ld1) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %ld1 : $Klass
cond_br undef, bb3, bb1
bb1:
%ld2 = load_borrow %stk : $*Klass
apply %f(%ld2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %ld2 : $Klass
%tk1 = load [take] %stk : $*Klass
destroy_value %tk1 : $Klass
dealloc_stack %stk : $*Klass
br bb4
bb3:
%tk2 = load [take] %stk : $*Klass
destroy_value %tk2 : $Klass
dealloc_stack %stk : $*Klass
br bb4
bb4:
%ret = tuple ()
return %ret : $()
}
// CHECK-LABEL: sil [ossa] @test_no_storeborrow3 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_no_storeborrow3'
sil [ossa] @test_no_storeborrow3 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] $Klass
store %0 to [init] %1 : $*Klass
%2 = load_borrow %1 : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test_no_storeborrow4 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_no_storeborrow4'
sil [ossa] @test_no_storeborrow4 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] $Klass
store %0 to [init] %1 : $*Klass
%2 = load_borrow %1 : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// load_borrow of projections are not optimized
// CHECK-LABEL: sil [ossa] @test_with_structs_and_borrows1 :
// CHECK: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_with_structs_and_borrows1'
sil [ossa] @test_with_structs_and_borrows1 : $@convention(thin) (@guaranteed WrapperStruct) -> () {
bb0(%0 : @guaranteed $WrapperStruct):
%stk = alloc_stack [lexical] $WrapperStruct
%sb = store_borrow %0 to %stk : $*WrapperStruct
%ele = struct_element_addr %sb : $*WrapperStruct, #WrapperStruct.val
%ld = load_borrow %ele : $*Klass
%f = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
apply %f(%ld) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %ld : $Klass
end_borrow %sb : $*WrapperStruct
dealloc_stack %stk : $*WrapperStruct
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @storeborrow_only_lexicalallocas :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'storeborrow_only_lexicalallocas'
sil [ossa] @storeborrow_only_lexicalallocas : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack [lexical] $Klass
%sb = store_borrow %0 to %1 : $*Klass
end_borrow %sb : $*Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test1 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test1'
sil [ossa] @test1 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack [lexical] $Klass
%sb = store_borrow %0 to %1 : $*Klass
%2 = load_borrow %sb : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
end_borrow %sb : $*Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test2 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test2'
sil [ossa] @test2 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack [lexical] $Klass
%sb = store_borrow %0 to %1 : $*Klass
%2 = load_borrow %sb : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
%5 = load_borrow %sb : $*Klass
%6 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%7 = apply %3(%5) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %5 : $Klass
end_borrow %sb : $*Klass
dealloc_stack %1 : $*Klass
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @test3 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test3'
sil [ossa] @test3 : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass):
%2 = alloc_stack [lexical] $Klass
%sb1 = store_borrow %0 to %2 : $*Klass
%3 = load_borrow %sb1 : $*Klass
%4 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %4(%3) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %3 : $Klass
end_borrow %sb1 : $*Klass
%sb2 = store_borrow %1 to %2 : $*Klass
%6 = load_borrow %sb2 : $*Klass
%7 = apply %4(%6) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %6 : $Klass
end_borrow %sb2 : $*Klass
dealloc_stack %2 : $*Klass
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @test4 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test4'
sil [ossa] @test4 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack [lexical] $Klass
%sb = store_borrow %0 to %1 : $*Klass
%2 = load_borrow %sb : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
%5 = load [copy] %sb : $*Klass
end_borrow %sb : $*Klass
%6 = function_ref @use_owned : $@convention(thin) (@owned Klass) -> ()
%7 = apply %6(%5) : $@convention(thin) (@owned Klass) -> ()
dealloc_stack %1 : $*Klass
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil [ossa] @test6 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test6'
sil [ossa] @test6 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] $Klass
%b = begin_borrow %0 : $Klass
%sb = store_borrow %b to %1 : $*Klass
%2 = load_borrow %sb : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
end_borrow %sb : $*Klass
end_borrow %b : $Klass
destroy_value %0 : $Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test7 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test7'
sil [ossa] @test7 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] $Klass
%b = begin_borrow %0 : $Klass
%sb = store_borrow %b to %1 : $*Klass
%2 = load_borrow %sb : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
end_borrow %sb : $*Klass
end_borrow %b : $Klass
destroy_value %0 : $Klass
unreachable
}
// CHECK-LABEL: sil [ossa] @test8 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test8'
sil [ossa] @test8 : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack [lexical] $Klass
%b = begin_borrow %0 : $Klass
%sb = store_borrow %b to %1 : $*Klass
%2 = load [copy] %sb : $*Klass
end_borrow %sb : $*Klass
end_borrow %b : $Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
destroy_value %2 : $Klass
destroy_value %0 : $Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test_control_flow4 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_control_flow4'
sil [ossa] @test_control_flow4 : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%stk = alloc_stack [lexical] $Klass
%sb = store_borrow %0 to %stk : $*Klass
cond_br undef, bb1, bb2
bb1:
%ld1 = load_borrow %sb : $*Klass
fix_lifetime %ld1 : $Klass
end_borrow %ld1 : $Klass
end_borrow %sb : $*Klass
dealloc_stack %stk : $*Klass
%r = tuple ()
return %r : $()
bb2:
%ld2 = load_borrow %sb : $*Klass
fix_lifetime %ld2 : $Klass
end_borrow %ld2 : $Klass
end_borrow %sb : $*Klass
dealloc_stack %stk : $*Klass
unreachable
}
// CHECK-LABEL: sil [ossa] @test_control_flow5 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_control_flow5'
sil [ossa] @test_control_flow5 : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass):
%stk = alloc_stack [lexical] $Klass
cond_br undef, bb1, bb2
bb1:
%copy1 = copy_value %0 : $Klass
store %copy1 to [init] %stk : $*Klass
br bb3
bb2:
%copy2 = copy_value %1 : $Klass
store %copy2 to [init] %stk : $*Klass
br bb3
bb3:
%2 = load_borrow %stk : $*Klass
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %2 : $Klass
destroy_addr %stk : $*Klass
dealloc_stack %stk : $*Klass
%8 = tuple ()
return %8 : $()
}
// Test no ownership verifier error
// CHECK-LABEL: sil [ossa] @test_control_flow6 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_control_flow6'
sil [ossa] @test_control_flow6 : $@convention(thin) () -> () {
bb0:
%0 = function_ref @get_owned : $@convention(thin) () -> @owned Klass
%1 = alloc_stack [lexical] $Klass
%2 = apply %0() : $@convention(thin) () -> @owned Klass
store %2 to [init] %1 : $*Klass
cond_br undef, bb1, bb2
bb1:
%5 = apply %0() : $@convention(thin) () -> @owned Klass
store %5 to [assign] %1 : $*Klass
br bb3
bb2:
br bb3
bb3:
%9 = load_borrow %1 : $*Klass
end_borrow %9 : $Klass
br bb4
bb4:
cond_br undef, bb8, bb9
bb8:
br bb4
bb9:
cond_br undef, bb10, bb11
bb10:
br bb12
bb11:
%19 = load [copy] %1 : $*Klass
%20 = function_ref @use_owned : $@convention(thin) (@owned Klass) -> ()
%21 = apply %20(%19) : $@convention(thin) (@owned Klass) -> ()
br bb12
bb12:
%23 = apply %0() : $@convention(thin) () -> @owned Klass
store %23 to [assign] %1 : $*Klass
cond_br undef, bb13, bb14
bb13:
%26 = load [take] %1 : $*Klass
destroy_value %26 : $Klass
dealloc_stack %1 : $*Klass
%29 = tuple ()
return %29 : $()
bb14:
unreachable
}
// CHECK-LABEL: sil [ossa] @test_control_flow7 :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_control_flow7'
sil [ossa] @test_control_flow7 : $@convention(thin) () -> () {
bb0:
%4 = alloc_stack [lexical] $Klass
%f = function_ref @get_owned : $@convention(thin) () -> @owned Klass
%5 = apply %f() : $@convention(thin) () -> @owned Klass
store %5 to [init] %4 : $*Klass
%7 = load_borrow %4 : $*Klass
end_borrow %7 : $Klass
cond_br undef, bb1, bb2
bb1:
br bb3
bb2:
%28 = load_borrow %4 : $*Klass
end_borrow %28 : $Klass
br bb3
bb3:
destroy_addr %4 : $*Klass
dealloc_stack %4 : $*Klass
%r = tuple ()
return %r : $()
}
// Test no ownership verifier error
// CHECK-LABEL: sil [ossa] @test_two_stacklocs :
// CHECK: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_two_stacklocs'
sil [ossa] @test_two_stacklocs : $@convention(method) (@guaranteed WrapperStruct) -> () {
bb0(%0 : @guaranteed $WrapperStruct):
%2 = struct_extract %0 : $WrapperStruct, #WrapperStruct.val
%3 = alloc_stack [lexical] $Klass
%4 = store_borrow %2 to %3 : $*Klass
%5 = load_borrow %4 : $*Klass
%6 = alloc_stack [lexical] $Klass
%7 = store_borrow %5 to %6 : $*Klass
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %f(%7) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_borrow %7 : $*Klass
end_borrow %5 : $Klass
end_borrow %4 : $*Klass
dealloc_stack %6 : $*Klass
dealloc_stack %3 : $*Klass
%t = tuple ()
return %t : $()
}
// CHECK-LABEL: sil [ossa] @test_optional :
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_optional'
sil [ossa] @test_optional : $@convention(thin) () -> () {
bb0:
%stk = alloc_stack [lexical] $FakeOptional
%none = enum $FakeOptional, #FakeOptional.none!enumelt
%sb = store_borrow %none to %stk : $*FakeOptional
%ld1 = load_borrow %sb : $*FakeOptional
end_borrow %ld1 : $FakeOptional
end_borrow %sb : $*FakeOptional
dealloc_stack %stk : $*FakeOptional
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_deinit_point :
// CHECK: alloc_stack
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_deinit_point'
sil [ossa] @test_deinit_point : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%stk = alloc_stack [lexical] $Klass
cond_br undef, bb1, bb2
bb1:
%tmp = alloc_stack $Klass
%sb = store_borrow %0 to %tmp : $*Klass
%f = function_ref @use_inguaranteed : $@convention(thin) (@in_guaranteed Klass) -> ()
%c = apply %f(%sb) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_borrow %sb : $*Klass
dealloc_stack %tmp : $*Klass
%copy = copy_value %0 : $Klass
store %copy to [init] %stk : $*Klass
%l = load [take] %stk : $*Klass
destroy_value %l : $Klass
br bb3
bb2:
br bb3
bb3:
dealloc_stack %stk : $*Klass
%r = tuple ()
return %r : $()
}
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'storeborrow_multi_only_lexicalallocas'
sil [ossa] @storeborrow_multi_only_lexicalallocas : $@convention(thin) (@in_guaranteed Klass) -> () {
bb0(%0 : $*Klass):
%l = load_borrow %0 : $*Klass
%1 = alloc_stack [lexical] $Klass
%sb = store_borrow %l to %1 : $*Klass
br bb1
bb1:
end_borrow %sb : $*Klass
end_borrow %l : $Klass
dealloc_stack %1 : $*Klass
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test_nested_sbi :
// CHECK: alloc_stack
// CHECK-NOT: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'test_nested_sbi'
sil [ossa] @test_nested_sbi : $@convention(thin) (@guaranteed WrapperStruct) -> () {
bb0(%0 : @guaranteed $WrapperStruct):
%2 = alloc_stack [lexical] $WrapperStruct
%3 = store_borrow %0 to %2 : $*WrapperStruct
%6 = load_borrow %3 : $*WrapperStruct
%7 = alloc_stack [lexical] $WrapperStruct
%8 = store_borrow %6 to %7 : $*WrapperStruct
%9 = struct_element_addr %8 : $*WrapperStruct, #WrapperStruct.val
%16 = load_borrow %9 : $*Klass
end_borrow %16 : $Klass
end_borrow %8 : $*WrapperStruct
dealloc_stack %7 : $*WrapperStruct
end_borrow %6 : $WrapperStruct
%27 = load_borrow %3 : $*WrapperStruct
end_borrow %27 : $WrapperStruct
end_borrow %3 : $*WrapperStruct
dealloc_stack %2 : $*WrapperStruct
%33 = tuple ()
return %33 : $()
}
// Ensure we don't have an ownership verification error
sil [ossa] @test_store_borrow_phi : $@convention(thin) () -> () {
bb0:
br bb1
bb1:
%1 = apply undef() : $@convention(thin) () -> @owned Klass
%2 = begin_borrow %1 : $Klass
%3 = alloc_stack [lexical] $Klass
%4 = store_borrow %2 to %3 : $*Klass
%5 = load_borrow %4 : $*Klass
%6 = apply undef(%5) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %5 : $Klass
end_borrow %4 : $*Klass
cond_br undef, bb2, bb3
bb2:
br bb4
bb3:
br bb4
bb4:
dealloc_stack %3 : $*Klass
end_borrow %2 : $Klass
destroy_value %1 : $Klass
cond_br undef, bb5, bb6
bb5:
br bb1
bb6:
%17 = tuple ()
return %17 : $()
}