@@ -98,107 +98,132 @@ extension LifetimeDependentApply {
98
98
99
99
/// A lifetime argument that either inherits or creates a new scope for the lifetime of the argument value.
100
100
struct LifetimeSource {
101
+ let targetKind : TargetKind
101
102
let convention : LifetimeDependenceConvention
102
103
let value : Value
103
104
}
104
105
105
106
/// List of lifetime dependencies for a single target.
106
- struct LifetimeSources {
107
- let targetKind : TargetKind
107
+ struct LifetimeSourceInfo {
108
108
var sources = SingleInlineArray < LifetimeSource > ( )
109
+ var bases = [ Value] ( )
109
110
}
110
111
111
- func getResultDependenceSources( ) -> LifetimeSources ? {
112
- guard applySite. hasResultDependence else { return nil }
113
- var sources : LifetimeSources
114
- switch applySite {
115
- case let beginApply as BeginApplyInst :
116
- if beginApply. yieldedValues. contains ( where: { $0. type. isAddress } ) {
117
- sources = LifetimeSources ( targetKind: . yieldAddress)
118
- } else {
119
- sources = LifetimeSources ( targetKind: . yield)
112
+ func getResultDependenceSources( ) -> LifetimeSourceInfo ? {
113
+ guard applySite. hasResultDependence else {
114
+ return nil
115
+ }
116
+ var info = LifetimeSourceInfo ( )
117
+ if let beginApply = applySite as? BeginApplyInst {
118
+ return getYieldDependenceSources ( beginApply: beginApply)
119
+ }
120
+ for operand in applySite. parameterOperands {
121
+ guard let dep = applySite. resultDependence ( on: operand) else {
122
+ continue
120
123
}
121
- default :
122
- sources = LifetimeSources ( targetKind: . result)
124
+ info. sources. push ( LifetimeSource ( targetKind: . result, convention: dep, value: operand. value) )
125
+ }
126
+ return info
127
+ }
128
+
129
+ func getYieldDependenceSources( beginApply: BeginApplyInst ) -> LifetimeSourceInfo ? {
130
+ var info = LifetimeSourceInfo ( )
131
+ let hasScopedYield = applySite. parameterOperands. contains {
132
+ if let dep = applySite. resultDependence ( on: $0) {
133
+ return dep == . scope
134
+ }
135
+ return false
136
+ }
137
+ if hasScopedYield {
138
+ // for consistency, we you yieldAddress if any yielded value is an address.
139
+ let targetKind = beginApply. yieldedValues. contains ( where: { $0. type. isAddress } )
140
+ ? TargetKind . yieldAddress : TargetKind . yield
141
+ info. sources. push ( LifetimeSource ( targetKind: targetKind, convention: . scope, value: beginApply. token) )
123
142
}
124
143
for operand in applySite. parameterOperands {
125
144
guard let dep = applySite. resultDependence ( on: operand) else {
126
145
continue
127
146
}
128
- sources. sources. push ( LifetimeSource ( convention: dep, value: operand. value) )
147
+ switch dep {
148
+ case . inherit:
149
+ continue
150
+ case . scope:
151
+ for yieldedValue in beginApply. yieldedValues {
152
+ let targetKind = yieldedValue. type. isAddress ? TargetKind . yieldAddress : TargetKind . yield
153
+ info. sources. push ( LifetimeSource ( targetKind: targetKind, convention: . inherit, value: operand. value) )
154
+ }
155
+ }
129
156
}
130
- return sources
157
+ return info
131
158
}
132
159
133
- func getParameterDependenceSources( target: Operand ) -> LifetimeSources ? {
160
+ func getParameterDependenceSources( target: Operand ) -> LifetimeSourceInfo ? {
134
161
guard let deps = applySite. parameterDependencies ( target: target) else {
135
162
return nil
136
163
}
137
- var sources : LifetimeSources
138
- let convention = applySite. convention ( of: target) !
139
- switch convention {
140
- case . indirectInout, . indirectInoutAliasable, . packInout:
141
- sources = LifetimeSources ( targetKind: . inoutParameter)
142
- case . indirectIn, . indirectInGuaranteed, . indirectInCXX, . directOwned, . directUnowned, . directGuaranteed,
143
- . packOwned, . packGuaranteed:
144
- sources = LifetimeSources ( targetKind: . inParameter)
145
- case . indirectOut, . packOut:
146
- debugLog ( " \( applySite) " )
147
- fatalError ( " Lifetime dependencies cannot target \( convention) parameter " )
148
- }
164
+ var info = LifetimeSourceInfo ( )
165
+ let targetKind = {
166
+ let convention = applySite. convention ( of: target) !
167
+ switch convention {
168
+ case . indirectInout, . indirectInoutAliasable, . packInout:
169
+ return TargetKind . inoutParameter
170
+ case . indirectIn, . indirectInGuaranteed, . indirectInCXX, . directOwned, . directUnowned, . directGuaranteed,
171
+ . packOwned, . packGuaranteed:
172
+ return TargetKind . inParameter
173
+ case . indirectOut, . packOut:
174
+ debugLog ( " \( applySite) " )
175
+ fatalError ( " Lifetime dependencies cannot target \( convention) parameter " )
176
+ }
177
+ } ( )
149
178
for (dep, operand) in zip ( deps, applySite. parameterOperands) {
150
179
guard let dep = dep else {
151
180
continue
152
181
}
153
- sources . sources. push ( LifetimeSource ( convention: dep, value: operand. value) )
182
+ info . sources. push ( LifetimeSource ( targetKind : targetKind , convention: dep, value: operand. value) )
154
183
}
155
- return sources
184
+ return info
156
185
}
186
+ }
157
187
158
- // Scoped dependencies require a mark_dependence for every variable that introduces this scope.
159
- //
160
- // Inherited dependencies do not require a mark_dependence if the target is a result or yielded value. The inherited
161
- // lifetime is nonescapable, so either
162
- //
163
- // (a) the result or yield is never returned from this function
164
- //
165
- // (b) the inherited lifetime has a dependence root within this function (it comes from a dependent function argument
166
- // or scoped dependence). In this case, when that depedence root is diagnosed, the analysis will find transtive uses
167
- // of this apply's result.
168
- //
169
- // (c) the dependent value is passed to another call with a dependent inout argument, or it is stored to a yielded
170
- // address of a coroutine that has a dependent inout argument. In this case, a mark_dependence will already be created
171
- // for that inout argument.
172
- //
173
- // Parameter dependencies and yielded addresses always require a mark_dependence.
174
- static func findDependenceBases( sources: LifetimeSources , _ context: FunctionPassContext ) -> [ Value ] {
175
- var bases : [ Value ] = [ ]
176
- for source in sources. sources {
188
+ private extension LifetimeDependentApply . LifetimeSourceInfo {
189
+ mutating func initializeBases( _ context: FunctionPassContext ) {
190
+ for source in sources {
191
+ // Inherited dependencies do not require a mark_dependence if the target is a result or yielded value. The
192
+ // inherited lifetime is nonescapable, so either
193
+ //
194
+ // (a) the result or yield is never returned from this function
195
+ //
196
+ // (b) the inherited lifetime has a dependence root within this function (it comes from a dependent function
197
+ // argument or scoped dependence). In this case, when that depedence root is diagnosed, the analysis will find
198
+ // transtive uses of this apply's result.
199
+ //
200
+ // (c) the dependent value is passed to another call with a dependent inout argument, or it is stored to a yielded
201
+ // address of a coroutine that has a dependent inout argument. In this case, a mark_dependence will already be
202
+ // created for that inout argument.
177
203
switch source. convention {
178
204
case . inherit:
179
- switch sources. targetKind {
180
- case . result, . yield:
181
- continue
182
- case . inParameter, . inoutParameter, . yieldAddress:
183
- _ = LifetimeDependence . visitDependenceRoots ( enclosing: source. value, context) { scope in
184
- log ( " Inherited lifetime from \( source. value) " )
185
- log ( " scope: \( scope) " )
186
- bases. append ( scope. parentValue)
187
- return . continueWalk
188
- }
189
- }
205
+ break
190
206
case . scope:
191
- // Create a new dependence on the apply's access to the argument.
192
- for varIntoducer in gatherVariableIntroducers ( for: source. value, context) {
193
- if let scope = LifetimeDependence . Scope ( base: varIntoducer, context) {
194
- log ( " Scoped lifetime from \( source. value) " )
195
- log ( " scope: \( scope) " )
196
- bases. append ( scope. parentValue)
197
- }
198
- }
207
+ initializeScopedBases ( source: source, context)
208
+ }
209
+ }
210
+ }
211
+
212
+ // Scoped dependencies require a mark_dependence for every variable that introduces this scope.
213
+ mutating func initializeScopedBases( source: LifetimeDependentApply . LifetimeSource , _ context: FunctionPassContext ) {
214
+ switch source. targetKind {
215
+ case . yield, . yieldAddress:
216
+ // A coroutine creates its own borrow scope, nested within its borrowed operand.
217
+ bases. append ( source. value)
218
+ case . result, . inParameter, . inoutParameter:
219
+ // Create a new dependence on the apply's access to the argument.
220
+ for varIntoducer in gatherVariableIntroducers ( for: source. value, context) {
221
+ let scope = LifetimeDependence . Scope ( base: varIntoducer, context)
222
+ log ( " Scoped lifetime from \( source. value) " )
223
+ log ( " scope: \( scope) " )
224
+ bases. append ( scope. parentValue)
199
225
}
200
226
}
201
- return bases
202
227
}
203
228
}
204
229
@@ -207,16 +232,17 @@ extension LifetimeDependentApply {
207
232
/// result on each argument so that the result is recognized as a
208
233
/// dependent value within each scope.
209
234
private func insertResultDependencies( for apply: LifetimeDependentApply , _ context: FunctionPassContext ) {
210
- guard let sources = apply. getResultDependenceSources ( ) else {
235
+ guard var sources = apply. getResultDependenceSources ( ) else {
211
236
return
212
237
}
213
238
log ( " Creating dependencies for \( apply. applySite) " )
214
239
215
- let bases = LifetimeDependentApply . findDependenceBases ( sources: sources, context)
240
+ // Find the dependence base for each source.
241
+ sources. initializeBases ( context)
216
242
217
243
for dependentValue in apply. applySite. resultOrYields {
218
244
let builder = Builder ( before: dependentValue. nextInstruction, context)
219
- insertMarkDependencies ( value: dependentValue, initializer: nil , bases: bases, builder: builder, context)
245
+ insertMarkDependencies ( value: dependentValue, initializer: nil , bases: sources . bases, builder: builder, context)
220
246
}
221
247
for resultOper in apply. applySite. indirectResultOperands {
222
248
let accessBase = resultOper. value. accessBase
@@ -231,23 +257,23 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
231
257
}
232
258
assert ( initializingStore == resultOper. instruction, " an indirect result is a store " )
233
259
Builder . insert ( after: apply. applySite, context) { builder in
234
- insertMarkDependencies ( value: initialAddress, initializer: initializingStore, bases: bases, builder : builder ,
235
- context)
260
+ insertMarkDependencies ( value: initialAddress, initializer: initializingStore, bases: sources . bases,
261
+ builder : builder , context)
236
262
}
237
263
}
238
264
}
239
265
240
266
private func insertParameterDependencies( apply: LifetimeDependentApply , target: Operand ,
241
267
_ context: FunctionPassContext ) {
242
- guard let sources = apply. getParameterDependenceSources ( target: target) else {
268
+ guard var sources = apply. getParameterDependenceSources ( target: target) else {
243
269
return
244
270
}
245
271
log ( " Creating dependencies for \( apply. applySite) " )
246
272
247
- let bases = LifetimeDependentApply . findDependenceBases ( sources : sources , context)
273
+ sources . initializeBases ( context)
248
274
249
275
Builder . insert ( after: apply. applySite, context) {
250
- insertMarkDependencies ( value: target. value, initializer: nil , bases: bases, builder: $0, context)
276
+ insertMarkDependencies ( value: target. value, initializer: nil , bases: sources . bases, builder: $0, context)
251
277
}
252
278
}
253
279
@@ -259,11 +285,18 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
259
285
let markDep = builder. createMarkDependence (
260
286
value: currentValue, base: base, kind: . Unresolved)
261
287
262
- let uses = currentValue. uses. lazy. filter {
263
- let inst = $0. instruction
264
- return inst != markDep && inst != initializer && !( inst is Deallocation )
288
+ // Address dependencies cannot be represented as SSA values, so it doesn not make sense to replace any uses of the
289
+ // dependent address. TODO: consider a separate mark_dependence_addr instruction since the semantics are different.
290
+ if !value. type. isAddress {
291
+ let uses = currentValue. uses. lazy. filter {
292
+ if $0. isScopeEndingUse {
293
+ return false
294
+ }
295
+ let inst = $0. instruction
296
+ return inst != markDep && inst != initializer && !( inst is Deallocation )
297
+ }
298
+ uses. replaceAll ( with: markDep, context)
265
299
}
266
- uses. replaceAll ( with: markDep, context)
267
300
currentValue = markDep
268
301
}
269
302
}
0 commit comments