mirror of
https://github.com/apple/swift.git
synced 2026-10-20 19:13:47 +02:00
1733 lines
66 KiB
Plaintext
1733 lines
66 KiB
Plaintext
// RUN: %target-sil-opt %s -test-runner -sil-print-types -sil-disable-input-verify | %FileCheck %s --implicit-check-not diagnose
|
|
|
|
sil_stage raw
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
class K {}
|
|
struct C { var k: K }
|
|
struct NC: ~Copyable { var k: K }
|
|
|
|
struct B { var c: C }
|
|
struct A { var b: B }
|
|
struct Two { var a: K; var b: K }
|
|
struct NCTwo: ~Copyable { var a: K; var b: K }
|
|
struct Nested { var p: Two; var k: K }
|
|
|
|
sil @getK : $@convention(thin) () -> @owned K
|
|
sil @useK : $@convention(thin) (@guaranteed K) -> ()
|
|
sil @consumeK : $@convention(thin) (@owned K) -> ()
|
|
sil @consumeTwo : $@convention(thin) (@owned Two) -> ()
|
|
sil @getNC : $@convention(thin) () -> @owned NC
|
|
sil @useNC : $@convention(thin) (@guaranteed NC) -> ()
|
|
sil @consumeNC : $@convention(thin) (@owned NC) -> ()
|
|
sil @getNC_addr : $@convention(thin) () -> @out NC
|
|
sil @consumeNC_addr : $@convention(thin) (@in NC) -> ()
|
|
sil @barrier : $@convention(thin) () -> ()
|
|
sil @useC_addr : $@convention(thin) (@in_guaranteed C) -> ()
|
|
sil @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
sil @consumeK_addr : $@convention(thin) (@in K) -> ()
|
|
sil @produceC : $@convention(thin) () -> @out C
|
|
|
|
// CHECK-LABEL: sil [ossa] @basic_take_of_uninit_box :
|
|
// CHECK: [[ROOT:%.*]] = project_box {{.*}} : ${ var K }, 0
|
|
// CHECK: assign undef to [init] [[ROOT]]
|
|
// CHECK: [[BA:%.*]] = begin_access [deinit] [unknown] [[ROOT]]
|
|
// CHECK-NEXT: [[LD:%.*]] = load [copy] [[BA]]
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[LD]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @basic_take_of_uninit_box : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_box ${ var K }, var, name "a"
|
|
%1 = mark_uninitialized [var] %0 : ${ var K }
|
|
%2 = begin_borrow [lexical] [var_decl] %1 : ${ var K }
|
|
%root = project_box %2 : ${ var K }, 0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%d1 = begin_access [deinit] [unknown] %root : $*K
|
|
%l1 = load [take] %d1 : $*K
|
|
end_access %d1 : $*K
|
|
ignored_use %l1
|
|
destroy_value %l1
|
|
|
|
end_borrow %2
|
|
destroy_value %1
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @basic_take_of_uninit :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: assign undef to [init] [[ROOT]]
|
|
// CHECK: [[BA:%.*]] = begin_access [deinit] [unknown] [[ROOT]]
|
|
// CHECK-NEXT: load [take] [[BA]]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @basic_take_of_uninit : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "x", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%d1 = begin_access [deinit] [unknown] %root : $*K
|
|
%l1 = load [take] %d1 : $*K
|
|
end_access %d1 : $*K
|
|
ignored_use %l1
|
|
destroy_value %l1
|
|
|
|
destroy_addr %root
|
|
dealloc_stack %0
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// FIXME: the `begin_access [deinit]`` should be corrected by LifetimeResolution into a `[read]`
|
|
// CHECK-LABEL: sil [ossa] @basic_double_take :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[BA:%.*]] = begin_access [deinit] [unknown] [[ROOT]] : $*K
|
|
// CHECK-NEXT: [[LD:%.*]] = load [copy] [[BA]] : $*K
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[LD]]
|
|
// CHECK-NEXT: end_access [[BA]] : $*K
|
|
// CHECK-NEXT: ignored_use [[LD]]
|
|
// CHECK: load [take]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @basic_double_take : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "a", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%val = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %val to %root
|
|
|
|
%d1 = begin_access [deinit] [unknown] %root : $*K
|
|
%l1 = load [take] %d1 : $*K
|
|
end_access %d1 : $*K
|
|
ignored_use %l1
|
|
destroy_value %l1
|
|
|
|
%r1 = begin_access [deinit] [unknown] %root : $*K
|
|
%l2 = load [take] %r1 : $*K
|
|
destroy_value %l2 : $K
|
|
end_access %r1 : $*K
|
|
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
|
|
// FIXME: the `begin_access [read]` should be corrected by LifetimeResolution into a `[deinit]` or `[modify]`
|
|
// CHECK-LABEL: sil [ossa] @basic_last_use_is_copy :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[BA:%.*]] = begin_access [read] [unknown] [[ROOT]] : $*K
|
|
// CHECK-NEXT: [[LD:%.*]] = load [take] [[BA]] : $*K
|
|
// CHECK-NEXT: end_access [[BA]] : $*K
|
|
// CHECK-NEXT: ignored_use [[LD]]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @basic_last_use_is_copy : $@convention(thin) () -> () {
|
|
bb0:
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "a", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%val = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %val to %root
|
|
|
|
%d1 = begin_access [read] [unknown] %root : $*K
|
|
%l1 = load [copy] %d1 : $*K
|
|
end_access %d1 : $*K
|
|
ignored_use %l1
|
|
destroy_value %l1
|
|
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nc_borrow_within_access_before_consume :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
|
|
// CHECK: [[BA:%.*]] = begin_access [read] [unknown] [[ROOT]] : $*NC
|
|
// CHECK-NEXT: [[LD:%.*]] = load_borrow [[BA]] : $*NC
|
|
// CHECK-NEXT: apply {{.*}}([[LD]]) : $@convention(thin) (@guaranteed NC) -> ()
|
|
// CHECK-NEXT: end_borrow [[LD]]
|
|
// CHECK-NEXT: end_access [[BA]]
|
|
|
|
// CHECK-NOT: load_borrow
|
|
|
|
// CHECK: [[BA2:%.*]] = begin_access [read] [unknown] [[ROOT]] : $*NC
|
|
// CHECK-NEXT: [[LD2:%.*]] = load_borrow [[BA]] : $*NC
|
|
// CHECK-NEXT: apply {{.*}}([[LD2]]) : $@convention(thin) (@guaranteed NC) -> ()
|
|
// CHECK-NEXT: apply {{.*}}() : $@convention(thin) () -> ()
|
|
// CHECK-NEXT: end_borrow [[LD2]]
|
|
// CHECK-NEXT: end_access [[BA2]]
|
|
|
|
// CHECK-NOT: load_borrow
|
|
// CHECK: load [take]
|
|
// CHECK-NOT: destroy_addr
|
|
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @nc_borrow_within_access_before_consume : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $NC, var, name "a", type $NC
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getNC : $@convention(thin) () -> @owned NC
|
|
%val = apply %getFn() : $@convention(thin) () -> @owned NC
|
|
assign %val to %root
|
|
|
|
// Basic load-and-use within access
|
|
|
|
%useFn = function_ref @useNC : $@convention(thin) (@guaranteed NC) -> ()
|
|
%r1 = begin_access [read] [unknown] %root : $*NC
|
|
%l1 = load [copy] %r1 : $*NC
|
|
apply %useFn(%l1) : $@convention(thin) (@guaranteed NC) -> ()
|
|
destroy_value %l1
|
|
end_access %r1 : $*NC
|
|
|
|
// Same as above, but with a deinit barrier after the apply.
|
|
|
|
%barrierFn = function_ref @barrier : $@convention(thin) () -> ()
|
|
%r2 = begin_access [read] [unknown] %root : $*NC
|
|
%l2 = load [copy] %r1 : $*NC
|
|
apply %useFn(%l2) : $@convention(thin) (@guaranteed NC) -> ()
|
|
apply %barrierFn() : $@convention(thin) () -> ()
|
|
destroy_value %l2
|
|
end_access %r2 : $*NC
|
|
|
|
%consumeFn = function_ref @consumeNC : $@convention(thin) (@owned NC) -> ()
|
|
%c1 = begin_access [deinit] [unknown] %root : $*NC
|
|
%c2 = load [take] %c1 : $*NC
|
|
end_access %c1 : $*NC
|
|
apply %consumeFn(%c2) : $@convention(thin) (@owned NC) -> ()
|
|
|
|
destroy_addr %root : $*NC
|
|
dealloc_stack %0 : $*NC
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Field sensitivity: demand is tracked per leaf subelement, so consumes and uses
|
|
// of distinct fields are independent.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Two independent leaves. Taking `a` and then `b` are each the last use of their
|
|
// own field, so both remain `load [take]`.
|
|
// CHECK-LABEL: sil [ossa] @fs_independent_field_takes :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: store {{%.*}} to [init] [[ROOT]] : $*Two
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: load [take] [[AA]] : $*K
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_independent_field_takes : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
%la = load [take] %aa : $*K
|
|
apply %consFn(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [take] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// take-then-copy --> copy-then-take + diagnostic
|
|
// CHECK-LABEL: sil [ossa] @fs_use_after_consume_same_field :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[A1:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: [[L1:%.*]] = load [copy] [[A1]] : $*K
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[L1]] : $K
|
|
// CHECK: [[A2:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: load [take] [[A2]] : $*K
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_use_after_consume_same_field : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
|
|
%aa1 = struct_element_addr %root : $*Two, #Two.a
|
|
%la1 = load [take] %aa1 : $*K
|
|
apply %consFn(%la1) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%aa2 = struct_element_addr %root : $*Two, #Two.a
|
|
%la2 = load [copy] %aa2 : $*K
|
|
apply %consFn(%la2) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [copy] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Consuming the whole aggregate, and then a field `b`.
|
|
// Since the whole take is interior it is legalized as a copy, leaving leaf `a`
|
|
// live, so the trailing destroy_addr is narrowed to `a`.
|
|
// CHECK-LABEL: sil [ossa] @fs_take_whole_then_field :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[LW:%.*]] = load [copy] [[ROOT]] : $*Two
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[LW]] : $Two
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_take_whole_then_field : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
|
|
%lw = load [take] %root : $*Two
|
|
destroy_value %lw : $Two
|
|
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [take] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// The dual of the above: consuming field `a` and then the whole aggregate. The
|
|
// whole take demands leaf `a` below, so the field-`a` take is interior and
|
|
// legalized as a copy.
|
|
// CHECK-LABEL: sil [ossa] @fs_take_field_then_whole :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: [[LA:%.*]] = load [copy] [[AA]] : $*K
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[LA]] : $K
|
|
// CHECK: load [take] [[ROOT]] : $*Two
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_take_field_then_whole : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%consTwoFn = function_ref @consumeTwo : $@convention(thin) (@owned Two) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
%la = load [take] %aa : $*K
|
|
apply %consFn(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%lw = load [take] %root : $*Two
|
|
apply %consTwoFn(%lw) : $@convention(thin) (@owned Two) -> ()
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Tuple elements are leaves too: taking `.0` and `.1` are independent last uses,
|
|
// so both stay takes.
|
|
// CHECK-LABEL: sil [ossa] @fs_tuple_independent_takes :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[E0:%.*]] = tuple_element_addr [[ROOT]] : $*(K, K), 0
|
|
// CHECK-NEXT: load [take] [[E0]] : $*K
|
|
// CHECK: [[E1:%.*]] = tuple_element_addr [[ROOT]] : $*(K, K), 1
|
|
// CHECK-NEXT: load [take] [[E1]] : $*K
|
|
// CHECK-NOT: load [copy]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_tuple_independent_takes : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $(K, K), var, name "t", type $(K, K)
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = tuple (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*(K, K)
|
|
|
|
%e0 = tuple_element_addr %root : $*(K, K), 0
|
|
%l0 = load [take] %e0 : $*K
|
|
apply %consFn(%l0) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%e1 = tuple_element_addr %root : $*(K, K), 1
|
|
%l1 = load [take] %e1 : $*K
|
|
apply %consFn(%l1) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*(K, K)
|
|
dealloc_stack %0 : $*(K, K)
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Nested aggregate: `Nested` linearizes to three leaves [p.a, p.b, k]. Consuming
|
|
// each once through a chained projection is three independent last uses that are
|
|
// converted to [take]'s
|
|
// CHECK-LABEL: sil [ossa] @fs_nested_leaves :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: struct_element_addr [[ROOT]] : $*Nested, #Nested.p
|
|
// CHECK-NEXT: struct_element_addr {{.*}} : $*Two, #Two.a
|
|
// CHECK-NEXT: load [take]
|
|
// CHECK: struct_element_addr [[ROOT]] : $*Nested, #Nested.p
|
|
// CHECK-NEXT: struct_element_addr {{.*}} : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take]
|
|
// CHECK: struct_element_addr [[ROOT]] : $*Nested, #Nested.k
|
|
// CHECK-NEXT: load [take]
|
|
// CHECK-NOT: load [copy]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_nested_leaves : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Nested, var, name "n", type $Nested
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vk = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%two = struct $Two (%va : $K, %vb : $K)
|
|
%n = struct $Nested (%two : $Two, %vk : $K)
|
|
store %n to [init] %root : $*Nested
|
|
|
|
%pa = struct_element_addr %root : $*Nested, #Nested.p
|
|
%aa = struct_element_addr %pa : $*Two, #Two.a
|
|
%la = load [copy] %aa : $*K
|
|
apply %consFn(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%pb = struct_element_addr %root : $*Nested, #Nested.p
|
|
%ab = struct_element_addr %pb : $*Two, #Two.b
|
|
%lb = load [copy] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%ka = struct_element_addr %root : $*Nested, #Nested.k
|
|
%lk = load [copy] %ka : $*K
|
|
apply %consFn(%lk) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*Nested
|
|
dealloc_stack %0 : $*Nested
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Noncopyable field sensitivity: consuming field `a` (last use) stays a take.
|
|
// Field `b` is read while still demanded below, so it becomes a `load_borrow`.
|
|
// CHECK-LABEL: sil [ossa] @fs_nc_take_field_borrow_other :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*NCTwo, #NCTwo.a
|
|
// CHECK-NEXT: load [take] [[AA]] : $*K
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*NCTwo, #NCTwo.b
|
|
// CHECK-NEXT: [[LB:%.*]] = load_borrow [[AB]] : $*K
|
|
// CHECK: end_borrow [[LB]] : $K
|
|
// CHECK: [[AB2:%.*]] = struct_element_addr [[ROOT]] : $*NCTwo, #NCTwo.b
|
|
// CHECK-NEXT: load [take] [[AB2]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_nc_take_field_borrow_other : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $NCTwo, var, name "t", type $NCTwo
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%useFn = function_ref @useK : $@convention(thin) (@guaranteed K) -> ()
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $NCTwo (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*NCTwo
|
|
|
|
%aa = struct_element_addr %root : $*NCTwo, #NCTwo.a
|
|
%la = load [take] %aa : $*K
|
|
apply %consFn(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%ab = struct_element_addr %root : $*NCTwo, #NCTwo.b
|
|
%lb = load [copy] %ab : $*K
|
|
apply %useFn(%lb) : $@convention(thin) (@guaranteed K) -> ()
|
|
destroy_value %lb : $K
|
|
|
|
%ab2 = struct_element_addr %root : $*NCTwo, #NCTwo.b
|
|
%lb2 = load [take] %ab2 : $*K
|
|
apply %consFn(%lb2) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*NCTwo
|
|
dealloc_stack %0 : $*NCTwo
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Field sensitivity is preserved through a `begin_access` scope: the field-`a`
|
|
// take within the access is the last use of `a` and stays a take; the field-`b`
|
|
// take is independent.
|
|
// CHECK-LABEL: sil [ossa] @fs_field_access_scope :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[ACC:%.*]] = begin_access [deinit] [unknown] [[ROOT]] : $*Two
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ACC]] : $*Two, #Two.a
|
|
// CHECK-NEXT: load [take] [[AA]] : $*K
|
|
// CHECK-NEXT: end_access [[ACC]] : $*Two
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK-NOT: load [copy]
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_field_access_scope : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
|
|
%acc = begin_access [deinit] [unknown] %root : $*Two
|
|
%aa = struct_element_addr %acc : $*Two, #Two.a
|
|
%la = load [copy] %aa : $*K
|
|
end_access %acc : $*Two
|
|
apply %consFn(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [copy] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Only the field that is both demanded and uninitialized is repaired with an undef.
|
|
// Field `a` is stored but never consumed, so the trailing destroy_addr is narrowed to `a`.
|
|
// CHECK-LABEL: sil [ossa] @fs_partial_use_before_init :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK-NEXT: [[UB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: assign undef to [init] [[UB]] : $*K
|
|
// CHECK-NOT: assign undef
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_partial_use_before_init : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%useFn = function_ref @useK : $@convention(thin) (@guaranteed K) -> ()
|
|
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
store %va to [init] %aa : $*K
|
|
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [copy] %ab : $*K
|
|
apply %useFn(%lb) : $@convention(thin) (@guaranteed K) -> ()
|
|
destroy_value %lb : $K
|
|
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Each demanded, uninitialized leaf gets its own undef, in leaf order, even through nested fields.
|
|
// Leaf `p.a` is initialized, so it receives none.
|
|
// CHECK-LABEL: sil [ossa] @fs_nested_use_before_init :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK-NEXT: [[P:%.*]] = struct_element_addr [[ROOT]] : $*Nested, #Nested.p
|
|
// CHECK-NEXT: [[PB:%.*]] = struct_element_addr [[P]] : $*Two, #Two.b
|
|
// CHECK-NEXT: assign undef to [init] [[PB]] : $*K
|
|
// CHECK-NEXT: [[K:%.*]] = struct_element_addr [[ROOT]] : $*Nested, #Nested.k
|
|
// CHECK-NEXT: assign undef to [init] [[K]] : $*K
|
|
// CHECK-NOT: assign undef
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @fs_nested_use_before_init : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Nested, var, name "n", type $Nested
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%useFn = function_ref @useK : $@convention(thin) (@guaranteed K) -> ()
|
|
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%p = struct_element_addr %root : $*Nested, #Nested.p
|
|
%pa = struct_element_addr %p : $*Two, #Two.a
|
|
store %va to [init] %pa : $*K
|
|
|
|
%pb = struct_element_addr %p : $*Two, #Two.b
|
|
%lb = load [copy] %pb : $*K
|
|
apply %useFn(%lb) : $@convention(thin) (@guaranteed K) -> ()
|
|
destroy_value %lb : $K
|
|
|
|
%k = struct_element_addr %root : $*Nested, #Nested.k
|
|
%lk = load [copy] %k : $*K
|
|
apply %useFn(%lk) : $@convention(thin) (@guaranteed K) -> ()
|
|
destroy_value %lk : $K
|
|
|
|
destroy_addr %root : $*Nested
|
|
dealloc_stack %0 : $*Nested
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// destroy_addr removal: a `load [take]` that consumes the storage leaves a
|
|
// dominated `destroy_addr` as a redundant, so it is deleted.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CHECK-LABEL: sil [ossa] @stack_final_take_deletes_destroy_addr :
|
|
// CHECK: load [take] {{.*}} : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @stack_final_take_deletes_destroy_addr : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
%1 = mark_uninitialized [var] %0 : $*C
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%vk = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%c = struct $C (%vk : $K)
|
|
assign %c to %1 : $*C
|
|
|
|
%ld = load [copy] %1 : $*C // gets turned into a [take]
|
|
ignored_use %ld
|
|
destroy_value %ld
|
|
|
|
destroy_addr %1 : $*C // gets deleted
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @stack_take_dominates_branch_destroys :
|
|
// CHECK: load [take] {{.*}} : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @stack_take_dominates_branch_destroys : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
%1 = mark_uninitialized [var] %0 : $*C
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%vk = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%c = struct $C (%vk : $K)
|
|
assign %c to %1 : $*C
|
|
|
|
%ld = load [copy] %1 : $*C // gets turned into a [take]; dominates both destroys
|
|
ignored_use %ld
|
|
destroy_value %ld
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
destroy_addr %1 : $*C // gets deleted
|
|
br bb3
|
|
|
|
bb2:
|
|
destroy_addr %1 : $*C // gets deleted
|
|
br bb3
|
|
|
|
bb3:
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// copy_addr: a copy out of the source is a use demanding ownership, so a final
|
|
// such use becomes a [take]. The destination address views this as a def.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CHECK-LABEL: sil [ossa] @cpa_last_use_becomes_take :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C, [[ARG:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK-NEXT: copy_addr [[ARG]] to [init] [[ROOT]] : $*C
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*C
|
|
// CHECK-NEXT: dealloc_stack [[ROOT]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_last_use_becomes_take : $@convention(thin) (@in_guaranteed C) -> @out C {
|
|
bb0(%out : $*C, %arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
copy_addr %0 to [init] %out : $*C
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @cpa_interior_copy_stays_copy :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C, [[ARG:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: copy_addr [[ROOT]] to [init] [[OUT]] : $*C
|
|
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $C
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] [[TMP]] : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_interior_copy_stays_copy : $@convention(thin) (@in_guaranteed C) -> @out C {
|
|
bb0(%out : $*C, %arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
copy_addr %0 to [init] %out : $*C
|
|
%tmp = alloc_stack $C
|
|
copy_addr %0 to [init] %tmp : $*C
|
|
%f = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
apply %f(%tmp) : $@convention(thin) (@in C) -> ()
|
|
dealloc_stack %tmp : $*C
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// An `@in_guaranteed` argument is not yet understood, so it conservatively demands
|
|
// the storage: the copy before it stays a copy, and the destroy is kept.
|
|
// CHECK-LABEL: sil [ossa] @cpa_unknown_use_keeps_copy_and_destroy :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C, [[ARG:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: copy_addr [[ROOT]] to [init] [[OUT]] : $*C
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) (@in_guaranteed C) -> ()
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_unknown_use_keeps_copy_and_destroy : $@convention(thin) (@in_guaranteed C) -> @out C {
|
|
bb0(%out : $*C, %arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
copy_addr %0 to [init] %out : $*C
|
|
%f = function_ref @useC_addr : $@convention(thin) (@in_guaranteed C) -> ()
|
|
apply %f(%0) : $@convention(thin) (@in_guaranteed C) -> ()
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// An `@out` argument initializes the storage.
|
|
// CHECK-LABEL: sil [ossa] @cpa_indirect_out_apply_is_def :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) () -> @out C
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) (@in_guaranteed C) -> ()
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*C
|
|
// CHECK-NOT: assign undef
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_indirect_out_apply_is_def : $@convention(thin) () -> @out C {
|
|
bb0(%out : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%f = function_ref @produceC : $@convention(thin) () -> @out C
|
|
apply %f(%0) : $@convention(thin) () -> @out C
|
|
%u = function_ref @useC_addr : $@convention(thin) (@in_guaranteed C) -> ()
|
|
apply %u(%0) : $@convention(thin) (@in_guaranteed C) -> ()
|
|
copy_addr %0 to [init] %out : $*C
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// An `@in` argument takes the storage.
|
|
// CHECK-LABEL: sil [ossa] @cpa_indirect_in_apply_is_take :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) () -> @out C
|
|
// CHECK-NEXT: apply {{%.*}}([[ROOT]]) : $@convention(thin) (@in C) -> ()
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_indirect_in_apply_is_take : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%gen = function_ref @produceC : $@convention(thin) () -> @out C
|
|
%take = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
|
|
apply %gen(%0) : $@convention(thin) () -> @out C
|
|
|
|
apply %take(%0) : $@convention(thin) (@in C) -> ()
|
|
|
|
destroy_addr %0 : $*C
|
|
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// For an @in use that has subsequent demand, we introduce a copy.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @cpa_indirect_in_apply_is_take_twice :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) () -> @out C
|
|
// CHECK: [[ALLOC:%.*]] = alloc_stack $C
|
|
// CHECK-NEXT: copy_addr [[ROOT]] to [init] [[ALLOC]] : $*C
|
|
// CHECK-NEXT: apply {{%.*}}([[ALLOC]]) : $@convention(thin) (@in C) -> ()
|
|
// CHECK-NEXT: dealloc_stack [[ALLOC]]
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) (@in C) -> ()
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: dealloc_stack [[ROOT]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_indirect_in_apply_is_take_twice : $@convention(thin) () -> () {
|
|
bb0:
|
|
%x = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %x"
|
|
%genC = function_ref @produceC : $@convention(thin) () -> @out C
|
|
%takeC = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
|
|
apply %genC(%x) : $@convention(thin) () -> @out C
|
|
|
|
apply %takeC(%x) : $@convention(thin) (@in C) -> ()
|
|
apply %takeC(%x) : $@convention(thin) (@in C) -> ()
|
|
|
|
destroy_addr %x : $*C
|
|
|
|
dealloc_stack %x : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
// CHECK-LABEL: sil [ossa] @cpa_indirect_in_apply_is_take_twice_nc :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $NC
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) () -> @out NC
|
|
// CHECK: [[ALLOC:%.*]] = alloc_stack $NC
|
|
// CHECK-NEXT: copy_addr [[ROOT]] to [init] [[ALLOC]] : $*NC
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[ROOT]]
|
|
// CHECK-NEXT: apply {{%.*}}([[ALLOC]]) : $@convention(thin) (@in NC) -> ()
|
|
// CHECK-NEXT: dealloc_stack [[ALLOC]]
|
|
// CHECK: apply {{%.*}}([[ROOT]]) : $@convention(thin) (@in NC) -> ()
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: dealloc_stack [[ROOT]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_indirect_in_apply_is_take_twice_nc : $@convention(thin) () -> () {
|
|
bb0:
|
|
%y = alloc_stack [lexical] [var_decl] $NC, let, name "y"
|
|
specify_test "lifetime_resolution_resolve %y"
|
|
%genNC = function_ref @getNC_addr : $@convention(thin) () -> @out NC
|
|
%takeNC = function_ref @consumeNC_addr : $@convention(thin) (@in NC) -> ()
|
|
|
|
apply %genNC(%y) : $@convention(thin) () -> @out NC
|
|
|
|
apply %takeNC(%y) : $@convention(thin) (@in NC) -> ()
|
|
apply %takeNC(%y) : $@convention(thin) (@in NC) -> ()
|
|
|
|
destroy_addr %y : $*NC
|
|
|
|
dealloc_stack %y : $*NC
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// SILGen's shape for a generic argument `var x = t; consume(x); x = t; return x`.
|
|
// when not using opaque values. Since `x` was consumed, the reassignment is an initialization.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @cpa_reassign_kills_demand :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C, [[ARG:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK-NEXT: copy_addr [[ARG]] to [init] [[ROOT]] : $*C
|
|
// CHECK: [[R1:%.*]] = begin_access [read] [unknown] [[ROOT]] : $*C
|
|
// CHECK-NEXT: copy_addr [take] [[R1]] to [init] {{%.*}} : $*C
|
|
// CHECK: [[M:%.*]] = begin_access [modify] [unknown] [[ROOT]] : $*C
|
|
// CHECK-NEXT: copy_addr [take] {{%.*}} to [init] [[M]] : $*C
|
|
// CHECK: [[R2:%.*]] = begin_access [read] [unknown] [[ROOT]] : $*C
|
|
// CHECK-NEXT: copy_addr [take] [[R2]] to [init] [[OUT]] : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_reassign_kills_demand : $@convention(thin) (@in_guaranteed C) -> @out C {
|
|
bb0(%out : $*C, %arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, var, name "x"
|
|
%mu = mark_uninitialized [var] %0 : $*C
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %mu : $*C
|
|
|
|
%tmp = alloc_stack $C
|
|
%r = begin_access [read] [unknown] %mu : $*C
|
|
copy_addr %r to [init] %tmp : $*C
|
|
end_access %r : $*C
|
|
%f = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
apply %f(%tmp) : $@convention(thin) (@in C) -> ()
|
|
dealloc_stack %tmp : $*C
|
|
|
|
%tmp2 = alloc_stack $C
|
|
copy_addr %arg to [init] %tmp2 : $*C
|
|
%m = begin_access [modify] [unknown] %mu : $*C
|
|
copy_addr [take] %tmp2 to %m : $*C
|
|
end_access %m : $*C
|
|
dealloc_stack %tmp2 : $*C
|
|
|
|
%r2 = begin_access [read] [unknown] %mu : $*C
|
|
copy_addr %r2 to [init] %out : $*C
|
|
end_access %r2 : $*C
|
|
destroy_addr %mu : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Consumed on every path, so the destroy in the merge block is deleted.
|
|
// CHECK-LABEL: sil [ossa] @cpa_consume_both_branches :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] {{%.*}} : $*C
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] {{%.*}} : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_consume_both_branches : $@convention(thin) (@in_guaranteed C) -> () {
|
|
bb0(%arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
%f = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
%tmp = alloc_stack $C
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
copy_addr %0 to [init] %tmp : $*C
|
|
br bb3
|
|
|
|
bb2:
|
|
copy_addr %0 to [init] %tmp : $*C
|
|
br bb3
|
|
|
|
bb3:
|
|
apply %f(%tmp) : $@convention(thin) (@in C) -> ()
|
|
dealloc_stack %tmp : $*C
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// The next iteration still demands the storage, so the copy in the loop stays a copy.
|
|
// CHECK-LABEL: sil [ossa] @cpa_loop_consume_stays_copy :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: bb1:
|
|
// CHECK: copy_addr [[ROOT]] to [init] {{%.*}} : $*C
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_loop_consume_stays_copy : $@convention(thin) (@in_guaranteed C) -> () {
|
|
bb0(%arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
%f = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
br bb1
|
|
|
|
bb1:
|
|
%tmp = alloc_stack $C
|
|
copy_addr %0 to [init] %tmp : $*C
|
|
apply %f(%tmp) : $@convention(thin) (@in C) -> ()
|
|
dealloc_stack %tmp : $*C
|
|
cond_br undef, bb2, bb3
|
|
|
|
bb2:
|
|
br bb1
|
|
|
|
bb3:
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Reinitializing within the loop body permits the copy before it to be a take.
|
|
// CHECK-LABEL: sil [ossa] @cpa_loop_reinit_is_take :
|
|
// CHECK: bb0([[ARG:%.*]] : $*C):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $C
|
|
// CHECK: bb1:
|
|
// CHECK: copy_addr [take] [[ROOT]] to [init] {{%.*}} : $*C
|
|
// CHECK: copy_addr [[ARG]] to [init] [[ROOT]] : $*C
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_loop_reinit_is_take : $@convention(thin) (@in_guaranteed C) -> () {
|
|
bb0(%arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, var, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
copy_addr %arg to [init] %0 : $*C
|
|
%f = function_ref @consumeC_addr : $@convention(thin) (@in C) -> ()
|
|
br bb1
|
|
|
|
bb1:
|
|
%tmp = alloc_stack $C
|
|
copy_addr %0 to [init] %tmp : $*C
|
|
apply %f(%tmp) : $@convention(thin) (@in C) -> ()
|
|
dealloc_stack %tmp : $*C
|
|
copy_addr %arg to [init] %0 : $*C
|
|
cond_br undef, bb2, bb3
|
|
|
|
bb2:
|
|
br bb1
|
|
|
|
bb3:
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// A copy_addr of a single field is tracked independently of its sibling.
|
|
// CHECK-LABEL: sil [ossa] @cpa_field_source :
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $Two
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: copy_addr [take] [[AA]] to [init] {{%.*}} : $*K
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_field_source : $@convention(thin) (@owned Two) -> () {
|
|
bb0(%w : @owned $Two):
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, let, name "t"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
store %w to [init] %0 : $*Two
|
|
|
|
%kt = alloc_stack $K
|
|
%aa = struct_element_addr %0 : $*Two, #Two.a
|
|
copy_addr %aa to [init] %kt : $*K
|
|
%f = function_ref @consumeK_addr : $@convention(thin) (@in K) -> ()
|
|
apply %f(%kt) : $@convention(thin) (@in K) -> ()
|
|
dealloc_stack %kt : $*K
|
|
|
|
%ab = struct_element_addr %0 : $*Two, #Two.b
|
|
%lb = load [copy] %ab : $*K
|
|
%c = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
apply %c(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
destroy_addr %0 : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// A whole-value copy_addr demands every field, so an earlier take of a field is
|
|
// legalized into a copy.
|
|
// CHECK-LABEL: sil [ossa] @cpa_field_take_then_whole :
|
|
// CHECK: bb0([[OUT:%.*]] : $*Two, {{%.*}} : @owned $Two):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $Two
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: [[LA:%.*]] = load [copy] [[AA]] : $*K
|
|
// CHECK-NEXT: diagnose [unpermitted_copy] [[LA]] : $K
|
|
// CHECK: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*Two
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_field_take_then_whole : $@convention(thin) (@owned Two) -> @out Two {
|
|
bb0(%out : $*Two, %w : @owned $Two):
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, let, name "t"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
store %w to [init] %0 : $*Two
|
|
|
|
%aa = struct_element_addr %0 : $*Two, #Two.a
|
|
%la = load [take] %aa : $*K
|
|
%c = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
apply %c(%la) : $@convention(thin) (@owned K) -> ()
|
|
|
|
copy_addr %0 to [init] %out : $*Two
|
|
destroy_addr %0 : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// Initializing each field separately, one of them with a copy_addr, defines the whole.
|
|
// CHECK-LABEL: sil [ossa] @cpa_field_defs_then_whole :
|
|
// CHECK: bb0([[OUT:%.*]] : $*Two, [[KA:%.*]] : $*K, {{%.*}} : @owned $K):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $Two
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: copy_addr [[KA]] to [init] [[AA]] : $*K
|
|
// CHECK: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*Two
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_field_defs_then_whole : $@convention(thin) (@in_guaranteed K, @owned K) -> @out Two {
|
|
bb0(%out : $*Two, %ka : $*K, %kb : @owned $K):
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%aa = struct_element_addr %0 : $*Two, #Two.a
|
|
copy_addr %ka to [init] %aa : $*K
|
|
%ab = struct_element_addr %0 : $*Two, #Two.b
|
|
store %kb to [init] %ab : $*K
|
|
copy_addr %0 to [init] %out : $*Two
|
|
destroy_addr %0 : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// A copy_addr whose source and destination are both fields of the storage: `b` is
|
|
// consumed then reinitialized from `a`, which the whole-value copy_addr still demands.
|
|
// CHECK-LABEL: sil [ossa] @cpa_field_to_field_same_root :
|
|
// CHECK: bb0([[OUT:%.*]] : $*Two, {{%.*}} : @owned $Two):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $Two
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: [[AB2:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: copy_addr [[AA]] to [init] [[AB2]] : $*K
|
|
// CHECK-NEXT: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*Two
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_field_to_field_same_root : $@convention(thin) (@owned Two) -> @out Two {
|
|
bb0(%out : $*Two, %w : @owned $Two):
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
store %w to [init] %0 : $*Two
|
|
|
|
%ab = struct_element_addr %0 : $*Two, #Two.b
|
|
%lb = load [take] %ab : $*K
|
|
%c = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
apply %c(%lb) : $@convention(thin) (@owned K) -> ()
|
|
|
|
%aa = struct_element_addr %0 : $*Two, #Two.a
|
|
%ab2 = struct_element_addr %0 : $*Two, #Two.b
|
|
copy_addr %aa to [init] %ab2 : $*K
|
|
|
|
copy_addr %0 to [init] %out : $*Two
|
|
destroy_addr %0 : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @cpa_nc_last_use :
|
|
// CHECK: bb0([[OUT:%.*]] : $*NC, {{%.*}} : @owned $NC):
|
|
// CHECK: [[ROOT:%.*]] = alloc_stack [lexical] [var_decl] $NC
|
|
// CHECK: copy_addr [take] [[ROOT]] to [init] [[OUT]] : $*NC
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_nc_last_use : $@convention(thin) (@owned NC) -> @out NC {
|
|
bb0(%out : $*NC, %v : @owned $NC):
|
|
%0 = alloc_stack [lexical] [var_decl] $NC, let, name "x"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
store %v to [init] %0 : $*NC
|
|
copy_addr %0 to [init] %out : $*NC
|
|
destroy_addr %0 : $*NC
|
|
dealloc_stack %0 : $*NC
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// The box's own lifetime end conservatively demands its contents.
|
|
// CHECK-LABEL: sil [ossa] @cpa_box_copy_stays_copy :
|
|
// CHECK: bb0([[OUT:%.*]] : $*C, {{%.*}} : $*C):
|
|
// CHECK: [[PB:%.*]] = project_box {{%.*}} : ${ var C }, 0
|
|
// CHECK: [[R:%.*]] = begin_access [read] [unknown] [[PB]] : $*C
|
|
// CHECK-NEXT: copy_addr [[R]] to [init] [[OUT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_box_copy_stays_copy : $@convention(thin) (@in_guaranteed C) -> @out C {
|
|
bb0(%out : $*C, %arg : $*C):
|
|
%b = alloc_box ${ var C }, var, name "x"
|
|
%bb = begin_borrow [lexical] [var_decl] %b : ${ var C }
|
|
%pb = project_box %bb : ${ var C }, 0
|
|
specify_test "lifetime_resolution_resolve %b"
|
|
copy_addr %arg to [init] %pb : $*C
|
|
%r = begin_access [read] [unknown] %pb : $*C
|
|
copy_addr %r to [init] %out : $*C
|
|
end_access %r : $*C
|
|
end_borrow %bb : ${ var C }
|
|
destroy_value %b : ${ var C }
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// `var a = ...; var b = a; return b`: resolving both `a` and `b` turns this into all takes.
|
|
// CHECK-LABEL: sil [ossa] @cpa_chain_two_roots :
|
|
// CHECK: [[A:%.*]] = alloc_stack [lexical] [var_decl] $C, var, name "a"
|
|
// CHECK-NEXT: [[B:%.*]] = alloc_stack [lexical] [var_decl] $C, var, name "b"
|
|
// CHECK: [[RA:%.*]] = begin_access [read] [unknown] [[A]] : $*C
|
|
// CHECK-NEXT: copy_addr [take] [[RA]] to [init] [[B]] : $*C
|
|
// CHECK: [[RB:%.*]] = begin_access [read] [unknown] [[B]] : $*C
|
|
// CHECK-NEXT: [[L:%.*]] = load [take] [[RB]] : $*C
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: return [[L]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @cpa_chain_two_roots : $@convention(thin) (@owned C) -> @owned C {
|
|
bb0(%v : @owned $C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, var, name "a"
|
|
%1 = alloc_stack [lexical] [var_decl] $C, var, name "b"
|
|
specify_test "lifetime_resolution_resolve %1"
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
store %v to [init] %0 : $*C
|
|
%r = begin_access [read] [unknown] %0 : $*C
|
|
copy_addr %r to [init] %1 : $*C
|
|
end_access %r : $*C
|
|
%r2 = begin_access [read] [unknown] %1 : $*C
|
|
%l = load [copy] %r2 : $*C
|
|
end_access %r2 : $*C
|
|
destroy_addr %1 : $*C
|
|
dealloc_stack %1 : $*C
|
|
destroy_addr %0 : $*C
|
|
dealloc_stack %0 : $*C
|
|
return %l
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Partial destroys: a destroy_addr of partially available storage is replaced
|
|
// by destroys of only its available fields, testing a control variable for
|
|
// those that are conditionally available.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Statically known per-field availability needs no control variable.
|
|
// CHECK-LABEL: sil [ossa] @pd_static_field :
|
|
// CHECK-NOT: $Builtin.Int
|
|
// CHECK: [[STK:%.*]] = alloc_stack [lexical] [var_decl] $Two
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var] [[STK]]
|
|
// CHECK: store {{%.*}} to [init] {{%.*}} : $*K
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NEXT: dealloc_stack [[STK]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_static_field : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
store %va to [init] %aa : $*K
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @pd_conditional_field :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int2
|
|
// CHECK: [[STK:%.*]] = alloc_stack [dynamic_lifetime] [lexical] [var_decl] $Two
|
|
// CHECK: [[Z:%.*]] = integer_literal $Builtin.Int2, 0
|
|
// CHECK-NEXT: store [[Z]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: [[ROOT:%.*]] = mark_uninitialized [var] [[STK]]
|
|
// CHECK: bb1:
|
|
// CHECK: [[OLD:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: [[BIT:%.*]] = integer_literal $Builtin.Int2, 1
|
|
// CHECK-NEXT: [[NEW:%.*]] = builtin "or_Int2"([[OLD]] : $Builtin.Int2, [[BIT]] : $Builtin.Int2)
|
|
// CHECK-NEXT: store [[NEW]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: store {{%.*}} to [init] {{%.*}} : $*K
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: [[M:%.*]] = integer_literal $Builtin.Int2, 1
|
|
// CHECK-NEXT: [[AND:%.*]] = builtin "and_Int2"([[V]] : $Builtin.Int2, [[M]] : $Builtin.Int2)
|
|
// CHECK-NEXT: [[Z2:%.*]] = integer_literal $Builtin.Int2, 0
|
|
// CHECK-NEXT: [[TEST:%.*]] = builtin "cmp_ne_Int2"([[AND]] : $Builtin.Int2, [[Z2]] : $Builtin.Int2) : $Builtin.Int1
|
|
// CHECK-NEXT: cond_br [[TEST]], [[YES:bb[0-9]+]], [[NO:bb[0-9]+]]
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NEXT: br [[CONT:bb[0-9]+]]
|
|
// CHECK: [[NO]]:
|
|
// CHECK-NEXT: br [[CONT]]
|
|
// CHECK: [[CONT]]:
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: dealloc_stack [[STK]]
|
|
// CHECK: dealloc_stack [[CV]]
|
|
// CHECK-NEXT: return
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_conditional_field : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
store %va to [init] %aa : $*K
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// A single-field control variable is tested directly.
|
|
// CHECK-LABEL: sil [ossa] @pd_conditional_whole :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int1
|
|
// CHECK: [[STK:%.*]] = alloc_stack [dynamic_lifetime] [lexical] [var_decl] $K
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var] [[STK]]
|
|
// CHECK: bb1:
|
|
// CHECK: [[ONE:%.*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NEXT: store [[ONE]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: store {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: cond_br [[V]], [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_conditional_whole : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%v = apply %getFn() : $@convention(thin) () -> @owned K
|
|
store %v to [init] %root : $*K
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// A take on one path clears its field's bit; the other field is destroyed unconditionally.
|
|
// CHECK-LABEL: sil [ossa] @pd_init_then_conditional_take :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int2
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[ALL:%.*]] = integer_literal $Builtin.Int2, -1
|
|
// CHECK-NEXT: store [[ALL]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: store {{%.*}} to [init] [[ROOT]] : $*Two
|
|
// CHECK: bb1:
|
|
// CHECK: [[AB:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: [[OLD:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: [[KEEP:%.*]] = integer_literal $Builtin.Int2, 1
|
|
// CHECK-NEXT: [[NEW:%.*]] = builtin "and_Int2"([[OLD]] : $Builtin.Int2, [[KEEP]] : $Builtin.Int2)
|
|
// CHECK-NEXT: store [[NEW]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: load [take] [[AB]] : $*K
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NEXT: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: [[M:%.*]] = integer_literal $Builtin.Int2, -2
|
|
// CHECK-NEXT: builtin "and_Int2"([[V]] : $Builtin.Int2, [[M]] : $Builtin.Int2)
|
|
// CHECK: cond_br {{%.*}}, [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: [[AB2:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.b
|
|
// CHECK-NEXT: destroy_addr [[AB2]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_init_then_conditional_take : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%va : $K, %vb : $K)
|
|
store %whole to [init] %root : $*Two
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%consFn = function_ref @consumeK : $@convention(thin) (@owned K) -> ()
|
|
%ab = struct_element_addr %root : $*Two, #Two.b
|
|
%lb = load [take] %ab : $*K
|
|
apply %consFn(%lb) : $@convention(thin) (@owned K) -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @pd_nested_projection :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int3
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: [[M:%.*]] = integer_literal $Builtin.Int3, 2
|
|
// CHECK-NEXT: builtin "and_Int3"([[V]] : $Builtin.Int3, [[M]] : $Builtin.Int3)
|
|
// CHECK: cond_br {{%.*}}, [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: [[NP:%.*]] = struct_element_addr [[ROOT]] : $*Nested, #Nested.p
|
|
// CHECK-NEXT: [[NPB:%.*]] = struct_element_addr [[NP]] : $*Two, #Two.b
|
|
// CHECK-NEXT: destroy_addr [[NPB]] : $*K
|
|
// CHECK: [[NK:%.*]] = struct_element_addr [[ROOT]] : $*Nested, #Nested.k
|
|
// CHECK-NEXT: destroy_addr [[NK]] : $*K
|
|
// CHECK-NOT: destroy_addr
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_nested_projection : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Nested, var, name "n", type $Nested
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%vk = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%nk = struct_element_addr %root : $*Nested, #Nested.k
|
|
store %vk to [init] %nk : $*K
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%vb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%np = struct_element_addr %root : $*Nested, #Nested.p
|
|
%npb = struct_element_addr %np : $*Two, #Two.b
|
|
store %vb to [init] %npb : $*K
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
destroy_addr %root : $*Nested
|
|
dealloc_stack %0 : $*Nested
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// The control variable is reset at the root each time it's reached.
|
|
// CHECK-LABEL: sil [ossa] @pd_loop_resets_at_root :
|
|
// CHECK: bb0:
|
|
// CHECK-NEXT: [[CV:%.*]] = alloc_stack $Builtin.Int1
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: bb1:
|
|
// CHECK-NEXT: [[STK:%.*]] = alloc_stack [dynamic_lifetime] [lexical] [var_decl] $K
|
|
// CHECK-NEXT: [[Z:%.*]] = integer_literal $Builtin.Int1, 0
|
|
// CHECK-NEXT: store [[Z]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: mark_uninitialized [var] [[STK]]
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @pd_loop_resets_at_root : $@convention(thin) () -> () {
|
|
bb0:
|
|
br bb1
|
|
|
|
bb1:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
cond_br undef, bb2, bb3
|
|
|
|
bb2:
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%v = apply %getFn() : $@convention(thin) () -> @owned K
|
|
store %v to [init] %root : $*K
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb4
|
|
|
|
bb4:
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
cond_br undef, bb5, bb6
|
|
|
|
bb5:
|
|
br bb1
|
|
|
|
bb6:
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Conditional init/assign: every def becomes either an initialization or an
|
|
// assignment of its destination, based on the availability of the fields it
|
|
// overwrites. Partially available fields are destroyed before an initialization.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_assign_uninit_is_init :
|
|
// CHECK-NOT: $Builtin.Int
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: assign {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_assign_uninit_is_init : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%v = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %v to %root : $*K
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_assign_init_is_reassign :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: store {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK: assign {{%.*}} to [reassign] [[ROOT]] : $*K
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_assign_init_is_reassign : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%v1 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
store %v1 to [init] %root : $*K
|
|
%v2 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %v2 to %root : $*K
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// The conditionally initialized value is destroyed before the assign becomes an init.
|
|
// Its bit is only set after being tested.
|
|
// CHECK-LABEL: sil [ossa] @ca_conditional_assign :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int1
|
|
// CHECK: alloc_stack [dynamic_lifetime] [lexical] [var_decl] $K
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: bb1:
|
|
// CHECK: assign {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK: bb3:
|
|
// CHECK: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: cond_br [[V]], [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: bb6:
|
|
// CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Int1, -1
|
|
// CHECK-NEXT: store [[ONE]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: assign {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_conditional_assign : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%v1 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %v1 to %root : $*K
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%v2 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
assign %v2 to %root : $*K
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_store_whole_over_field :
|
|
// CHECK-NOT: $Builtin.Int
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: [[WHOLE:%.*]] = struct $Two
|
|
// CHECK-NEXT: [[AA:%.*]] = struct_element_addr [[ROOT]] : $*Two, #Two.a
|
|
// CHECK-NEXT: destroy_addr [[AA]] : $*K
|
|
// CHECK-NEXT: store [[WHOLE]] to [init] [[ROOT]] : $*Two
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*Two
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_store_whole_over_field : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $Two, var, name "t", type $Two
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%va = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%aa = struct_element_addr %root : $*Two, #Two.a
|
|
store %va to [init] %aa : $*K
|
|
%wa = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%wb = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%whole = struct $Two (%wa : $K, %wb : $K)
|
|
store %whole to [assign] %root : $*Two
|
|
destroy_addr %root : $*Two
|
|
dealloc_stack %0 : $*Two
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_copy_addr_onto_conditional :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int1
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[V:%.*]] = load [trivial] [[CV]]
|
|
// CHECK-NEXT: cond_br [[V]], [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: [[CK:%.*]] = struct_element_addr [[ROOT]] : $*C, #C.k
|
|
// CHECK-NEXT: destroy_addr [[CK]] : $*K
|
|
// CHECK: bb6:
|
|
// CHECK: copy_addr {{%.*}} to [init] [[ROOT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_copy_addr_onto_conditional : $@convention(thin) (@in_guaranteed C) -> () {
|
|
bb0(%arg : $*C):
|
|
%0 = alloc_stack [lexical] [var_decl] $C, var, name "c", type $C
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
copy_addr %arg to [init] %root : $*C
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
copy_addr %arg to %root : $*C
|
|
destroy_addr %root : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_store_init_onto_live :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: store {{%.*}} to [init] [[ROOT]] : $*K
|
|
// CHECK: store {{%.*}} to [assign] [[ROOT]] : $*K
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*K
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_store_init_onto_live : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $K, var, name "k", type $K
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
%v1 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
store %v1 to [init] %root : $*K
|
|
%v2 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
store %v2 to [init] %root : $*K
|
|
destroy_addr %root : $*K
|
|
dealloc_stack %0 : $*K
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// An @out apply can't assign, so the live value is destroyed first.
|
|
// CHECK-LABEL: sil [ossa] @ca_indirect_out_onto_live :
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: apply {{%.*}}([[ROOT]])
|
|
// CHECK-NEXT: [[CK:%.*]] = struct_element_addr [[ROOT]] : $*C, #C.k
|
|
// CHECK-NEXT: destroy_addr [[CK]] : $*K
|
|
// CHECK-NEXT: apply {{%.*}}([[ROOT]])
|
|
// CHECK-NEXT: destroy_addr [[ROOT]] : $*C
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_indirect_out_onto_live : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $C, var, name "c", type $C
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%f = function_ref @produceC : $@convention(thin) () -> @out C
|
|
apply %f(%root) : $@convention(thin) () -> @out C
|
|
apply %f(%root) : $@convention(thin) () -> @out C
|
|
destroy_addr %root : $*C
|
|
dealloc_stack %0 : $*C
|
|
%t = tuple ()
|
|
return %t
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @ca_tuple_addr_constructor :
|
|
// CHECK: [[CV:%.*]] = alloc_stack $Builtin.Int2
|
|
// CHECK: [[ROOT:%.*]] = mark_uninitialized [var]
|
|
// CHECK: bb3:
|
|
// CHECK: cond_br {{%.*}}, [[YES:bb[0-9]+]], {{bb[0-9]+}}
|
|
// CHECK: [[YES]]:
|
|
// CHECK-NEXT: [[T0:%.*]] = tuple_element_addr [[ROOT]] : $*(K, K), 0
|
|
// CHECK-NEXT: destroy_addr [[T0]] : $*K
|
|
// CHECK: bb6:
|
|
// CHECK-NEXT: [[ALL:%.*]] = integer_literal $Builtin.Int2, -1
|
|
// CHECK-NEXT: store [[ALL]] to [trivial] [[CV]]
|
|
// CHECK-NEXT: tuple_addr_constructor [init] [[ROOT]] : $*(K, K)
|
|
// CHECK: } // end sil function
|
|
sil [ossa] @ca_tuple_addr_constructor : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack [lexical] [var_decl] $(K, K), var, name "t", type $(K, K)
|
|
%root = mark_uninitialized [var] %0
|
|
specify_test "lifetime_resolution_resolve %0"
|
|
%getFn = function_ref @getK : $@convention(thin) () -> @owned K
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%v0 = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%t0 = tuple_element_addr %root : $*(K, K), 0
|
|
store %v0 to [init] %t0 : $*K
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%a = apply %getFn() : $@convention(thin) () -> @owned K
|
|
%b = apply %getFn() : $@convention(thin) () -> @owned K
|
|
tuple_addr_constructor [assign] %root : $*(K, K) with (%a : $K, %b : $K)
|
|
destroy_addr %root : $*(K, K)
|
|
dealloc_stack %0 : $*(K, K)
|
|
%t = tuple ()
|
|
return %t
|
|
}
|