mirror of
https://github.com/apple/swift.git
synced 2026-10-06 12:37:28 +02:00
276 lines
15 KiB
Plaintext
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 : $()
|
|
}
|