@@ -112,7 +112,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
112112 let localReachabilityCache = LocalVariableReachabilityCache ( )
113113
114114 for instruction in function. instructions {
115- guard let markDep = instruction as? MarkDependenceInst else {
115+ guard let markDep = instruction as? MarkDependenceInstruction else {
116116 continue
117117 }
118118 guard let innerLifetimeDep = LifetimeDependence ( markDep, context) else {
@@ -129,46 +129,71 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
129129 }
130130}
131131
132- private extension MarkDependenceInst {
132+ private extension MarkDependenceInstruction {
133133 /// Rewrite the mark_dependence base operand to ignore inner borrow scopes (begin_borrow, load_borrow).
134134 ///
135135 /// Note: this could be done as a general simplification, e.g. after inlining. But currently this is only relevant for
136136 /// diagnostics.
137137 func rewriteSkippingBorrow( scope: LifetimeDependence . Scope , _ context: FunctionPassContext ) -> LifetimeDependence {
138138 guard let newScope = scope. ignoreBorrowScope ( context) else {
139- return LifetimeDependence ( scope: scope, dependentValue : self )
139+ return LifetimeDependence ( scope: scope, markDep : self ) !
140140 }
141141 let newBase = newScope. parentValue
142142 if newBase != self . baseOperand. value {
143143 self . baseOperand. set ( to: newBase, context)
144144 }
145- return LifetimeDependence ( scope: newScope, dependentValue : self )
145+ return LifetimeDependence ( scope: newScope, markDep : self ) !
146146 }
147147
148- /// Rewrite the mark_dependence base operand, setting it to a function argument.
149- ///
150- /// To handle more than one function argument, new mark_dependence instructions will be chained.
151- /// This is called when the dependent value is returned by the function and the dependence base is in the caller.
152148 func redirectFunctionReturn( to args: SingleInlineArray < FunctionArgument > , _ context: FunctionPassContext ) {
153- var updatedMarkDep : MarkDependenceInst ?
149+ var updatedMarkDep : MarkDependenceInstruction ?
154150 for arg in args {
155151 guard let currentMarkDep = updatedMarkDep else {
156152 self . baseOperand. set ( to: arg, context)
157153 updatedMarkDep = self
158154 continue
159155 }
160- let newMarkDep = Builder ( after: currentMarkDep, location: currentMarkDep. location, context)
161- . createMarkDependence ( value: currentMarkDep, base: arg, kind: . Unresolved)
162- let uses = currentMarkDep. uses. lazy. filter {
163- let inst = $0. instruction
164- return inst != newMarkDep
156+ switch currentMarkDep {
157+ case let mdi as MarkDependenceInst :
158+ updatedMarkDep = mdi. redirectFunctionReturnForward ( to: arg, input: mdi, context)
159+ case let mdi as MarkDependenceAddrInst :
160+ updatedMarkDep = mdi. redirectFunctionReturnAddress ( to: arg, context)
161+ default :
162+ fatalError ( " unexpected MarkDependenceInstruction " )
165163 }
166- uses. replaceAll ( with: newMarkDep, context)
167- updatedMarkDep = newMarkDep
168164 }
169165 }
170166}
171167
168+ private extension MarkDependenceInst {
169+ /// Rewrite the mark_dependence base operand, setting it to a function argument.
170+ ///
171+ /// This is called when the dependent value is returned by the function and the dependence base is in the caller.
172+ func redirectFunctionReturnForward( to arg: FunctionArgument , input: MarkDependenceInst ,
173+ _ context: FunctionPassContext ) -> MarkDependenceInst {
174+ // To handle more than one function argument, new mark_dependence instructions will be chained.
175+ let newMarkDep = Builder ( after: input, location: input. location, context)
176+ . createMarkDependence ( value: input, base: arg, kind: . Unresolved)
177+ let uses = input. uses. lazy. filter {
178+ let inst = $0. instruction
179+ return inst != newMarkDep
180+ }
181+ uses. replaceAll ( with: newMarkDep, context)
182+ return newMarkDep
183+ }
184+ }
185+
186+ private extension MarkDependenceAddrInst {
187+ /// Rewrite the mark_dependence_addr base operand, setting it to a function argument.
188+ ///
189+ /// This is called when the dependent value is returned by the function and the dependence base is in the caller.
190+ func redirectFunctionReturnAddress( to arg: FunctionArgument , _ context: FunctionPassContext )
191+ -> MarkDependenceAddrInst {
192+ return Builder ( after: self , location: self . location, context)
193+ . createMarkDependenceAddr ( value: self . address, base: arg, kind: . Unresolved)
194+ }
195+ }
196+
172197/// Transitively extend nested scopes that enclose the dependence base.
173198///
174199/// If the parent function returns the dependent value, then this returns the function arguments that represent the
@@ -211,8 +236,8 @@ private func extendScopes(dependence: LifetimeDependence,
211236 var dependsOnArgs = SingleInlineArray < FunctionArgument > ( )
212237 for scopeExtension in scopeExtensions {
213238 var scopeExtension = scopeExtension
214- guard var useRange = computeDependentUseRange ( of: dependence. dependentValue , within: & scopeExtension,
215- localReachabilityCache , context) else {
239+ guard var useRange = computeDependentUseRange ( of: dependence, within: & scopeExtension, localReachabilityCache ,
240+ context) else {
216241 continue
217242 }
218243
@@ -463,20 +488,19 @@ extension ScopeExtension {
463488 }
464489}
465490
466- /// Return an InstructionRange covering all the dependent uses of 'value '.
467- private func computeDependentUseRange( of value : Value , within scopeExtension: inout ScopeExtension ,
491+ /// Return an InstructionRange covering all the dependent uses of 'dependence '.
492+ private func computeDependentUseRange( of dependence : LifetimeDependence , within scopeExtension: inout ScopeExtension ,
468493 _ localReachabilityCache: LocalVariableReachabilityCache ,
469494 _ context: FunctionPassContext )
470495 -> InstructionRange ? {
471-
496+ let function = dependence . function
472497 guard var ownershipRange = scopeExtension. computeRange ( localReachabilityCache, context) else {
473498 return nil
474499 }
475500 defer { ownershipRange. deinitialize ( ) }
476501
477502 // The innermost scope that must be extended must dominate all uses.
478503 var useRange = InstructionRange ( begin: scopeExtension. innerScope. extendableBegin!. instruction, context)
479- let function = value. parentFunction
480504 var walker = LifetimeDependentUseWalker ( function, localReachabilityCache, context) {
481505 // Do not extend the useRange past the ownershipRange.
482506 let dependentInst = $0. instruction
@@ -487,17 +511,20 @@ private func computeDependentUseRange(of value: Value, within scopeExtension: in
487511 }
488512 defer { walker. deinitialize ( ) }
489513
490- _ = walker. walkDown ( root : value )
514+ _ = walker. walkDown ( dependence : dependence )
491515
492516 log ( " Scope fixup for dependent uses: \n \( useRange) " )
493517
494518 scopeExtension. dependsOnCaller = walker. dependsOnCaller
495519
496520 // Lifetime dependenent uses may not be dominated by the access. The dependent value may be used by a phi or stored
497521 // into a memory location. The access may be conditional relative to such uses. If any use was not dominated, then
498- // `useRange` will include the function entry.
522+ // `useRange` will include the function entry. There is not way to directly check
523+ // useRange.isValid. useRange.blockRange.isValid is not a strong enough check because it will always succeed when
524+ // useRange.begin == entryBlock even if a use if above useRange.begin.
499525 let firstInst = function. entryBlock. instructions. first!
500526 if firstInst != useRange. begin, useRange. contains ( firstInst) {
527+ useRange. deinitialize ( )
501528 return nil
502529 }
503530 return useRange
0 commit comments