| // RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine | %FileCheck %s |
| |
| sil_stage canonical |
| |
| import Builtin |
| import Swift |
| |
| class SomeClass { |
| func hash() -> Int |
| } |
| |
| enum Numerals { |
| case One |
| case Two |
| case Three |
| case Four |
| } |
| |
| sil @external_func: $@convention(thin) () -> () |
| |
| //CHECK-LABEL: eliminate_sw_enum_addr |
| //CHECK-NOT: switch_enum_addr |
| //CHECK: switch_enum |
| //CHECK: return |
| sil @eliminate_sw_enum_addr : $@convention(thin) () -> Int { |
| bb0: |
| %0 = alloc_stack $Optional<SomeClass>, var, name "x" // users: %2, %4, %5, %17, %19 |
| %1 = alloc_ref $SomeClass // user: %3 |
| %2 = init_enum_data_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt // user: %3 |
| store %1 to %2 : $*SomeClass // id: %3 |
| inject_enum_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt // id: %4 |
| %5 = load %0 : $*Optional<SomeClass> // users: %6, %8, %9, %14 |
| retain_value %5 : $Optional<SomeClass> |
| %7 = alloc_stack $Optional<SomeClass> // users: %9, %10, %11, %13 |
| retain_value %5 : $Optional<SomeClass> |
| store %5 to %7 : $*Optional<SomeClass> // id: %9 |
| switch_enum_addr %7 : $*Optional<SomeClass>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 // id: %10 |
| |
| bb1: // Preds: bb0 |
| %11 = unchecked_take_enum_data_addr %7 : $*Optional<SomeClass>, #Optional.some!enumelt // user: %12 |
| %12 = load %11 : $*SomeClass // users: %15, %16 |
| dealloc_stack %7 : $*Optional<SomeClass> // id: %13 |
| release_value %5 : $Optional<SomeClass> // id: %14 |
| %15 = class_method %12 : $SomeClass, #SomeClass.hash : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int // user: %16 |
| %16 = apply %15(%12) : $@convention(method) (@guaranteed SomeClass) -> Int // user: %20 |
| %17 = load %0 : $*Optional<SomeClass> // user: %18 |
| release_value %17 : $Optional<SomeClass> // id: %18 |
| dealloc_stack %0 : $*Optional<SomeClass> // id: %19 |
| return %16 : $Int // id: %20 |
| |
| bb2: // Preds: bb0 |
| unreachable // id: %23 |
| } |
| |
| // CHECK-LABEL: sil @eliminate_select_enum_addr |
| // CHECK-NOT: select_enum_addr |
| // CHECK: select_enum |
| // CHECK: return |
| |
| sil @eliminate_select_enum_addr : $@convention(thin) () -> Int { |
| bb0: |
| %0 = alloc_stack $Optional<SomeClass> |
| %1 = alloc_ref $SomeClass |
| %2 = init_enum_data_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt |
| store %1 to %2 : $*SomeClass |
| inject_enum_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt |
| %5 = load %0 : $*Optional<SomeClass> |
| retain_value %5 : $Optional<SomeClass> |
| %7 = alloc_stack $Optional<SomeClass> |
| retain_value %5 : $Optional<SomeClass> |
| store %5 to %7 : $*Optional<SomeClass> |
| %t = integer_literal $Builtin.Int1, -1 |
| %f = integer_literal $Builtin.Int1, 0 |
| %b = select_enum_addr %7 : $*Optional<SomeClass>, case #Optional.some!enumelt: %t, case #Optional.none!enumelt: %f : $Builtin.Int1 |
| cond_br %b, bb1, bb2 |
| |
| bb1: |
| %11 = unchecked_take_enum_data_addr %7 : $*Optional<SomeClass>, #Optional.some!enumelt |
| %12 = load %11 : $*SomeClass // users: %15, %16 |
| dealloc_stack %7 : $*Optional<SomeClass> |
| release_value %5 : $Optional<SomeClass> |
| %15 = class_method %12 : $SomeClass, #SomeClass.hash : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int |
| %16 = apply %15(%12) : $@convention(method) (@guaranteed SomeClass) -> Int |
| %17 = load %0 : $*Optional<SomeClass> |
| release_value %17 : $Optional<SomeClass> |
| dealloc_stack %0 : $*Optional<SomeClass> |
| return %16 : $Int |
| |
| bb2: |
| // Invoke something here and jump to bb1. This prevents a cond_br(select_enum) -> switch_enum conversion, |
| // since it would introduce a critical edge. |
| %20 = function_ref @external_func: $@convention(thin) () -> () |
| apply %20(): $@convention(thin) () -> () |
| br bb1 |
| } |
| |
| enum E { |
| case E0 |
| case E1 |
| case E2 |
| } |
| |
| // CHECK-LABEL: sil @canonicalize_select_enum |
| // CHECK: select_enum {{.*}} case #E.E2!enumelt: |
| // CHECK: return |
| |
| sil @canonicalize_select_enum : $@convention(thin) (E) -> Int32 { |
| bb0(%0 : $E): |
| %1 = integer_literal $Builtin.Int32, 0 |
| %2 = integer_literal $Builtin.Int32, 1 |
| %3 = integer_literal $Builtin.Int32, 2 |
| %4 = select_enum %0 : $E, case #E.E0!enumelt: %1, case #E.E1!enumelt: %2, default %3 : $Builtin.Int32 |
| %5 = struct $Int32 (%4 : $Builtin.Int32) |
| return %5 : $Int32 |
| } |
| |
| enum G<T> { |
| case E0 |
| case E1(T) |
| case E2 |
| } |
| |
| // CHECK-LABEL: sil @canonicalize_select_enum_addr |
| // CHECK: select_enum_addr {{.*}} case #G.E2!enumelt: |
| // CHECK: return |
| |
| sil @canonicalize_select_enum_addr : $@convention(thin) <T> (@in G<T>) -> Int32 { |
| bb0(%0 : $*G<T>): |
| %2 = integer_literal $Builtin.Int32, 0 |
| %3 = integer_literal $Builtin.Int32, 1 |
| %4 = integer_literal $Builtin.Int32, 2 |
| %5 = select_enum_addr %0 : $*G<T>, case #G.E0!enumelt: %2, case #G.E1!enumelt: %3, default %4 : $Builtin.Int32 |
| %6 = struct $Int32 (%5 : $Builtin.Int32) |
| return %6 : $Int32 |
| } |
| |
| // CHECK-LABEL: sil @canonicalize_init_enum_data_addr |
| // CHECK-NOT: init_enum_data_addr |
| // CHECK-NOT: inject_enum_addr |
| // CHECK: enum $Optional<Int32>, #Optional.some!enumelt |
| // CHECK-NOT: inject_enum_addr |
| // CHECK: return |
| sil @canonicalize_init_enum_data_addr : $@convention(thin) (@inout Int32, Builtin.Int32, Optional<Int32>) -> Int32 { |
| bb0(%0 : $*Int32, %1 : $Builtin.Int32, %2 : $Optional<Int32>): |
| %s1 = alloc_stack $Optional<Int32> |
| store %2 to %s1 : $*Optional<Int32> |
| %e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| %v = load %0 : $*Int32 |
| store %v to %e1 : $*Int32 |
| %i1 = integer_literal $Builtin.Int32, 1 |
| %i0 = integer_literal $Builtin.Int1, 0 |
| %a = builtin "sadd_with_overflow_Int32"(%1 : $Builtin.Int32, %i1 : $Builtin.Int32, %i0 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) |
| %w = tuple_extract %a : $(Builtin.Int32, Builtin.Int1), 0 |
| %i = struct $Int32 (%w : $Builtin.Int32) |
| store %i to %0 : $*Int32 |
| inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| dealloc_stack %s1 : $*Optional<Int32> |
| return %i : $Int32 |
| } |
| |
| // CHECK-LABEL: sil @canonicalize_init_enum_data_addr_diff_basic_blocks |
| // CHECK-NOT: init_enum_data_addr |
| // CHECK-NOT: inject_enum_addr |
| // CHECK: enum $Optional<Int32>, #Optional.some!enumelt |
| // CHECK-NOT: inject_enum_addr |
| // CHECK: return |
| sil @canonicalize_init_enum_data_addr_diff_basic_blocks : $@convention(thin) (@inout Int32, Builtin.Int32, Optional<Int32>) -> Int32 { |
| bb0(%0 : $*Int32, %1 : $Builtin.Int32, %2 : $Optional<Int32>): |
| %s1 = alloc_stack $Optional<Int32> |
| store %2 to %s1 : $*Optional<Int32> |
| %e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| %v = load %0 : $*Int32 |
| store %v to %e1 : $*Int32 |
| %i1 = integer_literal $Builtin.Int32, 1 |
| %i0 = integer_literal $Builtin.Int1, 0 |
| %a = builtin "sadd_with_overflow_Int32"(%1 : $Builtin.Int32, %i1 : $Builtin.Int32, %i0 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) |
| %w = tuple_extract %a : $(Builtin.Int32, Builtin.Int1), 0 |
| %i = struct $Int32 (%w : $Builtin.Int32) |
| br bb1 |
| |
| bb1: // Preds: bb0 |
| store %i to %0 : $*Int32 |
| inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| dealloc_stack %s1 : $*Optional<Int32> |
| return %i : $Int32 |
| } |
| |
| // CHECK-LABEL: sil @fail_to_canonicalize_init_enum_data_addr_reach_entry |
| // CHECK: init_enum_data_addr |
| // CHECK: inject_enum_addr |
| // CHECK-NOT: enum $Optional<Int32>, #Optional.some!enumelt |
| // CHECK: return |
| sil @fail_to_canonicalize_init_enum_data_addr_reach_entry : $@convention(thin) (@inout Int32, Builtin.Int32, Builtin.Int1) -> Int32 { |
| bb0(%0 : $*Int32, %1 : $Builtin.Int32, %2 : $Builtin.Int1): |
| %s1 = alloc_stack $Optional<Int32> |
| %i2 = load %0 : $*Int32 |
| %en = enum $Optional<Int32>, #Optional.none!enumelt |
| store %en to %s1 : $*Optional<Int32> |
| cond_br %2, bb1, bb2 |
| |
| bb1: |
| %e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| %v = load %0 : $*Int32 |
| store %v to %e1 : $*Int32 |
| %i1 = integer_literal $Builtin.Int32, 1 |
| %i0 = integer_literal $Builtin.Int1, 0 |
| %a = builtin "sadd_with_overflow_Int32"(%1 : $Builtin.Int32, %i1 : $Builtin.Int32, %i0 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) |
| %w = tuple_extract %a : $(Builtin.Int32, Builtin.Int1), 0 |
| %i = struct $Int32 (%w : $Builtin.Int32) |
| store %i to %0 : $*Int32 |
| br bb2 |
| |
| bb2: // Preds: bb0 bb1 |
| inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| dealloc_stack %s1 : $*Optional<Int32> |
| return %i2 : $Int32 |
| } |
| |
| // CHECK-LABEL: sil @fail_to_canonicalize_init_enum_data_addr |
| // CHECK: init_enum_data_addr |
| // CHECK: inject_enum_addr |
| // CHECK-NOT: enum $Optional<Int32>, #Optional.some!enumelt |
| // CHECK: return |
| sil @fail_to_canonicalize_init_enum_data_addr : $@convention(thin) (@inout Int32, Builtin.Int32) -> Int32 { |
| bb0(%0 : $*Int32, %1 : $Builtin.Int32): |
| %s1 = alloc_stack $Optional<Int32> |
| %e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| %v = load %0 : $*Int32 |
| store %v to %e1 : $*Int32 |
| %i1 = integer_literal $Builtin.Int32, 1 |
| %i0 = integer_literal $Builtin.Int1, 0 |
| %a = builtin "sadd_with_overflow_Int32"(%1 : $Builtin.Int32, %i1 : $Builtin.Int32, %i0 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) |
| %w = tuple_extract %a : $(Builtin.Int32, Builtin.Int1), 0 |
| %i = struct $Int32 (%w : $Builtin.Int32) |
| br bb1 |
| |
| bb1: |
| %o = enum $Optional<Int32>, #Optional.none!enumelt |
| store %o to %s1 : $*Optional<Int32> |
| br bb2 |
| |
| bb2: |
| store %i to %0 : $*Int32 |
| inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt |
| dealloc_stack %s1 : $*Optional<Int32> |
| return %i : $Int32 |
| } |
| |
| // Check the cond_br(select_enum) -> switch_enum conversion. |
| // |
| // CHECK-LABEL: sil @convert_select_enum_cond_br_to_switch_enum |
| // CHECK-NOT: select_enum |
| // CHECK: switch_enum |
| // CHECK: return |
| |
| sil @convert_select_enum_cond_br_to_switch_enum : $@convention(thin) (@owned Optional<SomeClass>) -> Int { |
| bb0(%0 : $Optional<SomeClass>): |
| %1 = integer_literal $Builtin.Int1, 0 |
| %2 = integer_literal $Builtin.Int1, -1 |
| %3 = select_enum %0 : $Optional<SomeClass>, case #Optional.none!enumelt: %2, case #Optional.some!enumelt: %1 : $Builtin.Int1 |
| cond_br %3, bb2, bb1 |
| |
| bb1: |
| %5 = unchecked_enum_data %0 : $Optional<SomeClass>, #Optional.some!enumelt |
| %6 = class_method %5 : $SomeClass, #SomeClass.hash : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int |
| %7 = apply %6(%5) : $@convention(method) (@guaranteed SomeClass) -> Int |
| fix_lifetime %5 : $SomeClass |
| strong_release %5 : $SomeClass |
| return %7 : $Int |
| |
| bb2: |
| cond_fail %2 : $Builtin.Int1 |
| unreachable |
| } |
| |
| // Check that cond_br(select_enum) is converted into switch_enum. |
| // CHECK-LABEL: sil @convert_select_enum_cond_br_to_switch_enum2 |
| // CHECK: bb0 |
| // CHECK-NOT: select_enum |
| // CHECK-NOT: return |
| // CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2 |
| // CHECK: return |
| // CHECK: } |
| sil @convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 { |
| bb0(%0 : $Numerals): |
| %2 = integer_literal $Builtin.Int1, 0 |
| %3 = integer_literal $Builtin.Int1, -1 |
| // All cases but one are the same. So, they can be made a default for the switch_enum. |
| %4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, case #Numerals.Four!enumelt: %3 : $Builtin.Int1 |
| |
| cond_br %4, bb2, bb3 |
| |
| bb1: |
| %7 = integer_literal $Builtin.Int64, 10 |
| return %7 : $Builtin.Int64 |
| |
| bb2: |
| %10 = function_ref @external_func: $@convention(thin) () -> () |
| apply %10(): $@convention(thin) () -> () |
| br bb1 |
| |
| bb3: |
| br bb1 |
| } |
| |
| // Check that cond_br(select_enum) is converted into switch_enum. |
| // This test checks that select_enum instructions with default cases are handled correctly. |
| // CHECK-LABEL: sil @convert_select_enum_cond_br_to_switch_enum3 |
| // CHECK: bb0 |
| // CHECK-NOT: select_enum |
| // CHECK-NOT: return |
| // CHECK: switch_enum %0 : $Numerals, case #Numerals.Two!enumelt: bb3, default bb2 |
| // CHECK: return |
| // CHECK: } |
| sil @convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals) -> Builtin.Int64 { |
| bb0(%0 : $Numerals): |
| %2 = integer_literal $Builtin.Int1, 0 |
| %3 = integer_literal $Builtin.Int1, -1 |
| // There is only one case, whose result is different from default and other cases |
| // Thus all other cases can be folded into a default cases of a switch_enum. |
| %4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, default %3 : $Builtin.Int1 |
| |
| cond_br %4, bb2, bb3 |
| |
| bb1: |
| %7 = integer_literal $Builtin.Int64, 10 |
| return %7 : $Builtin.Int64 |
| |
| bb2: |
| %10 = function_ref @external_func: $@convention(thin) () -> () |
| apply %10(): $@convention(thin) () -> () |
| br bb1 |
| |
| bb3: |
| br bb1 |
| } |
| |
| |
| // Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which |
| // is not originating from cond_br/br. And this is forbidden in a canonical SIL form. |
| // |
| // CHECK-LABEL: sil @dont_convert_select_enum_cond_br_to_switch_enum |
| // CHECK: select_enum |
| // CHECK-NOT: switch_enum |
| // CHECK: return |
| sil @dont_convert_select_enum_cond_br_to_switch_enum : $@convention(thin) (@owned Optional<SomeClass>) -> Int { |
| bb0(%0 : $Optional<SomeClass>): |
| %2 = integer_literal $Builtin.Int1, 0 |
| %3 = integer_literal $Builtin.Int1, -1 |
| %4 = select_enum %0 : $Optional<SomeClass>, case #Optional.none!enumelt: %3, case #Optional.some!enumelt: %2 : $Builtin.Int1 |
| cond_br %4, bb2, bb1 |
| |
| bb1: |
| %5 = unchecked_enum_data %0 : $Optional<SomeClass>, #Optional.some!enumelt |
| %6 = class_method %5 : $SomeClass, #SomeClass.hash : (SomeClass) -> () -> Int, $@convention(method) (@guaranteed SomeClass) -> Int |
| %7 = apply %6(%5) : $@convention(method) (@guaranteed SomeClass) -> Int |
| fix_lifetime %5 : $SomeClass |
| strong_release %5 : $SomeClass |
| return %7 : $Int |
| |
| bb2: |
| %10 = function_ref @external_func: $@convention(thin) () -> () |
| apply %10(): $@convention(thin) () -> () |
| br bb1 |
| } |
| |
| // Check that cond_br(select_enum) is not converted into switch_enum as it would create a critical edge, which |
| // is not originating from cond_br/br. And this is forbidden in a canonical SIL form. |
| // |
| // CHECK-LABEL: sil @dont_convert_select_enum_cond_br_to_switch_enum2 |
| // CHECK: select_enum |
| // CHECK-NOT: switch_enum |
| // CHECK: return |
| sil @dont_convert_select_enum_cond_br_to_switch_enum2 : $@convention(thin) (Numerals) -> Builtin.Int64 { |
| bb0(%0 : $Numerals): |
| %2 = integer_literal $Builtin.Int1, 0 |
| %3 = integer_literal $Builtin.Int1, -1 |
| // There are two cases for each possible outcome. |
| // This means that we would always get a critical edge, if we convert it into a switch_enum. |
| %4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, case #Numerals.Four!enumelt: %2 : $Builtin.Int1 |
| |
| cond_br %4, bb2, bb3 |
| |
| bb1: |
| %7 = integer_literal $Builtin.Int64, 10 |
| return %7 : $Builtin.Int64 |
| |
| bb2: |
| %10 = function_ref @external_func: $@convention(thin) () -> () |
| apply %10(): $@convention(thin) () -> () |
| br bb1 |
| |
| bb3: |
| br bb1 |
| } |
| |
| // Check that cond_br(select_enum) is not converted into switch_enum, |
| // because the result of the default case is not an integer literal. |
| // CHECK-LABEL: sil @dont_convert_select_enum_cond_br_to_switch_enum3 |
| // CHECK: select_enum |
| // CHECK-NOT: switch_enum |
| // CHECK: return |
| sil @dont_convert_select_enum_cond_br_to_switch_enum3 : $@convention(thin) (Numerals, Builtin.Int1) -> Builtin.Int64 { |
| bb0(%0 : $Numerals, %1 : $Builtin.Int1): |
| %2 = integer_literal $Builtin.Int1, 0 |
| %3 = integer_literal $Builtin.Int1, -1 |
| // All cases but one are the same. So, they can be made a default for the switch_enum. |
| %4 = select_enum %0 : $Numerals, case #Numerals.One!enumelt: %3, case #Numerals.Two!enumelt: %2, case #Numerals.Three!enumelt: %3, default %1 : $Builtin.Int1 |
| |
| cond_br %4, bb2, bb3 |
| |
| bb1: |
| %7 = integer_literal $Builtin.Int64, 10 |
| return %7 : $Builtin.Int64 |
| |
| bb2: |
| %10 = function_ref @external_func: $@convention(thin) () -> () |
| apply %10(): $@convention(thin) () -> () |
| br bb1 |
| |
| bb3: |
| br bb1 |
| } |
| |
| public class C {} |
| public struct S {} |
| public struct T { |
| @_hasStorage let c: C |
| @_hasStorage let s: S |
| } |
| public enum X { |
| case none |
| case some(T) |
| } |
| public enum Y { |
| case none |
| case some(T) |
| } |
| |
| // Verify that we do not optimize |
| // (unchecked_enum_data (unchecked_bitwise_cast V : $X to $Y), #Case) |
| // where Case holds of a payload of type P into: |
| // (unchecked_ref_cast V : $X to $P) |
| // even for single-payload enums, since we cannot know the layouts of |
| // the types involved, and we'll generate a trap at IRGen-time if the |
| // bitcasted types are not the same size. |
| |
| // CHECK-LABEL: sil @keep_unchecked_enum_data |
| sil @keep_unchecked_enum_data : $@convention(thin) (@owned X, @owned T) -> @owned T { |
| // CHECK: bb0 |
| bb0(%0 : $X, %1 : $T): |
| // CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $X to $Y |
| %4 = unchecked_bitwise_cast %0 : $X to $Y |
| switch_enum %4 : $Y, case #Y.none!enumelt: bb1, case #Y.some!enumelt: bb2 |
| |
| // CHECK: bb1 |
| bb1: |
| %7 = struct_extract %1 : $T, #T.c |
| strong_retain %7 : $C |
| br bb3(%7 : $C) |
| |
| // CHECK: bb2 |
| bb2(%10 : $T): |
| // CHECK: unchecked_enum_data [[CAST]] : $Y, #Y.some!enumelt |
| %11 = unchecked_enum_data %4 : $Y, #Y.some!enumelt |
| %12 = struct_extract %11 : $T, #T.c |
| br bb3(%12 : $C) |
| |
| // CHECK: bb3 |
| bb3(%14 : $C): |
| %15 = struct $S () |
| %16 = struct $T (%14 : $C, %15 : $S) |
| %17 = struct_extract %1 : $T, #T.c |
| strong_release %17 : $C |
| // CHECK: return |
| return %16 : $T |
| } |
| |
| // CHECK-LABEL: sil @test_inject_tuple |
| // CHECK: [[A:%[0-9]+]] = alloc_stack $Optional<(Int, Int)> |
| // CHECK: [[T:%[0-9]+]] = tuple (%0 : $Int, %1 : $Int) |
| // CHECK: [[E:%[0-9]+]] = enum $Optional<(Int, Int)>, #Optional.some!enumelt, [[T]] : $(Int, Int) |
| // CHECK: store [[E]] to [[A]] |
| // CHECK: [[L:%[0-9]+]] = load [[A]] |
| // CHECK: return [[L]] |
| // CHECK: } // end sil function 'test_inject_tuple' |
| sil @test_inject_tuple : $@convention(thin) (Int, Int) -> Optional<(Int, Int)> { |
| bb0(%0 : $Int, %1 : $Int): |
| %17 = alloc_stack $Optional<(Int, Int)> |
| %45 = init_enum_data_addr %17 : $*Optional<(Int, Int)>, #Optional.some!enumelt |
| %46 = tuple_element_addr %45 : $*(Int, Int), 0 |
| %47 = tuple_element_addr %45 : $*(Int, Int), 1 |
| store %0 to %46 : $*Int |
| store %1 to %47 : $*Int |
| inject_enum_addr %17 : $*Optional<(Int, Int)>, #Optional.some!enumelt |
| %r = load %17 : $*Optional<(Int, Int)> |
| dealloc_stack %17 : $*Optional<(Int, Int)> |
| return %r : $Optional<(Int, Int)> |
| } |
| |
| enum MP { |
| case A(S) |
| case B(S) |
| } |
| |
| sil @take_s : $@convention(thin) (@in S) -> () |
| sil @take_t : $@convention(thin) (@in T) -> () |
| sil @use_mp : $@convention(thin) (@in_guaranteed MP) -> () |
| |
| // CHECK-LABEL: sil @expand_alloc_stack_of_enum1 |
| // CHECK: [[A:%[0-9]+]] = alloc_stack $S |
| // CHECK: bb1: |
| // CHECK-NEXT: store %0 to [[A]] |
| // CHECK: bb2: |
| // CHECK-NEXT: store %0 to [[A]] |
| // CHECK: bb3: |
| // CHECK: apply {{%[0-9]+}}([[A]]) |
| // CHECK: } // end sil function 'expand_alloc_stack_of_enum1' |
| sil @expand_alloc_stack_of_enum1 : $@convention(method) (S) -> () { |
| bb0(%0 : $S): |
| %1 = alloc_stack $MP |
| cond_br undef, bb1, bb2 |
| bb1: |
| %2 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %2 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| br bb3 |
| bb2: |
| %3 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %3 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| br bb3 |
| bb3: |
| %7 = unchecked_take_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| %8 = function_ref @take_s : $@convention(thin) (@in S) -> () |
| %9 = apply %8(%7) : $@convention(thin) (@in S) -> () |
| dealloc_stack %1 : $*MP |
| %11 = tuple () |
| return %11 : $() |
| } |
| |
| // CHECK-LABEL: sil @expand_alloc_stack_of_enum_without_take |
| // CHECK: [[A:%[0-9]+]] = alloc_stack $S |
| // CHECK: bb1: |
| // CHECK-NEXT: store %0 to [[A]] |
| // CHECK: bb2: |
| // CHECK-NEXT: store %0 to [[A]] |
| // CHECK: bb3: |
| // CHECK: destroy_addr [[A]] |
| // CHECK: } // end sil function 'expand_alloc_stack_of_enum_without_take' |
| sil @expand_alloc_stack_of_enum_without_take : $@convention(method) (S) -> () { |
| bb0(%0 : $S): |
| %1 = alloc_stack $MP |
| cond_br undef, bb1, bb2 |
| bb1: |
| %2 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %2 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| br bb3 |
| bb2: |
| %3 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %3 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| br bb3 |
| bb3: |
| destroy_addr %1 : $*MP |
| dealloc_stack %1 : $*MP |
| %11 = tuple () |
| return %11 : $() |
| } |
| |
| // CHECK-LABEL: sil @expand_alloc_stack_of_enum_multiple_cases |
| // CHECK: [[A:%[0-9]+]] = alloc_stack $T |
| // CHECK: bb1: |
| // CHECK-NEXT: store %0 to [[A]] |
| // CHECK: apply {{%[0-9]+}}([[A]]) |
| // CHECK: bb2: |
| // CHECK-NEXT: br bb3 |
| // CHECK: bb3: |
| // CHECK: } // end sil function 'expand_alloc_stack_of_enum_multiple_cases' |
| sil @expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (T) -> () { |
| bb0(%0 : $T): |
| %1 = alloc_stack $X |
| cond_br undef, bb1, bb2 |
| bb1: |
| %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt |
| store %0 to %2 : $*T |
| inject_enum_addr %1 : $*X, #X.some!enumelt |
| %7 = unchecked_take_enum_data_addr %1 : $*X, #X.some!enumelt |
| %8 = function_ref @take_t : $@convention(thin) (@in T) -> () |
| %9 = apply %8(%7) : $@convention(thin) (@in T) -> () |
| br bb3 |
| bb2: |
| inject_enum_addr %1 : $*X, #X.none!enumelt |
| destroy_addr %1 : $*X |
| br bb3 |
| bb3: |
| dealloc_stack %1 : $*X |
| %11 = tuple () |
| return %11 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases |
| // CHECK: alloc_stack $MP |
| // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases' |
| sil @dont_expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (S) -> () { |
| bb0(%0 : $S): |
| %1 = alloc_stack $MP |
| cond_br undef, bb1, bb2 |
| bb1: |
| %2 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %2 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| br bb3 |
| bb2: |
| %3 = init_enum_data_addr %1 : $*MP, #MP.B!enumelt |
| store %0 to %3 : $*S |
| inject_enum_addr %1 : $*MP, #MP.B!enumelt |
| br bb3 |
| bb3: |
| destroy_addr %1 : $*MP |
| dealloc_stack %1 : $*MP |
| %11 = tuple () |
| return %11 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases2 |
| // CHECK: alloc_stack $X |
| // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases2' |
| sil @dont_expand_alloc_stack_of_enum_multiple_cases2 : $@convention(method) (T) -> () { |
| bb0(%0 : $T): |
| %1 = alloc_stack $X |
| cond_br undef, bb1, bb2 |
| bb1: |
| %2 = init_enum_data_addr %1 : $*X, #X.some!enumelt |
| store %0 to %2 : $*T |
| inject_enum_addr %1 : $*X, #X.some!enumelt |
| br bb3 |
| bb2: |
| inject_enum_addr %1 : $*X, #X.none!enumelt |
| br bb3 |
| bb3: |
| destroy_addr %1 : $*X |
| dealloc_stack %1 : $*X |
| %11 = tuple () |
| return %11 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_unknown_use |
| // CHECK: alloc_stack $MP |
| // CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_unknown_use' |
| sil @dont_expand_alloc_stack_of_enum_unknown_use : $@convention(method) (S) -> () { |
| bb0(%0 : $S): |
| %1 = alloc_stack $MP |
| %2 = init_enum_data_addr %1 : $*MP, #MP.A!enumelt |
| store %0 to %2 : $*S |
| inject_enum_addr %1 : $*MP, #MP.A!enumelt |
| %8 = function_ref @use_mp : $@convention(thin) (@in_guaranteed MP) -> () |
| %9 = apply %8(%1) : $@convention(thin) (@in_guaranteed MP) -> () |
| destroy_addr %1 : $*MP |
| dealloc_stack %1 : $*MP |
| %11 = tuple () |
| return %11 : $() |
| } |
| |