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