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