@@ -38,18 +38,51 @@ let tempRValueElimination = FunctionPass(name: "temp-rvalue-elimination") {
3838 ( function: Function , context: FunctionPassContext ) in
3939
4040 for inst in function. instructions {
41- if let copyAddr = inst as? CopyAddrInst {
42- tryEliminate ( copy: copyAddr, context)
41+ switch inst {
42+ case let copy as CopyAddrInst :
43+ if copy. source == copy. destination {
44+ // Remove identity copies which may have been created by an earlier iteration, where another `copy_addr`
45+ // copied the `alloc_stack` back to the source location.
46+ context. erase ( instruction: copy)
47+ } else {
48+ tryEliminate ( copy: copy, context)
49+ }
50+ case let store as StoreInst :
51+ // Also handle `load`-`store` pairs which are basically the same thing as a `copy_addr`.
52+ if let load = store. source as? LoadInst , load. uses. isSingleUse, load. parentBlock == store. parentBlock {
53+ tryEliminate ( copy: store, context)
54+ }
55+ default :
56+ break
4357 }
4458 }
4559}
4660
47- private func tryEliminate( copy: CopyAddrInst , _ context: FunctionPassContext ) {
48- // Remove identity copies which may have been created by an earlier iteration, where another `copy_addr`
49- // copied the `alloc_stack` back to the source location.
50- if copy. source == copy. destination {
51- context. erase ( instruction: copy)
52- }
61+ private protocol CopyLikeInstruction : Instruction {
62+ var sourceAddress : Value { get }
63+ var destinationAddress : Value { get }
64+ var isTakeOfSource : Bool { get }
65+ var isInitializationOfDestination : Bool { get }
66+ var loadingInstruction : Instruction { get }
67+ }
68+
69+ extension CopyAddrInst : CopyLikeInstruction {
70+ var sourceAddress : Value { source }
71+ var destinationAddress : Value { destination }
72+ var loadingInstruction : Instruction { self }
73+ }
74+
75+ // A `store` which has a `load` as source operand. This is basically the same as a `copy_addr`.
76+ extension StoreInst : CopyLikeInstruction {
77+ var sourceAddress : Value { load. address }
78+ var destinationAddress : Value { destination }
79+ var isTakeOfSource : Bool { load. loadOwnership == . take }
80+ var isInitializationOfDestination : Bool { storeOwnership != . assign }
81+ var loadingInstruction : Instruction { load }
82+ private var load : LoadInst { source as! LoadInst }
83+ }
84+
85+ private func tryEliminate( copy: CopyLikeInstruction , _ context: FunctionPassContext ) {
5386
5487 guard let ( allocStack, lastUseOfAllocStack) = getRemovableAllocStackDestination ( of: copy, context) else {
5588 return
@@ -61,7 +94,7 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
6194
6295 if needToInsertDestroy ( copy: copy, lastUseOfAllocStack: lastUseOfAllocStack) {
6396 Builder . insert ( after: lastUseOfAllocStack, context) { builder in
64- builder. createDestroyAddr ( address: copy. source )
97+ builder. createDestroyAddr ( address: copy. sourceAddress )
6598 }
6699 }
67100
@@ -76,25 +109,25 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
76109 assert ( cai == lastUseOfAllocStack && cai. source == allocStack)
77110 cai. set ( isTakeOfSource: false , context)
78111 }
79- use. set ( to: copy. source , context)
112+ use. set ( to: copy. sourceAddress , context)
80113 case let load as LoadInst :
81114 if load. loadOwnership == . take, !copy. isTakeOfSource {
82115 // If the original copy is not taking its source, a `load` from the `allocStack` must
83116 // not take its source either.
84117 assert ( load == lastUseOfAllocStack)
85118 load. set ( ownership: . copy, context)
86119 }
87- use. set ( to: copy. source , context) ;
120+ use. set ( to: copy. sourceAddress , context) ;
88121
89122 default :
90123 // Note that no operations that may be handled by this default clause can destroy the `allocStack`.
91124 // This includes operations that load the value from memory and release it or cast the address
92125 // before destroying it.
93- use. set ( to: copy. source , context) ;
126+ use. set ( to: copy. sourceAddress , context) ;
94127 }
95128 }
96129 context. erase ( instruction: allocStack)
97- context. erase ( instruction : copy)
130+ context. erase ( instructionIncludingAllUsers : copy. loadingInstruction )
98131}
99132
100133/// Checks if the `copy` is copying into an `alloc_stack` which is removable:
@@ -105,17 +138,17 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
105138/// %lastUseOfAllocStack = load %allocStack
106139/// ```
107140private func getRemovableAllocStackDestination(
108- of copy: CopyAddrInst , _ context: FunctionPassContext
141+ of copy: CopyLikeInstruction , _ context: FunctionPassContext
109142) -> ( allocStack: AllocStackInst , lastUseOfAllocStack: Instruction ) ? {
110143 guard copy. isInitializationOfDestination,
111- let allocStack = copy. destination as? AllocStackInst
144+ let allocStack = copy. destinationAddress as? AllocStackInst
112145 else {
113146 return nil
114147 }
115148
116149 // If the `allocStack` is lexical we can eliminate it if the source of the copy is lexical and
117150 // it is live for longer than the `allocStack`.
118- if allocStack. isLexical && !copy. source . accessBase. storageIsLexical {
151+ if allocStack. isLexical && !copy. sourceAddress . accessBase. storageIsLexical {
119152 return nil
120153 }
121154
@@ -149,7 +182,7 @@ private func getRemovableAllocStackDestination(
149182 if copy. isTakeOfSource,
150183 lastUseOfAllocStack != copy,
151184 !( lastUseOfAllocStack is DestroyAddrInst ) ,
152- lastUseOfAllocStack. mayWrite ( toAddress: copy. source , context. aliasAnalysis)
185+ lastUseOfAllocStack. mayWrite ( toAddress: copy. sourceAddress , context. aliasAnalysis)
153186 {
154187 return nil
155188 }
@@ -168,7 +201,7 @@ private func getRemovableAllocStackDestination(
168201
169202/// We need to insert a final destroy if the original `copy` is taking the source but the
170203/// `lastUseOfAllocStack` is not taking the `alloc_stack`.
171- private func needToInsertDestroy( copy: CopyAddrInst , lastUseOfAllocStack: Instruction ) -> Bool {
204+ private func needToInsertDestroy( copy: CopyLikeInstruction , lastUseOfAllocStack: Instruction ) -> Bool {
172205 if !copy. isTakeOfSource {
173206 return false
174207 }
@@ -177,13 +210,13 @@ private func needToInsertDestroy(copy: CopyAddrInst, lastUseOfAllocStack: Instru
177210 return true
178211 case let cai as CopyAddrInst :
179212 if cai. isTakeOfSource {
180- assert ( cai. source == copy. destination , " copy_addr must be not take a projected address " )
213+ assert ( cai. source == copy. destinationAddress , " copy_addr must be not take a projected address " )
181214 return false
182215 }
183216 return true
184217 case let li as LoadInst :
185218 if li. loadOwnership == . take {
186- assert ( li. address == copy. destination , " load must be not take a projected address " )
219+ assert ( li. address == copy. destinationAddress , " load must be not take a projected address " )
187220 return false
188221 }
189222 return true
@@ -247,7 +280,9 @@ private extension AllocStackInst {
247280/// ```
248281/// We must not replace %temp with %a after the `end_access`. Instead we try to move the `end_access`
249282/// after the last use.
250- private func extendAccessScopes( beyond lastUse: Instruction , copy: CopyAddrInst , _ context: FunctionPassContext ) -> Bool {
283+ private func extendAccessScopes( beyond lastUse: Instruction , copy: CopyLikeInstruction ,
284+ _ context: FunctionPassContext ) -> Bool
285+ {
251286 var endAccessToMove : EndAccessInst ? = nil
252287
253288 for inst in InstructionList ( first: copy. next) {
@@ -257,7 +292,7 @@ private func extendAccessScopes(beyond lastUse: Instruction, copy: CopyAddrInst,
257292 if endAccessToMove != nil {
258293 return false
259294 }
260- if context. aliasAnalysis. mayAlias ( copy. source , endAccess. beginAccess. address) ,
295+ if context. aliasAnalysis. mayAlias ( copy. sourceAddress , endAccess. beginAccess. address) ,
261296 // There cannot be any aliasing modifying accesses within the liverange of the `alloc_stack`,
262297 // because we would have cought this in `getLastUseWhileSourceIsNotModified`.
263298 // But there are cases where `aliasAnalysis.mayAlias` is less precise than `Instruction.mayWrite`.
@@ -301,7 +336,7 @@ private func extendAccessScopes(beyond lastUse: Instruction, copy: CopyAddrInst,
301336///
302337/// Unfortunately, we cannot simply use the destroy points as the lifetime end, because they can be in a
303338/// different basic block. Instead we look for the last non-destroy, non-dealloc use.
304- private func getLastUseWhileSourceIsNotModified( of copy: CopyAddrInst ,
339+ private func getLastUseWhileSourceIsNotModified( of copy: CopyLikeInstruction ,
305340 uses: InstructionSetWithCount ,
306341 _ context: FunctionPassContext ) -> Instruction ?
307342{
@@ -313,7 +348,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
313348
314349 // We already checked that the useful lifetime of the `alloc_stack` ends in the same block as the `copy`.
315350 // Therefore we can limit our search to the instructions of this block.
316- for inst in InstructionList ( first: copy. next) {
351+ for inst in InstructionList ( first: copy. loadingInstruction . next) {
317352 if uses. contains ( inst) {
318353 numUsesFound += 1
319354 }
@@ -327,7 +362,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
327362 // could occur _before_ the read of the `alloc_stack`.
328363 switch inst {
329364 case is FullApplySite , is YieldInst :
330- if inst. mayWrite ( toAddress: copy. source , aliasAnalysis) {
365+ if inst. mayWrite ( toAddress: copy. sourceAddress , aliasAnalysis) {
331366 return nil
332367 }
333368 return inst
@@ -336,7 +371,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
336371 }
337372 }
338373
339- if inst. mayWrite ( toAddress: copy. source , aliasAnalysis) {
374+ if inst. mayWrite ( toAddress: copy. sourceAddress , aliasAnalysis) {
340375 return nil
341376 }
342377 }
@@ -348,9 +383,9 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
348383/// Collects all uses of the `alloc_stack`.
349384private struct UseCollector : AddressDefUseWalker {
350385 private( set) var uses : InstructionSetWithCount
351- private let copy : CopyAddrInst
386+ private let copy : CopyLikeInstruction
352387
353- init ( copy: CopyAddrInst , _ context: FunctionPassContext ) {
388+ init ( copy: CopyLikeInstruction , _ context: FunctionPassContext ) {
354389 self . uses = InstructionSetWithCount ( context)
355390 self . copy = copy
356391 }
@@ -467,7 +502,7 @@ private struct UseCollector : AddressDefUseWalker {
467502 if load. loadOwnership == . take,
468503 // Only accept `load [take]` if it takes the whole `alloc_stack`. A `load [take]` from
469504 // a projection would destroy only a part of the `alloc_stack` and we don't handle this.
470- load. address != copy. destination
505+ load. address != copy. destinationAddress
471506 {
472507 return . abortWalk
473508 }
@@ -494,7 +529,7 @@ private struct UseCollector : AddressDefUseWalker {
494529 return . abortWalk
495530 }
496531 // As with `load [take]`, only accept `copy_addr [take]` if it takes the whole `alloc_stack`.
497- if copyFromStack. isTakeOfSource && copyFromStack. source != copy. destination {
532+ if copyFromStack. isTakeOfSource && copyFromStack. source != copy. destinationAddress {
498533 return . abortWalk
499534 }
500535 uses. insert ( copyFromStack)
0 commit comments