Files
swift-mirror/test/SILOptimizer/sil_combine_enums_ossa.sil
2024-12-17 09:55:39 +01:00

718 lines
26 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -sil-combine | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
class SomeClass {
func hash() -> Int
}
sil @get_some_class : $@convention(thin) () -> @owned SomeClass
enum Numerals {
case One
case Two
case Three
case Four
}
enum E2 {
case int(Int)
case str(String)
}
sil [ossa] @external_func: $@convention(thin) () -> ()
sil @useit : $@convention(thin) (@owned E2) -> ()
// CHECK-LABEL: sil [ossa] @eliminate_sw_enum_addr : $@convention(thin) () -> Int {
// CHECK-NOT: switch_enum_addr
// CHECK: switch_enum {{%.*}} :
// CHECK: } // end sil function 'eliminate_sw_enum_addr'
sil [ossa] @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 [init] %2 : $*SomeClass // id: %3
inject_enum_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt // id: %4
%5 = load [copy] %0 : $*Optional<SomeClass> // users: %6, %8, %9, %14
%7 = alloc_stack $Optional<SomeClass> // users: %9, %10, %11, %13
%5a = copy_value %5 : $Optional<SomeClass>
store %5a to [init] %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 [take] %11 : $*SomeClass // users: %15, %16
dealloc_stack %7 : $*Optional<SomeClass> // id: %13
destroy_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 [take] %0 : $*Optional<SomeClass> // user: %18
destroy_value %17 : $Optional<SomeClass> // id: %18
destroy_value %12 : $SomeClass
dealloc_stack %0 : $*Optional<SomeClass> // id: %19
return %16 : $Int // id: %20
bb2: // Preds: bb0
unreachable // id: %23
}
// CHECK-LABEL: sil [ossa] @eliminate_select_enum_addr :
// CHECK-NOT: select_enum_addr
// CHECK-NOT: select_enum
// CHECK: switch_enum
// CHECK-NOT: select_enum_addr
// CHECK-NOT: select_enum
// CHECK: } // end sil function 'eliminate_select_enum_addr'
sil [ossa] @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 [init] %2 : $*SomeClass
inject_enum_addr %0 : $*Optional<SomeClass>, #Optional.some!enumelt
%5 = load [copy] %0 : $*Optional<SomeClass>
%7 = alloc_stack $Optional<SomeClass>
%5a = copy_value %5 : $Optional<SomeClass>
store %5a to [init] %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, bb1a, bb2
bb1a:
br bb1
bb1:
%11 = unchecked_take_enum_data_addr %7 : $*Optional<SomeClass>, #Optional.some!enumelt
%12 = load [take] %11 : $*SomeClass // users: %15, %16
dealloc_stack %7 : $*Optional<SomeClass>
destroy_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 [take] %0 : $*Optional<SomeClass>
destroy_value %17 : $Optional<SomeClass>
destroy_value %12 : $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 [ossa] @canonicalize_select_enum
// CHECK: select_enum {{.*}} case #E.E2!enumelt:
// CHECK: return
// CHECK: } // end sil function 'canonicalize_select_enum'
sil [ossa] @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 [ossa] @canonicalize_select_enum_addr :
// CHECK: select_enum_addr {{.*}} case #G.E2!enumelt:
// CHECK: } // end sil function 'canonicalize_select_enum_addr'
sil [ossa] @canonicalize_select_enum_addr : $@convention(thin) <T> (@in_guaranteed 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 [ossa] @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 [ossa] @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 [trivial] %s1 : $*Optional<Int32>
%e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt
%v = load [trivial] %0 : $*Int32
store %v to [trivial] %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 [trivial] %0 : $*Int32
inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt
dealloc_stack %s1 : $*Optional<Int32>
return %i : $Int32
}
// CHECK-LABEL: sil [ossa] @canonicalize_init_enum_data_addr
// CHECK-NOT: init_enum_data_addr
// CHECK-NOT: inject_enum_addr
// CHECK: enum $Optional<SomeClass>, #Optional.some!enumelt
// CHECK-NOT: inject_enum_addr
// CHECK: return
sil [ossa] @canonicalize_init_enum_data_addr_non_trivial : $@convention(thin) (@inout SomeClass, @owned Optional<SomeClass>) -> @owned Optional<SomeClass> {
bb0(%0 : $*SomeClass, %2 : @owned $Optional<SomeClass>):
%s1 = alloc_stack $Optional<SomeClass>
%e1 = init_enum_data_addr %s1 : $*Optional<SomeClass>, #Optional.some!enumelt
%v = load [take] %0 : $*SomeClass
store %v to [init] %e1 : $*SomeClass
%f = function_ref @get_some_class : $@convention(thin) () -> @owned SomeClass
%newVal = apply %f() : $@convention(thin) () -> @owned SomeClass
store %newVal to [init] %0 : $*SomeClass
inject_enum_addr %s1 : $*Optional<SomeClass>, #Optional.some!enumelt
%i2 = load [take] %s1 : $*Optional<SomeClass>
dealloc_stack %s1 : $*Optional<SomeClass>
destroy_value %2 : $Optional<SomeClass>
return %i2 : $Optional<SomeClass>
}
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 [trivial] %s1 : $*Optional<Int32>
%e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt
%v = load [trivial] %0 : $*Int32
store %v to [trivial] %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 [trivial] %0 : $*Int32
inject_enum_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt
dealloc_stack %s1 : $*Optional<Int32>
return %i : $Int32
}
// CHECK-LABEL: sil [ossa] @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 [ossa] @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 [trivial] %0 : $*Int32
%en = enum $Optional<Int32>, #Optional.none!enumelt
store %en to [trivial] %s1 : $*Optional<Int32>
cond_br %2, bb1, bb2a
bb1:
%e1 = init_enum_data_addr %s1 : $*Optional<Int32>, #Optional.some!enumelt
%v = load [trivial] %0 : $*Int32
store %v to [trivial] %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 [trivial] %0 : $*Int32
br bb2
bb2a:
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 [ossa] @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 [ossa] @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 [trivial] %0 : $*Int32
store %v to [trivial] %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 [trivial] %s1 : $*Optional<Int32>
br bb2
bb2:
store %i to [trivial] %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 [ossa] @convert_select_enum_cond_br_to_switch_enum :
// CHECK-NOT: select_enum
// CHECK: switch_enum
// CHECK: return
// CHECK: } // end sil function 'convert_select_enum_cond_br_to_switch_enum'
sil [ossa] @convert_select_enum_cond_br_to_switch_enum : $@convention(thin) (@owned Optional<SomeClass>) -> Int {
bb0(%0 : @owned $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
destroy_value %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 [ossa] @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: } // end sil function 'convert_select_enum_cond_br_to_switch_enum2'
sil [ossa] @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 [ossa] @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: } // end sil function 'convert_select_enum_cond_br_to_switch_enum3'
sil [ossa] @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,
// because the result of the default case is not an integer literal.
// CHECK-LABEL: sil [ossa] @dont_convert_select_enum_cond_br_to_switch_enum3 :
// CHECK: select_enum
// CHECK-NOT: switch_enum
// CHECK: } // end sil function 'dont_convert_select_enum_cond_br_to_switch_enum3'
sil [ossa] @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 [ossa] @keep_unchecked_enum_data :
// CHECK: bb0
// CHECK: [[CAST:%.*]] = unchecked_bitwise_cast %0 : $X to $Y
// CHECK: bb1
// CHECK: bb2
// CHECK: bb3
// CHECK: return
// CHECK: } // end sil function 'keep_unchecked_enum_data'
sil [ossa] @keep_unchecked_enum_data : $@convention(thin) (@owned X, @owned T) -> @owned T {
bb0(%0 : @owned $X, %1 : @owned $T):
%4 = unchecked_bitwise_cast %0 : $X to $Y
%5 = copy_value %4 : $Y
switch_enum %5 : $Y, case #Y.none!enumelt: bb1, case #Y.some!enumelt: bb2
bb1:
(%7, %8) = destructure_struct %1 : $T
br bb3(%7 : $C)
bb2(%10 : @owned $T):
(%12a, %12b) = destructure_struct %10 : $T
destroy_value %1 : $T
br bb3(%12a : $C)
bb3(%14 : @owned $C):
%15 = struct $S ()
%16 = struct $T (%14 : $C, %15 : $S)
destroy_value %0 : $X
return %16 : $T
}
// CHECK-LABEL: sil [ossa] @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 [trivial] [[A]]
// CHECK: [[L:%[0-9]+]] = load [trivial] [[A]]
// CHECK: return [[L]]
// CHECK: } // end sil function 'test_inject_tuple'
sil [ossa] @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 [trivial] %46 : $*Int
store %1 to [trivial] %47 : $*Int
inject_enum_addr %17 : $*Optional<(Int, Int)>, #Optional.some!enumelt
%r = load [trivial] %17 : $*Optional<(Int, Int)>
dealloc_stack %17 : $*Optional<(Int, Int)>
return %r : $Optional<(Int, Int)>
}
enum MP {
case A(S)
case B(S)
}
sil [ossa] @take_s : $@convention(thin) (@in S) -> ()
sil [ossa] @take_t : $@convention(thin) (@in T) -> ()
sil [ossa] @use_mp : $@convention(thin) (@in_guaranteed MP) -> ()
// CHECK-LABEL: sil [ossa] @expand_alloc_stack_of_enum1
// CHECK: [[A:%[0-9]+]] = alloc_stack $S
// CHECK: bb1:
// CHECK-NEXT: store %0 to [trivial] [[A]]
// CHECK: bb2:
// CHECK-NEXT: store %0 to [trivial] [[A]]
// CHECK: bb3:
// CHECK: apply {{%[0-9]+}}([[A]])
// CHECK: } // end sil function 'expand_alloc_stack_of_enum1'
sil [ossa] @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 [trivial] %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 [trivial] %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 [ossa] @expand_alloc_stack_of_enum_without_take
// CHECK: [[A:%[0-9]+]] = alloc_stack $S
// CHECK: bb1:
// CHECK-NEXT: store %0 to [trivial] [[A]]
// CHECK: bb2:
// CHECK-NEXT: store %0 to [trivial] [[A]]
// CHECK: bb3:
// CHECK: destroy_addr [[A]]
// CHECK: } // end sil function 'expand_alloc_stack_of_enum_without_take'
sil [ossa] @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 [trivial] %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 [trivial] %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 [ossa] @expand_alloc_stack_of_enum_multiple_cases
// CHECK: [[A:%[0-9]+]] = alloc_stack $T
// CHECK: bb1:
// CHECK-NEXT: [[COPY_ARG:%.*]] = copy_value %0
// CHECK-NEXT: store [[COPY_ARG]] to [init] [[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 [ossa] @expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (@guaranteed T) -> () {
bb0(%0 : @guaranteed $T):
%1 = alloc_stack $X
cond_br undef, bb1, bb2
bb1:
%2 = init_enum_data_addr %1 : $*X, #X.some!enumelt
%0a = copy_value %0 : $T
store %0a to [init] %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 [ossa] @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 [ossa] @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 [trivial] %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 [trivial] %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 [ossa] @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 [ossa] @dont_expand_alloc_stack_of_enum_multiple_cases2 : $@convention(method) (@guaranteed T) -> () {
bb0(%0 : @guaranteed $T):
%1 = alloc_stack $X
cond_br undef, bb1, bb2
bb1:
%2 = init_enum_data_addr %1 : $*X, #X.some!enumelt
%0a = copy_value %0 : $T
store %0a to [init] %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 [ossa] @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 [ossa] @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 [trivial] %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 : $()
}
class Klass {}
// CHECK-LABEL: sil [ossa] @test_nontrivial_enum_inject_enum_addr_opt
// CHECK: [[VAL:%.*]] = enum $Optional<Klass>, #Optional.none!enumelt
// CHECK: store [[VAL]] to [trivial] %0 : $*Optional<Klass>
// CHECK: } // end sil function 'test_nontrivial_enum_inject_enum_addr_opt'
sil [ossa] @test_nontrivial_enum_inject_enum_addr_opt : $@convention(method) (@inout Optional<Klass>) -> @owned Optional<Klass> {
bb0(%0 : $*Optional<Klass>):
%1 = load [take] %0 : $*Optional<Klass>
inject_enum_addr %0 : $*Optional<Klass>, #Optional.none!enumelt
return %1 : $Optional<Klass>
}
struct NC : ~Copyable {}
// CHECK-LABEL: sil [ossa] @test_noncopyable_enum_inject_enum_addr_opt
// CHECK: [[VAL:%.*]] = enum $Optional<NC>, #Optional.none!enumelt
// CHECK: store [[VAL]] to [init] %0 : $*Optional<NC>
// CHECK: } // end sil function 'test_noncopyable_enum_inject_enum_addr_opt'
sil [ossa] @test_noncopyable_enum_inject_enum_addr_opt : $@convention(method) (@inout Optional<NC>) -> @owned Optional<NC> {
bb0(%0 : $*Optional<NC>):
%1 = load [take] %0 : $*Optional<NC>
inject_enum_addr %0 : $*Optional<NC>, #Optional.none!enumelt
return %1 : $Optional<NC>
}
// CHECK-LABEL: sil [ossa] @trivial_replacement
// CHECK: bb1({{.*}}:
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value %2
// CHECK: apply {{%[0-9]+}}(%1)
// CHECK: } // end sil function 'trivial_replacement'
sil [ossa] @trivial_replacement : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = enum $E2, #E2.int!enumelt, %0 : $Int
%2 = enum $Optional<E2>, #Optional.some!enumelt, %1 : $E2, forwarding: @owned
%3 = begin_borrow %2 : $Optional<E2>
switch_enum %3 : $Optional<E2>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
bb1(%5 : @guaranteed $E2):
end_borrow %3 : $Optional<E2>
%7 = unchecked_enum_data %2 : $Optional<E2>, #Optional.some!enumelt
%8 = function_ref @useit : $@convention(thin) (@owned E2) -> ()
%9 = apply %8(%7) : $@convention(thin) (@owned E2) -> ()
br bb3
bb2:
end_borrow %3 : $Optional<E2>
destroy_value %2 : $Optional<E2>
br bb3
bb3:
%r = tuple ()
return %r : $()
}