// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -temp-lvalue-opt | %FileCheck %s import Swift import Builtin // REQUIRES: swift_in_compiler // CHECK-LABEL: sil @test_enum_with_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [init] [[E]] // CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt // CHECK-NOT: copy_addr // CHECK: } // end sil function 'test_enum_with_initialize' sil @test_enum_with_initialize : $@convention(thin) (@in Any) -> @out Optional { bb0(%0 : $*Optional, %1 : $*Any): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %2 to [init] %0 : $*Optional dealloc_stack %2 : $*Optional %6 = tuple () return %6 : $() } // CHECK-LABEL: sil @test_enum_without_initialize : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr %0 // CHECK-NEXT: [[E:%[0-9]+]] = init_enum_data_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [init] [[E]] // CHECK-NEXT: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt // CHECK-NOT: copy_addr // CHECK: } // end sil function 'test_enum_without_initialize' sil @test_enum_without_initialize : $@convention(thin) (@in Any) -> @out Optional { bb0(%0 : $*Optional, %1 : $*Any): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %2 to %0 : $*Optional dealloc_stack %2 : $*Optional %6 = tuple () return %6 : $() } protocol Proto {} struct ConformingStruct : Proto { @_hasStorage let a: Any } // CHECK-LABEL: sil @test_existential : // CHECK: bb0(%0 : $*any Proto, %1 : $*ConformingStruct): // CHECK-NEXT: [[E:%[0-9]+]] = init_existential_addr %0 // CHECK-NEXT: copy_addr [take] %1 to [init] [[E]] // CHECK-NOT: copy_addr // CHECK: } // end sil function 'test_existential' sil @test_existential : $@convention(thin) (@in ConformingStruct) -> @out Proto { bb0(%0 : $*Proto, %1 : $*ConformingStruct): %2 = alloc_stack $Proto %3 = init_existential_addr %2 : $*Proto, $ConformingStruct copy_addr [take] %1 to [init] %3 : $*ConformingStruct copy_addr [take] %2 to [init] %0 : $*Proto dealloc_stack %2 : $*Proto %6 = tuple () return %6 : $() } enum Either { case left(Left), right(Right) } public struct TestStruct { @_hasStorage var e: Either } struct AddressOnlyPayload { @_hasStorage let a: Any @_hasStorage let i: Int } // There should only be a single copy_addr after optimization. // // CHECK-LABEL: sil @test_initialize_struct : // CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): // HECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0 // HECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]] // HECK-NEXT: [[A:%[0-9]+]] = struct_element_addr [[LEFT]] // HECK-NEXT: copy_addr %1 to [init] [[A]] // HECK-NEXT: [[I:%[0-9]+]] = struct_element_addr [[LEFT]] // HECK-NEXT: store %2 to [[I]] // HECK-NEXT: inject_enum_addr [[E]] // HECK-NOT: copy_addr // CHECK: } // end sil function 'test_initialize_struct' sil @test_initialize_struct : $@convention(method) (@in Any, Int) -> @out TestStruct { bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int): %3 = alloc_stack $TestStruct %4 = alloc_stack $Either %5 = alloc_stack $AddressOnlyPayload %6 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.a copy_addr %1 to [init] %6 : $*Any %8 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.i store %2 to %8 : $*Int %10 = init_enum_data_addr %4 : $*Either, #Either.left!enumelt copy_addr [take] %5 to [init] %10 : $*AddressOnlyPayload inject_enum_addr %4 : $*Either, #Either.left!enumelt dealloc_stack %5 : $*AddressOnlyPayload %14 = struct_element_addr %3 : $*TestStruct, #TestStruct.e copy_addr [take] %4 to [init] %14 : $*Either dealloc_stack %4 : $*Either copy_addr %3 to [init] %0 : $*TestStruct destroy_addr %3 : $*TestStruct dealloc_stack %3 : $*TestStruct %20 = tuple () return %20 : $() } // CHECK-LABEL: sil @bail_on_write_to_dest : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr // CHECK: } // end sil function 'bail_on_write_to_dest' sil @bail_on_write_to_dest : $@convention(thin) (@inout Optional, @in Any) -> () { bb0(%0 : $*Optional, %1 : $*Any): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt destroy_addr %0 : $*Optional copy_addr [take] %2 to [init] %0 : $*Optional dealloc_stack %2 : $*Optional %6 = tuple () return %6 : $() } // CHECK-LABEL: sil @write_to_dest_ok_if_before_liverange : // CHECK: bb0(%0 : $*Optional, %1 : $*Any): // CHECK-NEXT: destroy_addr // CHECK-NEXT: init_enum_data_addr // CHECK-NEXT: copy_addr // CHECK-NEXT: inject_enum_addr // CHECK-NOT: copy_addr // CHECK: } // end sil function 'write_to_dest_ok_if_before_liverange' sil @write_to_dest_ok_if_before_liverange : $@convention(thin) (@inout Optional, @in Any) -> () { bb0(%0 : $*Optional, %1 : $*Any): %2 = alloc_stack $Optional destroy_addr %0 : $*Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %2 to [init] %0 : $*Optional dealloc_stack %2 : $*Optional %6 = tuple () return %6 : $() } enum Enum { case A(Optional), B } struct StructWithEnum : Proto { @_hasStorage let e: Enum } // CHECK-LABEL: sil @move_projections : // CHECK: bb0(%0 : $*any Proto, %1 : $*Any): // HECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*any Proto, $StructWithEnum // HECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e // HECK-NEXT: [[ENUMA:%[0-9]+]] = init_enum_data_addr [[E]] : $*Enum, #Enum.A!enumelt // HECK-NEXT: [[OPTIONAL:%[0-9]+]] = init_enum_data_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt // HECK-NEXT: copy_addr [take] %1 to [init] [[OPTIONAL]] : $*Any // HECK-NEXT: inject_enum_addr [[ENUMA]] : $*Optional, #Optional.some!enumelt // HECK-NEXT: inject_enum_addr [[E]] : $*Enum, #Enum.A!enumelt // HECK-NOT: copy_addr // CHECK: } // end sil function 'move_projections' sil @move_projections : $@convention(thin) (@in Any) -> @out Proto { bb0(%0 : $*Proto, %1 : $*Any): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt %4 = init_existential_addr %0 : $*Proto, $StructWithEnum %5 = struct_element_addr %4 : $*StructWithEnum, #StructWithEnum.e %6 = init_enum_data_addr %5 : $*Enum, #Enum.A!enumelt copy_addr [take] %2 to [init] %6 : $*Optional inject_enum_addr %5 : $*Enum, #Enum.A!enumelt dealloc_stack %2 : $*Optional %10 = tuple () return %10 : $() } // CHECK-LABEL: sil @cant_move_projections : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: load // CHECK: copy_addr // CHECK: } // end sil function 'cant_move_projections' sil @cant_move_projections : $@convention(thin) (@in Any, @in_guaranteed Builtin.RawPointer) -> () { bb0(%0 : $*Any, %1 : $*Builtin.RawPointer): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %0 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt %4 = load %1 : $*Builtin.RawPointer %5 = pointer_to_address %4 : $Builtin.RawPointer to $*Optional copy_addr [take] %2 to [init] %5 : $*Optional dealloc_stack %2 : $*Optional %10 = tuple () return %10 : $() } sil @init_optional : $@convention(thin) () -> @out Optional // CHECK-LABEL: sil @instructions_after_copy_addr : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr // CHECK: apply // CHECK: } // end sil function 'instructions_after_copy_addr' sil @instructions_after_copy_addr : $@convention(thin) (@in Any) -> @out Optional { bb0(%0 : $*Optional, %1 : $*Any): %2 = alloc_stack $Optional %3 = init_enum_data_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %1 to [init] %3 : $*Any inject_enum_addr %2 : $*Optional, #Optional.some!enumelt copy_addr [take] %2 to [init] %0 : $*Optional %4 = function_ref @init_optional : $@convention(thin) () -> @out Optional %5 = apply %4(%2) : $@convention(thin) () -> @out Optional destroy_addr %2 : $*Optional dealloc_stack %2 : $*Optional %6 = tuple () return %6 : $() } // CHECK-LABEL: sil @dont_optimize_swap : // CHECK: alloc_stack // CHECK: copy_addr // CHECK: copy_addr // CHECK: copy_addr // CHECK: dealloc_stack // CHECK: } // end sil function 'dont_optimize_swap' sil @dont_optimize_swap : $@convention(thin) (@inout T, @inout T) -> () { bb0(%0 : $*T, %1 : $*T): %2 = alloc_stack $T copy_addr [take] %0 to [init] %2 : $*T copy_addr [take] %1 to [init] %0 : $*T copy_addr [take] %2 to [init] %1 : $*T dealloc_stack %2 : $*T %78 = tuple () return %78 : $() } // CHECK-LABEL: sil @optimize_builtin_zeroInitialize : {{.*}} { // CHECK: bb0([[RET_ADDR:%[^,]+]] : // CHECK: builtin "zeroInitializer"([[RET_ADDR]] : $*T) : $() // CHECK: apply undef([[RET_ADDR]]) // CHECK-LABEL: } // end sil function 'optimize_builtin_zeroInitialize' sil @optimize_builtin_zeroInitialize : $@convention(thin) () -> @out T { bb0(%ret_addr : $*T): %temporary = alloc_stack [lexical] $T builtin "zeroInitializer"(%temporary : $*T) : $() apply undef(%temporary) : $@convention(thin) () -> @out T copy_addr [take] %temporary to [init] %ret_addr : $*T dealloc_stack %temporary : $*T // Ensure that the following builtin instructions don't interfere with // temp l value from getting rid of the temporary. %empty = builtin "fence_release"() : $() %ptr = address_to_pointer %ret_addr : $*T to $Builtin.RawPointer %load = builtin "atomicload_monotonic_Int64"(%ptr : $Builtin.RawPointer) : $Builtin.Int64 %onetwoeight = integer_literal $Builtin.Int64, 128 %empty2 = builtin "atomicstore_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64) : $() %add = builtin "atomicrmw_add_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64) : $Builtin.Int64 %cmpxchg = builtin "cmpxchg_monotonic_monotonic_Int64"(%ptr : $Builtin.RawPointer, %onetwoeight : $Builtin.Int64, %onetwoeight : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %15 = tuple () return %15 : $() } class Klass { init() } enum FakeOptional { case none case some(Klass) } sil @initfn : $@convention(thin) () -> @out Klass // CHECK-LABEL: sil @test_enum // CHECK: alloc_stack // CHECK: alloc_stack // CHECK-LABEL: } // end sil function 'test_enum' sil @test_enum : $@convention(thin) () -> @out FakeOptional { bb0(%0 : $*FakeOptional): %1 = alloc_stack [lexical] [var_decl] $Klass %2 = function_ref @initfn : $@convention(thin) () -> @out Klass %3 = apply %2(%1) : $@convention(thin) () -> @out Klass %4 = alloc_stack $Klass %5 = apply %2(%4) : $@convention(thin) () -> @out Klass destroy_addr %1 copy_addr [take] %4 to [init] %1 dealloc_stack %4 %9 = init_enum_data_addr %0, #FakeOptional.some!enumelt copy_addr [take] %1 to [init] %9 inject_enum_addr %0, #FakeOptional.some!enumelt dealloc_stack %1 %13 = tuple () return %13 }