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