@@ -95,15 +95,10 @@ private struct CollectedEffects {
9595 addEffects ( . copy, to: inst. operands [ 0 ] . value, fromInitialPath: SmallProjectionPath ( . anyValueFields) )
9696
9797 case is DestroyValueInst , is ReleaseValueInst , is StrongReleaseInst :
98- addDestroyEffects ( of : inst. operands [ 0 ] . value)
98+ addDestroyEffects ( ofValue : inst. operands [ 0 ] . value)
9999
100100 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)
107102
108103 case let copy as CopyAddrInst :
109104 addEffects ( . read, to: copy. source)
@@ -113,17 +108,13 @@ private struct CollectedEffects {
113108 addEffects ( . copy, to: copy. source)
114109 }
115110 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)
119112 }
120113
121114 case let store as StoreInst :
122115 addEffects ( . write, to: store. destination)
123116 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)
127118 }
128119
129120 case let store as StoreWeakInst :
@@ -137,9 +128,9 @@ private struct CollectedEffects {
137128 addEffects ( . read, to: addr)
138129
139130 case let apply as FullApplySite :
140- let calleeValue = apply. callee
141131 if apply. callee. type. isCalleeConsumedFunction {
142- addDestroyEffects ( of: calleeValue)
132+ addEffects ( . destroy, to: apply. callee)
133+ globalEffects = . worstEffects
143134 }
144135 handleApply ( apply)
145136 checkedIfDeinitBarrier = true
@@ -230,11 +221,58 @@ private struct CollectedEffects {
230221
231222 private mutating func handleApply( _ apply: ApplySite ) {
232223 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+ }
233268
269+ private mutating func addEffects< Arguments: Sequence > ( ofFunctions callees: FunctionArray ? ,
270+ withArguments arguments: Arguments )
271+ where Arguments. Element == ( calleeArgumentIndex: Int , callerArgument: Value ) {
234272 guard let callees = callees else {
235273 // We don't know which function(s) are called.
236274 globalEffects = . worstEffects
237- for argument in apply . arguments {
275+ for (_ , argument) in arguments {
238276 addEffects ( . worstEffects, to: argument)
239277 }
240278 return
@@ -249,8 +287,7 @@ private struct CollectedEffects {
249287 }
250288 }
251289
252- for (argumentIdx, argument) in apply. arguments. enumerated ( ) {
253- let calleeArgIdx = apply. calleeArgIndex ( callerArgIndex: argumentIdx)
290+ for (calleeArgIdx, argument) in arguments {
254291 for callee in callees {
255292 if let sideEffects = callee. effects. sideEffects {
256293 let calleeEffect = sideEffects. getArgumentEffects ( for: calleeArgIdx)
@@ -262,7 +299,7 @@ private struct CollectedEffects {
262299 if let calleePath = calleeEffect. destroy { addEffects ( . destroy, to: argument, fromInitialPath: calleePath) }
263300 } else {
264301 let convention = callee. getArgumentConvention ( for: calleeArgIdx)
265- let wholeArgument = argument. at ( SmallProjectionPath ( . anything ) )
302+ let wholeArgument = argument. at ( defaultPath ( for : argument ) )
266303 let calleeEffects = callee. getSideEffects ( forArgument: wholeArgument,
267304 atIndex: calleeArgIdx,
268305 withConvention: convention)
@@ -272,27 +309,16 @@ private struct CollectedEffects {
272309 }
273310 }
274311
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-
290312 /// Adds effects to a specific value.
291313 ///
292314 /// If the value comes from an argument (or mutliple arguments), then the effects are added
293315 /// 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+
294320 private mutating func addEffects( _ effects: SideEffects . GlobalEffects , to value: Value ,
295- fromInitialPath: SmallProjectionPath ? = nil ) {
321+ fromInitialPath: SmallProjectionPath ) {
296322
297323 /// Collects the (non-address) roots of a value.
298324 struct GetRootsWalker : ValueUseDefWalker {
@@ -324,10 +350,7 @@ private struct CollectedEffects {
324350
325351 var findRoots = GetRootsWalker ( context)
326352 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)
331354 switch accessPath. base {
332355 case . stack:
333356 // We don't care about read and writes from/to stack locations (because they are
@@ -347,16 +370,7 @@ private struct CollectedEffects {
347370 }
348371 }
349372 } 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)
360374 }
361375 // Because of phi-arguments, a single (non-address) `value` can come from multiple arguments.
362376 while let ( arg, path) = findRoots. roots. pop ( ) {
@@ -369,6 +383,16 @@ private struct CollectedEffects {
369383 }
370384}
371385
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+
372396/// Checks if an argument escapes to some unknown user.
373397private struct ArgumentEscapingWalker : ValueDefUseWalker , AddressDefUseWalker {
374398 var walkDownCache = WalkerCache < UnusedWalkingPath > ( )
0 commit comments