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