scdf.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine, Sparse Conditional Data Flow Propagation Framework |
  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: Nikita Popov <nikic@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "ZendAccelerator.h"
  19. #include "Optimizer/zend_optimizer_internal.h"
  20. #include "Optimizer/scdf.h"
  21. /* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is
  22. * based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a
  23. * generalized implementation as described in chapter 8.3 of the SSA book.
  24. *
  25. * Every SSA variable is associated with an element on a finite-height lattice, those value can only
  26. * ever be lowered during the operation of the algorithm. If a value is lowered all instructions and
  27. * phis using that value need to be reconsidered (this is done by adding the variable to a
  28. * worklist). For phi functions the result is computed by applying the meet operation to the
  29. * operands. This continues until a fixed point is reached.
  30. *
  31. * The algorithm is control-flow sensitive: All blocks except the start block are initially assumed
  32. * to be unreachable. When considering a branch instruction, we determine the feasible successors
  33. * based on the current state of the variable lattice. If a new edge becomes feasible we either have
  34. * to mark the successor block executable and consider all instructions in it, or, if the target is
  35. * already executable, we only have to reconsider the phi functions (as we only consider phi
  36. * operands which are associated with a feasible edge).
  37. *
  38. * The generic framework requires the definition of three functions:
  39. * * visit_instr() should recompute the lattice values of all SSA variables defined by an
  40. * instruction.
  41. * * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While
  42. * doing this it should only consider operands for which scfg_is_edge_feasible() returns true.
  43. * * get_feasible_successors() should determine the feasible successors for a branch instruction.
  44. * Note that this callback only needs to handle conditional branches (with two successors).
  45. */
  46. #if 0
  47. #define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
  48. #else
  49. #define DEBUG_PRINT(...)
  50. #endif
  51. void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
  52. uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
  53. if (zend_bitset_in(scdf->feasible_edges, edge)) {
  54. /* We already handled this edge */
  55. return;
  56. }
  57. DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
  58. zend_bitset_incl(scdf->feasible_edges, edge);
  59. if (!zend_bitset_in(scdf->executable_blocks, to)) {
  60. if (!zend_bitset_in(scdf->block_worklist, to)) {
  61. DEBUG_PRINT("Adding block %d to worklist\n", to);
  62. }
  63. zend_bitset_incl(scdf->block_worklist, to);
  64. } else {
  65. /* Block is already executable, only a new edge became feasible.
  66. * Reevaluate phi nodes to account for changed source operands. */
  67. zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
  68. zend_ssa_phi *phi;
  69. for (phi = ssa_block->phis; phi; phi = phi->next) {
  70. zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
  71. scdf->handlers.visit_phi(scdf, phi);
  72. }
  73. }
  74. }
  75. void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) {
  76. scdf->op_array = op_array;
  77. scdf->ssa = ssa;
  78. scdf->instr_worklist_len = zend_bitset_len(op_array->last);
  79. scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count);
  80. scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count);
  81. scdf->instr_worklist = zend_arena_calloc(&ctx->arena,
  82. scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count),
  83. sizeof(zend_ulong));
  84. scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len;
  85. scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len;
  86. scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len;
  87. scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len;
  88. zend_bitset_incl(scdf->block_worklist, 0);
  89. zend_bitset_incl(scdf->executable_blocks, 0);
  90. }
  91. void scdf_solve(scdf_ctx *scdf, const char *name) {
  92. zend_ssa *ssa = scdf->ssa;
  93. DEBUG_PRINT("Start SCDF solve (%s)\n", name);
  94. while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
  95. || !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
  96. || !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
  97. ) {
  98. int i;
  99. while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
  100. zend_ssa_phi *phi = ssa->vars[i].definition_phi;
  101. ZEND_ASSERT(phi);
  102. if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
  103. scdf->handlers.visit_phi(scdf, phi);
  104. }
  105. }
  106. while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
  107. int block_num = ssa->cfg.map[i];
  108. if (zend_bitset_in(scdf->executable_blocks, block_num)) {
  109. zend_basic_block *block = &ssa->cfg.blocks[block_num];
  110. zend_op *opline = &scdf->op_array->opcodes[i];
  111. zend_ssa_op *ssa_op = &ssa->ops[i];
  112. if (opline->opcode == ZEND_OP_DATA) {
  113. opline--;
  114. ssa_op--;
  115. }
  116. scdf->handlers.visit_instr(scdf, opline, ssa_op);
  117. if (i == block->start + block->len - 1) {
  118. if (block->successors_count == 1) {
  119. scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
  120. } else if (block->successors_count > 1) {
  121. scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
  122. }
  123. }
  124. }
  125. }
  126. while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
  127. /* This block is now live. Interpret phis and instructions in it. */
  128. zend_basic_block *block = &ssa->cfg.blocks[i];
  129. zend_ssa_block *ssa_block = &ssa->blocks[i];
  130. DEBUG_PRINT("Pop block %d from worklist\n", i);
  131. zend_bitset_incl(scdf->executable_blocks, i);
  132. {
  133. zend_ssa_phi *phi;
  134. for (phi = ssa_block->phis; phi; phi = phi->next) {
  135. zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
  136. scdf->handlers.visit_phi(scdf, phi);
  137. }
  138. }
  139. if (block->len == 0) {
  140. /* Zero length blocks don't have a last instruction that would normally do this */
  141. scdf_mark_edge_feasible(scdf, i, block->successors[0]);
  142. } else {
  143. zend_op *opline;
  144. int j, end = block->start + block->len;
  145. for (j = block->start; j < end; j++) {
  146. opline = &scdf->op_array->opcodes[j];
  147. zend_bitset_excl(scdf->instr_worklist, j);
  148. if (opline->opcode != ZEND_OP_DATA) {
  149. scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
  150. }
  151. }
  152. if (block->successors_count == 1) {
  153. scdf_mark_edge_feasible(scdf, i, block->successors[0]);
  154. } else if (block->successors_count > 1) {
  155. if (opline->opcode == ZEND_OP_DATA) {
  156. opline--;
  157. j--;
  158. }
  159. scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
  160. }
  161. }
  162. }
  163. }
  164. }
  165. /* If a live range starts in a reachable block and ends in an unreachable block, we should
  166. * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
  167. * is necessary for the correctness of temporary compaction. */
  168. static zend_bool kept_alive_by_live_range(scdf_ctx *scdf, uint32_t block) {
  169. uint32_t i;
  170. const zend_op_array *op_array = scdf->op_array;
  171. const zend_cfg *cfg = &scdf->ssa->cfg;
  172. for (i = 0; i < op_array->last_live_range; i++) {
  173. zend_live_range *live_range = &op_array->live_range[i];
  174. uint32_t start_block = cfg->map[live_range->start];
  175. uint32_t end_block = cfg->map[live_range->end];
  176. if (end_block == block && start_block != block
  177. && zend_bitset_in(scdf->executable_blocks, start_block)) {
  178. return 1;
  179. }
  180. }
  181. return 0;
  182. }
  183. /* Removes unreachable blocks. This will remove both the instructions (and phis) in the
  184. * blocks, as well as remove them from the successor / predecessor lists and mark them
  185. * unreachable. Blocks already marked unreachable are not removed. */
  186. int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
  187. zend_ssa *ssa = scdf->ssa;
  188. int i;
  189. int removed_ops = 0;
  190. for (i = 0; i < ssa->cfg.blocks_count; i++) {
  191. if (!zend_bitset_in(scdf->executable_blocks, i)
  192. && (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
  193. && !kept_alive_by_live_range(scdf, i)) {
  194. removed_ops += ssa->cfg.blocks[i].len;
  195. zend_ssa_remove_block(scdf->op_array, ssa, i);
  196. }
  197. }
  198. return removed_ops;
  199. }