zend_cfg.c 26 KB

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