@@ -38,18 +38,51 @@ let tempRValueElimination = FunctionPass(name: "temp-rvalue-elimination") {
38
38
( function: Function , context: FunctionPassContext ) in
39
39
40
40
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
43
57
}
44
58
}
45
59
}
46
60
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 ) {
53
86
54
87
guard let ( allocStack, lastUseOfAllocStack) = getRemovableAllocStackDestination ( of: copy, context) else {
55
88
return
@@ -61,7 +94,7 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
61
94
62
95
if needToInsertDestroy ( copy: copy, lastUseOfAllocStack: lastUseOfAllocStack) {
63
96
Builder . insert ( after: lastUseOfAllocStack, context) { builder in
64
- builder. createDestroyAddr ( address: copy. source )
97
+ builder. createDestroyAddr ( address: copy. sourceAddress )
65
98
}
66
99
}
67
100
@@ -76,25 +109,25 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
76
109
assert ( cai == lastUseOfAllocStack && cai. source == allocStack)
77
110
cai. set ( isTakeOfSource: false , context)
78
111
}
79
- use. set ( to: copy. source , context)
112
+ use. set ( to: copy. sourceAddress , context)
80
113
case let load as LoadInst :
81
114
if load. loadOwnership == . take, !copy. isTakeOfSource {
82
115
// If the original copy is not taking its source, a `load` from the `allocStack` must
83
116
// not take its source either.
84
117
assert ( load == lastUseOfAllocStack)
85
118
load. set ( ownership: . copy, context)
86
119
}
87
- use. set ( to: copy. source , context) ;
120
+ use. set ( to: copy. sourceAddress , context) ;
88
121
89
122
default :
90
123
// Note that no operations that may be handled by this default clause can destroy the `allocStack`.
91
124
// This includes operations that load the value from memory and release it or cast the address
92
125
// before destroying it.
93
- use. set ( to: copy. source , context) ;
126
+ use. set ( to: copy. sourceAddress , context) ;
94
127
}
95
128
}
96
129
context. erase ( instruction: allocStack)
97
- context. erase ( instruction : copy)
130
+ context. erase ( instructionIncludingAllUsers : copy. loadingInstruction )
98
131
}
99
132
100
133
/// Checks if the `copy` is copying into an `alloc_stack` which is removable:
@@ -105,17 +138,17 @@ private func tryEliminate(copy: CopyAddrInst, _ context: FunctionPassContext) {
105
138
/// %lastUseOfAllocStack = load %allocStack
106
139
/// ```
107
140
private func getRemovableAllocStackDestination(
108
- of copy: CopyAddrInst , _ context: FunctionPassContext
141
+ of copy: CopyLikeInstruction , _ context: FunctionPassContext
109
142
) -> ( allocStack: AllocStackInst , lastUseOfAllocStack: Instruction ) ? {
110
143
guard copy. isInitializationOfDestination,
111
- let allocStack = copy. destination as? AllocStackInst
144
+ let allocStack = copy. destinationAddress as? AllocStackInst
112
145
else {
113
146
return nil
114
147
}
115
148
116
149
// If the `allocStack` is lexical we can eliminate it if the source of the copy is lexical and
117
150
// it is live for longer than the `allocStack`.
118
- if allocStack. isLexical && !copy. source . accessBase. storageIsLexical {
151
+ if allocStack. isLexical && !copy. sourceAddress . accessBase. storageIsLexical {
119
152
return nil
120
153
}
121
154
@@ -149,7 +182,7 @@ private func getRemovableAllocStackDestination(
149
182
if copy. isTakeOfSource,
150
183
lastUseOfAllocStack != copy,
151
184
!( lastUseOfAllocStack is DestroyAddrInst ) ,
152
- lastUseOfAllocStack. mayWrite ( toAddress: copy. source , context. aliasAnalysis)
185
+ lastUseOfAllocStack. mayWrite ( toAddress: copy. sourceAddress , context. aliasAnalysis)
153
186
{
154
187
return nil
155
188
}
@@ -168,7 +201,7 @@ private func getRemovableAllocStackDestination(
168
201
169
202
/// We need to insert a final destroy if the original `copy` is taking the source but the
170
203
/// `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 {
172
205
if !copy. isTakeOfSource {
173
206
return false
174
207
}
@@ -177,13 +210,13 @@ private func needToInsertDestroy(copy: CopyAddrInst, lastUseOfAllocStack: Instru
177
210
return true
178
211
case let cai as CopyAddrInst :
179
212
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 " )
181
214
return false
182
215
}
183
216
return true
184
217
case let li as LoadInst :
185
218
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 " )
187
220
return false
188
221
}
189
222
return true
@@ -247,7 +280,9 @@ private extension AllocStackInst {
247
280
/// ```
248
281
/// We must not replace %temp with %a after the `end_access`. Instead we try to move the `end_access`
249
282
/// 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
+ {
251
286
var endAccessToMove : EndAccessInst ? = nil
252
287
253
288
for inst in InstructionList ( first: copy. next) {
@@ -257,7 +292,7 @@ private func extendAccessScopes(beyond lastUse: Instruction, copy: CopyAddrInst,
257
292
if endAccessToMove != nil {
258
293
return false
259
294
}
260
- if context. aliasAnalysis. mayAlias ( copy. source , endAccess. beginAccess. address) ,
295
+ if context. aliasAnalysis. mayAlias ( copy. sourceAddress , endAccess. beginAccess. address) ,
261
296
// There cannot be any aliasing modifying accesses within the liverange of the `alloc_stack`,
262
297
// because we would have cought this in `getLastUseWhileSourceIsNotModified`.
263
298
// 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,
301
336
///
302
337
/// Unfortunately, we cannot simply use the destroy points as the lifetime end, because they can be in a
303
338
/// 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 ,
305
340
uses: InstructionSetWithCount ,
306
341
_ context: FunctionPassContext ) -> Instruction ?
307
342
{
@@ -313,7 +348,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
313
348
314
349
// We already checked that the useful lifetime of the `alloc_stack` ends in the same block as the `copy`.
315
350
// 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) {
317
352
if uses. contains ( inst) {
318
353
numUsesFound += 1
319
354
}
@@ -327,7 +362,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
327
362
// could occur _before_ the read of the `alloc_stack`.
328
363
switch inst {
329
364
case is FullApplySite , is YieldInst :
330
- if inst. mayWrite ( toAddress: copy. source , aliasAnalysis) {
365
+ if inst. mayWrite ( toAddress: copy. sourceAddress , aliasAnalysis) {
331
366
return nil
332
367
}
333
368
return inst
@@ -336,7 +371,7 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
336
371
}
337
372
}
338
373
339
- if inst. mayWrite ( toAddress: copy. source , aliasAnalysis) {
374
+ if inst. mayWrite ( toAddress: copy. sourceAddress , aliasAnalysis) {
340
375
return nil
341
376
}
342
377
}
@@ -348,9 +383,9 @@ private func getLastUseWhileSourceIsNotModified(of copy: CopyAddrInst,
348
383
/// Collects all uses of the `alloc_stack`.
349
384
private struct UseCollector : AddressDefUseWalker {
350
385
private( set) var uses : InstructionSetWithCount
351
- private let copy : CopyAddrInst
386
+ private let copy : CopyLikeInstruction
352
387
353
- init ( copy: CopyAddrInst , _ context: FunctionPassContext ) {
388
+ init ( copy: CopyLikeInstruction , _ context: FunctionPassContext ) {
354
389
self . uses = InstructionSetWithCount ( context)
355
390
self . copy = copy
356
391
}
@@ -467,7 +502,7 @@ private struct UseCollector : AddressDefUseWalker {
467
502
if load. loadOwnership == . take,
468
503
// Only accept `load [take]` if it takes the whole `alloc_stack`. A `load [take]` from
469
504
// 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
471
506
{
472
507
return . abortWalk
473
508
}
@@ -494,7 +529,7 @@ private struct UseCollector : AddressDefUseWalker {
494
529
return . abortWalk
495
530
}
496
531
// 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 {
498
533
return . abortWalk
499
534
}
500
535
uses. insert ( copyFromStack)
0 commit comments