| // RUN: %target-sil-opt -enable-sil-verify-all -mandatory-combine -sil-mandatory-combine-enable-canon-and-simple-dce -semantic-arc-opts %s | %FileCheck %s |
| |
| // Make sure that we can perform all of these RAUW without producing ARC traffic |
| // that semantic arc opts can't eliminate. |
| |
| sil_stage canonical |
| |
| import Builtin |
| |
| // Trivial declarations |
| |
| struct MyInt { |
| var value: Builtin.Int64 |
| } |
| |
| // Generic declarations |
| |
| protocol Addable { |
| static var an: Self { get } |
| } |
| |
| // Class declarations |
| |
| class Klass { |
| init() |
| deinit |
| } |
| |
| class SubKlass : Klass {} |
| |
| sil @use_klass_unowned : $@convention(thin) (Klass) -> () |
| |
| // Existential declarations |
| |
| protocol Proto { |
| static var an: Proto { get } |
| } |
| |
| // Trivial support |
| |
| sil @first_of_three_ints : $@convention(thin) (MyInt, MyInt, MyInt) -> MyInt |
| |
| sil @constant_zero : $@convention(thin) () -> MyInt |
| |
| sil @identity_int : $@convention(thin) (MyInt) -> MyInt |
| |
| // Generic support |
| |
| sil @first_of_three_addables : $@convention(thin) <A where A : Addable> (@in_guaranteed A, @guaranteed <τ_0_0 where τ_0_0 : Addable> { var τ_0_0 } <A>, @guaranteed <τ_0_0 where τ_0_0 : Addable> { var τ_0_0 } <A>) -> @ |
| out A |
| |
| // Class support |
| |
| sil [exact_self_class] @klass_alloc_init : $@convention(method) (@thick Klass.Type) -> @owned Klass |
| |
| // Klass.init() |
| sil @klass_init : $@convention(method) (@owned Klass) -> @owned Klass |
| // Klass.deinit |
| sil @klass_deinit : $@convention(method) (@guaranteed Klass) -> @owned Builtin.NativeObject |
| |
| // Klass.__deallocating_deinit |
| sil @klass_dealloc_deinit : $@convention(method) (@owned Klass) -> () |
| |
| sil_vtable Klass { |
| #Klass.init!allocator: (Klass.Type) -> () -> Klass : @klass_alloc_init |
| #Klass.deinit!deallocator: @klass_dealloc_deinit |
| } |
| |
| sil @first_of_three_klasses : $@convention(thin) (@guaranteed Klass, @guaranteed Klass, @guaranteed Klass) -> @owned Klass |
| |
| sil @use_klass_guaranteed : $@convention(thin) (@guaranteed Klass) -> () |
| |
| // Existential support |
| |
| sil @first_of_three_protos : $@convention(thin) (@in_guaranteed Proto, @guaranteed { var Proto }, @guaranteed { var Proto }) -> @out Proto |
| |
| sil @get_proto : $@convention(thin) () -> @out Proto |
| |
| // Mixed support |
| |
| sil @proto_from_proto_and_myint : $@convention(thin) (@in_guaranteed Proto, MyInt) -> @out Proto |
| |
| sil @myint_from_myint_and_proto : $@convention(thin) (MyInt, @guaranteed { var Proto }) -> MyInt |
| |
| sil @myint_from_proto_and_myint : $@convention(thin) (@guaranteed { var Proto }, MyInt) -> MyInt |
| |
| // Enum support |
| |
| enum FakeOptional<T> { |
| case none |
| case some(T) |
| } |
| |
| sil @use_fakeoptional_klass_guaranteed : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| |
| /////////// |
| // Tests // |
| /////////// |
| |
| //===--- |
| // None Tests |
| // |
| |
| // CHECK-LABEL: sil [ossa] @none_to_none_rauw : $@convention(thin) (MyInt) -> MyInt { |
| // CHECK: bb0 |
| // CHECK: return %0 |
| // CHECK: } // end sil function 'none_to_none_rauw' |
| sil [ossa] @none_to_none_rauw : $@convention(thin) (MyInt) -> MyInt { |
| bb0(%0 : $MyInt): |
| %1 = unchecked_bitwise_cast %0 : $MyInt to $Builtin.Int64 |
| %2 = unchecked_bitwise_cast %1 : $Builtin.Int64 to $MyInt |
| return %2 : $MyInt |
| } |
| |
| // We do not support replacing .none with non-trivial ownerships. This can only |
| // occur with enum cases without payloads or with trivial payload. That requires |
| // more infrastructure than we have currently. |
| |
| //===--- |
| // Owned Tests |
| // |
| |
| // CHECK-LABEL: sil [ossa] @owned_to_owned_rauw : $@convention(thin) (@owned Klass) -> @owned Klass { |
| // CHECK: bb0( |
| // CHECK-NEXT: return |
| // CHECK: } // end sil function 'owned_to_owned_rauw' |
| sil [ossa] @owned_to_owned_rauw : $@convention(thin) (@owned Klass) -> @owned Klass { |
| bb0(%0 : @owned $Klass): |
| %1 = unchecked_ref_cast %0 : $Klass to $SubKlass |
| %2 = upcast %1 : $SubKlass to $Klass |
| return %2 : $Klass |
| } |
| |
| // We get ARC traffic here today since we do not get rid of PhiArguments kept |
| // alive only by destroys/end_borrows. We will eventually though. |
| // |
| // CHECK-LABEL: sil [ossa] @owned_to_owned_consuming : $@convention(thin) (@owned FakeOptional<Klass>) -> () { |
| // CHECK: copy_value |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK: } // end sil function 'owned_to_owned_consuming' |
| sil [ossa] @owned_to_owned_consuming : $@convention(thin) (@owned FakeOptional<Klass>) -> () { |
| bb0(%0 : @owned $FakeOptional<Klass>): |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @owned $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| br bb3(%1 : $FakeOptional<Klass>) |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bb3(%3 : $FakeOptional<Klass>) |
| |
| bb3(%4 : @owned $FakeOptional<Klass>): |
| destroy_value %4 : $FakeOptional<Klass> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| //===--- |
| // Unowned Tests |
| // |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_owned_rauw : $@convention(thin) (@owned Klass) -> @owned Klass { |
| // CHECK: bb0( |
| // CHECK-NEXT: return |
| // CHECK: } // end sil function 'unowned_to_owned_rauw' |
| sil [ossa] @unowned_to_owned_rauw : $@convention(thin) (@owned Klass) -> @owned Klass { |
| bb0(%0 : @owned $Klass): |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = copy_value %2 : $Klass |
| destroy_value %0 : $Klass |
| return %3 : $Klass |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_owned_rauw_loop : $@convention(thin) (@owned Klass) -> @owned FakeOptional<Klass> { |
| // CHECK: bb0([[ARG:%.*]] : @owned $Klass): |
| // CHECK-NOT: unchecked_bitwise_cast |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: destroy_value |
| // |
| // CHECK: bb2: |
| // CHECK-NEXT: [[COPY:%.*]] = copy_value [[ARG]] |
| // CHECK-NEXT: cond_br undef, bb3, bb4 |
| // |
| // CHECK: bb3: |
| // CHECK-NEXT: destroy_value [[COPY]] |
| // CHECK-NEXT: br bb2 |
| // |
| // CHECK: bb4: |
| // CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, [[COPY]] |
| // CHECK-NEXT: br bb6([[ENUM_SOME_RESULT]] : $FakeOptional<Klass>) |
| // |
| // CHECK: bb5: |
| // CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| // CHECK-NEXT: br bb6([[ENUM_NONE_RESULT]] : |
| // |
| // CHECK: bb6([[RESULT:%.*]] : @owned $FakeOptional<Klass>): |
| // CHECK-NEXT: destroy_value [[ARG]] |
| // CHECK-NEXT: return [[RESULT]] |
| // CHECK: } // end sil function 'unowned_to_owned_rauw_loop' |
| sil [ossa] @unowned_to_owned_rauw_loop : $@convention(thin) (@owned Klass) -> @owned FakeOptional<Klass> { |
| bb0(%0 : @owned $Klass): |
| cond_br undef, bbLoopPreHeader, bbEarlyExit |
| |
| bbLoopPreHeader: |
| br bbLoopHeader |
| |
| bbLoopHeader: |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = copy_value %2 : $Klass |
| cond_br undef, bbBackEdge, bbExitingBlock |
| |
| bbBackEdge: |
| destroy_value %3 : $Klass |
| br bbLoopHeader |
| |
| bbExitingBlock: |
| %4 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %3 : $Klass |
| br bbExitBlock(%4 : $FakeOptional<Klass>) |
| |
| bbEarlyExit: |
| %5 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bbExitBlock(%5 : $FakeOptional<Klass>) |
| |
| bbExitBlock(%result : @owned $FakeOptional<Klass>): |
| destroy_value %0 : $Klass |
| return %result : $FakeOptional<Klass> |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw : $@convention(thin) (@guaranteed Klass) -> @owned Klass { |
| // CHECK: bb0( |
| // CHECK-NEXT: copy_value |
| // CHECK-NEXT: return |
| // CHECK: } // end sil function 'unowned_to_guaranteed_rauw' |
| sil [ossa] @unowned_to_guaranteed_rauw : $@convention(thin) (@guaranteed Klass) -> @owned Klass { |
| bb0(%0 : @guaranteed $Klass): |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = copy_value %2 : $Klass |
| return %3 : $Klass |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_loop : $@convention(thin) (@guaranteed Klass) -> @owned FakeOptional<Klass> { |
| // CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass): |
| // CHECK-NOT: unchecked_bitwise_cast |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: destroy_value |
| // |
| // CHECK: bb2: |
| // CHECK-NEXT: [[COPY:%.*]] = copy_value [[ARG]] |
| // CHECK-NEXT: cond_br undef, bb3, bb4 |
| // |
| // CHECK: bb3: |
| // CHECK-NEXT: destroy_value [[COPY]] |
| // CHECK-NEXT: br bb2 |
| // |
| // CHECK: bb4: |
| // CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, [[COPY]] |
| // CHECK-NEXT: br bb6([[ENUM_SOME_RESULT]] : $FakeOptional<Klass>) |
| // |
| // CHECK: bb5: |
| // CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt // user: %10 |
| // CHECK-NEXT: br bb6([[ENUM_NONE_RESULT]] : |
| // |
| // CHECK: bb6([[RESULT:%.*]] : @owned $FakeOptional<Klass>): |
| // CHECK-NEXT: return [[RESULT]] |
| // CHECK: } // end sil function 'unowned_to_guaranteed_rauw_loop' |
| sil [ossa] @unowned_to_guaranteed_rauw_loop : $@convention(thin) (@guaranteed Klass) -> @owned FakeOptional<Klass> { |
| bb0(%0 : @guaranteed $Klass): |
| cond_br undef, bbLoopPreHeader, bbEarlyExit |
| |
| bbLoopPreHeader: |
| br bbLoopHeader |
| |
| bbLoopHeader: |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = copy_value %2 : $Klass |
| cond_br undef, bbBackEdge, bbExitingBlock |
| |
| bbBackEdge: |
| destroy_value %3 : $Klass |
| br bbLoopHeader |
| |
| bbExitingBlock: |
| %4 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %3 : $Klass |
| br bbExitBlock(%4 : $FakeOptional<Klass>) |
| |
| bbEarlyExit: |
| %5 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bbExitBlock(%5 : $FakeOptional<Klass>) |
| |
| bbExitBlock(%result : @owned $FakeOptional<Klass>): |
| return %result : $FakeOptional<Klass> |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2 : $@convention(thin) (@guaranteed Klass) -> (Klass, Klass) { |
| // CHECK: bb0( |
| // CHECK-NEXT: unchecked_ownership_conversion |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| // CHECK: } // end sil function 'unowned_to_guaranteed_rauw_2' |
| sil [ossa] @unowned_to_guaranteed_rauw_2 : $@convention(thin) (@guaranteed Klass) -> (Klass, Klass) { |
| bb0(%0 : @guaranteed $Klass): |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = tuple(%2 : $Klass, %2 : $Klass) |
| return %3 : $(Klass, Klass) |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2_loop : $@convention(thin) (@guaranteed Klass) -> @owned FakeOptional<(Klass, Klass)> { |
| // CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass): |
| // CHECK-NOT: unchecked_bitwise_cast |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: destroy_value |
| // |
| // CHECK: bb2: |
| // CHECK-NEXT: [[ARG_CONVERT:%.*]] = unchecked_ownership_conversion [[ARG]] |
| // CHECK-NEXT: [[TUP:%.*]] = tuple ([[ARG_CONVERT]] : $Klass, [[ARG_CONVERT]] : $Klass) |
| // CHECK-NEXT: [[COPY:%.*]] = copy_value [[TUP]] |
| // CHECK-NEXT: cond_br undef, bb3, bb4 |
| // |
| // CHECK: bb3: |
| // CHECK-NEXT: destroy_value [[COPY]] |
| // CHECK-NEXT: br bb2 |
| // |
| // CHECK: bb4: |
| // CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional<{{.*}}>, #FakeOptional.some!enumelt, [[COPY]] |
| // CHECK-NEXT: br bb6([[ENUM_SOME_RESULT]] : $FakeOptional<{{.*}}>) |
| // |
| // CHECK: bb5: |
| // CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional<{{.*}}>, #FakeOptional.none!enumelt |
| // CHECK-NEXT: br bb6([[ENUM_NONE_RESULT]] : |
| // |
| // CHECK: bb6([[RESULT:%.*]] : @owned $FakeOptional<{{.*}}>): |
| // CHECK-NEXT: return [[RESULT]] |
| // CHECK: } // end sil function 'unowned_to_guaranteed_rauw_2_loop' |
| sil [ossa] @unowned_to_guaranteed_rauw_2_loop : $@convention(thin) (@guaranteed Klass) -> @owned FakeOptional<(Klass, Klass)> { |
| bb0(%0 : @guaranteed $Klass): |
| cond_br undef, bbLoopPreHeader, bbEarlyExit |
| |
| bbLoopPreHeader: |
| br bbLoopHeader |
| |
| bbLoopHeader: |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| %3 = tuple(%2 : $Klass, %2 : $Klass) |
| %4 = copy_value %3 : $(Klass, Klass) |
| cond_br undef, bbBackEdge, bbExitingBlock |
| |
| bbBackEdge: |
| destroy_value %4 : $(Klass, Klass) |
| br bbLoopHeader |
| |
| bbExitingBlock: |
| %5 = enum $FakeOptional<(Klass, Klass)>, #FakeOptional.some!enumelt, %4 : $(Klass, Klass) |
| br bbExitBlock(%5 : $FakeOptional<(Klass, Klass)>) |
| |
| bbEarlyExit: |
| %6 = enum $FakeOptional<(Klass, Klass)>, #FakeOptional.none!enumelt |
| br bbExitBlock(%6 : $FakeOptional<(Klass, Klass)>) |
| |
| bbExitBlock(%result : @owned $FakeOptional<(Klass, Klass)>): |
| return %result : $FakeOptional<(Klass, Klass)> |
| } |
| |
| // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_3 : $@convention(thin) (@guaranteed Klass) -> Klass { |
| // CHECK: bb0( |
| // CHECK-NEXT: unchecked_ownership_conversion |
| // CHECK-NEXT: return |
| // CHECK: } // end sil function 'unowned_to_guaranteed_rauw_3' |
| sil [ossa] @unowned_to_guaranteed_rauw_3 : $@convention(thin) (@guaranteed Klass) -> Klass { |
| bb0(%0 : @guaranteed $Klass): |
| %1 = unchecked_bitwise_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_bitwise_cast %1 : $SubKlass to $Klass |
| return %2 : $Klass |
| } |
| |
| //===--- |
| // Guaranteed Tests |
| // |
| |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_guaranteed : $@convention(thin) (@guaranteed Klass) -> () { |
| // CHECK-NOT: unchecked_ref_cast |
| // CHECK: } // end sil function 'guaranteed_to_guaranteed' |
| sil [ossa] @guaranteed_to_guaranteed : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : @guaranteed $Klass): |
| %1 = unchecked_ref_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_ref_cast %1 : $SubKlass to $Klass |
| %f = function_ref @use_klass_guaranteed : $@convention(thin) (@guaranteed Klass) -> () |
| apply %f(%2) : $@convention(thin) (@guaranteed Klass) -> () |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We should have no ARC traffic despite having a loop here. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_guaranteed_loop : $@convention(thin) (@guaranteed Klass) -> () { |
| // CHECK-NOT: unchecked_ref_cast |
| // CHECK-NOT: copy_value |
| // CHECK: } // end sil function 'guaranteed_to_guaranteed_loop' |
| sil [ossa] @guaranteed_to_guaranteed_loop : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : @guaranteed $Klass): |
| cond_br undef, bb1, bbSkipLoop |
| |
| bb1: |
| br bb1a |
| |
| bb1a: |
| br bb2 |
| |
| bb2: |
| %1 = unchecked_ref_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_ref_cast %1 : $SubKlass to $Klass |
| %f = function_ref @use_klass_guaranteed : $@convention(thin) (@guaranteed Klass) -> () |
| apply %f(%2) : $@convention(thin) (@guaranteed Klass) -> () |
| cond_br undef, bbBackEdge, bbExitingBlock |
| |
| bbBackEdge: |
| br bb1a |
| |
| bbSkipLoop: |
| br bbExit |
| |
| bbExitingBlock: |
| br bbExit |
| |
| bbExit: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_unowned : $@convention(thin) (@guaranteed Klass) -> () { |
| // CHECK-NOT: unchecked_ref_cast |
| // CHECK: } // end sil function 'guaranteed_to_unowned' |
| sil [ossa] @guaranteed_to_unowned : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : @guaranteed $Klass): |
| %1 = unchecked_ref_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_ref_cast %1 : $SubKlass to $Klass |
| %f = function_ref @use_klass_unowned : $@convention(thin) (Klass) -> () |
| apply %f(%2) : $@convention(thin) (Klass) -> () |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_unowned_loop : $@convention(thin) (@guaranteed Klass) -> () { |
| // CHECK-NOT: unchecked_ref_cast |
| // CHECK-NOT: copy_value |
| // CHECK: } // end sil function 'guaranteed_to_unowned_loop' |
| sil [ossa] @guaranteed_to_unowned_loop : $@convention(thin) (@guaranteed Klass) -> () { |
| bb0(%0 : @guaranteed $Klass): |
| cond_br undef, bb1, bbSkipLoop |
| |
| bb1: |
| br bb1a |
| |
| bb1a: |
| br bb2 |
| |
| bb2: |
| %1 = unchecked_ref_cast %0 : $Klass to $SubKlass |
| %2 = unchecked_ref_cast %1 : $SubKlass to $Klass |
| %f = function_ref @use_klass_unowned : $@convention(thin) (Klass) -> () |
| apply %f(%2) : $@convention(thin) (Klass) -> () |
| cond_br undef, bbBackEdge, bbExitingBlock |
| |
| bbBackEdge: |
| br bb1a |
| |
| bbSkipLoop: |
| br bbExit |
| |
| bbExitingBlock: |
| br bbExit |
| |
| bbExit: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Lifetime extend borrow to %3 and insert a copy. |
| // |
| // We should have no copies in this function when we are done. |
| // |
| // Just make sure we eliminated the FakeOptional.some. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_owned_consuming : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: destroy_value |
| // CHECK: } // end sil function 'guaranteed_to_owned_consuming' |
| sil [ossa] @guaranteed_to_owned_consuming : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @guaranteed $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| %2 = copy_value %1 : $FakeOptional<Klass> |
| br bb3(%2 : $FakeOptional<Klass>) |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bb3(%3 : $FakeOptional<Klass>) |
| |
| bb3(%4 : @owned $FakeOptional<Klass>): |
| destroy_value %4 : $FakeOptional<Klass> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_owned_consuming_loop : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: destroy_value |
| // CHECK: } // end sil function 'guaranteed_to_owned_consuming_loop' |
| sil [ossa] @guaranteed_to_owned_consuming_loop : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| cond_br undef, bbPreLoopHeader, bbEarlyExit |
| |
| bbPreLoopHeader: |
| br bbLoopHeader |
| |
| bbLoopHeader: |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @guaranteed $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| %2 = copy_value %1 : $FakeOptional<Klass> |
| br bb3(%2 : $FakeOptional<Klass>) |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bb3(%3 : $FakeOptional<Klass>) |
| |
| bb3(%4 : @owned $FakeOptional<Klass>): |
| destroy_value %4 : $FakeOptional<Klass> |
| cond_br undef, bbBackEdge, bbLoopExitingBlock |
| |
| bbBackEdge: |
| br bbLoopHeader |
| |
| bbLoopExitingBlock: |
| br bbExit |
| |
| bbEarlyExit: |
| br bbExit |
| |
| bbExit: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // For the normal check, make sure we performed the optimization. In this case |
| // it means eliminating the enum instruction in bb1. |
| // |
| // Then after cleaning up show that we do not have any copy_value or |
| // begin_borrows left. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_guaranteed_consuming : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: copy_value |
| // CHECK: begin_borrow |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK: } // end sil function 'guaranteed_to_guaranteed_consuming' |
| // |
| // We eliminate all begin borrows from this example with semantic-arc. |
| sil [ossa] @guaranteed_to_guaranteed_consuming : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @guaranteed $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| %2 = begin_borrow %1 : $FakeOptional<Klass> |
| br bb3(%2 : $FakeOptional<Klass>) |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bb3(%3 : $FakeOptional<Klass>) |
| |
| bb3(%4 : @guaranteed $FakeOptional<Klass>): |
| end_borrow %4 : $FakeOptional<Klass> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Make sure we performed the optimization. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_guaranteed_non_consuming_deadend : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: begin_borrow |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK: } // end sil function 'guaranteed_to_guaranteed_non_consuming_deadend' |
| sil [ossa] @guaranteed_to_guaranteed_non_consuming_deadend : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @guaranteed $Klass): |
| // We are going to replace %1 with %0. |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| %f2 = function_ref @use_fakeoptional_klass_guaranteed : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| apply %f2(%1) : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| unreachable |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| %f = function_ref @use_fakeoptional_klass_guaranteed : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| apply %f(%3) : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| unreachable |
| } |
| |
| // In this example we are replacing %1 with %0 inserting fix up copies. Make |
| // sure that we do not end up with any arc traffic! |
| // |
| // Make sure we actually eliminated the FakeOptional.some in bb1. We are |
| // replacing it with %0. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_to_guaranteed_nonconsuming_2 : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: copy_value |
| // CHECK-NOT: enum $FakeOptional<Klass>, #FakeOptional.some!enumelt |
| // CHECK-NOT: begin_borrow |
| // CHECK: } // end sil function 'guaranteed_to_guaranteed_nonconsuming_2' |
| sil [ossa] @guaranteed_to_guaranteed_nonconsuming_2 : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| switch_enum %0 : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @guaranteed $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| %f2 = function_ref @use_fakeoptional_klass_guaranteed : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| apply %f2(%1) : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| br bb3 |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| %f = function_ref @use_fakeoptional_klass_guaranteed : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| apply %f(%3) : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () |
| br bb3 |
| |
| bb3: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We do eliminate the copy_value here, but we do not eliminate the begin_borrow |
| // since we do not in semantic arc opts eliminate args kept alive just by |
| // borrows. |
| // |
| // CHECK-LABEL: sil [ossa] @guaranteed_copy_rauw_owned : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| // CHECK-NOT: copy_value |
| // CHECK: begin_borrow |
| // CHECK-NOT: copy_value |
| // CHECK: } // end sil function 'guaranteed_copy_rauw_owned' |
| sil [ossa] @guaranteed_copy_rauw_owned : $@convention(thin) (@guaranteed FakeOptional<Klass>) -> () { |
| bb0(%0 : @guaranteed $FakeOptional<Klass>): |
| %0c = copy_value %0 : $FakeOptional<Klass> |
| switch_enum %0c : $FakeOptional<Klass>, case #FakeOptional.some: bb1, case #FakeOptional.none: bb2 |
| |
| bb1(%0a : @owned $Klass): |
| %1 = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, %0a : $Klass |
| br bb3(%1 : $FakeOptional<Klass>) |
| |
| bb2: |
| %3 = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt |
| br bb3(%3 : $FakeOptional<Klass>) |
| |
| bb3(%4 : @owned $FakeOptional<Klass>): |
| destroy_value %4 : $FakeOptional<Klass> |
| %9999 = tuple() |
| return %9999 : $() |
| } |