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