Files
swift-mirror/test/SILOptimizer/templvalueopt_ossa.sil
Erik Eckstein 535a58e3fb SIL: assume a read-effect for unused indirect arguments
Even if an indirect argument is unused, pretend that the function reads from it.
If the unused argument is passed to another generic function and that function is specialized,
the argument may be re-abstracted and the specializer inserts a load from the indirect argument.
Therefore we must be prepared that unused indirect argument might get loaded from at some time.

Fixes a compiler crash
rdar://168623362
2026-01-23 15:58:23 +01:00

702 lines
23 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -temp-lvalue-elimination | %FileCheck %s
// REQUIRES: swift_in_compiler
import Swift
import Builtin
import SwiftShims
// CHECK-LABEL: sil [ossa] @test_enum_with_initialize :
// CHECK: bb0(%0 : $*Optional<Any>, %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<Any>, #Optional.some!enumelt
// CHECK-NOT: copy_addr
// CHECK: } // end sil function 'test_enum_with_initialize'
sil [ossa] @test_enum_with_initialize : $@convention(thin) (@in Any) -> @out Optional<Any> {
bb0(%0 : $*Optional<Any>, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %2 to [init] %0 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @test_enum_without_initialize :
// CHECK: bb0(%0 : $*Optional<Any>, %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<Any>, #Optional.some!enumelt
// CHECK-NOT: copy_addr
// CHECK: } // end sil function 'test_enum_without_initialize'
sil [ossa] @test_enum_without_initialize : $@convention(thin) (@inout Optional<Any>, @in Any) -> () {
bb0(%0 : $*Optional<Any>, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %2 to %0 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%6 = tuple ()
return %6 : $()
}
protocol Proto {}
struct ConformingStruct : Proto {
@_hasStorage let a: Any
}
// CHECK-LABEL: sil [ossa] @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 [ossa] @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<Left, Right> {
case left(Left), right(Right)
}
public struct TestStruct {
@_hasStorage var e: Either<AddressOnlyPayload, Int>
}
struct AddressOnlyPayload {
@_hasStorage let a: Any
@_hasStorage let i: Int
}
// There should only be a single copy_addr after optimization.
//
// CHECK-LABEL: sil [ossa] @test_initialize_struct :
// CHECK: bb0(%0 : $*TestStruct, %1 : $*Any, %2 : $Int):
// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr %0
// CHECK-NEXT: [[LEFT:%[0-9]+]] = init_enum_data_addr [[E]]
// CHECK-NEXT: [[A:%[0-9]+]] = struct_element_addr [[LEFT]]
// CHECK-NEXT: copy_addr [take] %1 to [init] [[A]]
// CHECK-NEXT: [[I:%[0-9]+]] = struct_element_addr [[LEFT]]
// CHECK-NEXT: store %2 to [trivial] [[I]]
// CHECK-NEXT: inject_enum_addr [[E]]
// CHECK-NOT: copy_addr
// CHECK: } // end sil function 'test_initialize_struct'
sil [ossa] @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<AddressOnlyPayload, Int>
%5 = alloc_stack $AddressOnlyPayload
%6 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.a
copy_addr [take] %1 to [init] %6 : $*Any
%8 = struct_element_addr %5 : $*AddressOnlyPayload, #AddressOnlyPayload.i
store %2 to [trivial] %8 : $*Int
%10 = init_enum_data_addr %4 : $*Either<AddressOnlyPayload, Int>, #Either.left!enumelt
copy_addr [take] %5 to [init] %10 : $*AddressOnlyPayload
inject_enum_addr %4 : $*Either<AddressOnlyPayload, Int>, #Either.left!enumelt
dealloc_stack %5 : $*AddressOnlyPayload
%14 = struct_element_addr %3 : $*TestStruct, #TestStruct.e
copy_addr [take] %4 to [init] %14 : $*Either<AddressOnlyPayload, Int>
dealloc_stack %4 : $*Either<AddressOnlyPayload, Int>
copy_addr %3 to [init] %0 : $*TestStruct
destroy_addr %3 : $*TestStruct
dealloc_stack %3 : $*TestStruct
%20 = tuple ()
return %20 : $()
}
// CHECK-LABEL: sil [ossa] @bail_on_write_to_dest :
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: copy_addr
// CHECK: } // end sil function 'bail_on_write_to_dest'
sil [ossa] @bail_on_write_to_dest : $@convention(thin) (@inout Optional<Any>, @in Any) -> () {
bb0(%0 : $*Optional<Any>, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
destroy_addr %0 : $*Optional<Any>
copy_addr [take] %2 to [init] %0 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @write_to_dest_ok_if_before_liverange :
// CHECK: bb0(%0 : $*Optional<Any>, %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 [ossa] @write_to_dest_ok_if_before_liverange : $@convention(thin) (@inout Optional<Any>, @in Any) -> () {
bb0(%0 : $*Optional<Any>, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
destroy_addr %0 : $*Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %2 to [init] %0 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%6 = tuple ()
return %6 : $()
}
enum Enum {
case A(Optional<Any>), B
}
struct StructWithEnum : Proto {
@_hasStorage let e: Enum
}
// CHECK-LABEL: sil [ossa] @move_projections :
// CHECK: bb0(%0 : $*any Proto, %1 : $*Any):
// CHECK-NEXT: [[S:%[0-9]+]] = init_existential_addr %0 : $*any Proto, $StructWithEnum
// CHECK-NEXT: [[E:%[0-9]+]] = struct_element_addr [[S]] : $*StructWithEnum, #StructWithEnum.e
// CHECK-NEXT: [[ENUMA:%[0-9]+]] = init_enum_data_addr [[E]] : $*Enum, #Enum.A!enumelt
// CHECK-NEXT: [[OPTIONAL:%[0-9]+]] = init_enum_data_addr [[ENUMA]] : $*Optional<Any>, #Optional.some!enumelt
// CHECK-NEXT: copy_addr [take] %1 to [init] [[OPTIONAL]] : $*Any
// CHECK-NEXT: inject_enum_addr [[ENUMA]] : $*Optional<Any>, #Optional.some!enumelt
// CHECK-NEXT: inject_enum_addr [[E]] : $*Enum, #Enum.A!enumelt
// CHECK-NOT: copy_addr
// CHECK: } // end sil function 'move_projections'
sil [ossa] @move_projections : $@convention(thin) (@in Any) -> @out Proto {
bb0(%0 : $*Proto, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #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<Any>
inject_enum_addr %5 : $*Enum, #Enum.A!enumelt
dealloc_stack %2 : $*Optional<Any>
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil [ossa] @cant_move_projections :
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: load
// CHECK: copy_addr
// CHECK: } // end sil function 'cant_move_projections'
sil [ossa] @cant_move_projections : $@convention(thin) (@in Any, @in_guaranteed Builtin.RawPointer) -> () {
bb0(%0 : $*Any, %1 : $*Builtin.RawPointer):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %0 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
%4 = load [trivial] %1 : $*Builtin.RawPointer
%5 = pointer_to_address %4 : $Builtin.RawPointer to $*Optional<Any>
copy_addr [take] %2 to [init] %5 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%10 = tuple ()
return %10 : $()
}
sil [ossa] @init_optional : $@convention(thin) () -> @out Optional<Any>
// CHECK-LABEL: sil [ossa] @instructions_after_copy_addr :
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: copy_addr
// CHECK: apply
// CHECK: } // end sil function 'instructions_after_copy_addr'
sil [ossa] @instructions_after_copy_addr : $@convention(thin) (@in Any) -> @out Optional<Any> {
bb0(%0 : $*Optional<Any>, %1 : $*Any):
%2 = alloc_stack $Optional<Any>
%3 = init_enum_data_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %1 to [init] %3 : $*Any
inject_enum_addr %2 : $*Optional<Any>, #Optional.some!enumelt
copy_addr [take] %2 to [init] %0 : $*Optional<Any>
%4 = function_ref @init_optional : $@convention(thin) () -> @out Optional<Any>
%5 = apply %4(%2) : $@convention(thin) () -> @out Optional<Any>
destroy_addr %2 : $*Optional<Any>
dealloc_stack %2 : $*Optional<Any>
%6 = tuple ()
return %6 : $()
}
// CHECK-LABEL: sil [ossa] @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 [ossa] @dont_optimize_swap : $@convention(thin) <T> (@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 : $()
}
class Child { }
struct Parent {
var c : Child
}
sil @gen_child : $@convention(thin) () -> @out Child
// Check that alias analysis is invalidated correctly and that the pass does
// not produce invalid SIL (which would trigger a MemoryLifetime failure).
//
// CHECK-LABEL: sil [ossa] @invalidateAliasAnalysis :
// CHECK: copy_addr
// CHECK: } // end sil function 'invalidateAliasAnalysis'
sil [ossa] @invalidateAliasAnalysis : $@convention(thin) (@owned Child) -> () {
bb0(%0 :@owned $Child):
%2 = alloc_stack $Parent
%4 = alloc_stack $Child
store %0 to [init] %4 : $*Child
%7 = alloc_stack $Child
%func = function_ref @gen_child : $@convention(thin) () -> @out Child
%10 = apply %func(%7) : $@convention(thin)() -> @out Child
%11 = struct_element_addr %2 : $*Parent, #Parent.c
copy_addr [take] %7 to [init] %11 : $*Child
dealloc_stack %7 : $*Child
copy_addr [take] %4 to %11 : $*Child
%17 = tuple ()
dealloc_stack %4 : $*Child
destroy_addr %2 : $*Parent
dealloc_stack %2 : $*Parent
%res = tuple ()
return %res : $()
}
// CHECK-LABEL: sil [ossa] @cleanup_debuginsts :
// CHECK: copy_addr [take]
// CHECK-NOT: debug_value {{.*}} expr op_deref
// CHECK-LABEL: } // end sil function 'cleanup_debuginsts'
sil [ossa] @cleanup_debuginsts : $@convention(thin) <T> (@in T) -> () {
bb0(%0 : $*T):
%2 = alloc_stack $T
debug_value %0 : $*T, expr op_deref
copy_addr %0 to [init] %2 : $*T
debug_value %0 : $*T, expr op_deref
destroy_addr %0 : $*T
destroy_addr %2 : $*T
dealloc_stack %2 : $*T
%78 = tuple ()
return %78 : $()
}
sil @createAny : $@convention(thin) () -> @out Any
sil @createAny_no_barrier : $@convention(thin) () -> @out Any {
[global:]
}
// CHECK-LABEL: sil [ossa] @test_deinit_barrier :
// CHECK: copy_addr [take]
// CHECK-LABEL: } // end sil function 'test_deinit_barrier'
sil [ossa] @test_deinit_barrier : $@convention(thin) (@guaranteed Any, @inout Any) -> () {
bb0(%0 : @guaranteed $Any, %1 : $*Any):
%2 = alloc_stack $Any
%4 = function_ref @createAny : $@convention(thin) () -> @out Any
%5 = apply %4(%2) : $@convention(thin) () -> @out Any
copy_addr [take] %2 to %1 : $*Any
dealloc_stack %2 : $*Any
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: sil [ossa] @test_no_deinit_barrier :
// CHECK-NOT: copy_addr
// CHECK-LABEL: } // end sil function 'test_no_deinit_barrier'
sil [ossa] @test_no_deinit_barrier : $@convention(thin) (@guaranteed Any, @inout Any) -> () {
bb0(%0 : @guaranteed $Any, %1 : $*Any):
%2 = alloc_stack $Any
%4 = function_ref @createAny_no_barrier : $@convention(thin) () -> @out Any
%5 = apply %4(%2) : $@convention(thin) () -> @out Any
copy_addr [take] %2 to %1 : $*Any
dealloc_stack %2 : $*Any
%11 = tuple ()
return %11 : $()
}
sil [readnone] @createAny_effects : $@convention(thin) () -> @out Any
sil [readnone] @createAny_no_barrier_effects : $@convention(thin) () -> @out Any {
[global:]
}
// CHECK-LABEL: sil [ossa] @test_deinit_barrier_effects :
// CHECK: copy_addr [take]
// CHECK-LABEL: } // end sil function 'test_deinit_barrier_effects'
sil [ossa] @test_deinit_barrier_effects : $@convention(thin) (@guaranteed Any, @inout Any) -> () {
bb0(%0 : @guaranteed $Any, %1 : $*Any):
%2 = alloc_stack $Any
%4 = function_ref @createAny_effects : $@convention(thin) () -> @out Any
%5 = apply %4(%2) : $@convention(thin) () -> @out Any
copy_addr [take] %2 to %1 : $*Any
dealloc_stack %2 : $*Any
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: sil [ossa] @test_no_deinit_barrier_effects :
// CHECK-NOT: copy_addr
// CHECK-LABEL: } // end sil function 'test_no_deinit_barrier_effects'
sil [ossa] @test_no_deinit_barrier_effects : $@convention(thin) (@guaranteed Any, @inout Any) -> () {
bb0(%0 : @guaranteed $Any, %1 : $*Any):
%2 = alloc_stack $Any
%4 = function_ref @createAny_no_barrier_effects : $@convention(thin) () -> @out Any
%5 = apply %4(%2) : $@convention(thin) () -> @out Any
copy_addr [take] %2 to %1 : $*Any
dealloc_stack %2 : $*Any
%11 = tuple ()
return %11 : $()
}
struct TwoFields {
var a: Child
var b: Child
}
sil @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child {
[%0: write v**]
[global: ]
}
sil @no_reads : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> () {
[global: ]
}
// CHECK-LABEL: sil [ossa] @argument_aliasing :
// CHECK: %1 = alloc_stack
// CHECK: apply %{{[0-9]+}}(%1, %0)
// CHECK-LABEL: } // end sil function 'argument_aliasing'
sil [ossa] @argument_aliasing : $@convention(thin) (@inout TwoFields) -> () {
bb0(%0 : $*TwoFields):
%1 = alloc_stack $Child
%2 = function_ref @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
%3 = apply %2(%1, %0) : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
%4 = struct_element_addr %0, #TwoFields.a
copy_addr [take] %1 to %4
dealloc_stack %1
%7 = tuple ()
return %7
}
// CHECK-LABEL: sil [ossa] @no_argument_aliasing :
// CHECK-NOT: alloc_stack
// CHECK: [[A:%.*]] = struct_element_addr %0 : $*TwoFields, #TwoFields.a
// CHECK-NEXT: destroy_addr [[A]]
// CHECK: apply %{{[0-9]+}}([[A]], %1)
// CHECK-NOT: copy_addr
// CHECK-LABEL: } // end sil function 'no_argument_aliasing'
sil [ossa] @no_argument_aliasing : $@convention(thin) (@inout TwoFields, @inout TwoFields) -> () {
bb0(%0 : $*TwoFields, %1 : $*TwoFields):
%2 = alloc_stack $Child
%3 = function_ref @no_read : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
%4 = apply %3(%2, %1) : $@convention(thin) (@in_guaranteed TwoFields) -> @out Child
%5 = struct_element_addr %0, #TwoFields.a
copy_addr [take] %2 to %5
dealloc_stack %2
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @guaranteed_argument_aliasing :
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK-LABEL: } // end sil function 'guaranteed_argument_aliasing'
sil [ossa] @guaranteed_argument_aliasing : $@convention(thin) (@inout TwoFields, @owned Child) -> () {
bb0(%0 : $*TwoFields, %1 : @owned $Child):
%2 = alloc_stack $Child
store %1 to [init] %2
%3 = function_ref @no_reads : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> ()
%4 = apply %3(%2, %0) : $@convention(thin) (@in_guaranteed Child, @in_guaranteed TwoFields) -> ()
%5 = struct_element_addr %0, #TwoFields.a
copy_addr [take] %2 to %5
dealloc_stack %2
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @multi_block :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: [[SA:%.*]] = struct_element_addr %0
// CHECK-NEXT: destroy_addr [[SA]]
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK: store {{.*}} to [init] [[SA]]
// CHECK: bb2:
// CHECK: store {{.*}} to [init] [[SA]]
// CHECK: bb3:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'multi_block'
sil [ossa] @multi_block : $@convention(thin) (@inout TwoFields, @guaranteed Child, @guaranteed Child) -> () {
bb0(%0 : $*TwoFields, %1 : @guaranteed $Child, %2 : @guaranteed $Child):
%3 = alloc_stack $Child
cond_br undef, bb1, bb2
bb1:
%5 = copy_value %1
store %5 to [init] %3
br bb3
bb2:
%8 = copy_value %2
store %8 to [init] %3
br bb3
bb3:
%11 = struct_element_addr %0, #TwoFields.a
copy_addr [take] %3 to %11
dealloc_stack %3
%14 = tuple ()
return %14
}
// CHECK-LABEL: sil [ossa] @multi_block_with_mutation :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: alloc_stack
// CHECK-LABEL: } // end sil function 'multi_block_with_mutation'
sil [ossa] @multi_block_with_mutation : $@convention(thin) (@inout TwoFields, @guaranteed Child, @guaranteed Child) -> () {
bb0(%0 : $*TwoFields, %1 : @guaranteed $Child, %2 : @guaranteed $Child):
%3 = alloc_stack $Child
%4 = struct_element_addr %0, #TwoFields.a
cond_br undef, bb1, bb2
bb1:
%5 = copy_value %1
store %5 to [init] %3
br bb3
bb2:
%8 = copy_value %2
store %8 to [init] %3
%10 = copy_value %2
store %10 to [assign] %4
br bb3
bb3:
copy_addr [take] %3 to %4
dealloc_stack %3
%14 = tuple ()
return %14
}
// CHECK-LABEL: sil [ossa] @multi_block_with_multiple_deallocs :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: alloc_stack
// CHECK-LABEL: } // end sil function 'multi_block_with_multiple_deallocs'
sil [ossa] @multi_block_with_multiple_deallocs : $@convention(thin) (@inout TwoFields, @guaranteed Child, @guaranteed Child) -> () {
bb0(%0 : $*TwoFields, %1 : @guaranteed $Child, %2 : @guaranteed $Child):
%3 = alloc_stack $Child
%4 = struct_element_addr %0, #TwoFields.a
%5 = struct_element_addr %0, #TwoFields.b
cond_br undef, bb1, bb2
bb1:
%6 = copy_value %1
store %6 to [init] %3
copy_addr [take] %3 to %4
dealloc_stack %3
br bb3
bb2:
%8 = copy_value %2
store %8 to [init] %3
copy_addr [take] %3 to %5
dealloc_stack %3
br bb3
bb3:
%14 = tuple ()
return %14
}
// CHECK-LABEL: sil [ossa] @copy_addr_not_in_dealloc_block :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: alloc_stack
// CHECK-LABEL: } // end sil function 'copy_addr_not_in_dealloc_block'
sil [ossa] @copy_addr_not_in_dealloc_block : $@convention(thin) (@inout TwoFields, @guaranteed Child, @guaranteed Child) -> () {
bb0(%0 : $*TwoFields, %1 : @guaranteed $Child, %2 : @guaranteed $Child):
%3 = alloc_stack $Child
%4 = struct_element_addr %0, #TwoFields.a
%5 = struct_element_addr %0, #TwoFields.b
cond_br undef, bb1, bb2
bb1:
%6 = copy_value %1
store %6 to [init] %3
copy_addr [take] %3 to %4
br bb3
bb2:
%8 = copy_value %2
store %8 to [init] %3
copy_addr [take] %3 to %5
br bb3
bb3:
dealloc_stack %3
%14 = tuple ()
return %14
}
// CHECK-LABEL: sil [ossa] @empty :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: alloc_stack
// CHECK-LABEL: } // end sil function 'empty'
sil [ossa] @empty : $@convention(thin) () -> @out () {
bb0(%0 : $*()):
%1 = alloc_stack $()
copy_addr [take] %1 to %0
dealloc_stack %1
%4 = tuple ()
return %4
}
// CHECK-LABEL: sil [ossa] @load_store :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: [[A:%.*]] = struct_element_addr %0 : $*TwoFields, #TwoFields.a
// CHECK-NEXT: destroy_addr [[A]]
// CHECK-NEXT: store %1 to [init] [[A]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'load_store'
sil [ossa] @load_store : $@convention(thin) (@inout TwoFields, @owned Child) -> () {
bb0(%0 : $*TwoFields, %1 : @owned $Child):
%2 = alloc_stack $Child
store %1 to [init] %2
%4 = load [take] %2
dealloc_stack %2
%6 = struct_element_addr %0, #TwoFields.a
store %4 to [assign] %6
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @load_store_with_mutation :
// CHECK: alloc_stack
// CHECK-LABEL: } // end sil function 'load_store_with_mutation'
sil [ossa] @load_store_with_mutation : $@convention(thin) (@inout TwoFields, @owned Child, @owned TwoFields) -> () {
bb0(%0 : $*TwoFields, %1 : @owned $Child, %2 : @owned $TwoFields):
%3 = alloc_stack $Child
store %1 to [init] %3
%4 = load [take] %3
dealloc_stack %3
%6 = struct_element_addr %0, #TwoFields.a
store %2 to [assign] %0
store %4 to [assign] %6
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @load_store_trivial :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: store %1 to [trivial] %0
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'load_store_trivial'
sil [ossa] @load_store_trivial : $@convention(thin) (@inout Int, Int) -> () {
bb0(%0 : $*Int, %1 : $Int):
%2 = alloc_stack $Int
store %1 to [trivial] %2
%4 = load [trivial] %2
dealloc_stack %2
store %4 to [trivial] %0
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @copy_addr_trivial :
// CHECK: bb0({{.*}}):
// CHECK-NEXT: store %1 to [trivial] %0
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function 'copy_addr_trivial'
sil [ossa] @copy_addr_trivial : $@convention(thin) (@inout Int, Int) -> () {
bb0(%0 : $*Int, %1 : $Int):
%2 = alloc_stack $Int
store %1 to [trivial] %2
copy_addr %2 to %0
dealloc_stack %2
%8 = tuple ()
return %8
}
// CHECK-LABEL: sil [ossa] @projections_outside_liverange :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'projections_outside_liverange'
sil [ossa] @projections_outside_liverange : $@convention(thin) (@guaranteed __ContiguousArrayStorageBase) -> () {
bb0(%0 : @guaranteed $__ContiguousArrayStorageBase):
%1 = alloc_stack $Int
%2 = ref_element_addr %0, #__ContiguousArrayStorageBase.countAndCapacity
%3 = struct_element_addr %2, #_ArrayBody._storage
%4 = struct_element_addr %3, #_SwiftArrayBodyStorage.count
%5 = struct_element_addr %4, #Int._value
%6 = load [trivial] %5
%7 = struct $Int (%6)
store %7 to [trivial] %1
%9 = load [trivial] %1
store %9 to [trivial] %4
dealloc_stack %1
%12 = tuple ()
return %12
}
// CHECK-LABEL: sil [ossa] @destroy_before_copy :
// CHECK: bb1:
// CHECK-NEXT: destroy_addr
// CHECK-LABEL: } // end sil function 'destroy_before_copy'
sil [ossa] @destroy_before_copy : $@convention(thin) (@in_guaranteed Child) -> @out Child {
bb0(%0 : $*Child, %1 : $*Child):
%2 = alloc_stack $Child
copy_addr %1 to [init] %2
br bb1
bb1:
destroy_addr %2
copy_addr %1 to [init] %2
copy_addr %2 to [init] %0
br bb2
bb2:
copy_addr [take] %2 to %0
dealloc_stack %2
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_propagate_with_access_scope :
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK-LABEL: } // end sil function 'dont_propagate_with_access_scope'
sil [ossa] @dont_propagate_with_access_scope : $@convention(thin) (@inout Int, Int) -> () {
bb0(%0 : $*Int, %1 : $Int):
%2 = alloc_stack $Int
%3 = begin_access [modify] [dynamic] %2
store %1 to [trivial] %3
end_access %3
copy_addr %2 to %0
dealloc_stack %2
%8 = tuple ()
return %8
}