Skip to content

LoopRotate: remove redundant phis after ssa-update #78136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,5 @@ private func registerSwiftAnalyses() {

private func registerUtilities() {
registerVerifier()
registerGuaranteedPhiUpdater()
registerPhiUpdater()
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
swift_compiler_sources(Optimizer
AccessUtilsTest.swift
AddressUtils.swift
GuaranteedPhiUpdater.swift
BorrowUtils.swift
SpecializationCloner.swift
DiagnosticEngine.swift
Expand All @@ -22,6 +21,7 @@ swift_compiler_sources(Optimizer
LocalVariableUtils.swift
OptUtils.swift
OwnershipLiveness.swift
PhiUpdater.swift
SSAUpdater.swift
StaticInitCloner.swift
Test.swift
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===--- GuaranteedPhiUpdater.swift ---------------------------------------===//
//===--- PhiUpdater.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
Expand Down Expand Up @@ -152,13 +152,100 @@ private func addEnclosingValues(
return true
}

func registerGuaranteedPhiUpdater() {
BridgedUtilities.registerGuaranteedPhiUpdater(
/// Replaces a phi with the unique incoming value if all incoming values are the same:
/// ```
/// bb1:
/// br bb3(%1)
/// bb2:
/// br bb3(%1)
/// bb3(%2 : $T): // Predecessors: bb1, bb2
/// use(%2)
/// ```
/// ->
/// ```
/// bb1:
/// br bb3
/// bb2:
/// br bb3
/// bb3:
/// use(%1)
/// ```
///
func replacePhiWithIncomingValue(phi: Phi, _ context: some MutatingContext) -> Bool {
if phi.predecessors.isEmpty {
return false
}
let uniqueIncomingValue = phi.incomingValues.first!
if !uniqueIncomingValue.parentFunction.hasOwnership {
// For the SSAUpdater it's only required to simplify phis in OSSA.
// This avoids that we need to handle cond_br instructions below.
return false
}
if phi.incomingValues.contains(where: { $0 != uniqueIncomingValue }) {
return false
}
if let borrowedFrom = phi.borrowedFrom {
borrowedFrom.uses.replaceAll(with: uniqueIncomingValue, context)
context.erase(instruction: borrowedFrom)
} else {
phi.value.uses.replaceAll(with: uniqueIncomingValue, context)
}

let block = phi.value.parentBlock
for incomingOp in phi.incomingOperands {
let existingBranch = incomingOp.instruction as! BranchInst
let argsWithRemovedPhiOp = existingBranch.operands.filter{ $0 != incomingOp }.map{ $0.value }
Builder(before: existingBranch, context).createBranch(to: block, arguments: argsWithRemovedPhiOp)
context.erase(instruction: existingBranch)
}
block.eraseArgument(at: phi.value.index, context)
return true
}

/// 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.
///
func replacePhisWithIncomingValues(phis: [Phi], _ context: some MutatingContext) {
var currentPhis = phis
// Do this in a loop because replacing one phi might open up the opportunity for another phi
// and the order of phis in the array can be arbitrary.
while true {
var newPhis = [Phi]()
for phi in currentPhis {
if !replacePhiWithIncomingValue(phi: phi, context) {
newPhis.append(phi)
}
}
if newPhis.count == currentPhis.count {
return
}
currentPhis = newPhis
}
}

func registerPhiUpdater() {
BridgedUtilities.registerPhiUpdater(
// updateAllGuaranteedPhis
{ (bridgedCtxt: BridgedPassContext, bridgedFunction: BridgedFunction) in
let context = FunctionPassContext(_bridged: bridgedCtxt)
let function = bridgedFunction.function;
updateGuaranteedPhis(in: function, context)
},
// updateGuaranteedPhis
{ (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in
let context = FunctionPassContext(_bridged: bridgedCtxt)
var guaranteedPhis = Stack<Phi>(context)
Expand All @@ -172,6 +259,15 @@ func registerGuaranteedPhiUpdater() {
}
}
updateGuaranteedPhis(phis: guaranteedPhis, context)
},
// replacePhisWithIncomingValues
{ (bridgedCtxt: BridgedPassContext, bridgedPhiArray: BridgedArrayRef) in
let context = FunctionPassContext(_bridged: bridgedCtxt)
var phis = [Phi]()
bridgedPhiArray.withElements(ofType: BridgedValue.self) {
phis = $0.map { Phi($0.value)! }
}
replacePhisWithIncomingValues(phis: phis, context)
}
)
}
Expand Down
2 changes: 1 addition & 1 deletion SwiftCompilerSources/Sources/SIL/Operand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import SILBridging

/// An operand of an instruction.
public struct Operand : CustomStringConvertible, NoReflectionChildren {
public struct Operand : CustomStringConvertible, NoReflectionChildren, Equatable {
public let bridged: BridgedOperand

public init(bridged: BridgedOperand) {
Expand Down
5 changes: 3 additions & 2 deletions include/swift/SILOptimizer/OptimizerBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ struct BridgedUtilities {
typedef void (* _Nonnull UpdatePhisFn)(BridgedPassContext, BridgedArrayRef);

static void registerVerifier(VerifyFunctionFn verifyFunctionFn);
static void registerGuaranteedPhiUpdater(UpdateFunctionFn updateBorrowedFromFn,
UpdatePhisFn updateBorrowedFromPhisFn);
static void registerPhiUpdater(UpdateFunctionFn updateBorrowedFromFn,
UpdatePhisFn updateBorrowedFromPhisFn,
UpdatePhisFn replacePhisWithIncomingValuesFn);
};

struct BridgedBasicBlockSet {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/SILOptimizer/Utils/OwnershipOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ void updateAllGuaranteedPhis(SILPassManager *pm, SILFunction *f);
/// Updates the reborrow flags and the borrowed-from instructions for all `phis`.
void updateGuaranteedPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);

/// Replaces phis with the unique incoming values if all incoming values are the same.
void replacePhisWithIncomingValues(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis);

} // namespace swift

#endif
4 changes: 0 additions & 4 deletions include/swift/SILOptimizer/Utils/SILSSAUpdater.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ class SILBasicBlock;
class SILType;
class SILUndef;

/// Independent utility that canonicalizes BB arguments by reusing structurally
/// equivalent arguments and replacing the original arguments with casts.
SILValue replaceBBArgWithCast(SILPhiArgument *arg);

/// This class updates SSA for a set of SIL instructions defined in multiple
/// blocks.
class SILSSAUpdater {
Expand Down
40 changes: 16 additions & 24 deletions lib/SILOptimizer/LoopTransforms/LoopRotate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static llvm::cl::opt<bool> RotateSingleBlockLoop("looprotate-single-block-loop",

static bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo,
SILLoopInfo *loopInfo, bool rotateSingleBlockLoops,
SILBasicBlock *upToBB);
SILBasicBlock *upToBB, SILPassManager *pm);

/// Check whether all operands are loop invariant.
static bool
Expand Down Expand Up @@ -151,7 +151,8 @@ static void mapOperands(SILInstruction *inst,
static void updateSSAForUseOfValue(
SILSSAUpdater &updater, SmallVectorImpl<SILPhiArgument *> &insertedPhis,
const llvm::DenseMap<ValueBase *, SILValue> &valueMap,
SILBasicBlock *Header, SILBasicBlock *EntryCheckBlock, SILValue Res) {
SILBasicBlock *Header, SILBasicBlock *EntryCheckBlock, SILValue Res,
SILPassManager *pm) {
// Find the mapped instruction.
assert(valueMap.count(Res) && "Expected to find value in map!");
SILValue MappedValue = valueMap.find(Res)->second;
Expand Down Expand Up @@ -187,34 +188,25 @@ static void updateSSAForUseOfValue(
updater.rewriteUse(*use);
}

// Canonicalize inserted phis to avoid extra BB Args and if we find an address
// phi, stash it so we can handle it after we are done rewriting.
for (SILPhiArgument *arg : insertedPhis) {
if (SILValue inst = replaceBBArgWithCast(arg)) {
arg->replaceAllUsesWith(inst);
// DCE+SimplifyCFG runs as a post-pass cleanup.
// DCE replaces dead arg values with undef.
// SimplifyCFG deletes the dead BB arg.
continue;
}
}
replacePhisWithIncomingValues(pm, insertedPhis);
}

static void
updateSSAForUseOfInst(SILSSAUpdater &updater,
SmallVectorImpl<SILPhiArgument *> &insertedPhis,
const llvm::DenseMap<ValueBase *, SILValue> &valueMap,
SILBasicBlock *header, SILBasicBlock *entryCheckBlock,
SILInstruction *inst) {
SILInstruction *inst, SILPassManager *pm) {
for (auto result : inst->getResults())
updateSSAForUseOfValue(updater, insertedPhis, valueMap, header,
entryCheckBlock, result);
entryCheckBlock, result, pm);
}

/// Rewrite the code we just created in the preheader and update SSA form.
static void rewriteNewLoopEntryCheckBlock(
SILBasicBlock *header, SILBasicBlock *entryCheckBlock,
const llvm::DenseMap<ValueBase *, SILValue> &valueMap) {
const llvm::DenseMap<ValueBase *, SILValue> &valueMap,
SILPassManager *pm) {
SmallVector<SILPhiArgument *, 8> insertedPhis;
SILSSAUpdater updater(&insertedPhis);

Expand All @@ -223,7 +215,7 @@ static void rewriteNewLoopEntryCheckBlock(
for (unsigned i : range(header->getNumArguments())) {
auto *arg = header->getArguments()[i];
updateSSAForUseOfValue(updater, insertedPhis, valueMap, header,
entryCheckBlock, arg);
entryCheckBlock, arg, pm);
}

auto instIter = header->begin();
Expand All @@ -232,7 +224,7 @@ static void rewriteNewLoopEntryCheckBlock(
while (instIter != header->end()) {
auto &inst = *instIter;
updateSSAForUseOfInst(updater, insertedPhis, valueMap, header,
entryCheckBlock, &inst);
entryCheckBlock, &inst, pm);
++instIter;
}
}
Expand All @@ -254,7 +246,7 @@ static void updateDomTree(DominanceInfo *domInfo, SILBasicBlock *preheader,
}

static bool rotateLoopAtMostUpToLatch(SILLoop *loop, DominanceInfo *domInfo,
SILLoopInfo *loopInfo) {
SILLoopInfo *loopInfo, SILPassManager *pm) {
auto *latch = loop->getLoopLatch();
if (!latch) {
LLVM_DEBUG(llvm::dbgs()
Expand All @@ -264,11 +256,11 @@ static bool rotateLoopAtMostUpToLatch(SILLoop *loop, DominanceInfo *domInfo,

bool didRotate = rotateLoop(
loop, domInfo, loopInfo,
RotateSingleBlockLoop /* rotateSingleBlockLoops */, latch);
RotateSingleBlockLoop /* rotateSingleBlockLoops */, latch, pm);

// Keep rotating at most until we hit the original latch.
if (didRotate)
while (rotateLoop(loop, domInfo, loopInfo, false, latch)) {
while (rotateLoop(loop, domInfo, loopInfo, false, latch, pm)) {
}

return didRotate;
Expand Down Expand Up @@ -317,7 +309,7 @@ static bool isSingleBlockLoop(SILLoop *L) {
/// loop for termination.
static bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo,
SILLoopInfo *loopInfo, bool rotateSingleBlockLoops,
SILBasicBlock *upToBB) {
SILBasicBlock *upToBB, SILPassManager *pm) {
assert(loop != nullptr && domInfo != nullptr && loopInfo != nullptr
&& "Missing loop information");

Expand Down Expand Up @@ -443,7 +435,7 @@ static bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo,

// If there were any uses of instructions in the duplicated loop entry check
// block rewrite them using the ssa updater.
rewriteNewLoopEntryCheckBlock(header, preheader, valueMap);
rewriteNewLoopEntryCheckBlock(header, preheader, valueMap, pm);

loop->moveToHeader(newHeader);

Expand Down Expand Up @@ -509,7 +501,7 @@ class LoopRotation : public SILFunctionTransform {
SILLoop *loop = worklist.pop_back_val();
changed |= canonicalizeLoop(loop, domInfo, loopInfo);
changed |=
rotateLoopAtMostUpToLatch(loop, domInfo, loopInfo);
rotateLoopAtMostUpToLatch(loop, domInfo, loopInfo, getPassManager());
}
}

Expand Down
18 changes: 16 additions & 2 deletions lib/SILOptimizer/Utils/OwnershipOptUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1915,11 +1915,14 @@ bool swift::extendStoreBorrow(StoreBorrowInst *sbi,

static BridgedUtilities::UpdateFunctionFn updateAllGuaranteedPhisFunction;
static BridgedUtilities::UpdatePhisFn updateGuaranteedPhisFunction;
static BridgedUtilities::UpdatePhisFn replacePhisWithIncomingValuesFunction;

void BridgedUtilities::registerGuaranteedPhiUpdater(UpdateFunctionFn updateAllGuaranteedPhisFn,
UpdatePhisFn updateGuaranteedPhisFn) {
void BridgedUtilities::registerPhiUpdater(UpdateFunctionFn updateAllGuaranteedPhisFn,
UpdatePhisFn updateGuaranteedPhisFn,
UpdatePhisFn replacePhisWithIncomingValuesFn) {
updateAllGuaranteedPhisFunction = updateAllGuaranteedPhisFn;
updateGuaranteedPhisFunction = updateGuaranteedPhisFn;
replacePhisWithIncomingValuesFunction = replacePhisWithIncomingValuesFn;
}

void swift::updateAllGuaranteedPhis(SILPassManager *pm, SILFunction *f) {
Expand All @@ -1937,3 +1940,14 @@ void swift::updateGuaranteedPhis(SILPassManager *pm, ArrayRef<SILPhiArgument *>
}
updateGuaranteedPhisFunction({pm->getSwiftPassInvocation()}, ArrayRef(bridgedPhis));
}

void swift::replacePhisWithIncomingValues(SILPassManager *pm, ArrayRef<SILPhiArgument *> phis) {
if (!replacePhisWithIncomingValuesFunction)
return;

llvm::SmallVector<BridgedValue, 8> bridgedPhis;
for (SILPhiArgument *phi : phis) {
bridgedPhis.push_back({phi});
}
replacePhisWithIncomingValuesFunction({pm->getSwiftPassInvocation()}, ArrayRef(bridgedPhis));
}
Loading