mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
301 lines
9.7 KiB
C++
301 lines
9.7 KiB
C++
//===--- BasicBlockOptUtils.cpp - SILOptimizer basic block utilities ------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/SIL/LoopInfo.h"
|
|
#include "swift/SIL/StackList.h"
|
|
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
|
|
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Invoke \p visitor for each reachable block in \p f in worklist order (at
|
|
/// least one predecessor has been visited).
|
|
bool ReachableBlocks::visit(function_ref<bool(SILBasicBlock *)> visitor) {
|
|
// Walk over the CFG, starting at the entry block, until all reachable blocks
|
|
// are visited.
|
|
auto *function = visited.getFunction();
|
|
auto *entry = function->getEntryBlock();
|
|
StackList<SILBasicBlock *> worklist(function);
|
|
worklist.push_back(entry);
|
|
visited.insert(entry);
|
|
while (!worklist.empty()) {
|
|
SILBasicBlock *bb = worklist.pop_back_val();
|
|
if (!visitor(bb))
|
|
return false;
|
|
|
|
for (auto &succ : bb->getSuccessors()) {
|
|
if (visited.insert(succ))
|
|
worklist.push_back(succ);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ReachableBlocks::compute() {
|
|
// Visit all the blocks without doing any extra work.
|
|
visit([](SILBasicBlock *) { return true; });
|
|
isComputed = true;
|
|
}
|
|
|
|
ReachingReturnBlocks::ReachingReturnBlocks(SILFunction *function)
|
|
: worklist(function) {
|
|
for (SILBasicBlock &block : *function) {
|
|
if (isa<ReturnInst>(block.getTerminator()))
|
|
worklist.push(&block);
|
|
}
|
|
|
|
while (SILBasicBlock *block = worklist.pop()) {
|
|
for (SILBasicBlock *pred : block->getPredecessorBlocks()) {
|
|
worklist.pushIfNotVisited(pred);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool swift::removeUnreachableBlocks(SILFunction &f) {
|
|
ReachableBlocks reachable(&f);
|
|
reachable.compute();
|
|
|
|
// Remove the blocks we never reached. Assume the entry block is visited.
|
|
// Reachable's visited set contains dangling pointers during this loop.
|
|
bool changed = false;
|
|
for (auto ii = std::next(f.begin()), end = f.end(); ii != end;) {
|
|
auto *bb = &*ii++;
|
|
if (!reachable.isReachable(bb)) {
|
|
bb->removeDeadBlock();
|
|
changed = true;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// BasicBlock Cloning
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Return true if a guaranteed terminator result can be borrowed such that the
|
|
// nested borrow scope covers all its uses.
|
|
static bool canBorrowGuaranteedResult(SILValue guaranteedResult) {
|
|
if (guaranteedResult->getOwnershipKind() != OwnershipKind::Guaranteed) {
|
|
// Either this terminator forwards an owned value, or it is some legal
|
|
// conversion to a non-guaranteed value. Either way, not interesting.
|
|
return true;
|
|
}
|
|
return findInnerTransitiveGuaranteedUses(guaranteedResult);
|
|
}
|
|
|
|
bool swift::canCloneTerminator(TermInst *termInst) {
|
|
// TODO: this is an awkward way to check for guaranteed terminator results.
|
|
for (Operand &oper : termInst->getAllOperands()) {
|
|
if (oper.getOperandOwnership() != OperandOwnership::GuaranteedForwarding)
|
|
continue;
|
|
|
|
if (!ForwardingOperand(&oper).visitForwardedValues(
|
|
[&](SILValue termResult) {
|
|
return canBorrowGuaranteedResult(termResult);
|
|
})) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void BasicBlockCloner::updateSSAAfterCloning() {
|
|
SmallVector<SILPhiArgument *, 4> updateSSAPhis;
|
|
// All instructions should have been checked by canCloneInstruction. But we
|
|
// still need to check the arguments.
|
|
for (auto arg : origBB->getArguments()) {
|
|
if ((needsSSAUpdate |= isUsedOutsideOfBlock(arg))) {
|
|
break;
|
|
}
|
|
}
|
|
if (needsSSAUpdate) {
|
|
SILSSAUpdater ssaUpdater(&updateSSAPhis);
|
|
for (auto availValPair : availVals) {
|
|
auto inst = availValPair.first;
|
|
if (inst->use_empty())
|
|
continue;
|
|
|
|
SILValue newResult(availValPair.second);
|
|
|
|
SmallVector<UseWrapper, 16> useList;
|
|
// Collect the uses of the value.
|
|
for (auto *use : inst->getUses())
|
|
useList.push_back(UseWrapper(use));
|
|
|
|
ssaUpdater.initialize(inst->getFunction(), inst->getType(),
|
|
inst->getOwnershipKind());
|
|
ssaUpdater.addAvailableValue(origBB, inst);
|
|
ssaUpdater.addAvailableValue(getNewBB(), newResult);
|
|
|
|
if (useList.empty())
|
|
continue;
|
|
|
|
// Update all the uses.
|
|
for (auto useWrapper : useList) {
|
|
Operand *use = useWrapper; // unwrap
|
|
SILInstruction *user = use->getUser();
|
|
assert(user && "Missing user");
|
|
|
|
// Ignore uses in the same basic block.
|
|
if (user->getParent() == origBB)
|
|
continue;
|
|
|
|
ssaUpdater.rewriteUse(*use);
|
|
}
|
|
}
|
|
}
|
|
for (SILBasicBlock *b : blocksWithNewPhiArgs) {
|
|
for (SILArgument *arg : b->getArguments()) {
|
|
if (arg->getOwnershipKind() == OwnershipKind::Guaranteed) {
|
|
updateSSAPhis.push_back(cast<SILPhiArgument>(arg));
|
|
}
|
|
}
|
|
}
|
|
updateGuaranteedPhis(pm, updateSSAPhis);
|
|
}
|
|
|
|
void BasicBlockCloner::sinkAddressProjections() {
|
|
// Because the address projections chains will be disjoint (an instruction
|
|
// in one chain cannot use the result of an instruction in another chain),
|
|
// the order they are sunk does not matter.
|
|
InstructionDeleter deleter;
|
|
for (auto ii = origBB->begin(), ie = origBB->end(); ii != ie;) {
|
|
bool canSink = sinkProj.analyzeAddressProjections(&*ii);
|
|
(void)canSink;
|
|
assert(canSink && "canCloneInstruction should catch this.");
|
|
|
|
sinkProj.cloneProjections();
|
|
assert((sinkProj.getInBlockDefs().empty() || needsSSAUpdate)
|
|
&& "canCloneInstruction should catch this.");
|
|
|
|
auto nextII = std::next(ii);
|
|
deleter.trackIfDead(&*ii);
|
|
ii = nextII;
|
|
}
|
|
deleter.cleanupDeadInstructions();
|
|
}
|
|
|
|
// Populate 'projections' with the chain of address projections leading
|
|
// to and including 'inst'.
|
|
//
|
|
// Populate 'inBlockDefs' with all the non-address value definitions in
|
|
// the block that will be used outside this block after projection sinking.
|
|
//
|
|
// Return true on success, even if projections is empty.
|
|
bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
|
|
oldProjections.clear();
|
|
inBlockDefs.clear();
|
|
|
|
SILBasicBlock *bb = inst->getParent();
|
|
auto pushOperandVal = [&](SILValue def) {
|
|
if (def->getParentBlock() != bb)
|
|
return true;
|
|
|
|
if (!def->getType().isAddress()) {
|
|
inBlockDefs.insert(def);
|
|
return true;
|
|
}
|
|
if (auto *addressProj = dyn_cast<SingleValueInstruction>(def)) {
|
|
if (addressProj->isPure()) {
|
|
oldProjections.push_back(addressProj);
|
|
return true;
|
|
}
|
|
}
|
|
// Can't handle a multi-value or unclonable address producer.
|
|
return false;
|
|
};
|
|
// Check the given instruction for any address-type results.
|
|
for (auto result : inst->getResults()) {
|
|
if (!isUsedOutsideOfBlock(result))
|
|
continue;
|
|
if (!pushOperandVal(result))
|
|
return false;
|
|
}
|
|
// Recurse upward through address projections.
|
|
for (unsigned idx = 0; idx < oldProjections.size(); ++idx) {
|
|
// Only one address result/operand can be handled per instruction.
|
|
if (oldProjections.size() != idx + 1)
|
|
return false;
|
|
|
|
for (SILValue operandVal : oldProjections[idx]->getOperandValues())
|
|
if (!pushOperandVal(operandVal))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Clone the projections gathered by 'analyzeAddressProjections' at
|
|
// their use site outside this block.
|
|
bool SinkAddressProjections::cloneProjections() {
|
|
if (oldProjections.empty())
|
|
return false;
|
|
|
|
SILBasicBlock *bb = oldProjections.front()->getParent();
|
|
// Clone projections in last-to-first order.
|
|
for (unsigned idx = 0; idx < oldProjections.size(); ++idx) {
|
|
auto *oldProj = oldProjections[idx];
|
|
assert(oldProj->getParent() == bb);
|
|
// Reset transient per-projection sets.
|
|
usesToReplace.clear();
|
|
firstBlockUse.clear();
|
|
// Gather uses.
|
|
for (Operand *use : oldProj->getUses()) {
|
|
auto *useBB = use->getUser()->getParent();
|
|
if (useBB != bb) {
|
|
firstBlockUse.try_emplace(useBB, use);
|
|
usesToReplace.push_back(use);
|
|
}
|
|
}
|
|
// Replace uses. Uses must be handled in the same order they were discovered
|
|
// above.
|
|
//
|
|
// Avoid cloning a projection multiple times per block. This avoids extra
|
|
// projections, but also prevents the removal of DebugValue. If a
|
|
// projection's only remaining is DebugValue, then it is deleted along with
|
|
// the DebugValue.
|
|
for (Operand *use : usesToReplace) {
|
|
auto *useBB = use->getUser()->getParent();
|
|
auto *firstUse = firstBlockUse.lookup(useBB);
|
|
SingleValueInstruction *newProj;
|
|
if (use == firstUse) {
|
|
newProj = cast<SingleValueInstruction>(oldProj->clone(use->getUser()));
|
|
if (newProjections) {
|
|
newProjections->push_back(newProj);
|
|
}
|
|
} else {
|
|
newProj = cast<SingleValueInstruction>(firstUse->get());
|
|
assert(newProj->getParent() == useBB);
|
|
newProj->moveFront(useBB);
|
|
}
|
|
use->set(newProj);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SinkAddressProjections::dump() const {
|
|
llvm::dbgs() << "Old projections: ";
|
|
for (auto *proj : oldProjections) {
|
|
proj->dump();
|
|
}
|
|
if (auto *np = newProjections) {
|
|
llvm::dbgs() << "New projections: ";
|
|
for (auto *proj : *np) {
|
|
proj->dump();
|
|
}
|
|
}
|
|
}
|