Files
swift-mirror/test/SILOptimizer/simplify_open_existential_ref.sil

276 lines
15 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -onone-simplification -simplify-instruction=open_existential_ref | %FileCheck %s
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -simplification -simplify-instruction=open_existential_ref | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
import SwiftShims
class Klass {}
sil @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
sil @use_generic_obj_guaranteed : $@convention(thin) <T> (@guaranteed T) -> ()
// A single direct `unchecked_ref_cast` consuming an owned existential -- the case the
// old SILCombiner fold in visitUncheckedRefCastInst already handled.
// CHECK-LABEL: sil [ossa] @fold_owned_direct_cast :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Builtin.NativeObject
// CHECK: return [[CAST]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'fold_owned_direct_cast'
sil [ossa] @fold_owned_direct_cast : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(1, AnyObject) Self
%3 = unchecked_ref_cast %2 : $@opened(1, AnyObject) Self to $Builtin.NativeObject
return %3 : $Builtin.NativeObject
}
// An owned existential with a `begin_borrow` scope (whose only inner use is a cast)
// plus a separate, final direct consuming cast -- this is the pattern
// SingleBlockOwnedForwardingInstFolder could not express, since it required exactly
// one non-debug use of the opened value.
// CHECK-LABEL: sil [ossa] @fold_owned_borrow_scope_and_direct_cast :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Klass
// CHECK: [[INNER:%.*]] = unchecked_ref_cast [[BORROW]] : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[INNER]])
// CHECK: end_borrow [[BORROW]] : $Klass
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Builtin.NativeObject
// CHECK: return [[CAST]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'fold_owned_borrow_scope_and_direct_cast'
sil [ossa] @fold_owned_borrow_scope_and_direct_cast : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(2, AnyObject) Self
%3 = begin_borrow %2 : $@opened(2, AnyObject) Self
%4 = unchecked_ref_cast %3 : $@opened(2, AnyObject) Self to $Klass
%5 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%6 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %3 : $@opened(2, AnyObject) Self
%7 = unchecked_ref_cast %2 : $@opened(2, AnyObject) Self to $Builtin.NativeObject
return %7 : $Builtin.NativeObject
}
// Same shape as above, but the borrow scope spans multiple blocks (two `end_borrow`s,
// one per predecessor of the join block) -- confirms the fold isn't restricted to a
// single basic block.
// CHECK-LABEL: sil [ossa] @fold_owned_multiblock_borrow_scope :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Klass
// CHECK: cond_br
// CHECK: bb1:
// CHECK: [[INNER:%.*]] = unchecked_ref_cast [[BORROW]] : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[INNER]])
// CHECK: end_borrow [[BORROW]] : $Klass
// CHECK: br bb3
// CHECK: bb2:
// CHECK: end_borrow [[BORROW]] : $Klass
// CHECK: br bb3
// CHECK: bb3:
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Builtin.NativeObject
// CHECK: return [[CAST]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'fold_owned_multiblock_borrow_scope'
sil [ossa] @fold_owned_multiblock_borrow_scope : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(3, AnyObject) Self
%3 = begin_borrow %2 : $@opened(3, AnyObject) Self
cond_br undef, bb1, bb2
bb1:
%4 = unchecked_ref_cast %3 : $@opened(3, AnyObject) Self to $Klass
%5 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%6 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %3 : $@opened(3, AnyObject) Self
br bb3
bb2:
end_borrow %3 : $@opened(3, AnyObject) Self
br bb3
bb3:
%7 = unchecked_ref_cast %2 : $@opened(3, AnyObject) Self to $Builtin.NativeObject
return %7 : $Builtin.NativeObject
}
// A guaranteed (non-owned) existential with one recognized direct cast, one recognized
// begin_borrow-wrapped cast, and one unrecognized direct use (a bare generic `apply`).
// Since forwarding a guaranteed value has no single-use restriction, the recognized
// uses are redirected even though the existential as a whole can't be fully removed.
// CHECK-LABEL: sil [ossa] @fold_guaranteed_existential_partial :
// CHECK: [[IER:%.*]] = init_existential_ref %0
// CHECK: [[OER:%.*]] = open_existential_ref [[IER]]
// CHECK: [[CAST1:%.*]] = unchecked_ref_cast %0 : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[CAST1]])
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Klass
// CHECK: [[CAST2:%.*]] = unchecked_ref_cast [[BORROW]] : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[CAST2]])
// CHECK: end_borrow [[BORROW]] : $Klass
// CHECK: apply {{%[0-9]+}}<@opened{{.*}}>([[OER]])
// CHECK-LABEL: } // end sil function 'fold_guaranteed_existential_partial'
sil [ossa] @fold_guaranteed_existential_partial : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(4, AnyObject) Self
%3 = unchecked_ref_cast %2 : $@opened(4, AnyObject) Self to $Klass
%4 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%5 = apply %4(%3) : $@convention(thin) (@guaranteed Klass) -> ()
%6 = begin_borrow %2 : $@opened(4, AnyObject) Self
%7 = unchecked_ref_cast %6 : $@opened(4, AnyObject) Self to $Klass
%8 = apply %4(%7) : $@convention(thin) (@guaranteed Klass) -> ()
end_borrow %6 : $@opened(4, AnyObject) Self
%9 = function_ref @use_generic_obj_guaranteed : $@convention(thin) <T> (@guaranteed T) -> ()
apply %9<@opened(4, AnyObject) Self>(%2) : $@convention(thin) <T> (@guaranteed T) -> ()
%r = tuple ()
return %r : $()
}
// A guaranteed existential whose opened archetype is also referenced by a `metatype`
// (a type-dependent, not a real data, use). The type-dependent operand must be ignored
// when classifying uses, and must keep the existential alive since the metatype's type
// still refers to its opened archetype.
// CHECK-LABEL: sil [ossa] @fold_guaranteed_ignoring_type_dependent_operand :
// CHECK: [[IER:%.*]] = init_existential_ref %0
// CHECK: [[OER:%.*]] = open_existential_ref [[IER]]
// CHECK: [[MT:%.*]] = metatype
// CHECK: fix_lifetime [[MT]]
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[CAST]])
// CHECK-LABEL: } // end sil function 'fold_guaranteed_ignoring_type_dependent_operand'
sil [ossa] @fold_guaranteed_ignoring_type_dependent_operand : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(5, AnyObject) Self
%3 = metatype $@thick (@opened(5, AnyObject) Self).Type
fix_lifetime %3
%4 = unchecked_ref_cast %2 : $@opened(5, AnyObject) Self to $Klass
%5 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%6 = apply %5(%4) : $@convention(thin) (@guaranteed Klass) -> ()
%r = tuple ()
return %r : $()
}
// A guaranteed existential whose opened archetype is named as the *destination* type of
// an unrelated `unchecked_ref_cast` -- a type-dependent, not a data, operand -- while that
// cast's real operand is a completely different value (%1). The type-dependent operand
// must not be mistaken for the cast's data operand: doing so would incorrectly redirect
// the unrelated cast's real operand to `ier.instance` (%0) instead of leaving %1 alone.
// CHECK-LABEL: sil [ossa] @fold_guaranteed_ignoring_type_dependent_cast_operand :
// CHECK: [[IER:%.*]] = init_existential_ref %0
// CHECK: [[OER:%.*]] = open_existential_ref [[IER]]
// CHECK: [[UNRELATED:%.*]] = unchecked_ref_cast %1 : $Klass to $@opened{{.*}} Self
// CHECK: fix_lifetime [[UNRELATED]]
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Klass
// CHECK: apply {{%[0-9]+}}([[CAST]])
// CHECK-LABEL: } // end sil function 'fold_guaranteed_ignoring_type_dependent_cast_operand'
sil [ossa] @fold_guaranteed_ignoring_type_dependent_cast_operand : $@convention(thin) (@guaranteed Klass, @guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass):
%2 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%3 = open_existential_ref %2 : $AnyObject to $@opened(6, AnyObject) Self
%4 = unchecked_ref_cast %1 : $Klass to $@opened(6, AnyObject) Self
fix_lifetime %4
%5 = unchecked_ref_cast %3 : $@opened(6, AnyObject) Self to $Klass
%6 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%7 = apply %6(%5) : $@convention(thin) (@guaranteed Klass) -> ()
%r = tuple ()
return %r : $()
}
// A non-OSSA function (post-ownership-lowering SIL, no explicit ownership qualifiers).
// `forwardingOwnership`/`ownership` both report `.none` here, so this takes the same
// permissive path as the guaranteed case above.
// CHECK-LABEL: sil @fold_non_ossa :
// CHECK-NOT: init_existential_ref
// CHECK-NOT: open_existential_ref
// CHECK: [[CAST:%.*]] = unchecked_ref_cast %0 : $Klass to $Builtin.NativeObject
// CHECK: return [[CAST]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'fold_non_ossa'
sil @fold_non_ossa : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
bb0(%0 : $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(7, AnyObject) Self
%3 = unchecked_ref_cast %2 : $@opened(7, AnyObject) Self to $Builtin.NativeObject
return %3 : $Builtin.NativeObject
}
// Negative: the owned existential is used directly (not through a cast or a
// begin_borrow scope) by a bare generic `apply`. This is not one of the two recognized
// shapes, so the owned/strict path must bail out entirely rather than partially fold.
// CHECK-LABEL: sil [ossa] @dont_fold_owned_apply_use :
// CHECK: init_existential_ref
// CHECK: open_existential_ref
// CHECK-LABEL: } // end sil function 'dont_fold_owned_apply_use'
sil [ossa] @dont_fold_owned_apply_use : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(8, AnyObject) Self
%f = function_ref @use_generic_obj_guaranteed : $@convention(thin) <T> (@guaranteed T) -> ()
apply %f<@opened(8, AnyObject) Self>(%2) : $@convention(thin) <T> (@guaranteed T) -> ()
destroy_value %2 : $@opened(8, AnyObject) Self
%x = tuple ()
return %x : $()
}
// Negative: the owned existential's begin_borrow scope contains a non-cast use (a bare
// generic `apply` on the borrowed value itself). The whole scope is unrecognized, and
// since the existential is owned, this forces a full bail-out.
// CHECK-LABEL: sil [ossa] @dont_fold_owned_noncast_use_in_borrow_scope :
// CHECK: init_existential_ref
// CHECK: open_existential_ref
// CHECK: begin_borrow
// CHECK-LABEL: } // end sil function 'dont_fold_owned_noncast_use_in_borrow_scope'
sil [ossa] @dont_fold_owned_noncast_use_in_borrow_scope : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%2 = open_existential_ref %1 : $AnyObject to $@opened(9, AnyObject) Self
%3 = begin_borrow %2 : $@opened(9, AnyObject) Self
%f = function_ref @use_generic_obj_guaranteed : $@convention(thin) <T> (@guaranteed T) -> ()
apply %f<@opened(9, AnyObject) Self>(%3) : $@convention(thin) <T> (@guaranteed T) -> ()
end_borrow %3 : $@opened(9, AnyObject) Self
destroy_value %2 : $@opened(9, AnyObject) Self
%x = tuple ()
return %x : $()
}
// Negative: `init_existential_ref` has an extra use besides feeding the
// `open_existential_ref` (a bare generic `apply` borrowing it directly). Redirecting
// %2's uses to `ier.instance` while `ier` itself is still alive and later consumed
// would consume the original instance twice, so the owned path requires `ier` to have
// exactly one use before attempting anything.
// CHECK-LABEL: sil [ossa] @dont_fold_owned_ier_extra_use :
// CHECK: [[IER:%.*]] = init_existential_ref %0
// CHECK: apply {{%[0-9]+}}<AnyObject>([[IER]])
// CHECK: [[OER:%.*]] = open_existential_ref [[IER]]
// CHECK: [[CAST:%.*]] = unchecked_ref_cast [[OER]]
// CHECK: return [[CAST]]
// CHECK-LABEL: } // end sil function 'dont_fold_owned_ier_extra_use'
sil [ossa] @dont_fold_owned_ier_extra_use : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Klass):
%1 = init_existential_ref %0 : $Klass : $Klass, $AnyObject
%f = function_ref @use_generic_obj_guaranteed : $@convention(thin) <T> (@guaranteed T) -> ()
apply %f<AnyObject>(%1) : $@convention(thin) <T> (@guaranteed T) -> ()
%2 = open_existential_ref %1 : $AnyObject to $@opened(10, AnyObject) Self
%3 = unchecked_ref_cast %2 : $@opened(10, AnyObject) Self to $Builtin.NativeObject
return %3 : $Builtin.NativeObject
}
// Negative: the existential operand of `open_existential_ref` is not an
// `init_existential_ref`.
// CHECK-LABEL: sil [ossa] @dont_fold_non_init_existential_source :
// CHECK: open_existential_ref %0
// CHECK-LABEL: } // end sil function 'dont_fold_non_init_existential_source'
sil [ossa] @dont_fold_non_init_existential_source : $@convention(thin) (@guaranteed AnyObject) -> () {
bb0(%0 : @guaranteed $AnyObject):
%1 = open_existential_ref %0 : $AnyObject to $@opened(11, AnyObject) Self
%2 = unchecked_ref_cast %1 : $@opened(11, AnyObject) Self to $Klass
%3 = function_ref @takeKlass : $@convention(thin) (@guaranteed Klass) -> ()
%4 = apply %3(%2) : $@convention(thin) (@guaranteed Klass) -> ()
%r = tuple ()
return %r : $()
}