mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
For example the pattern:
```
%e = load %1 : SomeEnum
switch_enum %e
```
The switch_enum_addr should reuse the load's address operand instead of
creating a new location for the SSA value of %e.
While we are there also don't create a new stack location for extracting payload
in a switch_enum_addr case target basic block if that extracted payload is not
used. That is if the basic block arguments in all switch target blocks are
unused in the switch_enum version.
```
switch_enum %enum, case #Optional.some!enumelt: bb1,
case #Optional.none!enumelt: bb2
bb2(%payload : $Payload):
// unused %payload value
```
rdar://156602951
466 lines
13 KiB
Plaintext
466 lines
13 KiB
Plaintext
// RUN: %target-swift-frontend %s -Xllvm -sil-print-types -Xllvm -sil-print-after=loadable-address -import-objc-header %S/Inputs/large_c.h -c -o %t/t.o 2>&1 | %FileCheck %s
|
|
|
|
// wasm currently disables aggressive reg2mem
|
|
// UNSUPPORTED: CPU=wasm32
|
|
// REQUIRES: PTRSIZE=64
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
struct X {
|
|
var x1 : Int
|
|
var x2 : Int
|
|
var x3 : Int
|
|
var x4: Int
|
|
var x5: Int
|
|
var x6: Int
|
|
var x7: Int
|
|
var x8: Int
|
|
var x9: Int
|
|
var x10: Int
|
|
var x11: Int
|
|
var x12: Int
|
|
var x13: Int
|
|
var x14: Int
|
|
var x15: Int
|
|
var x16: Int
|
|
}
|
|
|
|
struct X2 {
|
|
var x1 : Int
|
|
var x2 : Int
|
|
var x3 : Int
|
|
var x4: Int
|
|
var x5: Int
|
|
var x6: Int
|
|
var x7: Int
|
|
var x8: Int
|
|
var x9: Int
|
|
var x10: Int
|
|
var x11: Int
|
|
var x12: Int
|
|
var x13: Int
|
|
var x14: Int
|
|
var x15: Int
|
|
var x16: Int
|
|
}
|
|
|
|
struct Y {
|
|
var y1 : X
|
|
var y2: X
|
|
}
|
|
|
|
class C1 {
|
|
}
|
|
|
|
sil_vtable C1 {
|
|
}
|
|
|
|
struct Small {
|
|
var x1 : Int
|
|
}
|
|
|
|
// CHECK: sil @test1 : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: %0 = alloc_stack $Optional<X>
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = alloc_stack $Optional<X>
|
|
// CHECK: %3 = init_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
|
// CHECK: copy_addr [take] %1 to [init] %3 : $*X
|
|
// CHECK: inject_enum_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
|
// CHECK: copy_addr [take] %0 to [init] %2 : $*Optional<X>
|
|
// CHECK: dealloc_stack %2 : $*Optional<X>
|
|
// CHECK: dealloc_stack %1 : $*X
|
|
// CHECK: dealloc_stack %0 : $*Optional<X>
|
|
// CHECK: } // end sil function 'test1'
|
|
|
|
sil @test1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $X
|
|
%1 = alloc_stack $Optional<X>
|
|
%2 = load %0 : $*X
|
|
%3 = enum $Optional<X>, #Optional.some!enumelt, %2 : $X
|
|
store %3 to %1 : $*Optional<X>
|
|
dealloc_stack %1 : $*Optional<X>
|
|
dealloc_stack %0 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
sil @useX : $@convention(thin) (X) -> ()
|
|
|
|
// CHECK: sil @test2 : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: %0 = alloc_stack $Optional<X>
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = alloc_stack $Optional<X>
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*Optional<X>
|
|
// CHECK: switch_enum_addr %2 : $*Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
|
|
|
// CHECK: bb1:
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
|
// CHECK: copy_addr [take] %5 to [init] %1 : $*X
|
|
// CHECK: %7 = function_ref @useX : $@convention(thin) (@in_guaranteed X) -> ()
|
|
// CHECK: %8 = apply %7(%1) : $@convention(thin) (@in_guaranteed X) -> ()
|
|
// CHECK: br bb3
|
|
|
|
// CHECK: bb2:
|
|
// CHECK: br bb3
|
|
|
|
// CHECK: bb3:
|
|
// CHECK: dealloc_stack %2 : $*Optional<X>
|
|
// CHECK: dealloc_stack %1 : $*X
|
|
// CHECK: dealloc_stack %0 : $*Optional<X>
|
|
// CHECK: } // end sil function 'test2'
|
|
|
|
sil @test2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%1 = alloc_stack $Optional<X>
|
|
%2 = load %1 : $*Optional<X>
|
|
switch_enum %2 : $Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
|
|
|
bb1(%3: $X):
|
|
%f = function_ref @useX : $@convention(thin) (X) -> ()
|
|
apply %f(%3) : $@convention(thin) (X) -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
dealloc_stack %1 : $*Optional<X>
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test3 : $@convention(thin) () -> Builtin.Int1 {
|
|
// CHECK: bb0:
|
|
// CHECK: %0 = alloc_stack $Optional<X>
|
|
// CHECK: %1 = alloc_stack $Optional<X>
|
|
// CHECK: copy_addr [take] %1 to [init] %0 : $*Optional<X>
|
|
// CHECK: %3 = integer_literal $Builtin.Int1, -1
|
|
// CHECK: %4 = integer_literal $Builtin.Int1, 0
|
|
// CHECK: %5 = select_enum_addr %0 : $*Optional<X>, case #Optional.some!enumelt: %3, case #Optional.none!enumelt: %4 : $Builtin.Int1
|
|
// CHECK: dealloc_stack %1 : $*Optional<X>
|
|
// CHECK: dealloc_stack %0 : $*Optional<X>
|
|
// CHECK: return %5 : $Builtin.Int1
|
|
// CHECK: } // end sil function 'test3'
|
|
|
|
sil @test3 : $@convention(thin) () -> Builtin.Int1 {
|
|
bb0:
|
|
%1 = alloc_stack $Optional<X>
|
|
%2 = load %1 : $*Optional<X>
|
|
%3 = integer_literal $Builtin.Int1, -1
|
|
%4 = integer_literal $Builtin.Int1, 0
|
|
%r = select_enum %2 : $Optional<X>, case #Optional.some!enumelt: %3, case #Optional.none!enumelt: %4 : $Builtin.Int1
|
|
dealloc_stack %1 : $*Optional<X>
|
|
return %r : $Builtin.Int1
|
|
}
|
|
// CHECK: sil @test4 : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: %0 = alloc_stack $(X, X)
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = alloc_stack $X
|
|
// CHECK: %3 = alloc_stack $(X, X)
|
|
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
|
// CHECK: %5 = tuple_element_addr %0 : $*(X, X), 0
|
|
// CHECK: copy_addr [take] %1 to [init] %5 : $*X
|
|
// CHECK: %7 = tuple_element_addr %0 : $*(X, X), 1
|
|
// CHECK: copy_addr [take] %1 to [init] %7 : $*X
|
|
// CHECK: copy_addr [take] %0 to [init] %3 : $*(X, X)
|
|
// CHECK: } // end sil function 'test4'
|
|
|
|
sil @test4 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $X
|
|
%1 = alloc_stack $(X, X)
|
|
%2 = load %0 : $*X
|
|
%3 = tuple (%2 : $X , %2 : $X)
|
|
store %3 to %1 : $*(X, X)
|
|
dealloc_stack %1 : $*(X,X)
|
|
dealloc_stack %0 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test5 : $@convention(thin) (@in (X, X)) -> () {
|
|
// CHECK: bb0(%0 : $*(X, X)):
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = tuple_element_addr %0 : $*(X, X), 1
|
|
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
|
// CHECK: } // end sil function 'test5'
|
|
|
|
sil @test5 : $@convention(thin) (@in (X, X)) -> () {
|
|
bb0(%0 : $*(X, X)):
|
|
%1 = alloc_stack $X
|
|
%2 = load %0 : $*(X, X)
|
|
%4 = tuple_extract %2 : $(X, X), 1
|
|
store %4 to %1 : $*X
|
|
dealloc_stack %1 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test6 : $@convention(thin) () -> () {
|
|
// CHECK: bb0:
|
|
// CHECK: %0 = alloc_stack $Y
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = alloc_stack $X
|
|
// CHECK: %3 = alloc_stack $Y
|
|
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
|
// CHECK: %5 = struct_element_addr %0 : $*Y, #Y.y1
|
|
// CHECK: copy_addr [take] %1 to [init] %5 : $*X
|
|
// CHECK: %7 = struct_element_addr %0 : $*Y, #Y.y2
|
|
// CHECK: copy_addr [take] %1 to [init] %7 : $*X
|
|
// CHECK: copy_addr [take] %0 to [init] %3 : $*Y
|
|
// CHECK: } // end sil function 'test6'
|
|
|
|
sil @test6 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $X
|
|
%1 = alloc_stack $Y
|
|
%2 = load %0 : $*X
|
|
%3 = struct $Y (%2 : $X , %2 : $X)
|
|
store %3 to %1 : $*Y
|
|
dealloc_stack %1 : $*Y
|
|
dealloc_stack %0 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test7 : $@convention(thin) (@in Y) -> () {
|
|
// CHECK: bb0(%0 : $*Y):
|
|
// CHECK: %1 = alloc_stack $X
|
|
// CHECK: %2 = struct_element_addr %0 : $*Y, #Y.y1
|
|
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
|
// CHECK: } // end sil function 'test7'
|
|
|
|
sil @test7 : $@convention(thin) (@in Y) -> () {
|
|
bb0(%0 : $*Y):
|
|
%1 = alloc_stack $X
|
|
%2 = load %0 : $*Y
|
|
%4 = struct_extract %2 : $Y, #Y.y1
|
|
store %4 to %1 : $*X
|
|
dealloc_stack %1 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test8
|
|
// CHECK: bb0(%0 : $SamplesType):
|
|
// CHECK: %1 = alloc_stack $SamplesType
|
|
// CHECK: store %0 to %1 : $*SamplesType
|
|
sil @helper : $@convention(thin) (SamplesType) -> ()
|
|
|
|
sil @test8 : $@convention(c) (SamplesType) -> () {
|
|
bb0(%0 : $SamplesType):
|
|
%1 = function_ref @helper : $@convention(thin) (SamplesType) -> ()
|
|
%2 = apply %1(%0) : $@convention(thin) (SamplesType) -> ()
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// In this test case Container type is identified as not large but contained
|
|
// type is.
|
|
sil @test9 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $_ContainerType
|
|
%1 = load %0 : $*_ContainerType
|
|
%2 = alloc_stack $(_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType,
|
|
_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType)
|
|
|
|
%3 = struct_extract %1 : $_ContainerType, #_ContainerType.l
|
|
store %3 to %2 : $*(_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType,
|
|
_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType)
|
|
dealloc_stack %2 : $*(_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType,
|
|
_ContainedType, _ContainedType, _ContainedType, _ContainedType, _ContainedType)
|
|
dealloc_stack %0 : $*_ContainerType
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
sil @test10 : $@convention(thin) (@in_guaranteed C1, @in Small) -> () {
|
|
bb0(%0 : $*C1, %1 : $*Small):
|
|
%2 = load %1 : $*Small
|
|
%3 = load %0 : $*C1
|
|
retain_value %3 : $C1
|
|
store %2 to %1 : $*Small
|
|
retain_value %3 : $C1
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
sil @use : $@convention(thin) (UInt32) -> ()
|
|
|
|
// We need to make sure we use the bigger alloc_stack for the address of
|
|
// unchecked_trivial_bit_cast. A bitcast can go from bigger to smaller type.
|
|
|
|
// CHECK: sil @test11 : $@convention(thin) (@in union_t) -> () {
|
|
// CHECK-NOT: unchecked_addr_cast %1 : $*union_t.__Unnamed_struct_in to $*union_t
|
|
// CHECK: unchecked_addr_cast {{.*}} : $*union_t to $*union_t.__Unnamed_struct_in
|
|
// CHECK: } // end sil function 'test11'
|
|
|
|
sil @test11 : $@convention(thin) (@in union_t) -> () {
|
|
bb0(%0 : $*union_t):
|
|
%1 = alloc_stack $union_t
|
|
%2 = alloc_stack $union_t
|
|
copy_addr [take] %0 to [init] %2 : $*union_t
|
|
%4 = load %2 : $*union_t
|
|
%7 = unchecked_trivial_bit_cast %4 : $union_t to $union_t.__Unnamed_struct_in
|
|
%8 = struct_extract %7 : $union_t.__Unnamed_struct_in, #union_t.__Unnamed_struct_in.slot
|
|
%10 = struct_extract %8 : $enum_t, #enum_t.rawValue
|
|
%11 = function_ref @use : $@convention(thin) (UInt32) -> ()
|
|
%12 = apply %11(%10) : $@convention(thin) (UInt32) -> ()
|
|
%13 = tuple ()
|
|
dealloc_stack %2 : $*union_t
|
|
dealloc_stack %1 : $*union_t
|
|
return %13 : $()
|
|
}
|
|
|
|
// This test case used to crash because we did not handle `mark_dependence`.
|
|
sil @test12 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_stack $X
|
|
%1 = alloc_stack $Y
|
|
%2 = alloc_stack $X
|
|
%3 = load %0 : $*X
|
|
%4 = load %2 : $*X
|
|
%5 = mark_dependence %3 : $X on %4 : $X
|
|
%6 = struct $Y (%5 : $X , %5 : $X)
|
|
store %6 to %1 : $*Y
|
|
dealloc_stack %2 : $*X
|
|
dealloc_stack %1 : $*Y
|
|
dealloc_stack %0 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
// CHECK: sil @test13
|
|
// CHECK: [[ADDR:%.*]] = unchecked_addr_cast %2 : $*X to $*Y
|
|
// CHECK: copy_addr [take] [[ADDR]] to [init] %1 : $*Y
|
|
// CHECK: } // end sil function 'test13'
|
|
sil @test13 : $@convention(thin) (@in X) -> () {
|
|
bb0(%0 : $*X):
|
|
%1 = alloc_stack $Y
|
|
%2 = alloc_stack $X
|
|
copy_addr [take] %0 to [init] %2 : $*X
|
|
%4 = load %2 : $*X
|
|
%7 = unchecked_bitwise_cast %4 : $X to $Y
|
|
store %7 to %1: $*Y
|
|
%13 = tuple ()
|
|
dealloc_stack %2 : $*X
|
|
dealloc_stack %1 : $*Y
|
|
return %13 : $()
|
|
}
|
|
|
|
// CHECK: sil @test14
|
|
// CHECK: [[VAL:%.*]] = load {{.*}} : $*X
|
|
// CHECK: throw [[VAL]]
|
|
// CHECK: } // end sil function 'test14'
|
|
sil @test14 : $@convention(thin) (@in X) -> @error X {
|
|
bb0(%0 : $*X):
|
|
%1 = alloc_stack $X
|
|
copy_addr [take] %0 to [init] %1 : $*X
|
|
%3 = load %1 : $*X
|
|
dealloc_stack %1 : $*X
|
|
throw %3 : $X
|
|
}
|
|
|
|
// CHECK: sil @test15
|
|
// CHECK: cond_br %1, bb1, bb2
|
|
|
|
// CHECK: bb1:
|
|
// CHECK: copy_addr [take] {{.*}} to [init] [[PHI:%[0-9]+]] : $*X
|
|
// CHECK: br bb3
|
|
|
|
// CHECK: bb2:
|
|
// CHECK: [[UNDEF:%.*]] = alloc_stack $X
|
|
// CHECK: copy_addr [take] [[UNDEF]] to [init] [[PHI]] : $*X
|
|
// CHECK: dealloc_stack [[UNDEF]] : $*X
|
|
// CHECK: br bb3
|
|
// CHECK:} // end sil function 'test15'
|
|
|
|
sil @test15 : $@convention(thin) (X, Builtin.Int1) -> () {
|
|
bb0(%0: $X, %1: $Builtin.Int1):
|
|
%2 = alloc_stack $X
|
|
cond_br %1, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%0 : $X)
|
|
|
|
bb2:
|
|
br bb3(undef : $X)
|
|
|
|
bb3(%4 : $X):
|
|
store %4 to %2: $*X
|
|
|
|
dealloc_stack %2 : $*X
|
|
%t = tuple ()
|
|
return %t : $()
|
|
}
|
|
|
|
sil @usei8 : $@convention(thin) (UInt8) -> ()
|
|
|
|
// CHECK: sil @test16
|
|
// CHECK: bb0(%0 : $member_id_t):
|
|
// CHECK: [[T0:%.*]] = alloc_stack $member_id_t
|
|
// CHECK: store %0 to [[T0]] : $*member_id_t
|
|
// CHECK: struct_element_addr [[T0]]
|
|
// CHECK:} // end sil function 'test16'
|
|
|
|
sil @test16 : $@convention(thin) (member_id_t) -> () {
|
|
bb0(%0 : $member_id_t):
|
|
%2 = alloc_stack $X
|
|
%44 = struct_extract %0 : $member_id_t, #member_id_t.member_value // user: %45
|
|
%45 = unchecked_trivial_bit_cast %44 : $member_id_t.__Unnamed_union_member_value to $(UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)
|
|
%46 = tuple_extract %45 : $(UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8), 0
|
|
%11 = function_ref @usei8 : $@convention(thin) (UInt8) -> ()
|
|
%12 = apply %11(%46) : $@convention(thin) (UInt8) -> ()
|
|
dealloc_stack %2 : $*X
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
}
|
|
|
|
struct Z {
|
|
var r: String
|
|
var p: X
|
|
}
|
|
|
|
// CHECK: sil @test17 : $@convention(thin) (@owned String) -> () {
|
|
// CHECK: bb0(%0 : $String):
|
|
// CHECK: [[T0:%.*]] = alloc_stack $String
|
|
// CHECK: store %0 to [[T0]] : $*String
|
|
// CHECK: [[T1:%.*]] = unchecked_addr_cast [[T0]] : $*String to $*Z
|
|
// CHECK: release_value_addr [[T1]] : $*Z
|
|
|
|
sil @test17: $@convention(thin) (@owned String) -> () {
|
|
bb0(%0 : $String):
|
|
%1 = unchecked_bitwise_cast %0 : $String to $Z
|
|
release_value %1 : $Z
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
}
|
|
|
|
sil_global private @global : $Optional<X>
|
|
|
|
sil @test18: $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @global : $*Optional<X>
|
|
%1 = begin_access [modify] [static] [no_nested_conflict] %0
|
|
%2 = load %1
|
|
switch_enum %2, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1
|
|
|
|
bb1:
|
|
%4 = integer_literal $Builtin.Int1, -1
|
|
cond_fail %4, "Unexpectedly found nil while unwrapping an Optional value"
|
|
unreachable
|
|
|
|
bb2(%7 : $X):
|
|
%8 = unchecked_take_enum_data_addr %1, #Optional.some!enumelt
|
|
end_access %1
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
}
|