// RUN: %target-sil-opt -sil-print-types %s -compute-escape-effects -compute-side-effects -temp-rvalue-opt -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 // . // // 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) -> () -> (), %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) -> () -> (), %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) -> () -> () : @$s26escape_analysis_invalidate12SomeInstanceVAA0A8ProtocolA2aDP3fooyyFTW // protocol witness for SomeProtocol.foo() in conformance SomeInstance }