blob: 88f11083c49ee4ea5a73bf25277a6ff357367e28 [file] [log] [blame]
//===--- 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/SILOptimizer/Utils/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
using namespace swift;
/// Remove all instructions in the body of \p bb in safe manner by using
/// undef.
void swift::clearBlockBody(SILBasicBlock *bb) {
// Instructions in the dead block may be used by other dead blocks. Replace
// any uses of them with undef values.
while (!bb->empty()) {
// Grab the last instruction in the bb.
auto *inst = &bb->back();
// Replace any still-remaining uses with undef values and erase.
inst->replaceAllUsesOfAllResultsWithUndef();
inst->eraseFromParent();
}
}
// Handle the mechanical aspects of removing an unreachable block.
void swift::removeDeadBlock(SILBasicBlock *bb) {
// Clear the body of bb.
clearBlockBody(bb);
// Now that the bb is empty, eliminate it.
bb->eraseFromParent();
}
bool swift::removeUnreachableBlocks(SILFunction &f) {
// All reachable blocks, but does not include the entry block.
llvm::SmallPtrSet<SILBasicBlock *, 8> visited;
// Walk over the CFG, starting at the entry block, until all reachable blocks are visited.
llvm::SmallVector<SILBasicBlock *, 8> worklist(1, f.getEntryBlock());
while (!worklist.empty()) {
SILBasicBlock *bb = worklist.pop_back_val();
for (auto &Succ : bb->getSuccessors()) {
if (visited.insert(Succ).second)
worklist.push_back(Succ);
}
}
// Remove the blocks we never reached. Exclude the entry block from the iteration because it's
// not included in the Visited set.
bool changed = false;
for (auto ii = std::next(f.begin()), end = f.end(); ii != end;) {
auto *bb = &*ii++;
if (!visited.count(bb)) {
removeDeadBlock(bb);
changed = true;
}
}
return changed;
}
/// Helper function to perform SSA updates in case of jump threading.
void swift::updateSSAAfterCloning(BasicBlockCloner &cloner,
SILBasicBlock *srcBB, SILBasicBlock *destBB) {
SILSSAUpdater ssaUpdater;
for (auto availValPair : cloner.AvailVals) {
ValueBase *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->getType());
ssaUpdater.AddAvailableValue(destBB, inst);
ssaUpdater.AddAvailableValue(srcBB, newResult);
if (useList.empty())
continue;
// Update all the uses.
for (auto useWrapper : useList) {
Operand *use = useWrapper;
SILInstruction *user = use->getUser();
assert(user && "Missing user");
// Ignore uses in the same basic block.
if (user->getParent() == destBB)
continue;
ssaUpdater.RewriteUse(*use);
}
}
}
// FIXME: Remove this. SILCloner should not create critical edges.
bool BasicBlockCloner::splitCriticalEdges(DominanceInfo *domInfo,
SILLoopInfo *loopInfo) {
bool changed = false;
// Remove any critical edges that the EdgeThreadingCloner may have
// accidentally created.
for (unsigned succIdx = 0, succEnd = origBB->getSuccessors().size();
succIdx != succEnd; ++succIdx) {
if (nullptr
!= splitCriticalEdge(origBB->getTerminator(), succIdx, domInfo,
loopInfo))
changed |= true;
}
for (unsigned succIdx = 0, succEnd = getNewBB()->getSuccessors().size();
succIdx != succEnd; ++succIdx) {
auto *newBB = splitCriticalEdge(getNewBB()->getTerminator(), succIdx,
domInfo, loopInfo);
changed |= (newBB != nullptr);
}
return changed;
}
// 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) {
projections.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->isTriviallyDuplicatable()) {
projections.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 < projections.size(); ++idx) {
// Only one address result/operand can be handled per instruction.
if (projections.size() != idx + 1)
return false;
for (SILValue operandVal : projections[idx]->getOperandValues())
pushOperandVal(operandVal);
}
return true;
}
// Clone the projections gathered by 'analyzeAddressProjections' at
// their use site outside this block.
bool SinkAddressProjections::cloneProjections() {
if (projections.empty())
return false;
SILBasicBlock *bb = projections.front()->getParent();
SmallVector<Operand *, 4> usesToReplace;
// Clone projections in last-to-first order.
for (unsigned idx = 0; idx < projections.size(); ++idx) {
auto *oldProj = projections[idx];
assert(oldProj->getParent() == bb);
usesToReplace.clear();
for (Operand *use : oldProj->getUses()) {
if (use->getUser()->getParent() != bb)
usesToReplace.push_back(use);
}
for (Operand *use : usesToReplace) {
auto *newProj = oldProj->clone(use->getUser());
use->set(cast<SingleValueInstruction>(newProj));
}
}
return true;
}
void StaticInitCloner::add(SILInstruction *initVal) {
// Don't schedule an instruction twice for cloning.
if (numOpsToClone.count(initVal) != 0)
return;
ArrayRef<Operand> operands = initVal->getAllOperands();
numOpsToClone[initVal] = operands.size();
if (operands.empty()) {
// It's an instruction without operands, e.g. a literal. It's ready to be
// cloned first.
readyToClone.push_back(initVal);
} else {
// Recursively add all operands.
for (const Operand &operand : operands) {
add(cast<SingleValueInstruction>(operand.get()));
}
}
}
SingleValueInstruction *
StaticInitCloner::clone(SingleValueInstruction *initVal) {
assert(numOpsToClone.count(initVal) != 0 && "initVal was not added");
// Find the right order to clone: all operands of an instruction must be
// cloned before the instruction itself.
while (!readyToClone.empty()) {
SILInstruction *inst = readyToClone.pop_back_val();
// Clone the instruction into the SILGlobalVariable
visit(inst);
// Check if users of I can now be cloned.
for (SILValue result : inst->getResults()) {
for (Operand *use : result->getUses()) {
SILInstruction *user = use->getUser();
if (numOpsToClone.count(user) != 0 && --numOpsToClone[user] == 0)
readyToClone.push_back(user);
}
}
}
return cast<SingleValueInstruction>(getMappedValue(initVal));
}