Skip to content

Commit 09a5a44

Browse files
committed
Optimizer: add a utility to replaces phis with the unique incoming values if all incoming values are the same
This is needed after running the SSAUpdater for an existing OSSA value, because the updater can insert unnecessary phis in the middle of the original liverange which breaks up the original liverange into smaller ones: ``` %1 = def_of_owned_value %2 = begin_borrow %1 ... br bb2(%1) bb2(%3 : @owned $T): // inserted by SSAUpdater ... end_borrow %2 // use after end-of-lifetime! destroy_value %3 ``` It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange.
1 parent bd08d6a commit 09a5a44

File tree

4 files changed

+116
-2
lines changed

4 files changed

+116
-2
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/GuaranteedPhiUpdater.swift

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,100 @@ private func addEnclosingValues(
152152
return true
153153
}
154154

155+
/// Replaces a phi with the unique incoming value if all incoming values are the same:
156+
/// ```
157+
/// bb1:
158+
/// br bb3(%1)
159+
/// bb2:
160+
/// br bb3(%1)
161+
/// bb3(%2 : $T): // Predecessors: bb1, bb2
162+
/// use(%2)
163+
/// ```
164+
/// ->
165+
/// ```
166+
/// bb1:
167+
/// br bb3
168+
/// bb2:
169+
/// br bb3
170+
/// bb3:
171+
/// use(%1)
172+
/// ```
173+
///
174+
func replacePhiWithIncomingValue(phi: Phi, _ context: some MutatingContext) -> Bool {
175+
if phi.predecessors.isEmpty {
176+
return false
177+
}
178+
let uniqueIncomingValue = phi.incomingValues.first!
179+
if !uniqueIncomingValue.parentFunction.hasOwnership {
180+
// For the SSAUpdater it's only required to simplify phis in OSSA.
181+
// This avoids that we need to handle cond_br instructions below.
182+
return false
183+
}
184+
if phi.incomingValues.contains(where: { $0 != uniqueIncomingValue }) {
185+
return false
186+
}
187+
if let borrowedFrom = phi.borrowedFrom {
188+
borrowedFrom.uses.replaceAll(with: uniqueIncomingValue, context)
189+
context.erase(instruction: borrowedFrom)
190+
} else {
191+
phi.value.uses.replaceAll(with: uniqueIncomingValue, context)
192+
}
193+
194+
let block = phi.value.parentBlock
195+
for incomingOp in phi.incomingOperands {
196+
let existingBranch = incomingOp.instruction as! BranchInst
197+
let argsWithRemovedPhiOp = existingBranch.operands.filter{ $0 != incomingOp }.map{ $0.value }
198+
Builder(before: existingBranch, context).createBranch(to: block, arguments: argsWithRemovedPhiOp)
199+
context.erase(instruction: existingBranch)
200+
}
201+
block.eraseArgument(at: phi.value.index, context)
202+
return true
203+
}
204+
205+
/// Replaces phis with the unique incoming values if all incoming values are the same.
206+
/// This is needed after running the SSAUpdater for an existing OSSA value, because the updater can
207+
/// insert unnecessary phis in the middle of the original liverange which breaks up the original
208+
/// liverange into smaller ones:
209+
/// ```
210+
/// %1 = def_of_owned_value
211+
/// %2 = begin_borrow %1
212+
/// ...
213+
/// br bb2(%1)
214+
/// bb2(%3 : @owned $T): // inserted by SSAUpdater
215+
/// ...
216+
/// end_borrow %2 // use after end-of-lifetime!
217+
/// destroy_value %3
218+
/// ```
219+
///
220+
/// It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange.
221+
///
222+
func replacePhisWithIncomingValues(phis: [Phi], _ context: some MutatingContext) {
223+
var currentPhis = phis
224+
// Do this in a loop because replacing one phi might open up the opportunity for another phi
225+
// and the order of phis in the array can be arbitrary.
226+
while true {
227+
var newPhis = [Phi]()
228+
for phi in currentPhis {
229+
if !replacePhiWithIncomingValue(phi: phi, context) {
230+
newPhis.append(phi)
231+
}
232+
}
233+
if newPhis.count == currentPhis.count {
234+
return
235+
}
236+
currentPhis = newPhis
237+
}
238+
}
239+
155240
func registerGuaranteedPhiUpdater() {
156241
BridgedUtilities.registerGuaranteedPhiUpdater(
242+
// updateAllGuaranteedPhis
157243
{ (bridgedCtxt: BridgedPassContext, bridgedFunction: BridgedFunction) in
158244
let context = FunctionPassContext(_bridged: bridgedCtxt)
159245
let function = bridgedFunction.function;
160246
updateGuaranteedPhis(in: function, context)
161247
},
248+
// updateGuaranteedPhis
162249
{ (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in
163250
let context = FunctionPassContext(_bridged: bridgedCtxt)
164251
var guaranteedPhis = Stack<Phi>(context)
@@ -172,6 +259,15 @@ func registerGuaranteedPhiUpdater() {
172259
}
173260
}
174261
updateGuaranteedPhis(phis: guaranteedPhis, context)
262+
},
263+
// replacePhisWithIncomingValues
264+
{ (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in
265+
let context = FunctionPassContext(_bridged: bridgedCtxt)
266+
var phis = [Phi]()
267+
bridgedPhiArray.withElements(ofType: BridgedValue.self) {
268+
phis = $0.map { Phi($0.value)! }
269+
}
270+
replacePhisWithIncomingValues(phis: phis, context)
175271
}
176272
)
177273
}

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ struct BridgedUtilities {
135135

136136
static void registerVerifier(VerifyFunctionFn verifyFunctionFn);
137137
static void registerGuaranteedPhiUpdater(UpdateFunctionFn updateBorrowedFromFn,
138-
UpdatePhisFn updateBorrowedFromPhisFn);
138+
UpdatePhisFn updateBorrowedFromPhisFn,
139+
UpdatePhisFn replacePhisWithIncomingValuesFn);
139140
};
140141

