@@ -112,7 +112,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
112
112
let localReachabilityCache = LocalVariableReachabilityCache ( )
113
113
114
114
for instruction in function. instructions {
115
- guard let markDep = instruction as? MarkDependenceInst else {
115
+ guard let markDep = instruction as? MarkDependenceInstruction else {
116
116
continue
117
117
}
118
118
guard let innerLifetimeDep = LifetimeDependence ( markDep, context) else {
@@ -129,46 +129,71 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
129
129
}
130
130
}
131
131
132
- private extension MarkDependenceInst {
132
+ private extension MarkDependenceInstruction {
133
133
/// Rewrite the mark_dependence base operand to ignore inner borrow scopes (begin_borrow, load_borrow).
134
134
///
135
135
/// Note: this could be done as a general simplification, e.g. after inlining. But currently this is only relevant for
136
136
/// diagnostics.
137
137
func rewriteSkippingBorrow( scope: LifetimeDependence . Scope , _ context: FunctionPassContext ) -> LifetimeDependence {
138
138
guard let newScope = scope. ignoreBorrowScope ( context) else {
139
- return LifetimeDependence ( scope: scope, dependentValue : self )
139
+ return LifetimeDependence ( scope: scope, markDep : self ) !
140
140
}
141
141
let newBase = newScope. parentValue
142
142
if newBase != self . baseOperand. value {
143
143
self . baseOperand. set ( to: newBase, context)
144
144
}
145
- return LifetimeDependence ( scope: newScope, dependentValue : self )
145
+ return LifetimeDependence ( scope: newScope, markDep : self ) !
146
146
}
147
147
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.
152
148
func redirectFunctionReturn( to args: SingleInlineArray < FunctionArgument > , _ context: FunctionPassContext ) {
153
- var updatedMarkDep : MarkDependenceInst ?
149
+ var updatedMarkDep : MarkDependenceInstruction ?
154
150
for arg in args {
155
151
guard let currentMarkDep = updatedMarkDep else {
156
152
self . baseOperand. set ( to: arg, context)
157
153
updatedMarkDep = self
158
154
continue
159
155
}
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 " )
165
163
}
166
- uses. replaceAll ( with: newMarkDep, context)
167
- updatedMarkDep = newMarkDep
168
164
}
169
165
}
170
166
}
171
167
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
+
172
197
/// Transitively extend nested scopes that enclose the dependence base.
173
198
///
174
199
/// 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,
211
236
var dependsOnArgs = SingleInlineArray < FunctionArgument > ( )
212
237
for scopeExtension in scopeExtensions {
213
238
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 {
216
241
continue
217
242
}
218
243
@@ -463,20 +488,19 @@ extension ScopeExtension {
463
488
}
464
489
}
465
490
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 ,
468
493
_ localReachabilityCache: LocalVariableReachabilityCache ,
469
494
_ context: FunctionPassContext )
470
495
-> InstructionRange ? {
471
-
496
+ let function = dependence . function
472
497
guard var ownershipRange = scopeExtension. computeRange ( localReachabilityCache, context) else {
473
498
return nil
474
499
}
475
500
defer { ownershipRange. deinitialize ( ) }
476
501
477
502
// The innermost scope that must be extended must dominate all uses.
478
503
var useRange = InstructionRange ( begin: scopeExtension. innerScope. extendableBegin!. instruction, context)
479
- let function = value. parentFunction
480
504
var walker = LifetimeDependentUseWalker ( function, localReachabilityCache, context) {
481
505
// Do not extend the useRange past the ownershipRange.
482
506
let dependentInst = $0. instruction
@@ -487,17 +511,20 @@ private func computeDependentUseRange(of value: Value, within scopeExtension: in
487
511
}
488
512
defer { walker. deinitialize ( ) }
489
513
490
- _ = walker. walkDown ( root : value )
514
+ _ = walker. walkDown ( dependence : dependence )
491
515
492
516
log ( " Scope fixup for dependent uses: \n \( useRange) " )
493
517
494
518
scopeExtension. dependsOnCaller = walker. dependsOnCaller
495
519
496
520
// Lifetime dependenent uses may not be dominated by the access. The dependent value may be used by a phi or stored
497
521
// 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.
499
525
let firstInst = function. entryBlock. instructions. first!
500
526
if firstInst != useRange. begin, useRange. contains ( firstInst) {
527
+ useRange. deinitialize ( )
501
528
return nil
502
529
}
503
530
return useRange
0 commit comments