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