@@ -95,15 +95,10 @@ private struct CollectedEffects {
95
95
addEffects ( . copy, to: inst. operands [ 0 ] . value, fromInitialPath: SmallProjectionPath ( . anyValueFields) )
96
96
97
97
case is DestroyValueInst , is ReleaseValueInst , is StrongReleaseInst :
98
- addDestroyEffects ( of : inst. operands [ 0 ] . value)
98
+ addDestroyEffects ( ofValue : inst. operands [ 0 ] . value)
99
99
100
100
case let da as DestroyAddrInst :
101
- // A destroy_addr also involves a read from the address. It's equivalent to a `%x = load [take]` and `destroy_value %x`.
102
- addEffects ( . read, to: da. operand)
103
- // Conceptually, it's also a write, because the stored value is not available anymore after the destroy
104
- addEffects ( . write, to: da. operand)
105
-
106
- addDestroyEffects ( of: da. operand)
101
+ addDestroyEffects ( ofAddress: da. operand)
107
102
108
103
case let copy as CopyAddrInst :
109
104
addEffects ( . read, to: copy. source)
@@ -113,17 +108,13 @@ private struct CollectedEffects {
113
108
addEffects ( . copy, to: copy. source)
114
109
}
115
110
if !copy. isInitializationOfDest {
116
- // Like for destroy_addr, the destroy also involves a read.
117
- addEffects ( . read, to: copy. destination)
118
- addDestroyEffects ( of: copy. destination)
111
+ addDestroyEffects ( ofAddress: copy. destination)
119
112
}
120
113
121
114
case let store as StoreInst :
122
115
addEffects ( . write, to: store. destination)
123
116
if store. destinationOwnership == . assign {
124
- // Like for destroy_addr, the destroy also involves a read.
125
- addEffects ( . read, to: store. destination)
126
- addDestroyEffects ( of: store. destination)
117
+ addDestroyEffects ( ofAddress: store. destination)
127
118
}
128
119
129
120
case let store as StoreWeakInst :
@@ -137,9 +128,9 @@ private struct CollectedEffects {
137
128
addEffects ( . read, to: addr)
138
129
139
130
case let apply as FullApplySite :
140
- let calleeValue = apply. callee
141
131
if apply. callee. type. isCalleeConsumedFunction {
142
- addDestroyEffects ( of: calleeValue)
132
+ addEffects ( . destroy, to: apply. callee)
133
+ globalEffects = . worstEffects
143
134
}
144
135
handleApply ( apply)
145
136
checkedIfDeinitBarrier = true
@@ -230,11 +221,58 @@ private struct CollectedEffects {
230
221
231
222
private mutating func handleApply( _ apply: ApplySite ) {
232
223
let callees = calleeAnalysis. getCallees ( callee: apply. callee)
224
+ let args = apply. arguments. enumerated ( ) . lazy. map {
225
+ ( calleeArgumentIndex: apply. calleeArgIndex ( callerArgIndex: $0. 0 ) ,
226
+ callerArgument: $0. 1 )
227
+ }
228
+ addEffects ( ofFunctions: callees, withArguments: args)
229
+ }
230
+
231
+ private mutating func addDestroyEffects( ofValue value: Value ) {
232
+ // First thing: add the destroy effect itself.
233
+ addEffects ( . destroy, to: value)
234
+
235
+ if value. type. isClass {
236
+ // Treat destroying a class value just like a call to it's destructor(s).
237
+ let destructors = calleeAnalysis. getDestructors ( of: value. type)
238
+ let theSelfArgument = CollectionOfOne ( ( calleeArgumentIndex: 0 , callerArgument: value) )
239
+ addEffects ( ofFunctions: destructors, withArguments: theSelfArgument)
240
+ } else {
241
+ // TODO: dig into the type and check for destructors of individual class fields
242
+ addEffects ( . worstEffects, to: value)
243
+ globalEffects = . worstEffects
244
+ }
245
+ }
246
+
247
+ private mutating func addDestroyEffects( ofAddress address: Value ) {
248
+ // First thing: add the destroy effect itself.
249
+ addEffects ( . destroy, to: address)
250
+
251
+ // A destroy also involves a read from the address.
252
+ // E.g. a `destroy_addr` is equivalent to a `%x = load [take]` and `destroy_value %x`.
253
+ addEffects ( . read, to: address)
254
+ // Conceptually, it's also a write, because the stored value is not available anymore after the destroy
255
+ addEffects ( . write, to: address)
256
+
257
+ // Second: add all effects of (potential) destructors which might be called if the destroy deallocates an object.
258
+ // Note that we don't need to add any effects specific to the `address`, because the memory location is not
259
+ // affected by a destructor of the stored value (and effects don't include anything which is loaded from memory).
260
+ if let destructors = calleeAnalysis. getDestructors ( of: address. type) {
261
+ for destructor in destructors {
262
+ globalEffects. merge ( with: destructor. getSideEffects ( ) )
263
+ }
264
+ } else {
265
+ globalEffects = . worstEffects
266
+ }
267
+ }
233
268
269
+ private mutating func addEffects< Arguments: Sequence > ( ofFunctions callees: FunctionArray ? ,
270
+ withArguments arguments: Arguments )
271
+ where Arguments. Element == ( calleeArgumentIndex: Int , callerArgument: Value ) {
234
272
guard let callees = callees else {
235
273
// We don't know which function(s) are called.
236
274
globalEffects = . worstEffects
237
- for argument in apply . arguments {
275
+ for (_ , argument) in arguments {
238
276
addEffects ( . worstEffects, to: argument)
239
277
}
240
278
return
@@ -249,8 +287,7 @@ private struct CollectedEffects {
249
287
}
250
288
}
251
289
252
- for (argumentIdx, argument) in apply. arguments. enumerated ( ) {
253
- let calleeArgIdx = apply. calleeArgIndex ( callerArgIndex: argumentIdx)
290
+ for (calleeArgIdx, argument) in arguments {
254
291
for callee in callees {
255
292
if let sideEffects = callee. effects. sideEffects {
256
293
let calleeEffect = sideEffects. getArgumentEffects ( for: calleeArgIdx)
@@ -262,7 +299,7 @@ private struct CollectedEffects {
262
299
if let calleePath = calleeEffect. destroy { addEffects ( . destroy, to: argument, fromInitialPath: calleePath) }
263
300
} else {
264
301
let convention = callee. getArgumentConvention ( for: calleeArgIdx)
265
- let wholeArgument = argument. at ( SmallProjectionPath ( . anything ) )
302
+ let wholeArgument = argument. at ( defaultPath ( for : argument ) )
266
303
let calleeEffects = callee. getSideEffects ( forArgument: wholeArgument,
267
304
atIndex: calleeArgIdx,
268
305
withConvention: convention)
@@ -272,27 +309,16 @@ private struct CollectedEffects {
272
309
}
273
310
}
274
311
275
- private mutating func addDestroyEffects( of addressOrValue: Value ) {
276
- // First thing: add the destroy effect itself.
277
- addEffects ( . destroy, to: addressOrValue)
278
-
279
- // Second: add all effects of (potential) destructors which might be called if the destroy
280
- // deallocates an object.
281
- if let destructors = calleeAnalysis. getDestructors ( of: addressOrValue. type) {
282
- for destructor in destructors {
283
- globalEffects. merge ( with: destructor. getSideEffects ( ) )
284
- }
285
- } else {
286
- globalEffects = . worstEffects
287
- }
288
- }
289
-
290
312
/// Adds effects to a specific value.
291
313
///
292
314
/// If the value comes from an argument (or mutliple arguments), then the effects are added
293
315
/// to the corrseponding `argumentEffects`. Otherwise they are added to the `global` effects.
316
+ private mutating func addEffects( _ effects: SideEffects . GlobalEffects , to value: Value ) {
317
+ addEffects ( effects, to: value, fromInitialPath: defaultPath ( for: value) )
318
+ }
319
+
294
320
private mutating func addEffects( _ effects: SideEffects . GlobalEffects , to value: Value ,
295
- fromInitialPath: SmallProjectionPath ? = nil ) {
321
+ fromInitialPath: SmallProjectionPath ) {
296
322
297
323
/// Collects the (non-address) roots of a value.
298
324
struct GetRootsWalker : ValueUseDefWalker {
@@ -324,10 +350,7 @@ private struct CollectedEffects {
324
350
325
351
var findRoots = GetRootsWalker ( context)
326
352
if value. type. isAddress {
327
- // If there is no initial path provided, select all value fields.
328
- let path = fromInitialPath ?? SmallProjectionPath ( . anyValueFields)
329
-
330
- let accessPath = value. getAccessPath ( fromInitialPath: path)
353
+ let accessPath = value. getAccessPath ( fromInitialPath: fromInitialPath)
331
354
switch accessPath. base {
332
355
case . stack:
333
356
// We don't care about read and writes from/to stack locations (because they are
@@ -347,16 +370,7 @@ private struct CollectedEffects {
347
370
}
348
371
}
349
372
} else {
350
- // Handle non-address `value`s which are projections from a direct arguments.
351
- let path : SmallProjectionPath
352
- if let fromInitialPath = fromInitialPath {
353
- path = fromInitialPath
354
- } else if value. type. isClass {
355
- path = SmallProjectionPath ( . anyValueFields) . push ( . anyClassField)
356
- } else {
357
- path = SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) . push ( . anyValueFields)
358
- }
359
- _ = findRoots. walkUp ( value: value, path: path)
373
+ _ = findRoots. walkUp ( value: value, path: fromInitialPath)
360
374
}
361
375
// Because of phi-arguments, a single (non-address) `value` can come from multiple arguments.
362
376
while let ( arg, path) = findRoots. roots. pop ( ) {
@@ -369,6 +383,16 @@ private struct CollectedEffects {
369
383
}
370
384
}
371
385
386
+ private func defaultPath( for value: Value ) -> SmallProjectionPath {
387
+ if value. type. isAddress {
388
+ return SmallProjectionPath ( . anyValueFields)
389
+ }
390
+ if value. type. isClass {
391
+ return SmallProjectionPath ( . anyValueFields) . push ( . anyClassField)
392
+ }
393
+ return SmallProjectionPath ( . anyValueFields) . push ( . anyClassField) . push ( . anyValueFields)
394
+ }
395
+
372
396
/// Checks if an argument escapes to some unknown user.
373
397
private struct ArgumentEscapingWalker : ValueDefUseWalker , AddressDefUseWalker {
374
398
var walkDownCache = WalkerCache < UnusedWalkingPath > ( )
0 commit comments