PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/runtime/vm/verifier/cfg.cpp

http://github.com/facebook/hiphop-php
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
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include <runtime/vm/verifier/cfg.h>
  17. #include <runtime/vm/verifier/util.h>
  18. #include <util/range.h>
  19. namespace HPHP {
  20. namespace VM {
  21. namespace Verifier {
  22. /**
  23. * Create all blocks and edges for one Func.
  24. */
  25. Graph* GraphBuilder::build() {
  26. ASSERT(!funcInstrs(m_func).empty());
  27. m_graph = new (m_arena) Graph();
  28. createBlocks();
  29. createExBlocks();
  30. linkBlocks();
  31. linkExBlocks();
  32. return m_graph;
  33. }
  34. /**
  35. * Create blocks for each entry point as well as ordinary control
  36. * flow boundaries. Calls are not treated as basic-block ends.
  37. */
  38. void GraphBuilder::createBlocks() {
  39. PC bc = m_unit->entry();
  40. m_graph->param_count = m_func->params().size();
  41. m_graph->first_linear = createBlock(m_func->base());
  42. // DV entry points
  43. m_graph->entries = new (m_arena) Block*[m_graph->param_count + 1];
  44. int dv_index = 0;
  45. for (Range<Func::ParamInfoVec> p(m_func->params()); !p.empty(); ) {
  46. const Func::ParamInfo& param = p.popFront();
  47. m_graph->entries[dv_index++] = !param.hasDefaultValue() ? 0 :
  48. createBlock(param.funcletOff());
  49. }
  50. // main entry point
  51. ASSERT(dv_index == m_graph->param_count);
  52. m_graph->entries[dv_index] = createBlock(m_func->base());
  53. // ordinary basic block boundaries
  54. for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
  55. PC pc = i.popFront();
  56. if (isCF(pc) && !i.empty()) createBlock(i.front());
  57. if (Op(*pc) == OpSwitch) {
  58. ImmVector vec = getImmVector(pc);
  59. const int* v = vec.vec32();
  60. for (int i = 0, n = vec.size(); i < n; i++) createBlock(pc + v[i]);
  61. } else {
  62. Offset target = instrJumpTarget(bc, pc - bc);
  63. if (target != InvalidAbsoluteOffset) createBlock(target);
  64. }
  65. }
  66. }
  67. /**
  68. * Link ordinary blocks with ordinary edges and set their last instruction
  69. * and end offsets
  70. */
  71. void GraphBuilder::linkBlocks() {
  72. PC bc = m_unit->entry();
  73. Block* block = m_graph->first_linear;
  74. block->id = m_graph->block_count++;
  75. for (InstrRange i = funcInstrs(m_func); !i.empty(); ) {
  76. PC pc = i.popFront();
  77. block->last = pc;
  78. if (isCF(pc)) {
  79. if (Op(*pc) == OpSwitch) {
  80. ImmVector vec = getImmVector(pc);
  81. const int* v = vec.vec32();
  82. for (int i = 0, n = vec.size(); i < n; i++) {
  83. succs(block)[i] = at(pc + v[i]);
  84. }
  85. } else {
  86. Offset target = instrJumpTarget(bc, pc - bc);
  87. if (target != InvalidAbsoluteOffset) {
  88. ASSERT(numSuccBlocks(block) > 0);
  89. succs(block)[numSuccBlocks(block) - 1] = at(target);
  90. }
  91. }
  92. }
  93. PC next_pc = !i.empty() ? i.front() : m_unit->at(m_func->past());
  94. Block* next = at(next_pc);
  95. if (next) {
  96. block->next_linear = next;
  97. block->end = next_pc;
  98. if (!isTF(pc)) {
  99. ASSERT(numSuccBlocks(block) > 0);
  100. succs(block)[0] = next;
  101. }
  102. block = next;
  103. block->id = m_graph->block_count++;
  104. }
  105. }
  106. block->end = m_unit->at(m_func->past());
  107. }
  108. /**
  109. * Create blocks from exception handler boundaries; the start and end of
  110. * each protected region is a boundary, and the catch or fault entrypoint
  111. * of each handler is also a boundary.
  112. */
  113. void GraphBuilder::createExBlocks() {
  114. m_graph->exn_cap = m_func->ehtab().size();
  115. for (Range<Func::EHEntVec> i(m_func->ehtab()); !i.empty(); ) {
  116. const EHEnt& handler = i.popFront();
  117. createBlock(handler.m_base);
  118. createBlock(handler.m_past);
  119. if (handler.m_ehtype == EHEnt::EHType_Catch) {
  120. m_graph->exn_cap += handler.m_catches.size() - 1;
  121. for (Range<EHEnt::CatchVec> c(handler.m_catches); !c.empty(); ) {
  122. createBlock(c.popFront().second);
  123. }
  124. } else {
  125. createBlock(handler.m_fault);
  126. }
  127. }
  128. }
  129. /**
  130. * Find the EHEnt for the fault funclet that off is inside of. Off is an
  131. * offset in the funclet's handler (e.g. &Unwind), not the funclets protected
  132. * try region.
  133. */
  134. const EHEnt* findFunclet(const Func::EHEntVec& ehtab, Offset off) {
  135. const EHEnt* nearest = 0;
  136. for (Range<Func::EHEntVec> i(ehtab); !i.empty(); ) {
  137. const EHEnt* eh = &i.popFront();
  138. if (eh->m_ehtype != EHEnt::EHType_Fault) continue;
  139. if (eh->m_fault <= off && (!nearest || eh->m_fault > nearest->m_fault)) {
  140. nearest = eh;
  141. }
  142. }
  143. ASSERT(nearest != 0 && nearest->m_fault <= off);
  144. return nearest;
  145. }
  146. /**
  147. * return the next outermost handler or 0 if there aren't any
  148. */
  149. const EHEnt* nextOuter(const Func::EHEntVec& ehtab, const EHEnt* eh) {
  150. return eh->m_parentIndex != -1 ? &ehtab[eh->m_parentIndex] : 0;
  151. }
  152. /**
  153. * Link primary body blocks to exception handler blocks as follows:
  154. * 1. For each block in the body, traverse from the innermost to
  155. * outermost exception handler that includes the block. For
  156. * catch handlers, add an edge to each handler entry point. For
  157. * fault handlers, add an edge to the fault handler and stop.
  158. * 2. For each fault handler block ending in Unwind, find the EH
  159. * entry for the funclet, then traverse outwards starting from the
  160. * next outermost. Add any catch edges found, and stop at the first
  161. * enclosing fault funclet, as in step 1.
  162. * The resulting linkage reflects exception control-flow; fault funclets
  163. * unconditionally handle any exception in their protected region, so they
  164. * "dominate" outer-more handlers.
  165. */
  166. void GraphBuilder::linkExBlocks() {
  167. const Func::EHEntVec& ehtab = m_func->ehtab();
  168. // For every block, add edges to reachable fault and catch handlers.
  169. for (LinearBlocks i = linearBlocks(m_graph); !i.empty(); ) {
  170. Block* b = i.popFront();
  171. ASSERT(m_func->findEH(offset(b->start)) == m_func->findEH(offset(b->last)));
  172. Offset off = offset(b->start);
  173. int exn_index = 0;
  174. for (const EHEnt* eh = m_func->findEH(off); eh != 0; ) {
  175. ASSERT(eh->m_base <= off && off < eh->m_past);
  176. if (eh->m_ehtype == EHEnt::EHType_Catch) {
  177. // each catch block is reachable from b
  178. for (Range<EHEnt::CatchVec> j(eh->m_catches); !j.empty(); ) {
  179. exns(b)[exn_index++] = at(j.popFront().second);
  180. }
  181. eh = nextOuter(ehtab, eh);
  182. } else {
  183. // this is the innermost fault funclet reachable from b.
  184. exns(b)[exn_index++] = at(eh->m_fault);
  185. break;
  186. }
  187. }
  188. if (Op(*b->last) == OpUnwind) {
  189. // We're in a fault funclet. Find which one, then add edges
  190. // to reachable catches and the enclosing fault funclet, if any.
  191. const EHEnt* eh = findFunclet(ehtab, offset(b->last));
  192. eh = nextOuter(ehtab, eh);
  193. while (eh) {
  194. if (eh->m_ehtype == EHEnt::EHType_Catch) {
  195. // each catch target for eh is reachable from b
  196. for (Range<EHEnt::CatchVec> j(eh->m_catches); !j.empty(); ) {
  197. exns(b)[exn_index++] = at(j.popFront().second);
  198. }
  199. eh = nextOuter(ehtab, eh);
  200. } else {
  201. // eh is the innermost fault funclet reachable from b.
  202. exns(b)[exn_index++] = at(eh->m_fault);
  203. break;
  204. }
  205. }
  206. }
  207. }
  208. }
  209. Block** GraphBuilder::succs(Block* b) {
  210. if (!b->succs) {
  211. int num_succs = numSuccBlocks(b);
  212. Block** s = b->succs = new (m_arena) Block*[num_succs];
  213. memset(s, 0, sizeof(*s) * num_succs);
  214. }
  215. return b->succs;
  216. }
  217. Block** GraphBuilder::exns(Block* b) {
  218. if (!b->exns) {
  219. Block** e = b->exns = new (m_arena) Block*[m_graph->exn_cap];
  220. memset(e, 0, sizeof(*e) * m_graph->exn_cap);
  221. }
  222. return b->exns;
  223. }
  224. Block* GraphBuilder::createBlock(PC pc) {
  225. BlockMap::iterator i = m_blocks.find(pc);
  226. if (i != m_blocks.end()) return i->second;
  227. Block* b = new (m_arena) Block(pc);
  228. m_blocks.insert(std::pair<PC,Block*>(pc, b));
  229. return b;
  230. }
  231. Block* GraphBuilder::at(PC target) {
  232. BlockMap::iterator i = m_blocks.find(target);
  233. return i == m_blocks.end() ? 0 : i->second;
  234. }
  235. void GraphBuilder::addEdge(Block* from, EdgeKind k, Block* target) {
  236. ASSERT(target != 0);
  237. from->succs[k] = target;
  238. }
  239. /**
  240. * RpoSort does a depth-first search over successor and exception edges
  241. * of a Graph, visits blocks in postorder, and builds the reverse-postorder
  242. * list of blocks in-place. Each block's rpo_id is the reverse-postorder
  243. * number (entry starts at 0, and so-on).
  244. *
  245. * Note that in functions with optional parameters, the lowest-numbered
  246. * DV entry point is typically the "entry" since it falls through to
  247. * higher-numbered DV entry points, the last of which ultimately jumps to
  248. * the primary function body.
  249. */
  250. class RpoSort {
  251. public:
  252. RpoSort(Graph* g);
  253. private:
  254. void visit(Block* b);
  255. private:
  256. Graph* g;
  257. bool* visited;
  258. int rpo_id;
  259. };
  260. RpoSort::RpoSort(Graph* g) : g(g), rpo_id(0) {
  261. Arena scratch;
  262. visited = new (scratch) bool[g->block_count];
  263. memset(visited, 0, sizeof(bool) * g->block_count);
  264. g->first_rpo = 0;
  265. for (BlockPtrRange i = entryBlocks(g); !i.empty(); ) {
  266. visit(i.popFront());
  267. }
  268. }
  269. /**
  270. * Recursively visit b and its successors. We search exception edges
  271. * first in a weak attempt to put exception blocks later in the final
  272. * reverse-postorder numbering, but it shouldn't matter and anyone
  273. * counting on that has a bug.
  274. */
  275. void RpoSort::visit(Block* b) {
  276. if (visited[b->id]) return;
  277. visited[b->id] = true;
  278. for (BlockPtrRange i = exnBlocks(g, b); !i.empty(); ) visit(i.popBack());
  279. for (BlockPtrRange i = succBlocks(b); !i.empty(); ) visit(i.popBack());
  280. b->next_rpo = g->first_rpo;
  281. g->first_rpo = b;
  282. rpo_id++;
  283. b->rpo_id = g->block_count - rpo_id;
  284. }
  285. void sortRpo(Graph* g) {
  286. RpoSort _(g);
  287. }
  288. }}}