blob: f505506765dea6eeb3298859623d3c91bc407fbc [file] [log] [blame]
//===--- SCCVisitor.h - SIL SCC Visitor -------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SIL_SCCVISITOR_H
#define SWIFT_SIL_SCCVISITOR_H
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <tuple>
namespace swift {
/// A visitor class for visiting the instructions and basic block
/// arguments of a SIL function one strongly connected component at a
/// time in reverse post-order.
///
/// Inherit from this in the usual CRTP fashion and define a visit()
/// function. This function will get called with a vector of
/// pointer-to-ValueBase which are the elements of the SCC.
template <typename ImplClass>
class SCCVisitor {
public:
SCCVisitor(SILFunction &F) : F(F) {}
~SCCVisitor() {
cleanup();
}
ImplClass &asImpl() { return static_cast<ImplClass &>(*this); }
void visit(llvm::SmallVectorImpl<SILNode *> &SCC) { }
void run() {
llvm::ReversePostOrderTraversal<SILFunction *> RPOT(&F);
for (auto Iter = RPOT.begin(), E = RPOT.end(); Iter != E; ++Iter) {
auto *BB = *Iter;
for (auto &I : *BB)
maybeDFS(&I);
}
cleanup();
}
private:
struct DFSInfo {
SILNode *Node;
int DFSNum;
int LowNum;
DFSInfo(SILNode *node, int num) : Node(node), DFSNum(num), LowNum(num) {}
};
SILFunction &F;
int CurrentNum = 0;
llvm::DenseSet<SILNode *> Visited;
llvm::SetVector<SILNode *> DFSStack;
typedef llvm::DenseMap<SILNode *, std::unique_ptr<DFSInfo>> ValueInfoMapType;
ValueInfoMapType ValueInfoMap;
void cleanup() {
Visited.clear();
DFSStack.clear();
ValueInfoMap.clear();
CurrentNum = 0;
}
DFSInfo &addDFSInfo(SILNode *node) {
assert(node->isRepresentativeSILNodeInObject());
auto insertion = ValueInfoMap.try_emplace(node,
new DFSInfo(node, CurrentNum++));
assert(insertion.second && "Cannot add DFS info more than once!");
return *insertion.first->second;
}
DFSInfo &getDFSInfo(SILNode *node) {
assert(node->isRepresentativeSILNodeInObject());
auto it = ValueInfoMap.find(node);
assert(it != ValueInfoMap.end() &&
"Expected to find value in DFS info map!");
return *it->second;
}
void getArgsForTerminator(TermInst *Term, SILBasicBlock *SuccBB, int Index,
llvm::SmallVectorImpl<SILValue> &Operands) {
switch (Term->getTermKind()) {
case TermKind::BranchInst:
return Operands.push_back(cast<BranchInst>(Term)->getArg(Index));
case TermKind::CondBranchInst: {
auto *CBI = cast<CondBranchInst>(Term);
if (SuccBB == CBI->getTrueBB())
return Operands.push_back(CBI->getTrueArgs()[Index]);
assert(SuccBB == CBI->getFalseBB() &&
"Block is not a successor of terminator!");
Operands.push_back(CBI->getFalseArgs()[Index]);
return;
}
case TermKind::SwitchEnumInst:
case TermKind::SwitchEnumAddrInst:
case TermKind::CheckedCastBranchInst:
case TermKind::CheckedCastValueBranchInst:
case TermKind::CheckedCastAddrBranchInst:
case TermKind::DynamicMethodBranchInst:
assert(Index == 0 && "Expected argument index to always be zero!");
return Operands.push_back(Term->getOperand(0));
case TermKind::UnreachableInst:
case TermKind::ReturnInst:
case TermKind::SwitchValueInst:
case TermKind::ThrowInst:
case TermKind::UnwindInst:
llvm_unreachable("Did not expect terminator that does not have args!");
case TermKind::YieldInst:
for (auto &O : cast<YieldInst>(Term)->getAllOperands())
Operands.push_back(O.get());
return;
case TermKind::TryApplyInst:
for (auto &O : cast<TryApplyInst>(Term)->getAllOperands())
Operands.push_back(O.get());
return;
}
}
void collectOperandsForUser(SILNode *node,
llvm::SmallVectorImpl<SILValue> &Operands) {
if (auto *I = dyn_cast<SILInstruction>(node)) {
for (auto &O : I->getAllOperands())
Operands.push_back(O.get());
return;
}
if (auto *A = dyn_cast<SILArgument>(node)) {
auto *BB = A->getParent();
auto Index = A->getIndex();
for (auto *Pred : BB->getPredecessorBlocks())
getArgsForTerminator(Pred->getTerminator(), BB, Index, Operands);
return;
}
}
void maybeDFS(SILInstruction *inst) {
(void) maybeDFSCanonicalNode(inst->getRepresentativeSILNodeInObject());
}
/// Continue a DFS from the given node, finding the strongly
/// component that User is a part of, calling visit() with that SCC,
/// and returning the DFSInfo for the node.
/// But if we've already visited the node, just return null.
DFSInfo *maybeDFSCanonicalNode(SILNode *node) {
assert(node->isRepresentativeSILNodeInObject() &&
"should already be canonical");
if (!Visited.insert(node).second)
return nullptr;
DFSStack.insert(node);
auto &nodeInfo = addDFSInfo(node);
llvm::SmallVector<SILValue, 4> operands;
collectOperandsForUser(node, operands);
// Visit each unvisited operand, updating the lowest DFS number we've seen
// reachable in User's SCC.
for (SILValue operandValue : operands) {
SILNode *operandNode = operandValue->getRepresentativeSILNodeInObject();
if (auto operandNodeInfo = maybeDFSCanonicalNode(operandNode)) {
nodeInfo.LowNum = std::min(nodeInfo.LowNum, operandNodeInfo->LowNum);
} else if (DFSStack.count(operandNode)) {
auto operandNodeInfo = &getDFSInfo(operandNode);
nodeInfo.LowNum = std::min(nodeInfo.LowNum, operandNodeInfo->DFSNum);
}
}
// If User is the head of its own SCC, pop that SCC off the DFS stack.
if (nodeInfo.DFSNum == nodeInfo.LowNum) {
llvm::SmallVector<SILNode *, 4> SCC;
SILNode *poppedNode;
do {
poppedNode = DFSStack.pop_back_val();
SCC.push_back(poppedNode);
} while (poppedNode != node);
asImpl().visit(SCC);
}
return &nodeInfo;
}
};
}
#endif