Skip to content

Commit ef940c5

Browse files
committed
Redesign gatherBorrowIntroducers to return BeginBorrowValues.
This avoids a lot of confusion because the callers expect this type. Fixing it just required some redundancy and bridging in the EnclosigValues implementation.
1 parent a0c85be commit ef940c5

File tree

3 files changed

+79
-55
lines changed

3 files changed

+79
-55
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -393,8 +393,9 @@ enum BeginBorrowValue {
393393
/// %field = ref_element_addr %first // (none)
394394
/// %load = load_borrow %field : $*C // %load
395395
func gatherBorrowIntroducers(for value: Value,
396-
in borrowIntroducers: inout Stack<Value>,
396+
in borrowIntroducers: inout Stack<BeginBorrowValue>,
397397
_ context: Context) {
398+
assert(value.ownership == .guaranteed)
398399

399400
// Cache introducers across multiple instances of BorrowIntroducers.
400401
var cache = BorrowIntroducers.Cache(context)
@@ -404,7 +405,7 @@ func gatherBorrowIntroducers(for value: Value,
404405
}
405406

406407
private struct BorrowIntroducers {
407-
typealias CachedIntroducers = SingleInlineArray<Value>
408+
typealias CachedIntroducers = SingleInlineArray<BeginBorrowValue>
408409
struct Cache {
409410
// Cache the introducers already found for each SILValue.
410411
var valueIntroducers: Dictionary<HashableValue, CachedIntroducers>
@@ -430,21 +431,21 @@ private struct BorrowIntroducers {
430431
// introducer set to avoid adding duplicates.
431432
var visitedIntroducers: Set<HashableValue> = Set()
432433

433-
static func gather(for value: Value, in introducers: inout Stack<Value>,
434+
static func gather(for value: Value, in introducers: inout Stack<BeginBorrowValue>,
434435
_ cache: inout Cache, _ context: Context) {
435436
var borrowIntroducers = BorrowIntroducers(context: context)
436437
borrowIntroducers.gather(for: value, in: &introducers, &cache)
437438
}
438439

439-
private mutating func push(_ introducer: Value,
440-
in introducers: inout Stack<Value>) {
441-
if visitedIntroducers.insert(introducer.hashable).inserted {
442-
introducers.push(introducer)
440+
private mutating func push(_ beginBorrow: BeginBorrowValue,
441+
in introducers: inout Stack<BeginBorrowValue>) {
442+
if visitedIntroducers.insert(beginBorrow.value.hashable).inserted {
443+
introducers.push(beginBorrow)
443444
}
444445
}
445446

446447
private mutating func push<S: Sequence>(contentsOf other: S,
447-
in introducers: inout Stack<Value>) where S.Element == Value {
448+
in introducers: inout Stack<BeginBorrowValue>) where S.Element == BeginBorrowValue {
448449
for elem in other {
449450
push(elem, in: &introducers)
450451
}
@@ -457,8 +458,9 @@ private struct BorrowIntroducers {
457458
//
458459
// Otherwise recurse up the use-def chain to find all introducers.
459460
private mutating func gather(for value: Value,
460-
in introducers: inout Stack<Value>,
461+
in introducers: inout Stack<BeginBorrowValue>,
461462
_ cache: inout Cache) {
463+
assert(value.ownership == .guaranteed)
462464
// Check if this value's introducers have already been added to
463465
// 'introducers' to avoid duplicates and avoid exponential
464466
// recursion on aggregates.
@@ -478,23 +480,12 @@ private struct BorrowIntroducers {
478480
}
479481

480482
private mutating func gatherUncached(for value: Value,
481-
in introducers: inout Stack<Value>,
483+
in introducers: inout Stack<BeginBorrowValue>,
482484
_ cache: inout Cache) {
483-
switch value.ownership {
484-
case .none, .unowned:
485-
return
486-
487-
case .owned:
488-
push(value, in: &introducers);
489-
return
490-
491-
case .guaranteed:
492-
break
493-
}
494485
// BeginBorrowedValue handles the initial scope introducers: begin_borrow,
495486
// load_borrow, & reborrow.
496-
if BeginBorrowValue(value) != nil {
497-
push(value, in: &introducers)
487+
if let beginBorrow = BeginBorrowValue(value) {
488+
push(beginBorrow, in: &introducers)
498489
return
499490
}
500491
// Handle guaranteed forwarding phis
@@ -543,27 +534,35 @@ private struct BorrowIntroducers {
543534
// %reborrow_2 is returned.
544535
//
545536
private mutating func gather(forPhi phi: Phi,
546-
in introducers: inout Stack<Value>,
537+
in introducers: inout Stack<BeginBorrowValue>,
547538
_ cache: inout Cache) {
548539
// Phi cycles are skipped. They cannot contribute any new introducer.
549540
if !cache.pendingPhis.insert(phi.value) {
550541
return
551542
}
552543
for (pred, value) in zip(phi.predecessors, phi.incomingValues) {
544+
switch value.ownership {
545+
case .none:
546+
continue
547+
case .owned, .unowned:
548+
fatalError("unexpected ownership for a guaranteed phi operand")
549+
case .guaranteed:
550+
break
551+
}
553552
// Each phi operand requires a new introducer list and visited
554553
// values set. These values will be remapped to successor phis
555554
// before adding them to the caller's introducer list. It may be
556555
// necessary to revisit a value that was already visited by the
557556
// caller before remapping to phis.
558-
var incomingIntroducers = Stack<Value>(context)
557+
var incomingIntroducers = Stack<BeginBorrowValue>(context)
559558
defer {
560559
incomingIntroducers.deinitialize()
561560
}
562561
BorrowIntroducers.gather(for: value, in: &incomingIntroducers,
563562
&cache, context)
564563
// Map the incoming introducers to an outer-adjacent phi if one exists.
565-
push(contentsOf: mapToPhi(predecessor: pred,
566-
incomingValues: incomingIntroducers),
564+
push(contentsOf: mapToGuaranteedPhi(predecessor: pred,
565+
incomingBorrows: incomingIntroducers),
567566
in: &introducers)
568567
}
569568
// Remove this phi from the pending set. This phi may be visited
@@ -574,13 +573,35 @@ private struct BorrowIntroducers {
574573
}
575574
}
576575

577-
// Given incoming values on a predecessor path, return the
578-
// corresponding values on the successor block. Each incoming value is
576+
// Given incoming borrows on a predecessor path, return the
577+
// corresponding borrows on the successor block. Each incoming borrow is
579578
// either used by a phi in the successor block, or it must dominate
580579
// the successor block.
580+
private func mapToGuaranteedPhi<PredecessorSequence: Sequence<BeginBorrowValue>> (
581+
predecessor: BasicBlock, incomingBorrows: PredecessorSequence)
582+
-> LazyMapSequence<PredecessorSequence, BeginBorrowValue> {
583+
584+
let branch = predecessor.terminator as! BranchInst
585+
// Gather the new introducers for the successor block.
586+
return incomingBorrows.lazy.map { incomingBorrow in
587+
// Find an outer adjacent phi in the successor block.
588+
let incomingValue = incomingBorrow.value
589+
if let incomingOp = branch.operands.first(where: { $0.value == incomingValue }) {
590+
return BeginBorrowValue(branch.getArgument(for: incomingOp))!
591+
}
592+
// No candidates phi are outer-adjacent phis. The incoming
593+
// `predDef` must dominate the current guaranteed phi.
594+
return incomingBorrow
595+
}
596+
}
597+
598+
// Given incoming values on a predecessor path, return the corresponding values on the successor block. Each incoming
599+
// value is either used by a phi in the successor block, or it must dominate the successor block.
600+
//
601+
// This is Logically the same as mapToGuaranteedPhi but more efficient to simply duplicate the code.
581602
private func mapToPhi<PredecessorSequence: Sequence<Value>> (
582603
predecessor: BasicBlock, incomingValues: PredecessorSequence)
583-
-> LazyMapSequence<PredecessorSequence, Value> {
604+
-> LazyMapSequence<PredecessorSequence, Value> {
584605

585606
let branch = predecessor.terminator as! BranchInst
586607
// Gather the new introducers for the successor block.
@@ -612,7 +633,7 @@ private func mapToPhi<PredecessorSequence: Sequence<Value>> (
612633
/// introducers of the outer enclosing borrow scope that contains this
613634
/// inner scope.
614635
///
615-
/// If `value` is a `begin_borrow`, then this returns its operand.
636+
/// If `value` is a `begin_borrow`, then this returns its owned operand, or the introducers of its guaranteed operand.
616637
///
617638
/// If `value` is an owned value, a function argument, or a
618639
/// load_borrow, then this is an empty set.
@@ -725,9 +746,18 @@ private struct EnclosingValues {
725746
if let beginBorrow = BeginBorrowValue(value) {
726747
switch beginBorrow {
727748
case let .beginBorrow(bbi):
749+
let outerValue = bbi.operand.value
750+
switch outerValue.ownership {
751+
case .none, .unowned:
752+
return
753+
case .owned:
754+
push(outerValue, in: &enclosingValues);
755+
return
756+
case .guaranteed:
757+
break
758+
}
728759
// Gather the outer enclosing borrow scope.
729-
BorrowIntroducers.gather(for: bbi.operand.value, in: &enclosingValues,
730-
&cache.borrowIntroducerCache, context)
760+
gatherBorrows(for: outerValue, in: &enclosingValues, &cache)
731761
case .loadBorrow, .beginApply, .functionArgument:
732762
// There is no enclosing value on this path.
733763
break
@@ -736,11 +766,19 @@ private struct EnclosingValues {
736766
}
737767
} else {
738768
// Handle forwarded guaranteed values.
739-
BorrowIntroducers.gather(for: value, in: &enclosingValues,
740-
&cache.borrowIntroducerCache, context)
769+
gatherBorrows(for: value, in: &enclosingValues, &cache)
741770
}
742771
}
743-
772+
773+
mutating func gatherBorrows(for value: Value, in enclosingValues: inout Stack<Value>, _ cache: inout Cache) {
774+
var introducers = Stack<BeginBorrowValue>(context)
775+
defer { introducers.deinitialize() }
776+
BorrowIntroducers.gather(for: value, in: &introducers, &cache.borrowIntroducerCache, context)
777+
for beginBorrow in introducers {
778+
enclosingValues.push(beginBorrow.value)
779+
}
780+
}
781+
744782
// Given a reborrow, find the enclosing values. Each enclosing value
745783
// is represented by one of the following cases, which refer to the
746784
// example below:
@@ -841,12 +879,12 @@ let borrowIntroducersTest = FunctionTest("borrow_introducers") {
841879
let value = arguments.takeValue()
842880
print(function)
843881
print("Borrow introducers for: \(value)")
844-
var introducers = Stack<Value>(context)
882+
var introducers = Stack<BeginBorrowValue>(context)
845883
defer {
846884
introducers.deinitialize()
847885
}
848886
gatherBorrowIntroducers(for: value, in: &introducers, context)
849-
introducers.forEach { print($0) }
887+
introducers.forEach { print($0.value) }
850888
}
851889

852890
let enclosingValuesTest = FunctionTest("enclosing_values") {

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,16 @@ extension LifetimeDependence.Scope {
364364
}
365365

366366
private init?(guaranteed base: Value, _ context: some Context) {
367-
var introducers = Stack<Value>(context)
367+
var introducers = Stack<BeginBorrowValue>(context)
368368
gatherBorrowIntroducers(for: base, in: &introducers, context)
369369
// If introducers is empty, then the dependence is on a trivial value, so
370370
// there is no dependence scope.
371371
//
372372
// TODO: Add a SIL verifier check that a mark_dependence [nonescaping]
373373
// base is never a guaranteed phi.
374-
guard let introducer = introducers.pop() else { return nil }
374+
guard let beginBorrow = introducers.pop() else { return nil }
375375
assert(introducers.isEmpty,
376376
"guaranteed phis not allowed when diagnosing lifetime dependence")
377-
guard let beginBorrow = BeginBorrowValue(introducer) else {
378-
fatalError("unknown borrow introducer")
379-
}
380377
switch beginBorrow {
381378
case .beginBorrow, .loadBorrow:
382379
let borrowOperand = beginBorrow.baseOperand!

test/SILOptimizer/borrow_introducer_unit.sil

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,6 @@ bb2:
340340
unreachable
341341
}
342342

343-
// CHECK-LABEL: introducer_example: borrow_introducers with: %0
344-
// CHECK: Borrow introducers for: %0 = argument of bb0 : $C
345-
// CHECK-NEXT: %0 = argument of bb0 : $C
346-
// CHECK-NEXT: introducer_example: borrow_introducers with: %0
347-
348343
// CHECK-LABEL: introducer_example: borrow_introducers with: %borrow0
349344
// CHECK: Borrow introducers for: %{{.*}} = begin_borrow %0 : $C
350345
// CHECK-NEXT: %{{.*}} = begin_borrow %0 : $C
@@ -356,25 +351,19 @@ bb2:
356351
// CHECK-NEXT: %{{.*}} = argument of bb0 : $C
357352
// CHECK-NEXT: introducer_example: borrow_introducers with: %first
358353

359-
// CHECK-LABEL: introducer_example: borrow_introducers with: %field
360-
// CHECK: Borrow introducers for: %{{.*}} = ref_element_addr %{{.*}} : $C, #C.field
361-
// CHECK-NEXT: introducer_example: borrow_introducers with: %field
362-
363354
// CHECK-LABEL: introducer_example: borrow_introducers with: %load
364355
// CHECK-LABEL: Borrow introducers for: %{{.*}} = load_borrow %{{.*}} : $*C
365356
// CHECK-NEXT: %{{.*}} = load_borrow %{{.*}} : $*C
366357
// CHECK-NEXT: introducer_example: borrow_introducers with: %
367358
sil [ossa] @introducer_example : $@convention(thin) (@owned C, @guaranteed C) -> () {
368359
bb0(%0 : @owned $C,
369360
%1 : @guaranteed $C):
370-
specify_test "borrow_introducers %0"
371361
%borrow0 = begin_borrow %0 : $C
372362
specify_test "borrow_introducers %borrow0"
373363
%pair = struct $PairC(%borrow0 : $C, %1 : $C)
374364
%first = struct_extract %pair : $PairC, #PairC.first
375365
specify_test "borrow_introducers %first"
376366
%field = ref_element_addr %first : $C, #C.field
377-
specify_test "borrow_introducers %field"
378367
%load = load_borrow %field : $*C
379368
specify_test "borrow_introducers %load"
380369
end_borrow %load : $C
@@ -417,7 +406,7 @@ bb3:
417406
// visited once on each path, and each time the recursive algorithm
418407
// must find the enclosing def %0, which maps to a %outer0 on one path
419408
// and %outer1 on another path. If the cache fails, then we only find
420-
// one of the outer adjacent phis as and enclosing value for %inner.
409+
// one of the outer adjacent phis as an enclosing value for %inner.
421410
//
422411
// CHECK-LABEL: testEnclosingRevisitReborrow: enclosing_values with: %inner
423412
// CHECK: Enclosing values for: %10 = argument of bb4 : $C // user: %11

0 commit comments

Comments
 (0)