escape_analysis.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache, Escape Analysis |
  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 "Optimizer/zend_optimizer.h"
  20. #include "Optimizer/zend_optimizer_internal.h"
  21. #include "zend_bitset.h"
  22. #include "zend_cfg.h"
  23. #include "zend_ssa.h"
  24. #include "zend_inference.h"
  25. #include "zend_dump.h"
  26. /*
  27. * T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic
  28. * compilation and deoptimization. In Proceedings of the International
  29. * Conference on Virtual Execution Environments, pages 111-120, Chicago,
  30. * June 2005
  31. */
  32. static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
  33. {
  34. int i;
  35. for (i = 0; i < count; i++) {
  36. parent[i] = i;
  37. size[i] = 1;
  38. }
  39. }
  40. /* }}} */
  41. static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
  42. {
  43. int p = parent[i];
  44. while (i != p) {
  45. p = parent[p];
  46. parent[i] = p;
  47. i = p;
  48. p = parent[i];
  49. }
  50. return i;
  51. }
  52. /* }}} */
  53. static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
  54. {
  55. int r1 = union_find_root(parent, i);
  56. int r2 = union_find_root(parent, j);
  57. if (r1 != r2) {
  58. if (size[r1] < size[r2]) {
  59. parent[r1] = r2;
  60. size[r2] += size[r1];
  61. } else {
  62. parent[r2] = r1;
  63. size[r1] += size[r2];
  64. }
  65. }
  66. }
  67. /* }}} */
  68. static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
  69. {
  70. zend_ssa_var *ssa_vars = ssa->vars;
  71. int ssa_vars_count = ssa->vars_count;
  72. zend_ssa_phi *p;
  73. int i, j;
  74. int *size;
  75. ALLOCA_FLAG(use_heap)
  76. size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
  77. if (!size) {
  78. return FAILURE;
  79. }
  80. union_find_init(parent, size, ssa_vars_count);
  81. for (i = 0; i < ssa_vars_count; i++) {
  82. if (ssa_vars[i].definition_phi) {
  83. p = ssa_vars[i].definition_phi;
  84. if (p->pi >= 0) {
  85. union_find_unite(parent, size, i, p->sources[0]);
  86. } else {
  87. for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
  88. union_find_unite(parent, size, i, p->sources[j]);
  89. }
  90. }
  91. } else if (ssa_vars[i].definition >= 0) {
  92. int def = ssa_vars[i].definition;
  93. zend_ssa_op *op = ssa->ops + def;
  94. zend_op *opline = op_array->opcodes + def;
  95. if (op->op1_def >= 0) {
  96. if (op->op1_use >= 0) {
  97. if (opline->opcode != ZEND_ASSIGN) {
  98. union_find_unite(parent, size, op->op1_def, op->op1_use);
  99. }
  100. }
  101. if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
  102. union_find_unite(parent, size, op->op1_def, op->op2_use);
  103. }
  104. }
  105. if (op->op2_def >= 0) {
  106. if (op->op2_use >= 0) {
  107. union_find_unite(parent, size, op->op2_def, op->op2_use);
  108. }
  109. }
  110. if (op->result_def >= 0) {
  111. if (op->result_use >= 0) {
  112. if (opline->opcode != ZEND_QM_ASSIGN) {
  113. union_find_unite(parent, size, op->result_def, op->result_use);
  114. }
  115. }
  116. if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
  117. union_find_unite(parent, size, op->result_def, op->op1_use);
  118. }
  119. if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
  120. union_find_unite(parent, size, op->result_def, op->op2_use);
  121. }
  122. if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
  123. union_find_unite(parent, size, op->result_def, op->op1_def);
  124. }
  125. }
  126. }
  127. }
  128. for (i = 0; i < ssa_vars_count; i++) {
  129. parent[i] = union_find_root(parent, i);
  130. }
  131. free_alloca(size, use_heap);
  132. return SUCCESS;
  133. }
  134. /* }}} */
  135. static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */
  136. {
  137. zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
  138. if (ce) {
  139. return ce;
  140. }
  141. ce = zend_hash_find_ptr(CG(class_table), lcname);
  142. if (ce && ce->type == ZEND_INTERNAL_CLASS) {
  143. return ce;
  144. }
  145. return NULL;
  146. }
  147. /* }}} */
  148. static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
  149. {
  150. zend_ssa_op *op = ssa->ops + def;
  151. zend_op *opline = op_array->opcodes + def;
  152. if (op->result_def == var) {
  153. switch (opline->opcode) {
  154. case ZEND_INIT_ARRAY:
  155. return 1;
  156. case ZEND_NEW:
  157. /* objects with destructors should escape */
  158. if (opline->op1_type == IS_CONST) {
  159. zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)+1));
  160. uint32_t forbidden_flags = ZEND_ACC_INHERITED
  161. /* These flags will always cause an exception */
  162. | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
  163. | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
  164. if (ce && !ce->create_object && !ce->constructor &&
  165. !ce->destructor && !ce->__get && !ce->__set &&
  166. !(ce->ce_flags & forbidden_flags) &&
  167. (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
  168. return 1;
  169. }
  170. }
  171. break;
  172. case ZEND_QM_ASSIGN:
  173. if (opline->op1_type == IS_CONST
  174. && Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)) == IS_ARRAY) {
  175. return 1;
  176. }
  177. if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
  178. return 1;
  179. }
  180. break;
  181. case ZEND_ASSIGN:
  182. if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
  183. return 1;
  184. }
  185. break;
  186. }
  187. } else if (op->op1_def == var) {
  188. switch (opline->opcode) {
  189. case ZEND_ASSIGN:
  190. if (opline->op2_type == IS_CONST
  191. && Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)) == IS_ARRAY) {
  192. return 1;
  193. }
  194. if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
  195. return 1;
  196. }
  197. break;
  198. case ZEND_ASSIGN_DIM:
  199. case ZEND_ASSIGN_OBJ:
  200. if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
  201. /* implicit object/array allocation */
  202. return 1;
  203. }
  204. break;
  205. }
  206. }
  207. return 0;
  208. }
  209. /* }}} */
  210. static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
  211. {
  212. zend_ssa_op *op = ssa->ops + def;
  213. zend_op *opline = op_array->opcodes + def;
  214. if (op->result_def == var) {
  215. switch (opline->opcode) {
  216. case ZEND_INIT_ARRAY:
  217. case ZEND_ADD_ARRAY_ELEMENT:
  218. case ZEND_QM_ASSIGN:
  219. case ZEND_ASSIGN:
  220. return 1;
  221. case ZEND_NEW:
  222. /* objects with destructors should escape */
  223. if (opline->op1_type == IS_CONST) {
  224. zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)+1));
  225. if (ce && !ce->create_object && !ce->constructor &&
  226. !ce->destructor && !ce->__get && !ce->__set &&
  227. !(ce->ce_flags & ZEND_ACC_INHERITED)) {
  228. return 1;
  229. }
  230. }
  231. break;
  232. }
  233. } else if (op->op1_def == var) {
  234. switch (opline->opcode) {
  235. case ZEND_ASSIGN:
  236. return 1;
  237. case ZEND_ASSIGN_DIM:
  238. case ZEND_ASSIGN_OBJ:
  239. return 1;
  240. case ZEND_ASSIGN_ADD:
  241. case ZEND_ASSIGN_SUB:
  242. case ZEND_ASSIGN_MUL:
  243. case ZEND_ASSIGN_DIV:
  244. case ZEND_ASSIGN_MOD:
  245. case ZEND_ASSIGN_SL:
  246. case ZEND_ASSIGN_SR:
  247. case ZEND_ASSIGN_CONCAT:
  248. case ZEND_ASSIGN_BW_OR:
  249. case ZEND_ASSIGN_BW_AND:
  250. case ZEND_ASSIGN_BW_XOR:
  251. case ZEND_ASSIGN_POW:
  252. return (opline->extended_value != 0);
  253. case ZEND_PRE_INC_OBJ:
  254. case ZEND_PRE_DEC_OBJ:
  255. case ZEND_POST_INC_OBJ:
  256. case ZEND_POST_DEC_OBJ:
  257. return 1;
  258. }
  259. }
  260. return 0;
  261. }
  262. /* }}} */
  263. static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */
  264. {
  265. zend_ssa_op *op = ssa->ops + use;
  266. zend_op *opline = op_array->opcodes + use;
  267. if (op->op1_use == var) {
  268. switch (opline->opcode) {
  269. case ZEND_ASSIGN:
  270. /* no_val */
  271. break;
  272. case ZEND_QM_ASSIGN:
  273. if (opline->op1_type == IS_CV) {
  274. if (OP1_INFO() & MAY_BE_OBJECT) {
  275. /* object aliasing */
  276. return 1;
  277. }
  278. }
  279. break;
  280. case ZEND_ISSET_ISEMPTY_DIM_OBJ:
  281. case ZEND_ISSET_ISEMPTY_PROP_OBJ:
  282. case ZEND_FETCH_DIM_R:
  283. case ZEND_FETCH_OBJ_R:
  284. case ZEND_FETCH_DIM_IS:
  285. case ZEND_FETCH_OBJ_IS:
  286. break;
  287. case ZEND_ASSIGN_ADD:
  288. case ZEND_ASSIGN_SUB:
  289. case ZEND_ASSIGN_MUL:
  290. case ZEND_ASSIGN_DIV:
  291. case ZEND_ASSIGN_MOD:
  292. case ZEND_ASSIGN_SL:
  293. case ZEND_ASSIGN_SR:
  294. case ZEND_ASSIGN_CONCAT:
  295. case ZEND_ASSIGN_BW_OR:
  296. case ZEND_ASSIGN_BW_AND:
  297. case ZEND_ASSIGN_BW_XOR:
  298. case ZEND_ASSIGN_POW:
  299. if (!opline->extended_value) {
  300. return 1;
  301. }
  302. /* break missing intentionally */
  303. case ZEND_ASSIGN_DIM:
  304. case ZEND_ASSIGN_OBJ:
  305. break;
  306. case ZEND_PRE_INC_OBJ:
  307. case ZEND_PRE_DEC_OBJ:
  308. case ZEND_POST_INC_OBJ:
  309. case ZEND_POST_DEC_OBJ:
  310. break;
  311. case ZEND_INIT_ARRAY:
  312. case ZEND_ADD_ARRAY_ELEMENT:
  313. if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
  314. return 1;
  315. }
  316. if (OP1_INFO() & MAY_BE_OBJECT) {
  317. /* object aliasing */
  318. return 1;
  319. }
  320. /* reference dependencies processed separately */
  321. break;
  322. case ZEND_OP_DATA:
  323. if ((opline-1)->opcode != ZEND_ASSIGN_DIM
  324. && (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
  325. return 1;
  326. }
  327. if (OP1_INFO() & MAY_BE_OBJECT) {
  328. /* object aliasing */
  329. return 1;
  330. }
  331. opline--;
  332. op--;
  333. if (opline->op1_type != IS_CV
  334. || (OP1_INFO() & MAY_BE_REF)
  335. || (op->op1_def >= 0 && ssa->vars[op->op1_def].alias)) {
  336. /* asignment into escaping structure */
  337. return 1;
  338. }
  339. /* reference dependencies processed separately */
  340. break;
  341. default:
  342. return 1;
  343. }
  344. }
  345. if (op->op2_use == var) {
  346. switch (opline->opcode) {
  347. case ZEND_ASSIGN:
  348. if (opline->op1_type != IS_CV
  349. || (OP1_INFO() & MAY_BE_REF)
  350. || (op->op1_def >= 0 && ssa->vars[op->op1_def].alias)) {
  351. /* asignment into escaping variable */
  352. return 1;
  353. }
  354. if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
  355. if (OP2_INFO() & MAY_BE_OBJECT) {
  356. /* object aliasing */
  357. return 1;
  358. }
  359. }
  360. break;
  361. default:
  362. return 1;
  363. }
  364. }
  365. if (op->result_use == var) {
  366. switch (opline->opcode) {
  367. case ZEND_ASSIGN:
  368. case ZEND_QM_ASSIGN:
  369. case ZEND_INIT_ARRAY:
  370. case ZEND_ADD_ARRAY_ELEMENT:
  371. break;
  372. default:
  373. return 1;
  374. }
  375. }
  376. return 0;
  377. }
  378. /* }}} */
  379. int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
  380. {
  381. zend_ssa_var *ssa_vars = ssa->vars;
  382. int ssa_vars_count = ssa->vars_count;
  383. int i, root, use;
  384. int *ees;
  385. zend_bool has_allocations;
  386. int num_non_escaped;
  387. ALLOCA_FLAG(use_heap)
  388. if (!ssa_vars) {
  389. return SUCCESS;
  390. }
  391. has_allocations = 0;
  392. for (i = op_array->last_var; i < ssa_vars_count; i++) {
  393. if (ssa_vars[i].definition >= 0
  394. && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
  395. && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
  396. has_allocations = 1;
  397. break;
  398. }
  399. }
  400. if (!has_allocations) {
  401. return SUCCESS;
  402. }
  403. /* 1. Build EES (Equi-Esape Sets) */
  404. ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
  405. if (!ees) {
  406. return FAILURE;
  407. }
  408. if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) {
  409. return FAILURE;
  410. }
  411. /* 2. Identify Allocations */
  412. num_non_escaped = 0;
  413. for (i = op_array->last_var; i < ssa_vars_count; i++) {
  414. root = ees[i];
  415. if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
  416. /* already escape. skip */
  417. } else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
  418. if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
  419. num_non_escaped--;
  420. }
  421. ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
  422. } else if (ssa_vars[i].definition >= 0
  423. && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
  424. if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
  425. if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
  426. num_non_escaped--;
  427. }
  428. ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
  429. } else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
  430. && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
  431. ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
  432. num_non_escaped++;
  433. }
  434. }
  435. }
  436. /* 3. Mark escaped EES */
  437. if (num_non_escaped) {
  438. for (i = 0; i < ssa_vars_count; i++) {
  439. if (ssa_vars[i].use_chain >= 0) {
  440. root = ees[i];
  441. if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
  442. FOREACH_USE(ssa_vars + i, use) {
  443. if (is_escape_use(op_array, ssa, use, i)) {
  444. ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
  445. num_non_escaped--;
  446. if (num_non_escaped == 0) {
  447. i = ssa_vars_count;
  448. }
  449. break;
  450. }
  451. } FOREACH_USE_END();
  452. }
  453. }
  454. }
  455. }
  456. /* 4. Process referential dependencies */
  457. if (num_non_escaped) {
  458. zend_bool changed;
  459. do {
  460. changed = 0;
  461. for (i = 0; i < ssa_vars_count; i++) {
  462. if (ssa_vars[i].use_chain >= 0) {
  463. root = ees[i];
  464. if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
  465. FOREACH_USE(ssa_vars + i, use) {
  466. zend_ssa_op *op = ssa->ops + use;
  467. zend_op *opline = op_array->opcodes + use;
  468. int enclosing_root;
  469. if (opline->opcode == ZEND_OP_DATA &&
  470. ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
  471. (opline-1)->opcode == ZEND_ASSIGN_OBJ) &&
  472. op->op1_use == i &&
  473. (op-1)->op1_use >= 0) {
  474. enclosing_root = ees[(op-1)->op1_use];
  475. } else if ((opline->opcode == ZEND_INIT_ARRAY ||
  476. opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
  477. op->op1_use == i &&
  478. op->result_def >= 0) {
  479. enclosing_root = ees[op->result_def];
  480. } else {
  481. continue;
  482. }
  483. if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
  484. ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
  485. if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
  486. ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
  487. } else {
  488. ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
  489. }
  490. if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
  491. num_non_escaped--;
  492. if (num_non_escaped == 0) {
  493. i = ssa_vars_count;
  494. changed = 0;
  495. } else {
  496. changed = 1;
  497. }
  498. break;
  499. } else {
  500. changed = 1;
  501. }
  502. }
  503. } FOREACH_USE_END();
  504. }
  505. }
  506. }
  507. } while (changed);
  508. }
  509. /* 5. Propagate values of escape sets to variables */
  510. for (i = 0; i < ssa_vars_count; i++) {
  511. root = ees[i];
  512. if (i != root) {
  513. ssa_vars[i].escape_state = ssa_vars[root].escape_state;
  514. }
  515. }
  516. free_alloca(ees, use_heap);
  517. return SUCCESS;
  518. }
  519. /* }}} */
  520. /*
  521. * Local variables:
  522. * tab-width: 4
  523. * c-basic-offset: 4
  524. * indent-tabs-mode: t
  525. * End:
  526. */