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