dce.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine, DCE - Dead Code Elimination |
  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. | Dmitry Stogov <dmitry@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. #include "ZendAccelerator.h"
  20. #include "Optimizer/zend_optimizer_internal.h"
  21. #include "Optimizer/zend_inference.h"
  22. #include "Optimizer/zend_ssa.h"
  23. #include "Optimizer/zend_func_info.h"
  24. #include "Optimizer/zend_call_graph.h"
  25. #include "zend_bitset.h"
  26. /* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes
  27. * that all instructions and phis are dead. Instructions with immediate side-effects are then marked
  28. * as live. We then recursively (using a worklist) propagate liveness to the instructions that def
  29. * the used operands.
  30. *
  31. * Notes:
  32. * * This pass does not perform unreachable code elimination. This happens as part of the SCCP
  33. * pass.
  34. * * The DCE is performed without taking control-dependence into account, i.e. all conditional
  35. * branches are assumed to be live. It's possible to take control-dependence into account using
  36. * the DCE algorithm described by Cytron et al., however it requires the construction of a
  37. * postdominator tree and of postdominance frontiers, which does not seem worthwhile at this
  38. * point.
  39. * * We separate intrinsic side-effects from potential side-effects in the form of notices thrown
  40. * by the instruction (in case we want to make this configurable). See may_have_side_effects() and
  41. * zend_may_throw().
  42. * * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same
  43. * order. There is an optimization option to allow reordering of dtor effects.
  44. * * The algorithm is able to eliminate dead modifications of non-escaping arrays
  45. * and objects as well as dead arrays and objects allocations.
  46. */
  47. typedef struct {
  48. zend_ssa *ssa;
  49. zend_op_array *op_array;
  50. zend_bitset instr_dead;
  51. zend_bitset phi_dead;
  52. zend_bitset instr_worklist;
  53. zend_bitset phi_worklist;
  54. zend_bitset phi_worklist_no_val;
  55. uint32_t instr_worklist_len;
  56. uint32_t phi_worklist_len;
  57. unsigned reorder_dtor_effects : 1;
  58. } context;
  59. static inline zend_bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
  60. if (def < 0) {
  61. /* This modification is not tracked by SSA, assume the worst */
  62. return 1;
  63. }
  64. if (ssa->var_info[use].type & MAY_BE_REF) {
  65. /* Modification of reference may have side-effect */
  66. return 1;
  67. }
  68. return 0;
  69. }
  70. static inline zend_bool may_have_side_effects(
  71. zend_op_array *op_array, zend_ssa *ssa,
  72. const zend_op *opline, const zend_ssa_op *ssa_op,
  73. zend_bool reorder_dtor_effects) {
  74. switch (opline->opcode) {
  75. case ZEND_NOP:
  76. case ZEND_IS_IDENTICAL:
  77. case ZEND_IS_NOT_IDENTICAL:
  78. case ZEND_QM_ASSIGN:
  79. case ZEND_FREE:
  80. case ZEND_TYPE_CHECK:
  81. case ZEND_DEFINED:
  82. case ZEND_ADD:
  83. case ZEND_SUB:
  84. case ZEND_MUL:
  85. case ZEND_POW:
  86. case ZEND_BW_OR:
  87. case ZEND_BW_AND:
  88. case ZEND_BW_XOR:
  89. case ZEND_CONCAT:
  90. case ZEND_FAST_CONCAT:
  91. case ZEND_DIV:
  92. case ZEND_MOD:
  93. case ZEND_BOOL_XOR:
  94. case ZEND_BOOL:
  95. case ZEND_BOOL_NOT:
  96. case ZEND_BW_NOT:
  97. case ZEND_SL:
  98. case ZEND_SR:
  99. case ZEND_IS_EQUAL:
  100. case ZEND_IS_NOT_EQUAL:
  101. case ZEND_IS_SMALLER:
  102. case ZEND_IS_SMALLER_OR_EQUAL:
  103. case ZEND_CASE:
  104. case ZEND_CAST:
  105. case ZEND_ROPE_INIT:
  106. case ZEND_ROPE_ADD:
  107. case ZEND_INIT_ARRAY:
  108. case ZEND_ADD_ARRAY_ELEMENT:
  109. case ZEND_SPACESHIP:
  110. case ZEND_STRLEN:
  111. case ZEND_COUNT:
  112. case ZEND_GET_TYPE:
  113. case ZEND_ISSET_ISEMPTY_THIS:
  114. case ZEND_ISSET_ISEMPTY_DIM_OBJ:
  115. case ZEND_FETCH_DIM_IS:
  116. case ZEND_ISSET_ISEMPTY_CV:
  117. case ZEND_ISSET_ISEMPTY_VAR:
  118. case ZEND_FETCH_IS:
  119. case ZEND_IN_ARRAY:
  120. case ZEND_FUNC_NUM_ARGS:
  121. case ZEND_FUNC_GET_ARGS:
  122. /* No side effects */
  123. return 0;
  124. case ZEND_ROPE_END:
  125. /* TODO: Rope dce optmization, see #76446 */
  126. return 1;
  127. case ZEND_JMP:
  128. case ZEND_JMPZ:
  129. case ZEND_JMPNZ:
  130. case ZEND_JMPZNZ:
  131. case ZEND_JMPZ_EX:
  132. case ZEND_JMPNZ_EX:
  133. case ZEND_JMP_SET:
  134. case ZEND_COALESCE:
  135. case ZEND_ASSERT_CHECK:
  136. /* For our purposes a jumps and branches are side effects. */
  137. return 1;
  138. case ZEND_BEGIN_SILENCE:
  139. case ZEND_END_SILENCE:
  140. case ZEND_ECHO:
  141. case ZEND_INCLUDE_OR_EVAL:
  142. case ZEND_THROW:
  143. case ZEND_EXT_STMT:
  144. case ZEND_EXT_FCALL_BEGIN:
  145. case ZEND_EXT_FCALL_END:
  146. case ZEND_EXT_NOP:
  147. case ZEND_TICKS:
  148. case ZEND_YIELD:
  149. case ZEND_YIELD_FROM:
  150. /* Intrinsic side effects */
  151. return 1;
  152. case ZEND_DO_FCALL:
  153. case ZEND_DO_FCALL_BY_NAME:
  154. case ZEND_DO_ICALL:
  155. case ZEND_DO_UCALL:
  156. /* For now assume all calls have side effects */
  157. return 1;
  158. case ZEND_RECV:
  159. case ZEND_RECV_INIT:
  160. /* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
  161. * due to the prologue skipping code. */
  162. return 1;
  163. case ZEND_ASSIGN_REF:
  164. return 1;
  165. case ZEND_ASSIGN:
  166. {
  167. if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
  168. return 1;
  169. }
  170. if (!reorder_dtor_effects) {
  171. if (opline->op2_type != IS_CONST
  172. && (OP2_INFO() & MAY_HAVE_DTOR)
  173. && ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
  174. /* DCE might shorten lifetime */
  175. return 1;
  176. }
  177. }
  178. return 0;
  179. }
  180. case ZEND_UNSET_VAR:
  181. return 1;
  182. case ZEND_UNSET_CV:
  183. {
  184. uint32_t t1 = OP1_INFO();
  185. if (t1 & MAY_BE_REF) {
  186. /* We don't consider uses as the LHS of an assignment as real uses during DCE, so
  187. * an unset may be considered dead even if there is a later assignment to the
  188. * variable. Removing the unset in this case would not be correct if the variable
  189. * is a reference, because unset breaks references. */
  190. return 1;
  191. }
  192. return 0;
  193. }
  194. case ZEND_PRE_INC:
  195. case ZEND_POST_INC:
  196. case ZEND_PRE_DEC:
  197. case ZEND_POST_DEC:
  198. return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def);
  199. case ZEND_ASSIGN_ADD:
  200. case ZEND_ASSIGN_SUB:
  201. case ZEND_ASSIGN_MUL:
  202. case ZEND_ASSIGN_DIV:
  203. case ZEND_ASSIGN_MOD:
  204. case ZEND_ASSIGN_SL:
  205. case ZEND_ASSIGN_SR:
  206. case ZEND_ASSIGN_CONCAT:
  207. case ZEND_ASSIGN_BW_OR:
  208. case ZEND_ASSIGN_BW_AND:
  209. case ZEND_ASSIGN_BW_XOR:
  210. case ZEND_ASSIGN_POW:
  211. return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
  212. || (opline->extended_value
  213. && ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE);
  214. case ZEND_ASSIGN_DIM:
  215. case ZEND_ASSIGN_OBJ:
  216. if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
  217. || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
  218. return 1;
  219. }
  220. if (!reorder_dtor_effects) {
  221. opline++;
  222. ssa_op++;
  223. if (opline->op1_type != IS_CONST
  224. && (OP1_INFO() & MAY_HAVE_DTOR)) {
  225. /* DCE might shorten lifetime */
  226. return 1;
  227. }
  228. }
  229. return 0;
  230. case ZEND_PRE_INC_OBJ:
  231. case ZEND_PRE_DEC_OBJ:
  232. case ZEND_POST_INC_OBJ:
  233. case ZEND_POST_DEC_OBJ:
  234. if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
  235. || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
  236. return 1;
  237. }
  238. return 0;
  239. default:
  240. /* For everything we didn't handle, assume a side-effect */
  241. return 1;
  242. }
  243. }
  244. static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
  245. zend_ssa_var *var = &ctx->ssa->vars[var_num];
  246. if (var->definition >= 0) {
  247. if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
  248. zend_bitset_incl(ctx->instr_worklist, var->definition);
  249. }
  250. } else if (var->definition_phi) {
  251. if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
  252. zend_bitset_incl(ctx->phi_worklist, var_num);
  253. }
  254. }
  255. }
  256. static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
  257. zend_ssa_var *var = &ctx->ssa->vars[var_num];
  258. if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
  259. zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
  260. }
  261. }
  262. static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, int check) {
  263. if (ssa_op->result_use >= 0) {
  264. add_to_worklists(ctx, ssa_op->result_use, check);
  265. }
  266. if (ssa_op->op1_use >= 0) {
  267. if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)) {
  268. add_to_worklists(ctx, ssa_op->op1_use, check);
  269. } else {
  270. add_to_phi_worklist_no_val(ctx, ssa_op->op1_use);
  271. }
  272. }
  273. if (ssa_op->op2_use >= 0) {
  274. if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)) {
  275. add_to_worklists(ctx, ssa_op->op2_use, check);
  276. } else {
  277. add_to_phi_worklist_no_val(ctx, ssa_op->op2_use);
  278. }
  279. }
  280. }
  281. static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
  282. zend_ssa *ssa = ctx->ssa;
  283. int source;
  284. FOREACH_PHI_SOURCE(phi, source) {
  285. add_to_worklists(ctx, source, check);
  286. } FOREACH_PHI_SOURCE_END();
  287. }
  288. static inline zend_bool is_var_dead(context *ctx, int var_num) {
  289. zend_ssa_var *var = &ctx->ssa->vars[var_num];
  290. if (var->definition_phi) {
  291. return zend_bitset_in(ctx->phi_dead, var_num);
  292. } else if (var->definition >= 0) {
  293. return zend_bitset_in(ctx->instr_dead, var->definition);
  294. } else {
  295. /* Variable has no definition, so either the definition has already been removed (var is
  296. * dead) or this is one of the implicit variables at the start of the function (for our
  297. * purposes live) */
  298. return var_num >= ctx->op_array->last_var;
  299. }
  300. }
  301. // Sometimes we can mark the var as EXT_UNUSED
  302. static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
  303. if (use_chain >= 0) {
  304. return 0;
  305. }
  306. zend_ssa_var *var = &ctx->ssa->vars[free_var];
  307. int def = var->definition;
  308. if (def >= 0) {
  309. zend_ssa_op *def_op = &ctx->ssa->ops[def];
  310. if (def_op->result_def == free_var
  311. && var->phi_use_chain == NULL
  312. && var->use_chain == (opline - ctx->op_array->opcodes)) {
  313. zend_op *def_opline = &ctx->op_array->opcodes[def];
  314. switch (def_opline->opcode) {
  315. case ZEND_ASSIGN:
  316. case ZEND_ASSIGN_REF:
  317. case ZEND_ASSIGN_DIM:
  318. case ZEND_ASSIGN_OBJ:
  319. case ZEND_ASSIGN_ADD:
  320. case ZEND_ASSIGN_SUB:
  321. case ZEND_ASSIGN_MUL:
  322. case ZEND_ASSIGN_DIV:
  323. case ZEND_ASSIGN_MOD:
  324. case ZEND_ASSIGN_SL:
  325. case ZEND_ASSIGN_SR:
  326. case ZEND_ASSIGN_CONCAT:
  327. case ZEND_ASSIGN_BW_OR:
  328. case ZEND_ASSIGN_BW_AND:
  329. case ZEND_ASSIGN_BW_XOR:
  330. case ZEND_ASSIGN_POW:
  331. case ZEND_PRE_INC:
  332. case ZEND_PRE_DEC:
  333. case ZEND_PRE_INC_OBJ:
  334. case ZEND_POST_INC_OBJ:
  335. case ZEND_PRE_DEC_OBJ:
  336. case ZEND_POST_DEC_OBJ:
  337. case ZEND_DO_ICALL:
  338. case ZEND_DO_UCALL:
  339. case ZEND_DO_FCALL_BY_NAME:
  340. case ZEND_DO_FCALL:
  341. case ZEND_INCLUDE_OR_EVAL:
  342. case ZEND_YIELD:
  343. case ZEND_YIELD_FROM:
  344. case ZEND_ASSERT_CHECK:
  345. def_opline->result_type = IS_UNUSED;
  346. def_opline->result.var = 0;
  347. def_op->result_def = -1;
  348. var->definition = -1;
  349. return 1;
  350. default:
  351. break;
  352. }
  353. }
  354. }
  355. return 0;
  356. }
  357. /* Returns whether the instruction has been DCEd */
  358. static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
  359. zend_ssa *ssa = ctx->ssa;
  360. int free_var = -1;
  361. zend_uchar free_var_type;
  362. if (opline->opcode == ZEND_NOP) {
  363. return 0;
  364. }
  365. /* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
  366. if (opline->opcode == ZEND_FREE
  367. && (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
  368. && !is_var_dead(ctx, ssa_op->op1_use)) {
  369. return 0;
  370. }
  371. if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
  372. if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
  373. if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)
  374. && opline->opcode != ZEND_CASE) {
  375. free_var = ssa_op->op1_use;
  376. free_var_type = opline->op1_type;
  377. }
  378. }
  379. }
  380. if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
  381. if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
  382. if (ssa->var_info[ssa_op->op2_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
  383. if (free_var >= 0) {
  384. // TODO: We can't free two vars. Keep instruction alive.
  385. zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
  386. return 0;
  387. }
  388. free_var = ssa_op->op2_use;
  389. free_var_type = opline->op2_type;
  390. }
  391. }
  392. }
  393. zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
  394. zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
  395. if (free_var >= 0) {
  396. opline->opcode = ZEND_FREE;
  397. opline->op1.var = (uintptr_t) ZEND_CALL_VAR_NUM(NULL, ssa->vars[free_var].var);
  398. opline->op1_type = free_var_type;
  399. ssa_op->op1_use = free_var;
  400. ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
  401. ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
  402. return 0;
  403. }
  404. return 1;
  405. }
  406. static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
  407. int common_source = -1;
  408. int source;
  409. FOREACH_PHI_SOURCE(phi, source) {
  410. if (common_source == -1) {
  411. common_source = source;
  412. } else if (common_source != source && source != phi->ssa_var) {
  413. return -1;
  414. }
  415. } FOREACH_PHI_SOURCE_END();
  416. ZEND_ASSERT(common_source != -1);
  417. return common_source;
  418. }
  419. static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
  420. zend_ssa *ssa = ctx->ssa;
  421. if (phi->pi < 0) {
  422. /* Phi assignment with identical source operands */
  423. int common_source = get_common_phi_source(ssa, phi);
  424. if (common_source >= 0) {
  425. zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1);
  426. zend_ssa_remove_phi(ssa, phi);
  427. }
  428. } else {
  429. /* Pi assignment that is only used in Phi/Pi assignments */
  430. // TODO What if we want to rerun type inference after DCE? Maybe separate this?
  431. /*ZEND_ASSERT(phi->sources[0] != -1);
  432. if (ssa->vars[phi->ssa_var].use_chain < 0) {
  433. zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1);
  434. zend_ssa_remove_phi(ssa, phi);
  435. }*/
  436. }
  437. }
  438. static inline zend_bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
  439. if (ssa_op->op1_def >= 0
  440. && ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
  441. return 1;
  442. }
  443. if (ssa_op->op2_def >= 0
  444. && ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
  445. return 1;
  446. }
  447. if (ssa_op->result_def >= 0
  448. && ssa->vars[ssa_op->result_def].var < op_array->num_args) {
  449. return 1;
  450. }
  451. return 0;
  452. }
  453. static void dce_live_ranges(context *ctx, zend_op_array *op_array, zend_ssa *ssa)
  454. {
  455. int i = 0;
  456. int j = 0;
  457. zend_live_range *live_range = op_array->live_range;
  458. while (i < op_array->last_live_range) {
  459. if ((live_range->var & ZEND_LIVE_MASK) != ZEND_LIVE_TMPVAR) {
  460. /* keep */
  461. j++;
  462. } else {
  463. uint32_t var = live_range->var & ~ZEND_LIVE_MASK;
  464. uint32_t def = live_range->start - 1;
  465. if ((op_array->opcodes[def].result_type == IS_UNUSED) &&
  466. (UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_STMT) ||
  467. UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_FCALL_END) ||
  468. UNEXPECTED(op_array->opcodes[def].opcode == ZEND_END_SILENCE))) {
  469. def--;
  470. }
  471. if (op_array->opcodes[def].result_type == IS_UNUSED) {
  472. if (op_array->opcodes[def].opcode == ZEND_DO_FCALL) {
  473. /* constructor call */
  474. do {
  475. def--;
  476. if ((op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR))
  477. && op_array->opcodes[def].result.var == var) {
  478. ZEND_ASSERT(op_array->opcodes[def].opcode == ZEND_NEW);
  479. break;
  480. }
  481. } while (def > 0);
  482. } else if (op_array->opcodes[def].opcode == ZEND_OP_DATA) {
  483. def--;
  484. }
  485. }
  486. #if ZEND_DEBUG
  487. ZEND_ASSERT(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR));
  488. ZEND_ASSERT(op_array->opcodes[def].result.var == var);
  489. ZEND_ASSERT(ssa->ops[def].result_def >= 0);
  490. #else
  491. if (!(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR))
  492. || op_array->opcodes[def].result.var != var
  493. || ssa->ops[def].result_def < 0) {
  494. /* TODO: Some wrong live-range? keep it. */
  495. j++;
  496. live_range++;
  497. i++;
  498. continue;
  499. }
  500. #endif
  501. var = ssa->ops[def].result_def;
  502. if ((ssa->var_info[var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
  503. && !is_var_dead(ctx, var)) {
  504. /* keep */
  505. j++;
  506. } else if (i != j) {
  507. op_array->live_range[j] = *live_range;
  508. }
  509. }
  510. live_range++;
  511. i++;
  512. }
  513. op_array->last_live_range = j;
  514. if (op_array->last_live_range == 0) {
  515. efree(op_array->live_range);
  516. op_array->live_range = NULL;
  517. }
  518. }
  519. int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) {
  520. int i;
  521. zend_ssa_phi *phi;
  522. int removed_ops = 0;
  523. /* DCE of CV operations that changes arguments may affect vararg functions. */
  524. zend_bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0;
  525. context ctx;
  526. ctx.ssa = ssa;
  527. ctx.op_array = op_array;
  528. ctx.reorder_dtor_effects = reorder_dtor_effects;
  529. /* We have no dedicated phi vector, so we use the whole ssa var vector instead */
  530. ctx.instr_worklist_len = zend_bitset_len(op_array->last);
  531. ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
  532. memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
  533. ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
  534. ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
  535. memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
  536. ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
  537. memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
  538. /* Optimistically assume all instructions and phis to be dead */
  539. ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
  540. memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
  541. ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
  542. memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
  543. /* Mark reacable instruction without side effects as dead */
  544. int b = ssa->cfg.blocks_count;
  545. while (b > 0) {
  546. int op_data = -1;
  547. b--;
  548. zend_basic_block *block = &ssa->cfg.blocks[b];
  549. if (!(block->flags & ZEND_BB_REACHABLE)) {
  550. continue;
  551. }
  552. i = block->start + block->len;
  553. while (i > block->start) {
  554. i--;
  555. if (op_array->opcodes[i].opcode == ZEND_OP_DATA) {
  556. op_data = i;
  557. continue;
  558. }
  559. if (zend_bitset_in(ctx.instr_worklist, i)) {
  560. zend_bitset_excl(ctx.instr_worklist, i);
  561. add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 0);
  562. if (op_data >= 0) {
  563. add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], 0);
  564. }
  565. } else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects)
  566. || zend_may_throw(&op_array->opcodes[i], op_array, ssa)
  567. || (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) {
  568. if (op_array->opcodes[i].opcode == ZEND_NEW
  569. && op_array->opcodes[i+1].opcode == ZEND_DO_FCALL
  570. && ssa->ops[i].result_def >= 0
  571. && ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
  572. zend_bitset_incl(ctx.instr_dead, i);
  573. zend_bitset_incl(ctx.instr_dead, i+1);
  574. } else {
  575. add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 0);
  576. if (op_data >= 0) {
  577. add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], 0);
  578. }
  579. }
  580. } else {
  581. zend_bitset_incl(ctx.instr_dead, i);
  582. if (op_data >= 0) {
  583. zend_bitset_incl(ctx.instr_dead, op_data);
  584. }
  585. }
  586. op_data = -1;
  587. }
  588. }
  589. /* Propagate liveness backwards to all definitions of used vars */
  590. while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len)
  591. || !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) {
  592. while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) {
  593. zend_bitset_excl(ctx.instr_dead, i);
  594. add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], 1);
  595. if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) {
  596. zend_bitset_excl(ctx.instr_dead, i+1);
  597. add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], 1);
  598. }
  599. }
  600. while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) {
  601. zend_bitset_excl(ctx.phi_dead, i);
  602. zend_bitset_excl(ctx.phi_worklist_no_val, i);
  603. add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1);
  604. }
  605. }
  606. if (op_array->live_range) {
  607. dce_live_ranges(&ctx, op_array, ssa);
  608. }
  609. /* Eliminate dead instructions */
  610. ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
  611. removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
  612. } ZEND_BITSET_FOREACH_END();
  613. /* Improper uses don't count as "uses" for the purpose of instruction elimination,
  614. * but we have to retain phis defining them.
  615. * Propagate this information backwards, marking any phi with an improperly used
  616. * target as non-dead. */
  617. while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) {
  618. zend_ssa_phi *phi = ssa->vars[i].definition_phi;
  619. int source;
  620. zend_bitset_excl(ctx.phi_dead, i);
  621. FOREACH_PHI_SOURCE(phi, source) {
  622. add_to_phi_worklist_no_val(&ctx, source);
  623. } FOREACH_PHI_SOURCE_END();
  624. }
  625. /* Now collect the actually dead phis */
  626. FOREACH_PHI(phi) {
  627. if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) {
  628. zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
  629. zend_ssa_remove_phi(ssa, phi);
  630. } else {
  631. /* Remove trivial phis (phis with identical source operands) */
  632. try_remove_trivial_phi(&ctx, phi);
  633. }
  634. } FOREACH_PHI_END();
  635. return removed_ops;
  636. }