@@ -215,10 +215,11 @@ private func extendScopes(dependence: LifetimeDependence,
215215 localReachabilityCache, context) else {
216216 continue
217217 }
218- defer { useRange. deinitialize ( ) }
219-
220- scopeExtension. extend ( over: & useRange, context)
221218
219+ // deinitializes 'useRange'
220+ guard scopeExtension. tryExtendScopes ( over: & useRange, context) else {
221+ continue
222+ }
222223 if scopeExtension. dependsOnCaller, let arg = scopeExtension. dependsOnArg {
223224 dependsOnArgs. push ( arg)
224225 }
@@ -249,15 +250,15 @@ private struct ScopeExtension {
249250
250251private extension LifetimeDependence . Scope {
251252 /// The instruction that introduces an extendable scope. This returns a non-nil scope introducer for
252- /// ScopeExtension.nestedScopes.
253- var extendableBegin : Instruction ? {
253+ /// each scope in ScopeExtension.nestedScopes.
254+ var extendableBegin : ScopedInstruction ? {
254255 switch self {
255256 case let . access( beginAccess) :
256257 return beginAccess
257258 case let . borrowed( beginBorrow) :
258- return beginBorrow. value. definingInstruction!
259+ return beginBorrow. value. definingInstruction as? ScopedInstruction
259260 case let . yield( yieldedValue) :
260- return yieldedValue. definingInstruction!
261+ return yieldedValue. definingInstruction as? ScopedInstruction
261262 case let . initialized( initializer) :
262263 switch initializer {
263264 case let . store( initializingStore: store, initialAddress: _) :
@@ -274,24 +275,32 @@ private extension LifetimeDependence.Scope {
274275 }
275276 }
276277
277- /// Find any nested scopes that may be extended.
278+ /// Precondition: the 'self' scope encloses a dependent value. 'innerScopes' are the extendable scopes enclosed by
279+ /// 'self' that also enclose the dependent value.
278280 ///
279- /// Return 'nil' if 'self' is not extendable.
281+ /// Gather the list of ScopeExtensions. Each extension is a list of scopes, including 'innerScopes', 'self' and,
282+ /// recursively, any of its enclosing scopes that are extendable. We may have multiple extensions because a scope
283+ /// introducer may itself depend on multiple operands.
280284 ///
281- /// TODO: handle trivial variable scopes
285+ /// Return 'nil' if 'self' is not extendable.
282286 func gatherExtensions( innerScopes: SingleInlineArray < LifetimeDependence . Scope > ? = nil , _ context: FunctionPassContext )
283287 -> SingleInlineArray < ScopeExtension > ? {
284288
289+ // Note: LifetimeDependence.Scope.extend() will assume that all inner scopes begin with a ScopedInstruction.
285290 var innerScopes = innerScopes ?? SingleInlineArray ( )
286291 switch self {
287292 case let . access( beginAccess) :
288- let accessExtension = gatherAccessExtension ( beginAccess: beginAccess, innerScopes: & innerScopes)
289- return SingleInlineArray ( element : accessExtension )
293+ return gatherAccessExtension ( beginAccess: beginAccess, innerScopes: & innerScopes, context )
294+
290295 case let . borrowed( beginBorrow) :
291- return gatherBorrowExtension ( borrowedValue: beginBorrow. baseOperand!. value, innerScopes: & innerScopes, context)
296+ // begin_borrow is extendable, so push this scope.
297+ innerScopes. push ( self )
298+ return gatherBorrowExtension ( borrowedValue: beginBorrow. baseOperand!. value, innerScopes: innerScopes, context)
292299
293300 case let . yield( yieldedValue) :
301+ // A yield is extendable, so push this scope.
294302 innerScopes. push ( self )
303+ // Create a separate ScopeExtension for each operand that the yielded value depends on.
295304 var extensions = SingleInlineArray < ScopeExtension > ( )
296305 let applySite = yieldedValue. definingInstruction as! BeginApplyInst
297306 for operand in applySite. parameterOperands {
@@ -306,7 +315,9 @@ private extension LifetimeDependence.Scope {
306315 switch initializer {
307316 case let . store( initializingStore: store, initialAddress: _) :
308317 if let sb = store as? StoreBorrowInst {
309- return gatherBorrowExtension ( borrowedValue: sb. source, innerScopes: & innerScopes, context)
318+ innerScopes. push ( self )
319+ // Only follow the source of the store_borrow. The address is always an alloc_stack without any access scope.
320+ return gatherBorrowExtension ( borrowedValue: sb. source, innerScopes: innerScopes, context)
310321 }
311322 return nil
312323 case . argument, . yield:
@@ -344,7 +355,8 @@ private extension LifetimeDependence.Scope {
344355 // all nested accesses were extended to the return statement, it is valid to logically combine them into a single
345356 // access for the purpose of diagnostinc lifetime dependence.
346357 func gatherAccessExtension( beginAccess: BeginAccessInst ,
347- innerScopes: inout SingleInlineArray < LifetimeDependence . Scope > ) -> ScopeExtension {
358+ innerScopes: inout SingleInlineArray < LifetimeDependence . Scope > ,
359+ _ context: FunctionPassContext ) -> SingleInlineArray < ScopeExtension > {
348360 // Finding the access base also finds all intermediate nested scopes; there is no need to recursively call
349361 // gatherExtensions().
350362 let accessBaseAndScopes = beginAccess. accessBaseWithScopes
@@ -368,22 +380,28 @@ private extension LifetimeDependence.Scope {
368380 }
369381 if case let . argument( arg) = accessBaseAndScopes. base {
370382 if isCompatibleAccess && beginAccess. accessKind. isCompatible ( with: arg. convention) {
371- return ScopeExtension ( owner: outerBeginAccess. address, nestedScopes: innerScopes, dependsOnArg: arg)
383+ let scopes = ScopeExtension ( owner: outerBeginAccess. address, nestedScopes: innerScopes, dependsOnArg: arg)
384+ return SingleInlineArray ( element: scopes)
372385 }
373386 }
374- return ScopeExtension ( owner: outerBeginAccess, nestedScopes: innerScopes, dependsOnArg: nil )
387+ /// Recurse in case of indirect yields.
388+ let enclosingScope = LifetimeDependence . Scope ( base: outerBeginAccess. address, context)
389+ if let extensions = enclosingScope. gatherExtensions ( innerScopes: innerScopes, context) {
390+ return extensions
391+ }
392+ // When the owner is an address, the owner's scope is considered the availability of its access base starting at the
393+ // position of innerScopes.last.
394+ let scopes = ScopeExtension ( owner: outerBeginAccess. address, nestedScopes: innerScopes, dependsOnArg: nil )
395+ return SingleInlineArray ( element: scopes)
375396 }
376397
377398 func gatherBorrowExtension( borrowedValue: Value ,
378- innerScopes: inout SingleInlineArray < LifetimeDependence . Scope > ,
399+ innerScopes: SingleInlineArray < LifetimeDependence . Scope > ,
379400 _ context: FunctionPassContext )
380401 -> SingleInlineArray < ScopeExtension > {
381402
382403 let enclosingScope = LifetimeDependence . Scope ( base: borrowedValue, context)
383- innerScopes. push ( self )
384- var innerBorrowScopes = innerScopes
385- innerBorrowScopes. push ( enclosingScope)
386- if let extensions = enclosingScope. gatherExtensions ( innerScopes: innerBorrowScopes, context) {
404+ if let extensions = enclosingScope. gatherExtensions ( innerScopes: innerScopes, context) {
387405 return extensions
388406 }
389407 // This is the outermost scope to be extended because gatherExtensions did not find an enclosing scope.
@@ -429,7 +447,7 @@ extension ScopeExtension {
429447 {
430448 if owner. type. isAddress {
431449 // Get the range of the accessBase lifetime at the point where the outermost extendable scope begins.
432- if let range = AddressOwnershipLiveRange . compute ( for: owner, at: nestedScopes. last!. extendableBegin!,
450+ if let range = AddressOwnershipLiveRange . compute ( for: owner, at: nestedScopes. last!. extendableBegin!. instruction ,
433451 localReachabilityCache, context) {
434452 return . addressRange( range)
435453 }
@@ -457,7 +475,7 @@ private func computeDependentUseRange(of value: Value, within scopeExtension: in
457475 defer { ownershipRange. deinitialize ( ) }
458476
459477 // The innermost scope that must be extended must dominate all uses.
460- var useRange = InstructionRange ( begin: scopeExtension. innerScope. extendableBegin!, context)
478+ var useRange = InstructionRange ( begin: scopeExtension. innerScope. extendableBegin!. instruction , context)
461479 let function = value. parentFunction
462480 var walker = LifetimeDependentUseWalker ( function, localReachabilityCache, context) {
463481 // Do not extend the useRange past the ownershipRange.
@@ -487,44 +505,64 @@ private func computeDependentUseRange(of value: Value, within scopeExtension: in
487505
488506// Extend nested scopes across a use-range within their owner's range.
489507extension ScopeExtension {
490- func extend( over useRange: inout InstructionRange , _ context: some MutatingContext ) {
491-
492- // Prepare to extend each scope.
493- var scopesToExtend = SingleInlineArray < LifetimeDependence . Scope > ( )
508+ // Prepare to extend each scope.
509+ func tryExtendScopes( over useRange: inout InstructionRange , _ context: some MutatingContext ) -> Bool {
510+ var extendedUseRange = InstructionRange ( begin: useRange. begin!, ends: useRange. ends, context)
511+ // Insert the first instruction of the exit blocks to mimic 'useRange'. This is innacurate, but it produces the same
512+ // result for canExtend() check below, which only checks reachability of end_apply.
513+ extendedUseRange. insert ( contentsOf: useRange. exits)
494514 for innerScope in nestedScopes {
495- guard let beginInst = innerScope. extendableBegin as? ScopedInstruction else {
515+ guard let beginInst = innerScope. extendableBegin else {
496516 fatalError ( " all nested scopes must have a scoped begin instruction " )
497517 }
498- // Extend 'useRange ' to to cover this scope's end instructions. The extended scope must at least cover the
518+ // Extend 'extendedUseRange ' to to cover this scope's end instructions. The extended scope must at least cover the
499519 // original scope because the original scope may protect other operations.
500- var requiresExtension = false
501- for endInst in beginInst. endInstructions {
502- if useRange. contains ( endInst) {
503- // If any end instruction is inside the new range, then all end instructions must be rewritten.
504- requiresExtension = true
505- } else {
506- // Update 'range' with the current scope-ending instructions.
507- useRange. insert ( endInst)
508- }
509- }
510- if !requiresExtension {
511- break
512- }
513- if !innerScope. canExtend ( over: & useRange, context) {
520+ extendedUseRange. insert ( contentsOf: beginInst. endInstructions)
521+ if !innerScope. canExtend ( over: & extendedUseRange, context) {
514522 // Scope ending instructions cannot be inserted at the 'range' boundary. Ignore all nested scopes.
515523 //
516524 // Note: We could still extend previously prepared inner scopes up to this 'innerScope'. To do that, we would
517525 // need to repeat the steps above: treat 'innerScope' as the new owner, and recompute 'useRange'. But this
518526 // scenario could only happen with nested coroutine, where the range boundary is reachable from the outer
519527 // coroutine's EndApply and AbortApply--it is vanishingly unlikely if not impossible.
520- return
528+ return false
521529 }
522- scopesToExtend. push ( innerScope)
523530 }
531+ extendedUseRange. deinitialize ( )
532+ // extend(over:) must receive the original unmodified 'useRange'.
533+ extend ( over: & useRange, context)
534+ return true
535+ }
536+
537+ // Extend the scopes that actually required extension.
538+ //
539+ // Consumes 'useRange'
540+ private func extend( over useRange: inout InstructionRange , _ context: some MutatingContext ) {
541+ var deadInsts = [ Instruction] ( )
542+ for innerScope in nestedScopes {
543+ guard let beginInst = innerScope. extendableBegin else {
544+ fatalError ( " all nested scopes must have a scoped begin instruction " )
545+ }
546+ let mustExtend = beginInst. endInstructions. contains ( where: { useRange. contains ( $0) } )
524547
525- // Extend the scopes that actually required extension.
526- for innerScope in scopesToExtend {
527- innerScope. extend ( over: & useRange, context)
548+ // Extend 'useRange' to to cover this scope's end instructions. 'useRange' cannot be extended until the
549+ // inner scopes have been extended.
550+ for endInst in beginInst. endInstructions {
551+ useRange. insert ( endInst)
552+ }
553+ if mustExtend {
554+ deadInsts += innerScope. extend ( over: & useRange, context)
555+ }
556+ // Continue checking enclosing scopes for extension even if 'mustExtend' is false. Multiple ScopeExtensions may
557+ // share the same inner scope, so this inner scope may already have been extended while handling a previous
558+ // ScopeExtension. Nonetheless, some enclosing scopes may still require extension. This only happens when a
559+ // yielded value depends on multiple begin_apply operands.
560+ }
561+ // 'useRange' is invalid as soon as instructions are deleted.
562+ useRange. deinitialize ( )
563+ // Delete original end instructions.
564+ for deadInst in deadInsts {
565+ context. erase ( instruction: deadInst)
528566 }
529567 }
530568}
@@ -561,9 +599,9 @@ private extension LifetimeDependence.Scope {
561599 }
562600 }
563601
564- /// Extend this scope over the 'range' boundary.
565- func extend( over range: inout InstructionRange , _ context: some MutatingContext ) {
566- guard let beginInst = extendableBegin as? ScopedInstruction else {
602+ /// Extend this scope over the 'range' boundary. Return the old scope ending instructions to be deleted.
603+ func extend( over range: inout InstructionRange , _ context: some MutatingContext ) -> [ Instruction ] {
604+ guard let beginInst = extendableBegin else {
567605 fatalError ( " all nested scoped must have a scoped begin instruction " )
568606 }
569607 // Collect the original end instructions and extend the range to to cover them. The resulting access scope
@@ -574,11 +612,7 @@ private extension LifetimeDependence.Scope {
574612 endInsts. append ( end)
575613 }
576614 insertBoundaryEnds ( range: & range, context)
577-
578- // Delete original end instructions
579- for endInst in endInsts {
580- context. erase ( instruction: endInst)
581- }
615+ return endInsts
582616 }
583617
584618 /// Create new scope-ending instructions at the boundary of 'range'.
@@ -590,44 +624,46 @@ private extension LifetimeDependence.Scope {
590624 // function argument.
591625 let builder = Builder ( before: end, location: location, context)
592626 // Insert newEnd so that this scope will be nested in any outer scopes.
593- range. insert ( createEndInstruction ( builder, context) ! )
627+ range. insert ( createEndInstruction ( builder, context) )
594628 continue
595629 }
596630 Builder . insert ( after: end, location: location, context) {
597- range. insert ( createEndInstruction ( $0, context) ! )
631+ range. insert ( createEndInstruction ( $0, context) )
598632 }
599633 }
600634 for exitInst in range. exits {
601635 let location = exitInst. location. autoGenerated
602636 let builder = Builder ( before: exitInst, location: location, context)
603- range. insert ( createEndInstruction ( builder, context) ! )
637+ range. insert ( createEndInstruction ( builder, context) )
604638 }
605639 }
606640
607641 /// Create a scope-ending instruction at 'builder's insertion point.
608- func createEndInstruction( _ builder: Builder , _ context: some Context ) -> Instruction ? {
642+ func createEndInstruction( _ builder: Builder , _ context: some Context ) -> Instruction {
609643 switch self {
610644 case let . access( beginAccess) :
611645 return builder. createEndAccess ( beginAccess: beginAccess)
612646 case let . borrowed( beginBorrow) :
613647 return builder. createEndBorrow ( of: beginBorrow. value)
614648 case let . yield( yieldedValue) :
615649 let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
616- return beginApply. createEnd ( builder, context)
650+ // createEnd() returns non-nil because beginApply.endReaches() was checked by canExtend()
651+ return beginApply. createEnd ( builder, context) !
617652 case let . initialized( initializer) :
618653 switch initializer {
619654 case let . store( initializingStore: store, initialAddress: _) :
620655 if let sb = store as? StoreBorrowInst {
621656 return builder. createEndBorrow ( of: sb)
622657 }
623- return nil
658+ break
624659 case . argument, . yield:
625660 // TODO: extend indirectly yielded scopes.
626- return nil
661+ break
627662 }
628663 default :
629- return nil
664+ break
630665 }
666+ fatalError ( " Unsupported scoped extension: \( self ) " )
631667 }
632668}
633669
0 commit comments