Files
swift-mirror/test/SILOptimizer/escape_analysis_invalidate.sil
Erik Eckstein c6b1e3e854 TempRValueElimination: re-implement the pass in swift
Beside cleaning up the source code, the motivation for the translation into Swift is to make it easier to improve the pass for some InlineArray specific optimizations (though I'm not sure, yet if we really need those).
Also, the new implementation doesn't contain the optimize-store-into-temp optimization anymore, because this is covered by redundant load elimination.
2025-05-06 13:08:09 +02:00

115 lines
6.7 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types %s -compute-escape-effects -compute-side-effects -temp-rvalue-elimination -enable-sil-verify-all -wmo | %FileCheck %s
// REQUIRES: swift_in_compiler
// TempRValue iteratively uses EscapeAnalysis and deletes
// instructions. Make sure that the connection graph remains valid
// <rdar://57290845>.
//
// This test requires -wmo so EscapeAnalysis can find all
// implementations of SomeProtocol.foo. Otherwise the existential
// address appears to escape. As an alternative, we could be more
// aggressive about considering address-type argument not to escape,
// but that would require some limiting address_to_pointer to never
// occur on an exclusive address argument.
import Swift
sil_stage canonical
protocol SomeProtocol {
func foo()
}
struct SomeInstance : SomeProtocol {
func foo()
}
class SomeClass {
var someProperty: SomeProtocol
}
struct SomeStruct {
var someRef: SomeClass
}
// CHECK-LABEL: sil @testTempRvalueEscapeAnalysisUpdate : $@convention(thin) (@in_guaranteed any SomeProtocol, @guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : $*any SomeProtocol, %1 : $SomeClass):
// CHECK: alloc_ref $SomeClass
// CHECK: alloc_stack $SomeStruct
// CHECK-NOT: alloc_stack $any SomeProtocol
// CHECK-NOT: copy_addr
// CHECK: init_existential_addr %{{.*}} : $*any SomeProtocol, $SomeInstance
// CHECK: [[OPEN1:%.*]] = open_existential_addr immutable_access %0 : $*any SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001122", any SomeProtocol) Self
// CHECK: apply %{{.*}}<@opened("6419340C-0B14-11EA-9897-ACDE48001122", any SomeProtocol) Self>([[OPEN1]]) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
// CHECK-NOT: destroy_addr
// CHECK-NOT: alloc_stack $any SomeProtocol
// CHECK-NOT: copy_addr
// CHECK: [[OPEN2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*any SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001123", any SomeProtocol) Self
// CHECK: apply %{{.*}}<@opened("6419340C-0B14-11EA-9897-ACDE48001123", any SomeProtocol) Self>([[OPEN2]]) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
// CHECK-NOT: destroy_addr
// CHECK-LABEL: } // end sil function 'testTempRvalueEscapeAnalysisUpdate'
sil @testTempRvalueEscapeAnalysisUpdate : $@convention(thin) (@in_guaranteed SomeProtocol, @guaranteed SomeClass) -> () {
bb0(%0 : $*SomeProtocol, %1 : $SomeClass):
// First create a uniquely identified protocol value. This way
// EscapeAnalysis canPointToSameMemory will kick in later. It can't
// be an exclusive argument, or AliasAnalysis will filter it before
// querying EscapeAnalysis.
%localRef = alloc_ref $SomeClass
%localPropAdr = ref_element_addr %localRef : $SomeClass, #SomeClass.someProperty
copy_addr %0 to [init] %localPropAdr : $*SomeProtocol
%stk0 = alloc_stack $SomeStruct
%stk0Ref = struct_element_addr %stk0 : $*SomeStruct, #SomeStruct.someRef
store %localRef to %stk0Ref : $*SomeClass
%indirectRef = load %stk0Ref : $*SomeClass
%indirectLocalPropAdr = ref_element_addr %indirectRef : $SomeClass, #SomeClass.someProperty
%propAdr1 = ref_element_addr %1 : $SomeClass, #SomeClass.someProperty
// TempRValue tries to kick in on this copy, but there is an
// interfering write that can't be handled by AliasAnalysis without
// consulting EscapingAnalysis. MemoryBehavior drops down to
// EscapeAnalysis for only a few special instructions, like
// init_existential_addr.
%stkAdr1 = alloc_stack $SomeProtocol
copy_addr %0 to [init] %stkAdr1 : $*SomeProtocol
%instanceAdr = init_existential_addr %indirectLocalPropAdr : $*SomeProtocol, $SomeInstance
%openadr1 = open_existential_addr immutable_access %stkAdr1 : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001122", SomeProtocol) Self
%witness1 = witness_method $@opened("6419340C-0B14-11EA-9897-ACDE48001122", SomeProtocol) Self, #SomeProtocol.foo : <Self where Self : SomeProtocol> (Self) -> () -> (), %openadr1 : $*@opened("6419340C-0B14-11EA-9897-ACDE48001122", SomeProtocol) Self : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
%call1 = apply %witness1<@opened("6419340C-0B14-11EA-9897-ACDE48001122", SomeProtocol) Self>(%openadr1) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
destroy_addr %stkAdr1 : $*SomeProtocol
// TempRValue optimization kicks in here. The open_existential_addr
// creates a content node that refers back to the dead stack
// location.
%stkAdr2 = alloc_stack $SomeProtocol
copy_addr %propAdr1 to [init] %stkAdr2 : $*SomeProtocol
%openadr2 = open_existential_addr immutable_access %stkAdr2 : $*SomeProtocol to $*@opened("6419340C-0B14-11EA-9897-ACDE48001123", SomeProtocol) Self
%witness2 = witness_method $@opened("6419340C-0B14-11EA-9897-ACDE48001123", SomeProtocol) Self, #SomeProtocol.foo : <Self where Self : SomeProtocol> (Self) -> () -> (), %openadr2 : $*@opened("6419340C-0B14-11EA-9897-ACDE48001123", SomeProtocol) Self : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
%call2 = apply %witness2<@opened("6419340C-0B14-11EA-9897-ACDE48001123", SomeProtocol) Self>(%openadr2) : $@convention(witness_method: SomeProtocol) <τ_0_0 where τ_0_0 : SomeProtocol> (@in_guaranteed τ_0_0) -> ()
destroy_addr %stkAdr2 : $*SomeProtocol
dealloc_stack %stkAdr2 : $*SomeProtocol
dealloc_stack %stkAdr1 : $*SomeProtocol
dealloc_stack %stk0 : $*SomeStruct
%v = tuple ()
return %v : $()
}
sil hidden @$s26escape_analysis_invalidate12SomeInstanceV3fooyyF : $@convention(method) (SomeInstance) -> () {
bb0(%0 : $SomeInstance):
debug_value %0 : $SomeInstance, let, name "self", argno 1 // id: %1
%2 = tuple () // user: %3
return %2 : $() // id: %3
}
sil private [transparent] [thunk] @$s26escape_analysis_invalidate12SomeInstanceVAA0A8ProtocolA2aDP3fooyyFTW : $@convention(witness_method: SomeProtocol) (@in_guaranteed SomeInstance) -> () {
bb0(%0 : $*SomeInstance):
%1 = load %0 : $*SomeInstance
// function_ref SomeInstance.foo()
%2 = function_ref @$s26escape_analysis_invalidate12SomeInstanceV3fooyyF : $@convention(method) (SomeInstance) -> ()
%3 = apply %2(%1) : $@convention(method) (SomeInstance) -> ()
%4 = tuple ()
return %4 : $()
}
sil_witness_table hidden SomeInstance: SomeProtocol module t {
method #SomeProtocol.foo: <Self where Self : SomeProtocol> (Self) -> () -> () : @$s26escape_analysis_invalidate12SomeInstanceVAA0A8ProtocolA2aDP3fooyyFTW // protocol witness for SomeProtocol.foo() in conformance SomeInstance
}