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: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "php.h"
23 #include "Optimizer/zend_optimizer.h"
24 #include "Optimizer/zend_optimizer_internal.h"
25 #include "zend_API.h"
26 #include "zend_constants.h"
27 #include "zend_execute.h"
28 #include "zend_vm.h"
29 #include "zend_cfg.h"
30 #include "zend_func_info.h"
31 #include "zend_call_graph.h"
32 #include "zend_inference.h"
33 #include "zend_dump.h"
34 
35 #ifndef HAVE_DFA_PASS
36 # define HAVE_DFA_PASS 1
37 #endif
38 
zend_optimizer_zval_dtor_wrapper(zval * zvalue)39 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
40 {
41 	zval_ptr_dtor_nogc(zvalue);
42 }
43 
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)44 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
45 {
46 	zval val;
47 
48 	if (!ctx->constants) {
49 		ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
50 		zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
51 	}
52 	ZVAL_COPY(&val, value);
53 	zend_hash_add(ctx->constants, Z_STR_P(name), &val);
54 }
55 
zend_optimizer_eval_binary_op(zval * result,zend_uchar opcode,zval * op1,zval * op2)56 int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */
57 {
58 	binary_op_type binary_op = get_binary_op(opcode);
59 	int er, ret;
60 
61 	if (zend_binary_op_produces_numeric_string_error(opcode, op1, op2)) {
62 		/* produces numeric string E_NOTICE/E_WARNING */
63 		return FAILURE;
64 	}
65 
66 	switch (opcode) {
67 		case ZEND_ADD:
68 			if ((Z_TYPE_P(op1) == IS_ARRAY
69 			  || Z_TYPE_P(op2) == IS_ARRAY)
70 			 && Z_TYPE_P(op1) != Z_TYPE_P(op2)) {
71 				/* produces "Unsupported operand types" exception */
72 				return FAILURE;
73 			}
74 			break;
75 		case ZEND_DIV:
76 		case ZEND_MOD:
77 			if (zval_get_long(op2) == 0) {
78 				/* division by 0 */
79 				return FAILURE;
80 			}
81 			/* break missing intentionally */
82 		case ZEND_SUB:
83 		case ZEND_MUL:
84 		case ZEND_POW:
85 		case ZEND_CONCAT:
86 		case ZEND_FAST_CONCAT:
87 			if (Z_TYPE_P(op1) == IS_ARRAY
88 			 || Z_TYPE_P(op2) == IS_ARRAY) {
89 				/* produces "Unsupported operand types" exception */
90 				return FAILURE;
91 			}
92 			break;
93 		case ZEND_SL:
94 		case ZEND_SR:
95 			if (zval_get_long(op2) < 0) {
96 				/* shift by negative number */
97 				return FAILURE;
98 			}
99 			break;
100 	}
101 
102 	er = EG(error_reporting);
103 	EG(error_reporting) = 0;
104 	ret = binary_op(result, op1, op2);
105 	EG(error_reporting) = er;
106 
107 	return ret;
108 }
109 /* }}} */
110 
zend_optimizer_eval_unary_op(zval * result,zend_uchar opcode,zval * op1)111 int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */
112 {
113 	unary_op_type unary_op = get_unary_op(opcode);
114 
115 	if (unary_op) {
116 		if (opcode == ZEND_BW_NOT
117 		 && Z_TYPE_P(op1) != IS_LONG
118 		 && Z_TYPE_P(op1) != IS_DOUBLE
119 		 && Z_TYPE_P(op1) != IS_STRING) {
120 			/* produces "Unsupported operand types" exception */
121 			return FAILURE;
122 		}
123 		return unary_op(result, op1);
124 	} else { /* ZEND_BOOL */
125 		ZVAL_BOOL(result, zend_is_true(op1));
126 		return SUCCESS;
127 	}
128 }
129 /* }}} */
130 
zend_optimizer_eval_cast(zval * result,uint32_t type,zval * op1)131 int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
132 {
133 	switch (type) {
134 		case IS_NULL:
135 			ZVAL_NULL(result);
136 			return SUCCESS;
137 		case _IS_BOOL:
138 			ZVAL_BOOL(result, zval_is_true(op1));
139 			return SUCCESS;
140 		case IS_LONG:
141 			ZVAL_LONG(result, zval_get_long(op1));
142 			return SUCCESS;
143 		case IS_DOUBLE:
144 			ZVAL_DOUBLE(result, zval_get_double(op1));
145 			return SUCCESS;
146 		case IS_STRING:
147 			/* Conversion from double to string takes into account run-time
148 			   'precision' setting and cannot be evaluated at compile-time */
149 			if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
150 				ZVAL_STR(result, zval_get_string(op1));
151 				return SUCCESS;
152 			}
153 			break;
154 		case IS_ARRAY:
155 			ZVAL_COPY(result, op1);
156 			convert_to_array(result);
157 			return SUCCESS;
158 	}
159 	return FAILURE;
160 }
161 /* }}} */
162 
zend_optimizer_eval_strlen(zval * result,zval * op1)163 int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */
164 {
165 	if (Z_TYPE_P(op1) != IS_STRING) {
166 		return FAILURE;
167 	}
168 	ZVAL_LONG(result, Z_STRLEN_P(op1));
169 	return SUCCESS;
170 }
171 /* }}} */
172 
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)173 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
174 {
175 	zval *val;
176 
177 	if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
178 		ZVAL_COPY(value, val);
179 		return 1;
180 	}
181 	return 0;
182 }
183 
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)184 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
185 {
186 	int i = op_array->last_literal;
187 	op_array->last_literal++;
188 	op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
189 	ZVAL_COPY_VALUE(&op_array->literals[i], zv);
190 	Z_EXTRA(op_array->literals[i]) = 0;
191 	return i;
192 }
193 
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)194 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
195 	zval zv;
196 	ZVAL_STR(&zv, str);
197 	zend_string_hash_val(str);
198 	return zend_optimizer_add_literal(op_array, &zv);
199 }
200 
zend_optimizer_is_disabled_func(const char * name,size_t len)201 int zend_optimizer_is_disabled_func(const char *name, size_t len) {
202 	zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
203 
204 	return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
205 			fbc->internal_function.handler == ZEND_FN(display_disabled_function));
206 }
207 
drop_leading_backslash(zval * val)208 static inline void drop_leading_backslash(zval *val) {
209 	if (Z_STRVAL_P(val)[0] == '\\') {
210 		zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
211 		zval_ptr_dtor_nogc(val);
212 		ZVAL_STR(val, str);
213 	}
214 }
215 
alloc_cache_slots(zend_op_array * op_array,uint32_t num)216 static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) {
217 	uint32_t ret = op_array->cache_size;
218 	op_array->cache_size += num * sizeof(void *);
219 	return ret;
220 }
221 
222 #define REQUIRES_STRING(val) do { \
223 	if (Z_TYPE_P(val) != IS_STRING) { \
224 		return 0; \
225 	} \
226 } while (0)
227 
228 #define TO_STRING_NOWARN(val) do { \
229 	if (Z_TYPE_P(val) >= IS_ARRAY) { \
230 		return 0; \
231 	} \
232 	convert_to_string(val); \
233 } while (0)
234 
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)235 int zend_optimizer_update_op1_const(zend_op_array *op_array,
236                                     zend_op       *opline,
237                                     zval          *val)
238 {
239 	switch (opline->opcode) {
240 		case ZEND_OP_DATA:
241 			switch ((opline-1)->opcode) {
242 				case ZEND_ASSIGN_OBJ_REF:
243 				case ZEND_ASSIGN_STATIC_PROP_REF:
244 					return 0;
245 			}
246 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
247 			break;
248 		case ZEND_FREE:
249 		case ZEND_CHECK_VAR:
250 			MAKE_NOP(opline);
251 			zval_ptr_dtor_nogc(val);
252 			return 1;
253 		case ZEND_SEND_VAR_EX:
254 		case ZEND_SEND_FUNC_ARG:
255 		case ZEND_FETCH_DIM_W:
256 		case ZEND_FETCH_DIM_RW:
257 		case ZEND_FETCH_DIM_FUNC_ARG:
258 		case ZEND_FETCH_DIM_UNSET:
259 		case ZEND_FETCH_LIST_W:
260 		case ZEND_ASSIGN_DIM:
261 		case ZEND_RETURN_BY_REF:
262 		case ZEND_INSTANCEOF:
263 		case ZEND_MAKE_REF:
264 			return 0;
265 		case ZEND_CATCH:
266 			REQUIRES_STRING(val);
267 			drop_leading_backslash(val);
268 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
269 			opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH);
270 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
271 			break;
272 		case ZEND_DEFINED:
273 			REQUIRES_STRING(val);
274 			drop_leading_backslash(val);
275 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
276 			opline->extended_value = alloc_cache_slots(op_array, 1);
277 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
278 			break;
279 		case ZEND_NEW:
280 			REQUIRES_STRING(val);
281 			drop_leading_backslash(val);
282 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
283 			opline->op2.num = alloc_cache_slots(op_array, 1);
284 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
285 			break;
286 		case ZEND_INIT_STATIC_METHOD_CALL:
287 			REQUIRES_STRING(val);
288 			drop_leading_backslash(val);
289 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
290 			if (opline->op2_type != IS_CONST) {
291 				opline->result.num = alloc_cache_slots(op_array, 1);
292 			}
293 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
294 			break;
295 		case ZEND_FETCH_CLASS_CONSTANT:
296 			REQUIRES_STRING(val);
297 			drop_leading_backslash(val);
298 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
299 			if (opline->op2_type != IS_CONST) {
300 				opline->extended_value = alloc_cache_slots(op_array, 1);
301 			}
302 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
303 			break;
304 		case ZEND_ASSIGN_OP:
305 		case ZEND_ASSIGN_DIM_OP:
306 		case ZEND_ASSIGN_OBJ_OP:
307 			break;
308 		case ZEND_ASSIGN_STATIC_PROP_OP:
309 		case ZEND_ASSIGN_STATIC_PROP:
310 		case ZEND_ASSIGN_STATIC_PROP_REF:
311 		case ZEND_FETCH_STATIC_PROP_R:
312 		case ZEND_FETCH_STATIC_PROP_W:
313 		case ZEND_FETCH_STATIC_PROP_RW:
314 		case ZEND_FETCH_STATIC_PROP_IS:
315 		case ZEND_FETCH_STATIC_PROP_UNSET:
316 		case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
317 		case ZEND_UNSET_STATIC_PROP:
318 		case ZEND_ISSET_ISEMPTY_STATIC_PROP:
319 		case ZEND_PRE_INC_STATIC_PROP:
320 		case ZEND_PRE_DEC_STATIC_PROP:
321 		case ZEND_POST_INC_STATIC_PROP:
322 		case ZEND_POST_DEC_STATIC_PROP:
323 			TO_STRING_NOWARN(val);
324 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
325 			if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) {
326 				op_array->cache_size += sizeof(void *);
327 			} else {
328 				opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
329 			}
330 			break;
331 		case ZEND_SEND_VAR:
332 			opline->opcode = ZEND_SEND_VAL;
333 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
334 			break;
335 		case ZEND_SEPARATE:
336 		case ZEND_SEND_VAR_NO_REF:
337 		case ZEND_SEND_VAR_NO_REF_EX:
338 			return 0;
339 		case ZEND_VERIFY_RETURN_TYPE:
340 			/* This would require a non-local change.
341 			 * zend_optimizer_replace_by_const() supports this. */
342 			return 0;
343 		case ZEND_CASE:
344 		case ZEND_FETCH_LIST_R:
345 		case ZEND_COPY_TMP:
346 			return 0;
347 		case ZEND_CONCAT:
348 		case ZEND_FAST_CONCAT:
349 		case ZEND_FETCH_R:
350 		case ZEND_FETCH_W:
351 		case ZEND_FETCH_RW:
352 		case ZEND_FETCH_IS:
353 		case ZEND_FETCH_UNSET:
354 		case ZEND_FETCH_FUNC_ARG:
355 			TO_STRING_NOWARN(val);
356 			if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) {
357 				opline->opcode = ZEND_FAST_CONCAT;
358 			}
359 			/* break missing intentionally */
360 		default:
361 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
362 			break;
363 	}
364 
365 	opline->op1_type = IS_CONST;
366 	if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
367 		zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
368 	}
369 	return 1;
370 }
371 
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)372 int zend_optimizer_update_op2_const(zend_op_array *op_array,
373                                     zend_op       *opline,
374                                     zval          *val)
375 {
376 	zval tmp;
377 
378 	switch (opline->opcode) {
379 		case ZEND_ASSIGN_REF:
380 		case ZEND_FAST_CALL:
381 			return 0;
382 		case ZEND_FETCH_CLASS:
383 			if ((opline + 1)->opcode == ZEND_INSTANCEOF &&
384 				(opline + 1)->op2.var == opline->result.var) {
385 				return 0;
386 			}
387 			/* break missing intentionally */
388 		case ZEND_INSTANCEOF:
389 			REQUIRES_STRING(val);
390 			drop_leading_backslash(val);
391 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
392 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
393 			opline->extended_value = alloc_cache_slots(op_array, 1);
394 			break;
395 		case ZEND_INIT_FCALL_BY_NAME:
396 			REQUIRES_STRING(val);
397 			drop_leading_backslash(val);
398 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
399 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
400 			opline->result.num = alloc_cache_slots(op_array, 1);
401 			break;
402 		case ZEND_ASSIGN_STATIC_PROP:
403 		case ZEND_ASSIGN_STATIC_PROP_REF:
404 		case ZEND_FETCH_STATIC_PROP_R:
405 		case ZEND_FETCH_STATIC_PROP_W:
406 		case ZEND_FETCH_STATIC_PROP_RW:
407 		case ZEND_FETCH_STATIC_PROP_IS:
408 		case ZEND_FETCH_STATIC_PROP_UNSET:
409 		case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
410 		case ZEND_UNSET_STATIC_PROP:
411 		case ZEND_PRE_INC_STATIC_PROP:
412 		case ZEND_PRE_DEC_STATIC_PROP:
413 		case ZEND_POST_INC_STATIC_PROP:
414 		case ZEND_POST_DEC_STATIC_PROP:
415 		case ZEND_ASSIGN_STATIC_PROP_OP:
416 			REQUIRES_STRING(val);
417 			drop_leading_backslash(val);
418 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
419 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
420 			if (opline->op1_type != IS_CONST) {
421 				opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS));
422 			}
423 			break;
424 		case ZEND_INIT_FCALL:
425 			REQUIRES_STRING(val);
426 			if (Z_REFCOUNT_P(val) == 1) {
427 				zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
428 			} else {
429 				ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
430 				zval_ptr_dtor_nogc(val);
431 				val = &tmp;
432 			}
433 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
434 			opline->result.num = alloc_cache_slots(op_array, 1);
435 			break;
436 		case ZEND_INIT_DYNAMIC_CALL:
437 			if (Z_TYPE_P(val) == IS_STRING) {
438 				if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
439 					return 0;
440 				}
441 
442 				if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
443 					/* Dynamic call to various special functions must stay dynamic,
444 					 * otherwise would drop a warning */
445 					return 0;
446 				}
447 
448 				opline->opcode = ZEND_INIT_FCALL_BY_NAME;
449 				drop_leading_backslash(val);
450 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
451 				zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
452 				opline->result.num = alloc_cache_slots(op_array, 1);
453 			} else {
454 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
455 			}
456 			break;
457 		case ZEND_INIT_METHOD_CALL:
458 			REQUIRES_STRING(val);
459 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
460 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
461 			opline->result.num = alloc_cache_slots(op_array, 2);
462 			break;
463 		case ZEND_INIT_STATIC_METHOD_CALL:
464 			REQUIRES_STRING(val);
465 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
466 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
467 			if (opline->op1_type != IS_CONST) {
468 				opline->result.num = alloc_cache_slots(op_array, 2);
469 			}
470 			break;
471 		case ZEND_ASSIGN_OBJ:
472 		case ZEND_ASSIGN_OBJ_REF:
473 		case ZEND_FETCH_OBJ_R:
474 		case ZEND_FETCH_OBJ_W:
475 		case ZEND_FETCH_OBJ_RW:
476 		case ZEND_FETCH_OBJ_IS:
477 		case ZEND_FETCH_OBJ_UNSET:
478 		case ZEND_FETCH_OBJ_FUNC_ARG:
479 		case ZEND_UNSET_OBJ:
480 		case ZEND_PRE_INC_OBJ:
481 		case ZEND_PRE_DEC_OBJ:
482 		case ZEND_POST_INC_OBJ:
483 		case ZEND_POST_DEC_OBJ:
484 		case ZEND_ASSIGN_OBJ_OP:
485 			TO_STRING_NOWARN(val);
486 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
487 			opline->extended_value = alloc_cache_slots(op_array, 3);
488 			break;
489 		case ZEND_ISSET_ISEMPTY_PROP_OBJ:
490 			TO_STRING_NOWARN(val);
491 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
492 			opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY);
493 			break;
494 		case ZEND_ASSIGN_DIM_OP:
495 		case ZEND_ISSET_ISEMPTY_DIM_OBJ:
496 		case ZEND_ASSIGN_DIM:
497 		case ZEND_UNSET_DIM:
498 		case ZEND_FETCH_DIM_R:
499 		case ZEND_FETCH_DIM_W:
500 		case ZEND_FETCH_DIM_RW:
501 		case ZEND_FETCH_DIM_IS:
502 		case ZEND_FETCH_DIM_FUNC_ARG:
503 		case ZEND_FETCH_DIM_UNSET:
504 		case ZEND_FETCH_LIST_R:
505 		case ZEND_FETCH_LIST_W:
506 			if (Z_TYPE_P(val) == IS_STRING) {
507 				zend_ulong index;
508 
509 				if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
510 					ZVAL_LONG(&tmp, index);
511 					opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
512 					zend_string_hash_val(Z_STR_P(val));
513 					zend_optimizer_add_literal(op_array, val);
514 					Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE;
515 					break;
516 				}
517 			}
518 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
519 			break;
520 		case ZEND_ADD_ARRAY_ELEMENT:
521 		case ZEND_INIT_ARRAY:
522 			if (Z_TYPE_P(val) == IS_STRING) {
523 				zend_ulong index;
524 				if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
525 					zval_ptr_dtor_nogc(val);
526 					ZVAL_LONG(val, index);
527 				}
528 			}
529 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
530 			break;
531 		case ZEND_ROPE_INIT:
532 		case ZEND_ROPE_ADD:
533 		case ZEND_ROPE_END:
534 		case ZEND_CONCAT:
535 		case ZEND_FAST_CONCAT:
536 			TO_STRING_NOWARN(val);
537 			if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) {
538 				opline->opcode = ZEND_FAST_CONCAT;
539 			}
540 			/* break missing intentionally */
541 		default:
542 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
543 			break;
544 	}
545 
546 	opline->op2_type = IS_CONST;
547 	if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
548 		zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
549 	}
550 	return 1;
551 }
552 
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)553 int zend_optimizer_replace_by_const(zend_op_array *op_array,
554                                     zend_op       *opline,
555                                     zend_uchar     type,
556                                     uint32_t       var,
557                                     zval          *val)
558 {
559 	zend_op *end = op_array->opcodes + op_array->last;
560 
561 	while (opline < end) {
562 		if (opline->op1_type == type &&
563 			opline->op1.var == var) {
564 			switch (opline->opcode) {
565 				case ZEND_FETCH_DIM_W:
566 				case ZEND_FETCH_DIM_RW:
567 				case ZEND_FETCH_DIM_FUNC_ARG:
568 				case ZEND_FETCH_DIM_UNSET:
569 				case ZEND_FETCH_LIST_W:
570 				case ZEND_ASSIGN_DIM:
571 				case ZEND_SEPARATE:
572 				case ZEND_RETURN_BY_REF:
573 					return 0;
574 				case ZEND_SEND_VAR:
575 					opline->extended_value = 0;
576 					opline->opcode = ZEND_SEND_VAL;
577 					break;
578 				case ZEND_SEND_VAR_EX:
579 				case ZEND_SEND_FUNC_ARG:
580 					opline->extended_value = 0;
581 					opline->opcode = ZEND_SEND_VAL_EX;
582 					break;
583 				case ZEND_SEND_VAR_NO_REF:
584 					return 0;
585 				case ZEND_SEND_VAR_NO_REF_EX:
586 					opline->opcode = ZEND_SEND_VAL;
587 					break;
588 				case ZEND_SEND_USER:
589 					opline->opcode = ZEND_SEND_VAL_EX;
590 					break;
591 				/* In most cases IS_TMP_VAR operand may be used only once.
592 				 * The operands are usually destroyed by the opcode handler.
593 				 * ZEND_CASE and ZEND_FETCH_LIST_R are exceptions, they keeps operand
594 				 * unchanged, and allows its reuse. these instructions
595 				 * usually terminated by ZEND_FREE that finally kills the value.
596 				 */
597 				case ZEND_FETCH_LIST_R: {
598 					zend_op *m = opline;
599 
600 					do {
601 						if (m->opcode == ZEND_FETCH_LIST_R &&
602 							m->op1_type == type &&
603 							m->op1.var == var) {
604 							zval v;
605 							ZVAL_COPY(&v, val);
606 							if (Z_TYPE(v) == IS_STRING) {
607 								zend_string_hash_val(Z_STR(v));
608 							}
609 							m->op1.constant = zend_optimizer_add_literal(op_array, &v);
610 							m->op1_type = IS_CONST;
611 						}
612 						m++;
613 					} while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var);
614 
615 					ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
616 					MAKE_NOP(m);
617 					zval_ptr_dtor_nogc(val);
618 					return 1;
619 				}
620 				case ZEND_SWITCH_LONG:
621 				case ZEND_SWITCH_STRING:
622 				case ZEND_CASE: {
623 					zend_op *end = op_array->opcodes + op_array->last;
624 					while (opline < end) {
625 						if (opline->op1_type == type && opline->op1.var == var) {
626 							if (opline->opcode == ZEND_CASE
627 									|| opline->opcode == ZEND_SWITCH_LONG
628 									|| opline->opcode == ZEND_SWITCH_STRING) {
629 								zval v;
630 
631 								if (opline->opcode == ZEND_CASE) {
632 									opline->opcode = ZEND_IS_EQUAL;
633 								}
634 								ZVAL_COPY(&v, val);
635 								if (Z_TYPE(v) == IS_STRING) {
636 									zend_string_hash_val(Z_STR(v));
637 								}
638 								opline->op1.constant = zend_optimizer_add_literal(op_array, &v);
639 								opline->op1_type = IS_CONST;
640 							} else if (opline->opcode == ZEND_FREE) {
641 								if (opline->extended_value == ZEND_FREE_SWITCH) {
642 									/* We found the end of the switch. */
643 									MAKE_NOP(opline);
644 									break;
645 								}
646 
647 								ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN);
648 								MAKE_NOP(opline);
649 							} else {
650 								ZEND_ASSERT(0);
651 							}
652 						}
653 						opline++;
654 					}
655 					zval_ptr_dtor_nogc(val);
656 					return 1;
657 				}
658 				case ZEND_VERIFY_RETURN_TYPE: {
659 					zend_arg_info *ret_info = op_array->arg_info - 1;
660 					if (ZEND_TYPE_IS_CLASS(ret_info->type)
661 						|| ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE
662 						|| !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val))
663 						|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
664 						return 0;
665 					}
666 					MAKE_NOP(opline);
667 
668 					/* zend_handle_loops_and_finally may inserts other oplines */
669 					do {
670 						++opline;
671 					} while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
672 					ZEND_ASSERT(opline->op1.var == var);
673 
674 					break;
675 				  }
676 				default:
677 					break;
678 			}
679 			return zend_optimizer_update_op1_const(op_array, opline, val);
680 		}
681 
682 		if (opline->op2_type == type &&
683 			opline->op2.var == var) {
684 			return zend_optimizer_update_op2_const(op_array, opline, val);
685 		}
686 		opline++;
687 	}
688 
689 	return 1;
690 }
691 
692 /* Update jump offsets after a jump was migrated to another opline */
zend_optimizer_migrate_jump(zend_op_array * op_array,zend_op * new_opline,zend_op * opline)693 void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
694 	switch (new_opline->opcode) {
695 		case ZEND_JMP:
696 		case ZEND_FAST_CALL:
697 			ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
698 			break;
699 		case ZEND_JMPZNZ:
700 			new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
701 			/* break missing intentionally */
702 		case ZEND_JMPZ:
703 		case ZEND_JMPNZ:
704 		case ZEND_JMPZ_EX:
705 		case ZEND_JMPNZ_EX:
706 		case ZEND_FE_RESET_R:
707 		case ZEND_FE_RESET_RW:
708 		case ZEND_JMP_SET:
709 		case ZEND_COALESCE:
710 		case ZEND_ASSERT_CHECK:
711 			ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
712 			break;
713 		case ZEND_FE_FETCH_R:
714 		case ZEND_FE_FETCH_RW:
715 			new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
716 			break;
717 		case ZEND_CATCH:
718 			if (!(opline->extended_value & ZEND_LAST_CATCH)) {
719 				ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
720 			}
721 			break;
722 		case ZEND_SWITCH_LONG:
723 		case ZEND_SWITCH_STRING:
724 		{
725 			HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
726 			zval *zv;
727 			ZEND_HASH_FOREACH_VAL(jumptable, zv) {
728 				Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
729 			} ZEND_HASH_FOREACH_END();
730 			new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
731 			break;
732 		}
733 	}
734 }
735 
736 /* Shift jump offsets based on shiftlist */
zend_optimizer_shift_jump(zend_op_array * op_array,zend_op * opline,uint32_t * shiftlist)737 void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
738 	switch (opline->opcode) {
739 		case ZEND_JMP:
740 		case ZEND_FAST_CALL:
741 			ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
742 			break;
743 		case ZEND_JMPZNZ:
744 			opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
745 			/* break missing intentionally */
746 		case ZEND_JMPZ:
747 		case ZEND_JMPNZ:
748 		case ZEND_JMPZ_EX:
749 		case ZEND_JMPNZ_EX:
750 		case ZEND_FE_RESET_R:
751 		case ZEND_FE_RESET_RW:
752 		case ZEND_JMP_SET:
753 		case ZEND_COALESCE:
754 		case ZEND_ASSERT_CHECK:
755 			ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
756 			break;
757 		case ZEND_CATCH:
758 			if (!(opline->extended_value & ZEND_LAST_CATCH)) {
759 				ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
760 			}
761 			break;
762 		case ZEND_FE_FETCH_R:
763 		case ZEND_FE_FETCH_RW:
764 			opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
765 			break;
766 		case ZEND_SWITCH_LONG:
767 		case ZEND_SWITCH_STRING:
768 		{
769 			HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
770 			zval *zv;
771 			ZEND_HASH_FOREACH_VAL(jumptable, zv) {
772 				Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
773 			} ZEND_HASH_FOREACH_END();
774 			opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
775 			break;
776 		}
777 	}
778 }
779 
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)780 static zend_class_entry *get_class_entry_from_op1(
781 		zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
782 	if (opline->op1_type == IS_CONST) {
783 		zval *op1 = CRT_CONSTANT_EX(op_array, opline, opline->op1, rt_constants);
784 		if (Z_TYPE_P(op1) == IS_STRING) {
785 			zend_string *class_name = Z_STR_P(op1 + 1);
786 			zend_class_entry *ce;
787 			if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
788 				return ce;
789 			} else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
790 				if (ce->type == ZEND_INTERNAL_CLASS) {
791 					return ce;
792 				} else if (ce->type == ZEND_USER_CLASS &&
793 						   ce->info.user.filename &&
794 						   ce->info.user.filename == op_array->filename) {
795 					return ce;
796 				}
797 			}
798 		}
799 	} else if (opline->op1_type == IS_UNUSED && op_array->scope
800 			&& !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
801 			&& (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
802 		return op_array->scope;
803 	}
804 	return NULL;
805 }
806 
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)807 zend_function *zend_optimizer_get_called_func(
808 		zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
809 {
810 #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline, opline->op, rt_constants)
811 	switch (opline->opcode) {
812 		case ZEND_INIT_FCALL:
813 		{
814 			zend_string *function_name = Z_STR_P(GET_OP(op2));
815 			zend_function *func;
816 			if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
817 				return func;
818 			} else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
819 				if (func->type == ZEND_INTERNAL_FUNCTION) {
820 					return func;
821 				} else if (func->type == ZEND_USER_FUNCTION &&
822 				           func->op_array.filename &&
823 				           func->op_array.filename == op_array->filename) {
824 					return func;
825 				}
826 			}
827 			break;
828 		}
829 		case ZEND_INIT_FCALL_BY_NAME:
830 		case ZEND_INIT_NS_FCALL_BY_NAME:
831 			if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
832 				zval *function_name = GET_OP(op2) + 1;
833 				zend_function *func;
834 				if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
835 					return func;
836 				} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
837 					if (func->type == ZEND_INTERNAL_FUNCTION) {
838 						return func;
839 					} else if (func->type == ZEND_USER_FUNCTION &&
840 					           func->op_array.filename &&
841 					           func->op_array.filename == op_array->filename) {
842 						return func;
843 					}
844 				}
845 			}
846 			break;
847 		case ZEND_INIT_STATIC_METHOD_CALL:
848 			if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
849 				zend_class_entry *ce = get_class_entry_from_op1(
850 					script, op_array, opline, rt_constants);
851 				if (ce) {
852 					zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
853 					zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name);
854 					if (fbc) {
855 						zend_bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0;
856 						zend_bool same_scope = fbc->common.scope == op_array->scope;
857 						if (is_public|| same_scope) {
858 							return fbc;
859 						}
860 					}
861 				}
862 			}
863 			break;
864 		case ZEND_INIT_METHOD_CALL:
865 			if (opline->op1_type == IS_UNUSED
866 					&& opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
867 					&& op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
868 				zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
869 				zend_function *fbc = zend_hash_find_ptr(
870 					&op_array->scope->function_table, method_name);
871 				if (fbc) {
872 					zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
873 					zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
874 					zend_bool same_scope = fbc->common.scope == op_array->scope;
875 					if ((is_private && same_scope)
876 							|| (is_final && (!is_private || same_scope))) {
877 						return fbc;
878 					}
879 				}
880 			}
881 			break;
882 		case ZEND_NEW:
883 		{
884 			zend_class_entry *ce = get_class_entry_from_op1(
885 				script, op_array, opline, rt_constants);
886 			if (ce && ce->type == ZEND_USER_CLASS) {
887 				return ce->constructor;
888 			}
889 			break;
890 		}
891 	}
892 	return NULL;
893 #undef GET_OP
894 }
895 
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)896 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
897 	if (zend_string_equals_literal(name, "extract")) {
898 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
899 	} else if (zend_string_equals_literal(name, "compact")) {
900 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
901 	} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
902 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
903 	} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
904 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
905 	} else if (zend_string_equals_literal(name, "get_defined_vars")) {
906 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
907 	} else if (zend_string_equals_literal(name, "assert")) {
908 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
909 	} else if (zend_string_equals_literal(name, "db2_execute")) {
910 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
911 	} else if (zend_string_equals_literal(name, "func_num_args")) {
912 		return ZEND_FUNC_VARARG;
913 	} else if (zend_string_equals_literal(name, "func_get_arg")) {
914 		return ZEND_FUNC_VARARG;
915 	} else if (zend_string_equals_literal(name, "func_get_args")) {
916 		return ZEND_FUNC_VARARG;
917 	} else {
918 		return 0;
919 	}
920 }
921 
zend_optimizer_get_loop_var_def(const zend_op_array * op_array,zend_op * free_opline)922 zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) {
923 	uint32_t var = free_opline->op1.var;
924 	ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline));
925 
926 	while (--free_opline >= op_array->opcodes) {
927 		if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) {
928 			return free_opline;
929 		}
930 	}
931 	return NULL;
932 }
933 
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)934 static void zend_optimize(zend_op_array      *op_array,
935                           zend_optimizer_ctx *ctx)
936 {
937 	if (op_array->type == ZEND_EVAL_CODE) {
938 		return;
939 	}
940 
941 	if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
942 		zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
943 	}
944 
945 	/* pass 1
946 	 * - substitute persistent constants (true, false, null, etc)
947 	 * - perform compile-time evaluation of constant binary and unary operations
948 	 * - optimize series of ADD_STRING and/or ADD_CHAR
949 	 * - convert CAST(IS_BOOL,x) into BOOL(x)
950          * - pre-evaluate constant function calls
951 	 */
952 	if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
953 		zend_optimizer_pass1(op_array, ctx);
954 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
955 			zend_dump_op_array(op_array, 0, "after pass 1", NULL);
956 		}
957 	}
958 
959 	/* pass 2:
960 	 * - convert non-numeric constants to numeric constants in numeric operators
961 	 * - optimize constant conditional JMPs
962 	 */
963 	if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
964 		zend_optimizer_pass2(op_array);
965 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
966 			zend_dump_op_array(op_array, 0, "after pass 2", NULL);
967 		}
968 	}
969 
970 	/* pass 3:
971 	 * - optimize $i = $i+expr to $i+=expr
972 	 * - optimize series of JMPs
973 	 * - change $i++ to ++$i where possible
974 	 */
975 	if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
976 		zend_optimizer_pass3(op_array, ctx);
977 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
978 			zend_dump_op_array(op_array, 0, "after pass 3", NULL);
979 		}
980 	}
981 
982 	/* pass 4:
983 	 * - INIT_FCALL_BY_NAME -> DO_FCALL
984 	 */
985 	if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
986 		zend_optimize_func_calls(op_array, ctx);
987 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
988 			zend_dump_op_array(op_array, 0, "after pass 4", NULL);
989 		}
990 	}
991 
992 	/* pass 5:
993 	 * - CFG optimization
994 	 */
995 	if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
996 		zend_optimize_cfg(op_array, ctx);
997 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
998 			zend_dump_op_array(op_array, 0, "after pass 5", NULL);
999 		}
1000 	}
1001 
1002 #if HAVE_DFA_PASS
1003 	/* pass 6:
1004 	 * - DFA optimization
1005 	 */
1006 	if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
1007 	    !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
1008 		zend_optimize_dfa(op_array, ctx);
1009 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
1010 			zend_dump_op_array(op_array, 0, "after pass 6", NULL);
1011 		}
1012 	}
1013 #endif
1014 
1015 	/* pass 9:
1016 	 * - Optimize temp variables usage
1017 	 */
1018 	if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
1019 		zend_optimize_temporary_variables(op_array, ctx);
1020 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
1021 			zend_dump_op_array(op_array, 0, "after pass 9", NULL);
1022 		}
1023 	}
1024 
1025 	/* pass 10:
1026 	 * - remove NOPs
1027 	 */
1028 	if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
1029 		zend_optimizer_nop_removal(op_array, ctx);
1030 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
1031 			zend_dump_op_array(op_array, 0, "after pass 10", NULL);
1032 		}
1033 	}
1034 
1035 	/* pass 11:
1036 	 * - Compact literals table
1037 	 */
1038 	if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
1039 	    (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1040 	     !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1041 		zend_optimizer_compact_literals(op_array, ctx);
1042 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
1043 			zend_dump_op_array(op_array, 0, "after pass 11", NULL);
1044 		}
1045 	}
1046 
1047 	if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
1048 	    (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
1049 	     !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
1050 		zend_optimizer_compact_vars(op_array);
1051 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
1052 			zend_dump_op_array(op_array, 0, "after pass 13", NULL);
1053 		}
1054 	}
1055 
1056 	if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
1057 		return;
1058 	}
1059 
1060 	if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
1061 		zend_dump_op_array(op_array, 0, "after optimizer", NULL);
1062 	}
1063 }
1064 
zend_revert_pass_two(zend_op_array * op_array)1065 static void zend_revert_pass_two(zend_op_array *op_array)
1066 {
1067 	zend_op *opline, *end;
1068 
1069 	opline = op_array->opcodes;
1070 	end = opline + op_array->last;
1071 	while (opline < end) {
1072 		if (opline->op1_type == IS_CONST) {
1073 			ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
1074 		}
1075 		if (opline->op2_type == IS_CONST) {
1076 			ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2);
1077 		}
1078 		opline++;
1079 	}
1080 #if !ZEND_USE_ABS_CONST_ADDR
1081 	if (op_array->literals) {
1082 		zval *literals = emalloc(sizeof(zval) * op_array->last_literal);
1083 		memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal);
1084 		op_array->literals = literals;
1085 	}
1086 #endif
1087 }
1088 
zend_redo_pass_two(zend_op_array * op_array)1089 static void zend_redo_pass_two(zend_op_array *op_array)
1090 {
1091 	zend_op *opline, *end;
1092 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1093 	zend_op *old_opcodes = op_array->opcodes;
1094 #endif
1095 
1096 #if !ZEND_USE_ABS_CONST_ADDR
1097 	if (op_array->last_literal) {
1098 		op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1099 			ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1100 			sizeof(zval) * op_array->last_literal);
1101 		memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1102 			op_array->literals, sizeof(zval) * op_array->last_literal);
1103 		efree(op_array->literals);
1104 		op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1105 	} else {
1106 		if (op_array->literals) {
1107 			efree(op_array->literals);
1108 		}
1109 		op_array->literals = NULL;
1110 	}
1111 #endif
1112 
1113 	opline = op_array->opcodes;
1114 	end = opline + op_array->last;
1115 	while (opline < end) {
1116 		if (opline->op1_type == IS_CONST) {
1117 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1118 		}
1119 		if (opline->op2_type == IS_CONST) {
1120 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1121 		}
1122 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1123 		if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
1124 			/* fix jumps to point to new array */
1125 			switch (opline->opcode) {
1126 				case ZEND_JMP:
1127 				case ZEND_FAST_CALL:
1128 					opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1129 					break;
1130 				case ZEND_JMPZNZ:
1131 					/* relative extended_value don't have to be changed */
1132 					/* break omitted intentionally */
1133 				case ZEND_JMPZ:
1134 				case ZEND_JMPNZ:
1135 				case ZEND_JMPZ_EX:
1136 				case ZEND_JMPNZ_EX:
1137 				case ZEND_JMP_SET:
1138 				case ZEND_COALESCE:
1139 				case ZEND_FE_RESET_R:
1140 				case ZEND_FE_RESET_RW:
1141 				case ZEND_ASSERT_CHECK:
1142 					opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1143 					break;
1144 				case ZEND_CATCH:
1145 					if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1146 						opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1147 					}
1148 					break;
1149 				case ZEND_FE_FETCH_R:
1150 				case ZEND_FE_FETCH_RW:
1151 				case ZEND_SWITCH_LONG:
1152 				case ZEND_SWITCH_STRING:
1153 					/* relative extended_value don't have to be changed */
1154 					break;
1155 			}
1156 		}
1157 #endif
1158 		ZEND_VM_SET_OPCODE_HANDLER(opline);
1159 		opline++;
1160 	}
1161 }
1162 
1163 #if HAVE_DFA_PASS
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)1164 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
1165 {
1166 	zend_op *opline, *end;
1167 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1168 	zend_op *old_opcodes = op_array->opcodes;
1169 #endif
1170 
1171 #if !ZEND_USE_ABS_CONST_ADDR
1172 	if (op_array->last_literal) {
1173 		op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1174 			ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1175 			sizeof(zval) * op_array->last_literal);
1176 		memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1177 			op_array->literals, sizeof(zval) * op_array->last_literal);
1178 		efree(op_array->literals);
1179 		op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1180 	} else {
1181 		if (op_array->literals) {
1182 			efree(op_array->literals);
1183 		}
1184 		op_array->literals = NULL;
1185 	}
1186 #endif
1187 
1188 	opline = op_array->opcodes;
1189 	end = opline + op_array->last;
1190 	while (opline < end) {
1191 		uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
1192 		uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
1193 		uint32_t res_info =
1194 			(opline->opcode == ZEND_PRE_INC ||
1195 			 opline->opcode == ZEND_PRE_DEC ||
1196 			 opline->opcode == ZEND_POST_INC ||
1197 			 opline->opcode == ZEND_POST_DEC) ?
1198 				((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
1199 				(opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)));
1200 
1201 		if (opline->op1_type == IS_CONST) {
1202 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1203 		}
1204 		if (opline->op2_type == IS_CONST) {
1205 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1206 		}
1207 
1208 		zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
1209 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1210 		if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
1211 			/* fix jumps to point to new array */
1212 			switch (opline->opcode) {
1213 				case ZEND_JMP:
1214 				case ZEND_FAST_CALL:
1215 					opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1216 					break;
1217 				case ZEND_JMPZNZ:
1218 					/* relative extended_value don't have to be changed */
1219 					/* break omitted intentionally */
1220 				case ZEND_JMPZ:
1221 				case ZEND_JMPNZ:
1222 				case ZEND_JMPZ_EX:
1223 				case ZEND_JMPNZ_EX:
1224 				case ZEND_JMP_SET:
1225 				case ZEND_COALESCE:
1226 				case ZEND_FE_RESET_R:
1227 				case ZEND_FE_RESET_RW:
1228 				case ZEND_ASSERT_CHECK:
1229 					opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1230 					break;
1231 				case ZEND_CATCH:
1232 					if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1233 						opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1234 					}
1235 					break;
1236 				case ZEND_FE_FETCH_R:
1237 				case ZEND_FE_FETCH_RW:
1238 				case ZEND_SWITCH_LONG:
1239 				case ZEND_SWITCH_STRING:
1240 					/* relative extended_value don't have to be changed */
1241 					break;
1242 			}
1243 		}
1244 #endif
1245 		opline++;
1246 	}
1247 }
1248 #endif
1249 
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)1250 static void zend_optimize_op_array(zend_op_array      *op_array,
1251                                    zend_optimizer_ctx *ctx)
1252 {
1253 	/* Revert pass_two() */
1254 	zend_revert_pass_two(op_array);
1255 
1256 	/* Do actual optimizations */
1257 	zend_optimize(op_array, ctx);
1258 
1259 	/* Redo pass_two() */
1260 	zend_redo_pass_two(op_array);
1261 
1262 	if (op_array->live_range) {
1263 #if HAVE_DFA_PASS
1264 		if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
1265 		    (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
1266 			return;
1267 		}
1268 #endif
1269 		zend_recalc_live_ranges(op_array, NULL);
1270 	}
1271 }
1272 
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)1273 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1274 {
1275 	zend_function *func;
1276 	zend_op *opline, *end;
1277 
1278 	opline = op_array->opcodes;
1279 	end = opline + op_array->last;
1280 	while (opline < end) {
1281 		if (opline->opcode == ZEND_INIT_FCALL) {
1282 			func = zend_hash_find_ptr(
1283 				&ctx->script->function_table,
1284 				Z_STR_P(RT_CONSTANT(opline, opline->op2)));
1285 			if (func) {
1286 				opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
1287 			}
1288 		}
1289 		opline++;
1290 	}
1291 }
1292 
1293 #if HAVE_DFA_PASS
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)1294 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
1295 {
1296 	zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1297 
1298 	if (func_info) {
1299 		zend_call_info *call_info =func_info->callee_info;
1300 
1301 		while (call_info) {
1302 			zend_op *opline = call_info->caller_init_opline;
1303 
1304 			if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
1305 				opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
1306 			}
1307 			call_info = call_info->next_callee;
1308 		}
1309 	}
1310 }
1311 
needs_live_range(zend_op_array * op_array,zend_op * def_opline)1312 static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) {
1313 	zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1314 	zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes];
1315 	int ssa_var = ssa_op->result_def;
1316 	if (ssa_var < 0) {
1317 		/* Be conservative. */
1318 		return 1;
1319 	}
1320 
1321 	/* If the variable is used by a PHI, this may be the assignment of the final branch of a
1322 	 * ternary/etc structure. While this is where the live range starts, the value from the other
1323 	 * branch may also be used. As such, use the type of the PHI node for the following check. */
1324 	if (func_info->ssa.vars[ssa_var].phi_use_chain) {
1325 		ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var;
1326 	}
1327 
1328 	uint32_t type = func_info->ssa.var_info[ssa_var].type;
1329 	return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
1330 }
1331 #endif
1332 
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)1333 int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
1334 {
1335 	zend_class_entry *ce;
1336 	zend_string *key;
1337 	zend_op_array *op_array;
1338 	zend_string *name;
1339 	zend_optimizer_ctx ctx;
1340 #if HAVE_DFA_PASS
1341 	zend_call_graph call_graph;
1342 #endif
1343 
1344 	ctx.arena = zend_arena_create(64 * 1024);
1345 	ctx.script = script;
1346 	ctx.constants = NULL;
1347 	ctx.optimization_level = optimization_level;
1348 	ctx.debug_level = debug_level;
1349 
1350 	zend_optimize_op_array(&script->main_op_array, &ctx);
1351 
1352 	ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1353 		zend_optimize_op_array(op_array, &ctx);
1354 	} ZEND_HASH_FOREACH_END();
1355 
1356 	ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1357 		if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1358 			continue;
1359 		}
1360 		ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1361 			if (op_array->scope == ce
1362 			 && op_array->type == ZEND_USER_FUNCTION
1363 			 && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1364 				zend_optimize_op_array(op_array, &ctx);
1365 			}
1366 		} ZEND_HASH_FOREACH_END();
1367 	} ZEND_HASH_FOREACH_END();
1368 
1369 #if HAVE_DFA_PASS
1370 	if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
1371 	    (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
1372 	    zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
1373 		/* Optimize using call-graph */
1374 		void *checkpoint = zend_arena_checkpoint(ctx.arena);
1375 		int i;
1376 		zend_func_info *func_info;
1377 
1378 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1379 			zend_revert_pass_two(call_graph.op_arrays[i]);
1380 		}
1381 
1382 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1383 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1384 			if (func_info) {
1385 				func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
1386 				if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1387 					zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
1388 				}
1389 			}
1390 		}
1391 
1392 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1393 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1394 			if (func_info) {
1395 				if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
1396 					func_info->flags = func_info->ssa.cfg.flags;
1397 				} else {
1398 					ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1399 				}
1400 			}
1401 		}
1402 
1403 		//TODO: perform inner-script inference???
1404 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1405 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1406 			if (func_info) {
1407 				zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
1408 			}
1409 		}
1410 
1411 		if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1412 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1413 				zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1414 			}
1415 		}
1416 
1417 		if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
1418 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1419 				zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
1420 				if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
1421 					zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
1422 				}
1423 			}
1424 		}
1425 
1426 		if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
1427 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1428 				zend_optimizer_compact_vars(call_graph.op_arrays[i]);
1429 				if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
1430 					zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
1431 				}
1432 			}
1433 		}
1434 
1435 		if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1436 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1437 				zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1438 			}
1439 		}
1440 
1441 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1442 			op_array = call_graph.op_arrays[i];
1443 			func_info = ZEND_FUNC_INFO(op_array);
1444 			if (func_info && func_info->ssa.var_info) {
1445 				zend_redo_pass_two_ex(op_array, &func_info->ssa);
1446 				if (op_array->live_range) {
1447 					zend_recalc_live_ranges(op_array, needs_live_range);
1448 				}
1449 			} else {
1450 				zend_redo_pass_two(op_array);
1451 				if (op_array->live_range) {
1452 					zend_recalc_live_ranges(op_array, NULL);
1453 				}
1454 			}
1455 		}
1456 
1457 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1458 			ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1459 		}
1460 
1461 		zend_arena_release(&ctx.arena, checkpoint);
1462 	} else
1463 #endif
1464 
1465 	if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1466 		zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
1467 
1468 		ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1469 			zend_adjust_fcall_stack_size(op_array, &ctx);
1470 		} ZEND_HASH_FOREACH_END();
1471 
1472 		ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1473 			if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1474 				continue;
1475 			}
1476 			ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1477 				if (op_array->scope == ce
1478 				 && op_array->type == ZEND_USER_FUNCTION
1479 				 && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1480 					zend_adjust_fcall_stack_size(op_array, &ctx);
1481 				}
1482 			} ZEND_HASH_FOREACH_END();
1483 		} ZEND_HASH_FOREACH_END();
1484 	}
1485 
1486 	ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1487 		if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1488 			continue;
1489 		}
1490 		ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1491 			if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
1492 				zend_op_array *orig_op_array =
1493 					zend_hash_find_ptr(&op_array->scope->function_table, name);
1494 
1495 				ZEND_ASSERT(orig_op_array != NULL);
1496 				if (orig_op_array != op_array) {
1497 					uint32_t fn_flags = op_array->fn_flags;
1498 					zend_function *prototype = op_array->prototype;
1499 					HashTable *ht = op_array->static_variables;
1500 
1501 					*op_array = *orig_op_array;
1502 					op_array->fn_flags = fn_flags;
1503 					op_array->prototype = prototype;
1504 					op_array->static_variables = ht;
1505 				}
1506 			}
1507 		} ZEND_HASH_FOREACH_END();
1508 	} ZEND_HASH_FOREACH_END();
1509 
1510 	if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
1511 	    (ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
1512 		zend_dump_op_array(&script->main_op_array,
1513 			ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
1514 
1515 		ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1516 			zend_dump_op_array(op_array,
1517 				ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
1518 		} ZEND_HASH_FOREACH_END();
1519 
1520 		ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1521 			ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1522 				if (op_array->scope == ce
1523 				 && op_array->type == ZEND_USER_FUNCTION
1524 				 && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1525 					zend_dump_op_array(op_array,
1526 						ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
1527 				}
1528 			} ZEND_HASH_FOREACH_END();
1529 		} ZEND_HASH_FOREACH_END();
1530 	}
1531 
1532 	if (ctx.constants) {
1533 		zend_hash_destroy(ctx.constants);
1534 	}
1535 	zend_arena_destroy(ctx.arena);
1536 
1537 	return 1;
1538 }
1539 
zend_optimizer_startup(void)1540 int zend_optimizer_startup(void)
1541 {
1542 	return zend_func_info_startup();
1543 }
1544 
zend_optimizer_shutdown(void)1545 int zend_optimizer_shutdown(void)
1546 {
1547 	return zend_func_info_shutdown();
1548 }
1549