Files
swift-mirror/test/SILOptimizer/temp_rvalue_opt_ossa.sil
Erik Eckstein f9b524b1cb AliasAnalysis: a complete overhaul of alias- and memory-behavior analysis
The main changes are:

*) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification.

*) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely.

*) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`.

*) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope.

*) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
2024-07-29 17:33:46 +02:00

1879 lines
76 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -update-borrowed-from -temp-rvalue-opt | %FileCheck %s
// REQUIRES: swift_in_compiler
sil_stage canonical
import Builtin
import Swift
/////////////
// Utility //
/////////////
struct GS<Base> {
var _base: Base
var _value: Builtin.Int64
}
class Klass {
@_hasStorage var i: Int
init()
}
class OtherClass {
var klass: Klass
}
struct Two {
var a: Klass
var b: Klass
}
struct NonTrivialStruct {
var val:Klass
}
public enum FakeOptional<T> {
case none
case some(T)
}
struct MOS: ~Copyable {}
sil [ossa] @getKlass : $@convention(thin) () -> @owned Klass
sil [ossa] @getNonTrivialStruct : $@convention(thin) () -> @owned NonTrivialStruct
sil [ossa] @getMOS : $@convention(thin) () -> @owned MOS
sil @unknown : $@convention(thin) () -> ()
sil [ossa] @guaranteed_user : $@convention(thin) (@guaranteed Klass) -> ()
sil [ossa] @guaranteed_user_with_result : $@convention(thin) (@guaranteed Klass) -> @out Klass
sil [ossa] @inguaranteed_user_without_result_NTS : $@convention(thin) (@in_guaranteed NonTrivialStruct) -> ()
sil [ossa] @inguaranteed_user_without_result_MOS : $@convention(thin) (@in_guaranteed MOS) -> ()
sil [ossa] @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> () {
bb0(%0 : $*Klass):
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @inguaranteed_user_with_result : $@convention(thin) (@in_guaranteed Klass) -> @out Klass {
bb0(%0 : $*Klass, %1 : $*Klass):
copy_addr %1 to [init] %0 : $*Klass
%9999 = tuple()
return %9999 : $()
}
sil @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error)
sil @use_gsbase_builtinnativeobject : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
sil [readonly] @readonly_throwing_func : $@convention(thin) (@in_guaranteed Int) -> @error Error
sil_global @globalString : $String
///////////
// Tests //
///////////
// CHECK-LABEL: sil [ossa] @rvalue_simple
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>):
// CHECK: [[A1:%.*]] = struct_element_addr %0 : $*GS<B>, #GS._value
// CHECK: [[V1:%.*]] = load [trivial] [[A1]] : $*Builtin.Int64
// CHECK-NOT: alloc_stack
// CHECK-NOT: copy_addr
// CHECK: [[A2:%.*]] = struct_element_addr %1 : $*GS<B>, #GS._value
// CHECK: [[V2:%.*]] = load [trivial] [[A2]] : $*Builtin.Int64
// CHECK: %{{.*}} = builtin "cmp_slt_Int64"([[V1]] : $Builtin.Int64, [[V2]] : $Builtin.Int64) : $Builtin.Int1
// CHECK-NOT: destroy_addr
// CHECK-NOT: dealloc_stack
// CHECK: return %{{.*}} : $()
// CHECK-LABEL: } // end sil function 'rvalue_simple'
sil [ossa] @rvalue_simple : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>):
%2 = struct_element_addr %0 : $*GS<B>, #GS._value
%3 = load [trivial] %2 : $*Builtin.Int64
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%3 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @copy_from_temp
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: builtin
// CHECK-NEXT: copy_addr %1 to [init] %0 : $*GS<B>
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @copy_from_temp : $@convention(thin) <B> (@inout GS<B>, Builtin.Int64) -> @out GS<B> {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%8 = builtin "cmp_slt_Int64"(%2 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
copy_addr %4 to [init] %0 : $*GS<B>
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @copy_back_to_src
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>):
// CHECK-NEXT: struct_element_addr %1
// CHECK-NEXT: load
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @copy_back_to_src : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>):
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
copy_addr %4 to %1 : $*GS<B>
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @take_from_temp
// CHECK: bb0(%0 : $*B, %1 : $*GS<B>):
// CHECK-NEXT: [[STK:%.*]] = alloc_stack
// CHECK-NEXT: copy_addr %1 to [init] [[STK]]
// CHECK-NEXT: [[INNER:%.*]] = struct_element_addr
// CHECK-NEXT: copy_addr [take] [[INNER]]
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @take_from_temp : $@convention(thin) <B> (@inout B, @inout GS<B>) -> () {
bb0(%0 : $*B, %1 : $*GS<B>):
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%7 = struct_element_addr %4 : $*GS<B>, #GS._base
copy_addr [take] %7 to %0: $*B
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_before_load_take
// CHECK: [[STK:%[0-9]+]] = alloc_stack
// CHECK: copy_addr [take] %0 to [init] [[STK]]
// CHECK: store
// CHECK: load [take] [[STK]]
// CHECK: return
// CHECK: } // end sil function 'store_before_load_take'
sil [ossa] @store_before_load_take : $@convention(thin) (@inout Builtin.NativeObject, @owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $*Builtin.NativeObject, %1 : @owned $Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
copy_addr [take] %0 to [init] %stk : $*Builtin.NativeObject
store %1 to [init] %0 : $*Builtin.NativeObject
%obj = load [take] %stk : $*Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
return %obj : $Builtin.NativeObject
}
// CHECK-LABEL: sil [ossa] @copy_with_take_and_copy_from_src
// CHECK: bb0({{.*}}):
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: copy_addr [take] %1 to [init] %0
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK: } // end sil function 'copy_with_take_and_copy_from_src'
sil [ossa] @copy_with_take_and_copy_from_src : $@convention(thin) (@inout Builtin.NativeObject, @in Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
copy_addr [take] %0 to [init] %stk : $*Builtin.NativeObject
copy_addr [take] %1 to [init] %0 : $*Builtin.NativeObject
destroy_addr %stk : $*Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// CHECK-LABEL: sil [ossa] @copy_take_and_try_apply
// CHECK-NOT: copy_addr
// CHECK: try_apply {{%[0-9]+}}(%0)
// CHECK: bb1({{.*}}):
// CHECK: destroy_addr %0
// CHECK: bb2({{.*}}):
// CHECK: destroy_addr %0
// CHECK: bb3:
// CHECK: store %1 to [init] %0
// CHECK: } // end sil function 'copy_take_and_try_apply'
sil [ossa] @copy_take_and_try_apply : $@convention(thin) (@inout Klass, @owned Klass) -> () {
bb0(%0 : $*Klass, %1 : @owned $Klass):
%2 = alloc_stack $Klass
copy_addr [take] %0 to [init] %2 : $*Klass
%5 = function_ref @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error)
try_apply %5(%2) : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error), normal bb1, error bb2
bb1(%r : $()):
br bb3
bb2(%e : $Error):
br bb3
bb3:
store %1 to [init] %0 : $*Klass
destroy_addr %2 : $*Klass
dealloc_stack %2 : $*Klass
%9 = tuple ()
return %9 : $()
}
// Currently we cannot optimize this. But in theory it's possible to eliminate
// both copies.
//
// CHECK-LABEL: sil [ossa] @copy_and_copy_back
// CHECK: [[STK:%[0-9]+]] = alloc_stack
// CHECK: copy_addr [take] %0 to [init] [[STK]]
// CHECK: copy_addr [take] [[STK]] to [init] %0
// CHECK: } // end sil function 'copy_and_copy_back'
sil [ossa] @copy_and_copy_back : $@convention(thin) (@inout Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
copy_addr [take] %0 to [init] %stk : $*Builtin.NativeObject
copy_addr [take] %stk to [init] %0 : $*Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// CHECK-LABEL: sil [ossa] @dont_allow_copy_take_from_projection
// CHECK: [[STK:%[0-9]+]] = alloc_stack
// CHECK: copy_addr [take] %1 to [init] [[STK]]
// CHECK: } // end sil function 'dont_allow_copy_take_from_projection'
sil [ossa] @dont_allow_copy_take_from_projection : $@convention(thin) (@in Two) -> @out Two {
bb0(%0 : $*Two, %1 : $*Two):
%a0 = struct_element_addr %0 : $*Two, #Two.a
%b0 = struct_element_addr %0 : $*Two, #Two.b
%s = alloc_stack $Two
copy_addr [take] %1 to [init] %s : $*Two
%as = struct_element_addr %s : $*Two, #Two.a
%bs = struct_element_addr %s : $*Two, #Two.b
copy_addr [take] %as to [init] %a0 : $*Klass
copy_addr [take] %bs to [init] %b0 : $*Klass
dealloc_stack %s : $*Two
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @load_in_wrong_block
// CHECK: bb0(%0 : $*GS<B>):
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: copy_addr
// CHECK-NEXT: struct_element_addr
// CHECK-NEXT: br bb1
// CHECK: return
sil [ossa] @load_in_wrong_block : $@convention(thin) <B> (@in_guaranteed GS<B>) -> () {
bb0(%0 : $*GS<B>):
%4 = alloc_stack $GS<B>
copy_addr %0 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
br bb1
bb1:
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @projection_in_wrong_block
// CHECK: bb0(%0 : $*GS<B>):
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: copy_addr
// CHECK-NEXT: br bb1
// CHECK: return
sil [ossa] @projection_in_wrong_block : $@convention(thin) <B> (@in_guaranteed GS<B>) -> () {
bb0(%0 : $*GS<B>):
%4 = alloc_stack $GS<B>
copy_addr %0 to [init] %4 : $*GS<B>
br bb1
bb1:
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_after_load
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: [[A1:%.*]] = struct_element_addr %1
// CHECK-NEXT: [[A2:%.*]] = struct_element_addr %1
// CHECK-NEXT: load [trivial] [[A2]]
// CHECK-NEXT: store %2 to [trivial] [[A1]]
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @store_after_load : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>, Builtin.Int64) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%3 = struct_element_addr %1 : $*GS<B>, #GS._value
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
store %2 to [trivial] %3 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_after_two_loads
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: [[A1:%.*]] = struct_element_addr %1
// CHECK-NEXT: [[A2:%.*]] = struct_element_addr %1
// CHECK-NEXT: load [trivial] [[A2]]
// CHECK-NEXT: load [trivial] [[A2]]
// CHECK-NEXT: store %2 to [trivial] [[A1]]
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @store_after_two_loads : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>, Builtin.Int64) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%3 = struct_element_addr %1 : $*GS<B>, #GS._value
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = load [trivial] %6 : $*Builtin.Int64
store %2 to [trivial] %3 : $*Builtin.Int64
%9 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %8 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_before_load
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: struct_element_addr %1
// CHECK-NEXT: [[T:%.*]] = alloc_stack
// CHECK-NEXT: copy_addr %1 to [init] [[T]]
// CHECK-NEXT: [[A:%.*]] = struct_element_addr [[T]]
// CHECK-NEXT: store
// CHECK-NEXT: load [trivial] [[A]]
// CHECK-NEXT: builtin
// CHECK-NEXT: destroy_addr [[T]]
// CHECK-NEXT: dealloc_stack [[T]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @store_before_load : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>, Builtin.Int64) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%3 = struct_element_addr %1 : $*GS<B>, #GS._value
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
store %2 to [trivial] %3 : $*Builtin.Int64
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_between_loads
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: struct_element_addr %1
// CHECK-NEXT: [[T:%.*]] = alloc_stack
// CHECK-NEXT: copy_addr %1 to [init] [[T]]
// CHECK-NEXT: [[A:%.*]] = struct_element_addr [[T]]
// CHECK-NEXT: load [trivial] [[A]]
// CHECK-NEXT: store
// CHECK-NEXT: load [trivial] [[A]]
// CHECK-NEXT: builtin
// CHECK-NEXT: destroy_addr [[T]]
// CHECK-NEXT: dealloc_stack [[T]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @store_between_loads : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>, Builtin.Int64) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%3 = struct_element_addr %1 : $*GS<B>, #GS._value
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
store %2 to [trivial] %3 : $*Builtin.Int64
%8 = load [trivial] %6 : $*Builtin.Int64
%9 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %8 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @potential_store_before_load
// CHECK: bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
// CHECK-NEXT: struct_element_addr %1
// CHECK-NEXT: [[T:%.*]] = alloc_stack
// CHECK-NEXT: copy_addr %1 to [init] [[T]]
// CHECK-NEXT: [[A:%.*]] = struct_element_addr [[T]]
// CHECK: apply
// CHECK-NEXT: load [trivial] [[A]]
// CHECK-NEXT: builtin
// CHECK-NEXT: destroy_addr [[T]]
// CHECK-NEXT: dealloc_stack [[T]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @potential_store_before_load : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout_aliasable GS<B>, Builtin.Int64) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>, %2 : $Builtin.Int64):
%3 = struct_element_addr %1 : $*GS<B>, #GS._value
%4 = alloc_stack $GS<B>
copy_addr %1 to [init] %4 : $*GS<B>
%6 = struct_element_addr %4 : $*GS<B>, #GS._value
%f = function_ref @unknown : $@convention(thin) () -> ()
%a = apply %f() : $@convention(thin) () -> ()
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<B>
dealloc_stack %4 : $*GS<B>
%9999 = tuple()
return %9999 : $()
}
// Test temp RValue elimination on switches.
// CHECK-LABEL: sil [ossa] @rvalueSwitch
// CHECK: bb1:
// CHECK-NEXT: struct_element_addr %1
// CHECK-NEXT: load
// CHECK-NOT: alloc_stack $UnfoldSequence
// CHECK: return
sil [ossa] @rvalueSwitch : $@convention(method) <Element, State> (@inout UnfoldSequence<Element, State>) -> @out Optional<Element> {
bb0(%0 : $*Optional<Element>, %1 : $*UnfoldSequence<Element, State>):
%2 = struct_element_addr %1 : $*UnfoldSequence<Element, State>, #UnfoldSequence._done
%3 = struct_element_addr %2 : $*Bool, #Bool._value
%4 = load [trivial] %3 : $*Builtin.Int1
cond_br %4, bb4, bb1
bb1:
%6 = alloc_stack $UnfoldSequence<Element, State>
copy_addr %1 to [init] %6 : $*UnfoldSequence<Element, State>
%8 = struct_element_addr %6 : $*UnfoldSequence<Element, State>, #UnfoldSequence._next
%9 = load [copy] %8 : $*@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @out Optional<τ_0_1> for <State, Element>
%10 = alloc_stack $Optional<Element>
%11 = struct_element_addr %1 : $*UnfoldSequence<Element, State>, #UnfoldSequence._state
%13 = apply %9(%10, %11) : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @out Optional<τ_0_1> for <State, Element>
destroy_value %9 : $@callee_guaranteed @substituted <τ_0_0, τ_0_1> (@inout τ_0_0) -> @out Optional<τ_0_1> for <State, Element>
switch_enum_addr %10 : $*Optional<Element>, case #Optional.some!enumelt: bb3, case #Optional.none!enumelt: bb2
bb2:
destroy_addr %10 : $*Optional<Element>
dealloc_stack %10 : $*Optional<Element>
destroy_addr %6 : $*UnfoldSequence<Element, State>
dealloc_stack %6 : $*UnfoldSequence<Element, State>
%19 = integer_literal $Builtin.Int1, -1
%20 = struct $Bool (%19 : $Builtin.Int1)
store %20 to [trivial] %2 : $*Bool
%22 = alloc_stack $Optional<Element>
inject_enum_addr %22 : $*Optional<Element>, #Optional.none!enumelt
copy_addr [take] %22 to [init] %0 : $*Optional<Element>
dealloc_stack %22 : $*Optional<Element>
br bb5
bb3:
%27 = unchecked_take_enum_data_addr %10 : $*Optional<Element>, #Optional.some!enumelt
%28 = init_enum_data_addr %0 : $*Optional<Element>, #Optional.some!enumelt
copy_addr [take] %27 to [init] %28 : $*Element
dealloc_stack %10 : $*Optional<Element>
destroy_addr %6 : $*UnfoldSequence<Element, State>
dealloc_stack %6 : $*UnfoldSequence<Element, State>
inject_enum_addr %0 : $*Optional<Element>, #Optional.some!enumelt
br bb5
bb4:
%35 = alloc_stack $Optional<Element>
inject_enum_addr %35 : $*Optional<Element>, #Optional.none!enumelt
copy_addr [take] %35 to [init] %0 : $*Optional<Element>
dealloc_stack %35 : $*Optional<Element>
br bb5
bb5:
%40 = tuple ()
return %40 : $()
}
// Make sure that we can eliminate temporaries passed via a temporary rvalue to
// an @in_guaranteed function.
//
// CHECK-LABEL: sil [ossa] @inguaranteed_no_result : $@convention(thin) (@inout Klass) -> () {
// CHECK: bb0([[ARG:%.*]] : $*Klass):
// CHECK-NOT: copy_addr
// CHECK: apply {{%.*}}([[ARG]])
// CHECK-NOT: destroy_addr
// CHECK: } // end sil function 'inguaranteed_no_result'
sil [ossa] @inguaranteed_no_result : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = alloc_stack $Klass
copy_addr %0 to [init] %1 : $*Klass
%5 = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%6 = apply %5(%1) : $@convention(thin) (@in_guaranteed Klass) -> ()
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @try_apply_argument : $@convention(thin) (@inout Klass) -> () {
// CHECK-NOT: copy_addr
// CHECK: try_apply {{%[0-9]+}}(%0)
// CHECK: } // end sil function 'try_apply_argument'
sil [ossa] @try_apply_argument : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = alloc_stack $Klass
copy_addr %0 to [init] %1 : $*Klass
%5 = function_ref @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error)
try_apply %5(%1) : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error), normal bb1, error bb2
bb1(%r : $()):
br bb3
bb2(%e : $Error):
br bb3
bb3:
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// Make sure that we can eliminate temporaries passed via a temporary rvalue to
// an @in_guaranteed function.
//
// CHECK-LABEL: sil [ossa] @inguaranteed_with_result : $@convention(thin) (@inout Klass) -> () {
// CHECK: bb0([[ARG:%.*]] : $*Klass):
// dead temp
// CHECK: [[TMP_OUT:%.*]] = alloc_stack $Klass
// CHECK-NOT: copy_addr
// CHECK: apply {{%.*}}([[TMP_OUT]], [[ARG]])
// CHECK-NOT: copy_addr
// CHECK: destroy_addr [[TMP_OUT]]
// CHECK-NOT: destroy_addr
// CHECK: } // end sil function 'inguaranteed_with_result'
sil [ossa] @inguaranteed_with_result : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%1 = alloc_stack $Klass
%1a = alloc_stack $Klass
copy_addr %0 to [init] %1 : $*Klass
%5 = function_ref @inguaranteed_user_with_result : $@convention(thin) (@in_guaranteed Klass) -> @out Klass
%6 = apply %5(%1a, %1) : $@convention(thin) (@in_guaranteed Klass) -> @out Klass
destroy_addr %1a : $*Klass
destroy_addr %1 : $*Klass
dealloc_stack %1a : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil [ossa] @non_overlapping_lifetime : $@convention(thin) (@in Klass) -> () {
// CHECK: bb0([[ARG:%.*]] : $*Klass):
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $Klass
// CHECK-NEXT: copy_addr [[ARG]] to [init] [[TMP]]
// CHECK-NEXT: destroy_addr [[ARG]]
// CHECK: apply %{{[0-9]*}}([[TMP]])
// CHECK-NEXT: destroy_addr [[TMP]]
// CHECK-NEXT: dealloc_stack [[TMP]]
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-NEXT: } // end sil function 'non_overlapping_lifetime'
sil [ossa] @non_overlapping_lifetime : $@convention(thin) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1a = alloc_stack $Klass
%1 = alloc_stack $Klass
%2 = alloc_stack $Klass
copy_addr %0 to [init] %2 : $*Klass
copy_addr [take] %2 to [init] %1 : $*Klass
dealloc_stack %2 : $*Klass
copy_addr %1 to [init] %1a : $*Klass
destroy_addr %0 : $*Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%3 = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %3(%1a) : $@convention(thin) (@in_guaranteed Klass) -> ()
destroy_addr %1a : $*Klass
dealloc_stack %1a : $*Klass
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @$createKlass : $@convention(thin) () -> @out Klass
sil [ossa] @$appendKlass : $@convention(method) (@in_guaranteed Klass, @inout Klass) -> ()
// CHECK-LABEL: sil [ossa] @$overlapping_lifetime_in_function_all : $@convention(thin) () -> @out Klass {
// CHECK: [[S1:%.*]] = alloc_stack $Klass
// CHECK: [[S2:%.*]] = alloc_stack $Klass
// CHECK: copy_addr [[S1]] to [init] [[S2]]
// CHECK: apply {{%.*}}([[S2]], [[S1]])
// CHECK: }
sil [ossa] @$overlapping_lifetime_in_function_all : $@convention(thin) () -> @out Klass {
bb0(%0 : $*Klass):
%1 = alloc_stack $Klass
%2 = function_ref @$createKlass : $@convention(thin) () -> @out Klass
%3 = apply %2(%1) : $@convention(thin) () -> @out Klass
%4 = alloc_stack $Klass
copy_addr %1 to [init] %4 : $*Klass
%6 = function_ref @$appendKlass : $@convention(method) (@in_guaranteed Klass, @inout Klass) -> ()
%7 = apply %6(%4, %1) : $@convention(method) (@in_guaranteed Klass, @inout Klass) -> ()
destroy_addr %4 : $*Klass
dealloc_stack %4 : $*Klass
copy_addr [take] %1 to [init] %0 : $*Klass
dealloc_stack %1 : $*Klass
%12 = tuple ()
return %12 : $()
}
protocol P {
func foo()
}
sil [ossa] @getP : $@convention(thin) () -> @out Optional<P>
// CHECK-LABEL: sil [ossa] @handle_open_existential_addr : $@convention(thin) () -> () {
// CHECK: [[P:%.*]] = unchecked_take_enum_data_addr
// CHECK-NOT: copy_addr
// CHECK: open_existential_addr immutable_access [[P]]
// CHECK: }
sil [ossa] @handle_open_existential_addr : $@convention(thin) () -> () {
bb0:
%2 = alloc_stack $Optional<P>
%3 = function_ref @getP : $@convention(thin) () -> @out Optional<P>
%4 = apply %3(%2) : $@convention(thin) () -> @out Optional<P>
cond_br undef, bb1, bb3
bb1:
%9 = unchecked_take_enum_data_addr %2 : $*Optional<P>, #Optional.some!enumelt
%10 = alloc_stack $P
copy_addr %9 to [init] %10 : $*P
%13 = open_existential_addr immutable_access %10 : $*P to $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self
%14 = witness_method $@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self, #P.foo : <Self where Self : P> (Self) -> () -> (), %13 : $*@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
%15 = apply %14<@opened("5E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
destroy_addr %2 : $*Optional<P>
destroy_addr %10 : $*P
dealloc_stack %10 : $*P
dealloc_stack %2 : $*Optional<P>
br bb2
bb2:
%23 = tuple ()
return %23 : $()
bb3:
destroy_addr %2 : $*Optional<P>
dealloc_stack %2 : $*Optional<P>
br bb2
}
// CHECK-LABEL: sil [ossa] @open_existential_addr_blocks_optimization : $@convention(thin) () -> () {
// CHECK: [[P:%.*]] = alloc_stack $any P
// CHECK: copy_addr {{.*}} to [init] [[P]]
// CHECK: }
sil [ossa] @open_existential_addr_blocks_optimization : $@convention(thin) () -> () {
bb0:
%2 = alloc_stack $Optional<P>
%3 = function_ref @getP : $@convention(thin) () -> @out Optional<P>
%4 = apply %3(%2) : $@convention(thin) () -> @out Optional<P>
cond_br undef, bb1, bb3
bb1:
%9 = unchecked_take_enum_data_addr %2 : $*Optional<P>, #Optional.some!enumelt
%10 = alloc_stack $P
copy_addr %9 to [init] %10 : $*P
destroy_addr %2 : $*Optional<P>
%13 = open_existential_addr immutable_access %10 : $*P to $*@opened("6E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self
%14 = witness_method $@opened("6E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self, #P.foo : <Self where Self : P> (Self) -> () -> (), %13 : $*@opened("6E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
%15 = apply %14<@opened("6E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
destroy_addr %10 : $*P
dealloc_stack %10 : $*P
dealloc_stack %2 : $*Optional<P>
br bb2
bb2:
%23 = tuple ()
return %23 : $()
bb3:
destroy_addr %2 : $*Optional<P>
dealloc_stack %2 : $*Optional<P>
br bb2
}
// CHECK-LABEL: sil [ossa] @witness_method_blocks_optimization : $@convention(thin) () -> () {
// CHECK: [[P:%.*]] = alloc_stack $any P
// CHECK: copy_addr {{.*}} to [init] [[P]]
// CHECK: }
sil [ossa] @witness_method_blocks_optimization : $@convention(thin) () -> () {
bb0:
%2 = alloc_stack $Optional<P>
%3 = function_ref @getP : $@convention(thin) () -> @out Optional<P>
%4 = apply %3(%2) : $@convention(thin) () -> @out Optional<P>
cond_br undef, bb1, bb3
bb1:
%9 = unchecked_take_enum_data_addr %2 : $*Optional<P>, #Optional.some!enumelt
%10 = alloc_stack $P
copy_addr %9 to [init] %10 : $*P
%13 = open_existential_addr immutable_access %10 : $*P to $*@opened("7E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self
destroy_addr %2 : $*Optional<P>
%14 = witness_method $@opened("7E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self, #P.foo : <Self where Self : P> (Self) -> () -> (), %13 : $*@opened("7E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
%15 = apply %14<@opened("7E7A6328-EF75-11E9-A383-D0817AD3F637", P) Self>(%13) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
destroy_addr %10 : $*P
dealloc_stack %10 : $*P
dealloc_stack %2 : $*Optional<P>
br bb2
bb2:
%23 = tuple ()
return %23 : $()
bb3:
destroy_addr %2 : $*Optional<P>
dealloc_stack %2 : $*Optional<P>
br bb2
}
sil [ossa] @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
// Now that we support ossa, eliminate the alloc_stack and change the load
// [take] to a load [copy] in the process.
//
// CHECK-LABEL: sil [ossa] @copyWithLoadRelease : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
// CHECK: bb0(%0 : $*Builtin.NativeObject):
// CHECK-NOT: alloc_stack
// CHECK: [[VAL:%.*]] = load [copy] %0 : $*Builtin.NativeObject
// CHECK: apply %{{.*}}([[VAL]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
// CHECK: destroy_value [[VAL]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'copyWithLoadRelease'
sil [ossa] @copyWithLoadRelease : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
copy_addr %0 to [init] %stk : $*Builtin.NativeObject
%obj = load [take] %stk : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// Remove a copy that is released via a load. Leave the load [take] alone since
// our copy_addr is taking from source.
//
// CHECK-LABEL: sil [ossa] @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () {
// CHECK: bb0(%0 : $*Builtin.NativeObject):
// CHECK: [[V:%.*]] = load [take] %0 : $*Builtin.NativeObject
// CHECK: apply %{{.*}}([[V]]) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
// CHECK: destroy_value [[V]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'takeWithLoadRelease'
sil [ossa] @takeWithLoadRelease : $@convention(thin) (@in Builtin.NativeObject) -> () {
bb0(%0 : $*Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
copy_addr [take] %0 to [init] %stk : $*Builtin.NativeObject
%obj = load [take] %stk : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// Do not remove a copy that is released via a load of a projection. This is not
// the pattern from SILGen that we are targeting, so we reduce the state space by banning the pattern.
//
// CHECK-LABEL: sil [ossa] @takeWithLoadReleaseOfProjection : $@convention(thin) (@in GS<Builtin.NativeObject>) -> () {
// CHECK: alloc_stack
// CHECK: } // end sil function 'takeWithLoadReleaseOfProjection'
sil [ossa] @takeWithLoadReleaseOfProjection : $@convention(thin) (@in GS<Builtin.NativeObject>) -> () {
bb0(%0 : $*GS<Builtin.NativeObject>):
%stk = alloc_stack $GS<Builtin.NativeObject>
copy_addr [take] %0 to [init] %stk : $*GS<Builtin.NativeObject>
%proj = struct_element_addr %stk : $*GS<Builtin.NativeObject>, #GS._base
%obj = load [take] %proj : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*GS<Builtin.NativeObject>
%v = tuple ()
return %v : $()
}
// Make sure that when we convert the load [take] to a load [copy], we hoist the
// load of src /before/ the destroy of %0.
// CHECK-LABEL: sil [ossa] @hoist_load_copy_to_src_copy_addr_site : $@convention(thin) (@owned GS<Builtin.NativeObject>) -> @owned GS<Builtin.NativeObject> {
// CHECK: bb0([[ARG:%.*]] : @owned
// CHECK: apply {{%.*}}([[ARG]])
// CHECK: return [[ARG]]
// CHECK: } // end sil function 'hoist_load_copy_to_src_copy_addr_site'
sil [ossa] @hoist_load_copy_to_src_copy_addr_site : $@convention(thin) (@owned GS<Builtin.NativeObject>) -> @owned GS<Builtin.NativeObject> {
bb0(%0 : @owned $GS<Builtin.NativeObject>):
%f = function_ref @use_gsbase_builtinnativeobject : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
%stk = alloc_stack $GS<Builtin.NativeObject>
store %0 to [init] %stk : $*GS<Builtin.NativeObject>
%obj = load [take] %stk : $*GS<Builtin.NativeObject>
dealloc_stack %stk : $*GS<Builtin.NativeObject>
apply %f(%obj) : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
return %obj : $GS<Builtin.NativeObject>
}
// CHECK-LABEL: sil [ossa] @dont_optimize_with_load_in_different_block
// CHECK: [[STK:%[0-9]+]] = alloc_stack
// CHECK: copy_addr %0 to [init] [[STK]]
// CHECK: bb1:
// CHECK: load [take] [[STK]]
// CHECK: bb2:
// CHECK: copy_addr %1 to %0
// CHECK: load [take] [[STK]]
// CHECK: } // end sil function 'dont_optimize_with_load_in_different_block'
sil [ossa] @dont_optimize_with_load_in_different_block : $@convention(thin) (@inout GS<Builtin.NativeObject>, @in_guaranteed GS<Builtin.NativeObject>) -> @owned GS<Builtin.NativeObject> {
bb0(%0 : $*GS<Builtin.NativeObject>, %1 : $*GS<Builtin.NativeObject>):
%f = function_ref @use_gsbase_builtinnativeobject : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
%stk = alloc_stack $GS<Builtin.NativeObject>
copy_addr %0 to [init] %stk : $*GS<Builtin.NativeObject>
cond_br undef, bb1, bb2
bb1:
%obj = load [take] %stk : $*GS<Builtin.NativeObject>
br bb3(%obj : $GS<Builtin.NativeObject>)
bb2:
copy_addr %1 to %0 : $*GS<Builtin.NativeObject>
%obj2 = load [take] %stk : $*GS<Builtin.NativeObject>
br bb3(%obj2 : $GS<Builtin.NativeObject>)
bb3(%obj3 : @owned $GS<Builtin.NativeObject>):
dealloc_stack %stk : $*GS<Builtin.NativeObject>
apply %f(%obj3) : $@convention(thin) (@guaranteed GS<Builtin.NativeObject>) -> ()
return %obj3 : $GS<Builtin.NativeObject>
}
// CHECK-LABEL: sil [ossa] @extendAccessScopeOverLoad
// CHECK: [[GLOBAL:%.*]] = global_addr @globalString
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL]]
// CHECK: [[LOAD:%.*]] = load [copy] [[ACCESS]]
// CHECK: end_access [[ACCESS]]
// CHECK: return [[LOAD]]
// CHECK-LABEL: } // end sil function 'extendAccessScopeOverLoad'
sil [ossa] @extendAccessScopeOverLoad : $@convention(thin) () -> @owned String {
bb0:
%1 = global_addr @globalString : $*String
%3 = begin_access [read] [dynamic] %1 : $*String
%4 = alloc_stack $String
copy_addr %3 to [init] %4 : $*String
end_access %3 : $*String
%6 = load [copy] %4 : $*String
destroy_addr %4 : $*String
dealloc_stack %4 : $*String
return %6 : $String
}
sil [ossa] @loadString : $@convention(thin) (@in_guaranteed String) -> @owned String {
[%0: read v**]
[global: ]
bb0(%0 : $*String):
%1 = load [copy] %0 : $*String
return %1 : $String
}
// CHECK-LABEL: sil [ossa] @extendAccessScopeOverApply
// CHECK: [[GLOBAL:%.*]] = global_addr @globalString
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[GLOBAL]]
// CHECK: [[RESULT:%.*]] = apply {{%[0-9]+}}([[ACCESS]])
// CHECK: end_access [[ACCESS]]
// CHECK: return [[RESULT]]
// CHECK-LABEL: } // end sil function 'extendAccessScopeOverApply'
sil [ossa] @extendAccessScopeOverApply : $@convention(thin) () -> @owned String {
bb0:
%1 = global_addr @globalString : $*String
%3 = begin_access [read] [dynamic] %1 : $*String
%4 = alloc_stack $String
copy_addr %3 to [init] %4 : $*String
end_access %3 : $*String
%f = function_ref @loadString : $@convention(thin) (@in_guaranteed String) -> @owned String
%a = apply %f(%4) : $@convention(thin) (@in_guaranteed String) -> @owned String
destroy_addr %4 : $*String
dealloc_stack %4 : $*String
return %a : $String
}
// CHECK-LABEL: sil [ossa] @dontExtendModifyAccess
// CHECK: begin_access
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: copy_addr
// CHECK-NEXT: end_access
// CHECK-NEXT: load
// CHECK-LABEL: } // end sil function 'dontExtendModifyAccess'
sil [ossa] @dontExtendModifyAccess : $@convention(thin) () -> @owned String {
bb0:
%1 = global_addr @globalString : $*String
%3 = begin_access [modify] [dynamic] %1 : $*String
%4 = alloc_stack $String
copy_addr %3 to [init] %4 : $*String
end_access %3 : $*String
%6 = load [copy] %4 : $*String
destroy_addr %4 : $*String
dealloc_stack %4 : $*String
return %6 : $String
}
// CHECK-LABEL: sil [ossa] @dontExtendAccessScopeOverEndAccess
// CHECK: begin_access [read] [dynamic] %0 : $*Int
// CHECK-NEXT: begin_access [read] [dynamic] %{{[0-9]+}} : $*String
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: copy_addr
// CHECK-NEXT: end_access %{{[0-9]+}} : $*String
// CHECK-NEXT: end_access %{{[0-9]+}} : $*Int
// CHECK-NEXT: load
// CHECK-LABEL: } // end sil function 'dontExtendAccessScopeOverEndAccess'
sil [ossa] @dontExtendAccessScopeOverEndAccess : $@convention(thin) (@in_guaranteed Int) -> @owned String {
bb0(%0 : $*Int):
%1 = global_addr @globalString : $*String
%2 = begin_access [read] [dynamic] %0 : $*Int
%3 = begin_access [read] [dynamic] %1 : $*String
%4 = alloc_stack $String
copy_addr %3 to [init] %4 : $*String
end_access %3 : $*String
end_access %2 : $*Int
%6 = load [copy] %4 : $*String
destroy_addr %4 : $*String
dealloc_stack %4 : $*String
return %6 : $String
}
// CHECK-LABEL: sil [ossa] @dontExtendAccessScopeOverBeginAccess : $@convention(thin) (@in Klass) -> () {
// CHECK: bb0(%0 : $*Klass):
// CHECK: [[STACK:%.*]] = alloc_stack $Klass
// CHECK: [[ACCESS:%.*]] = begin_access [read] [static] [[STACK]] : $*Klass
// CHECK: apply %{{.*}}([[ACCESS]]) : $@convention(thin) (@in_guaranteed Klass) -> ()
// CHECK: destroy_addr [[STACK]] : $*Klass
// CHECK-LABEL: } // end sil function 'dontExtendAccessScopeOverBeginAccess'
sil [ossa] @dontExtendAccessScopeOverBeginAccess : $@convention(thin) (@in Klass) -> () {
bb0(%0 : $*Klass):
%stack = alloc_stack $Klass
%access = begin_access [read] [static] %0 : $*Klass
copy_addr [take] %access to [init] %stack : $*Klass
end_access %access : $*Klass
%f = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%access2 = begin_access [read] [static] %stack : $*Klass
%call = apply %f(%access2) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_access %access2 : $*Klass
destroy_addr %stack : $*Klass
dealloc_stack %stack : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @dont_optimize_with_modify_inside_access
// CHECK: [[STK:%[0-9]+]] = alloc_stack $Klass
// CHECK: copy_addr %0 to [init] [[STK]]
// CHECK: begin_access [read] [static] [[STK]]
// CHECK-LABEL: } // end sil function 'dont_optimize_with_modify_inside_access'
sil [ossa] @dont_optimize_with_modify_inside_access : $@convention(thin) (@inout Klass, @owned Klass) -> () {
bb0(%0 : $*Klass, %1 : @owned $Klass):
%stack = alloc_stack $Klass
copy_addr %0 to [init] %stack : $*Klass
%f = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%access = begin_access [read] [static] %stack : $*Klass
store %1 to [assign] %0 : $*Klass // This store prevents the optimization
%call = apply %f(%access) : $@convention(thin) (@in_guaranteed Klass) -> ()
end_access %access : $*Klass
destroy_addr %stack : $*Klass
dealloc_stack %stack : $*Klass
%9999 = tuple()
return %9999 : $()
}
// Just check that we don't crash here.
// Currently this pattern is not optimized, but we might in future.
sil [ossa] @dont_extend_access_scope_over_term_inst : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = ref_element_addr %0 : $Klass, #Klass.i
%2 = begin_access [read] [dynamic] %1 : $*Int
%3 = alloc_stack $Int
copy_addr %2 to [init] %3 : $*Int
end_access %2 : $*Int
%6 = function_ref @readonly_throwing_func : $@convention(thin) (@in_guaranteed Int) -> @error Error
try_apply %6(%3) : $@convention(thin) (@in_guaranteed Int) -> @error Error, normal bb1, error bb2
bb1(%8 : $()):
destroy_addr %3 : $*Int
dealloc_stack %3 : $*Int
br bb3
bb2(%12 : @owned $Error):
destroy_addr %3 : $*Int
dealloc_stack %3 : $*Int
destroy_value %12 : $Error
br bb3
bb3:
%17 = tuple ()
return %17 : $()
}
/////////////////
// Store Tests //
/////////////////
// We do not support this today while we are bringing up store support.
//
// CHECK-LABEL: sil [ossa] @store_rvalue_simple
// CHECK: alloc_stack
// CHECK-LABEL: } // end sil function 'store_rvalue_simple'
sil [ossa] @store_rvalue_simple : $@convention(thin) (@in_guaranteed GS<Klass>, @owned GS<Klass>) -> () {
bb0(%0 : $*GS<Klass>, %1 : @owned $GS<Klass>):
%2 = struct_element_addr %0 : $*GS<Klass>, #GS._value
%3 = load [trivial] %2 : $*Builtin.Int64
%4 = alloc_stack $GS<Klass>
store %1 to [init] %4 : $*GS<Klass>
%6 = struct_element_addr %4 : $*GS<Klass>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%3 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<Klass>
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_copy_from_temp :
// CHECK: bb0([[RESULT:%.*]] : $*GS<Klass>, [[ARG0:%.*]] : @owned $GS<Klass>,
// CHECK: builtin
// CHECK: [[ARG0_COPY:%.*]] = copy_value [[ARG0]]
// CHECK: store [[ARG0_COPY]] to [init] [[RESULT]]
// CHECK: destroy_value [[ARG0]]
// CHECK: } // end sil function 'store_copy_from_temp'
sil [ossa] @store_copy_from_temp : $@convention(thin) (@owned GS<Klass>, Builtin.Int64) -> @out GS<Klass> {
bb0(%0 : $*GS<Klass>, %1 : @owned $GS<Klass>, %2 : $Builtin.Int64):
%4 = alloc_stack $GS<Klass>
store %1 to [init] %4 : $*GS<Klass>
%8 = builtin "cmp_slt_Int64"(%2 : $Builtin.Int64, %2 : $Builtin.Int64) : $Builtin.Int1
copy_addr %4 to [init] %0 : $*GS<Klass>
destroy_addr %4 : $*GS<Klass>
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// We do not support this today.
//
// CHECK-LABEL: sil [ossa] @store_take_from_temp :
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_take_from_temp'
sil [ossa] @store_take_from_temp : $@convention(thin) (@inout Klass, @owned GS<Klass>) -> () {
bb0(%0 : $*Klass, %1 : @owned $GS<Klass>):
%4 = alloc_stack $GS<Klass>
store %1 to [init] %4 : $*GS<Klass>
%7 = struct_element_addr %4 : $*GS<Klass>, #GS._base
copy_addr [take] %7 to %0 : $*Klass
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_load_in_wrong_block :
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_load_in_wrong_block'
sil [ossa] @store_load_in_wrong_block : $@convention(thin) (@guaranteed GS<Klass>) -> () {
bb0(%0 : @guaranteed $GS<Klass>):
%4 = alloc_stack $GS<Klass>
%1 = copy_value %0 : $GS<Klass>
store %1 to [init] %4 : $*GS<Klass>
%6 = struct_element_addr %4 : $*GS<Klass>, #GS._value
br bb1
bb1:
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<Klass>
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_projection_in_wrong_block :
// CHECK: bb0(%0 : @guaranteed $GS<Klass>):
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_projection_in_wrong_block'
sil [ossa] @store_projection_in_wrong_block : $@convention(thin) (@guaranteed GS<Klass>) -> () {
bb0(%0 : @guaranteed $GS<Klass>):
%4 = alloc_stack $GS<Klass>
%0a = copy_value %0 : $GS<Klass>
store %0a to [init] %4 : $*GS<Klass>
br bb1
bb1:
%6 = struct_element_addr %4 : $*GS<Klass>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<Klass>
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @store_store_after_load :
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_store_after_load'
sil [ossa] @store_store_after_load : $@convention(thin) (@guaranteed GS<Klass>, Builtin.Int64) -> () {
bb0(%1 : @guaranteed $GS<Klass>, %2 : $Builtin.Int64):
%3 = struct_extract %1 : $GS<Klass>, #GS._value
%4 = alloc_stack $GS<Klass>
%1a = copy_value %1 : $GS<Klass>
store %1a to [init] %4 : $*GS<Klass>
%6 = struct_element_addr %4 : $*GS<Klass>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%7 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*GS<Klass>
dealloc_stack %4 : $*GS<Klass>
%9999 = tuple()
return %9999 : $()
}
// TODO: We do not support this today due to the in_guaranteed parameter. Maybe
// instead we use store_borrow just around the call site?
//
// Make sure that we can eliminate temporaries passed via a temporary rvalue to
// an @guaranteed function.
//
// CHECK-LABEL: sil [ossa] @store_inguaranteed_no_result : $@convention(thin) (@guaranteed Klass) -> () {
// CHECK: bb0([[ARG:%.*]] : @guaranteed $Klass):
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_inguaranteed_no_result'
sil [ossa] @store_inguaranteed_no_result : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack $Klass
%0a = copy_value %0 : $Klass
store %0a to [init] %1 : $*Klass
%5 = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%6 = apply %5(%1) : $@convention(thin) (@in_guaranteed Klass) -> ()
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// We do not support this today since we need to make it so that we can use
// store_borrow to pass to the in_guaranteed function.
//
// CHECK-LABEL: sil [ossa] @store_try_apply_argument : $@convention(thin) (@guaranteed Klass) -> () {
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_try_apply_argument'
sil [ossa] @store_try_apply_argument : $@convention(thin) (@guaranteed Klass) -> () {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack $Klass
%0copy = copy_value %0 : $Klass
store %0copy to [init] %1 : $*Klass
%5 = function_ref @throwing_function : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error)
try_apply %5(%1) : $@convention(thin) (@in_guaranteed Klass) -> ((), @error Error), normal bb1, error bb2
bb1(%r : $()):
br bb3
bb2(%e : $Error):
br bb3
bb3:
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// TODO: We need to support using store_borrow to shrink the lifetime here.
//
// Make sure that we can eliminate temporaries passed via a temporary rvalue to
// an @guaranteed function.
//
// CHECK-LABEL: sil [ossa] @store_inguaranteed_with_result : $@convention(thin) (@owned Klass) -> () {
// CHECK: bb0([[ARG:%.*]] : @owned $Klass):
// CHECK: alloc_stack
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_inguaranteed_with_result'
sil [ossa] @store_inguaranteed_with_result : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = alloc_stack $Klass
%1a = alloc_stack $Klass
store %0 to [init] %1 : $*Klass
%5 = function_ref @inguaranteed_user_with_result : $@convention(thin) (@in_guaranteed Klass) -> @out Klass
%6 = apply %5(%1a, %1) : $@convention(thin) (@in_guaranteed Klass) -> @out Klass
destroy_addr %1a : $*Klass
destroy_addr %1 : $*Klass
dealloc_stack %1a : $*Klass
dealloc_stack %1 : $*Klass
%9 = tuple ()
return %9 : $()
}
// TODO: Once we are able to use store_borrow to shrink lifetimes, we will have
// no alloc_stack in this function.
//
// CHECK-LABEL: sil [ossa] @store_non_overlapping_lifetime : $@convention(thin) (@owned Klass) -> () {
// CHECK: = alloc_stack
// CHECK-NOT: = alloc_stack
// CHECK: } // end sil function 'store_non_overlapping_lifetime'
sil [ossa] @store_non_overlapping_lifetime : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1a = alloc_stack $Klass
%1 = alloc_stack $Klass
%2 = alloc_stack $Klass
%0a = copy_value %0 : $Klass
store %0a to [init] %2 : $*Klass
copy_addr [take] %2 to [init] %1 : $*Klass
dealloc_stack %2 : $*Klass
copy_addr %1 to [init] %1a : $*Klass
destroy_value %0 : $Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%3 = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
apply %3(%1a) : $@convention(thin) (@in_guaranteed Klass) -> ()
destroy_addr %1a : $*Klass
dealloc_stack %1a : $*Klass
%9999 = tuple()
return %9999 : $()
}
sil [ossa] @store_$createKlass : $@convention(thin) () -> @out Klass
sil [ossa] @store_$appendKlass : $@convention(method) (@guaranteed Klass, @inout Klass) -> ()
// TODO: With time we should be able to shrink the lifetime of the first
// argument here with time.
//
// CHECK-LABEL: sil [ossa] @store_overlapping_lifetime_in_function_all : $@convention(thin) () -> @out Klass {
// CHECK: [[S1:%.*]] = alloc_stack $Klass
// CHECK: [[S2:%.*]] = alloc_stack $Klass
// CHECK: [[S1_LOADED:%.*]] = load [copy] [[S1]]
// CHECK: store [[S1_LOADED]] to [init] [[S2]]
// CHECK: apply {{%.*}}([[S2]], [[S1]])
// CHECK: } // end sil function 'store_overlapping_lifetime_in_function_all'
sil [ossa] @store_overlapping_lifetime_in_function_all : $@convention(thin) () -> @out Klass {
bb0(%0 : $*Klass):
%1 = alloc_stack $Klass
%2 = function_ref @$createKlass : $@convention(thin) () -> @out Klass
%3 = apply %2(%1) : $@convention(thin) () -> @out Klass
%4 = alloc_stack $Klass
%2a = load [copy] %1 : $*Klass
store %2a to [init] %4 : $*Klass
%6 = function_ref @$appendKlass : $@convention(method) (@in_guaranteed Klass, @inout Klass) -> ()
%7 = apply %6(%4, %1) : $@convention(method) (@in_guaranteed Klass, @inout Klass) -> ()
destroy_addr %4 : $*Klass
dealloc_stack %4 : $*Klass
copy_addr [take] %1 to [init] %0 : $*Klass
dealloc_stack %1 : $*Klass
%12 = tuple ()
return %12 : $()
}
sil [ossa] @store_getP : $@convention(thin) () -> @out Optional<P>
sil [ossa] @store_takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
// Now that we support ossa, eliminate the alloc_stack and change the load
// [take] to a load [copy] in the process.
//
// CHECK-LABEL: sil [ossa] @store_copyWithLoadRelease : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
// CHECK: bb0(%0 : @guaranteed $Builtin.NativeObject):
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'store_copyWithLoadRelease'
sil [ossa] @store_copyWithLoadRelease : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @guaranteed $Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
%0copy = copy_value %0 : $Builtin.NativeObject
store %0copy to [init] %stk : $*Builtin.NativeObject
%obj = load [take] %stk : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// Remove a copy that is released via a load. Leave the load [take] alone since
// our copy_addr is taking from source.
//
// CHECK-LABEL: sil [ossa] @store_takeWithLoadRelease : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'store_takeWithLoadRelease'
sil [ossa] @store_takeWithLoadRelease : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
%stk = alloc_stack $Builtin.NativeObject
store %0 to [init] %stk : $*Builtin.NativeObject
%obj = load [take] %stk : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*Builtin.NativeObject
%v = tuple ()
return %v : $()
}
// Do not remove a copy that is released via a load of a projection. This is not
// the pattern from SILGen that we are targeting, so we reduce the state space by banning the pattern.
//
// CHECK-LABEL: sil [ossa] @store_takeWithLoadReleaseOfProjection : $@convention(thin) (@owned GS<Builtin.NativeObject>) -> () {
// CHECK: alloc_stack
// CHECK: } // end sil function 'store_takeWithLoadReleaseOfProjection'
sil [ossa] @store_takeWithLoadReleaseOfProjection : $@convention(thin) (@owned GS<Builtin.NativeObject>) -> () {
bb0(%0 : @owned $GS<Builtin.NativeObject>):
%stk = alloc_stack $GS<Builtin.NativeObject>
store %0 to [init] %stk : $*GS<Builtin.NativeObject>
%proj = struct_element_addr %stk : $*GS<Builtin.NativeObject>, #GS._base
%obj = load [take] %proj : $*Builtin.NativeObject
%f = function_ref @takeGuaranteedObj : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
%call = apply %f(%obj) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
destroy_value %obj : $Builtin.NativeObject
dealloc_stack %stk : $*GS<Builtin.NativeObject>
%v = tuple ()
return %v : $()
}
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum1
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'test_optimize_store_of_enum1'
sil [ossa] @test_optimize_store_of_enum1 : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
bb0(%0 : @guaranteed $Optional<Klass>):
%1 = copy_value %0 : $Optional<Klass>
%32 = alloc_stack $Optional<Klass>
store %1 to [init] %32 : $*Optional<Klass>
switch_enum %0 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
bb5:
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb6(%50 : @guaranteed $Klass):
%53 = load [take] %32 : $*Optional<Klass>
destroy_value %53 : $Optional<Klass>
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb7:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_optimize_store_of_enum2
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'test_optimize_store_of_enum2'
sil [ossa] @test_optimize_store_of_enum2 : $@convention(method) <Element> (@owned Optional<Klass>) -> () {
bb0(%0 : @owned $Optional<Klass>):
%1 = copy_value %0 : $Optional<Klass>
%32 = alloc_stack $Optional<Klass>
store %0 to [init] %32 : $*Optional<Klass>
switch_enum %1 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
bb5:
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb6(%50 : @owned $Klass):
%53 = load [take] %32 : $*Optional<Klass>
destroy_value %53 : $Optional<Klass>
destroy_value %50 : $Klass
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb7:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @store_of_enum_must_be_in_same_block
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'store_of_enum_must_be_in_same_block'
sil [ossa] @store_of_enum_must_be_in_same_block : $@convention(method) <Element> (@guaranteed Optional<Klass>) -> () {
bb0(%0 : @guaranteed $Optional<Klass>):
%32 = alloc_stack $Optional<Klass>
switch_enum %0 : $Optional<Klass>, case #Optional.some!enumelt: bb6, case #Optional.none!enumelt: bb5
bb5:
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb6(%50 : @guaranteed $Klass):
%1 = copy_value %0 : $Optional<Klass>
store %1 to [init] %32 : $*Optional<Klass>
%53 = load [take] %32 : $*Optional<Klass>
destroy_value %53 : $Optional<Klass>
dealloc_stack %32 : $*Optional<Klass>
br bb7
bb7:
%r = tuple ()
return %r : $()
}
////////////////////////////////////////
// Unchecked Take Enum Data Addr Inst //
////////////////////////////////////////
// Make sure we only handle this in the copy_addr case. With time, we should
// also handle the store case.
//
// CHECK-LABEL: sil [ossa] @unchecked_take_enum_data_addr_rvalue_simple : $@convention(thin) <B> (@in_guaranteed Optional<GS<B>>, @inout Optional<GS<B>>) -> () {
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'unchecked_take_enum_data_addr_rvalue_simple'
sil [ossa] @unchecked_take_enum_data_addr_rvalue_simple : $@convention(thin) <B> (@in_guaranteed Optional<GS<B>>, @inout Optional<GS<B>>) -> () {
bb0(%0 : $*Optional<GS<B>>, %1 : $*Optional<GS<B>>):
%0a = unchecked_take_enum_data_addr %0 : $*Optional<GS<B>>, #Optional.some!enumelt
%2 = struct_element_addr %0a : $*GS<B>, #GS._value
%3 = load [trivial] %2 : $*Builtin.Int64
%4 = alloc_stack $Optional<GS<B>>
copy_addr %1 to [init] %4 : $*Optional<GS<B>>
%4a = unchecked_take_enum_data_addr %4 : $*Optional<GS<B>>, #Optional.some!enumelt
%6 = struct_element_addr %4a : $*GS<B>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%3 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*Optional<GS<B>>
dealloc_stack %4 : $*Optional<GS<B>>
%9999 = tuple()
return %9999 : $()
}
// We do not support this today, since I am still bringing up store support.
//
// CHECK-LABEL: sil [ossa] @unchecked_take_enum_data_addr_store_rvalue_simple : $@convention(thin) (@in_guaranteed Optional<GS<Klass>>, @owned Optional<GS<Klass>>) -> () {
// CHECK: alloc_stack
// CHECK: } // end sil function 'unchecked_take_enum_data_addr_store_rvalue_simple'
sil [ossa] @unchecked_take_enum_data_addr_store_rvalue_simple : $@convention(thin) (@in_guaranteed Optional<GS<Klass>>, @owned Optional<GS<Klass>>) -> () {
bb0(%0 : $*Optional<GS<Klass>>, %1 : @owned $Optional<GS<Klass>>):
%0a = unchecked_take_enum_data_addr %0 : $*Optional<GS<Klass>>, #Optional.some!enumelt
%2 = struct_element_addr %0a : $*GS<Klass>, #GS._value
%3 = load [trivial] %2 : $*Builtin.Int64
%4 = alloc_stack $Optional<GS<Klass>>
store %1 to [init] %4 : $*Optional<GS<Klass>>
%4a = unchecked_take_enum_data_addr %4 : $*Optional<GS<Klass>>, #Optional.some!enumelt
%6 = struct_element_addr %4a : $*GS<Klass>, #GS._value
%7 = load [trivial] %6 : $*Builtin.Int64
%8 = builtin "cmp_slt_Int64"(%3 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
destroy_addr %4 : $*Optional<GS<Klass>>
dealloc_stack %4 : $*Optional<GS<Klass>>
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @eliminate_fix_lifetime_on_dest_copyaddr : $@convention(thin) (@inout Klass) -> () {
// CHECK-NOT: alloc_stack
// CHECK: fix_lifetime %0
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'eliminate_fix_lifetime_on_dest_copyaddr'
sil [ossa] @eliminate_fix_lifetime_on_dest_copyaddr : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%3 = alloc_stack $Klass
copy_addr %0 to [init] %3 : $*Klass
fix_lifetime %3 : $*Klass
destroy_addr %3 : $*Klass
dealloc_stack %3 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil [ossa] @eliminate_fix_lifetime_on_dest_store : $@convention(thin) (@inout Klass) -> () {
// CHECK-NOT: alloc_stack
// CHECK: [[VALUE:%.*]] = load [copy] %0
// CHECK-NEXT: fix_lifetime [[VALUE]]
// CHECK-NEXT: destroy_value [[VALUE]]
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'eliminate_fix_lifetime_on_dest_store'
sil [ossa] @eliminate_fix_lifetime_on_dest_store : $@convention(thin) (@inout Klass) -> () {
bb0(%0 : $*Klass):
%2 = load [copy] %0 : $*Klass
%3 = alloc_stack $Klass
store %2 to [init] %3 : $*Klass
fix_lifetime %3 : $*Klass
destroy_addr %3 : $*Klass
dealloc_stack %3 : $*Klass
%9999 = tuple()
return %9999 : $()
}
// Check that we don't crash with this.
// CHECK-LABEL: sil [ossa] @unhandled_user : $@convention(thin) (@owned Klass) -> @owned Klass {
// CHECK: alloc_stack
// CHECK: store
// CHECK: begin_access
// CHECK: load
// CHECK: } // end sil function 'unhandled_user'
sil [ossa] @unhandled_user : $@convention(thin) (@owned Klass) -> @owned Klass {
bb0(%0 : @owned $Klass):
%5 = alloc_stack $Klass
store %0 to [init] %5 : $*Klass
%104 = begin_access [read] [static] %5 : $*Klass
%105 = load [take] %104 : $*Klass
end_access %104 : $*Klass
dealloc_stack %5 : $*Klass
return %105 : $Klass
}
// CHECK-LABEL: sil [ossa] @test_yield
// CHECK: [[TA:%[0-9]+]] = ref_tail_addr
// CHECK-NOT: copy_addr
// CHECK: yield [[TA]]
// CHECK: } // end sil function 'test_yield'
sil [ossa] @test_yield : $@yield_once @convention(thin) (@guaranteed Klass) -> @yields @in_guaranteed Two {
bb0(%0 : @guaranteed $Klass):
%1 = alloc_stack $Two
%2 = ref_tail_addr [immutable] %0 : $Klass, $Two
copy_addr %2 to [init] %1 : $*Two
yield %1 : $*Two, resume bb1, unwind bb2
bb1:
destroy_addr %1 : $*Two
dealloc_stack %1 : $*Two
%90 = tuple ()
return %90 : $()
bb2:
destroy_addr %1 : $*Two
dealloc_stack %1 : $*Two
unwind
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_load_take : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getKlass
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result
// CHECK: [[INSTANCE_1:%[^,]+]] = apply [[GET]]()
// CHECK: [[ADDR:%[^,]+]] = alloc_stack $Klass
// CHECK: store [[INSTANCE_1]] to [init] [[ADDR]]
// CHECK: apply [[USER]]([[ADDR]])
// CHECK: [[INSTANCE_2:%[^,]+]] = load [take] [[ADDR]]
// CHECK: dealloc_stack [[ADDR]]
// CHECK: return [[INSTANCE_2]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_load_take'
sil [ossa] @take_from_original_copy_addr__final_use_load_take : $() -> @owned Klass {
%getKlass = function_ref @getKlass : $@convention(thin) () -> @owned Klass
%user = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%instance_1 = apply %getKlass() : $@convention(thin) () -> @owned Klass
%src = alloc_stack $Klass
store %instance_1 to [init] %src : $*Klass
apply %user(%src) : $@convention(thin) (@in_guaranteed Klass) -> ()
%tmp = alloc_stack $Klass
copy_addr [take] %src to [init] %tmp : $*Klass
%instance_2 = load [take] %tmp : $*Klass
dealloc_stack %tmp : $*Klass
dealloc_stack %src : $*Klass
return %instance_2 : $Klass
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_copy_addr_take : {{.*}} {
// CHECK: {{bb[0-9]+}}([[OUT:%[^,]+]] :
// CHECK: [[GET:%[^,]+]] = function_ref @getKlass
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result
// CHECK: [[INSTANCE_1:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $Klass
// CHECK: store [[INSTANCE_1]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: copy_addr [take] [[SRC]] to [init] [[OUT]]
// CHECK: dealloc_stack [[SRC]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_copy_addr_take'
sil [ossa] @take_from_original_copy_addr__final_use_copy_addr_take : $() -> @out Klass {
entry(%out : $*Klass):
%getKlass = function_ref @getKlass : $@convention(thin) () -> @owned Klass
%user = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%instance_1 = apply %getKlass() : $@convention(thin) () -> @owned Klass
%src = alloc_stack $Klass
store %instance_1 to [init] %src : $*Klass
apply %user(%src) : $@convention(thin) (@in_guaranteed Klass) -> ()
%tmp = alloc_stack $Klass
copy_addr [take] %src to [init] %tmp : $*Klass
copy_addr [take] %tmp to [init] %out : $*Klass
dealloc_stack %tmp : $*Klass
dealloc_stack %src : $*Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_field_load_copy : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getNonTrivialStruct
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result_NTS
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $NonTrivialStruct
// CHECK: store [[INSTANCE]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: [[FIELD_ADDR:%[^,]+]] = struct_element_addr [[SRC]]
// CHECK: [[FIELD:%[^,]+]] = load [copy] [[FIELD_ADDR]]
// CHECK: destroy_addr [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK: return [[FIELD]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_field_load_copy'
sil [ossa] @take_from_original_copy_addr__final_use_field_load_copy : $() -> @owned Klass {
%get = function_ref @getNonTrivialStruct : $@convention(thin) () -> @owned NonTrivialStruct
%user = function_ref @inguaranteed_user_without_result_NTS : $@convention(thin) (@in_guaranteed NonTrivialStruct) -> ()
%instance_1 = apply %get() : $@convention(thin) () -> @owned NonTrivialStruct
%src = alloc_stack $NonTrivialStruct
store %instance_1 to [init] %src : $*NonTrivialStruct
apply %user(%src) : $@convention(thin) (@in_guaranteed NonTrivialStruct) -> ()
%tmp = alloc_stack $NonTrivialStruct
copy_addr [take] %src to [init] %tmp : $*NonTrivialStruct
%field_addr = struct_element_addr %tmp : $*NonTrivialStruct, #NonTrivialStruct.val
%field = load [copy] %field_addr : $*Klass
destroy_addr %tmp : $*NonTrivialStruct
dealloc_stack %tmp : $*NonTrivialStruct
dealloc_stack %src : $*NonTrivialStruct
return %field : $Klass
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_field_copy_addr_take : {{.*}} {
// CHECK: {{bb[0-9]+}}([[OUT:%[^,]+]] :
// CHECK: [[GET:%[^,]+]] = function_ref @getNonTrivialStruct
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result_NTS
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $NonTrivialStruct
// CHECK: store [[INSTANCE]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: [[FIELD_ADDR:%[^,]+]] = struct_element_addr [[SRC]]
// CHECK: copy_addr [[FIELD_ADDR]] to [init] [[OUT]]
// CHECK: destroy_addr [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_field_copy_addr_take'
sil [ossa] @take_from_original_copy_addr__final_use_field_copy_addr_take : $() -> @out Klass {
entry(%out : $*Klass):
%getNonTrivialStruct = function_ref @getNonTrivialStruct : $@convention(thin) () -> @owned NonTrivialStruct
%user = function_ref @inguaranteed_user_without_result_NTS : $@convention(thin) (@in_guaranteed NonTrivialStruct) -> ()
%instance_1 = apply %getNonTrivialStruct() : $@convention(thin) () -> @owned NonTrivialStruct
%src = alloc_stack $NonTrivialStruct
store %instance_1 to [init] %src : $*NonTrivialStruct
apply %user(%src) : $@convention(thin) (@in_guaranteed NonTrivialStruct) -> ()
%tmp = alloc_stack $NonTrivialStruct
copy_addr [take] %src to [init] %tmp : $*NonTrivialStruct
%field_addr = struct_element_addr %tmp : $*NonTrivialStruct, #NonTrivialStruct.val
copy_addr %field_addr to [init] %out : $*Klass
destroy_addr %tmp : $*NonTrivialStruct
dealloc_stack %tmp : $*NonTrivialStruct
dealloc_stack %src : $*NonTrivialStruct
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_load_copy : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getKlass
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result
// CHECK: [[INSTANCE_1:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $Klass
// CHECK: store [[INSTANCE_1]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: [[INSTANCE_2:%[^,]+]] = load [copy] [[SRC]]
// CHECK: destroy_addr [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK: return [[INSTANCE_2]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_load_copy'
sil [ossa] @take_from_original_copy_addr__final_use_load_copy : $() -> @owned Klass {
%getKlass = function_ref @getKlass : $@convention(thin) () -> @owned Klass
%user = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%instance_1 = apply %getKlass() : $@convention(thin) () -> @owned Klass
%src = alloc_stack $Klass
store %instance_1 to [init] %src : $*Klass
apply %user(%src) : $@convention(thin) (@in_guaranteed Klass) -> ()
%tmp = alloc_stack $Klass
copy_addr [take] %src to [init] %tmp : $*Klass
%instance_2 = load [copy] %tmp : $*Klass
destroy_addr %tmp : $*Klass
dealloc_stack %tmp : $*Klass
dealloc_stack %src : $*Klass
return %instance_2 : $Klass
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_copy_addr_copy : {{.*}} {
// CHECK: {{bb[0-9]+}}([[OUT:%[^,]+]] : $*Klass):
// CHECK: [[GET:%[^,]+]] = function_ref @getKlass
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $Klass
// CHECK: store [[INSTANCE]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: copy_addr [[SRC]] to [init] [[OUT]]
// CHECK: destroy_addr [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_copy_addr_copy'
sil [ossa] @take_from_original_copy_addr__final_use_copy_addr_copy : $() -> @out Klass {
entry(%out : $*Klass):
%getKlass = function_ref @getKlass : $@convention(thin) () -> @owned Klass
%user = function_ref @inguaranteed_user_without_result : $@convention(thin) (@in_guaranteed Klass) -> ()
%instance_1 = apply %getKlass() : $@convention(thin) () -> @owned Klass
%src = alloc_stack $Klass
store %instance_1 to [init] %src : $*Klass
apply %user(%src) : $@convention(thin) (@in_guaranteed Klass) -> ()
%tmp = alloc_stack $Klass
copy_addr [take] %src to [init] %tmp : $*Klass
copy_addr %tmp to [init] %out : $*Klass
destroy_addr %tmp : $*Klass
dealloc_stack %tmp : $*Klass
dealloc_stack %src : $*Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @take_from_original_copy_addr__final_use_apply__move_only : {{.*}} {
// CHECK: [[GET:%[^,]+]] = function_ref @getMOS
// CHECK: [[USER:%[^,]+]] = function_ref @inguaranteed_user_without_result_MOS
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET]]()
// CHECK: [[SRC:%[^,]+]] = alloc_stack $MOS
// CHECK: store [[INSTANCE]] to [init] [[SRC]]
// CHECK: apply [[USER]]([[SRC]])
// CHECK: apply [[USER]]([[SRC]])
// CHECK: destroy_addr [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK-LABEL: } // end sil function 'take_from_original_copy_addr__final_use_apply__move_only'
sil [ossa] @take_from_original_copy_addr__final_use_apply__move_only : $() -> () {
%getMOS = function_ref @getMOS : $@convention(thin) () -> @owned MOS
%user = function_ref @inguaranteed_user_without_result_MOS : $@convention(thin) (@in_guaranteed MOS) -> ()
%instance_1 = apply %getMOS() : $@convention(thin) () -> @owned MOS
%src = alloc_stack $MOS
store %instance_1 to [init] %src : $*MOS
apply %user(%src) : $@convention(thin) (@in_guaranteed MOS) -> ()
%tmp = alloc_stack $MOS
copy_addr [take] %src to [init] %tmp : $*MOS
apply %user(%tmp) : $@convention(thin) (@in_guaranteed MOS) -> ()
destroy_addr %tmp : $*MOS
dealloc_stack %tmp : $*MOS
dealloc_stack %src : $*MOS
%tuple = tuple ()
return %tuple : $()
}
// CHECK-LABEL: sil [ossa] @test_temprvoborrowboundary1 :
// CHECK-NOT: alloc_stack
// CHECK-NOT: copy_addr
// CHECK: [[ADDR:%.*]] = unchecked_take_enum_data_addr %0 : $*FakeOptional<NonTrivialStruct>, #FakeOptional.some!enumelt
// CHECK: [[ELE:%.*]] = struct_element_addr [[ADDR]] : $*NonTrivialStruct, #NonTrivialStruct.val
// CHECK: [[LD:%.*]] = load_borrow [[ELE]] : $*Klass
// CHECK: end_borrow [[LD]] : $Klass
// CHECK: destroy_addr [[ADDR]]
// CHECK: } // end sil function 'test_temprvoborrowboundary1'
sil [ossa] @test_temprvoborrowboundary1 : $@convention(thin) (@in FakeOptional<NonTrivialStruct>) -> () {
bb0(%0 : $*FakeOptional<NonTrivialStruct>):
%1 = alloc_stack $NonTrivialStruct
%2 = unchecked_take_enum_data_addr %0 : $*FakeOptional<NonTrivialStruct>, #FakeOptional.some!enumelt
copy_addr [take] %2 to [init] %1 : $*NonTrivialStruct
%4 = struct_element_addr %1 : $*NonTrivialStruct, #NonTrivialStruct.val
%5 = load_borrow %4 : $*Klass
end_borrow %5 : $Klass
destroy_addr %1 : $*NonTrivialStruct
dealloc_stack %1 : $*NonTrivialStruct
%res = tuple ()
return %res : $()
}
// This does not get optimized because of the end borrow in a different block
// CHECK-LABEL: sil [ossa] @test_temprvoborrowboundary2 :
// CHECK: copy_addr
// CHECK: } // end sil function 'test_temprvoborrowboundary2'
sil [ossa] @test_temprvoborrowboundary2 : $@convention(thin) (@in FakeOptional<NonTrivialStruct>) -> () {
bb0(%0 : $*FakeOptional<NonTrivialStruct>):
%1 = alloc_stack $NonTrivialStruct
%2 = unchecked_take_enum_data_addr %0 : $*FakeOptional<NonTrivialStruct>, #FakeOptional.some!enumelt
copy_addr [take] %2 to [init] %1 : $*NonTrivialStruct
%4 = struct_element_addr %1 : $*NonTrivialStruct, #NonTrivialStruct.val
%5 = load_borrow %4 : $*Klass
br bb1(%5 : $Klass)
bb1(%6 : @guaranteed $Klass):
end_borrow %6 : $Klass
destroy_addr %1 : $*NonTrivialStruct
dealloc_stack %1 : $*NonTrivialStruct
%res = tuple ()
return %res : $()
}
// Lexical alloc_stacks can still be eliminated if their sources are guaranteed
// function arguments (because those are lexical and have a live range that
// contains the live range of the value stored into the alloc stack).
// CHECK-LABEL: sil [ossa] @copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg : {{.*}} {
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg'
sil [ossa] @copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg : $@convention(thin) (@guaranteed OtherClass) -> () {
entry(%instance : @guaranteed $OtherClass):
%field_ptr = ref_element_addr %instance : $OtherClass, #OtherClass.klass
%addr = alloc_stack [lexical] $Klass
copy_addr %field_ptr to [init] %addr : $*Klass
destroy_addr %addr : $*Klass
dealloc_stack %addr : $*Klass
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @copy_addr__lexical_alloc_stack_temporary_in_guaranteed_function_arg : {{.*}} {
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'copy_addr__lexical_alloc_stack_temporary_in_guaranteed_function_arg'
sil [ossa] @copy_addr__lexical_alloc_stack_temporary_in_guaranteed_function_arg : $@convention(thin) (@in_guaranteed Klass) -> () {
entry(%instance : $*Klass):
%addr = alloc_stack [lexical] $Klass
copy_addr %instance to [init] %addr : $*Klass
destroy_addr %addr : $*Klass
dealloc_stack %addr : $*Klass
%retval = tuple ()
return %retval : $()
}
// Lexical alloc_stacks can't be eliminated even if their source is a function
// argument if that function argument is inout.
// CHECK-LABEL: sil [ossa] @copy_addr__lexical_alloc_stack_temporary__inout_function_arg : {{.*}} {
// CHECK: alloc_stack [lexical]
// CHECK-LABEL: } // end sil function 'copy_addr__lexical_alloc_stack_temporary__inout_function_arg'
sil [ossa] @copy_addr__lexical_alloc_stack_temporary__inout_function_arg : $@convention(thin) (@inout NonTrivialStruct) -> () {
entry(%instance : $*NonTrivialStruct):
%field_ptr = struct_element_addr %instance : $*NonTrivialStruct, #NonTrivialStruct.val
%addr = alloc_stack [lexical] $Klass
copy_addr %field_ptr to [init] %addr : $*Klass
destroy_addr %addr : $*Klass
dealloc_stack %addr : $*Klass
%retval = tuple ()
return %retval : $()
}
// Verify that no copy of an instance of the move-only type MOS is introduced.
// CHECK-LABEL: sil hidden [ossa] @dont_copy_move_only_struct : {{.*}} {
// CHECK: [[SRC:%[^,]+]] = alloc_stack $MOS
// CHECK: [[GET:%[^,]+]] = function_ref @getMOS
// CHECK: [[INSTANCE_1:%[^,]+]] = apply [[GET]]()
// CHECK: store [[INSTANCE_1]] to [init] [[SRC]]
// CHECK: [[INSTANCE_2:%[^,]+]] = load [take] [[SRC]]
// CHECK: store [[INSTANCE_2]] to [init] [[SRC]]
// CHECK: [[INSTANCE_3:%[^,]+]] = load [take] [[SRC]]
// CHECK: dealloc_stack [[SRC]]
// CHECK: return [[INSTANCE_3]]
// CHECK-LABEL: } // end sil function 'dont_copy_move_only_struct'
sil hidden [ossa] @dont_copy_move_only_struct : $@convention(thin) () -> @owned MOS {
bb0:
%src = alloc_stack $MOS
%getMOS = function_ref @getMOS : $@convention(thin) () -> @owned MOS
%instance_1 = apply %getMOS() : $@convention(thin) () -> @owned MOS
store %instance_1 to [init] %src : $*MOS
%tmp = alloc_stack $MOS
copy_addr [take] %src to [init] %tmp : $*MOS
%instance_2 = load [take] %tmp : $*MOS
store %instance_2 to [init] %src : $*MOS
%instance_3 = load [take] %src : $*MOS
dealloc_stack %tmp : $*MOS
dealloc_stack %src : $*MOS
return %instance_3 : $MOS
}
// CHECK-LABEL: sil [ossa] @dont_optimize_use_before_copy :
// CHECK: copy_addr
// CHECK-LABEL: } // end sil function 'dont_optimize_use_before_copy'
sil [ossa] @dont_optimize_use_before_copy : $@convention(thin) <B> (@in_guaranteed GS<B>, @inout GS<B>) -> () {
bb0(%0 : $*GS<B>, %1 : $*GS<B>):
%2 = alloc_stack $GS<B>
%3 = struct_element_addr %2 : $*GS<B>, #GS._value
copy_addr %1 to [init] %2 : $*GS<B>
%5 = load [trivial] %3 : $*Builtin.Int64
%6 = builtin "cmp_slt_Int64"(%5 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int1
copy_addr %2 to %1 : $*GS<B>
destroy_addr %2 : $*GS<B>
dealloc_stack %2 : $*GS<B>
%10 = tuple ()
return %10 : $()
}