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