@@ -388,138 +388,172 @@ extension Value {
388
388
}
389
389
}
390
390
391
- /// Find the borrow introducers for `value`. This gives you a set of
392
- /// OSSA lifetimes that directly include `value`. If `value` is owned,
393
- /// or introduces a borrow scope, then `value` is the single
394
- /// introducer for itself.
395
- ///
396
- /// If `value` is an address or any trivial type, then it has no introducers.
397
- ///
398
- /// Example: // introducers:
399
- /// // ~~~~~~~~~~~~
400
- /// bb0(%0 : @owned $Class, // %0
401
- /// %1 : @guaranteed $Class): // %1
402
- /// %borrow0 = begin_borrow %0 // %borrow0
403
- /// %pair = struct $Pair(%borrow0, %1) // %borrow0, %1
404
- /// %first = struct_extract %pair // %borrow0, %1
405
- /// %field = ref_element_addr %first // (none)
406
- /// %load = load_borrow %field : $*C // %load
407
- func gatherBorrowIntroducers( for initialValue: Value ,
408
- in borrowIntroducers: inout Stack < Value > ,
409
- _ context: some Context )
410
- {
411
- var worklist = ValueWorklist ( context)
412
- defer { worklist. deinitialize ( ) }
413
-
414
- worklist. pushIfNotVisited ( initialValue)
415
- while let value = worklist. pop ( ) {
416
- switch value. ownership {
417
- case . none, . unowned:
418
- break
419
-
420
- case . owned:
421
- borrowIntroducers. append ( value)
422
-
423
- case . guaranteed:
424
- if BeginBorrowValue ( value) != nil {
425
- borrowIntroducers. append ( value)
426
- } else if let bfi = value as? BorrowedFromInst ,
427
- let phi = Phi ( bfi. borrowedValue)
428
- {
429
- if phi. isReborrow {
430
- worklist. pushIfNotVisited ( phi. value)
391
+ struct BorrowIntroducers < Ctxt: Context > : CollectionLikeSequence {
392
+ let initialValue : Value
393
+ let context : Ctxt
394
+
395
+ func makeIterator( ) -> EnclosingValueIterator {
396
+ EnclosingValueIterator ( forBorrowIntroducers: initialValue, context)
397
+ }
398
+ }
399
+
400
+ struct EnclosingValues < Ctxt: Context > : CollectionLikeSequence {
401
+ let initialValue : Value
402
+ let context : Ctxt
403
+
404
+ func makeIterator( ) -> EnclosingValueIterator {
405
+ EnclosingValueIterator ( forEnclosingValues: initialValue, context)
406
+ }
407
+ }
408
+
409
+ // This iterator must be a class because we need a deinit.
410
+ // It shouldn't be a performance problem because the optimizer should always be able to stack promote the iterator.
411
+ // TODO: Make it a struct once this is possible with non-copyable types.
412
+ final class EnclosingValueIterator : IteratorProtocol {
413
+ var worklist : ValueWorklist
414
+
415
+ init ( forBorrowIntroducers value: Value , _ context: some Context ) {
416
+ self . worklist = ValueWorklist ( context)
417
+ self . worklist. pushIfNotVisited ( value)
418
+ }
419
+
420
+ init ( forEnclosingValues value: Value , _ context: some Context ) {
421
+ self . worklist = ValueWorklist ( context)
422
+ if value is Undef || value. ownership != . guaranteed {
423
+ return
424
+ }
425
+ if let beginBorrow = BeginBorrowValue ( value. lookThroughBorrowedFrom) {
426
+ switch beginBorrow {
427
+ case let . beginBorrow( bbi) :
428
+ // Gather the outer enclosing borrow scope.
429
+ worklist. pushIfNotVisited ( bbi. borrowedValue)
430
+ case . loadBorrow, . beginApply, . functionArgument:
431
+ // There is no enclosing value on this path.
432
+ break
433
+ case . reborrow( let phi) :
434
+ worklist. pushIfNotVisited ( contentsOf: phi. borrowedFrom!. enclosingValues)
435
+ }
436
+ } else {
437
+ // Handle forwarded guaranteed values.
438
+ worklist. pushIfNotVisited ( value)
439
+ }
440
+ }
441
+
442
+ deinit {
443
+ worklist. deinitialize ( )
444
+ }
445
+
446
+ func next( ) -> Value ? {
447
+ while let value = worklist. pop ( ) {
448
+ switch value. ownership {
449
+ case . none, . unowned:
450
+ break
451
+
452
+ case . owned:
453
+ return value
454
+
455
+ case . guaranteed:
456
+ if BeginBorrowValue ( value) != nil {
457
+ return value
458
+ } else if let bfi = value as? BorrowedFromInst {
459
+ if bfi. borrowedPhi. isReborrow {
460
+ worklist. pushIfNotVisited ( bfi. borrowedValue)
461
+ } else {
462
+ worklist. pushIfNotVisited ( contentsOf: bfi. enclosingValues)
463
+ }
464
+ } else if let forwardingInst = value. forwardingInstruction {
465
+ // Recurse through guaranteed forwarding non-phi instructions.
466
+ let ops = forwardingInst. forwardedOperands
467
+ worklist. pushIfNotVisited ( contentsOf: ops. lazy. map { $0. value } )
431
468
} else {
432
- worklist . pushIfNotVisited ( contentsOf : bfi . enclosingValues )
469
+ fatalError ( " cannot get borrow introducers for unknown guaranteed value " )
433
470
}
434
- } else if let forwardingInst = value. forwardingInstruction {
435
- // Recurse through guaranteed forwarding non-phi instructions.
436
- let ops = forwardingInst. forwardedOperands
437
- worklist. pushIfNotVisited ( contentsOf: ops. lazy. map { $0. value } )
438
- } else {
439
- fatalError ( " cannot get borrow introducers for unknown guaranteed value " )
440
471
}
441
472
}
473
+ return nil
442
474
}
443
475
}
444
476
445
- /// Find each "enclosing value" whose OSSA lifetime immediately
446
- /// encloses a guaranteed value. The guaranteed `value` being enclosed
447
- /// effectively keeps these enclosing values alive. This lets you walk
448
- /// up the levels of nested OSSA lifetimes to determine all the
449
- /// lifetimes that are kept alive by a given SILValue. In particular,
450
- /// it discovers "outer-adjacent phis": phis that are kept alive by
451
- /// uses of another phi in the same block.
452
- ///
453
- /// If `value` is a forwarded guaranteed value, then this finds the
454
- /// introducers of the current borrow scope, which is never an empty
455
- /// set.
456
- ///
457
- /// If `value` introduces a borrow scope, then this finds the
458
- /// introducers of the outer enclosing borrow scope that contains this
459
- /// inner scope.
460
- ///
461
- /// If `value` is a `begin_borrow`, then this returns its operand.
462
- ///
463
- /// If `value` is an owned value, a function argument, or a
464
- /// load_borrow, then this is an empty set.
465
- ///
466
- /// If `value` is a reborrow, then this either returns a dominating
467
- /// enclosing value or an outer adjacent phi.
468
- ///
469
- /// Example: // enclosing value:
470
- /// // ~~~~~~~~~~~~
471
- /// bb0(%0 : @owned $Class, // (none)
472
- /// %1 : @guaranteed $Class): // (none)
473
- /// %borrow0 = begin_borrow %0 // %0
474
- /// %pair = struct $Pair(%borrow0, %1) // %borrow0, %1
475
- /// %first = struct_extract %pair // %borrow0, %1
476
- /// %field = ref_element_addr %first // (none)
477
- /// %load = load_borrow %field : $*C // %load
478
- ///
479
- /// Example: // enclosing value:
480
- /// // ~~~~~~~~~~~~
481
- /// %outerBorrow = begin_borrow %0 // %0
482
- /// %innerBorrow = begin_borrow %outerBorrow // %outerBorrow
483
- /// br bb1(%outerBorrow, %innerBorrow)
484
- /// bb1(%outerReborrow : @reborrow, // %0
485
- /// %innerReborrow : @reborrow) // %outerReborrow
486
- ///
487
- func gatherEnclosingValues( for value: Value ,
488
- in enclosingValues: inout Stack < Value > ,
489
- _ context: some Context )
490
- {
491
- if value is Undef || value. ownership != . guaranteed {
492
- return
477
+
478
+ extension Value {
479
+ /// Get the borrow introducers for `value`. This gives you a set of
480
+ /// OSSA lifetimes that directly include `value`. If `value` is owned,
481
+ /// or introduces a borrow scope, then `value` is the single
482
+ /// introducer for itself.
483
+ ///
484
+ /// If `value` is an address or any trivial type, then it has no introducers.
485
+ ///
486
+ /// Example: // introducers:
487
+ /// // ~~~~~~~~~~~~
488
+ /// bb0(%0 : @owned $Class, // %0
489
+ /// %1 : @guaranteed $Class): // %1
490
+ /// %borrow0 = begin_borrow %0 // %borrow0
491
+ /// %pair = struct $Pair(%borrow0, %1) // %borrow0, %1
492
+ /// %first = struct_extract %pair // %borrow0, %1
493
+ /// %field = ref_element_addr %first // (none)
494
+ /// %load = load_borrow %field : $*C // %load
495
+ func getBorrowIntroducers< Ctxt: Context > ( _ context: Ctxt ) -> BorrowIntroducers < Ctxt > {
496
+ BorrowIntroducers ( initialValue: self , context: context)
493
497
}
494
- if let beginBorrow = BeginBorrowValue ( value. lookThroughBorrowedFrom) {
495
- switch beginBorrow {
496
- case let . beginBorrow( bbi) :
497
- // Gather the outer enclosing borrow scope.
498
- gatherBorrowIntroducers ( for: bbi. borrowedValue, in: & enclosingValues, context)
499
- case . loadBorrow, . beginApply, . functionArgument:
500
- // There is no enclosing value on this path.
501
- break
502
- case . reborrow( let phi) :
503
- enclosingValues. append ( contentsOf: phi. borrowedFrom!. enclosingValues)
504
- }
505
- } else {
506
- // Handle forwarded guaranteed values.
507
- gatherBorrowIntroducers ( for: value, in: & enclosingValues, context)
498
+
499
+ /// Get "enclosing values" whose OSSA lifetime immediately
500
+ /// encloses a guaranteed value. The guaranteed `value` being enclosed
501
+ /// effectively keeps these enclosing values alive. This lets you walk
502
+ /// up the levels of nested OSSA lifetimes to determine all the
503
+ /// lifetimes that are kept alive by a given SILValue. In particular,
504
+ /// it discovers "outer-adjacent phis": phis that are kept alive by
505
+ /// uses of another phi in the same block.
506
+ ///
507
+ /// If `value` is a forwarded guaranteed value, then this finds the
508
+ /// introducers of the current borrow scope, which is never an empty
509
+ /// set.
510
+ ///
511
+ /// If `value` introduces a borrow scope, then this finds the
512
+ /// introducers of the outer enclosing borrow scope that contains this
513
+ /// inner scope.
514
+ ///
515
+ /// If `value` is a `begin_borrow`, then this returns its operand.
516
+ ///
517
+ /// If `value` is an owned value, a function argument, or a
518
+ /// load_borrow, then this is an empty set.
519
+ ///
520
+ /// If `value` is a reborrow, then this either returns a dominating
521
+ /// enclosing value or an outer adjacent phi.
522
+ ///
523
+ /// Example: // enclosing value:
524
+ /// // ~~~~~~~~~~~~
525
+ /// bb0(%0 : @owned $Class, // (none)
526
+ /// %1 : @guaranteed $Class): // (none)
527
+ /// %borrow0 = begin_borrow %0 // %0
528
+ /// %pair = struct $Pair(%borrow0, %1) // %borrow0, %1
529
+ /// %first = struct_extract %pair // %borrow0, %1
530
+ /// %field = ref_element_addr %first // (none)
531
+ /// %load = load_borrow %field : $*C // %load
532
+ ///
533
+ /// Example: // enclosing value:
534
+ /// // ~~~~~~~~~~~~
535
+ /// %outerBorrow = begin_borrow %0 // %0
536
+ /// %innerBorrow = begin_borrow %outerBorrow // %outerBorrow
537
+ /// br bb1(%outerBorrow, %innerBorrow)
538
+ /// bb1(%outerReborrow : @reborrow, // %0
539
+ /// %innerReborrow : @reborrow) // %outerReborrow
540
+ ///
541
+ func getEnclosingValues< Ctxt: Context > ( _ context: Ctxt ) -> EnclosingValues < Ctxt > {
542
+ EnclosingValues ( initialValue: self , context: context)
508
543
}
509
544
}
510
545
511
- /// Find inner adjacent phis in the same block as `enclosingPhi`.
512
- /// These keep the enclosing (outer adjacent) phi alive.
513
- func gatherInnerAdjacentPhis( for enclosingPhi: Phi ,
514
- in innerAdjacentPhis: inout Stack < Phi > ,
515
- _ context: Context )
516
- {
517
- for use in enclosingPhi. value. uses {
518
- if let bfi = use. instruction as? BorrowedFromInst ,
519
- use. index != 0 ,
520
- let innerPhi = Phi ( bfi. borrowedValue)
521
- {
522
- innerAdjacentPhis. append ( innerPhi)
546
+ extension Phi {
547
+ /// Inner adjacent phis in the same block as `enclosingPhi`.
548
+ /// These keep the enclosing (outer adjacent) phi alive.
549
+ var innerAdjacentPhis : some Sequence < Phi > {
550
+ value. uses. lazy. compactMap { use in
551
+ if let bfi = use. instruction as? BorrowedFromInst ,
552
+ use. index != 0
553
+ {
554
+ return Phi ( bfi. borrowedValue)
555
+ }
556
+ return nil
523
557
}
524
558
}
525
559
}
@@ -535,11 +569,7 @@ func gatherEnclosingValuesFromPredecessors(
535
569
for predecessor in phi. predecessors {
536
570
let incomingOperand = phi. incomingOperand ( inPredecessor: predecessor)
537
571
538
- var predecessorEVs = Stack < Value > ( context)
539
- defer { predecessorEVs. deinitialize ( ) }
540
- gatherEnclosingValues ( for: incomingOperand. value, in: & predecessorEVs, context)
541
-
542
- for predEV in predecessorEVs {
572
+ for predEV in incomingOperand. value. getEnclosingValues ( context) {
543
573
let ev = predecessor. mapToPhiInSuccessor ( incomingEnclosingValue: predEV)
544
574
if alreadyAdded. insert ( ev) {
545
575
enclosingValues. push ( ev)
@@ -569,8 +599,9 @@ let borrowIntroducersTest = FunctionTest("borrow_introducers") {
569
599
defer {
570
600
introducers. deinitialize ( )
571
601
}
572
- gatherBorrowIntroducers ( for: value. lookThroughBorrowedFromUser, in: & introducers, context)
573
- introducers. forEach { print ( $0) }
602
+ for bi in value. lookThroughBorrowedFromUser. getBorrowIntroducers ( context) {
603
+ print ( bi)
604
+ }
574
605
}
575
606
576
607
let enclosingValuesTest = FunctionTest ( " enclosing_values " ) {
@@ -582,8 +613,9 @@ let enclosingValuesTest = FunctionTest("enclosing_values") {
582
613
defer {
583
614
enclosing. deinitialize ( )
584
615
}
585
- gatherEnclosingValues ( for: value. lookThroughBorrowedFromUser, in: & enclosing, context)
586
- enclosing. forEach { print ( $0) }
616
+ for ev in value. lookThroughBorrowedFromUser. getEnclosingValues ( context) {
617
+ print ( ev)
618
+ }
587
619
}
588
620
589
621
extension Value {
0 commit comments