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