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