mirror of https://github.com/exaloop/codon.git
471 lines
13 KiB
C++
471 lines
13 KiB
C++
#include "cfg.h"
|
|
|
|
#include <vector>
|
|
|
|
#include "sir/dsl/codegen.h"
|
|
#include "sir/dsl/nodes.h"
|
|
|
|
namespace codon {
|
|
namespace ir {
|
|
namespace analyze {
|
|
namespace dataflow {
|
|
namespace {
|
|
// TODO: this logic is very similar to lowering/pipeline -- unify somehow?
|
|
Value *callStage(analyze::dataflow::CFGraph *cfg, PipelineFlow::Stage *stage,
|
|
Value *last) {
|
|
std::vector<Value *> args;
|
|
for (auto *arg : *stage) {
|
|
args.push_back(arg ? arg : last);
|
|
}
|
|
return cfg->N<CallInstr>(stage->getCallee(), args);
|
|
}
|
|
|
|
Value *convertPipelineToForLoopsHelper(analyze::dataflow::CFGraph *cfg,
|
|
std::vector<PipelineFlow::Stage *> &stages,
|
|
unsigned idx = 0, Value *last = nullptr) {
|
|
if (idx >= stages.size())
|
|
return last;
|
|
|
|
auto *stage = stages[idx];
|
|
if (idx == 0)
|
|
return convertPipelineToForLoopsHelper(cfg, stages, idx + 1, stage->getCallee());
|
|
|
|
auto *prev = stages[idx - 1];
|
|
if (prev->isGenerator()) {
|
|
auto *var = cfg->N<Var>(prev->getOutputElementType());
|
|
auto *body = convertPipelineToForLoopsHelper(
|
|
cfg, stages, idx + 1, callStage(cfg, stage, cfg->N<VarValue>(var)));
|
|
auto *series = cfg->N<SeriesFlow>();
|
|
series->push_back(body);
|
|
return cfg->N<ForFlow>(last, series, var);
|
|
} else {
|
|
return convertPipelineToForLoopsHelper(cfg, stages, idx + 1,
|
|
callStage(cfg, stage, last));
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
const Value *convertPipelineToForLoops(analyze::dataflow::CFGraph *cfg,
|
|
const PipelineFlow *p) {
|
|
std::vector<PipelineFlow::Stage *> stages;
|
|
for (const auto &stage : *p) {
|
|
stages.push_back(const_cast<PipelineFlow::Stage *>(&stage));
|
|
}
|
|
return convertPipelineToForLoopsHelper(cfg, stages);
|
|
}
|
|
|
|
void CFBlock::reg(const Value *v) { graph->valueLocations[v->getId()] = this; }
|
|
|
|
const char SyntheticAssignInstr::NodeId = 0;
|
|
|
|
int SyntheticAssignInstr::doReplaceUsedValue(id_t id, Value *newValue) {
|
|
if (arg && arg->getId() == id) {
|
|
arg = newValue;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SyntheticAssignInstr::doReplaceUsedVariable(id_t id, Var *newVar) {
|
|
if (lhs->getId() == id) {
|
|
lhs = newVar;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char SyntheticPhiInstr::NodeId = 0;
|
|
|
|
std::vector<Value *> SyntheticPhiInstr::doGetUsedValues() const {
|
|
std::vector<Value *> ret;
|
|
for (auto &p : *this) {
|
|
ret.push_back(const_cast<Value *>(p.getResult()));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int SyntheticPhiInstr::doReplaceUsedValue(id_t id, Value *newValue) {
|
|
auto res = 0;
|
|
for (auto &p : *this) {
|
|
if (p.getResult()->getId() == id) {
|
|
p.setResult(newValue);
|
|
++res;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
CFGraph::CFGraph(const BodiedFunc *f) : func(f) { newBlock("entry", true); }
|
|
|
|
std::ostream &operator<<(std::ostream &os, const CFGraph &cfg) {
|
|
os << "digraph \"" << cfg.func->getName() << "\" {\n";
|
|
for (auto *block : cfg) {
|
|
os << " ";
|
|
os << block->getName() << "_" << reinterpret_cast<uintptr_t>(block);
|
|
os << " [ label=\"" << block->getName() << "\"";
|
|
if (block == cfg.getEntryBlock()) {
|
|
os << " shape=square";
|
|
}
|
|
os << " ];\n";
|
|
}
|
|
for (auto *block : cfg) {
|
|
for (auto next = block->successors_begin(); next != block->successors_end();
|
|
++next) {
|
|
CFBlock *succ = *next;
|
|
os << " ";
|
|
os << block->getName() << "_" << reinterpret_cast<uintptr_t>(block);
|
|
os << " -> ";
|
|
os << succ->getName() << "_" << reinterpret_cast<uintptr_t>(succ);
|
|
os << ";\n";
|
|
}
|
|
}
|
|
os << "}";
|
|
return os;
|
|
}
|
|
|
|
std::unique_ptr<CFGraph> buildCFGraph(const BodiedFunc *f) {
|
|
auto ret = std::make_unique<CFGraph>(f);
|
|
CFVisitor v(ret.get());
|
|
v.process(f);
|
|
return ret;
|
|
}
|
|
|
|
const std::string CFAnalysis::KEY = "core-analyses-cfg";
|
|
|
|
std::unique_ptr<Result> CFAnalysis::run(const Module *m) {
|
|
auto res = std::make_unique<CFResult>();
|
|
if (const auto *main = cast<BodiedFunc>(m->getMainFunc())) {
|
|
res->graphs.insert(std::make_pair(main->getId(), buildCFGraph(main)));
|
|
}
|
|
|
|
for (const auto *var : *m) {
|
|
if (const auto *f = cast<BodiedFunc>(var)) {
|
|
res->graphs.insert(std::make_pair(f->getId(), buildCFGraph(f)));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void CFVisitor::visit(const BodiedFunc *f) {
|
|
auto *blk = graph->getCurrentBlock();
|
|
for (auto it = f->arg_begin(); it != f->arg_end(); it++) {
|
|
blk->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(
|
|
const_cast<Var *>(*it), const_cast<VarValue *>(graph->N<VarValue>(*it))));
|
|
}
|
|
process(f->getBody());
|
|
}
|
|
|
|
void CFVisitor::visit(const SeriesFlow *v) {
|
|
for (auto *c : *v) {
|
|
process(c);
|
|
}
|
|
}
|
|
|
|
void CFVisitor::visit(const IfFlow *v) {
|
|
process(v->getCond());
|
|
auto *original = graph->getCurrentBlock();
|
|
auto *end = graph->newBlock("endIf");
|
|
|
|
auto *tBranch = graph->newBlock("trueBranch", true);
|
|
process(v->getTrueBranch());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
|
|
analyze::dataflow::CFBlock *fBranch = nullptr;
|
|
if (v->getFalseBranch()) {
|
|
fBranch = graph->newBlock("falseBranch", true);
|
|
process(v->getFalseBranch());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
}
|
|
|
|
original->successors_insert(tBranch);
|
|
if (fBranch)
|
|
original->successors_insert(fBranch);
|
|
else
|
|
original->successors_insert(end);
|
|
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const WhileFlow *v) {
|
|
auto *original = graph->getCurrentBlock();
|
|
auto *end = graph->newBlock("endWhile");
|
|
|
|
auto *loopBegin = graph->newBlock("whileBegin", true);
|
|
original->successors_insert(loopBegin);
|
|
process(v->getCond());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
|
|
loopStack.emplace_back(loopBegin, end, v->getId(), tryCatchStack.size() - 1);
|
|
auto *body = graph->newBlock("whileBody", true);
|
|
loopBegin->successors_insert(body);
|
|
process(v->getBody());
|
|
loopStack.pop_back();
|
|
graph->getCurrentBlock()->successors_insert(loopBegin);
|
|
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const ForFlow *v) {
|
|
if (v->isParallel()) {
|
|
for (auto *v : v->getSchedule()->getUsedValues()) {
|
|
process(v);
|
|
}
|
|
}
|
|
auto *original = graph->getCurrentBlock();
|
|
auto *end = graph->newBlock("endFor");
|
|
|
|
auto *loopBegin = graph->newBlock("forBegin", true);
|
|
original->successors_insert(loopBegin);
|
|
process(v->getIter());
|
|
|
|
auto *loopCheck = graph->newBlock("forCheck");
|
|
graph->getCurrentBlock()->successors_insert(loopCheck);
|
|
loopCheck->successors_insert(end);
|
|
|
|
auto *loopNext = graph->newBlock("forNext");
|
|
loopCheck->successors_insert(loopNext);
|
|
loopNext->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(
|
|
const_cast<Var *>(v->getVar()), const_cast<Value *>(v->getIter()),
|
|
analyze::dataflow::SyntheticAssignInstr::NEXT_VALUE));
|
|
|
|
loopStack.emplace_back(loopCheck, end, v->getId(), tryCatchStack.size() - 1);
|
|
auto *loopBody = graph->newBlock("forBody", true);
|
|
loopNext->successors_insert(loopBody);
|
|
process(v->getBody());
|
|
graph->getCurrentBlock()->successors_insert(loopCheck);
|
|
loopStack.pop_back();
|
|
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const ImperativeForFlow *v) {
|
|
if (v->isParallel()) {
|
|
for (auto *v : v->getSchedule()->getUsedValues()) {
|
|
process(v);
|
|
}
|
|
}
|
|
auto *original = graph->getCurrentBlock();
|
|
auto *end = graph->newBlock("endFor");
|
|
|
|
auto *loopBegin = graph->newBlock("forBegin", true);
|
|
original->successors_insert(loopBegin);
|
|
loopBegin->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(
|
|
const_cast<Var *>(v->getVar()), const_cast<Value *>(v->getStart()),
|
|
analyze::dataflow::SyntheticAssignInstr::KNOWN));
|
|
process(v->getStart());
|
|
process(v->getEnd());
|
|
|
|
auto *loopCheck = graph->newBlock("forCheck");
|
|
graph->getCurrentBlock()->successors_insert(loopCheck);
|
|
loopCheck->successors_insert(end);
|
|
|
|
auto *loopNext = graph->newBlock("forUpdate");
|
|
loopNext->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(
|
|
const_cast<Var *>(v->getVar()), v->getStep()));
|
|
loopNext->successors_insert(loopCheck);
|
|
|
|
loopStack.emplace_back(loopCheck, end, v->getId(), tryCatchStack.size() - 1);
|
|
auto *loopBody = graph->newBlock("forBody", true);
|
|
loopCheck->successors_insert(loopBody);
|
|
process(v->getBody());
|
|
graph->getCurrentBlock()->successors_insert(loopCheck);
|
|
loopStack.pop_back();
|
|
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const TryCatchFlow *v) {
|
|
auto *routeBlock = graph->newBlock("tcRoute");
|
|
auto *end = graph->newBlock("tcEnd");
|
|
analyze::dataflow::CFBlock *finally = nullptr;
|
|
if (v->getFinally())
|
|
finally = graph->newBlock("tcFinally");
|
|
|
|
auto *dst = finally ? finally : end;
|
|
|
|
tryCatchStack.emplace_back(routeBlock, finally);
|
|
process(v->getBody());
|
|
graph->getCurrentBlock()->successors_insert(dst);
|
|
|
|
for (auto &c : *v) {
|
|
auto *cBlock = graph->newBlock("catch", true);
|
|
if (c.getVar())
|
|
cBlock->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(
|
|
const_cast<Var *>(c.getVar())));
|
|
process(c.getHandler());
|
|
routeBlock->successors_insert(cBlock);
|
|
graph->getCurrentBlock()->successors_insert(dst);
|
|
}
|
|
|
|
tryCatchStack.pop_back();
|
|
|
|
if (v->getFinally()) {
|
|
graph->setCurrentBlock(finally);
|
|
process(v->getFinally());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
routeBlock->successors_insert(finally);
|
|
}
|
|
|
|
if (!tryCatchStack.empty()) {
|
|
if (finally)
|
|
finally->successors_insert(tryCatchStack.back().first);
|
|
else
|
|
routeBlock->successors_insert(tryCatchStack.back().first);
|
|
}
|
|
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const PipelineFlow *v) {
|
|
if (auto *loops = convertPipelineToForLoops(graph, v)) {
|
|
process(loops);
|
|
} else {
|
|
// pipeline is empty
|
|
}
|
|
}
|
|
|
|
void CFVisitor::visit(const dsl::CustomFlow *v) {
|
|
v->getCFBuilder()->buildCFNodes(this);
|
|
}
|
|
|
|
void CFVisitor::visit(const AssignInstr *v) {
|
|
process(v->getRhs());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const ExtractInstr *v) {
|
|
process(v->getVal());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const InsertInstr *v) {
|
|
process(v->getLhs());
|
|
process(v->getRhs());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const CallInstr *v) {
|
|
process(v->getCallee());
|
|
for (auto *a : *v)
|
|
process(a);
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const TernaryInstr *v) {
|
|
auto *end = graph->newBlock("ternaryDone");
|
|
auto *tBranch = graph->newBlock("ternaryTrue");
|
|
auto *fBranch = graph->newBlock("ternaryFalse");
|
|
|
|
process(v->getCond());
|
|
graph->getCurrentBlock()->successors_insert(tBranch);
|
|
graph->getCurrentBlock()->successors_insert(fBranch);
|
|
|
|
graph->setCurrentBlock(tBranch);
|
|
process(v->getTrueValue());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
|
|
graph->setCurrentBlock(fBranch);
|
|
process(v->getFalseValue());
|
|
graph->getCurrentBlock()->successors_insert(end);
|
|
|
|
auto *phi = graph->N<analyze::dataflow::SyntheticPhiInstr>();
|
|
phi->emplace_back(tBranch, const_cast<Value *>(v->getTrueValue()));
|
|
phi->emplace_back(fBranch, const_cast<Value *>(v->getFalseValue()));
|
|
|
|
end->push_back(phi);
|
|
graph->remapValue(v, phi);
|
|
graph->setCurrentBlock(end);
|
|
}
|
|
|
|
void CFVisitor::visit(const BreakInstr *v) {
|
|
auto &loop = v->getLoop() ? findLoop(v->getLoop()->getId()) : loopStack.back();
|
|
defaultJump(loop.end, loop.tcIndex);
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const ContinueInstr *v) {
|
|
auto &loop = v->getLoop() ? findLoop(v->getLoop()->getId()) : loopStack.back();
|
|
defaultJump(loop.nextIt, loop.tcIndex);
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const ReturnInstr *v) {
|
|
if (v->getValue())
|
|
process(v->getValue());
|
|
defaultJump(nullptr, -1);
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const YieldInstr *v) {
|
|
if (v->getValue())
|
|
process(v->getValue());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const ThrowInstr *v) {
|
|
if (v->getValue())
|
|
process(v->getValue());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const FlowInstr *v) {
|
|
process(v->getFlow());
|
|
if (v->getValue())
|
|
process(v->getValue());
|
|
defaultInsert(v);
|
|
}
|
|
|
|
void CFVisitor::visit(const dsl::CustomInstr *v) {
|
|
v->getCFBuilder()->buildCFNodes(this);
|
|
}
|
|
|
|
void CFVisitor::defaultInsert(const Value *v) {
|
|
if (tryCatchStack.empty()) {
|
|
graph->getCurrentBlock()->push_back(v);
|
|
} else {
|
|
auto *original = graph->getCurrentBlock();
|
|
auto *newBlock = graph->newBlock("default", true);
|
|
original->successors_insert(newBlock);
|
|
newBlock->successors_insert(tryCatchStack.back().first);
|
|
graph->getCurrentBlock()->push_back(v);
|
|
}
|
|
seenIds.insert(v->getId());
|
|
}
|
|
|
|
void CFVisitor::defaultJump(const CFBlock *cf, int newTcLevel) {
|
|
int curTc = tryCatchStack.size() - 1;
|
|
|
|
if (curTc == -1 || curTc <= newTcLevel) {
|
|
if (cf)
|
|
graph->getCurrentBlock()->successors_insert(const_cast<CFBlock *>(cf));
|
|
} else {
|
|
CFBlock *nearestFinally = nullptr;
|
|
for (auto i = newTcLevel + 1; i <= curTc; ++i) {
|
|
if (auto *n = tryCatchStack[i].second) {
|
|
nearestFinally = n;
|
|
break;
|
|
}
|
|
}
|
|
if (nearestFinally) {
|
|
graph->getCurrentBlock()->successors_insert(tryCatchStack.back().first);
|
|
if (cf)
|
|
nearestFinally->successors_insert(const_cast<CFBlock *>(cf));
|
|
} else {
|
|
if (cf)
|
|
graph->getCurrentBlock()->successors_insert(const_cast<CFBlock *>(cf));
|
|
}
|
|
}
|
|
}
|
|
|
|
CFVisitor::Loop &CFVisitor::findLoop(id_t id) {
|
|
return *std::find_if(loopStack.begin(), loopStack.end(),
|
|
[=](auto &it) { return it.loopId == id; });
|
|
}
|
|
|
|
} // namespace dataflow
|
|
} // namespace analyze
|
|
} // namespace ir
|
|
} // namespace codon
|
|
|
|
#undef DEFAULT_VISIT
|