zend_cfg.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine, CFG - Control Flow Graph |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2018 The PHP Group |
  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. | Authors: Dmitry Stogov <dmitry@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #include "zend_compile.h"
  20. #include "zend_cfg.h"
  21. #include "zend_func_info.h"
  22. #include "zend_worklist.h"
  23. #include "zend_optimizer.h"
  24. #include "zend_optimizer_internal.h"
  25. static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
  26. {
  27. zend_basic_block *blocks = cfg->blocks;
  28. while (1) {
  29. int i;
  30. b->flags |= ZEND_BB_REACHABLE;
  31. if (b->successors_count == 0) {
  32. b->flags |= ZEND_BB_EXIT;
  33. return;
  34. }
  35. for (i = 0; i < b->successors_count; i++) {
  36. zend_basic_block *succ = blocks + b->successors[i];
  37. if (b->len != 0) {
  38. zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
  39. if (b->successors_count == 1) {
  40. if (opcode == ZEND_JMP) {
  41. succ->flags |= ZEND_BB_TARGET;
  42. } else {
  43. succ->flags |= ZEND_BB_FOLLOW;
  44. if ((cfg->flags & ZEND_CFG_STACKLESS)) {
  45. if (opcode == ZEND_INCLUDE_OR_EVAL ||
  46. opcode == ZEND_GENERATOR_CREATE ||
  47. opcode == ZEND_YIELD ||
  48. opcode == ZEND_YIELD_FROM ||
  49. opcode == ZEND_DO_FCALL ||
  50. opcode == ZEND_DO_UCALL ||
  51. opcode == ZEND_DO_FCALL_BY_NAME) {
  52. succ->flags |= ZEND_BB_ENTRY;
  53. }
  54. }
  55. if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
  56. if (opcode == ZEND_RECV ||
  57. opcode == ZEND_RECV_INIT) {
  58. succ->flags |= ZEND_BB_RECV_ENTRY;
  59. }
  60. }
  61. }
  62. } else if (b->successors_count == 2) {
  63. if (i == 0 || opcode == ZEND_JMPZNZ) {
  64. succ->flags |= ZEND_BB_TARGET;
  65. } else {
  66. succ->flags |= ZEND_BB_FOLLOW;
  67. }
  68. } else {
  69. ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
  70. if (i == b->successors_count - 1) {
  71. succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
  72. } else {
  73. succ->flags |= ZEND_BB_TARGET;
  74. }
  75. }
  76. } else {
  77. succ->flags |= ZEND_BB_FOLLOW;
  78. }
  79. if (i == b->successors_count - 1) {
  80. /* Tail call optimization */
  81. if (succ->flags & ZEND_BB_REACHABLE) {
  82. return;
  83. }
  84. b = succ;
  85. break;
  86. } else {
  87. /* Recusively check reachability */
  88. if (!(succ->flags & ZEND_BB_REACHABLE)) {
  89. zend_mark_reachable(opcodes, cfg, succ);
  90. }
  91. }
  92. }
  93. }
  94. }
  95. /* }}} */
  96. static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
  97. {
  98. zend_basic_block *blocks = cfg->blocks;
  99. blocks[start].flags = ZEND_BB_START;
  100. zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
  101. if (op_array->last_live_range || op_array->last_try_catch) {
  102. zend_basic_block *b;
  103. int j, changed;
  104. uint32_t *block_map = cfg->map;
  105. do {
  106. changed = 0;
  107. /* Add live range paths */
  108. for (j = 0; j < op_array->last_live_range; j++) {
  109. zend_live_range *live_range = &op_array->live_range[j];
  110. if (live_range->var == (uint32_t)-1) {
  111. /* this live range already removed */
  112. continue;
  113. }
  114. b = blocks + block_map[live_range->start];
  115. if (b->flags & ZEND_BB_REACHABLE) {
  116. while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
  117. /* check if NOP breaks incorrect smart branch */
  118. if (b->len == 2
  119. && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
  120. || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
  121. && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
  122. && b->start > 0
  123. && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
  124. break;
  125. }
  126. b->start++;
  127. b->len--;
  128. }
  129. if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
  130. /* mark as removed (empty live range) */
  131. live_range->var = (uint32_t)-1;
  132. continue;
  133. }
  134. b->flags |= ZEND_BB_GEN_VAR;
  135. b = blocks + block_map[live_range->end];
  136. b->flags |= ZEND_BB_KILL_VAR;
  137. if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
  138. if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
  139. changed = 1;
  140. zend_mark_reachable(op_array->opcodes, cfg, b);
  141. } else {
  142. b->flags |= ZEND_BB_UNREACHABLE_FREE;
  143. }
  144. }
  145. } else {
  146. ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
  147. }
  148. }
  149. /* Add exception paths */
  150. for (j = 0; j < op_array->last_try_catch; j++) {
  151. /* check for jumps into the middle of try block */
  152. b = blocks + block_map[op_array->try_catch_array[j].try_op];
  153. if (!(b->flags & ZEND_BB_REACHABLE)) {
  154. zend_basic_block *end;
  155. if (op_array->try_catch_array[j].catch_op) {
  156. end = blocks + block_map[op_array->try_catch_array[j].catch_op];
  157. while (b != end) {
  158. if (b->flags & ZEND_BB_REACHABLE) {
  159. op_array->try_catch_array[j].try_op = b->start;
  160. break;
  161. }
  162. b++;
  163. }
  164. }
  165. b = blocks + block_map[op_array->try_catch_array[j].try_op];
  166. if (!(b->flags & ZEND_BB_REACHABLE)) {
  167. if (op_array->try_catch_array[j].finally_op) {
  168. end = blocks + block_map[op_array->try_catch_array[j].finally_op];
  169. while (b != end) {
  170. if (b->flags & ZEND_BB_REACHABLE) {
  171. op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
  172. changed = 1;
  173. zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
  174. break;
  175. }
  176. b++;
  177. }
  178. }
  179. }
  180. }
  181. b = blocks + block_map[op_array->try_catch_array[j].try_op];
  182. if (b->flags & ZEND_BB_REACHABLE) {
  183. b->flags |= ZEND_BB_TRY;
  184. if (op_array->try_catch_array[j].catch_op) {
  185. b = blocks + block_map[op_array->try_catch_array[j].catch_op];
  186. b->flags |= ZEND_BB_CATCH;
  187. if (!(b->flags & ZEND_BB_REACHABLE)) {
  188. changed = 1;
  189. zend_mark_reachable(op_array->opcodes, cfg, b);
  190. }
  191. }
  192. if (op_array->try_catch_array[j].finally_op) {
  193. b = blocks + block_map[op_array->try_catch_array[j].finally_op];
  194. b->flags |= ZEND_BB_FINALLY;
  195. if (!(b->flags & ZEND_BB_REACHABLE)) {
  196. changed = 1;
  197. zend_mark_reachable(op_array->opcodes, cfg, b);
  198. }
  199. }
  200. if (op_array->try_catch_array[j].finally_end) {
  201. b = blocks + block_map[op_array->try_catch_array[j].finally_end];
  202. b->flags |= ZEND_BB_FINALLY_END;
  203. if (!(b->flags & ZEND_BB_REACHABLE)) {
  204. changed = 1;
  205. zend_mark_reachable(op_array->opcodes, cfg, b);
  206. }
  207. }
  208. } else {
  209. if (op_array->try_catch_array[j].catch_op) {
  210. ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
  211. }
  212. if (op_array->try_catch_array[j].finally_op) {
  213. ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
  214. }
  215. if (op_array->try_catch_array[j].finally_end) {
  216. ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
  217. }
  218. }
  219. }
  220. } while (changed);
  221. }
  222. }
  223. /* }}} */
  224. void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
  225. {
  226. zend_basic_block *blocks = cfg->blocks;
  227. int i;
  228. int start = 0;
  229. for (i = 0; i < cfg->blocks_count; i++) {
  230. if (blocks[i].flags & ZEND_BB_REACHABLE) {
  231. start = i;
  232. i++;
  233. break;
  234. }
  235. }
  236. /* clear all flags */
  237. for (i = 0; i < cfg->blocks_count; i++) {
  238. blocks[i].flags = 0;
  239. }
  240. zend_mark_reachable_blocks(op_array, cfg, start);
  241. }
  242. /* }}} */
  243. static void initialize_block(zend_basic_block *block) {
  244. block->flags = 0;
  245. block->successors = block->successors_storage;
  246. block->successors_count = 0;
  247. block->predecessors_count = 0;
  248. block->predecessor_offset = -1;
  249. block->idom = -1;
  250. block->loop_header = -1;
  251. block->level = -1;
  252. block->children = -1;
  253. block->next_child = -1;
  254. }
  255. #define BB_START(i) do { \
  256. if (!block_map[i]) { blocks_count++;} \
  257. block_map[i]++; \
  258. } while (0)
  259. int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
  260. {
  261. uint32_t flags = 0;
  262. uint32_t i;
  263. int j;
  264. uint32_t *block_map;
  265. zend_function *fn;
  266. int blocks_count = 0;
  267. zend_basic_block *blocks;
  268. zval *zv;
  269. zend_bool extra_entry_block = 0;
  270. cfg->flags = build_flags & (ZEND_CFG_SPLIT_AT_LIVE_RANGES|ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
  271. cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
  272. /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
  273. BB_START(0);
  274. for (i = 0; i < op_array->last; i++) {
  275. zend_op *opline = op_array->opcodes + i;
  276. switch (opline->opcode) {
  277. case ZEND_RECV:
  278. case ZEND_RECV_INIT:
  279. if (build_flags & ZEND_CFG_RECV_ENTRY) {
  280. BB_START(i + 1);
  281. }
  282. break;
  283. case ZEND_RETURN:
  284. case ZEND_RETURN_BY_REF:
  285. case ZEND_GENERATOR_RETURN:
  286. case ZEND_EXIT:
  287. case ZEND_THROW:
  288. if (i + 1 < op_array->last) {
  289. BB_START(i + 1);
  290. }
  291. break;
  292. case ZEND_INCLUDE_OR_EVAL:
  293. flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
  294. case ZEND_GENERATOR_CREATE:
  295. case ZEND_YIELD:
  296. case ZEND_YIELD_FROM:
  297. if (build_flags & ZEND_CFG_STACKLESS) {
  298. BB_START(i + 1);
  299. }
  300. break;
  301. case ZEND_DO_FCALL:
  302. case ZEND_DO_UCALL:
  303. case ZEND_DO_FCALL_BY_NAME:
  304. flags |= ZEND_FUNC_HAS_CALLS;
  305. if (build_flags & ZEND_CFG_STACKLESS) {
  306. BB_START(i + 1);
  307. }
  308. break;
  309. case ZEND_DO_ICALL:
  310. flags |= ZEND_FUNC_HAS_CALLS;
  311. break;
  312. case ZEND_INIT_FCALL:
  313. case ZEND_INIT_NS_FCALL_BY_NAME:
  314. zv = CRT_CONSTANT(opline->op2);
  315. if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
  316. /* The third literal is the lowercased unqualified name */
  317. zv += 2;
  318. }
  319. if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
  320. if (fn->type == ZEND_INTERNAL_FUNCTION) {
  321. flags |= zend_optimizer_classify_function(
  322. Z_STR_P(zv), opline->extended_value);
  323. }
  324. }
  325. break;
  326. case ZEND_FAST_CALL:
  327. BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
  328. BB_START(i + 1);
  329. break;
  330. case ZEND_FAST_RET:
  331. if (i + 1 < op_array->last) {
  332. BB_START(i + 1);
  333. }
  334. break;
  335. case ZEND_JMP:
  336. BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
  337. if (i + 1 < op_array->last) {
  338. BB_START(i + 1);
  339. }
  340. break;
  341. case ZEND_JMPZNZ:
  342. BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
  343. BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
  344. if (i + 1 < op_array->last) {
  345. BB_START(i + 1);
  346. }
  347. break;
  348. case ZEND_JMPZ:
  349. case ZEND_JMPNZ:
  350. case ZEND_JMPZ_EX:
  351. case ZEND_JMPNZ_EX:
  352. case ZEND_JMP_SET:
  353. case ZEND_COALESCE:
  354. case ZEND_ASSERT_CHECK:
  355. BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
  356. BB_START(i + 1);
  357. break;
  358. case ZEND_CATCH:
  359. if (!(opline->extended_value & ZEND_LAST_CATCH)) {
  360. BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
  361. }
  362. BB_START(i + 1);
  363. break;
  364. case ZEND_DECLARE_ANON_CLASS:
  365. case ZEND_DECLARE_ANON_INHERITED_CLASS:
  366. case ZEND_FE_FETCH_R:
  367. case ZEND_FE_FETCH_RW:
  368. BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
  369. BB_START(i + 1);
  370. break;
  371. case ZEND_FE_RESET_R:
  372. case ZEND_FE_RESET_RW:
  373. BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
  374. BB_START(i + 1);
  375. break;
  376. case ZEND_SWITCH_LONG:
  377. case ZEND_SWITCH_STRING:
  378. {
  379. HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
  380. zval *zv;
  381. ZEND_HASH_FOREACH_VAL(jumptable, zv) {
  382. BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
  383. } ZEND_HASH_FOREACH_END();
  384. BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
  385. BB_START(i + 1);
  386. break;
  387. }
  388. case ZEND_UNSET_VAR:
  389. case ZEND_ISSET_ISEMPTY_VAR:
  390. if (opline->extended_value & ZEND_FETCH_LOCAL) {
  391. flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
  392. } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
  393. !op_array->function_name) {
  394. flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
  395. }
  396. break;
  397. case ZEND_FETCH_R:
  398. case ZEND_FETCH_W:
  399. case ZEND_FETCH_RW:
  400. case ZEND_FETCH_FUNC_ARG:
  401. case ZEND_FETCH_IS:
  402. case ZEND_FETCH_UNSET:
  403. if (opline->extended_value & ZEND_FETCH_LOCAL) {
  404. flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
  405. } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
  406. !op_array->function_name) {
  407. flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
  408. }
  409. break;
  410. case ZEND_FUNC_GET_ARGS:
  411. flags |= ZEND_FUNC_VARARG;
  412. break;
  413. case ZEND_EXT_NOP:
  414. case ZEND_EXT_STMT:
  415. case ZEND_EXT_FCALL_BEGIN:
  416. case ZEND_EXT_FCALL_END:
  417. flags |= ZEND_FUNC_HAS_EXTENDED_INFO;
  418. break;
  419. }
  420. }
  421. /* If the entry block has predecessors, we may need to split it */
  422. if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
  423. && op_array->last > 0 && block_map[0] > 1) {
  424. extra_entry_block = 1;
  425. }
  426. if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
  427. for (j = 0; j < op_array->last_live_range; j++) {
  428. BB_START(op_array->live_range[j].start);
  429. BB_START(op_array->live_range[j].end);
  430. }
  431. }
  432. if (op_array->last_try_catch) {
  433. for (j = 0; j < op_array->last_try_catch; j++) {
  434. BB_START(op_array->try_catch_array[j].try_op);
  435. if (op_array->try_catch_array[j].catch_op) {
  436. BB_START(op_array->try_catch_array[j].catch_op);
  437. }
  438. if (op_array->try_catch_array[j].finally_op) {
  439. BB_START(op_array->try_catch_array[j].finally_op);
  440. }
  441. if (op_array->try_catch_array[j].finally_end) {
  442. BB_START(op_array->try_catch_array[j].finally_end);
  443. }
  444. }
  445. }
  446. blocks_count += extra_entry_block;
  447. cfg->blocks_count = blocks_count;
  448. /* Build CFG, Step 2: Build Array of Basic Blocks */
  449. cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
  450. blocks_count = -1;
  451. if (extra_entry_block) {
  452. initialize_block(&blocks[0]);
  453. blocks[0].start = 0;
  454. blocks[0].len = 0;
  455. blocks_count++;
  456. }
  457. for (i = 0; i < op_array->last; i++) {
  458. if (block_map[i]) {
  459. if (blocks_count >= 0) {
  460. blocks[blocks_count].len = i - blocks[blocks_count].start;
  461. }
  462. blocks_count++;
  463. initialize_block(&blocks[blocks_count]);
  464. blocks[blocks_count].start = i;
  465. }
  466. block_map[i] = blocks_count;
  467. }
  468. blocks[blocks_count].len = i - blocks[blocks_count].start;
  469. blocks_count++;
  470. /* Build CFG, Step 3: Calculate successors */
  471. for (j = 0; j < blocks_count; j++) {
  472. zend_basic_block *block = &blocks[j];
  473. zend_op *opline;
  474. if (block->len == 0) {
  475. block->successors_count = 1;
  476. block->successors[0] = j + 1;
  477. continue;
  478. }
  479. opline = op_array->opcodes + block->start + block->len - 1;
  480. switch (opline->opcode) {
  481. case ZEND_FAST_RET:
  482. case ZEND_RETURN:
  483. case ZEND_RETURN_BY_REF:
  484. case ZEND_GENERATOR_RETURN:
  485. case ZEND_EXIT:
  486. case ZEND_THROW:
  487. break;
  488. case ZEND_JMP:
  489. block->successors_count = 1;
  490. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
  491. break;
  492. case ZEND_JMPZNZ:
  493. block->successors_count = 2;
  494. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
  495. block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
  496. break;
  497. case ZEND_JMPZ:
  498. case ZEND_JMPNZ:
  499. case ZEND_JMPZ_EX:
  500. case ZEND_JMPNZ_EX:
  501. case ZEND_JMP_SET:
  502. case ZEND_COALESCE:
  503. case ZEND_ASSERT_CHECK:
  504. block->successors_count = 2;
  505. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
  506. block->successors[1] = j + 1;
  507. break;
  508. case ZEND_CATCH:
  509. if (!(opline->extended_value & ZEND_LAST_CATCH)) {
  510. block->successors_count = 2;
  511. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
  512. block->successors[1] = j + 1;
  513. } else {
  514. block->successors_count = 1;
  515. block->successors[0] = j + 1;
  516. }
  517. break;
  518. case ZEND_DECLARE_ANON_CLASS:
  519. case ZEND_DECLARE_ANON_INHERITED_CLASS:
  520. case ZEND_FE_FETCH_R:
  521. case ZEND_FE_FETCH_RW:
  522. block->successors_count = 2;
  523. block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
  524. block->successors[1] = j + 1;
  525. break;
  526. case ZEND_FE_RESET_R:
  527. case ZEND_FE_RESET_RW:
  528. block->successors_count = 2;
  529. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
  530. block->successors[1] = j + 1;
  531. break;
  532. case ZEND_FAST_CALL:
  533. block->successors_count = 2;
  534. block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
  535. block->successors[1] = j + 1;
  536. break;
  537. case ZEND_SWITCH_LONG:
  538. case ZEND_SWITCH_STRING:
  539. {
  540. HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
  541. zval *zv;
  542. uint32_t s = 0;
  543. block->successors_count = 2 + zend_hash_num_elements(jumptable);
  544. block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
  545. ZEND_HASH_FOREACH_VAL(jumptable, zv) {
  546. block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
  547. } ZEND_HASH_FOREACH_END();
  548. block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
  549. block->successors[s++] = j + 1;
  550. break;
  551. }
  552. default:
  553. block->successors_count = 1;
  554. block->successors[0] = j + 1;
  555. break;
  556. }
  557. }
  558. /* Build CFG, Step 4, Mark Reachable Basic Blocks */
  559. zend_mark_reachable_blocks(op_array, cfg, 0);
  560. cfg->flags |= flags;
  561. return SUCCESS;
  562. }
  563. /* }}} */
  564. int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
  565. {
  566. int j, s, edges;
  567. zend_basic_block *b;
  568. zend_basic_block *blocks = cfg->blocks;
  569. zend_basic_block *end = blocks + cfg->blocks_count;
  570. int *predecessors;
  571. edges = 0;
  572. for (b = blocks; b < end; b++) {
  573. b->predecessors_count = 0;
  574. }
  575. for (b = blocks; b < end; b++) {
  576. if (!(b->flags & ZEND_BB_REACHABLE)) {
  577. b->successors_count = 0;
  578. b->predecessors_count = 0;
  579. } else {
  580. for (s = 0; s < b->successors_count; s++) {
  581. edges++;
  582. blocks[b->successors[s]].predecessors_count++;
  583. }
  584. }
  585. }
  586. cfg->edges_count = edges;
  587. cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
  588. edges = 0;
  589. for (b = blocks; b < end; b++) {
  590. if (b->flags & ZEND_BB_REACHABLE) {
  591. b->predecessor_offset = edges;
  592. edges += b->predecessors_count;
  593. b->predecessors_count = 0;
  594. }
  595. }
  596. for (j = 0; j < cfg->blocks_count; j++) {
  597. if (blocks[j].flags & ZEND_BB_REACHABLE) {
  598. /* SWITCH_STRING/LONG may have few identical successors */
  599. for (s = 0; s < blocks[j].successors_count; s++) {
  600. int duplicate = 0;
  601. int p;
  602. for (p = 0; p < s; p++) {
  603. if (blocks[j].successors[p] == blocks[j].successors[s]) {
  604. duplicate = 1;
  605. break;
  606. }
  607. }
  608. if (!duplicate) {
  609. zend_basic_block *b = blocks + blocks[j].successors[s];
  610. predecessors[b->predecessor_offset + b->predecessors_count] = j;
  611. b->predecessors_count++;
  612. }
  613. }
  614. }
  615. }
  616. return SUCCESS;
  617. }
  618. /* }}} */
  619. /* Computes a postorder numbering of the CFG */
  620. static void compute_postnum_recursive(
  621. int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
  622. {
  623. int s;
  624. zend_basic_block *block = &cfg->blocks[block_num];
  625. if (postnum[block_num] != -1) {
  626. return;
  627. }
  628. postnum[block_num] = -2; /* Marker for "currently visiting" */
  629. for (s = 0; s < block->successors_count; s++) {
  630. compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
  631. }
  632. postnum[block_num] = (*cur)++;
  633. }
  634. /* }}} */
  635. /* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
  636. * Cooper, Harvey and Kennedy. */
  637. int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
  638. {
  639. zend_basic_block *blocks = cfg->blocks;
  640. int blocks_count = cfg->blocks_count;
  641. int j, k, changed;
  642. ALLOCA_FLAG(use_heap)
  643. int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
  644. memset(postnum, -1, sizeof(int) * cfg->blocks_count);
  645. j = 0;
  646. compute_postnum_recursive(postnum, &j, cfg, 0);
  647. /* FIXME: move declarations */
  648. blocks[0].idom = 0;
  649. do {
  650. changed = 0;
  651. /* Iterating in RPO here would converge faster */
  652. for (j = 1; j < blocks_count; j++) {
  653. int idom = -1;
  654. if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
  655. continue;
  656. }
  657. for (k = 0; k < blocks[j].predecessors_count; k++) {
  658. int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
  659. if (idom < 0) {
  660. if (blocks[pred].idom >= 0)
  661. idom = pred;
  662. continue;
  663. }
  664. if (blocks[pred].idom >= 0) {
  665. while (idom != pred) {
  666. while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
  667. while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
  668. }
  669. }
  670. }
  671. if (idom >= 0 && blocks[j].idom != idom) {
  672. blocks[j].idom = idom;
  673. changed = 1;
  674. }
  675. }
  676. } while (changed);
  677. blocks[0].idom = -1;
  678. for (j = 1; j < blocks_count; j++) {
  679. if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
  680. continue;
  681. }
  682. if (blocks[j].idom >= 0) {
  683. /* Sort by block number to traverse children in pre-order */
  684. if (blocks[blocks[j].idom].children < 0 ||
  685. j < blocks[blocks[j].idom].children) {
  686. blocks[j].next_child = blocks[blocks[j].idom].children;
  687. blocks[blocks[j].idom].children = j;
  688. } else {
  689. int k = blocks[blocks[j].idom].children;
  690. while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
  691. k = blocks[k].next_child;
  692. }
  693. blocks[j].next_child = blocks[k].next_child;
  694. blocks[k].next_child = j;
  695. }
  696. }
  697. }
  698. for (j = 0; j < blocks_count; j++) {
  699. int idom = blocks[j].idom, level = 0;
  700. if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
  701. continue;
  702. }
  703. while (idom >= 0) {
  704. level++;
  705. if (blocks[idom].level >= 0) {
  706. level += blocks[idom].level;
  707. break;
  708. } else {
  709. idom = blocks[idom].idom;
  710. }
  711. }
  712. blocks[j].level = level;
  713. }
  714. free_alloca(postnum, use_heap);
  715. return SUCCESS;
  716. }
  717. /* }}} */
  718. static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
  719. {
  720. while (blocks[b].level > blocks[a].level) {
  721. b = blocks[b].idom;
  722. }
  723. return a == b;
  724. }
  725. /* }}} */
  726. typedef struct {
  727. int id;
  728. int level;
  729. } block_info;
  730. static int compare_block_level(const block_info *a, const block_info *b) {
  731. return b->level - a->level;
  732. }
  733. static void swap_blocks(block_info *a, block_info *b) {
  734. block_info tmp = *a;
  735. *a = *b;
  736. *b = tmp;
  737. }
  738. int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
  739. {
  740. int i, j, k, n;
  741. int time;
  742. zend_basic_block *blocks = cfg->blocks;
  743. int *entry_times, *exit_times;
  744. zend_worklist work;
  745. int flag = ZEND_FUNC_NO_LOOPS;
  746. block_info *sorted_blocks;
  747. ALLOCA_FLAG(list_use_heap)
  748. ALLOCA_FLAG(tree_use_heap)
  749. ALLOCA_FLAG(sorted_blocks_use_heap)
  750. ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
  751. /* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
  752. * queries. These are implemented by checking entry/exit times of the DFS search. */
  753. entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
  754. exit_times = entry_times + cfg->blocks_count;
  755. memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
  756. zend_worklist_push(&work, 0);
  757. time = 0;
  758. while (zend_worklist_len(&work)) {
  759. next:
  760. i = zend_worklist_peek(&work);
  761. if (entry_times[i] == -1) {
  762. entry_times[i] = time++;
  763. }
  764. /* Visit blocks immediately dominated by i. */
  765. for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
  766. if (zend_worklist_push(&work, j)) {
  767. goto next;
  768. }
  769. }
  770. /* Visit join edges. */
  771. for (j = 0; j < blocks[i].successors_count; j++) {
  772. int succ = blocks[i].successors[j];
  773. if (blocks[succ].idom == i) {
  774. continue;
  775. } else if (zend_worklist_push(&work, succ)) {
  776. goto next;
  777. }
  778. }
  779. exit_times[i] = time++;
  780. zend_worklist_pop(&work);
  781. }
  782. /* Sort blocks by decreasing level, which is the order in which we want to process them */
  783. sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
  784. for (i = 0; i < cfg->blocks_count; i++) {
  785. sorted_blocks[i].id = i;
  786. sorted_blocks[i].level = blocks[i].level;
  787. }
  788. zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
  789. (compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
  790. /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
  791. Graphs". */
  792. for (n = 0; n < cfg->blocks_count; n++) {
  793. i = sorted_blocks[n].id;
  794. zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
  795. for (j = 0; j < blocks[i].predecessors_count; j++) {
  796. int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
  797. /* A join edge is one for which the predecessor does not
  798. immediately dominate the successor. */
  799. if (blocks[i].idom == pred) {
  800. continue;
  801. }
  802. /* In a loop back-edge (back-join edge), the successor dominates
  803. the predecessor. */
  804. if (dominates(blocks, i, pred)) {
  805. blocks[i].flags |= ZEND_BB_LOOP_HEADER;
  806. flag &= ~ZEND_FUNC_NO_LOOPS;
  807. zend_worklist_push(&work, pred);
  808. } else {
  809. /* Otherwise it's a cross-join edge. See if it's a branch
  810. to an ancestor on the DJ spanning tree. */
  811. if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
  812. blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
  813. flag |= ZEND_FUNC_IRREDUCIBLE;
  814. flag &= ~ZEND_FUNC_NO_LOOPS;
  815. }
  816. }
  817. }
  818. while (zend_worklist_len(&work)) {
  819. j = zend_worklist_pop(&work);
  820. while (blocks[j].loop_header >= 0) {
  821. j = blocks[j].loop_header;
  822. }
  823. if (j != i) {
  824. blocks[j].loop_header = i;
  825. for (k = 0; k < blocks[j].predecessors_count; k++) {
  826. zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
  827. }
  828. }
  829. }
  830. }
  831. free_alloca(sorted_blocks, sorted_blocks_use_heap);
  832. free_alloca(entry_times, tree_use_heap);
  833. ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
  834. cfg->flags |= flag;
  835. return SUCCESS;
  836. }
  837. /* }}} */
  838. /*
  839. * Local variables:
  840. * tab-width: 4
  841. * c-basic-offset: 4
  842. * indent-tabs-mode: t
  843. * End:
  844. */