Files
swift-mirror/test/SILOptimizer/lexical_destroy_folding.sil
Erik Eckstein 18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.

The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
2026-01-22 17:41:48 +01:00

849 lines
36 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -copy-propagation -enable-sil-verify-all %s | %FileCheck %s
// =============================================================================
// = DECLARATIONS {{
// =============================================================================
class C {}
class D {}
sil [ossa] @barrier : $@convention(thin) () -> ()
sil [ossa] @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
sil [ossa] @callee_owned : $@convention(thin) (@owned C) -> ()
sil [ossa] @callee_owned_owned : $@convention(thin) (@owned C, @owned C) -> ()
sil [ossa] @callee_owned_guaranteed : $@convention(thin) (@owned C, @guaranteed C) -> ()
sil [ossa] @get_owned : $@convention(thin) () -> @owned C
// =============================================================================
// = DECLARATIONS }}
// =============================================================================
// =============================================================================
// TESTS {{
// =============================================================================
// The simplest test for LexicalDestroyFolding. Check that an outer lexical
// owned lexical lifetime is added around the existing inner guaranteed lexical
// lifetime. The new lifetime should begin with a move_value [lexical] and end
// with an apply.
//
// CHECK-LABEL: sil [ossa] @fold_simplest : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: apply [[CALEE_OWNED]]([[MOVE]])
// CHECK-LABEL: } // end sil function 'fold_simplest'
sil [ossa] @fold_simplest : $@convention(thin) () -> () {
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Fold when there is a use of %original_borrowee prior to the lexical lifetime.
sil [ossa] @fold_prior_use : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold an apply where an owned value is passed to a guaranteed parameter.
// The callee can't end ownership.
//
// CHECK-LABEL: sil [ossa] @nofold_owned_arg_to_guaranteed_param : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_owned_arg_to_guaranteed_param'
sil [ossa] @nofold_owned_arg_to_guaranteed_param : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%copy) : $@convention(thin) (@guaranteed C) -> ()
end_borrow %lifetime : $C
destroy_value %copy : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Enclose a single block guaranteed lexical scope ending in a consuming use
// and destroy within a single block owned lexical scope, leaving the original
// borrowee intact on other paths.
//
// CHECK-LABEL: sil [ossa] @fold_left_only : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: apply [[CALLEE_OWNED]]([[INSTANCE]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_left_only'
sil [ossa] @fold_left_only : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
cond_br undef, left, right
left:
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
apply %callee_owned(%instance) : $@convention(thin) (@owned C) -> ()
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Enclose two independent parallel guaranteed lexical scopes within two
// independents owned lexical scopes.
//
// CHECK-LABEL: sil [ossa] @fold_two_independent_scopes : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[MOVE_LEFT:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: apply [[CALLEE_OWNED]]([[MOVE_LEFT]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: [[MOVE_RIGHT:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: apply [[CALLEE_OWNED]]([[MOVE_RIGHT]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_two_independent_scopes'
sil [ossa] @fold_two_independent_scopes : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
cond_br undef, left, right
left:
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
%lifetime_1 = begin_borrow [lexical] %instance : $C
%copy_1 = copy_value %lifetime_1 : $C
apply %callee_owned(%copy_1) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime_1 : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Enclose a single guaranteed lexical scope that contains two parallel
// applies of owned arguments with the destroy at a NONcanonical position
// within a single owned lexical scope.
//
// CHECK-LABEL: sil [ossa] @fold_two_parallel_applies___noncanonical_destroys : {{.*}} {
// CHECK: {{bb[0-9]+}}:
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_two_parallel_applies___noncanonical_destroys'
sil [ossa] @fold_two_parallel_applies___noncanonical_destroys : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%lifetime = begin_borrow [lexical] %instance : $C
cond_br undef, left, right
left:
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
br exit
right:
%copy_2 = copy_value %lifetime : $C
apply %callee_owned(%copy_2) : $@convention(thin) (@owned C) -> ()
br exit
exit:
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Enclose a single guaranteed lexical scope that contains two parallel
// applies of owned arguments with the destroy at a noncanonical position
// within a single owned lexical scope.
//
// CHECK-LABEL: sil [ossa] @fold_two_parallel_applies___canonical_destroys : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]] : $C
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT:bb[0-9]+]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_two_parallel_applies___canonical_destroys'
sil [ossa] @fold_two_parallel_applies___canonical_destroys : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%lifetime = begin_borrow [lexical] %instance : $C
cond_br undef, left, right
left:
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
%copy_2 = copy_value %lifetime : $C
apply %callee_owned(%copy_2) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Enclose a guaranteed lexical scope within an owned lexical scope when it
// appears in a multiblock subgraph that is parallel to another apply use of the
// borrowee that is not within the scope.
//
// CHECK-LABEL: sil [ossa] @fold_multiblock_borrow_single_apply_with_parallel_use : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: cond_br undef, [[LEFT1:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT1]]:
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: cond_br undef, [[LEFT2:bb[0-9]+]], [[LEFT3:bb[0-9]+]]
// CHECK: [[LEFT2]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT3]]:
// CHECK: destroy_value [[MOVE]]
// CHECK: br [[EXIT]]
// CHECK: [[RIGHT]]:
// CHECK: apply [[CALLEE_OWNED]]([[INSTANCE]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_multiblock_borrow_single_apply_with_parallel_use'
sil [ossa] @fold_multiblock_borrow_single_apply_with_parallel_use : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
cond_br undef, left1, right
left1:
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
cond_br undef, left2, left3
left2:
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
left3:
destroy_value %copy : $C
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
apply %callee_owned(%instance) : $@convention(thin) (@owned C) -> ()
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Enclose a guaranteed lexical scope within an owned lexical scope when it
// appears in a multiblock subgraph that is parallel to another apply use of the
// borrowee that is not within the scope.
//
// CHECK-LABEL: sil [ossa] @fold_multiblock_borrow_single_apply_and_parallel_singleblock_borrow : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: cond_br undef, [[LEFT1:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT1]]:
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: cond_br undef, [[LEFT2:bb[0-9]+]], [[LEFT3:bb[0-9]+]]
// CHECK: [[LEFT2]]:
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT3]]:
// CHECK: destroy_value [[MOVE]]
// CHECK: br [[EXIT]]
// CHECK: [[RIGHT]]:
// CHECK: [[MOVE_2:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: apply [[CALLEE_OWNED]]([[MOVE_2]])
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_multiblock_borrow_single_apply_and_parallel_singleblock_borrow'
sil [ossa] @fold_multiblock_borrow_single_apply_and_parallel_singleblock_borrow : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
cond_br undef, left1, right
left1:
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
cond_br undef, left2, left3
left2:
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
left3:
destroy_value %copy : $C
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
%lifetime_1 = begin_borrow [lexical] %instance : $C
%copy_1 = copy_value %lifetime_1 : $C
apply %callee_owned(%copy_1) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime_1 : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Fold with an apply that takes the borrowee @owned twice and no use of the
// guaranteed lifetime remains.
//
// CHECK-LABEL: sil [ossa] @fold_single_block_one_apply_double_use_all_owned : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[LIFETIME:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: [[LIFETIME_GUARANTEED:%[^,]+]] = begin_borrow [lexical] [[LIFETIME]]
// CHECK: [[CALLEE_GUARANTEED:%[^,]+]] = function_ref @callee_guaranteed
// CHECK: apply [[CALLEE_GUARANTEED]]([[LIFETIME_GUARANTEED]])
// CHECK: end_borrow [[LIFETIME_GUARANTEED]]
// CHECK: [[COPY:%[^,]+]] = copy_value [[LIFETIME]]
// CHECK: [[CALLEE_OWNED_OWNED:%[^,]+]] = function_ref @callee_owned_owned
// CHECK: apply [[CALLEE_OWNED_OWNED]]([[LIFETIME]], [[COPY]])
// CHECK-LABEL: } // end sil function 'fold_single_block_one_apply_double_use_all_owned'
sil [ossa] @fold_single_block_one_apply_double_use_all_owned : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%lifetime = begin_borrow [lexical] %instance : $C
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
%copy_1 = copy_value %lifetime : $C
%copy_2 = copy_value %lifetime : $C
%callee_owned_owned = function_ref @callee_owned_owned : $@convention(thin) (@owned C, @owned C) -> ()
apply %callee_owned_owned(%copy_1, %copy_2) : $@convention(thin) (@owned C, @owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Fold with an apply that takes the borrowee @owned twice and a use of the
// guaranteed lifetime remains.
//
// CHECK-LABEL: sil [ossa] @fold_single_block_one_apply_double_use_all_owned__no_remaining_uses : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: [[COPY:%[^,]+]] = copy_value [[MOVE]]
// CHECK: [[CALLEE_OWNED_OWNED:%[^,]+]] = function_ref @callee_owned_owned
// CHECK: apply [[CALLEE_OWNED_OWNED]]([[MOVE]], [[COPY]])
// CHECK-LABEL: } // end sil function 'fold_single_block_one_apply_double_use_all_owned__no_remaining_uses'
sil [ossa] @fold_single_block_one_apply_double_use_all_owned__no_remaining_uses : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%lifetime = begin_borrow [lexical] %instance : $C
%copy_1 = copy_value %lifetime : $C
%copy_2 = copy_value %lifetime : $C
%callee_owned_owned = function_ref @callee_owned_owned : $@convention(thin) (@owned C, @owned C) -> ()
apply %callee_owned_owned(%copy_1, %copy_2) : $@convention(thin) (@owned C, @owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
} // end sil function 'test'
// Do NOT fold with an apply that takes both a copy of the borrow @owned and
// also the borrow itself @guaranteed.
//
// CHECK-LABEL: sil [ossa] @nofold_single_block_one_apply_double_use_one_guaranteed : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_single_block_one_apply_double_use_one_guaranteed'
sil [ossa] @nofold_single_block_one_apply_double_use_one_guaranteed : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
%callee_owned_guaranteed = function_ref @callee_owned_guaranteed : $@convention(thin) (@owned C, @guaranteed C) -> ()
apply %callee_owned_guaranteed(%copy, %lifetime) : $@convention(thin) (@owned C, @guaranteed C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Do NOT fold an apply of an owned value when the borrowee is guaranteed.
//
// CHECK-LABEL: sil [ossa] @nofold_guaranteed_borrowee : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_guaranteed_borrowee'
sil [ossa] @nofold_guaranteed_borrowee : $@convention(thin) (@guaranteed C) -> () {
entry(%instance : @guaranteed $C):
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @nofold_nonlexical : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_nonlexical'
sil [ossa] @nofold_nonlexical : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%lifetime = begin_borrow %instance : $C
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Fold with the last consuming use but not with a previous consuming use.
//
// CHECK-LABEL: sil [ossa] @fold_single_block_two_applies_first_only : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[MOVE]]
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: [[COPY:%[^,]+]] = copy_value [[LIFETIME]]
// CHECK: apply [[CALLEE_OWNED]]([[COPY]])
// CHECK: end_borrow [[LIFETIME]]
// CHECK: tuple ()
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK-LABEL: } // end sil function 'fold_single_block_two_applies_first_only'
sil [ossa] @fold_single_block_two_applies_first_only : $@convention(thin) () -> () {
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%copy_1 = copy_value %lifetime : $C
apply %callee_owned(%copy_1) : $@convention(thin) (@owned C) -> ()
%retval = tuple ()
%copy_2 = copy_value %lifetime : $C
apply %callee_owned(%copy_2) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
return %retval : $()
}
// Don't fold with apply when guaranteed lexical value used in one but not two
// branches and the lexical scope ends after the use on the non-lexical branch.
//
// CHECK-LABEL: sil [ossa] @nofold_two_parallel_owned_uses_one_lexical : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_two_parallel_owned_uses_one_lexical'
sil [ossa] @nofold_two_parallel_owned_uses_one_lexical : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
cond_br undef, left, right
left:
%copy_1 = copy_value %lifetime : $C
apply %callee_owned(%copy_1) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
right:
%copy_2 = copy_value %instance : $C
apply %callee_owned(%copy_2) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Fold even if there is a noncanonicalized copy which is otherwise used.
// CHECK-LABEL: sil [ossa] @fold_noncanonical_copy_with_other_use : {{.*}} {
// CHECK: {{bb[0-9]+}}:
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]()
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[MOVE]]
// CHECK: [[COPY:%[^,]+]] = copy_value [[LIFETIME]]
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: destroy_value [[COPY]]
// CHECK: end_borrow [[LIFETIME]]
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: [[CALLEE_GUARANTEED:%[^,]+]] = function_ref @callee_guaranteed
// CHECK: apply [[CALLEE_GUARANTEED]]([[COPY]])
// CHECK: destroy_value [[COPY]]
// CHECK: end_borrow [[LIFETIME]]
// CHECK: destroy_value [[MOVE]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_noncanonical_copy_with_other_use'
sil [ossa] @fold_noncanonical_copy_with_other_use : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%lifetime = begin_borrow [lexical] %instance : $C
%copy = copy_value %lifetime : $C
cond_br undef, left, right
left:
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
br exit
right:
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%copy) : $@convention(thin) (@guaranteed C) -> ()
destroy_value %copy : $C
br exit
exit:
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Fold in the face of an unreachable block on one path so long as there is no
// use of %original_borrowee in that unreachable-terminated branch.
//
// CHECK-LABEL: sil [ossa] @fold_unreachable : {{.*}} {
// CHECK: [[GET_OWNED:%[^,]+]] = function_ref @get_owned
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_OWNED]]
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: unreachable
// CHECK: [[RIGHT]]:
// CHECK: [[CALLEE_OWNED:%[^,]+]] = function_ref @callee_owned
// CHECK: apply [[CALLEE_OWNED]]([[MOVE]])
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'fold_unreachable'
sil [ossa] @fold_unreachable : $@convention(thin) () -> () {
entry:
%get_owned = function_ref @get_owned : $@convention(thin) () -> (@owned C)
%instance = apply %get_owned() : $@convention(thin) () -> (@owned C)
%lifetime = begin_borrow [lexical] %instance : $C
cond_br undef, left, right
left:
end_borrow %lifetime
destroy_value [dead_end] %instance
unreachable
right:
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Don't fold in the face of an unreachable block on one path so long as there
// is no use of %original_borrowee in that "unterminated" branch.
//
// CHECK-LABEL: sil [ossa] @nofold_unreachable : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_unreachable'
sil [ossa] @nofold_unreachable : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%lifetime = begin_borrow [lexical] %instance : $C
cond_br undef, left, right
left:
end_borrow %lifetime
apply %callee_owned(%instance) : $@convention(thin) (@owned C) -> ()
unreachable
right:
%copy = copy_value %lifetime : $C
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Don't fold if there's an outer borrow scope enclosing the guaranteed lexical
// scope and it has a use within the lexical scope.
//
// CHECK-LABEL: sil [ossa] @nofold_outer_borrow : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_outer_borrow'
sil [ossa] @nofold_outer_borrow : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%outer = begin_borrow %instance : $C
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_guaranteed(%outer) : $@convention(thin) (@guaranteed C) -> ()
apply %barrier() : $@convention(thin) () -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
end_borrow %outer : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold if there's an outer borrow scope started in one block enclosing
// the guaranteed lexical scope started in another block and it has a use
// within the lexical scope.
//
// CHECK-LABEL: sil [ossa] @nofold_outer_borrow__twoblock : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_outer_borrow__twoblock'
sil [ossa] @nofold_outer_borrow__twoblock : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%outer = begin_borrow %instance : $C
br exit
exit:
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_guaranteed(%outer) : $@convention(thin) (@guaranteed C) -> ()
apply %barrier() : $@convention(thin) () -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
end_borrow %outer : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold if there are TWO layers of outer borrows, both started in one
// block enclosing the guaranteed lexical scope started--all together in the
// same block--and both have a use within the lexical scope.
//
// CHECK-LABEL: sil [ossa] @nofold_outer_borrow_2x : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_outer_borrow_2x'
sil [ossa] @nofold_outer_borrow_2x : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%outer = begin_borrow %instance : $C
%middle = begin_borrow %outer : $C
br exit
exit:
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_guaranteed(%outer) : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%middle) : $@convention(thin) (@guaranteed C) -> ()
apply %barrier() : $@convention(thin) () -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
end_borrow %middle : $C
end_borrow %outer : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold if there are TWO layers of outer borrows, both started in one
// block, enclosing the guaranteed lexical scope started in another block and
// both have a use within the lexical scope.
//
// CHECK-LABEL: sil [ossa] @nofold_outer_borrow_2x__twoblock : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_outer_borrow_2x__twoblock'
sil [ossa] @nofold_outer_borrow_2x__twoblock : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%outer = begin_borrow %instance : $C
%middle = begin_borrow %outer : $C
br exit
exit:
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_guaranteed(%outer) : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%middle) : $@convention(thin) (@guaranteed C) -> ()
apply %barrier() : $@convention(thin) () -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
end_borrow %middle : $C
end_borrow %outer : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold when a different borrow of the enclosing %original_borrowee end
// in the middle of the lexical scope.
//
// CHECK-LABEL: sil [ossa] @nofold_outer_borrow_2x_end_inside__twoblock : {{.*}} {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_outer_borrow_2x_end_inside__twoblock'
sil [ossa] @nofold_outer_borrow_2x_end_inside__twoblock : $@convention(thin) (@owned C) -> () {
entry(%instance : @owned $C):
%outer = begin_borrow %instance : $C
%middle = begin_borrow %outer : $C
br exit
exit:
%lifetime = begin_borrow [lexical] %instance : $C
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%barrier = function_ref @barrier : $@convention(thin) () -> ()
%callee_guaranteed = function_ref @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
%copy = copy_value %lifetime : $C
apply %callee_guaranteed(%outer) : $@convention(thin) (@guaranteed C) -> ()
apply %callee_guaranteed(%middle) : $@convention(thin) (@guaranteed C) -> ()
end_borrow %middle : $C
end_borrow %outer : $C
apply %barrier() : $@convention(thin) () -> ()
apply %callee_owned(%copy) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
destroy_value %instance : $C
%retval = tuple ()
return %retval : $()
}
// Don't fold applies of the borrowed value.
//
// CHECK-LABEL: sil [ossa] @nofold_closure : $@convention(thin) (@owned () -> ()) -> () {
// CHECK-NOT: move_value [lexical]
// CHECK-LABEL: } // end sil function 'nofold_closure'
sil [ossa] @nofold_closure : $@convention(thin) (@owned @convention(thick) () -> ()) -> () {
entry(%instance : @owned $@convention(thick) () -> ()):
%lifetime = begin_borrow [lexical] %instance : $@convention(thick) () -> ()
apply %lifetime() : $@convention(thick) () -> ()
end_borrow %lifetime : $@convention(thick) () -> ()
destroy_value %instance : $@convention(thick) () -> ()
%retval = tuple ()
return %retval : $()
}
// Lexical destroy folding cannot handle a PointerEscape.
//
// CHECK-LABEL: sil [ossa] @dont_fold_over_escape : $@convention(thin) (@owned C) -> @owned D {
// CHECK: bb0(%0 : @owned $C):
// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $C to $D
// CHECK: [[BORROW:%.*]] = begin_borrow [lexical] %0 : $C
// CHECK: [[COPY0:%.*]] = copy_value [[BORROW]] : $C
// CHECK: apply %{{.*}}([[COPY0]]) : $@convention(thin) (@owned C) -> ()
// CHECK: [[COPY1:%.*]] = copy_value [[CAST]] : $D
// CHECK: destroy_value %0 : $C
// CHECK-LABEL: } // end sil function 'dont_fold_over_escape'
sil [ossa] @dont_fold_over_escape : $@convention(thin) (@owned C) -> @owned D {
bb0(%0 : @owned $C):
%callee_owned = function_ref @callee_owned : $@convention(thin) (@owned C) -> ()
%cast = unchecked_bitwise_cast %0 : $C to $D
%lifetime = begin_borrow [lexical] %0 : $C
%copy0 = copy_value %lifetime : $C
apply %callee_owned(%copy0) : $@convention(thin) (@owned C) -> ()
end_borrow %lifetime : $C
%copy = copy_value %cast : $D
destroy_value %0 : $C
return %copy : $D
}
// =============================================================================
// TESTS }}
// =============================================================================