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