xref: /PHP-8.1/Zend/Optimizer/block_pass.c (revision 19dfe05f)
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_bitset.h"
29 #include "zend_cfg.h"
30 #include "zend_dump.h"
31 
32 /* Checks if a constant (like "true") may be replaced by its value */
zend_optimizer_get_persistent_constant(zend_string * name,zval * result,int copy)33 int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
34 {
35 	zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
36 	if (c) {
37 		if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
38 		 && !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
39 		 && (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
40 		  || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
41 			ZVAL_COPY_VALUE(result, &c->value);
42 			if (copy) {
43 				Z_TRY_ADDREF_P(result);
44 			}
45 			return 1;
46 		} else {
47 			return 0;
48 		}
49 	}
50 
51 	/* Special constants null/true/false can always be substituted. */
52 	c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
53 	if (c) {
54 		ZVAL_COPY_VALUE(result, &c->value);
55 		return 1;
56 	}
57 	return 0;
58 }
59 
60 /* Data dependencies macros */
61 
62 #define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
63 #define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
64 
strip_leading_nops(zend_op_array * op_array,zend_basic_block * b)65 static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
66 {
67 	zend_op *opcodes = op_array->opcodes;
68 
69 	do {
70 		b->start++;
71 		b->len--;
72 	} while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP);
73 }
74 
strip_nops(zend_op_array * op_array,zend_basic_block * b)75 static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
76 {
77 	uint32_t i, j;
78 
79 	if (b->len == 0) {
80 		return;
81 	}
82 
83 	if (op_array->opcodes[b->start].opcode == ZEND_NOP) {
84 		strip_leading_nops(op_array, b);
85 	}
86 
87 	if (b->len == 0) {
88 		return;
89 	}
90 
91 	/* strip the inside NOPs */
92 	i = j = b->start + 1;
93 	while (i < b->start + b->len) {
94 		if (op_array->opcodes[i].opcode != ZEND_NOP) {
95 			if (i != j) {
96 				op_array->opcodes[j] = op_array->opcodes[i];
97 			}
98 			j++;
99 		}
100 		i++;
101 	}
102 	b->len = j - b->start;
103 	while (j < i) {
104 		MAKE_NOP(op_array->opcodes + j);
105 		j++;
106 	}
107 }
108 
get_const_switch_target(zend_cfg * cfg,zend_op_array * op_array,zend_basic_block * block,zend_op * opline,zval * val)109 static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) {
110 	HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
111 	zval *zv;
112 	if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
113 			|| (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) {
114 		/* fallback to next block */
115 		return block->successors[block->successors_count - 1];
116 	}
117 	if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) {
118 		/* always jump to the default arm */
119 		return block->successors[block->successors_count - 1];
120 	}
121 	if (Z_TYPE_P(val) == IS_LONG) {
122 		zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
123 	} else {
124 		ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING);
125 		zv = zend_hash_find(jumptable, Z_STR_P(val));
126 	}
127 	if (!zv) {
128 		/* default */
129 		return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)];
130 	}
131 	return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
132 }
133 
zend_optimize_block(zend_basic_block * block,zend_op_array * op_array,zend_bitset used_ext,zend_cfg * cfg,zend_op ** Tsource,uint32_t * opt_count)134 static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count)
135 {
136 	zend_op *opline, *src;
137 	zend_op *end, *last_op = NULL;
138 
139 	if (block->len == 0) {
140 		return;
141 	}
142 
143 	if (op_array->opcodes[block->start].opcode == ZEND_NOP) {
144 		/* remove leading NOPs */
145 		strip_leading_nops(op_array, block);
146 	}
147 
148 	opline = op_array->opcodes + block->start;
149 	end = opline + block->len;
150 	while (opline < end) {
151 		/* Constant Propagation: strip X = QM_ASSIGN(const) */
152 		if (opline->op1_type == IS_TMP_VAR &&
153 		    opline->opcode != ZEND_FREE) {
154 			src = VAR_SOURCE(opline->op1);
155 			if (src &&
156 			    src->opcode == ZEND_QM_ASSIGN &&
157 			    src->op1_type == IS_CONST
158 			) {
159 				znode_op op1 = opline->op1;
160 				if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
161 					COPY_NODE(opline->result, opline->op1);
162 					COPY_NODE(opline->op1, src->op1);
163 					VAR_SOURCE(op1) = NULL;
164 					MAKE_NOP(src);
165 					++(*opt_count);
166 				} else {
167 					zval c;
168 					ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
169 					if (opline->opcode != ZEND_CASE
170 					 && opline->opcode != ZEND_CASE_STRICT
171 					 && opline->opcode != ZEND_FETCH_LIST_R
172 					 && opline->opcode != ZEND_SWITCH_LONG
173 					 && opline->opcode != ZEND_SWITCH_STRING
174 					 && opline->opcode != ZEND_MATCH
175 					 && opline->opcode != ZEND_MATCH_ERROR
176 					 && zend_optimizer_update_op1_const(op_array, opline, &c)) {
177 						VAR_SOURCE(op1) = NULL;
178 						if (opline->opcode != ZEND_JMP_NULL
179 						 && !zend_bitset_in(used_ext, VAR_NUM(src->result.var))) {
180 							literal_dtor(&ZEND_OP1_LITERAL(src));
181 							MAKE_NOP(src);
182 						}
183 						++(*opt_count);
184 					} else {
185 						zval_ptr_dtor_nogc(&c);
186 					}
187 				}
188 			}
189 		}
190 
191 		/* Constant Propagation: strip X = QM_ASSIGN(const) */
192 		if (opline->op2_type == IS_TMP_VAR) {
193 			src = VAR_SOURCE(opline->op2);
194 			if (src &&
195 			    src->opcode == ZEND_QM_ASSIGN &&
196 			    src->op1_type == IS_CONST) {
197 
198 				znode_op op2 = opline->op2;
199 				zval c;
200 
201 				ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
202 				if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
203 					VAR_SOURCE(op2) = NULL;
204 					if (!zend_bitset_in(used_ext, VAR_NUM(src->result.var))) {
205 						literal_dtor(&ZEND_OP1_LITERAL(src));
206 						MAKE_NOP(src);
207 					}
208 					++(*opt_count);
209 				} else {
210 					zval_ptr_dtor_nogc(&c);
211 				}
212 			}
213 		}
214 
215 		switch (opline->opcode) {
216 			case ZEND_ECHO:
217 				if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
218 					src = VAR_SOURCE(opline->op1);
219 					if (src &&
220 					    src->opcode == ZEND_CAST &&
221 					    src->extended_value == IS_STRING) {
222 						/* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
223 						VAR_SOURCE(opline->op1) = NULL;
224 						COPY_NODE(opline->op1, src->op1);
225 						MAKE_NOP(src);
226 						++(*opt_count);
227 					}
228 				} else if (opline->op1_type == IS_CONST &&
229 				           Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) {
230 					if (last_op == opline - 1) {
231 						/* compress consecutive ECHO's.
232 						 * Float to string conversion may be affected by current
233 						 * locale setting.
234 						 */
235 						int l, old_len;
236 
237 						if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
238 							convert_to_string(&ZEND_OP1_LITERAL(opline));
239 						}
240 						if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
241 							convert_to_string(&ZEND_OP1_LITERAL(last_op));
242 						}
243 						old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
244 						l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
245 						if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
246 							zend_string *tmp = zend_string_alloc(l, 0);
247 							memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
248 							Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
249 						} else {
250 							Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
251 						}
252 						Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
253 						memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
254 						Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
255 						zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
256 						ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
257 						ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
258 						MAKE_NOP(last_op);
259 						++(*opt_count);
260 					}
261 					last_op = opline;
262 				}
263 				break;
264 
265 			case ZEND_FREE:
266 				/* Note: Only remove the source if the source is local to this block.
267 				 * If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
268 				 * hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
269 				 * A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
270 				if (opline->op1_type == IS_TMP_VAR) {
271 					src = VAR_SOURCE(opline->op1);
272 					if (src) {
273 						switch (src->opcode) {
274 							case ZEND_BOOL:
275 							case ZEND_BOOL_NOT:
276 								/* T = BOOL(X), FREE(T) => T = BOOL(X) */
277 								/* The remaining BOOL is removed by a separate optimization */
278 								/* The source is a bool, no source removals take place, so this may be done non-locally. */
279 								VAR_SOURCE(opline->op1) = NULL;
280 								MAKE_NOP(opline);
281 								++(*opt_count);
282 								break;
283 							case ZEND_ASSIGN:
284 							case ZEND_ASSIGN_DIM:
285 							case ZEND_ASSIGN_OBJ:
286 							case ZEND_ASSIGN_STATIC_PROP:
287 							case ZEND_ASSIGN_OP:
288 							case ZEND_ASSIGN_DIM_OP:
289 							case ZEND_ASSIGN_OBJ_OP:
290 							case ZEND_ASSIGN_STATIC_PROP_OP:
291 							case ZEND_PRE_INC:
292 							case ZEND_PRE_DEC:
293 							case ZEND_PRE_INC_OBJ:
294 							case ZEND_PRE_DEC_OBJ:
295 							case ZEND_PRE_INC_STATIC_PROP:
296 							case ZEND_PRE_DEC_STATIC_PROP:
297 								if (src < op_array->opcodes + block->start) {
298 									break;
299 								}
300 								src->result_type = IS_UNUSED;
301 								VAR_SOURCE(opline->op1) = NULL;
302 								MAKE_NOP(opline);
303 								++(*opt_count);
304 								break;
305 							default:
306 								break;
307 						}
308 					}
309 				} else if (opline->op1_type == IS_VAR) {
310 					src = VAR_SOURCE(opline->op1);
311 					/* V = OP, FREE(V) => OP. NOP */
312 					if (src >= op_array->opcodes + block->start &&
313 					    src->opcode != ZEND_FETCH_R &&
314 					    src->opcode != ZEND_FETCH_STATIC_PROP_R &&
315 					    src->opcode != ZEND_FETCH_DIM_R &&
316 					    src->opcode != ZEND_FETCH_OBJ_R &&
317 					    src->opcode != ZEND_NEW &&
318 					    src->opcode != ZEND_FETCH_THIS) {
319 						src->result_type = IS_UNUSED;
320 						MAKE_NOP(opline);
321 						++(*opt_count);
322 						if (src->opcode == ZEND_QM_ASSIGN) {
323 							if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
324 								src->opcode = ZEND_FREE;
325 							} else {
326 								MAKE_NOP(src);
327 							}
328 						}
329 					}
330 				}
331 				break;
332 
333 #if 0
334 		/* pre-evaluate functions:
335 		   constant(x)
336 		   function_exists(x)
337 		   extension_loaded(x)
338 		   BAD: interacts badly with Accelerator
339 		*/
340 		if((opline->op1_type & IS_VAR) &&
341 		   VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
342 		   VAR_SOURCE(opline->op1)->extended_value == 1) {
343 			zend_op *fcall = VAR_SOURCE(opline->op1);
344 			zend_op *sv = fcall-1;
345 			if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
346 			   sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
347 			   Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
348 			   ) {
349 				zval *arg = &OPLINE_OP1_LITERAL(sv);
350 				char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
351 				int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
352 				if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
353 						  (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
354 						  ) {
355 					zend_function *function;
356 					if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
357 						literal_dtor(arg);
358 						MAKE_NOP(sv);
359 						MAKE_NOP(fcall);
360 						LITERAL_BOOL(opline->op1, 1);
361 						opline->op1_type = IS_CONST;
362 					}
363 				} else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
364 					zval c;
365 					if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) {
366 						literal_dtor(arg);
367 						MAKE_NOP(sv);
368 						MAKE_NOP(fcall);
369 						ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
370 						/* no copy ctor - get already copied it */
371 						opline->op1_type = IS_CONST;
372 					}
373 				} else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
374 					if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
375 						literal_dtor(arg);
376 						MAKE_NOP(sv);
377 						MAKE_NOP(fcall);
378 						LITERAL_BOOL(opline->op1, 1);
379 						opline->op1_type = IS_CONST;
380 					}
381 				}
382 			}
383 		}
384 #endif
385 
386 			case ZEND_FETCH_LIST_R:
387 			case ZEND_FETCH_LIST_W:
388 				if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
389 					/* LIST variable will be deleted later by FREE */
390 					Tsource[VAR_NUM(opline->op1.var)] = NULL;
391 				}
392 				break;
393 
394 			case ZEND_SWITCH_LONG:
395 			case ZEND_SWITCH_STRING:
396 			case ZEND_MATCH:
397 				if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
398 					/* SWITCH variable will be deleted later by FREE, so we can't optimize it */
399 					Tsource[VAR_NUM(opline->op1.var)] = NULL;
400 					break;
401 				}
402 				if (opline->op1_type == IS_CONST) {
403 					int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
404 					literal_dtor(&ZEND_OP1_LITERAL(opline));
405 					literal_dtor(&ZEND_OP2_LITERAL(opline));
406 					opline->opcode = ZEND_JMP;
407 					opline->op1_type = IS_UNUSED;
408 					opline->op2_type = IS_UNUSED;
409 					block->successors_count = 1;
410 					block->successors[0] = target;
411 				}
412 				break;
413 
414 			case ZEND_CASE:
415 			case ZEND_CASE_STRICT:
416 			case ZEND_COPY_TMP:
417 				if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
418 					/* Variable will be deleted later by FREE, so we can't optimize it */
419 					Tsource[VAR_NUM(opline->op1.var)] = NULL;
420 					break;
421 				}
422 				ZEND_FALLTHROUGH;
423 
424 			case ZEND_IS_EQUAL:
425 			case ZEND_IS_NOT_EQUAL:
426 				if (opline->op1_type == IS_CONST &&
427 				    opline->op2_type == IS_CONST) {
428 					goto optimize_constant_binary_op;
429 				}
430 		        /* IS_EQ(TRUE, X)      => BOOL(X)
431 		         * IS_EQ(FALSE, X)     => BOOL_NOT(X)
432 		         * IS_NOT_EQ(TRUE, X)  => BOOL_NOT(X)
433 		         * IS_NOT_EQ(FALSE, X) => BOOL(X)
434 		         * CASE(TRUE, X)       => BOOL(X)
435 		         * CASE(FALSE, X)      => BOOL_NOT(X)
436 		         */
437 				if (opline->op1_type == IS_CONST &&
438 					(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
439 					 Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
440 					/* Optimization of comparison with "null" is not safe,
441 					 * because ("0" == null) is not equal to !("0")
442 					 */
443 					opline->opcode =
444 						((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
445 						ZEND_BOOL : ZEND_BOOL_NOT;
446 					COPY_NODE(opline->op1, opline->op2);
447 					SET_UNUSED(opline->op2);
448 					++(*opt_count);
449 					goto optimize_bool;
450 				} else if (opline->op2_type == IS_CONST &&
451 				           (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
452 				            Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
453 					/* Optimization of comparison with "null" is not safe,
454 					 * because ("0" == null) is not equal to !("0")
455 					 */
456 					opline->opcode =
457 						((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
458 						ZEND_BOOL : ZEND_BOOL_NOT;
459 					SET_UNUSED(opline->op2);
460 					++(*opt_count);
461 					goto optimize_bool;
462 				}
463 				break;
464 
465 			case ZEND_BOOL:
466 			case ZEND_BOOL_NOT:
467 			optimize_bool:
468 				if (opline->op1_type == IS_CONST) {
469 					goto optimize_const_unary_op;
470 				}
471 				if (opline->op1_type == IS_TMP_VAR &&
472 				    !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
473 					src = VAR_SOURCE(opline->op1);
474 					if (src) {
475 						switch (src->opcode) {
476 							case ZEND_BOOL_NOT:
477 								/* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
478 								VAR_SOURCE(opline->op1) = NULL;
479 								COPY_NODE(opline->op1, src->op1);
480 								opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
481 								MAKE_NOP(src);
482 								++(*opt_count);
483 								goto optimize_bool;
484 							case ZEND_BOOL:
485 								/* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
486 								VAR_SOURCE(opline->op1) = NULL;
487 								COPY_NODE(opline->op1, src->op1);
488 								MAKE_NOP(src);
489 								++(*opt_count);
490 								goto optimize_bool;
491 							case ZEND_IS_EQUAL:
492 								if (opline->opcode == ZEND_BOOL_NOT) {
493 									src->opcode = ZEND_IS_NOT_EQUAL;
494 								}
495 								COPY_NODE(src->result, opline->result);
496 								SET_VAR_SOURCE(src);
497 								MAKE_NOP(opline);
498 								++(*opt_count);
499 								break;
500 							case ZEND_IS_NOT_EQUAL:
501 								if (opline->opcode == ZEND_BOOL_NOT) {
502 									src->opcode = ZEND_IS_EQUAL;
503 								}
504 								COPY_NODE(src->result, opline->result);
505 								SET_VAR_SOURCE(src);
506 								MAKE_NOP(opline);
507 								++(*opt_count);
508 								break;
509 							case ZEND_IS_IDENTICAL:
510 								if (opline->opcode == ZEND_BOOL_NOT) {
511 									src->opcode = ZEND_IS_NOT_IDENTICAL;
512 								}
513 								COPY_NODE(src->result, opline->result);
514 								SET_VAR_SOURCE(src);
515 								MAKE_NOP(opline);
516 								++(*opt_count);
517 								break;
518 							case ZEND_IS_NOT_IDENTICAL:
519 								if (opline->opcode == ZEND_BOOL_NOT) {
520 									src->opcode = ZEND_IS_IDENTICAL;
521 								}
522 								COPY_NODE(src->result, opline->result);
523 								SET_VAR_SOURCE(src);
524 								MAKE_NOP(opline);
525 								++(*opt_count);
526 								break;
527 							case ZEND_IS_SMALLER:
528 								if (opline->opcode == ZEND_BOOL_NOT) {
529 									zend_uchar tmp_type;
530 									uint32_t tmp;
531 
532 									src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
533 									tmp_type = src->op1_type;
534 									src->op1_type = src->op2_type;
535 									src->op2_type = tmp_type;
536 									tmp = src->op1.num;
537 									src->op1.num = src->op2.num;
538 									src->op2.num = tmp;
539 								}
540 								COPY_NODE(src->result, opline->result);
541 								SET_VAR_SOURCE(src);
542 								MAKE_NOP(opline);
543 								++(*opt_count);
544 								break;
545 							case ZEND_IS_SMALLER_OR_EQUAL:
546 								if (opline->opcode == ZEND_BOOL_NOT) {
547 									zend_uchar tmp_type;
548 									uint32_t tmp;
549 
550 									src->opcode = ZEND_IS_SMALLER;
551 									tmp_type = src->op1_type;
552 									src->op1_type = src->op2_type;
553 									src->op2_type = tmp_type;
554 									tmp = src->op1.num;
555 									src->op1.num = src->op2.num;
556 									src->op2.num = tmp;
557 								}
558 								COPY_NODE(src->result, opline->result);
559 								SET_VAR_SOURCE(src);
560 								MAKE_NOP(opline);
561 								++(*opt_count);
562 								break;
563 							case ZEND_ISSET_ISEMPTY_CV:
564 							case ZEND_ISSET_ISEMPTY_VAR:
565 							case ZEND_ISSET_ISEMPTY_DIM_OBJ:
566 							case ZEND_ISSET_ISEMPTY_PROP_OBJ:
567 							case ZEND_ISSET_ISEMPTY_STATIC_PROP:
568 							case ZEND_INSTANCEOF:
569 							case ZEND_TYPE_CHECK:
570 							case ZEND_DEFINED:
571 							case ZEND_IN_ARRAY:
572 							case ZEND_ARRAY_KEY_EXISTS:
573 								if (opline->opcode == ZEND_BOOL_NOT) {
574 									break;
575 								}
576 								COPY_NODE(src->result, opline->result);
577 								SET_VAR_SOURCE(src);
578 								MAKE_NOP(opline);
579 								++(*opt_count);
580 								break;
581 						}
582 					}
583 				}
584 				break;
585 
586 			case ZEND_JMPZ:
587 			case ZEND_JMPNZ:
588 			    while (1) {
589 					if (opline->op1_type == IS_CONST) {
590 						++(*opt_count);
591 						block->successors_count = 1;
592 						if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
593 						    (opline->opcode == ZEND_JMPZ)) {
594 
595 							MAKE_NOP(opline);
596 							block->successors[0] = block->successors[1];
597 							block->len--;
598 							cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
599 							break;
600 						} else {
601 							zend_basic_block *next = cfg->blocks + block->successors[1];
602 
603 							next->flags &= ~ZEND_BB_FOLLOW;
604 							if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
605 								next->flags &= ~ZEND_BB_REACHABLE;
606 							}
607 							opline->opcode = ZEND_JMP;
608 							COPY_NODE(opline->op1, opline->op2);
609 							break;
610 						}
611 					} else if (opline->op1_type == IS_TMP_VAR &&
612 					           !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
613 						src = VAR_SOURCE(opline->op1);
614 						if (src) {
615 							if (src->opcode == ZEND_BOOL_NOT) {
616 								VAR_SOURCE(opline->op1) = NULL;
617 								COPY_NODE(opline->op1, src->op1);
618 								/* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
619 								opline->opcode = INV_COND(opline->opcode);
620 								MAKE_NOP(src);
621 								++(*opt_count);
622 								continue;
623 							} else if (src->opcode == ZEND_BOOL ||
624 							           src->opcode == ZEND_QM_ASSIGN) {
625 								VAR_SOURCE(opline->op1) = NULL;
626 								COPY_NODE(opline->op1, src->op1);
627 								MAKE_NOP(src);
628 								++(*opt_count);
629 								continue;
630 							}
631 						}
632 					}
633 					break;
634 				}
635 				break;
636 
637 			case ZEND_JMPZNZ:
638 				while (1) {
639 					if (opline->op1_type == IS_CONST) {
640 						++(*opt_count);
641 						if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
642 							zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
643 							ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
644 							block->successors[0] = block->successors[1];
645 						} else {
646 							zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
647 							ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
648 						}
649 						block->successors_count = 1;
650 						opline->op1_type = IS_UNUSED;
651 						opline->extended_value = 0;
652 						opline->opcode = ZEND_JMP;
653 						break;
654 					} else if (opline->op1_type == IS_TMP_VAR &&
655 					           !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
656 						src = VAR_SOURCE(opline->op1);
657 						if (src) {
658 							if (src->opcode == ZEND_BOOL_NOT) {
659 								/* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
660 								uint32_t tmp;
661 
662 								VAR_SOURCE(opline->op1) = NULL;
663 								COPY_NODE(opline->op1, src->op1);
664 								tmp = block->successors[0];
665 								block->successors[0] = block->successors[1];
666 								block->successors[1] = tmp;
667 								MAKE_NOP(src);
668 								++(*opt_count);
669 								continue;
670 							} else if (src->opcode == ZEND_BOOL ||
671 							           src->opcode == ZEND_QM_ASSIGN) {
672 								VAR_SOURCE(opline->op1) = NULL;
673 								COPY_NODE(opline->op1, src->op1);
674 								MAKE_NOP(src);
675 								++(*opt_count);
676 								continue;
677 							}
678 						}
679 					}
680 					break;
681 				}
682 				break;
683 
684 			case ZEND_JMPZ_EX:
685 			case ZEND_JMPNZ_EX:
686 				while (1) {
687 					if (opline->op1_type == IS_CONST) {
688 						bool is_jmpz_ex = opline->opcode == ZEND_JMPZ_EX;
689 						if (zend_is_true(&ZEND_OP1_LITERAL(opline)) == is_jmpz_ex) {
690 
691 							++(*opt_count);
692 							opline->opcode = ZEND_QM_ASSIGN;
693 							zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
694 							ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), is_jmpz_ex);
695 							opline->op2.num = 0;
696 							block->successors_count = 1;
697 							block->successors[0] = block->successors[1];
698 							cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
699 							break;
700 						}
701 					} else if (opline->op1_type == IS_TMP_VAR &&
702 					           (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
703 					            opline->result.var == opline->op1.var)) {
704 						src = VAR_SOURCE(opline->op1);
705 						if (src) {
706 							if (src->opcode == ZEND_BOOL ||
707 							    src->opcode == ZEND_QM_ASSIGN) {
708 								VAR_SOURCE(opline->op1) = NULL;
709 								COPY_NODE(opline->op1, src->op1);
710 								MAKE_NOP(src);
711 								++(*opt_count);
712 								continue;
713 							}
714 						}
715 					}
716 					break;
717 				}
718 				break;
719 
720 			case ZEND_CONCAT:
721 			case ZEND_FAST_CONCAT:
722 				if (opline->op1_type == IS_CONST &&
723 				    opline->op2_type == IS_CONST) {
724 					goto optimize_constant_binary_op;
725 				}
726 
727 				if (opline->op2_type == IS_CONST &&
728 				    opline->op1_type == IS_TMP_VAR) {
729 
730 					src = VAR_SOURCE(opline->op1);
731 				    if (src &&
732 					    (src->opcode == ZEND_CONCAT ||
733 					     src->opcode == ZEND_FAST_CONCAT) &&
734 					    src->op2_type == IS_CONST) {
735 						/* compress consecutive CONCATs */
736 						int l, old_len;
737 
738 						if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
739 							convert_to_string(&ZEND_OP2_LITERAL(opline));
740 						}
741 						if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
742 							convert_to_string(&ZEND_OP2_LITERAL(src));
743 						}
744 
745 						VAR_SOURCE(opline->op1) = NULL;
746 						COPY_NODE(opline->op1, src->op1);
747 						old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
748 						l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
749 						if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
750 							zend_string *tmp = zend_string_alloc(l, 0);
751 							memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
752 							Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
753 						} else {
754 							Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
755 						}
756 						Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
757 						memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
758 						Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
759 						zval_ptr_dtor_str(&ZEND_OP2_LITERAL(opline));
760 						ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
761 						ZVAL_NULL(&ZEND_OP2_LITERAL(src));
762 						MAKE_NOP(src);
763 						++(*opt_count);
764 					}
765 				}
766 
767 				if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
768 					src = VAR_SOURCE(opline->op1);
769 					if (src &&
770 					    src->opcode == ZEND_CAST &&
771 					    src->extended_value == IS_STRING &&
772 					    src->op1_type != IS_CONST) {
773 						/* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
774 						VAR_SOURCE(opline->op1) = NULL;
775 						COPY_NODE(opline->op1, src->op1);
776 						MAKE_NOP(src);
777 						++(*opt_count);
778 					}
779 	            }
780 				if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
781 					src = VAR_SOURCE(opline->op2);
782 					if (src &&
783 					    src->opcode == ZEND_CAST &&
784 					    src->extended_value == IS_STRING &&
785 					    src->op1_type != IS_CONST) {
786 						/* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
787 						zend_op *src = VAR_SOURCE(opline->op2);
788 						VAR_SOURCE(opline->op2) = NULL;
789 						COPY_NODE(opline->op2, src->op1);
790 						MAKE_NOP(src);
791 						++(*opt_count);
792 					}
793 				}
794 				if (opline->op1_type == IS_CONST &&
795 				    Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
796 				    Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
797 					/* convert CONCAT('', X) => CAST(STRING, X) */
798 					literal_dtor(&ZEND_OP1_LITERAL(opline));
799 					opline->opcode = ZEND_CAST;
800 					opline->extended_value = IS_STRING;
801 					COPY_NODE(opline->op1, opline->op2);
802 					opline->op2_type = IS_UNUSED;
803 					opline->op2.var = 0;
804 					++(*opt_count);
805 				} else if (opline->op2_type == IS_CONST &&
806 			           Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
807 			           Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
808 					/* convert CONCAT(X, '') => CAST(STRING, X) */
809 					literal_dtor(&ZEND_OP2_LITERAL(opline));
810 					opline->opcode = ZEND_CAST;
811 					opline->extended_value = IS_STRING;
812 					opline->op2_type = IS_UNUSED;
813 					opline->op2.var = 0;
814 					++(*opt_count);
815 				} else if (opline->opcode == ZEND_CONCAT &&
816 				           (opline->op1_type == IS_CONST ||
817 				            (opline->op1_type == IS_TMP_VAR &&
818 				             VAR_SOURCE(opline->op1) &&
819 				             (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
820 				              VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
821 				              VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
822 				              VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
823 				           (opline->op2_type == IS_CONST ||
824 				            (opline->op2_type == IS_TMP_VAR &&
825 				             VAR_SOURCE(opline->op2) &&
826 				             (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
827 				              VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
828 				              VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
829 				              VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
830 					opline->opcode = ZEND_FAST_CONCAT;
831 					++(*opt_count);
832 				}
833 				break;
834 
835 			case ZEND_ADD:
836 			case ZEND_SUB:
837 			case ZEND_MUL:
838 			case ZEND_DIV:
839 			case ZEND_MOD:
840 			case ZEND_SL:
841 			case ZEND_SR:
842 			case ZEND_IS_SMALLER:
843 			case ZEND_IS_SMALLER_OR_EQUAL:
844 			case ZEND_IS_IDENTICAL:
845 			case ZEND_IS_NOT_IDENTICAL:
846 			case ZEND_BOOL_XOR:
847 			case ZEND_BW_OR:
848 			case ZEND_BW_AND:
849 			case ZEND_BW_XOR:
850 				if (opline->op1_type == IS_CONST &&
851 				    opline->op2_type == IS_CONST) {
852 					/* evaluate constant expressions */
853 					zval result;
854 
855 optimize_constant_binary_op:
856 					if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
857 						literal_dtor(&ZEND_OP1_LITERAL(opline));
858 						literal_dtor(&ZEND_OP2_LITERAL(opline));
859 						opline->opcode = ZEND_QM_ASSIGN;
860 						SET_UNUSED(opline->op2);
861 						zend_optimizer_update_op1_const(op_array, opline, &result);
862 						++(*opt_count);
863 					}
864 				}
865 				break;
866 
867 			case ZEND_BW_NOT:
868 				if (opline->op1_type == IS_CONST) {
869 					/* evaluate constant unary ops */
870 					zval result;
871 
872 optimize_const_unary_op:
873 					if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
874 						literal_dtor(&ZEND_OP1_LITERAL(opline));
875 						opline->opcode = ZEND_QM_ASSIGN;
876 						zend_optimizer_update_op1_const(op_array, opline, &result);
877 						++(*opt_count);
878 					}
879 				}
880 				break;
881 
882 			case ZEND_CAST:
883 				if (opline->op1_type == IS_CONST) {
884 					/* cast of constant operand */
885 					zval result;
886 
887 					if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
888 						literal_dtor(&ZEND_OP1_LITERAL(opline));
889 						opline->opcode = ZEND_QM_ASSIGN;
890 						opline->extended_value = 0;
891 						zend_optimizer_update_op1_const(op_array, opline, &result);
892 						++(*opt_count);
893 					}
894 				}
895 				break;
896 
897 			case ZEND_STRLEN:
898 				if (opline->op1_type == IS_CONST) {
899 					zval result;
900 
901 					if (zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
902 						literal_dtor(&ZEND_OP1_LITERAL(opline));
903 						opline->opcode = ZEND_QM_ASSIGN;
904 						zend_optimizer_update_op1_const(op_array, opline, &result);
905 						++(*opt_count);
906 					}
907 				}
908 				break;
909 
910 			case ZEND_RETURN:
911 			case ZEND_EXIT:
912 				if (opline->op1_type == IS_TMP_VAR) {
913 					src = VAR_SOURCE(opline->op1);
914 					if (src && src->opcode == ZEND_QM_ASSIGN) {
915 						zend_op *op = src + 1;
916 						bool optimize = 1;
917 
918 						while (op < opline) {
919 							if ((op->op1_type == opline->op1_type
920 							  && op->op1.var == opline->op1.var)
921 							 || (op->op2_type == opline->op1_type
922 							  && op->op2.var == opline->op1.var)) {
923 								optimize = 0;
924 								break;
925 							}
926 							op++;
927 						}
928 
929 						if (optimize) {
930 							/* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
931 							VAR_SOURCE(opline->op1) = NULL;
932 							COPY_NODE(opline->op1, src->op1);
933 							MAKE_NOP(src);
934 							++(*opt_count);
935 						}
936 					}
937 				}
938 				break;
939 
940 			case ZEND_QM_ASSIGN:
941 				if (opline->op1_type == opline->result_type &&
942 				    opline->op1.var == opline->result.var) {
943 					/* strip T = QM_ASSIGN(T) */
944 					MAKE_NOP(opline);
945 					++(*opt_count);
946 				} else if (opline->op1_type == IS_TMP_VAR &&
947 				           opline->result_type == IS_TMP_VAR &&
948 				           !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
949 					/* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */
950 					src = VAR_SOURCE(opline->op1);
951 					if (src &&
952 						src->opcode != ZEND_COPY_TMP &&
953 						src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
954 						src->opcode != ZEND_ADD_ARRAY_UNPACK &&
955 						(src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
956 						 src == opline -1)) {
957 						src->result.var = opline->result.var;
958 						VAR_SOURCE(opline->op1) = NULL;
959 						VAR_SOURCE(opline->result) = src;
960 						MAKE_NOP(opline);
961 						++(*opt_count);
962 					}
963 				}
964 				break;
965 		}
966 
967 		/* get variable source */
968 		if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
969 			SET_VAR_SOURCE(opline);
970 		}
971 		opline++;
972 	}
973 }
974 
975 /* Rebuild plain (optimized) op_array from CFG */
assemble_code_blocks(zend_cfg * cfg,zend_op_array * op_array,zend_optimizer_ctx * ctx)976 static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
977 {
978 	zend_basic_block *blocks = cfg->blocks;
979 	zend_basic_block *end = blocks + cfg->blocks_count;
980 	zend_basic_block *b;
981 	zend_op *new_opcodes;
982 	zend_op *opline;
983 	uint32_t len = 0;
984 	int n;
985 
986 	for (b = blocks; b < end; b++) {
987 		if (b->len == 0) {
988 			continue;
989 		}
990 		if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
991 			opline = op_array->opcodes + b->start + b->len - 1;
992 			if (opline->opcode == ZEND_JMP) {
993 				zend_basic_block *next = b + 1;
994 
995 				while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
996 					next++;
997 				}
998 				if (next < end && next == blocks + b->successors[0]) {
999 					/* JMP to the next block - strip it */
1000 					MAKE_NOP(opline);
1001 					b->len--;
1002 				}
1003 			} else if (b->len == 1 && opline->opcode == ZEND_NOP) {
1004 				/* skip empty block */
1005 				b->len--;
1006 			}
1007 			len += b->len;
1008 		} else {
1009 			/* this block will not be used, delete all constants there */
1010 			zend_op *op = op_array->opcodes + b->start;
1011 			zend_op *end = op + b->len;
1012 			for (; op < end; op++) {
1013 				if (op->op1_type == IS_CONST) {
1014 					literal_dtor(&ZEND_OP1_LITERAL(op));
1015 				}
1016 				if (op->op2_type == IS_CONST) {
1017 					literal_dtor(&ZEND_OP2_LITERAL(op));
1018 				}
1019 			}
1020 		}
1021 	}
1022 
1023 	new_opcodes = emalloc(len * sizeof(zend_op));
1024 	opline = new_opcodes;
1025 
1026 	/* Copy code of reachable blocks into a single buffer */
1027 	for (b = blocks; b < end; b++) {
1028 		if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1029 			memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
1030 			b->start = opline - new_opcodes;
1031 			opline += b->len;
1032 		}
1033 	}
1034 
1035 	/* adjust jump targets */
1036 	efree(op_array->opcodes);
1037 	op_array->opcodes = new_opcodes;
1038 	op_array->last = len;
1039 
1040 	for (b = blocks; b < end; b++) {
1041 		if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
1042 			continue;
1043 		}
1044 		opline = op_array->opcodes + b->start + b->len - 1;
1045 		switch (opline->opcode) {
1046 			case ZEND_FAST_CALL:
1047 			case ZEND_JMP:
1048 				ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
1049 				break;
1050 			case ZEND_JMPZNZ:
1051 				opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
1052 				ZEND_FALLTHROUGH;
1053 			case ZEND_JMPZ:
1054 			case ZEND_JMPNZ:
1055 			case ZEND_JMPZ_EX:
1056 			case ZEND_JMPNZ_EX:
1057 			case ZEND_FE_RESET_R:
1058 			case ZEND_FE_RESET_RW:
1059 			case ZEND_JMP_SET:
1060 			case ZEND_COALESCE:
1061 			case ZEND_ASSERT_CHECK:
1062 			case ZEND_JMP_NULL:
1063 				ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
1064 				break;
1065 			case ZEND_CATCH:
1066 				if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1067 					ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
1068 				}
1069 				break;
1070 			case ZEND_FE_FETCH_R:
1071 			case ZEND_FE_FETCH_RW:
1072 				opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
1073 				break;
1074 			case ZEND_SWITCH_LONG:
1075 			case ZEND_SWITCH_STRING:
1076 			case ZEND_MATCH:
1077 			{
1078 				HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
1079 				zval *zv;
1080 				uint32_t s = 0;
1081 				ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
1082 
1083 				ZEND_HASH_FOREACH_VAL(jumptable, zv) {
1084 					Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
1085 				} ZEND_HASH_FOREACH_END();
1086 				opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
1087 				break;
1088 			}
1089 		}
1090 	}
1091 
1092 	/* adjust exception jump targets & remove unused try_catch_array entries */
1093 	if (op_array->last_try_catch) {
1094 		int i, j;
1095 		uint32_t *map;
1096 		ALLOCA_FLAG(use_heap);
1097 
1098 		map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
1099 		for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
1100 			if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
1101 				map[i] = j;
1102 				op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
1103 				if (op_array->try_catch_array[i].catch_op) {
1104 					op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
1105 				} else {
1106 					op_array->try_catch_array[j].catch_op =  0;
1107 				}
1108 				if (op_array->try_catch_array[i].finally_op) {
1109 					op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
1110 				} else {
1111 					op_array->try_catch_array[j].finally_op =  0;
1112 				}
1113 				if (!op_array->try_catch_array[i].finally_end) {
1114 					op_array->try_catch_array[j].finally_end = 0;
1115 				} else {
1116 					op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
1117 				}
1118 				j++;
1119 			}
1120 		}
1121 		if (i != j) {
1122 			op_array->last_try_catch = j;
1123 			if (j == 0) {
1124 				efree(op_array->try_catch_array);
1125 				op_array->try_catch_array = NULL;
1126 			}
1127 
1128 			if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1129 				zend_op *opline = new_opcodes;
1130 				zend_op *end = opline + len;
1131 				while (opline < end) {
1132 					if (opline->opcode == ZEND_FAST_RET &&
1133 					    opline->op2.num != (uint32_t)-1 &&
1134 					    opline->op2.num < (uint32_t)j) {
1135 						opline->op2.num = map[opline->op2.num];
1136 					}
1137 					opline++;
1138 				}
1139 			}
1140 		}
1141 		free_alloca(map, use_heap);
1142 	}
1143 
1144 	/* adjust early binding list */
1145 	if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
1146 		ZEND_ASSERT(op_array == &ctx->script->main_op_array);
1147 		ctx->script->first_early_binding_opline =
1148 			zend_build_delayed_early_binding_list(op_array);
1149 	}
1150 
1151 	/* rebuild map (just for printing) */
1152 	memset(cfg->map, -1, sizeof(int) * op_array->last);
1153 	for (n = 0; n < cfg->blocks_count; n++) {
1154 		if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1155 			cfg->map[cfg->blocks[n].start] = n;
1156 		}
1157 	}
1158 }
1159 
get_target_block(const zend_cfg * cfg,zend_basic_block * block,int n,uint32_t * opt_count)1160 static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
1161 {
1162 	int b;
1163 	zend_basic_block *target_block = cfg->blocks + block->successors[n];
1164 
1165 	if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
1166 		do {
1167 			b = target_block->successors[0];
1168 			target_block = cfg->blocks + b;
1169 		} while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
1170 		block->successors[n] = b;
1171 		++(*opt_count);
1172 	}
1173 	return target_block;
1174 }
1175 
get_follow_block(const zend_cfg * cfg,zend_basic_block * block,int n,uint32_t * opt_count)1176 static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count)
1177 {
1178 	int b;
1179 	zend_basic_block *target_block = cfg->blocks + block->successors[n];
1180 
1181 	if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
1182 		do {
1183 			b = target_block->successors[0];
1184 			target_block = cfg->blocks + b;
1185 		} while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
1186 		block->successors[n] = b;
1187 		++(*opt_count);
1188 	}
1189 	return target_block;
1190 }
1191 
get_next_block(const zend_cfg * cfg,zend_basic_block * block)1192 static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
1193 {
1194 	zend_basic_block *next_block = block + 1;
1195 	zend_basic_block *end = cfg->blocks + cfg->blocks_count;
1196 
1197 	while (1) {
1198 		if (next_block == end) {
1199 			return NULL;
1200 		} else if (next_block->flags & ZEND_BB_REACHABLE) {
1201 			break;
1202 		}
1203 		next_block++;
1204 	}
1205 	while (next_block->len == 0 && !(next_block->flags & ZEND_BB_PROTECTED)) {
1206 		next_block = cfg->blocks + next_block->successors[0];
1207 	}
1208 	return next_block;
1209 }
1210 
1211 
1212 /* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
in_hitlist(int target,int * jmp_hitlist,int jmp_hitlist_count)1213 static zend_always_inline int in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count)
1214 {
1215 	int i;
1216 
1217 	for (i = 0; i < jmp_hitlist_count; i++) {
1218 		if (jmp_hitlist[i] == target) {
1219 			return 1;
1220 		}
1221 	}
1222 	return 0;
1223 }
1224 
1225 #define CHECK_LOOP(target) \
1226 	if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
1227 		jmp_hitlist[jmp_hitlist_count++] = target;	\
1228 	} else { \
1229 		break; \
1230 	}
1231 
zend_jmp_optimization(zend_basic_block * block,zend_op_array * op_array,const zend_cfg * cfg,int * jmp_hitlist,uint32_t * opt_count)1232 static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count)
1233 {
1234 	/* last_op is the last opcode of the current block */
1235 	zend_basic_block *target_block, *follow_block, *next_block;
1236 	zend_op *last_op, *target;
1237 	int next, jmp_hitlist_count;
1238 
1239 	if (block->len == 0) {
1240 		return;
1241 	}
1242 
1243 	last_op = op_array->opcodes + block->start + block->len - 1;
1244 	switch (last_op->opcode) {
1245 		case ZEND_JMP:
1246 			jmp_hitlist_count = 0;
1247 
1248 			target_block = get_target_block(cfg, block, 0, opt_count);
1249 			while (target_block->len == 1) {
1250 				target = op_array->opcodes + target_block->start;
1251 				if (target->opcode == ZEND_JMP) {
1252 					/* JMP L, L: JMP L1 -> JMP L1 */
1253 					next = target_block->successors[0];
1254 				} else {
1255 					break;
1256 				}
1257 				CHECK_LOOP(next);
1258 				block->successors[0] = next;
1259 				++(*opt_count);
1260 				target_block = get_target_block(cfg, block, 0, opt_count);
1261 			}
1262 
1263 			next_block = get_next_block(cfg, block);
1264 			if (target_block == next_block) {
1265 				/* JMP(next) -> NOP */
1266 				MAKE_NOP(last_op);
1267 				++(*opt_count);
1268 				block->len--;
1269 			} else if (target_block->len == 1) {
1270 				target = op_array->opcodes + target_block->start;
1271 				if (target->opcode == ZEND_JMPZNZ) {
1272 					/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
1273 					*last_op = *target;
1274 					if (last_op->op1_type == IS_CONST) {
1275 						zval zv;
1276 						ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
1277 						last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
1278 					}
1279 					block->successors_count = 2;
1280 					block->successors[0] = target_block->successors[0];
1281 					block->successors[1] = target_block->successors[1];
1282 					++(*opt_count);
1283 					goto optimize_jmpznz;
1284 				} else if ((target->opcode == ZEND_RETURN ||
1285 				            target->opcode == ZEND_RETURN_BY_REF ||
1286 				            target->opcode == ZEND_GENERATOR_RETURN ||
1287 				            target->opcode == ZEND_EXIT) &&
1288 				           !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
1289 					/* JMP L, L: RETURN to immediate RETURN */
1290 					*last_op = *target;
1291 					if (last_op->op1_type == IS_CONST) {
1292 						zval zv;
1293 						ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
1294 						last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
1295 					}
1296 					block->successors_count = 0;
1297 					++(*opt_count);
1298 				}
1299 			}
1300 			break;
1301 
1302 		case ZEND_JMP_SET:
1303 		case ZEND_COALESCE:
1304 		case ZEND_JMP_NULL:
1305 			jmp_hitlist_count = 0;
1306 
1307 			target_block = get_target_block(cfg, block, 0, opt_count);
1308 			while (target_block->len == 1) {
1309 				target = op_array->opcodes + target_block->start;
1310 
1311 				if (target->opcode == ZEND_JMP) {
1312 					/* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */
1313 					next = target_block->successors[0];
1314 					CHECK_LOOP(next);
1315 					block->successors[0] = next;
1316 					++(*opt_count);
1317 				} else {
1318 					break;
1319 				}
1320 				target_block = get_target_block(cfg, block, 0, opt_count);
1321 			}
1322 			break;
1323 
1324 		case ZEND_JMPZ:
1325 		case ZEND_JMPNZ:
1326 			jmp_hitlist_count = 0;
1327 
1328 			target_block = get_target_block(cfg, block, 0, opt_count);
1329 			while (target_block->len == 1) {
1330 				target = op_array->opcodes + target_block->start;
1331 
1332 				if (target->opcode == ZEND_JMP) {
1333 					/* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
1334 					next = target_block->successors[0];
1335 				} else if (target->opcode == last_op->opcode &&
1336 				           SAME_VAR(target->op1, last_op->op1)) {
1337 					/* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
1338 					next = target_block->successors[0];
1339 				} else if (target->opcode == INV_COND(last_op->opcode) &&
1340 				           SAME_VAR(target->op1, last_op->op1)) {
1341 					/* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
1342 					next = target_block->successors[1];
1343 				} else if (target->opcode == ZEND_JMPZNZ &&
1344 				           SAME_VAR(target->op1, last_op->op1)) {
1345 					/* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
1346 					next = target_block->successors[last_op->opcode == ZEND_JMPNZ];
1347 				} else {
1348 					break;
1349 				}
1350 				CHECK_LOOP(next);
1351 				block->successors[0] = next;
1352 				++(*opt_count);
1353 				target_block = get_target_block(cfg, block, 0, opt_count);
1354 			}
1355 
1356 			follow_block = get_follow_block(cfg, block, 1, opt_count);
1357 			if (target_block == follow_block) {
1358 				/* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
1359 				if (last_op->op1_type == IS_CV) {
1360 					last_op->opcode = ZEND_CHECK_VAR;
1361 					last_op->op2.num = 0;
1362 				} else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
1363 					last_op->opcode = ZEND_FREE;
1364 					last_op->op2.num = 0;
1365 				} else {
1366 					MAKE_NOP(last_op);
1367 					block->len--;
1368 				}
1369 				block->successors_count = 1;
1370 				++(*opt_count);
1371 			} else if (follow_block->len == 1) {
1372 				target = op_array->opcodes + follow_block->start;
1373 				if (target->opcode == ZEND_JMP) {
1374 				    if (block->successors[0] == follow_block->successors[0]) {
1375 						/* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
1376 						if (last_op->op1_type == IS_CV) {
1377 							last_op->opcode = ZEND_CHECK_VAR;
1378 							last_op->op2.num = 0;
1379 						} else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
1380 							last_op->opcode = ZEND_FREE;
1381 							last_op->op2.num = 0;
1382 						} else {
1383 							MAKE_NOP(last_op);
1384 							block->len--;
1385 						}
1386 						block->successors[0] = follow_block - cfg->blocks;
1387 						block->successors_count = 1;
1388 						++(*opt_count);
1389 						break;
1390 					} else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) {
1391 						next_block = get_next_block(cfg, follow_block);
1392 
1393 						if (target_block == next_block) {
1394 							/* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/
1395 
1396 							last_op->opcode = INV_COND(last_op->opcode);
1397 
1398 							block->successors[0] = follow_block->successors[0];
1399 							block->successors[1] = next_block - cfg->blocks;
1400 
1401 							follow_block->flags &= ~ZEND_BB_REACHABLE;
1402 							MAKE_NOP(target);
1403 							follow_block->len = 0;
1404 
1405 							next_block->flags |= ZEND_BB_FOLLOW;
1406 
1407 							break;
1408 						}
1409 					}
1410 
1411 					/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
1412 					if (last_op->opcode == ZEND_JMPZ) {
1413 						block->successors[1] = follow_block->successors[0];
1414 					} else {
1415 						block->successors[1] = block->successors[0];
1416 						block->successors[0] = follow_block->successors[0];
1417 					}
1418 					last_op->opcode = ZEND_JMPZNZ;
1419 					++(*opt_count);
1420 				}
1421 			}
1422 			break;
1423 
1424 		case ZEND_JMPNZ_EX:
1425 		case ZEND_JMPZ_EX:
1426 			jmp_hitlist_count = 0;
1427 
1428 			target_block = get_target_block(cfg, block, 0, opt_count);
1429 			while (target_block->len == 1) {
1430 				target = op_array->opcodes + target_block->start;
1431 
1432 				if (target->opcode == ZEND_JMP) {
1433 					/* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
1434 					next = target_block->successors[0];
1435 				} else if (target->opcode == last_op->opcode-3 &&
1436 				           (SAME_VAR(target->op1, last_op->result) ||
1437 				            SAME_VAR(target->op1, last_op->op1))) {
1438 					/* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1439 					next = target_block->successors[0];
1440 				} else if (target->opcode == last_op->opcode &&
1441 				           target->result.var == last_op->result.var &&
1442 				           (SAME_VAR(target->op1, last_op->result) ||
1443 				            SAME_VAR(target->op1, last_op->op1))) {
1444 					/* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1445 					next = target_block->successors[0];
1446 				} else if (target->opcode == ZEND_JMPZNZ &&
1447 				           (SAME_VAR(target->op1, last_op->result) ||
1448 				            SAME_VAR(target->op1, last_op->op1))) {
1449 					/* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
1450 					next = target_block->successors[last_op->opcode == ZEND_JMPNZ_EX];
1451 				} else if (target->opcode == INV_EX_COND(last_op->opcode) &&
1452 				           (SAME_VAR(target->op1, last_op->result) ||
1453 				            SAME_VAR(target->op1, last_op->op1))) {
1454 					/* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
1455 					next = target_block->successors[1];
1456 				} else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
1457 				           target->result.var == last_op->result.var &&
1458 				           (SAME_VAR(target->op1, last_op->result) ||
1459 				            SAME_VAR(target->op1, last_op->op1))) {
1460 					/* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */
1461 					next = target_block->successors[1];
1462 				} else if (target->opcode == ZEND_BOOL &&
1463 				           (SAME_VAR(target->op1, last_op->result) ||
1464 				            SAME_VAR(target->op1, last_op->op1))) {
1465 					/* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
1466 					   Z = JMPZ_EX(X,L1+1) */
1467 
1468 					/* NOTE: This optimization pattern is not safe, but works, */
1469 					/*       because result of JMPZ_EX instruction             */
1470 					/*       is not used on the following path and             */
1471 					/*       should be used once on the branch path.           */
1472 					/*                                                         */
1473 					/*       The pattern works well only if jumps processed in */
1474 					/*       direct order, otherwise it breaks JMPZ_EX         */
1475 					/*       sequences too early.                              */
1476 					last_op->result.var = target->result.var;
1477 					next = target_block->successors[0];
1478 				} else {
1479 					break;
1480 				}
1481 				CHECK_LOOP(next);
1482 				block->successors[0] = next;
1483 				++(*opt_count);
1484 				target_block = get_target_block(cfg, block, 0, opt_count);
1485 			}
1486 
1487 			follow_block = get_follow_block(cfg, block, 1, opt_count);
1488 			if (target_block == follow_block) {
1489 				/* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */
1490 				last_op->opcode = ZEND_BOOL;
1491 				last_op->op2.num = 0;
1492 				block->successors_count = 1;
1493 				++(*opt_count);
1494 				break;
1495 			}
1496 			break;
1497 
1498 		case ZEND_JMPZNZ: {
1499 optimize_jmpznz:
1500 			jmp_hitlist_count = 0;
1501 			target_block = get_target_block(cfg, block, 0, opt_count);
1502 			while (target_block->len == 1) {
1503 				target = op_array->opcodes + target_block->start;
1504 
1505 				if (target->opcode == ZEND_JMP) {
1506 					/* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
1507 					next = target_block->successors[0];
1508 				} else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
1509 				           SAME_VAR(target->op1, last_op->op1)) {
1510 					/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
1511 					next = target_block->successors[0];
1512 				} else if (target->opcode == ZEND_JMPNZ &&
1513 				           SAME_VAR(target->op1, last_op->op1)) {
1514 					/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
1515 					next = target_block->successors[1];
1516 				} else {
1517 					break;
1518 				}
1519 				CHECK_LOOP(next);
1520 				block->successors[0] = next;
1521 				++(*opt_count);
1522 				target_block = get_target_block(cfg, block, 0, opt_count);
1523 			}
1524 
1525 			jmp_hitlist_count = 0;
1526 			follow_block = get_target_block(cfg, block, 1, opt_count);
1527 			while (follow_block->len == 1) {
1528 				target = op_array->opcodes + follow_block->start;
1529 
1530 				if (target->opcode == ZEND_JMP) {
1531 					/* JMPZNZ(X, L1, L2), L2: JMP(L3) -> JMPZNZ(X, L1, L3) */
1532 					next = follow_block->successors[0];
1533 				} else if (target->opcode == ZEND_JMPNZ &&
1534 				           SAME_VAR(target->op1, last_op->op1)) {
1535 					/* JMPZNZ(X, L1, L2), L2: X = JMPNZ(X, L3) -> JMPZNZ(X, L1, L3) */
1536 					next = follow_block->successors[0];
1537 				} else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
1538 				           SAME_VAR(target->op1, last_op->op1)) {
1539 					/* JMPZNZ(X, L1, L2), L2: JMPZ(X, L3) -> JMPZNZ(X, L1, L2+1) */
1540 					next = follow_block->successors[1];
1541 				} else {
1542 					break;
1543 				}
1544 				CHECK_LOOP(next);
1545 				block->successors[1] = next;
1546 				++(*opt_count);
1547 				follow_block = get_target_block(cfg, block, 1, opt_count);
1548 			}
1549 
1550 			next_block = get_next_block(cfg, block);
1551 			if (target_block == follow_block &&
1552 			    !(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
1553 				/* JMPZNZ(?,L,L) -> JMP(L) */
1554 				last_op->opcode = ZEND_JMP;
1555 				SET_UNUSED(last_op->op1);
1556 				SET_UNUSED(last_op->op2);
1557 				last_op->extended_value = 0;
1558 				block->successors_count = 1;
1559 				++(*opt_count);
1560 			} else if (target_block == next_block) {
1561 				/* jumping to next on Z - can follow to it and jump only on NZ */
1562 				/* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
1563 				int tmp = block->successors[0];
1564 				last_op->opcode = ZEND_JMPNZ;
1565 				block->successors[0] = block->successors[1];
1566 				block->successors[1] = tmp;
1567 				++(*opt_count);
1568 			} else if (follow_block == next_block) {
1569 				/* jumping to next on NZ - can follow to it and jump only on Z */
1570 				/* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
1571 				last_op->opcode = ZEND_JMPZ;
1572 				++(*opt_count);
1573 			}
1574 			break;
1575 		}
1576 	}
1577 }
1578 
1579 /* Global data dependencies */
1580 
1581 /* Find a set of variables which are used outside of the block where they are
1582  * defined. We won't apply some optimization patterns for such variables. */
zend_t_usage(zend_cfg * cfg,zend_op_array * op_array,zend_bitset used_ext,zend_optimizer_ctx * ctx)1583 static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
1584 {
1585 	int n;
1586 	zend_basic_block *block, *next_block;
1587 	uint32_t var_num;
1588 	uint32_t bitset_len;
1589 	zend_bitset usage;
1590 	zend_bitset defined_here;
1591 	void *checkpoint;
1592 	zend_op *opline, *end;
1593 
1594 
1595 	if (op_array->T == 0) {
1596 		/* shortcut - if no Ts, nothing to do */
1597 		return;
1598 	}
1599 
1600 	checkpoint = zend_arena_checkpoint(ctx->arena);
1601 	bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
1602 	defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1603 
1604 	zend_bitset_clear(defined_here, bitset_len);
1605 	for (n = 1; n < cfg->blocks_count; n++) {
1606 		block = cfg->blocks + n;
1607 
1608 		if (!(block->flags & ZEND_BB_REACHABLE)) {
1609 			continue;
1610 		}
1611 
1612 		opline = op_array->opcodes + block->start;
1613 		end = opline + block->len;
1614 		if (!(block->flags & ZEND_BB_FOLLOW) ||
1615 		    (block->flags & ZEND_BB_TARGET)) {
1616 			/* Skip continuation of "extended" BB */
1617 			zend_bitset_clear(defined_here, bitset_len);
1618 		}
1619 
1620 		while (opline<end) {
1621 			if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1622 				var_num = VAR_NUM(opline->op1.var);
1623 				if (!zend_bitset_in(defined_here, var_num)) {
1624 					zend_bitset_incl(used_ext, var_num);
1625 				}
1626 			}
1627 			if (opline->op2_type == IS_VAR) {
1628 				var_num = VAR_NUM(opline->op2.var);
1629 				if (opline->opcode == ZEND_FE_FETCH_R ||
1630 				    opline->opcode == ZEND_FE_FETCH_RW) {
1631 					/* these opcode use the op2 as result */
1632 					zend_bitset_incl(defined_here, var_num);
1633 				} else if (!zend_bitset_in(defined_here, var_num)) {
1634 					zend_bitset_incl(used_ext, var_num);
1635 				}
1636 			} else if (opline->op2_type == IS_TMP_VAR) {
1637 				var_num = VAR_NUM(opline->op2.var);
1638 				if (!zend_bitset_in(defined_here, var_num)) {
1639 					zend_bitset_incl(used_ext, var_num);
1640 				}
1641 			}
1642 
1643 			if (opline->result_type == IS_VAR) {
1644 				var_num = VAR_NUM(opline->result.var);
1645 				zend_bitset_incl(defined_here, var_num);
1646 			} else if (opline->result_type == IS_TMP_VAR) {
1647 				var_num = VAR_NUM(opline->result.var);
1648 				switch (opline->opcode) {
1649 					case ZEND_ADD_ARRAY_ELEMENT:
1650 					case ZEND_ADD_ARRAY_UNPACK:
1651 					case ZEND_ROPE_ADD:
1652 						/* these opcodes use the result as argument */
1653 						if (!zend_bitset_in(defined_here, var_num)) {
1654 							zend_bitset_incl(used_ext, var_num);
1655 						}
1656 						break;
1657 					default :
1658 						zend_bitset_incl(defined_here, var_num);
1659 				}
1660 			}
1661 			opline++;
1662 		}
1663 	}
1664 
1665 	if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
1666 		int printed = 0;
1667 		uint32_t i;
1668 
1669 		for (i = op_array->last_var; i< op_array->T; i++) {
1670 			if (zend_bitset_in(used_ext, i)) {
1671 				if (!printed) {
1672 					fprintf(stderr, "NON-LOCAL-VARS: %d", i);
1673 					printed = 1;
1674 				} else {
1675 					fprintf(stderr, ", %d", i);
1676 				}
1677 			}
1678 		}
1679 		if (printed) {
1680 			fprintf(stderr, "\n");
1681 		}
1682 	}
1683 
1684 	usage = defined_here;
1685 	next_block = NULL;
1686 	for (n = cfg->blocks_count; n > 0;) {
1687 		block = cfg->blocks + (--n);
1688 
1689 		if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
1690 			continue;
1691 		}
1692 
1693 		end = op_array->opcodes + block->start;
1694 		opline = end + block->len - 1;
1695 		if (!next_block ||
1696 		    !(next_block->flags & ZEND_BB_FOLLOW) ||
1697 		    (next_block->flags & ZEND_BB_TARGET)) {
1698 			/* Skip continuation of "extended" BB */
1699 			zend_bitset_copy(usage, used_ext, bitset_len);
1700 		} else if (block->successors_count > 1) {
1701 			zend_bitset_union(usage, used_ext, bitset_len);
1702 		}
1703 		next_block = block;
1704 
1705 		while (opline >= end) {
1706 			/* usage checks */
1707 			if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1708 				if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
1709 					switch (opline->opcode) {
1710 						case ZEND_ASSIGN_OP:
1711 						case ZEND_ASSIGN_DIM_OP:
1712 						case ZEND_ASSIGN_OBJ_OP:
1713 						case ZEND_ASSIGN_STATIC_PROP_OP:
1714 						case ZEND_PRE_INC:
1715 						case ZEND_PRE_DEC:
1716 						case ZEND_ASSIGN:
1717 						case ZEND_ASSIGN_REF:
1718 						case ZEND_DO_FCALL:
1719 						case ZEND_DO_ICALL:
1720 						case ZEND_DO_UCALL:
1721 						case ZEND_DO_FCALL_BY_NAME:
1722 							opline->result_type = IS_UNUSED;
1723 							break;
1724 						case ZEND_POST_INC:
1725 						case ZEND_POST_DEC:
1726 						case ZEND_POST_INC_OBJ:
1727 						case ZEND_POST_DEC_OBJ:
1728 						case ZEND_POST_INC_STATIC_PROP:
1729 						case ZEND_POST_DEC_STATIC_PROP:
1730 							opline->opcode -= 2;
1731 							opline->result_type = IS_UNUSED;
1732 							break;
1733 						case ZEND_QM_ASSIGN:
1734 						case ZEND_BOOL:
1735 						case ZEND_BOOL_NOT:
1736 							if (opline->op1_type == IS_CV) {
1737 								opline->opcode = ZEND_CHECK_VAR;
1738 								SET_UNUSED(opline->result);
1739 							} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
1740 								opline->opcode = ZEND_FREE;
1741 								SET_UNUSED(opline->result);
1742 							} else {
1743 								if (opline->op1_type == IS_CONST) {
1744 									literal_dtor(&ZEND_OP1_LITERAL(opline));
1745 								}
1746 								MAKE_NOP(opline);
1747 							}
1748 							break;
1749 						case ZEND_JMPZ_EX:
1750 						case ZEND_JMPNZ_EX:
1751 							opline->opcode -= 3;
1752 							SET_UNUSED(opline->result);
1753 							break;
1754 						case ZEND_ADD_ARRAY_ELEMENT:
1755 						case ZEND_ADD_ARRAY_UNPACK:
1756 						case ZEND_ROPE_ADD:
1757 							zend_bitset_incl(usage, VAR_NUM(opline->result.var));
1758 							break;
1759 					}
1760 				} else {
1761 					switch (opline->opcode) {
1762 						case ZEND_ADD_ARRAY_ELEMENT:
1763 						case ZEND_ADD_ARRAY_UNPACK:
1764 						case ZEND_ROPE_ADD:
1765 							break;
1766 						default:
1767 							zend_bitset_excl(usage, VAR_NUM(opline->result.var));
1768 							break;
1769 					}
1770 				}
1771 			}
1772 
1773 			if (opline->op2_type == IS_VAR) {
1774 				switch (opline->opcode) {
1775 					case ZEND_FE_FETCH_R:
1776 					case ZEND_FE_FETCH_RW:
1777 						zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
1778 						break;
1779 					default:
1780 						zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
1781 						break;
1782 				}
1783 			} else if (opline->op2_type == IS_TMP_VAR) {
1784 				zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
1785 			}
1786 
1787 			if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1788 				zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
1789 			}
1790 
1791 			opline--;
1792 		}
1793 	}
1794 
1795 	zend_arena_release(&ctx->arena, checkpoint);
1796 }
1797 
zend_merge_blocks(zend_op_array * op_array,zend_cfg * cfg,uint32_t * opt_count)1798 static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count)
1799 {
1800 	int i;
1801 	zend_basic_block *b, *bb;
1802 	zend_basic_block *prev = NULL;
1803 
1804 	for (i = 0; i < cfg->blocks_count; i++) {
1805 		b = cfg->blocks + i;
1806 		if (b->flags & ZEND_BB_REACHABLE) {
1807 			if ((b->flags & ZEND_BB_FOLLOW) &&
1808 			    !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
1809 			    prev && prev->successors_count == 1 && prev->successors[0] == i)
1810 			{
1811 				zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
1812 				if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
1813 					MAKE_NOP(last_op);
1814 				}
1815 
1816 				for (bb = prev + 1; bb != b; bb++) {
1817 					zend_op *op = op_array->opcodes + bb->start;
1818 					zend_op *end = op + bb->len;
1819 					while (op < end) {
1820 						if (op->op1_type == IS_CONST) {
1821 							literal_dtor(&ZEND_OP1_LITERAL(op));
1822 						}
1823 						if (op->op2_type == IS_CONST) {
1824 							literal_dtor(&ZEND_OP2_LITERAL(op));
1825 						}
1826 						MAKE_NOP(op);
1827 						op++;
1828 					}
1829 					/* make block empty */
1830 					bb->len = 0;
1831 				}
1832 
1833 				/* re-link */
1834 				prev->flags |= (b->flags & ZEND_BB_EXIT);
1835 				prev->len = b->start + b->len - prev->start;
1836 				prev->successors_count = b->successors_count;
1837 				if (b->successors != b->successors_storage) {
1838 					prev->successors = b->successors;
1839 					b->successors = b->successors_storage;
1840 				} else {
1841 					memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
1842 				}
1843 
1844 				/* unlink & make block empty and unreachable */
1845 				b->flags = 0;
1846 				b->len = 0;
1847 				b->successors_count = 0;
1848 				++(*opt_count);
1849 			} else {
1850 				prev = b;
1851 			}
1852 		}
1853 	}
1854 }
1855 
1856 #define PASSES 3
1857 
zend_optimize_cfg(zend_op_array * op_array,zend_optimizer_ctx * ctx)1858 void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1859 {
1860 	zend_cfg cfg;
1861 	zend_basic_block *blocks, *end, *b;
1862 	int pass;
1863 	uint32_t bitset_len;
1864 	zend_bitset usage;
1865 	void *checkpoint;
1866 	zend_op **Tsource;
1867 	uint32_t opt_count;
1868 	int *jmp_hitlist;
1869 
1870     /* Build CFG */
1871 	checkpoint = zend_arena_checkpoint(ctx->arena);
1872 	if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) {
1873 		zend_arena_release(&ctx->arena, checkpoint);
1874 		return;
1875 	}
1876 
1877 	if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
1878 		zend_arena_release(&ctx->arena, checkpoint);
1879 		return;
1880 	}
1881 
1882 	if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
1883 		zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
1884 	}
1885 
1886 	bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
1887 	Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
1888 	usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1889 	jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int));
1890 
1891 	blocks = cfg.blocks;
1892 	end = blocks + cfg.blocks_count;
1893 	for (pass = 0; pass < PASSES; pass++) {
1894 		opt_count = 0;
1895 
1896 		/* Compute data dependencies */
1897 		zend_bitset_clear(usage, bitset_len);
1898 		zend_t_usage(&cfg, op_array, usage, ctx);
1899 
1900 		/* optimize each basic block separately */
1901 		for (b = blocks; b < end; b++) {
1902 			if (!(b->flags & ZEND_BB_REACHABLE)) {
1903 				continue;
1904 			}
1905 			/* we track data dependencies only inside a single basic block */
1906 			if (!(b->flags & ZEND_BB_FOLLOW) ||
1907 			    (b->flags & ZEND_BB_TARGET)) {
1908 				/* Skip continuation of "extended" BB */
1909 				memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
1910 			}
1911 			zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count);
1912 		}
1913 
1914 		/* Eliminate NOPs */
1915 		for (b = blocks; b < end; b++) {
1916 			if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
1917 				/* In unreachable_free blocks only preserve loop var frees. */
1918 				for (uint32_t i = b->start; i < b->start + b->len; i++) {
1919 					zend_op *opline = &op_array->opcodes[i];
1920 					if (!zend_optimizer_is_loop_var_free(opline)) {
1921 						MAKE_NOP(opline);
1922 					}
1923 				}
1924 			}
1925 			if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1926 				strip_nops(op_array, b);
1927 			}
1928 		}
1929 
1930 		opt_count = 0;
1931 
1932 		/* Jump optimization for each block */
1933 		for (b = blocks; b < end; b++) {
1934 			if (b->flags & ZEND_BB_REACHABLE) {
1935 				zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count);
1936 			}
1937 		}
1938 
1939 		/* Eliminate unreachable basic blocks */
1940 		zend_cfg_remark_reachable_blocks(op_array, &cfg);
1941 
1942 		/* Merge Blocks */
1943 		zend_merge_blocks(op_array, &cfg, &opt_count);
1944 
1945 		if (opt_count == 0) {
1946 			break;
1947 		}
1948 	}
1949 
1950 	assemble_code_blocks(&cfg, op_array, ctx);
1951 
1952 	if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
1953 		zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
1954 	}
1955 
1956 	/* Destroy CFG */
1957 	zend_arena_release(&ctx->arena, checkpoint);
1958 }
1959