compact_literals.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  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. | Xinchen Hui <laruence@php.net> |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* pass 11
  20. * - compact literals table
  21. */
  22. #include "Optimizer/zend_optimizer.h"
  23. #include "Optimizer/zend_optimizer_internal.h"
  24. #include "zend_API.h"
  25. #include "zend_constants.h"
  26. #include "zend_execute.h"
  27. #include "zend_vm.h"
  28. #include "zend_extensions.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 size_t type_num_classes(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. return 0;
  55. }
  56. if (EXPECTED(arg_num <= op_array->num_args)) {
  57. arg_info = &op_array->arg_info[arg_num-1];
  58. } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
  59. arg_info = &op_array->arg_info[op_array->num_args];
  60. } else {
  61. return 0;
  62. }
  63. } else {
  64. arg_info = op_array->arg_info - 1;
  65. }
  66. if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
  67. if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
  68. return ZEND_TYPE_LIST(arg_info->type)->num_types;
  69. }
  70. return 1;
  71. }
  72. return 0;
  73. }
  74. static uint32_t add_static_slot(HashTable *hash,
  75. zend_op_array *op_array,
  76. uint32_t op1,
  77. uint32_t op2,
  78. uint32_t kind,
  79. int *cache_size)
  80. {
  81. uint32_t ret;
  82. zval *class_name = &op_array->literals[op1];
  83. zval *prop_name = &op_array->literals[op2];
  84. zval *pos, tmp;
  85. zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
  86. ZSTR_H(key) = zend_string_hash_func(key);
  87. ZSTR_H(key) += kind;
  88. pos = zend_hash_find(hash, key);
  89. if (pos) {
  90. ret = Z_LVAL_P(pos);
  91. } else {
  92. ret = *cache_size;
  93. *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
  94. ZVAL_LONG(&tmp, ret);
  95. zend_hash_add(hash, key, &tmp);
  96. }
  97. zend_string_release_ex(key, 0);
  98. return ret;
  99. }
  100. static zend_string *create_str_cache_key(zval *literal, uint32_t flags)
  101. {
  102. ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
  103. uint32_t num_related = LITERAL_NUM_RELATED(flags);
  104. if (num_related == 1) {
  105. return zend_string_copy(Z_STR_P(literal));
  106. }
  107. if ((flags & LITERAL_KIND_MASK) == LITERAL_VALUE) {
  108. /* Don't merge LITERAL_VALUE that has related literals */
  109. return NULL;
  110. }
  111. /* Concatenate all the related literals for the cache key. */
  112. zend_string *key;
  113. if (num_related == 2) {
  114. ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
  115. key = zend_string_concat2(
  116. Z_STRVAL_P(literal), Z_STRLEN_P(literal),
  117. Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
  118. } else if (num_related == 3) {
  119. ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
  120. key = zend_string_concat3(
  121. Z_STRVAL_P(literal), Z_STRLEN_P(literal),
  122. Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
  123. Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
  124. } else {
  125. ZEND_ASSERT(0 && "Currently not needed");
  126. }
  127. /* Add a bias to the hash so we can distinguish keys
  128. * that would otherwise be the same after concatenation. */
  129. ZSTR_H(key) = zend_string_hash_val(key) + num_related - 1;
  130. return key;
  131. }
  132. void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
  133. {
  134. zend_op *opline, *end;
  135. int i, j, n, *map, cache_size;
  136. zval zv, *pos;
  137. literal_info *info;
  138. int l_null = -1;
  139. int l_false = -1;
  140. int l_true = -1;
  141. int l_empty_arr = -1;
  142. HashTable hash, double_hash;
  143. zend_string *key = NULL;
  144. void *checkpoint = zend_arena_checkpoint(ctx->arena);
  145. int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
  146. if (op_array->last_literal) {
  147. info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
  148. /* Mark literals of specific types */
  149. opline = op_array->opcodes;
  150. end = opline + op_array->last;
  151. while (opline < end) {
  152. switch (opline->opcode) {
  153. case ZEND_INIT_FCALL:
  154. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
  155. break;
  156. case ZEND_INIT_FCALL_BY_NAME:
  157. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
  158. break;
  159. case ZEND_INIT_NS_FCALL_BY_NAME:
  160. LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
  161. break;
  162. case ZEND_INIT_METHOD_CALL:
  163. if (opline->op1_type == IS_CONST) {
  164. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  165. }
  166. if (opline->op2_type == IS_CONST) {
  167. LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
  168. }
  169. break;
  170. case ZEND_INIT_STATIC_METHOD_CALL:
  171. if (opline->op1_type == IS_CONST) {
  172. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  173. }
  174. if (opline->op2_type == IS_CONST) {
  175. LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
  176. }
  177. break;
  178. case ZEND_CATCH:
  179. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  180. break;
  181. case ZEND_DEFINED:
  182. LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1);
  183. break;
  184. case ZEND_FETCH_CONSTANT:
  185. if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
  186. LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
  187. } else {
  188. LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2);
  189. }
  190. break;
  191. case ZEND_FETCH_CLASS_CONSTANT:
  192. if (opline->op1_type == IS_CONST) {
  193. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  194. }
  195. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
  196. break;
  197. case ZEND_ASSIGN_STATIC_PROP:
  198. case ZEND_ASSIGN_STATIC_PROP_REF:
  199. case ZEND_FETCH_STATIC_PROP_R:
  200. case ZEND_FETCH_STATIC_PROP_W:
  201. case ZEND_FETCH_STATIC_PROP_RW:
  202. case ZEND_FETCH_STATIC_PROP_IS:
  203. case ZEND_FETCH_STATIC_PROP_UNSET:
  204. case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
  205. case ZEND_UNSET_STATIC_PROP:
  206. case ZEND_ISSET_ISEMPTY_STATIC_PROP:
  207. case ZEND_PRE_INC_STATIC_PROP:
  208. case ZEND_PRE_DEC_STATIC_PROP:
  209. case ZEND_POST_INC_STATIC_PROP:
  210. case ZEND_POST_DEC_STATIC_PROP:
  211. case ZEND_ASSIGN_STATIC_PROP_OP:
  212. if (opline->op2_type == IS_CONST) {
  213. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
  214. }
  215. if (opline->op1_type == IS_CONST) {
  216. LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
  217. }
  218. break;
  219. case ZEND_FETCH_CLASS:
  220. case ZEND_INSTANCEOF:
  221. if (opline->op2_type == IS_CONST) {
  222. LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
  223. }
  224. break;
  225. case ZEND_NEW:
  226. if (opline->op1_type == IS_CONST) {
  227. LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
  228. }
  229. break;
  230. case ZEND_ASSIGN_OBJ:
  231. case ZEND_ASSIGN_OBJ_REF:
  232. case ZEND_FETCH_OBJ_R:
  233. case ZEND_FETCH_OBJ_W:
  234. case ZEND_FETCH_OBJ_RW:
  235. case ZEND_FETCH_OBJ_IS:
  236. case ZEND_FETCH_OBJ_UNSET:
  237. case ZEND_FETCH_OBJ_FUNC_ARG:
  238. case ZEND_UNSET_OBJ:
  239. case ZEND_PRE_INC_OBJ:
  240. case ZEND_PRE_DEC_OBJ:
  241. case ZEND_POST_INC_OBJ:
  242. case ZEND_POST_DEC_OBJ:
  243. case ZEND_ISSET_ISEMPTY_PROP_OBJ:
  244. case ZEND_ASSIGN_OBJ_OP:
  245. if (opline->op1_type == IS_CONST) {
  246. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  247. }
  248. if (opline->op2_type == IS_CONST) {
  249. LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
  250. }
  251. break;
  252. case ZEND_BIND_GLOBAL:
  253. LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
  254. break;
  255. case ZEND_RECV_INIT:
  256. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  257. break;
  258. case ZEND_DECLARE_CLASS:
  259. case ZEND_DECLARE_CLASS_DELAYED:
  260. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
  261. if (opline->op2_type == IS_CONST) {
  262. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  263. }
  264. break;
  265. case ZEND_ISSET_ISEMPTY_DIM_OBJ:
  266. case ZEND_ASSIGN_DIM:
  267. case ZEND_UNSET_DIM:
  268. case ZEND_FETCH_DIM_R:
  269. case ZEND_FETCH_DIM_W:
  270. case ZEND_FETCH_DIM_RW:
  271. case ZEND_FETCH_DIM_IS:
  272. case ZEND_FETCH_DIM_FUNC_ARG:
  273. case ZEND_FETCH_DIM_UNSET:
  274. case ZEND_FETCH_LIST_R:
  275. case ZEND_FETCH_LIST_W:
  276. case ZEND_ASSIGN_DIM_OP:
  277. if (opline->op1_type == IS_CONST) {
  278. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  279. }
  280. if (opline->op2_type == IS_CONST) {
  281. if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
  282. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
  283. } else {
  284. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  285. }
  286. }
  287. break;
  288. default:
  289. if (opline->op1_type == IS_CONST) {
  290. LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
  291. }
  292. if (opline->op2_type == IS_CONST) {
  293. LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
  294. }
  295. break;
  296. }
  297. opline++;
  298. }
  299. #if DEBUG_COMPACT_LITERALS
  300. {
  301. fprintf(stderr, "File %s func %s\n", op_array->filename->val,
  302. op_array->function_name ? op_array->function_name->val : "main");
  303. fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
  304. for (int i = 0; i < op_array->last_literal; i++) {
  305. zend_string *str = zval_get_string(op_array->literals + i);
  306. fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
  307. zend_string_release(str);
  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_new(&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. key = create_str_cache_key(&op_array->literals[i], info[i].flags);
  414. if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
  415. Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
  416. LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
  417. (LITERAL_NUM_RELATED(info[i].flags) != 2 ||
  418. ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
  419. (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
  420. zend_string_release_ex(key, 0);
  421. map[i] = Z_LVAL_P(pos);
  422. zval_ptr_dtor_nogc(&op_array->literals[i]);
  423. n = LITERAL_NUM_RELATED(info[i].flags);
  424. while (n > 1) {
  425. i++;
  426. zval_ptr_dtor_nogc(&op_array->literals[i]);
  427. n--;
  428. }
  429. } else {
  430. map[i] = j;
  431. ZVAL_LONG(&zv, j);
  432. if (key) {
  433. zend_hash_add_new(&hash, key, &zv);
  434. zend_string_release_ex(key, 0);
  435. }
  436. if (i != j) {
  437. op_array->literals[j] = op_array->literals[i];
  438. info[j] = info[i];
  439. }
  440. j++;
  441. n = LITERAL_NUM_RELATED(info[i].flags);
  442. while (n > 1) {
  443. i++;
  444. if (i != j) op_array->literals[j] = op_array->literals[i];
  445. j++;
  446. n--;
  447. }
  448. }
  449. break;
  450. }
  451. case IS_ARRAY:
  452. if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
  453. if (l_empty_arr < 0) {
  454. l_empty_arr = j;
  455. if (i != j) {
  456. op_array->literals[j] = op_array->literals[i];
  457. info[j] = info[i];
  458. }
  459. j++;
  460. } else {
  461. zval_ptr_dtor_nogc(&op_array->literals[i]);
  462. }
  463. map[i] = l_empty_arr;
  464. break;
  465. }
  466. ZEND_FALLTHROUGH;
  467. default:
  468. /* don't merge other types */
  469. map[i] = j;
  470. if (i != j) {
  471. op_array->literals[j] = op_array->literals[i];
  472. info[j] = info[i];
  473. }
  474. j++;
  475. break;
  476. }
  477. }
  478. /* Only clean "hash", as it will be reused in the loop below. */
  479. zend_hash_clean(&hash);
  480. zend_hash_destroy(&double_hash);
  481. op_array->last_literal = j;
  482. const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
  483. memset(const_slot, -1, j * 6 * sizeof(int));
  484. class_slot = const_slot + j;
  485. func_slot = class_slot + j;
  486. bind_var_slot = func_slot + j;
  487. property_slot = bind_var_slot + j;
  488. method_slot = property_slot + j;
  489. /* Update opcodes to use new literals table */
  490. cache_size = zend_op_array_extension_handles * sizeof(void*);
  491. opline = op_array->opcodes;
  492. end = opline + op_array->last;
  493. while (opline < end) {
  494. if (opline->op1_type == IS_CONST) {
  495. opline->op1.constant = map[opline->op1.constant];
  496. }
  497. if (opline->op2_type == IS_CONST) {
  498. opline->op2.constant = map[opline->op2.constant];
  499. }
  500. switch (opline->opcode) {
  501. case ZEND_RECV_INIT:
  502. case ZEND_RECV:
  503. case ZEND_RECV_VARIADIC:
  504. {
  505. size_t num_classes = type_num_classes(op_array, opline->op1.num);
  506. if (num_classes) {
  507. opline->extended_value = cache_size;
  508. cache_size += num_classes * sizeof(void *);
  509. }
  510. break;
  511. }
  512. case ZEND_VERIFY_RETURN_TYPE:
  513. {
  514. size_t num_classes = type_num_classes(op_array, 0);
  515. if (num_classes) {
  516. opline->op2.num = cache_size;
  517. cache_size += num_classes * sizeof(void *);
  518. }
  519. break;
  520. }
  521. case ZEND_ASSIGN_STATIC_PROP_OP:
  522. if (opline->op1_type == IS_CONST) {
  523. // op1 static property
  524. if (opline->op2_type == IS_CONST) {
  525. (opline+1)->extended_value = add_static_slot(&hash, op_array,
  526. opline->op2.constant,
  527. opline->op1.constant,
  528. LITERAL_STATIC_PROPERTY,
  529. &cache_size);
  530. } else {
  531. (opline+1)->extended_value = cache_size;
  532. cache_size += 3 * sizeof(void *);
  533. }
  534. } else if (opline->op2_type == IS_CONST) {
  535. // op2 class
  536. if (class_slot[opline->op2.constant] >= 0) {
  537. (opline+1)->extended_value = class_slot[opline->op2.constant];
  538. } else {
  539. (opline+1)->extended_value = cache_size;
  540. class_slot[opline->op2.constant] = cache_size;
  541. cache_size += sizeof(void *);
  542. }
  543. }
  544. break;
  545. case ZEND_ASSIGN_OBJ_OP:
  546. if (opline->op2_type == IS_CONST) {
  547. // op2 property
  548. if (opline->op1_type == IS_UNUSED &&
  549. property_slot[opline->op2.constant] >= 0) {
  550. (opline+1)->extended_value = property_slot[opline->op2.constant];
  551. } else {
  552. (opline+1)->extended_value = cache_size;
  553. cache_size += 3 * sizeof(void *);
  554. if (opline->op1_type == IS_UNUSED) {
  555. property_slot[opline->op2.constant] = (opline+1)->extended_value;
  556. }
  557. }
  558. }
  559. break;
  560. case ZEND_ASSIGN_OBJ:
  561. case ZEND_ASSIGN_OBJ_REF:
  562. case ZEND_FETCH_OBJ_R:
  563. case ZEND_FETCH_OBJ_W:
  564. case ZEND_FETCH_OBJ_RW:
  565. case ZEND_FETCH_OBJ_IS:
  566. case ZEND_FETCH_OBJ_UNSET:
  567. case ZEND_FETCH_OBJ_FUNC_ARG:
  568. case ZEND_UNSET_OBJ:
  569. case ZEND_PRE_INC_OBJ:
  570. case ZEND_PRE_DEC_OBJ:
  571. case ZEND_POST_INC_OBJ:
  572. case ZEND_POST_DEC_OBJ:
  573. if (opline->op2_type == IS_CONST) {
  574. // op2 property
  575. if (opline->op1_type == IS_UNUSED &&
  576. property_slot[opline->op2.constant] >= 0) {
  577. opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  578. } else {
  579. opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  580. cache_size += 3 * sizeof(void *);
  581. if (opline->op1_type == IS_UNUSED) {
  582. property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
  583. }
  584. }
  585. }
  586. break;
  587. case ZEND_ISSET_ISEMPTY_PROP_OBJ:
  588. if (opline->op2_type == IS_CONST) {
  589. // op2 property
  590. if (opline->op1_type == IS_UNUSED &&
  591. property_slot[opline->op2.constant] >= 0) {
  592. opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
  593. } else {
  594. opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
  595. cache_size += 3 * sizeof(void *);
  596. if (opline->op1_type == IS_UNUSED) {
  597. property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
  598. }
  599. }
  600. }
  601. break;
  602. case ZEND_INIT_FCALL:
  603. case ZEND_INIT_FCALL_BY_NAME:
  604. case ZEND_INIT_NS_FCALL_BY_NAME:
  605. // op2 func
  606. if (func_slot[opline->op2.constant] >= 0) {
  607. opline->result.num = func_slot[opline->op2.constant];
  608. } else {
  609. opline->result.num = cache_size;
  610. cache_size += sizeof(void *);
  611. func_slot[opline->op2.constant] = opline->result.num;
  612. }
  613. break;
  614. case ZEND_INIT_METHOD_CALL:
  615. if (opline->op2_type == IS_CONST) {
  616. // op2 method
  617. if (opline->op1_type == IS_UNUSED &&
  618. method_slot[opline->op2.constant] >= 0) {
  619. opline->result.num = method_slot[opline->op2.constant];
  620. } else {
  621. opline->result.num = cache_size;
  622. cache_size += 2 * sizeof(void *);
  623. if (opline->op1_type == IS_UNUSED) {
  624. method_slot[opline->op2.constant] = opline->result.num;
  625. }
  626. }
  627. }
  628. break;
  629. case ZEND_INIT_STATIC_METHOD_CALL:
  630. if (opline->op2_type == IS_CONST) {
  631. // op2 static method
  632. if (opline->op1_type == IS_CONST) {
  633. opline->result.num = add_static_slot(&hash, op_array,
  634. opline->op1.constant,
  635. opline->op2.constant,
  636. LITERAL_STATIC_METHOD,
  637. &cache_size);
  638. } else {
  639. opline->result.num = cache_size;
  640. cache_size += 2 * sizeof(void *);
  641. }
  642. } else if (opline->op1_type == IS_CONST) {
  643. // op1 class
  644. if (class_slot[opline->op1.constant] >= 0) {
  645. opline->result.num = class_slot[opline->op1.constant];
  646. } else {
  647. opline->result.num = cache_size;
  648. cache_size += sizeof(void *);
  649. class_slot[opline->op1.constant] = opline->result.num;
  650. }
  651. }
  652. break;
  653. case ZEND_DEFINED:
  654. // op1 const
  655. if (const_slot[opline->op1.constant] >= 0) {
  656. opline->extended_value = const_slot[opline->op1.constant];
  657. } else {
  658. opline->extended_value = cache_size;
  659. cache_size += sizeof(void *);
  660. const_slot[opline->op1.constant] = opline->extended_value;
  661. }
  662. break;
  663. case ZEND_FETCH_CONSTANT:
  664. // op2 const
  665. if (const_slot[opline->op2.constant] >= 0) {
  666. opline->extended_value = const_slot[opline->op2.constant];
  667. } else {
  668. opline->extended_value = cache_size;
  669. cache_size += sizeof(void *);
  670. const_slot[opline->op2.constant] = opline->extended_value;
  671. }
  672. break;
  673. case ZEND_FETCH_CLASS_CONSTANT:
  674. if (opline->op1_type == IS_CONST) {
  675. // op1/op2 class_const
  676. opline->extended_value = add_static_slot(&hash, op_array,
  677. opline->op1.constant,
  678. opline->op2.constant,
  679. LITERAL_CLASS_CONST,
  680. &cache_size);
  681. } else {
  682. opline->extended_value = cache_size;
  683. cache_size += 2 * sizeof(void *);
  684. }
  685. break;
  686. case ZEND_ASSIGN_STATIC_PROP:
  687. case ZEND_ASSIGN_STATIC_PROP_REF:
  688. case ZEND_FETCH_STATIC_PROP_R:
  689. case ZEND_FETCH_STATIC_PROP_W:
  690. case ZEND_FETCH_STATIC_PROP_RW:
  691. case ZEND_FETCH_STATIC_PROP_IS:
  692. case ZEND_FETCH_STATIC_PROP_UNSET:
  693. case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
  694. case ZEND_UNSET_STATIC_PROP:
  695. case ZEND_ISSET_ISEMPTY_STATIC_PROP:
  696. case ZEND_PRE_INC_STATIC_PROP:
  697. case ZEND_PRE_DEC_STATIC_PROP:
  698. case ZEND_POST_INC_STATIC_PROP:
  699. case ZEND_POST_DEC_STATIC_PROP:
  700. if (opline->op1_type == IS_CONST) {
  701. // op1 static property
  702. if (opline->op2_type == IS_CONST) {
  703. opline->extended_value = add_static_slot(&hash, op_array,
  704. opline->op2.constant,
  705. opline->op1.constant,
  706. LITERAL_STATIC_PROPERTY,
  707. &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  708. } else {
  709. opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  710. cache_size += 3 * sizeof(void *);
  711. }
  712. } else if (opline->op2_type == IS_CONST) {
  713. // op2 class
  714. if (class_slot[opline->op2.constant] >= 0) {
  715. opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  716. } else {
  717. opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
  718. class_slot[opline->op2.constant] = cache_size;
  719. cache_size += sizeof(void *);
  720. }
  721. }
  722. break;
  723. case ZEND_FETCH_CLASS:
  724. case ZEND_INSTANCEOF:
  725. if (opline->op2_type == IS_CONST) {
  726. // op2 class
  727. if (class_slot[opline->op2.constant] >= 0) {
  728. opline->extended_value = class_slot[opline->op2.constant];
  729. } else {
  730. opline->extended_value = cache_size;
  731. cache_size += sizeof(void *);
  732. class_slot[opline->op2.constant] = opline->extended_value;
  733. }
  734. }
  735. break;
  736. case ZEND_NEW:
  737. if (opline->op1_type == IS_CONST) {
  738. // op1 class
  739. if (class_slot[opline->op1.constant] >= 0) {
  740. opline->op2.num = class_slot[opline->op1.constant];
  741. } else {
  742. opline->op2.num = cache_size;
  743. cache_size += sizeof(void *);
  744. class_slot[opline->op1.constant] = opline->op2.num;
  745. }
  746. }
  747. break;
  748. case ZEND_CATCH:
  749. if (opline->op1_type == IS_CONST) {
  750. // op1 class
  751. if (class_slot[opline->op1.constant] >= 0) {
  752. opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
  753. } else {
  754. opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
  755. cache_size += sizeof(void *);
  756. class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
  757. }
  758. }
  759. break;
  760. case ZEND_BIND_GLOBAL:
  761. // op2 bind var
  762. if (bind_var_slot[opline->op2.constant] >= 0) {
  763. opline->extended_value = bind_var_slot[opline->op2.constant];
  764. } else {
  765. opline->extended_value = cache_size;
  766. cache_size += sizeof(void *);
  767. bind_var_slot[opline->op2.constant] = opline->extended_value;
  768. }
  769. break;
  770. case ZEND_DECLARE_ANON_CLASS:
  771. case ZEND_DECLARE_CLASS_DELAYED:
  772. opline->extended_value = cache_size;
  773. cache_size += sizeof(void *);
  774. break;
  775. case ZEND_SEND_VAL:
  776. case ZEND_SEND_VAL_EX:
  777. case ZEND_SEND_VAR:
  778. case ZEND_SEND_VAR_EX:
  779. case ZEND_SEND_VAR_NO_REF:
  780. case ZEND_SEND_VAR_NO_REF_EX:
  781. case ZEND_SEND_REF:
  782. case ZEND_SEND_FUNC_ARG:
  783. case ZEND_CHECK_FUNC_ARG:
  784. if (opline->op2_type == IS_CONST) {
  785. opline->result.num = cache_size;
  786. cache_size += 2 * sizeof(void *);
  787. }
  788. break;
  789. }
  790. opline++;
  791. }
  792. op_array->cache_size = cache_size;
  793. zend_hash_destroy(&hash);
  794. zend_arena_release(&ctx->arena, checkpoint);
  795. if (1) {
  796. opline = op_array->opcodes;
  797. while (1) {
  798. if (opline->opcode == ZEND_RECV_INIT) {
  799. zval *val = &op_array->literals[opline->op2.constant];
  800. if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
  801. /* Ensure zval is aligned to 8 bytes */
  802. op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
  803. Z_CACHE_SLOT_P(val) = op_array->cache_size;
  804. op_array->cache_size += sizeof(zval);
  805. }
  806. } else if (opline->opcode != ZEND_RECV) {
  807. break;
  808. }
  809. opline++;
  810. }
  811. }
  812. #if DEBUG_COMPACT_LITERALS
  813. {
  814. fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
  815. for (int i = 0; i < op_array->last_literal; i++) {
  816. zend_string *str = zval_get_string(op_array->literals + i);
  817. fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
  818. zend_string_release(str);
  819. }
  820. fflush(stderr);
  821. }
  822. #endif
  823. }
  824. }