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 if (opline->op2_type == IS_CONST) {
215 LITERAL_INFO(opline->op2.constant, 1);
216 }
217 break;
218 case ZEND_ASSIGN_STATIC_PROP:
219 case ZEND_ASSIGN_STATIC_PROP_REF:
220 case ZEND_FETCH_STATIC_PROP_R:
221 case ZEND_FETCH_STATIC_PROP_W:
222 case ZEND_FETCH_STATIC_PROP_RW:
223 case ZEND_FETCH_STATIC_PROP_IS:
224 case ZEND_FETCH_STATIC_PROP_UNSET:
225 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
226 case ZEND_UNSET_STATIC_PROP:
227 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
228 case ZEND_PRE_INC_STATIC_PROP:
229 case ZEND_PRE_DEC_STATIC_PROP:
230 case ZEND_POST_INC_STATIC_PROP:
231 case ZEND_POST_DEC_STATIC_PROP:
232 case ZEND_ASSIGN_STATIC_PROP_OP:
233 if (opline->op2_type == IS_CONST) {
234 LITERAL_INFO(opline->op2.constant, 2);
235 }
236 if (opline->op1_type == IS_CONST) {
237 LITERAL_INFO(opline->op1.constant, 1);
238 }
239 break;
240 case ZEND_FETCH_CLASS:
241 case ZEND_INSTANCEOF:
242 if (opline->op2_type == IS_CONST) {
243 LITERAL_INFO(opline->op2.constant, 2);
244 }
245 break;
246 case ZEND_NEW:
247 if (opline->op1_type == IS_CONST) {
248 LITERAL_INFO(opline->op1.constant, 2);
249 }
250 break;
251 case ZEND_DECLARE_CLASS:
252 case ZEND_DECLARE_CLASS_DELAYED:
253 LITERAL_INFO(opline->op1.constant, 2);
254 if (opline->op2_type == IS_CONST) {
255 LITERAL_INFO(opline->op2.constant, 1);
256 }
257 break;
258 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
259 case ZEND_ASSIGN_DIM:
260 case ZEND_UNSET_DIM:
261 case ZEND_FETCH_DIM_R:
262 case ZEND_FETCH_DIM_W:
263 case ZEND_FETCH_DIM_RW:
264 case ZEND_FETCH_DIM_IS:
265 case ZEND_FETCH_DIM_FUNC_ARG:
266 case ZEND_FETCH_DIM_UNSET:
267 case ZEND_FETCH_LIST_R:
268 case ZEND_FETCH_LIST_W:
269 case ZEND_ASSIGN_DIM_OP:
270 if (opline->op1_type == IS_CONST) {
271 LITERAL_INFO(opline->op1.constant, 1);
272 }
273 if (opline->op2_type == IS_CONST) {
274 if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
275 LITERAL_INFO(opline->op2.constant, 2);
276 } else {
277 LITERAL_INFO(opline->op2.constant, 1);
278 }
279 }
280 break;
281 default:
282 if (opline->op1_type == IS_CONST) {
283 LITERAL_INFO(opline->op1.constant, 1);
284 }
285 if (opline->op2_type == IS_CONST) {
286 LITERAL_INFO(opline->op2.constant, 1);
287 }
288 break;
289 }
290 opline++;
291 }
292
293 #if DEBUG_COMPACT_LITERALS
294 {
295 fprintf(stderr, "File %s func %s\n", op_array->filename->val,
296 op_array->function_name ? op_array->function_name->val : "main");
297 fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
298
299 for (int i = 0; i < op_array->last_literal; i++) {
300 zend_string *str = zval_get_string(op_array->literals + i);
301 fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
302 zend_string_release(str);
303 }
304 fflush(stderr);
305 }
306 #endif
307
308 /* Merge equal constants */
309 j = 0;
310 zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
311 map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
312 memset(map, 0, op_array->last_literal * sizeof(int));
313 for (i = 0; i < op_array->last_literal; i++) {
314 if (!info[i].num_related) {
315 /* unset literal */
316 zval_ptr_dtor_nogc(&op_array->literals[i]);
317 continue;
318 }
319 switch (Z_TYPE(op_array->literals[i])) {
320 case IS_NULL:
321 ZEND_ASSERT(info[i].num_related == 1);
322 if (l_null < 0) {
323 l_null = j;
324 if (i != j) {
325 op_array->literals[j] = op_array->literals[i];
326 info[j] = info[i];
327 }
328 j++;
329 }
330 map[i] = l_null;
331 break;
332 case IS_FALSE:
333 ZEND_ASSERT(info[i].num_related == 1);
334 if (l_false < 0) {
335 l_false = j;
336 if (i != j) {
337 op_array->literals[j] = op_array->literals[i];
338 info[j] = info[i];
339 }
340 j++;
341 }
342 map[i] = l_false;
343 break;
344 case IS_TRUE:
345 ZEND_ASSERT(info[i].num_related == 1);
346 if (l_true < 0) {
347 l_true = j;
348 if (i != j) {
349 op_array->literals[j] = op_array->literals[i];
350 info[j] = info[i];
351 }
352 j++;
353 }
354 map[i] = l_true;
355 break;
356 case IS_LONG:
357 if (info[i].num_related == 1) {
358 if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
359 map[i] = Z_LVAL_P(pos);
360 } else {
361 map[i] = j;
362 ZVAL_LONG(&zv, j);
363 zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
364 if (i != j) {
365 op_array->literals[j] = op_array->literals[i];
366 info[j] = info[i];
367 }
368 j++;
369 }
370 } else {
371 ZEND_ASSERT(info[i].num_related == 2);
372 key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
373 bias_key(key, 100 + info[i].num_related - 1);
374 if ((pos = zend_hash_find(&hash, key)) != NULL) {
375 ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
376 map[i] = Z_LVAL_P(pos);
377 zval_ptr_dtor_nogc(&op_array->literals[i+1]);
378 } else {
379 map[i] = j;
380 ZVAL_LONG(&zv, j);
381 zend_hash_add_new(&hash, key, &zv);
382 if (i != j) {
383 op_array->literals[j] = op_array->literals[i];
384 info[j] = info[i];
385 op_array->literals[j+1] = op_array->literals[i+1];
386 info[j+1] = info[i+1];
387 }
388 j += 2;
389 }
390 zend_string_release_ex(key, 0);
391 i++;
392 }
393 break;
394 case IS_DOUBLE:
395 ZEND_ASSERT(info[i].num_related == 1);
396 key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
397 bias_key(key, 200);
398 if ((pos = zend_hash_find(&hash, key))) {
399 map[i] = Z_LVAL_P(pos);
400 } else {
401 map[i] = j;
402 ZVAL_LONG(&zv, j);
403 zend_hash_add_new(&hash, key, &zv);
404 if (i != j) {
405 op_array->literals[j] = op_array->literals[i];
406 info[j] = info[i];
407 }
408 j++;
409 }
410 zend_string_release_ex(key, 0);
411 break;
412 case IS_STRING: {
413 key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
414 if ((pos = zend_hash_find(&hash, key)) != NULL) {
415 ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
416 info[i].num_related == info[Z_LVAL_P(pos)].num_related);
417 zend_string_release_ex(key, 0);
418 map[i] = Z_LVAL_P(pos);
419 zval_ptr_dtor_nogc(&op_array->literals[i]);
420 n = info[i].num_related;
421 while (n > 1) {
422 i++;
423 zval_ptr_dtor_nogc(&op_array->literals[i]);
424 n--;
425 }
426 } else {
427 map[i] = j;
428 ZVAL_LONG(&zv, j);
429 zend_hash_add_new(&hash, key, &zv);
430 zend_string_release_ex(key, 0);
431 if (i != j) {
432 op_array->literals[j] = op_array->literals[i];
433 info[j] = info[i];
434 }
435 j++;
436 n = info[i].num_related;
437 while (n > 1) {
438 i++;
439 if (i != j) op_array->literals[j] = op_array->literals[i];
440 j++;
441 n--;
442 }
443 }
444 break;
445 }
446 case IS_ARRAY:
447 ZEND_ASSERT(info[i].num_related == 1);
448 if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
449 if (l_empty_arr < 0) {
450 l_empty_arr = j;
451 if (i != j) {
452 op_array->literals[j] = op_array->literals[i];
453 info[j] = info[i];
454 }
455 j++;
456 } else {
457 zval_ptr_dtor_nogc(&op_array->literals[i]);
458 }
459 map[i] = l_empty_arr;
460 break;
461 }
462 ZEND_FALLTHROUGH;
463 default:
464 /* don't merge other types */
465 ZEND_ASSERT(info[i].num_related == 1);
466 map[i] = j;
467 if (i != j) {
468 op_array->literals[j] = op_array->literals[i];
469 info[j] = info[i];
470 }
471 j++;
472 break;
473 }
474 }
475
476 /* Only clean "hash", as it will be reused in the loop below. */
477 zend_hash_clean(&hash);
478 op_array->last_literal = j;
479
480 const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
481 memset(const_slot, -1, j * 6 * sizeof(int));
482 class_slot = const_slot + j;
483 func_slot = class_slot + j;
484 bind_var_slot = func_slot + j;
485 property_slot = bind_var_slot + j;
486 method_slot = property_slot + j;
487
488 /* Update opcodes to use new literals table */
489 cache_size = zend_op_array_extension_handles * sizeof(void*);
490 opline = op_array->opcodes;
491 end = opline + op_array->last;
492 while (opline < end) {
493 if (opline->op1_type == IS_CONST) {
494 opline->op1.constant = map[opline->op1.constant];
495 }
496 if (opline->op2_type == IS_CONST) {
497 opline->op2.constant = map[opline->op2.constant];
498 }
499 switch (opline->opcode) {
500 case ZEND_RECV_INIT:
501 case ZEND_RECV:
502 case ZEND_RECV_VARIADIC:
503 {
504 size_t num_classes = type_num_classes(op_array, opline->op1.num);
505 if (num_classes) {
506 opline->extended_value = cache_size;
507 cache_size += num_classes * sizeof(void *);
508 }
509 break;
510 }
511 case ZEND_VERIFY_RETURN_TYPE:
512 {
513 size_t num_classes = type_num_classes(op_array, 0);
514 if (num_classes) {
515 opline->op2.num = cache_size;
516 cache_size += num_classes * sizeof(void *);
517 }
518 break;
519 }
520 case ZEND_ASSIGN_STATIC_PROP_OP:
521 if (opline->op1_type == IS_CONST) {
522 // op1 static property
523 if (opline->op2_type == IS_CONST) {
524 (opline+1)->extended_value = add_static_slot(&hash, op_array,
525 opline->op2.constant,
526 opline->op1.constant,
527 LITERAL_STATIC_PROPERTY,
528 &cache_size);
529 } else {
530 (opline+1)->extended_value = cache_size;
531 cache_size += 3 * sizeof(void *);
532 }
533 } else if (opline->op2_type == IS_CONST) {
534 // op2 class
535 if (class_slot[opline->op2.constant] >= 0) {
536 (opline+1)->extended_value = class_slot[opline->op2.constant];
537 } else {
538 (opline+1)->extended_value = cache_size;
539 class_slot[opline->op2.constant] = cache_size;
540 cache_size += sizeof(void *);
541 }
542 }
543 break;
544 case ZEND_ASSIGN_OBJ_OP:
545 if (opline->op2_type == IS_CONST) {
546 // op2 property
547 if (opline->op1_type == IS_UNUSED &&
548 property_slot[opline->op2.constant] >= 0) {
549 (opline+1)->extended_value = property_slot[opline->op2.constant];
550 } else {
551 (opline+1)->extended_value = cache_size;
552 cache_size += 3 * sizeof(void *);
553 if (opline->op1_type == IS_UNUSED) {
554 property_slot[opline->op2.constant] = (opline+1)->extended_value;
555 }
556 }
557 }
558 break;
559 case ZEND_ASSIGN_OBJ:
560 case ZEND_ASSIGN_OBJ_REF:
561 case ZEND_FETCH_OBJ_R:
562 case ZEND_FETCH_OBJ_W:
563 case ZEND_FETCH_OBJ_RW:
564 case ZEND_FETCH_OBJ_IS:
565 case ZEND_FETCH_OBJ_UNSET:
566 case ZEND_FETCH_OBJ_FUNC_ARG:
567 case ZEND_UNSET_OBJ:
568 case ZEND_PRE_INC_OBJ:
569 case ZEND_PRE_DEC_OBJ:
570 case ZEND_POST_INC_OBJ:
571 case ZEND_POST_DEC_OBJ:
572 if (opline->op2_type == IS_CONST) {
573 // op2 property
574 if (opline->op1_type == IS_UNUSED &&
575 property_slot[opline->op2.constant] >= 0) {
576 opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
577 } else {
578 opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
579 cache_size += 3 * sizeof(void *);
580 if (opline->op1_type == IS_UNUSED) {
581 property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
582 }
583 }
584 }
585 break;
586 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
587 if (opline->op2_type == IS_CONST) {
588 // op2 property
589 if (opline->op1_type == IS_UNUSED &&
590 property_slot[opline->op2.constant] >= 0) {
591 opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
592 } else {
593 opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
594 cache_size += 3 * sizeof(void *);
595 if (opline->op1_type == IS_UNUSED) {
596 property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
597 }
598 }
599 }
600 break;
601 case ZEND_INIT_FCALL:
602 case ZEND_INIT_FCALL_BY_NAME:
603 case ZEND_INIT_NS_FCALL_BY_NAME:
604 // op2 func
605 if (func_slot[opline->op2.constant] >= 0) {
606 opline->result.num = func_slot[opline->op2.constant];
607 } else {
608 opline->result.num = cache_size;
609 cache_size += sizeof(void *);
610 func_slot[opline->op2.constant] = opline->result.num;
611 }
612 break;
613 case ZEND_INIT_METHOD_CALL:
614 if (opline->op2_type == IS_CONST) {
615 // op2 method
616 if (opline->op1_type == IS_UNUSED &&
617 method_slot[opline->op2.constant] >= 0) {
618 opline->result.num = method_slot[opline->op2.constant];
619 } else {
620 opline->result.num = cache_size;
621 cache_size += 2 * sizeof(void *);
622 if (opline->op1_type == IS_UNUSED) {
623 method_slot[opline->op2.constant] = opline->result.num;
624 }
625 }
626 }
627 break;
628 case ZEND_INIT_STATIC_METHOD_CALL:
629 if (opline->op2_type == IS_CONST) {
630 // op2 static method
631 if (opline->op1_type == IS_CONST) {
632 opline->result.num = add_static_slot(&hash, op_array,
633 opline->op1.constant,
634 opline->op2.constant,
635 LITERAL_STATIC_METHOD,
636 &cache_size);
637 } else {
638 opline->result.num = cache_size;
639 cache_size += 2 * sizeof(void *);
640 }
641 } else if (opline->op1_type == IS_CONST) {
642 // op1 class
643 if (class_slot[opline->op1.constant] >= 0) {
644 opline->result.num = class_slot[opline->op1.constant];
645 } else {
646 opline->result.num = cache_size;
647 cache_size += sizeof(void *);
648 class_slot[opline->op1.constant] = opline->result.num;
649 }
650 }
651 break;
652 case ZEND_DEFINED:
653 // op1 const
654 if (const_slot[opline->op1.constant] >= 0) {
655 opline->extended_value = const_slot[opline->op1.constant];
656 } else {
657 opline->extended_value = cache_size;
658 cache_size += sizeof(void *);
659 const_slot[opline->op1.constant] = opline->extended_value;
660 }
661 break;
662 case ZEND_FETCH_CONSTANT:
663 // op2 const
664 if (const_slot[opline->op2.constant] >= 0) {
665 opline->extended_value = const_slot[opline->op2.constant];
666 } else {
667 opline->extended_value = cache_size;
668 cache_size += sizeof(void *);
669 const_slot[opline->op2.constant] = opline->extended_value;
670 }
671 break;
672 case ZEND_FETCH_CLASS_CONSTANT:
673 if (opline->op1_type == IS_CONST
674 && opline->op2_type == IS_CONST
675 && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) {
676 // op1/op2 class_const
677 opline->extended_value = add_static_slot(&hash, op_array,
678 opline->op1.constant,
679 opline->op2.constant,
680 LITERAL_CLASS_CONST,
681 &cache_size);
682 } else {
683 opline->extended_value = cache_size;
684 cache_size += 2 * sizeof(void *);
685 }
686 break;
687 case ZEND_ASSIGN_STATIC_PROP:
688 case ZEND_ASSIGN_STATIC_PROP_REF:
689 case ZEND_FETCH_STATIC_PROP_R:
690 case ZEND_FETCH_STATIC_PROP_W:
691 case ZEND_FETCH_STATIC_PROP_RW:
692 case ZEND_FETCH_STATIC_PROP_IS:
693 case ZEND_FETCH_STATIC_PROP_UNSET:
694 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
695 case ZEND_UNSET_STATIC_PROP:
696 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
697 case ZEND_PRE_INC_STATIC_PROP:
698 case ZEND_PRE_DEC_STATIC_PROP:
699 case ZEND_POST_INC_STATIC_PROP:
700 case ZEND_POST_DEC_STATIC_PROP:
701 if (opline->op1_type == IS_CONST) {
702 // op1 static property
703 if (opline->op2_type == IS_CONST) {
704 opline->extended_value = add_static_slot(&hash, op_array,
705 opline->op2.constant,
706 opline->op1.constant,
707 LITERAL_STATIC_PROPERTY,
708 &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
709 } else {
710 opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
711 cache_size += 3 * sizeof(void *);
712 }
713 } else if (opline->op2_type == IS_CONST) {
714 // op2 class
715 if (class_slot[opline->op2.constant] >= 0) {
716 opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
717 } else {
718 opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
719 class_slot[opline->op2.constant] = cache_size;
720 cache_size += sizeof(void *);
721 }
722 }
723 break;
724 case ZEND_FETCH_CLASS:
725 case ZEND_INSTANCEOF:
726 if (opline->op2_type == IS_CONST) {
727 // op2 class
728 if (class_slot[opline->op2.constant] >= 0) {
729 opline->extended_value = class_slot[opline->op2.constant];
730 } else {
731 opline->extended_value = cache_size;
732 cache_size += sizeof(void *);
733 class_slot[opline->op2.constant] = opline->extended_value;
734 }
735 }
736 break;
737 case ZEND_NEW:
738 if (opline->op1_type == IS_CONST) {
739 // op1 class
740 if (class_slot[opline->op1.constant] >= 0) {
741 opline->op2.num = class_slot[opline->op1.constant];
742 } else {
743 opline->op2.num = cache_size;
744 cache_size += sizeof(void *);
745 class_slot[opline->op1.constant] = opline->op2.num;
746 }
747 }
748 break;
749 case ZEND_CATCH:
750 if (opline->op1_type == IS_CONST) {
751 // op1 class
752 if (class_slot[opline->op1.constant] >= 0) {
753 opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
754 } else {
755 opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
756 cache_size += sizeof(void *);
757 class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
758 }
759 }
760 break;
761 case ZEND_BIND_GLOBAL:
762 // op2 bind var
763 if (bind_var_slot[opline->op2.constant] >= 0) {
764 opline->extended_value = bind_var_slot[opline->op2.constant];
765 } else {
766 opline->extended_value = cache_size;
767 cache_size += sizeof(void *);
768 bind_var_slot[opline->op2.constant] = opline->extended_value;
769 }
770 break;
771 case ZEND_DECLARE_ANON_CLASS:
772 case ZEND_DECLARE_CLASS_DELAYED:
773 opline->extended_value = cache_size;
774 cache_size += sizeof(void *);
775 break;
776 case ZEND_SEND_VAL:
777 case ZEND_SEND_VAL_EX:
778 case ZEND_SEND_VAR:
779 case ZEND_SEND_VAR_EX:
780 case ZEND_SEND_VAR_NO_REF:
781 case ZEND_SEND_VAR_NO_REF_EX:
782 case ZEND_SEND_REF:
783 case ZEND_SEND_FUNC_ARG:
784 case ZEND_CHECK_FUNC_ARG:
785 if (opline->op2_type == IS_CONST) {
786 opline->result.num = cache_size;
787 cache_size += 2 * sizeof(void *);
788 }
789 break;
790 }
791 opline++;
792 }
793 op_array->cache_size = cache_size;
794 zend_hash_destroy(&hash);
795 zend_arena_release(&ctx->arena, checkpoint);
796
797 if (1) {
798 opline = op_array->opcodes;
799 while (1) {
800 if (opline->opcode == ZEND_RECV_INIT) {
801 zval *val = &op_array->literals[opline->op2.constant];
802
803 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
804 /* Ensure zval is aligned to 8 bytes */
805 op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
806 Z_CACHE_SLOT_P(val) = op_array->cache_size;
807 op_array->cache_size += sizeof(zval);
808 }
809 } else if (opline->opcode != ZEND_RECV) {
810 break;
811 }
812 opline++;
813 }
814 }
815
816 #if DEBUG_COMPACT_LITERALS
817 {
818 fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
819
820 for (int i = 0; i < op_array->last_literal; i++) {
821 zend_string *str = zval_get_string(op_array->literals + i);
822 fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
823 zend_string_release(str);
824 }
825 fflush(stderr);
826 }
827 #endif
828 }
829 }
830