/src/runtime/vm/verifier/cfg.cpp
C++ | 307 lines | 208 code | 19 blank | 80 comment | 61 complexity | ebbf0d4e22708bdeef3ec380143f33dc MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
- /*
- +----------------------------------------------------------------------+
- | HipHop for PHP |
- +----------------------------------------------------------------------+
- | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- */
- #include <runtime/vm/verifier/cfg.h>
- #include <runtime/vm/verifier/util.h>
- #include <util/range.h>
- namespace HPHP {
- namespace VM {
- namespace Verifier {
- /**
- * Create all blocks and edges for one Func.
- */
- Graph* GraphBuilder::build() {
- ASSERT(!funcInstrs(m_func).empty());
- m_graph = new (m_arena) Graph();
- createBlocks();
- createExBlocks();
- linkBlocks();
- linkExBlocks();
- return m_graph;
- }
- /**
- * Create blocks for each entry point as well as ordinary control
- * flow boundaries. Calls are not treated as basic-block ends.
- */
- void GraphBuilder::createBlocks() {
- PC bc = m_unit->entry();
- m_graph->param_count = m_func->params().size();
- m_graph->first_linear = createBlock(m_func->base());
- // DV entry points
- m_graph->entries = new (m_arena) Block*[m_graph->param_count + 1];
- int dv_index = 0;
- for (Range<Func::ParamInfoVec> p(m_func->params()); !p.empty(); ) {
- const Func::ParamInfo& param = p.popFront();
- m_graph->entries[dv_index++] = !param.hasDefaultValue() ? 0 :
- createBlock(param.funcletOff());
- }
- // main entry point
- ASSERT(dv_index == m_graph->param_count);
- m_graph->entries[dv_index] = createBlock(m_func->base());
- // ordinary basic block boundaries
- for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
- PC pc = i.popFront();
- if (isCF(pc) && !i.empty()) createBlock(i.front());
- if (Op(*pc) == OpSwitch) {
- ImmVector vec = getImmVector(pc);
- const int* v = vec.vec32();
- for (int i = 0, n = vec.size(); i < n; i++) createBlock(pc + v[i]);
- } else {
- Offset target = instrJumpTarget(bc, pc - bc);
- if (target != InvalidAbsoluteOffset) createBlock(target);
- }
- }
- }
- /**
- * Link ordinary blocks with ordinary edges and set their last instruction
- * and end offsets
- */
- void GraphBuilder::linkBlocks() {
- PC bc = m_unit->entry();
- Block* block = m_graph->first_linear;
- block->id = m_graph->block_count++;
- for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
- PC pc = i.popFront();
- block->last = pc;
- if (isCF(pc)) {
- if (Op(*pc) == OpSwitch) {
- ImmVector vec = getImmVector(pc);
- const int* v = vec.vec32();
- for (int i = 0, n = vec.size(); i < n; i++) {
- succs(block)[i] = at(pc + v[i]);
- }
- } else {
- Offset target = instrJumpTarget(bc, pc - bc);
- if (target != InvalidAbsoluteOffset) {
- ASSERT(numSuccBlocks(block) > 0);
- succs(block)[numSuccBlocks(block) - 1] = at(target);
- }
- }
- }
- PC next_pc = !i.empty() ? i.front() : m_unit->at(m_func->past());
- Block* next = at(next_pc);
- if (next) {
- block->next_linear = next;
- block->end = next_pc;
- if (!isTF(pc)) {
- ASSERT(numSuccBlocks(block) > 0);
- succs(block)[0] = next;
- }
- block = next;
- block->id = m_graph->block_count++;
- }
- }
- block->end = m_unit->at(m_func->past());
- }
- /**
- * Create blocks from exception handler boundaries; the start and end of
- * each protected region is a boundary, and the catch or fault entrypoint
- * of each handler is also a boundary.
- */
- void GraphBuilder::createExBlocks() {
- m_graph->exn_cap = m_func->ehtab().size();
- for (Range<Func::EHEntVec> i(m_func->ehtab()); !i.empty(); ) {
- const EHEnt& handler = i.popFront();
- createBlock(handler.m_base);
- createBlock(handler.m_past);
- if (handler.m_ehtype == EHEnt::EHType_Catch) {
- m_graph->exn_cap += handler.m_catches.size() - 1;
- for (Range<EHEnt::CatchVec> c(handler.m_catches); !c.empty(); ) {
- createBlock(c.popFront().second);
- }
- } else {
- createBlock(handler.m_fault);
- }
- }
- }
- /**
- * Find the EHEnt for the fault funclet that off is inside of. Off is an
- * offset in the funclet's handler (e.g. &Unwind), not the funclets protected
- * try region.
- */
- const EHEnt* findFunclet(const Func::EHEntVec& ehtab, Offset off) {
- const EHEnt* nearest = 0;
- for (Range<Func::EHEntVec> i(ehtab); !i.empty(); ) {
- const EHEnt* eh = &i.popFront();
- if (eh->m_ehtype != EHEnt::EHType_Fault) continue;
- if (eh->m_fault <= off && (!nearest || eh->m_fault > nearest->m_fault)) {
- nearest = eh;
- }
- }
- ASSERT(nearest != 0 && nearest->m_fault <= off);
- return nearest;
- }
- /**
- * return the next outermost handler or 0 if there aren't any
- */
- const EHEnt* nextOuter(const Func::EHEntVec& ehtab, const EHEnt* eh) {
- return eh->m_parentIndex != -1 ? &ehtab[eh->m_parentIndex] : 0;
- }
- /**
- * Link primary body blocks to exception handler blocks as follows:
- * 1. For each block in the body, traverse from the innermost to
- * outermost exception handler that includes the block. For
- * catch handlers, add an edge to each handler entry point. For
- * fault handlers, add an edge to the fault handler and stop.
- * 2. For each fault handler block ending in Unwind, find the EH
- * entry for the funclet, then traverse outwards starting from the
- * next outermost. Add any catch edges found, and stop at the first
- * enclosing fault funclet, as in step 1.
- * The resulting linkage reflects exception control-flow; fault funclets
- * unconditionally handle any exception in their protected region, so they
- * "dominate" outer-more handlers.
- */
- void GraphBuilder::linkExBlocks() {
- const Func::EHEntVec& ehtab = m_func->ehtab();
- // For every block, add edges to reachable fault and catch handlers.
- for (LinearBlocks i = linearBlocks(m_graph); !i.empty(); ) {
- Block* b = i.popFront();
- ASSERT(m_func->findEH(offset(b->start)) == m_func->findEH(offset(b->last)));
- Offset off = offset(b->start);
- int exn_index = 0;
- for (const EHEnt* eh = m_func->findEH(off); eh != 0; ) {
- ASSERT(eh->m_base <= off && off < eh->m_past);
- if (eh->m_ehtype == EHEnt::EHType_Catch) {
- // each catch block is reachable from b
- for (Range<EHEnt::CatchVec> j(eh->m_catches); !j.empty(); ) {
- exns(b)[exn_index++] = at(j.popFront().second);
- }
- eh = nextOuter(ehtab, eh);
- } else {
- // this is the innermost fault funclet reachable from b.
- exns(b)[exn_index++] = at(eh->m_fault);
- break;
- }
- }
- if (Op(*b->last) == OpUnwind) {
- // We're in a fault funclet. Find which one, then add edges
- // to reachable catches and the enclosing fault funclet, if any.
- const EHEnt* eh = findFunclet(ehtab, offset(b->last));
- eh = nextOuter(ehtab, eh);
- while (eh) {
- if (eh->m_ehtype == EHEnt::EHType_Catch) {
- // each catch target for eh is reachable from b
- for (Range<EHEnt::CatchVec> j(eh->m_catches); !j.empty(); ) {
- exns(b)[exn_index++] = at(j.popFront().second);
- }
- eh = nextOuter(ehtab, eh);
- } else {
- // eh is the innermost fault funclet reachable from b.
- exns(b)[exn_index++] = at(eh->m_fault);
- break;
- }
- }
- }
- }
- }
- Block** GraphBuilder::succs(Block* b) {
- if (!b->succs) {
- int num_succs = numSuccBlocks(b);
- Block** s = b->succs = new (m_arena) Block*[num_succs];
- memset(s, 0, sizeof(*s) * num_succs);
- }
- return b->succs;
- }
- Block** GraphBuilder::exns(Block* b) {
- if (!b->exns) {
- Block** e = b->exns = new (m_arena) Block*[m_graph->exn_cap];
- memset(e, 0, sizeof(*e) * m_graph->exn_cap);
- }
- return b->exns;
- }
- Block* GraphBuilder::createBlock(PC pc) {
- BlockMap::iterator i = m_blocks.find(pc);
- if (i != m_blocks.end()) return i->second;
- Block* b = new (m_arena) Block(pc);
- m_blocks.insert(std::pair<PC,Block*>(pc, b));
- return b;
- }
- Block* GraphBuilder::at(PC target) {
- BlockMap::iterator i = m_blocks.find(target);
- return i == m_blocks.end() ? 0 : i->second;
- }
- void GraphBuilder::addEdge(Block* from, EdgeKind k, Block* target) {
- ASSERT(target != 0);
- from->succs[k] = target;
- }
- /**
- * RpoSort does a depth-first search over successor and exception edges
- * of a Graph, visits blocks in postorder, and builds the reverse-postorder
- * list of blocks in-place. Each block's rpo_id is the reverse-postorder
- * number (entry starts at 0, and so-on).
- *
- * Note that in functions with optional parameters, the lowest-numbered
- * DV entry point is typically the "entry" since it falls through to
- * higher-numbered DV entry points, the last of which ultimately jumps to
- * the primary function body.
- */
- class RpoSort {
- public:
- RpoSort(Graph* g);
- private:
- void visit(Block* b);
- private:
- Graph* g;
- bool* visited;
- int rpo_id;
- };
- RpoSort::RpoSort(Graph* g) : g(g), rpo_id(0) {
- Arena scratch;
- visited = new (scratch) bool[g->block_count];
- memset(visited, 0, sizeof(bool) * g->block_count);
- g->first_rpo = 0;
- for (BlockPtrRange i = entryBlocks(g); !i.empty(); ) {
- visit(i.popFront());
- }
- }
- /**
- * Recursively visit b and its successors. We search exception edges
- * first in a weak attempt to put exception blocks later in the final
- * reverse-postorder numbering, but it shouldn't matter and anyone
- * counting on that has a bug.
- */
- void RpoSort::visit(Block* b) {
- if (visited[b->id]) return;
- visited[b->id] = true;
- for (BlockPtrRange i = exnBlocks(g, b); !i.empty(); ) visit(i.popBack());
- for (BlockPtrRange i = succBlocks(b); !i.empty(); ) visit(i.popBack());
- b->next_rpo = g->first_rpo;
- g->first_rpo = b;
- rpo_id++;
- b->rpo_id = g->block_count - rpo_id;
- }
- void sortRpo(Graph* g) {
- RpoSort _(g);
- }
- }}}