Skip to content

Commit 4934b79

Browse files
authored
Merge pull request #77527 from eeckstein/fix-reborrow-flag
Fix the computation of the re-borrow flags for guaranteed phi arguments
2 parents fa819fe + 156f1fc commit 4934b79

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+391
-252
lines changed

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,13 @@ extension BasicBlock {
628628
}
629629
}
630630

631+
extension Argument {
632+
func set(reborrow: Bool, _ context: some MutatingContext) {
633+
context.notifyInstructionsChanged()
634+
bridged.setReborrow(reborrow)
635+
}
636+
}
637+
631638
extension AllocRefInstBase {
632639
func setIsStackAllocatable(_ context: some MutatingContext) {
633640
context.notifyInstructionsChanged()

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,5 @@ private func registerSwiftAnalyses() {
136136

137137
private func registerUtilities() {
138138
registerVerifier()
139-
registerBorrowedFromUpdater()
139+
registerGuaranteedPhiUpdater()
140140
}

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ func gatherEnclosingValuesFromPredecessors(
592592
let incomingOperand = phi.incomingOperand(inPredecessor: predecessor)
593593

594594
for predEV in incomingOperand.value.getEnclosingValues(context) {
595-
let ev = predecessor.mapToPhiInSuccessor(incomingEnclosingValue: predEV)
595+
let ev = predecessor.getEnclosingValueInSuccessor(ofIncoming: predEV)
596596
if alreadyAdded.insert(ev) {
597597
enclosingValues.push(ev)
598598
}
@@ -601,13 +601,31 @@ func gatherEnclosingValuesFromPredecessors(
601601
}
602602

603603
extension BasicBlock {
604-
func mapToPhiInSuccessor(incomingEnclosingValue: Value) -> Value {
604+
// Returns either the `incomingEnclosingValue` or an adjacent phi in the successor block.
605+
func getEnclosingValueInSuccessor(ofIncoming incomingEnclosingValue: Value) -> Value {
605606
let branch = terminator as! BranchInst
606-
if let incomingEV = branch.operands.first(where: { $0.value.lookThroughBorrowedFrom == incomingEnclosingValue }) {
607+
if let incomingEV = branch.operands.first(where: { branchOp in
608+
// Only if the lifetime of `branchOp` ends at the branch (either because it's a reborrow or an owned value),
609+
// the corresponding phi argument can be the adjacent phi for the incoming value.
610+
// bb1:
611+
// %incomingEnclosingValue = some_owned_value
612+
// %2 = begin_borrow %incomingEnclosingValue // %incomingEnclosingValue = the enclosing value of %2 in bb1
613+
// br bb2(%incomingEnclosingValue, %2) // lifetime of incomingEnclosingValue ends here
614+
// bb2(%4 : @owned, %5 : @guaranteed): // -> %4 = the enclosing value of %5 in bb2
615+
//
616+
branchOp.endsLifetime &&
617+
branchOp.value.lookThroughBorrowedFrom == incomingEnclosingValue
618+
}) {
607619
return branch.getArgument(for: incomingEV)
608620
}
609-
// No candidates phi are outer-adjacent phis. The incoming
610-
// `predDef` must dominate the current guaranteed phi.
621+
// No candidates phi are outer-adjacent phis. The incomingEnclosingValue must dominate the successor block.
622+
// bb1: // dominates bb3
623+
// %incomingEnclosingValue = some_owned_value
624+
// bb2:
625+
// %2 = begin_borrow %incomingEnclosingValue
626+
// br bb3(%2)
627+
// bb3(%5 : @guaranteed): // -> %incomingEnclosingValue = the enclosing value of %5 in bb3
628+
//
611629
return incomingEnclosingValue
612630
}
613631
}

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
AccessUtilsTest.swift
1111
AddressUtils.swift
12-
BorrowedFromUpdater.swift
12+
GuaranteedPhiUpdater.swift
1313
BorrowUtils.swift
1414
SpecializationCloner.swift
1515
DiagnosticEngine.swift

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowedFromUpdater.swift renamed to SwiftCompilerSources/Sources/Optimizer/Utilities/GuaranteedPhiUpdater.swift

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- BorrowedFromUpdater.swift ----------------------------------------===//
1+
//===--- GuaranteedPhiUpdater.swift ---------------------------------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -13,6 +13,18 @@
1313
import SIL
1414
import OptimizerBridging
1515

16+
/// Updates the reborrow flags and the borrowed-from instructions for all guaranteed phis in `function`.
17+
func updateGuaranteedPhis(in function: Function, _ context: some MutatingContext) {
18+
updateReborrowFlags(in: function, context)
19+
updateBorrowedFrom(in: function, context)
20+
}
21+
22+
/// Updates the reborrow flags and the borrowed-from instructions for all `phis`.
23+
func updateGuaranteedPhis(phis: some Sequence<Phi>, _ context: some MutatingContext) {
24+
updateReborrowFlags(for: phis, context)
25+
updateBorrowedFrom(for: phis, context)
26+
}
27+
1628
/// Update all borrowed-from instructions in the `function`
1729
func updateBorrowedFrom(in function: Function, _ context: some MutatingContext) {
1830
if !function.hasOwnership {
@@ -54,6 +66,47 @@ func updateBorrowedFrom(for phis: some Sequence<Phi>, _ context: some MutatingCo
5466
} while changed
5567
}
5668

69+
/// Updates the reborrow flags for all guaranteed phis in `function`.
70+
func updateReborrowFlags(in function: Function, _ context: some MutatingContext) {
71+
if !function.hasOwnership {
72+
return
73+
}
74+
var guaranteedPhis = Stack<Phi>(context)
75+
defer { guaranteedPhis.deinitialize() }
76+
77+
for block in function.blocks.reversed() {
78+
for arg in block.arguments {
79+
if let phi = Phi(arg), phi.value.ownership == .guaranteed {
80+
guaranteedPhis.append(phi)
81+
}
82+
}
83+
}
84+
updateReborrowFlags(for: guaranteedPhis, context)
85+
}
86+
87+
/// Updates the reborrow flags for all `phis`.
88+
func updateReborrowFlags(for phis: some Sequence<Phi>, _ context: some MutatingContext) {
89+
// TODO: clear reborrow flags before re-computing when we have complete OSSA lifetimes.
90+
// It would be cleaner to first clear all flags. But this is not possible because some end_borrow instructions
91+
// might be missing in dead-end blocks. This will be fixed with complete OSSA lifetimes.
92+
93+
if let phi = phis.first(where: { phi in true }), !phi.value.parentFunction.hasOwnership {
94+
return
95+
}
96+
97+
var changed: Bool
98+
repeat {
99+
changed = false
100+
101+
for phi in phis where phi.value.ownership == .guaranteed {
102+
if !phi.value.isReborrow && phi.hasBorrowEndingUse {
103+
phi.value.set(reborrow: true, context)
104+
changed = true
105+
}
106+
}
107+
} while changed
108+
}
109+
57110
private func updateBorrowedFrom(for phi: Phi, _ context: some MutatingContext) -> Bool {
58111
var computedEVs = Stack<Value>(context)
59112
defer { computedEVs.deinitialize() }
@@ -98,12 +151,12 @@ private func addEnclosingValues(
98151
return true
99152
}
100153

101-
func registerBorrowedFromUpdater() {
102-
BridgedUtilities.registerBorrowedFromUpdater(
154+
func registerGuaranteedPhiUpdater() {
155+
BridgedUtilities.registerGuaranteedPhiUpdater(
103156
{ (bridgedCtxt: BridgedPassContext, bridgedFunction: BridgedFunction) in
104157
let context = FunctionPassContext(_bridged: bridgedCtxt)
105158
let function = bridgedFunction.function;
106-
updateBorrowedFrom(in: function, context)
159+
updateGuaranteedPhis(in: function, context)
107160
},
108161
{ (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in
109162
let context = FunctionPassContext(_bridged: bridgedCtxt)
@@ -117,7 +170,7 @@ func registerBorrowedFromUpdater() {
117170
}
118171
}
119172
}
120-
updateBorrowedFrom(for: guaranteedPhis, context)
173+
updateGuaranteedPhis(phis: guaranteedPhis, context)
121174
}
122175
)
123176
}

SwiftCompilerSources/Sources/Optimizer/Utilities/Verifier.swift

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,32 @@ private extension Instruction {
5757
private extension Argument {
5858
func verify(_ context: FunctionPassContext) {
5959
if let phi = Phi(self), phi.value.ownership == .guaranteed {
60-
var forwardingBorrowedFromFound = false
61-
for use in phi.value.uses {
62-
require(use.instruction is BorrowedFromInst,
63-
"guaranteed phi: \(self)\n has non borrowed-from use: \(use)")
64-
if use.index == 0 {
65-
require(!forwardingBorrowedFromFound, "phi \(self) has multiple forwarding borrowed-from uses")
66-
forwardingBorrowedFromFound = true
67-
}
60+
61+
phi.verifyBorrowedFromUse()
62+
63+
require(phi.isReborrow == phi.hasBorrowEndingUse ||
64+
// In a dead-end block an end_borrow might have been deleted.
65+
// TODO: this check is not needed anymore once we have complete OSSA lifetimes.
66+
(isReborrow && context.deadEndBlocks.isDeadEnd(parentBlock)),
67+
"\(self) has stale reborrow flag");
68+
}
69+
}
70+
71+
}
72+
73+
private extension Phi {
74+
func verifyBorrowedFromUse() {
75+
var forwardingBorrowedFromFound = false
76+
for use in value.uses {
77+
require(use.instruction is BorrowedFromInst,
78+
"guaranteed phi: \(self)\n has non borrowed-from use: \(use)")
79+
if use.index == 0 {
80+
require(!forwardingBorrowedFromFound, "phi \(self) has multiple forwarding borrowed-from uses")
81+
forwardingBorrowedFromFound = true
6882
}
69-
require (forwardingBorrowedFromFound,
70-
"missing forwarding borrowed-from user of guaranteed phi \(phi)")
7183
}
84+
require(forwardingBorrowedFromFound,
85+
"missing forwarding borrowed-from user of guaranteed phi \(self)")
7286
}
7387
}
7488

SwiftCompilerSources/Sources/SIL/Argument.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public class Argument : Value, Hashable {
2424
return bridged.getParent().block
2525
}
2626

27-
var bridged: BridgedArgument { BridgedArgument(obj: SwiftObject(self)) }
28-
27+
public var bridged: BridgedArgument { BridgedArgument(obj: SwiftObject(self)) }
28+
2929
public var index: Int {
3030
return parentBlock.arguments.firstIndex(of: self)!
3131
}
@@ -165,6 +165,18 @@ public struct Phi {
165165
return nil
166166
}
167167

168+
// Returns true if the phi has an end_borrow or a re-borrowing branch as user.
169+
// This should be consistent with the `isReborrow` flag.
170+
public var hasBorrowEndingUse: Bool {
171+
let phiValue: Value = borrowedFrom ?? value
172+
return phiValue.uses.contains {
173+
switch $0.ownership {
174+
case .endBorrow, .reborrow: return true
175+
default: return false
176+
}
177+
}
178+
}
179+
168180
public static func ==(lhs: Phi, rhs: Phi) -> Bool {
169181
lhs.value === rhs.value
170182
}

include/swift/SIL/OwnershipUtils.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,6 @@ bool canOpcodeForwardInnerGuaranteedValues(SILValue value);
5757
/// the operation may be trivially rewritten with Guaranteed ownership.
5858
bool canOpcodeForwardInnerGuaranteedValues(Operand *use);
5959

60-
bool computeIsScoped(SILArgument *arg);
61-
62-
bool computeIsReborrow(SILArgument *arg);
63-
64-
// This is the use-def equivalent of use->getOperandOwnership() ==
65-
// OperandOwnership::GuaranteedForwarding.
66-
bool computeIsGuaranteedForwarding(SILValue value);
67-
6860
/// Is the opcode that produces \p value capable of forwarding owned values?
6961
///
7062
/// This may be true even if the current instance of the instruction is not a
@@ -626,6 +618,12 @@ struct BorrowedValue {
626618
/// BorrowScopeIntroducingValue::isLocalScope().
627619
bool visitLocalScopeEndingUses(function_ref<bool(Operand *)> visitor) const;
628620

621+
/// Returns false if the value has no scope-ending uses because all control flow
622+
/// paths end in dead-end blocks.
623+
bool hasLocalScopeEndingUses() const {
624+
return !visitLocalScopeEndingUses([](Operand *) { return false; });
625+
}
626+
629627
bool isLocalScope() const { return kind.isLocalScope(); }
630628

631629
/// Add this scope's live blocks into the PrunedLiveness result. This
@@ -1404,6 +1402,10 @@ bool isNestedLexicalBeginBorrow(BeginBorrowInst *bbi);
14041402
/// then the move_value is redundant.
14051403
bool isRedundantMoveValue(MoveValueInst *mvi);
14061404

1405+
/// Sets the reborrow flags for all transitively incoming phi-arguments of
1406+
/// `forEndBorrowValue`, which is the operand value of an `end_borrow`.
1407+
void updateReborrowFlags(SILValue forEndBorrowValue);
1408+
14071409
} // namespace swift
14081410

14091411
#endif

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ struct BridgedArgument {
864864
BRIDGED_INLINE swift::SILArgument * _Nonnull getArgument() const;
865865
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedBasicBlock getParent() const;
866866
BRIDGED_INLINE bool isReborrow() const;
867+
BRIDGED_INLINE void setReborrow(bool reborrow) const;
867868
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getVarDecl() const;
868869
BRIDGED_INLINE void copyFlags(BridgedArgument fromArgument) const;
869870
};

include/swift/SIL/SILBridgingImpl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ BridgedBasicBlock BridgedArgument::getParent() const {
672672
}
673673

674674
bool BridgedArgument::isReborrow() const { return getArgument()->isReborrow(); }
675+
void BridgedArgument::setReborrow(bool reborrow) const {
676+
getArgument()->setReborrow(reborrow);
677+
}
675678

676679
OptionalBridgedDeclObj BridgedArgument::getVarDecl() const {
677680
return {llvm::dyn_cast_or_null<swift::VarDecl>(getArgument()->getDecl())};

include/swift/SIL/SILBuilder.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -941,14 +941,7 @@ class SILBuilder {
941941
return lowering.emitStore(*this, Loc, Src, DestAddr, Qualifier);
942942
}
943943

944-
EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue) {
945-
ASSERT(!SILArgument::isTerminatorResult(borrowedValue) &&
946-
"terminator results do not have end_borrow");
947-
ASSERT(!isa<SILFunctionArgument>(borrowedValue) &&
948-
"Function arguments should never have an end_borrow");
949-
return insert(new (getModule())
950-
EndBorrowInst(getSILDebugLocation(loc), borrowedValue));
951-
}
944+
EndBorrowInst *createEndBorrow(SILLocation loc, SILValue borrowedValue);
952945

953946
BeginAccessInst *createBeginAccess(SILLocation loc, SILValue address,
954947
SILAccessKind accessKind,

include/swift/SIL/SILValue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,9 +1073,11 @@ class Operand {
10731073
removeFromCurrent();
10741074
TheValue = newValue;
10751075
insertIntoCurrent();
1076+
updateReborrowFlags();
10761077
verify();
10771078
}
10781079

1080+
void updateReborrowFlags();
10791081
void verify() const;
10801082

10811083
/// Swap the given operand with the current one.

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ struct BridgedPostDomTree {
130130

131131
struct BridgedUtilities {
132132
typedef void (* _Nonnull VerifyFunctionFn)(BridgedPassContext, BridgedFunction);
133-
typedef void (* _Nonnull UpdateBorrowedFromFn)(BridgedPassContext, BridgedFunction);
134-
typedef void (* _Nonnull UpdateBorrowedFromPhisFn)(BridgedPassContext, BridgedArrayRef);
133+
typedef void (* _Nonnull UpdateFunctionFn)(BridgedPassContext, BridgedFunction);
134+
typedef void (* _Nonnull UpdatePhisFn)(BridgedPassContext, BridgedArrayRef);
135135

136136
static void registerVerifier(VerifyFunctionFn verifyFunctionFn);
137-
static void registerBorrowedFromUpdater(UpdateBorrowedFromFn updateBorrowedFromFn,
138-
UpdateBorrowedFromPhisFn updateBorrowedFromPhisFn);
137+
static void registerGuaranteedPhiUpdater(UpdateFunctionFn updateBorrowedFromFn,
138+
UpdatePhisFn updateBorrowedFromPhisFn);
139139
};
140140

141141
struct BridgedBasicBlockSet {

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,12 @@ bool extendStoreBorrow(StoreBorrowInst *sbi,
357357
DeadEndBlocks *deadEndBlocks,
358358
InstModCallbacks callbacks = InstModCallbacks());
359359

360-
void updateBorrowedFrom(SILPassManager *pm, SILFunction *f);
360+
/// Updates the reborrow flags and the borrowed-from instructions for all
361+
/// guaranteed phis in function `f`.
362+
void updateAllGuaranteedPhis(SILPassManager *pm, SILFunction *f);
361363

362-
void updateBorrowedFromPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);
364+
/// Updates the reborrow flags and the borrowed-from instructions for all `phis`.
365+
void updateGuaranteedPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);
363366

364367
} // namespace swift
365368

lib/SIL/IR/SILBuilder.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/SIL/SILBuilder.h"
1414
#include "swift/AST/Expr.h"
1515
#include "swift/Basic/Assertions.h"
16+
#include "swift/SIL/OwnershipUtils.h"
1617
#include "swift/SIL/Projection.h"
1718
#include "swift/SIL/SILGlobalVariable.h"
1819

@@ -681,6 +682,17 @@ void SILBuilder::emitScopedBorrowOperation(SILLocation loc, SILValue original,
681682
createEndBorrow(loc, value);
682683
}
683684

685+
EndBorrowInst *SILBuilder::createEndBorrow(SILLocation loc, SILValue borrowedValue) {
686+
ASSERT(!SILArgument::isTerminatorResult(borrowedValue) &&
687+
"terminator results do not have end_borrow");
688+
ASSERT(!isa<SILFunctionArgument>(borrowedValue) &&
689+
"Function arguments should never have an end_borrow");
690+
updateReborrowFlags(borrowedValue);
691+
return insert(new (getModule())
692+
EndBorrowInst(getSILDebugLocation(loc), borrowedValue));
693+
}
694+
695+
684696
SILPhiArgument *SILBuilder::createSwitchOptional(
685697
SILLocation loc, SILValue operand,
686698
SILBasicBlock *someBB, SILBasicBlock *noneBB,

0 commit comments

Comments
 (0)