Files
swift-mirror/test/SILOptimizer/lifetime_resolution_resolve_unit.sil
Kavon Farvardin d7167cac54 LifetimeResolution: field-sensitive address analysis
This is basically an address form of `resolveSingleDef`.

It is broken up into two major stages:

1. A backwards "demand" analysis

Demand analysis' legalization converts loads/copies into
take/borrow/copy based on downstream demand, leaving
behind `diagnose [unpermitted_copy]` markers for copies
that aren't permitted by language rules, to be turned into
diagnostics by a later pass.

This is meant to handle much of what the existing move-checking
passes do today, but on arbitrary addresses. For now, the focus is
on alloc_box / alloc_stack storage locations.

2. A forwards "availability" analysis

Add a forward, availability analysis that determines, at each use,
which fields of storage are available. This will be needed to insert
conditional destroys and other pieces of the existing
DefiniteInitialization.

Includes additional fixes to demand analysis to improve compatability with
a broader range of input programs.
2026-09-23 15:20:59 -07:00

845 lines
28 KiB
Plaintext

// RUN: %target-sil-opt %s -test-runner -sil-disable-input-verify | %FileCheck %s
sil_stage raw
import Builtin
import Swift
class Kl {}
class Sub : Kl {}
struct Wrapper { var k: Kl }
struct NC: ~Copyable { var k: Kl }
sil @getK : $@convention(thin) () -> @owned Kl
sil @useK : $@convention(thin) (@guaranteed Kl) -> ()
sil @consumeK : $@convention(thin) (@owned Kl) -> ()
sil @getNC : $@convention(thin) () -> @owned NC
sil @useNC : $@convention(thin) (@guaranteed NC) -> ()
sil @consumeNC : $@convention(thin) (@owned NC) -> ()
sil @consumeTwoK : $@convention(thin) (@owned Kl, @owned Kl) -> ()
sil @barrierK : $@convention(thin) () -> ()
sil @useRawPtr : $@convention(thin) (Builtin.RawPointer) -> ()
// CHECK-LABEL: sil [ossa] @test_copy_before_interior_consume :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[V]]
// CHECK-NEXT: apply {{%.*}}([[COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_copy_before_interior_consume : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_copy_before_interior_consume_noncopyable :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned NC
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[V]]
// CHECK-NEXT: diagnose [unpermitted_copy] [[COPY]]
// CHECK-NEXT: apply {{%.*}}([[COPY]]) : $@convention(thin) (@owned NC) -> ()
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed NC) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_copy_before_interior_consume_noncopyable : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getNC : $@convention(thin) () -> @owned NC
%useFn = function_ref @useNC : $@convention(thin) (@guaranteed NC) -> ()
%consFn = function_ref @consumeNC : $@convention(thin) (@owned NC) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned NC
specify_test "lifetime_resolution_resolve %0"
apply %consFn(%0) : $@convention(thin) (@owned NC) -> ()
apply %useFn(%0) : $@convention(thin) (@guaranteed NC) -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_use_before_final_consume :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_use_before_final_consume : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_hoist_destroy_across_branch :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: br bb3
// CHECK-NOT: destroy_value
// CHECK: } // end sil function
sil [ossa] @test_hoist_destroy_across_branch : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_copy_and_hoist_across_branch :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[V]]
// CHECK-NEXT: apply {{%.*}}([[COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK-NOT: destroy_value
// CHECK: } // end sil function
sil [ossa] @test_copy_and_hoist_across_branch : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
br bb3
bb2:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_hoist_into_dead_end :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [dead_end] [[V]]
// CHECK-NEXT: apply
// CHECK-NEXT: unreachable
// CHECK-NOT: destroy_value
// CHECK: } // end sil function
sil [ossa] @test_hoist_into_dead_end : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%failFn = function_ref @barrierK : $@convention(thin) () -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %failFn() : $@convention(thin) () -> ()
unreachable
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_consume_inside_loop :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: bb1:
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[V]]
// CHECK-NEXT: apply {{%.*}}([[COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: cond_br undef, bb2, bb3
// CHECK: bb2:
// CHECK: bb3:
// CHECK-NEXT: destroy_value [[V]]
// CHECK: } // end sil function
sil [ossa] @test_consume_inside_loop : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
br bb1
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
cond_br undef, bb2, bb3
bb2:
br bb1
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_copy_before_assign :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[ADDR:%.*]] = alloc_stack $Kl
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[V]]
// CHECK-NEXT: assign [[COPY]] to [[ADDR]]
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: destroy_addr [[ADDR]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_copy_before_assign : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
%addr = alloc_stack $Kl
specify_test "lifetime_resolution_resolve %0"
assign %0 to %addr : $*Kl
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
destroy_value %0
destroy_addr %addr : $*Kl
dealloc_stack %addr : $*Kl
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_hoist_destroy_across_assign_branch :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: [[ADDR:%.*]] = alloc_stack $Kl
// CHECK: bb1:
// CHECK-NEXT: assign [[V]] to [[ADDR]]
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_hoist_destroy_across_assign_branch : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
%addr = alloc_stack $Kl
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
assign %0 to %addr : $*Kl
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
br bb3
bb3:
destroy_value %0
destroy_addr %addr : $*Kl
dealloc_stack %addr : $*Kl
%t = tuple ()
return %t
}
// A deinit barrier appearing between the lifetime end-point of a lexical value and a final consume
// forces that consume to only consume a copy, keeping the original value live.
// CHECK-LABEL: sil [ossa] @test_deinit_barrier_forces_boundary_consume_copy :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [[V]]
// CHECK-NEXT: [[LEX_COPY:%.*]] = copy_value [[LEX]]
// CHECK-NEXT: apply {{%.*}}([[LEX_COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_deinit_barrier_forces_boundary_consume_copy : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%consumeFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
apply %consumeFn(%0) : $@convention(thin) (@owned Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// A lexical value's destroy must not be hoisted across a deinit barrier that lies
// between its last non-consuming use and its original scope-end destroy.
// CHECK-LABEL: sil [ossa] @test_lexical_no_hoist_across_barrier :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [[V]]
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_no_hoist_across_barrier : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// Similar to above, but now we have no use of the value, yet there is a deinit barrier
// before the destroy:
// CHECK-LABEL: sil [ossa] @test_dead_def_lexical_barrier :
// CHECK: [[LEX:%.*]] = move_value [lexical]
// CHECK: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_dead_def_lexical_barrier : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// Contrast with the above: absent a real deinit barrier, a lexical value's destroy
// still hoists to its last non-consuming use, same as a non-lexical value would.
// CHECK-LABEL: sil [ossa] @test_lexical_hoist_without_barrier :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [[V]]
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-NEXT: tuple ()
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_hoist_without_barrier : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
%ignore = tuple ()
destroy_value %0
%t = tuple ()
return %t
}
// Branching version: one arm consumes the lexical value outright (no destroy needed
// there), the other has a non-consuming use followed by a deinit barrier before the
// merge point's shared destroy. The barrier must block hoisting on the using arm only.
// CHECK-LABEL: sil [ossa] @test_lexical_hoist_across_branch_with_barrier :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [[V]]
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_hoist_across_branch_with_barrier : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// Similar to above, but now the barrier is at the join point.
// Since we cannot destroy the lexical value before the barrier, a copy is required on the consuming side.
// CHECK-LABEL: sil [ossa] @test_lexical_hoist_join_point_barrier :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [[V]]
// CHECK: bb1:
// CHECK-NEXT: [[LEX_COPY:%.*]] = copy_value [[LEX]]
// CHECK-NEXT: apply {{%.*}}([[LEX_COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_hoist_join_point_barrier : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
br bb3
bb3:
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_use_before_branch_consume :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: br bb3
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_use_before_branch_consume : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_interior_destroy :
// CHECK: [[V:%.*]] = mark_uninitialized [rootself] %0
// CHECK-NEXT: return [[V]]
// CHECK-NEXT: } // end sil function
sil [ossa] @test_interior_destroy : $@convention(method) (@owned Kl) -> @owned Kl {
bb0(%0 : @owned $Kl):
debug_value %0, let, name "self", argno 1
%2 = mark_uninitialized [rootself] %0
specify_test "lifetime_resolution_resolve %2"
destroy_value %2
return %2
}
// A borrow scope is an interval, not a point: the destroy must land after the `end_borrow`,
// never inside the scope.
// CHECK-LABEL: sil [ossa] @test_borrow_scope :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[B:%.*]] = begin_borrow [[V]]
// CHECK-NEXT: apply {{%.*}}([[B]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: end_borrow [[B]]
// CHECK-NEXT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_borrow_scope : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
%b = begin_borrow %0
apply %useFn(%b) : $@convention(thin) (@guaranteed Kl) -> ()
end_borrow %b
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_borrow_scope_then_consume :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[B:%.*]] = begin_borrow [[V]]
// CHECK-NEXT: apply {{%.*}}([[B]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: end_borrow [[B]]
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_borrow_scope_then_consume : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
%b = begin_borrow %0
apply %useFn(%b) : $@convention(thin) (@guaranteed Kl) -> ()
end_borrow %b
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
destroy_value %0
%t = tuple ()
return %t
}
// Consumed on one path with no non-consuming use anywhere: the other path still needs a
// destroy, which lands at its entry.
// CHECK-LABEL: sil [ossa] @test_consume_one_path_no_use :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: bb1:
// CHECK-NEXT: apply {{%.*}}([[V]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: destroy_value [[V]]
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_consume_one_path_no_use : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
br bb3
bb2:
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// The root's only use is a forwarding consume, so it needs no destroy of its own; the
// forwarded aggregate keeps its.
// CHECK-LABEL: sil [ossa] @test_forwarding_consume_only :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[W:%.*]] = struct $Wrapper ([[V]])
// CHECK-NEXT: destroy_value [[W]]
// CHECK-NOT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_forwarding_consume_only : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
%1 = struct $Wrapper (%0 : $Kl)
destroy_value %1
%t = tuple ()
return %t
}
// A lexical value consumed at every boundary point needs no destroy, even though a deinit
// barrier sits below the consume.
// CHECK-LABEL: sil [ossa] @test_lexical_barrier_below_consume :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: [[LEX:%.*]] = move_value [lexical] [var_decl] [[V]]
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-NOT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_barrier_below_consume : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
%1 = move_value [lexical] [var_decl] %0
apply %useFn(%1) : $@convention(thin) (@guaranteed Kl) -> ()
destroy_value %1
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_lexical_barrier_below_consume_branch :
// CHECK: [[LEX:%.*]] = move_value [lexical]
// CHECK: bb1:
// CHECK-NEXT: [[LEX_COPY:%.*]] = copy_value [[LEX]]
// CHECK-NEXT: apply {{%.*}}([[LEX_COPY]]) : $@convention(thin) (@owned Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: apply {{%.*}}([[LEX]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[LEX]]
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NOT: destroy_value
// CHECK-LABEL: } // end sil function
sil [ossa] @test_lexical_barrier_below_consume_branch : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%consFn = function_ref @consumeK : $@convention(thin) (@owned Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%raw = apply %getFn() : $@convention(thin) () -> @owned Kl
%0 = move_value [lexical] %raw
specify_test "lifetime_resolution_resolve %0"
cond_br undef, bb1, bb2
bb1:
apply %consFn(%0) : $@convention(thin) (@owned Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
br bb3
bb2:
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
br bb3
bb3:
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_no_use_destroy :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK-NEXT: destroy_value [[V]]
// CHECK: } // end sil function
sil [ossa] @test_no_use_destroy : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_dead_def_distant_destroy :
// CHECK: [[V:%.*]] = apply {{%.*}}() : $@convention(thin) () -> @owned Kl
// CHECK: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_dead_def_distant_destroy : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
specify_test "lifetime_resolution_resolve %0"
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_dead_def_block_arg :
// CHECK: bb2([[V:%.*]] : @owned $Kl):
// CHECK: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_dead_def_block_arg : $@convention(thin) () -> () {
bb0:
%getFn = function_ref @getK : $@convention(thin) () -> @owned Kl
%0 = apply %getFn() : $@convention(thin) () -> @owned Kl
checked_cast_br Kl in %0 to Sub, bb1, bb2
bb1(%1 : @owned $Sub):
destroy_value %1
br bb3
bb2(%2 : @owned $Kl):
specify_test "lifetime_resolution_resolve @argument"
destroy_value %2
br bb3
bb3:
%t = tuple ()
return %t
}
// An `@owned` block argument has no defining instruction, but is still resolved. It is
// lexical, so the barrier below its use blocks hoisting.
// CHECK-LABEL: sil [ossa] @test_owned_block_arg :
// CHECK: bb0([[V:%.*]] : @owned $Kl):
// CHECK: apply {{%.*}}([[V]]) : $@convention(thin) (@guaranteed Kl) -> ()
// CHECK-NEXT: apply {{%.*}}() : $@convention(thin) () -> ()
// CHECK-NEXT: destroy_value [[V]]
// CHECK-LABEL: } // end sil function
sil [ossa] @test_owned_block_arg : $@convention(thin) (@owned Kl) -> () {
bb0(%0 : @owned $Kl):
%useFn = function_ref @useK : $@convention(thin) (@guaranteed Kl) -> ()
%barrierFn = function_ref @barrierK : $@convention(thin) () -> ()
specify_test "lifetime_resolution_resolve @argument[0]"
apply %useFn(%0) : $@convention(thin) (@guaranteed Kl) -> ()
apply %barrierFn() : $@convention(thin) () -> ()
destroy_value %0
%t = tuple ()
return %t
}
// CHECK-LABEL: sil [ossa] @test_trivial_root :
// CHECK: integer_literal $Builtin.Int64, 0
// CHECK-NEXT: tuple ()
// CHECK-LABEL: } // end sil function
sil [ossa] @test_trivial_root : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 0
specify_test "lifetime_resolution_resolve %0"
%t = tuple ()
return %t
}