@@ -98,107 +98,132 @@ extension LifetimeDependentApply {
9898
9999 /// A lifetime argument that either inherits or creates a new scope for the lifetime of the argument value.
100100 struct LifetimeSource {
101+ let targetKind : TargetKind
101102 let convention : LifetimeDependenceConvention
102103 let value : Value
103104 }
104105
105106 /// List of lifetime dependencies for a single target.
106- struct LifetimeSources {
107- let targetKind : TargetKind
107+ struct LifetimeSourceInfo {
108108 var sources = SingleInlineArray < LifetimeSource > ( )
109+ var bases = [ Value] ( )
109110 }
110111
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
120123 }
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) )
123142 }
124143 for operand in applySite. parameterOperands {
125144 guard let dep = applySite. resultDependence ( on: operand) else {
126145 continue
127146 }
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+ }
129156 }
130- return sources
157+ return info
131158 }
132159
133- func getParameterDependenceSources( target: Operand ) -> LifetimeSources ? {
160+ func getParameterDependenceSources( target: Operand ) -> LifetimeSourceInfo ? {
134161 guard let deps = applySite. parameterDependencies ( target: target) else {
135162 return nil
136163 }
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+ } ( )
149178 for (dep, operand) in zip ( deps, applySite. parameterOperands) {
150179 guard let dep = dep else {
151180 continue
152181 }
153- sources . sources. push ( LifetimeSource ( convention: dep, value: operand. value) )
182+ info . sources. push ( LifetimeSource ( targetKind : targetKind , convention: dep, value: operand. value) )
154183 }
155- return sources
184+ return info
156185 }
186+ }
157187
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.
177203 switch source. convention {
178204 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
190206 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)
199225 }
200226 }
201- return bases
202227 }
203228}
204229
@@ -207,16 +232,17 @@ extension LifetimeDependentApply {
207232/// result on each argument so that the result is recognized as a
208233/// dependent value within each scope.
209234private func insertResultDependencies( for apply: LifetimeDependentApply , _ context: FunctionPassContext ) {
210- guard let sources = apply. getResultDependenceSources ( ) else {
235+ guard var sources = apply. getResultDependenceSources ( ) else {
211236 return
212237 }
213238 log ( " Creating dependencies for \( apply. applySite) " )
214239
215- let bases = LifetimeDependentApply . findDependenceBases ( sources: sources, context)
240+ // Find the dependence base for each source.
241+ sources. initializeBases ( context)
216242
217243 for dependentValue in apply. applySite. resultOrYields {
218244 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)
220246 }
221247 for resultOper in apply. applySite. indirectResultOperands {
222248 let accessBase = resultOper. value. accessBase
@@ -231,23 +257,23 @@ private func insertResultDependencies(for apply: LifetimeDependentApply, _ conte
231257 }
232258 assert ( initializingStore == resultOper. instruction, " an indirect result is a store " )
233259 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)
236262 }
237263 }
238264}
239265
240266private func insertParameterDependencies( apply: LifetimeDependentApply , target: Operand ,
241267 _ context: FunctionPassContext ) {
242- guard let sources = apply. getParameterDependenceSources ( target: target) else {
268+ guard var sources = apply. getParameterDependenceSources ( target: target) else {
243269 return
244270 }
245271 log ( " Creating dependencies for \( apply. applySite) " )
246272
247- let bases = LifetimeDependentApply . findDependenceBases ( sources : sources , context)
273+ sources . initializeBases ( context)
248274
249275 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)
251277 }
252278}
253279
@@ -259,11 +285,18 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
259285 let markDep = builder. createMarkDependence (
260286 value: currentValue, base: base, kind: . Unresolved)
261287
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)
265299 }
266- uses. replaceAll ( with: markDep, context)
267300 currentValue = markDep
268301 }
269302}
0 commit comments