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