compact_literals.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  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. | Xinchen Hui <laruence@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* pass 11
  20. * - compact literals table
  21. */
  22. #include "php.h"
  23. #include "Optimizer/zend_optimizer.h"
  24. #include "Optimizer/zend_optimizer_internal.h"
  25. #include "zend_API.h"
  26. #include "zend_constants.h"
  27. #include "zend_execute.h"
  28. #include "zend_vm.h"
  29. #define DEBUG_COMPACT_LITERALS 0
  30. #define LITERAL_VALUE 0x0100
  31. #define LITERAL_FUNC 0x0200
  32. #define LITERAL_CLASS 0x0300
  33. #define LITERAL_CONST 0x0400
  34. #define LITERAL_CLASS_CONST 0x0500
  35. #define LITERAL_STATIC_METHOD 0x0600
  36. #define LITERAL_STATIC_PROPERTY 0x0700
  37. #define LITERAL_METHOD 0x0800
  38. #define LITERAL_PROPERTY 0x0900
  39. #define LITERAL_GLOBAL 0x0A00
  40. #define LITERAL_KIND_MASK 0x0f00
  41. #define LITERAL_NUM_RELATED_MASK 0x000f
  42. #define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
  43. typedef struct _literal_info {
  44. uint32_t flags; /* bitmask (see defines above) */
  45. } literal_info;
  46. #define LITERAL_INFO(n, kind, related) do { \
  47. info[n].flags = ((kind) | (related)); \
  48. } while (0)
  49. static zend_bool class_name_type_hint(const zend_op_array *op_array, uint32_t arg_num)
  50. {
  51. zend_arg_info *arg_info;
  52. if (arg_num > 0) {
  53. if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
  54. if (EXPECTED(arg_num <= op_array->num_args)) {
  55. arg_info = &op_array->arg_info[arg_num-1];
  56. } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
  57. arg_info = &op_array->arg_info[op_array->num_args];
  58. } else {
  59. return 0;
  60. }
  61. return ZEND_TYPE_IS_CLASS(arg_info->type);
  62. }
  63. } else {
  64. arg_info = op_array->arg_info - 1;
  65. return ZEND_TYPE_IS_CLASS(arg_info->type);
  66. }
  67. return 0;
  68. }
  69. static uint32_t add_static_slot(HashTable *hash,
  70. zend_op_array *op_array,
  71. uint32_t op1,
  72. uint32_t op2,
  73. uint32_t kind,
  74. int *cache_size)
  75. {
  76. uint32_t ret;
  77. zend_string *key;
  78. size_t key_len;
  79. zval *class_name = &op_array->literals[op1];
  80. zval *prop_name = &op_array->literals[op2];
  81. zval *pos, tmp;
  82. key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN_P(prop_name);
  83. key = zend_string_alloc(key_len, 0);
  84. memcpy(ZSTR_VAL(key), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
  85. memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
  86. memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name) + sizeof("::") - 1,
  87. Z_STRVAL_P(prop_name),
  88. Z_STRLEN_P(prop_name) + 1);
  89. ZSTR_H(key) = zend_string_hash_func(key);
  90. ZSTR_H(key) += kind;
  91. pos = zend_hash_find(hash, key);
  92. if (pos) {
  93. ret = Z_LVAL_P(pos);
  94. } else {
  95. ret = *cache_size;
  96. *cache_size += 2 * sizeof(void *);
  97. ZVAL_LONG(&tmp, ret);
  98. zend_hash_add(hash, key, &tmp);
  99. }
  100. zend_string_release_ex(key, 0);
  101. return ret;
  102. }
  103. void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
  104. {
  105. zend_op *opline, *end;
  106. int i, j, n, *map, cache_size;
  107. zval zv, *pos;
  108. literal_info *info;
  109. int l_null = -1;
  110. int l_false = -1;
  111. int l_true = -1;
  112. int l_empty_arr = -1;
  113. HashTable hash, double_hash;
  114. zend_string *key = NULL;
  115. void *checkpoint = zend_arena_checkpoint(ctx->arena);
  116. int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
  117. if (op_array->last_literal) {
  118. info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
  119. /* Mark literals of specific types */
  120. opline = op_array->opcodes;
  121. end = opline + op_array->last;
  122. while (opline < end) {
  123. switch (opline->opcode) {
  124. case ZEND_INIT_FCALL:
  125. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
  126. break;
  127. case ZEND_INIT_FCALL_BY_NAME:
  128. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
  129. break;
  130. case ZEND_INIT_NS_FCALL_BY_NAME:
  131. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
  132. break;
  133. case ZEND_INIT_METHOD_CALL:
  134. if (opline->op1_type == IS_CONST) {
  135. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  136. }
  137. if (opline->op2_type == IS_CONST) {
  138. LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
  139. }
  140. break;
  141. case ZEND_INIT_STATIC_METHOD_CALL:
  142. if (opline->op1_type == IS_CONST) {
  143. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  144. }
  145. if (opline->op2_type == IS_CONST) {
  146. LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
  147. }
  148. break;
  149. case ZEND_CATCH:
  150. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  151. break;
  152. case ZEND_DEFINED:
  153. LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 2);
  154. break;
  155. case ZEND_FETCH_CONSTANT:
  156. if ((opline->op1.num & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
  157. LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 5);
  158. } else {
  159. LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
  160. }
  161. break;
  162. case ZEND_FETCH_CLASS_CONSTANT:
  163. if (opline->op1_type == IS_CONST) {
  164. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  165. }
  166. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
  167. break;
  168. case ZEND_FETCH_STATIC_PROP_R:
  169. case ZEND_FETCH_STATIC_PROP_W:
  170. case ZEND_FETCH_STATIC_PROP_RW:
  171. case ZEND_FETCH_STATIC_PROP_IS:
  172. case ZEND_FETCH_STATIC_PROP_UNSET:
  173. case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
  174. case ZEND_UNSET_STATIC_PROP:
  175. case ZEND_ISSET_ISEMPTY_STATIC_PROP:
  176. if (opline->op2_type == IS_CONST) {
  177. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
  178. }
  179. if (opline->op1_type == IS_CONST) {
  180. LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
  181. }
  182. break;
  183. case ZEND_FETCH_CLASS:
  184. case ZEND_ADD_INTERFACE:
  185. case ZEND_ADD_TRAIT:
  186. case ZEND_INSTANCEOF:
  187. if (opline->op2_type == IS_CONST) {
  188. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
  189. }
  190. break;
  191. case ZEND_NEW:
  192. if (opline->op1_type == IS_CONST) {
  193. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  194. }
  195. break;
  196. case ZEND_ASSIGN_OBJ:
  197. case ZEND_FETCH_OBJ_R:
  198. case ZEND_FETCH_OBJ_W:
  199. case ZEND_FETCH_OBJ_RW:
  200. case ZEND_FETCH_OBJ_IS:
  201. case ZEND_FETCH_OBJ_UNSET:
  202. case ZEND_FETCH_OBJ_FUNC_ARG:
  203. case ZEND_UNSET_OBJ:
  204. case ZEND_PRE_INC_OBJ:
  205. case ZEND_PRE_DEC_OBJ:
  206. case ZEND_POST_INC_OBJ:
  207. case ZEND_POST_DEC_OBJ:
  208. case ZEND_ISSET_ISEMPTY_PROP_OBJ:
  209. if (opline->op1_type == IS_CONST) {
  210. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  211. }
  212. if (opline->op2_type == IS_CONST) {
  213. LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
  214. }
  215. break;
  216. case ZEND_ASSIGN_ADD:
  217. case ZEND_ASSIGN_SUB:
  218. case ZEND_ASSIGN_MUL:
  219. case ZEND_ASSIGN_DIV:
  220. case ZEND_ASSIGN_POW:
  221. case ZEND_ASSIGN_MOD:
  222. case ZEND_ASSIGN_SL:
  223. case ZEND_ASSIGN_SR:
  224. case ZEND_ASSIGN_CONCAT:
  225. case ZEND_ASSIGN_BW_OR:
  226. case ZEND_ASSIGN_BW_AND:
  227. case ZEND_ASSIGN_BW_XOR:
  228. if (opline->op2_type == IS_CONST) {
  229. if (opline->extended_value == ZEND_ASSIGN_OBJ) {
  230. LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
  231. } else if (opline->extended_value == ZEND_ASSIGN_DIM) {
  232. if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
  233. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
  234. } else {
  235. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  236. }
  237. } else {
  238. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  239. }
  240. }
  241. break;
  242. case ZEND_BIND_GLOBAL:
  243. LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
  244. break;
  245. case ZEND_RECV_INIT:
  246. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  247. break;
  248. case ZEND_DECLARE_FUNCTION:
  249. case ZEND_DECLARE_CLASS:
  250. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
  251. break;
  252. case ZEND_DECLARE_INHERITED_CLASS:
  253. case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
  254. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
  255. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
  256. break;
  257. case ZEND_DECLARE_ANON_INHERITED_CLASS:
  258. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  259. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
  260. break;
  261. case ZEND_ISSET_ISEMPTY_DIM_OBJ:
  262. case ZEND_ASSIGN_DIM:
  263. case ZEND_UNSET_DIM:
  264. case ZEND_FETCH_DIM_R:
  265. case ZEND_FETCH_DIM_W:
  266. case ZEND_FETCH_DIM_RW:
  267. case ZEND_FETCH_DIM_IS:
  268. case ZEND_FETCH_DIM_FUNC_ARG:
  269. case ZEND_FETCH_DIM_UNSET:
  270. case ZEND_FETCH_LIST_R:
  271. case ZEND_FETCH_LIST_W:
  272. if (opline->op1_type == IS_CONST) {
  273. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  274. }
  275. if (opline->op2_type == IS_CONST) {
  276. if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
  277. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
  278. } else {
  279. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  280. }
  281. }
  282. break;
  283. default:
  284. if (opline->op1_type == IS_CONST) {
  285. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  286. }
  287. if (opline->op2_type == IS_CONST) {
  288. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  289. }
  290. break;
  291. }
  292. opline++;
  293. }
  294. #if DEBUG_COMPACT_LITERALS
  295. {
  296. int i, use_copy;
  297. fprintf(stderr, "File %s func %s\n", op_array->filename->val,
  298. op_array->function_name ? op_array->function_name->val : "main");
  299. fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
  300. for (i = 0; i < op_array->last_literal; i++) {
  301. zval zv;
  302. ZVAL_COPY_VALUE(&zv, op_array->literals + i);
  303. use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
  304. fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
  305. if (use_copy) {
  306. zval_ptr_dtor_nogc(&zv);
  307. }
  308. }
  309. fflush(stderr);
  310. }
  311. #endif
  312. /* Merge equal constants */
  313. j = 0;
  314. zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
  315. /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
  316. zend_hash_init(&double_hash, 0, NULL, NULL, 0);
  317. map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
  318. memset(map, 0, op_array->last_literal * sizeof(int));
  319. for (i = 0; i < op_array->last_literal; i++) {
  320. if (!info[i].flags) {
  321. /* unset literal */
  322. zval_ptr_dtor_nogc(&op_array->literals[i]);
  323. continue;
  324. }
  325. switch (Z_TYPE(op_array->literals[i])) {
  326. case IS_NULL:
  327. if (l_null < 0) {
  328. l_null = j;
  329. if (i != j) {
  330. op_array->literals[j] = op_array->literals[i];
  331. info[j] = info[i];
  332. }
  333. j++;
  334. }
  335. map[i] = l_null;
  336. break;
  337. case IS_FALSE:
  338. if (l_false < 0) {
  339. l_false = j;
  340. if (i != j) {
  341. op_array->literals[j] = op_array->literals[i];
  342. info[j] = info[i];
  343. }
  344. j++;
  345. }
  346. map[i] = l_false;
  347. break;
  348. case IS_TRUE:
  349. if (l_true < 0) {
  350. l_true = j;
  351. if (i != j) {
  352. op_array->literals[j] = op_array->literals[i];
  353. info[j] = info[i];
  354. }
  355. j++;
  356. }
  357. map[i] = l_true;
  358. break;
  359. case IS_LONG:
  360. if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
  361. if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
  362. map[i] = Z_LVAL_P(pos);
  363. } else {
  364. map[i] = j;
  365. ZVAL_LONG(&zv, j);
  366. zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
  367. if (i != j) {
  368. op_array->literals[j] = op_array->literals[i];
  369. info[j] = info[i];
  370. }
  371. j++;
  372. }
  373. } else {
  374. ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
  375. key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
  376. ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
  377. LITERAL_NUM_RELATED(info[i].flags) - 1;
  378. if ((pos = zend_hash_find(&hash, key)) != NULL
  379. && LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
  380. map[i] = Z_LVAL_P(pos);
  381. zval_ptr_dtor_nogc(&op_array->literals[i+1]);
  382. } else {
  383. map[i] = j;
  384. ZVAL_LONG(&zv, j);
  385. zend_hash_add_new(&hash, key, &zv);
  386. if (i != j) {
  387. op_array->literals[j] = op_array->literals[i];
  388. info[j] = info[i];
  389. op_array->literals[j+1] = op_array->literals[i+1];
  390. info[j+1] = info[i+1];
  391. }
  392. j += 2;
  393. }
  394. zend_string_release_ex(key, 0);
  395. i++;
  396. }
  397. break;
  398. case IS_DOUBLE:
  399. if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
  400. map[i] = Z_LVAL_P(pos);
  401. } else {
  402. map[i] = j;
  403. ZVAL_LONG(&zv, j);
  404. zend_hash_str_add(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
  405. if (i != j) {
  406. op_array->literals[j] = op_array->literals[i];
  407. info[j] = info[i];
  408. }
  409. j++;
  410. }
  411. break;
  412. case IS_STRING:
  413. if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
  414. key = zend_string_copy(Z_STR(op_array->literals[i]));
  415. } else {
  416. key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
  417. ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) +
  418. LITERAL_NUM_RELATED(info[i].flags) - 1;
  419. }
  420. pos = zend_hash_find(&hash, key);
  421. if (pos != NULL &&
  422. Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
  423. LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
  424. (LITERAL_NUM_RELATED(info[i].flags) != 2 ||
  425. ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
  426. (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
  427. zend_string_release_ex(key, 0);
  428. map[i] = Z_LVAL_P(pos);
  429. zval_ptr_dtor_nogc(&op_array->literals[i]);
  430. n = LITERAL_NUM_RELATED(info[i].flags);
  431. while (n > 1) {
  432. i++;
  433. zval_ptr_dtor_nogc(&op_array->literals[i]);
  434. n--;
  435. }
  436. } else {
  437. map[i] = j;
  438. ZVAL_LONG(&zv, j);
  439. zend_hash_add_new(&hash, key, &zv);
  440. zend_string_release_ex(key, 0);
  441. if (i != j) {
  442. op_array->literals[j] = op_array->literals[i];
  443. info[j] = info[i];
  444. }
  445. j++;
  446. n = LITERAL_NUM_RELATED(info[i].flags);
  447. while (n > 1) {
  448. i++;
  449. if (i != j) op_array->literals[j] = op_array->literals[i];
  450. j++;
  451. n--;
  452. }
  453. }
  454. break;
  455. case IS_ARRAY:
  456. if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
  457. if (l_empty_arr < 0) {
  458. l_empty_arr = j;
  459. if (i != j) {
  460. op_array->literals[j] = op_array->literals[i];
  461. info[j] = info[i];
  462. }
  463. j++;
  464. } else {
  465. zval_ptr_dtor_nogc(&op_array->literals[i]);
  466. }
  467. map[i] = l_empty_arr;
  468. break;
  469. }
  470. /* break missing intentionally */
  471. default:
  472. /* don't merge other types */
  473. map[i] = j;
  474. if (i != j) {
  475. op_array->literals[j] = op_array->literals[i];
  476. info[j] = info[i];
  477. }
  478. j++;
  479. break;
  480. }
  481. }
  482. /* Only clean "hash", as it will be reused in the loop below. */
  483. zend_hash_clean(&hash);
  484. zend_hash_destroy(&double_hash);
  485. op_array->last_literal = j;
  486. const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
  487. memset(const_slot, -1, j * 6 * sizeof(int));
  488. class_slot = const_slot + j;
  489. func_slot = class_slot + j;
  490. bind_var_slot = func_slot + j;
  491. property_slot = bind_var_slot + j;
  492. method_slot = property_slot + j;
  493. /* Update opcodes to use new literals table */
  494. cache_size = 0;
  495. opline = op_array->opcodes;
  496. end = opline + op_array->last;
  497. while (opline < end) {
  498. if (opline->op1_type == IS_CONST) {
  499. opline->op1.constant = map[opline->op1.constant];
  500. }
  501. if (opline->op2_type == IS_CONST) {
  502. opline->op2.constant = map[opline->op2.constant];
  503. }
  504. switch (opline->opcode) {
  505. case ZEND_RECV_INIT:
  506. if (class_name_type_hint(op_array, opline->op1.num)) {
  507. opline->extended_value = cache_size;
  508. cache_size += sizeof(void *);
  509. }
  510. break;
  511. case ZEND_RECV:
  512. case ZEND_RECV_VARIADIC:
  513. if (class_name_type_hint(op_array, opline->op1.num)) {
  514. opline->op2.num = cache_size;
  515. cache_size += sizeof(void *);
  516. }
  517. break;
  518. case ZEND_VERIFY_RETURN_TYPE:
  519. if (class_name_type_hint(op_array, 0)) {
  520. opline->op2.num = cache_size;
  521. cache_size += sizeof(void *);
  522. }
  523. break;
  524. case ZEND_ASSIGN_ADD:
  525. case ZEND_ASSIGN_SUB:
  526. case ZEND_ASSIGN_MUL:
  527. case ZEND_ASSIGN_DIV:
  528. case ZEND_ASSIGN_POW:
  529. case ZEND_ASSIGN_MOD:
  530. case ZEND_ASSIGN_SL:
  531. case ZEND_ASSIGN_SR:
  532. case ZEND_ASSIGN_CONCAT:
  533. case ZEND_ASSIGN_BW_OR:
  534. case ZEND_ASSIGN_BW_AND:
  535. case ZEND_ASSIGN_BW_XOR:
  536. if (opline->extended_value != ZEND_ASSIGN_OBJ) {
  537. break;
  538. }
  539. if (opline->op2_type == IS_CONST) {
  540. // op2 property
  541. if (opline->op1_type == IS_UNUSED &&
  542. property_slot[opline->op2.constant] >= 0) {
  543. (opline+1)->extended_value = property_slot[opline->op2.constant];
  544. } else {
  545. (opline+1)->extended_value = cache_size;
  546. cache_size += 2 * sizeof(void *);
  547. if (opline->op1_type == IS_UNUSED) {
  548. property_slot[opline->op2.constant] = (opline+1)->extended_value;
  549. }
  550. }
  551. }
  552. break;
  553. case ZEND_ASSIGN_OBJ:
  554. case ZEND_FETCH_OBJ_R:
  555. case ZEND_FETCH_OBJ_W:
  556. case ZEND_FETCH_OBJ_RW:
  557. case ZEND_FETCH_OBJ_IS:
  558. case ZEND_FETCH_OBJ_UNSET:
  559. case ZEND_FETCH_OBJ_FUNC_ARG:
  560. case ZEND_UNSET_OBJ:
  561. case ZEND_PRE_INC_OBJ:
  562. case ZEND_PRE_DEC_OBJ:
  563. case ZEND_POST_INC_OBJ:
  564. case ZEND_POST_DEC_OBJ:
  565. if (opline->op2_type == IS_CONST) {
  566. // op2 property
  567. if (opline->op1_type == IS_UNUSED &&
  568. property_slot[opline->op2.constant] >= 0) {
  569. opline->extended_value = property_slot[opline->op2.constant];
  570. } else {
  571. opline->extended_value = cache_size;
  572. cache_size += 2 * sizeof(void *);
  573. if (opline->op1_type == IS_UNUSED) {
  574. property_slot[opline->op2.constant] = opline->extended_value;
  575. }
  576. }
  577. }
  578. break;
  579. case ZEND_ISSET_ISEMPTY_PROP_OBJ:
  580. if (opline->op2_type == IS_CONST) {
  581. // op2 property
  582. if (opline->op1_type == IS_UNUSED &&
  583. property_slot[opline->op2.constant] >= 0) {
  584. opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
  585. } else {
  586. opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
  587. cache_size += 2 * sizeof(void *);
  588. if (opline->op1_type == IS_UNUSED) {
  589. property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
  590. }
  591. }
  592. }
  593. break;
  594. case ZEND_INIT_FCALL:
  595. case ZEND_INIT_FCALL_BY_NAME:
  596. case ZEND_INIT_NS_FCALL_BY_NAME:
  597. // op2 func
  598. if (func_slot[opline->op2.constant] >= 0) {
  599. opline->result.num = func_slot[opline->op2.constant];
  600. } else {
  601. opline->result.num = cache_size;
  602. cache_size += sizeof(void *);
  603. func_slot[opline->op2.constant] = opline->result.num;
  604. }
  605. break;
  606. case ZEND_INIT_METHOD_CALL:
  607. if (opline->op2_type == IS_CONST) {
  608. // op2 method
  609. if (opline->op1_type == IS_UNUSED &&
  610. method_slot[opline->op2.constant] >= 0) {
  611. opline->result.num = method_slot[opline->op2.constant];
  612. } else {
  613. opline->result.num = cache_size;
  614. cache_size += 2 * sizeof(void *);
  615. if (opline->op1_type == IS_UNUSED) {
  616. method_slot[opline->op2.constant] = opline->result.num;
  617. }
  618. }
  619. }
  620. break;
  621. case ZEND_INIT_STATIC_METHOD_CALL:
  622. if (opline->op2_type == IS_CONST) {
  623. // op2 static method
  624. if (opline->op1_type == IS_CONST) {
  625. opline->result.num = add_static_slot(&hash, op_array,
  626. opline->op1.constant,
  627. opline->op2.constant,
  628. LITERAL_STATIC_METHOD,
  629. &cache_size);
  630. } else {
  631. opline->result.num = cache_size;
  632. cache_size += 2 * sizeof(void *);
  633. }
  634. } else if (opline->op1_type == IS_CONST) {
  635. // op1 class
  636. if (class_slot[opline->op1.constant] >= 0) {
  637. opline->result.num = class_slot[opline->op1.constant];
  638. } else {
  639. opline->result.num = cache_size;
  640. cache_size += sizeof(void *);
  641. class_slot[opline->op1.constant] = opline->result.num;
  642. }
  643. }
  644. break;
  645. case ZEND_DEFINED:
  646. // op1 const
  647. if (const_slot[opline->op1.constant] >= 0) {
  648. opline->extended_value = const_slot[opline->op1.constant];
  649. } else {
  650. opline->extended_value = cache_size;
  651. cache_size += sizeof(void *);
  652. const_slot[opline->op1.constant] = opline->extended_value;
  653. }
  654. break;
  655. case ZEND_FETCH_CONSTANT:
  656. // op2 const
  657. if (const_slot[opline->op2.constant] >= 0) {
  658. opline->extended_value = const_slot[opline->op2.constant];
  659. } else {
  660. opline->extended_value = cache_size;
  661. cache_size += sizeof(void *);
  662. const_slot[opline->op2.constant] = opline->extended_value;
  663. }
  664. break;
  665. case ZEND_FETCH_CLASS_CONSTANT:
  666. if (opline->op1_type == IS_CONST) {
  667. // op1/op2 class_const
  668. opline->extended_value = add_static_slot(&hash, op_array,
  669. opline->op1.constant,
  670. opline->op2.constant,
  671. LITERAL_CLASS_CONST,
  672. &cache_size);
  673. } else {
  674. opline->extended_value = cache_size;
  675. cache_size += 2 * sizeof(void *);
  676. }
  677. break;
  678. case ZEND_FETCH_STATIC_PROP_R:
  679. case ZEND_FETCH_STATIC_PROP_W:
  680. case ZEND_FETCH_STATIC_PROP_RW:
  681. case ZEND_FETCH_STATIC_PROP_IS:
  682. case ZEND_FETCH_STATIC_PROP_UNSET:
  683. case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
  684. case ZEND_UNSET_STATIC_PROP:
  685. if (opline->op1_type == IS_CONST) {
  686. // op1 static property
  687. if (opline->op2_type == IS_CONST) {
  688. opline->extended_value = add_static_slot(&hash, op_array,
  689. opline->op2.constant,
  690. opline->op1.constant,
  691. LITERAL_STATIC_PROPERTY,
  692. &cache_size);
  693. } else {
  694. opline->extended_value = cache_size;
  695. cache_size += 2 * sizeof(void *);
  696. }
  697. } else if (opline->op2_type == IS_CONST) {
  698. // op2 class
  699. if (class_slot[opline->op2.constant] >= 0) {
  700. opline->extended_value = class_slot[opline->op2.constant];
  701. } else {
  702. opline->extended_value = cache_size;
  703. cache_size += sizeof(void *);
  704. class_slot[opline->op2.constant] = opline->extended_value;
  705. }
  706. }
  707. break;
  708. case ZEND_ISSET_ISEMPTY_STATIC_PROP:
  709. if (opline->op1_type == IS_CONST) {
  710. // op1 static property
  711. if (opline->op2_type == IS_CONST) {
  712. opline->extended_value = add_static_slot(&hash, op_array,
  713. opline->op2.constant,
  714. opline->op1.constant,
  715. LITERAL_STATIC_PROPERTY,
  716. &cache_size) | (opline->extended_value & ZEND_ISEMPTY);
  717. } else {
  718. opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
  719. cache_size += 2 * sizeof(void *);
  720. }
  721. } else if (opline->op2_type == IS_CONST) {
  722. // op2 class
  723. if (class_slot[opline->op2.constant] >= 0) {
  724. opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
  725. } else {
  726. opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
  727. cache_size += sizeof(void *);
  728. class_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
  729. }
  730. }
  731. break;
  732. case ZEND_FETCH_CLASS:
  733. case ZEND_INSTANCEOF:
  734. if (opline->op2_type == IS_CONST) {
  735. // op2 class
  736. if (class_slot[opline->op2.constant] >= 0) {
  737. opline->extended_value = class_slot[opline->op2.constant];
  738. } else {
  739. opline->extended_value = cache_size;
  740. cache_size += sizeof(void *);
  741. class_slot[opline->op2.constant] = opline->extended_value;
  742. }
  743. }
  744. break;
  745. case ZEND_NEW:
  746. if (opline->op1_type == IS_CONST) {
  747. // op1 class
  748. if (class_slot[opline->op1.constant] >= 0) {
  749. opline->op2.num = class_slot[opline->op1.constant];
  750. } else {
  751. opline->op2.num = cache_size;
  752. cache_size += sizeof(void *);
  753. class_slot[opline->op1.constant] = opline->op2.num;
  754. }
  755. }
  756. break;
  757. case ZEND_CATCH:
  758. if (opline->op1_type == IS_CONST) {
  759. // op1 class
  760. if (class_slot[opline->op1.constant] >= 0) {
  761. opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
  762. } else {
  763. opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
  764. cache_size += sizeof(void *);
  765. class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
  766. }
  767. }
  768. break;
  769. case ZEND_BIND_GLOBAL:
  770. // op2 bind var
  771. if (bind_var_slot[opline->op2.constant] >= 0) {
  772. opline->extended_value = bind_var_slot[opline->op2.constant];
  773. } else {
  774. opline->extended_value = cache_size;
  775. cache_size += sizeof(void *);
  776. bind_var_slot[opline->op2.constant] = opline->extended_value;
  777. }
  778. break;
  779. }
  780. opline++;
  781. }
  782. op_array->cache_size = cache_size;
  783. zend_hash_destroy(&hash);
  784. zend_arena_release(&ctx->arena, checkpoint);
  785. if (1) {
  786. opline = op_array->opcodes;
  787. while (1) {
  788. if (opline->opcode == ZEND_RECV_INIT) {
  789. zval *val = &op_array->literals[opline->op2.constant];
  790. if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
  791. /* Ensure zval is aligned to 8 bytes */
  792. op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
  793. Z_CACHE_SLOT_P(val) = op_array->cache_size;
  794. op_array->cache_size += sizeof(zval);
  795. }
  796. } else if (opline->opcode != ZEND_RECV && opline->opcode != ZEND_EXT_NOP) {
  797. break;
  798. }
  799. opline++;
  800. }
  801. }
  802. #if DEBUG_COMPACT_LITERALS
  803. {
  804. int i, use_copy;
  805. fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
  806. for (i = 0; i < op_array->last_literal; i++) {
  807. zval zv;
  808. ZVAL_COPY_VALUE(&zv, op_array->literals + i);
  809. use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
  810. fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
  811. if (use_copy) {
  812. zval_ptr_dtor_nogc(&zv);
  813. }
  814. }
  815. fflush(stderr);
  816. }
  817. #endif
  818. }
  819. }