Arquivos
hhvm/hphp/runtime/vm/verifier/cfg.cpp
T
Sean Cannella 303ea58976 convert enums to enum classes, part 2
C++11 cleanup (clean up easy enums)

Since sandcastle is failing:
2013-06-25 13:19:05 -07:00

306 linhas
10 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 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 "hphp/runtime/vm/verifier/cfg.h"
#include "hphp/runtime/vm/verifier/util.h"
#include "hphp/util/range.h"
namespace HPHP {
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 (isSwitch(*pc)) {
foreachSwitchTarget((Opcode*)pc, [&](Offset& o) {
createBlock(pc + o);
});
} 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 (isSwitch(*pc)) {
int i = 0;
foreachSwitchTarget((Opcode*)pc, [&](Offset& o) {
succs(block)[i++] = at(pc + o);
});
} 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_type == EHEnt::Type::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_type != EHEnt::Type::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_type == EHEnt::Type::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_type == EHEnt::Type::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:
explicit 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);
}
}}