141142
struct BridgedBasicBlockSet {

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ void updateAllGuaranteedPhis(SILPassManager *pm, SILFunction *f);
364364
/// Updates the reborrow flags and the borrowed-from instructions for all `phis`.
365365
void updateGuaranteedPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);
366366

367+
/// Replaces phis with the unique incoming values if all incoming values are the same.
368+
void replacePhisWithIncomingValues(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);
369+
367370
} // namespace swift
368371

369372
#endif

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1915,11 +1915,14 @@ bool swift::extendStoreBorrow(StoreBorrowInst *sbi,
19151915

19161916
static BridgedUtilities::UpdateFunctionFn updateAllGuaranteedPhisFunction;
19171917
static BridgedUtilities::UpdatePhisFn updateGuaranteedPhisFunction;
1918+
static BridgedUtilities::UpdatePhisFn replacePhisWithIncomingValuesFunction;
19181919

19191920
void BridgedUtilities::registerGuaranteedPhiUpdater(UpdateFunctionFn updateAllGuaranteedPhisFn,
1920-
UpdatePhisFn updateGuaranteedPhisFn) {
1921+
UpdatePhisFn updateGuaranteedPhisFn,
1922+
UpdatePhisFn replacePhisWithIncomingValuesFn) {
19211923
updateAllGuaranteedPhisFunction = updateAllGuaranteedPhisFn;
19221924
updateGuaranteedPhisFunction = updateGuaranteedPhisFn;
1925+
replacePhisWithIncomingValuesFunction = replacePhisWithIncomingValuesFn;
19231926
}
19241927

19251928
void swift::updateAllGuaranteedPhis(SILPassManager *pm, SILFunction *f) {
@@ -1937,3 +1940,14 @@ void swift::updateGuaranteedPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *>
19371940
}
19381941
updateGuaranteedPhisFunction({pm->getSwiftPassInvocation()}, ArrayRef(bridgedPhis));
19391942
}
1943+
1944+
void swift::replacePhisWithIncomingValues(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis) {
1945+
if (!replacePhisWithIncomingValuesFunction)
1946+
return;
1947+
1948+
llvm::SmallVector<BridgedValue, 8> bridgedPhis;
1949+
for (SILPhiArgument *phi : phis) {
1950+
bridgedPhis.push_back({phi});
1951+
}
1952+
replacePhisWithIncomingValuesFunction({pm->getSwiftPassInvocation()}, ArrayRef(bridgedPhis));
1953+
}

0 commit comments

Comments
 (0)