xref: /PHP-7.2/ext/opcache/Optimizer/dfa_pass.c (revision 003c13d7)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "php.h"
20 #include "Optimizer/zend_optimizer.h"
21 #include "Optimizer/zend_optimizer_internal.h"
22 #include "zend_API.h"
23 #include "zend_constants.h"
24 #include "zend_execute.h"
25 #include "zend_vm.h"
26 #include "zend_bitset.h"
27 #include "zend_cfg.h"
28 #include "zend_ssa.h"
29 #include "zend_func_info.h"
30 #include "zend_call_graph.h"
31 #include "zend_inference.h"
32 #include "zend_dump.h"
33 
34 #ifndef ZEND_DEBUG_DFA
35 # define ZEND_DEBUG_DFA ZEND_DEBUG
36 #endif
37 
38 #if ZEND_DEBUG_DFA
39 # include "ssa_integrity.c"
40 #endif
41 
zend_dfa_analyze_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx,zend_ssa * ssa,uint32_t * flags)42 int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags)
43 {
44 	uint32_t build_flags;
45 
46 	if (op_array->last_try_catch) {
47 		/* TODO: we can't analyze functions with try/catch/finally ??? */
48 		return FAILURE;
49 	}
50 
51     /* Build SSA */
52 	memset(ssa, 0, sizeof(zend_ssa));
53 
54 	if (zend_build_cfg(&ctx->arena, op_array,
55 			ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg, flags) != SUCCESS) {
56 		return FAILURE;
57 	}
58 
59 	if (*flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
60 		/* TODO: we can't analyze functions with indirect variable access ??? */
61 		return FAILURE;
62 	}
63 
64 	if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
65 		return FAILURE;
66 	}
67 
68 	if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
69 		zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
70 	}
71 
72 	/* Compute Dominators Tree */
73 	if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
74 		return FAILURE;
75 	}
76 
77 	/* Identify reducible and irreducible loops */
78 	if (zend_cfg_identify_loops(op_array, &ssa->cfg, flags) != SUCCESS) {
79 		return FAILURE;
80 	}
81 
82 	if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
83 		zend_dump_dominators(op_array, &ssa->cfg);
84 	}
85 
86 	build_flags = 0;
87 	if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
88 		build_flags |= ZEND_SSA_DEBUG_LIVENESS;
89 	}
90 	if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
91 		build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
92 	}
93 	if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa, flags) != SUCCESS) {
94 		return FAILURE;
95 	}
96 
97 	if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
98 		zend_dump_op_array(op_array, ZEND_DUMP_SSA, "dfa ssa", ssa);
99 	}
100 
101 
102 	if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
103 		return FAILURE;
104 	}
105 
106 	if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
107 		return FAILURE;
108 	}
109 
110 	if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
111 		return FAILURE;
112 	}
113 
114 	if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) {
115 		return FAILURE;
116 	}
117 
118 	if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
119 		zend_dump_ssa_variables(op_array, ssa, 0);
120 	}
121 
122 	return SUCCESS;
123 }
124 
zend_ssa_remove_nops(zend_op_array * op_array,zend_ssa * ssa)125 static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
126 {
127 	zend_basic_block *blocks = ssa->cfg.blocks;
128 	zend_basic_block *end = blocks + ssa->cfg.blocks_count;
129 	zend_basic_block *b;
130 	zend_func_info *func_info;
131 	int j;
132 	uint32_t i = 0;
133 	uint32_t target = 0;
134 	uint32_t *shiftlist;
135 	ALLOCA_FLAG(use_heap);
136 
137 	shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
138 	memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
139 	/* remove empty callee_info */
140 	func_info = ZEND_FUNC_INFO(op_array);
141 	if (func_info) {
142 		zend_call_info **call_info = &func_info->callee_info;
143 		while ((*call_info)) {
144 			if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) {
145 				*call_info = (*call_info)->next_callee;
146 			} else {
147 				call_info = &(*call_info)->next_callee;
148 			}
149 		}
150 	}
151 
152 	for (b = blocks; b < end; b++) {
153 		if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
154 			uint32_t end;
155 
156 			if (b->len) {
157 				while (i < b->start) {
158 					shiftlist[i] = i - target;
159 					i++;
160 				}
161 
162 				if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
163 					/* Only keep the FREE for the loop var */
164 					ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
165 							|| op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
166 					b->len = 1;
167 				}
168 
169 				end = b->start + b->len;
170 				b->start = target;
171 				while (i < end) {
172 					shiftlist[i] = i - target;
173 					if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
174 					   /* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of
175 					    * "i-1" here to check the last non-NOP instruction. */
176 					   (target > 0 &&
177 					    i + 1 < op_array->last &&
178 					    (op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
179 					     op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
180 					    zend_is_smart_branch(op_array->opcodes + target - 1))) {
181 						if (i != target) {
182 							op_array->opcodes[target] = op_array->opcodes[i];
183 							ssa->ops[target] = ssa->ops[i];
184 							ssa->cfg.map[target] = b - blocks;
185 						}
186 						target++;
187 					}
188 					i++;
189 				}
190 				if (target != end) {
191 					zend_op *opline;
192 					zend_op *new_opline;
193 
194 					b->len = target - b->start;
195 					opline = op_array->opcodes + end - 1;
196 					if (opline->opcode == ZEND_NOP) {
197 						continue;
198 					}
199 
200 					new_opline = op_array->opcodes + target - 1;
201 					zend_optimizer_migrate_jump(op_array, new_opline, opline);
202 				}
203 			} else {
204 				b->start = target;
205 			}
206 		} else {
207 			b->start = target;
208 			b->len = 0;
209 		}
210 	}
211 
212 	if (target != op_array->last) {
213 		/* reset rest opcodes */
214 		for (i = target; i < op_array->last; i++) {
215 			MAKE_NOP(op_array->opcodes + i);
216 		}
217 
218 		/* update SSA variables */
219 		for (j = 0; j < ssa->vars_count; j++) {
220 			if (ssa->vars[j].definition >= 0) {
221 				ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
222 			}
223 			if (ssa->vars[j].use_chain >= 0) {
224 				ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
225 			}
226 		}
227 		for (i = 0; i < op_array->last; i++) {
228 			if (ssa->ops[i].op1_use_chain >= 0) {
229 				ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
230 			}
231 			if (ssa->ops[i].op2_use_chain >= 0) {
232 				ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
233 			}
234 			if (ssa->ops[i].res_use_chain >= 0) {
235 				ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
236 			}
237 		}
238 
239 		/* update branch targets */
240 		for (b = blocks; b < end; b++) {
241 			if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
242 				zend_op *opline = op_array->opcodes + b->start + b->len - 1;
243 				zend_optimizer_shift_jump(op_array, opline, shiftlist);
244 			}
245 		}
246 
247 		/* update brk/cont array */
248 		for (j = 0; j < op_array->last_live_range; j++) {
249 			op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
250 			op_array->live_range[j].end   -= shiftlist[op_array->live_range[j].end];
251 		}
252 
253 		/* update try/catch array */
254 		for (j = 0; j < op_array->last_try_catch; j++) {
255 			op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
256 			op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
257 			if (op_array->try_catch_array[j].finally_op) {
258 				op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
259 				op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
260 			}
261 		}
262 
263 		/* update early binding list */
264 		if (op_array->early_binding != (uint32_t)-1) {
265 			uint32_t *opline_num = &op_array->early_binding;
266 
267 			do {
268 				*opline_num -= shiftlist[*opline_num];
269 				opline_num = &op_array->opcodes[*opline_num].result.opline_num;
270 			} while (*opline_num != (uint32_t)-1);
271 		}
272 
273 		/* update call graph */
274 		if (func_info) {
275 			zend_call_info *call_info = func_info->callee_info;
276 			while (call_info) {
277 				call_info->caller_init_opline -=
278 					shiftlist[call_info->caller_init_opline - op_array->opcodes];
279 				call_info->caller_call_opline -=
280 					shiftlist[call_info->caller_call_opline - op_array->opcodes];
281 				call_info = call_info->next_callee;
282 			}
283 		}
284 
285 		op_array->last = target;
286 	}
287 	free_alloca(shiftlist, use_heap);
288 }
289 
can_elide_return_type_check(zend_op_array * op_array,zend_ssa * ssa,zend_ssa_op * ssa_op)290 static inline zend_bool can_elide_return_type_check(
291 		zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
292 	zend_arg_info *info = &op_array->arg_info[-1];
293 	zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
294 	zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
295 
296 	if (use_info->type & MAY_BE_REF) {
297 		return 0;
298 	}
299 
300 	/* A type is possible that is not in the allowed types */
301 	if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
302 		return 0;
303 	}
304 
305 	/* These types are not represented exactly */
306 	if (ZEND_TYPE_CODE(info->type) == IS_CALLABLE || ZEND_TYPE_CODE(info->type) == IS_ITERABLE) {
307 		return 0;
308 	}
309 
310 	if (ZEND_TYPE_IS_CLASS(info->type)) {
311 		if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) {
312 			return 0;
313 		}
314 	}
315 
316 	return 1;
317 }
318 
opline_supports_assign_contraction(zend_ssa * ssa,zend_op * opline,int src_var,uint32_t cv_var)319 static zend_bool opline_supports_assign_contraction(
320 		zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
321 	if (opline->opcode == ZEND_NEW) {
322 		/* see Zend/tests/generators/aborted_yield_during_new.phpt */
323 		return 0;
324 	}
325 
326 	if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
327 			|| opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
328 		/* Function calls may dtor the return value after it has already been written -- allow
329 		 * direct assignment only for types where a double-dtor does not matter. */
330 		uint32_t type = ssa->var_info[src_var].type;
331 		uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
332 		return !((type & MAY_BE_ANY) & ~simple);
333 	}
334 
335 	if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
336 		/* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
337 		 * eliding the temporary variable would thus yield an incorrect result. */
338 		return opline->op1_type != IS_CV || opline->op1.var != cv_var;
339 	}
340 
341 	if (opline->opcode == ZEND_INIT_ARRAY) {
342 		/* INIT_ARRAY initializes the result array before reading key/value. */
343 		return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
344 			&& (opline->op2_type != IS_CV || opline->op2.var != cv_var);
345 	}
346 
347 	if (opline->opcode == ZEND_CAST
348 			&& (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
349 		/* CAST to array/object may initialize the result to an empty array/object before
350 		 * reading the expression. */
351 		return opline->op1_type != IS_CV || opline->op1.var != cv_var;
352 	}
353 
354 	return 1;
355 }
356 
zend_dfa_optimize_calls(zend_op_array * op_array,zend_ssa * ssa)357 int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
358 {
359 	zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
360 	int removed_ops = 0;
361 
362 	if (func_info->callee_info) {
363 		zend_call_info *call_info = func_info->callee_info;
364 
365 		do {
366 			if (call_info->caller_call_opline->opcode == ZEND_DO_ICALL
367 			 && call_info->callee_func
368 			 && ZSTR_LEN(call_info->callee_func->common.function_name) == sizeof("in_array")-1
369 			 && memcmp(ZSTR_VAL(call_info->callee_func->common.function_name), "in_array", sizeof("in_array")-1) == 0
370 			 && (call_info->caller_init_opline->extended_value == 2
371 			  || (call_info->caller_init_opline->extended_value == 3
372 			   && (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL
373 			   && (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) {
374 
375 				zend_op *send_array;
376 				zend_op *send_needly;
377 				zend_bool strict = 0;
378 
379 				if (call_info->caller_init_opline->extended_value == 2) {
380 					send_array = call_info->caller_call_opline - 1;
381 					send_needly = call_info->caller_call_opline - 2;
382 				} else {
383 					if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) {
384 						strict = 1;
385 					}
386 					send_array = call_info->caller_call_opline - 2;
387 					send_needly = call_info->caller_call_opline - 3;
388 				}
389 
390 				if (send_array->opcode == ZEND_SEND_VAL
391 				 && send_array->op1_type == IS_CONST
392 				 && Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY
393 				 && (send_needly->opcode == ZEND_SEND_VAL
394 				  || send_needly->opcode == ZEND_SEND_VAR)
395 			    ) {
396 					int ok = 1;
397 
398 					HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant));
399 					HashTable *dst;
400 					zval *val, tmp;
401 					zend_ulong idx;
402 
403 					ZVAL_TRUE(&tmp);
404 					dst = emalloc(sizeof(HashTable));
405 					zend_hash_init(dst, zend_hash_num_elements(src), NULL, ZVAL_PTR_DTOR, 0);
406 					if (strict) {
407 						ZEND_HASH_FOREACH_VAL(src, val) {
408 							if (Z_TYPE_P(val) == IS_STRING) {
409 								zend_hash_add(dst, Z_STR_P(val), &tmp);
410 							} else if (Z_TYPE_P(val) == IS_LONG) {
411 								zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
412 							} else {
413 								zend_array_destroy(dst);
414 								ok = 0;
415 								break;
416 							}
417 						} ZEND_HASH_FOREACH_END();
418 					} else {
419 						ZEND_HASH_FOREACH_VAL(src, val) {
420 							if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
421 								zend_array_destroy(dst);
422 								ok = 0;
423 								break;
424 							}
425 							zend_hash_add(dst, Z_STR_P(val), &tmp);
426 						} ZEND_HASH_FOREACH_END();
427 					}
428 
429 					if (ok) {
430 						uint32_t op_num = send_needly - op_array->opcodes;
431 						zend_ssa_op *ssa_op = ssa->ops + op_num;
432 
433 						if (ssa_op->op1_use >= 0) {
434 							/* Reconstruct SSA */
435 							int var_num = ssa_op->op1_use;
436 							zend_ssa_var *var = ssa->vars + var_num;
437 
438 							ZEND_ASSERT(ssa_op->op1_def < 0);
439 							zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
440 							ssa_op->op1_use = -1;
441 							ssa_op->op1_use_chain = -1;
442 							op_num = call_info->caller_call_opline - op_array->opcodes;
443 							ssa_op = ssa->ops + op_num;
444 							ssa_op->op1_use = var_num;
445 							ssa_op->op1_use_chain = var->use_chain;
446 							var->use_chain = op_num;
447 						}
448 
449 						ZVAL_ARR(&tmp, dst);
450 
451 						/* Update opcode */
452 						call_info->caller_call_opline->opcode = ZEND_IN_ARRAY;
453 						call_info->caller_call_opline->extended_value = strict;
454 						call_info->caller_call_opline->op1_type = send_needly->op1_type;
455 						call_info->caller_call_opline->op1.num = send_needly->op1.num;
456 						call_info->caller_call_opline->op2_type = IS_CONST;
457 						call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
458 						if (call_info->caller_init_opline->extended_value == 3) {
459 							MAKE_NOP(call_info->caller_call_opline - 1);
460 						}
461 						MAKE_NOP(call_info->caller_init_opline);
462 						MAKE_NOP(send_needly);
463 						MAKE_NOP(send_array);
464 						removed_ops++;
465 
466 					}
467 				}
468 			}
469 			call_info = call_info->next_callee;
470 		} while (call_info);
471 	}
472 
473 	return removed_ops;
474 }
475 
zend_dfa_optimize_jmps(zend_op_array * op_array,zend_ssa * ssa)476 static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
477 {
478 	int removed_ops = 0;
479 	int block_num = 0;
480 
481 	while (block_num < ssa->cfg.blocks_count
482 		&& !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)) {
483 		block_num++;
484 	}
485 	while (block_num < ssa->cfg.blocks_count) {
486 		int next_block_num = block_num + 1;
487 		zend_basic_block *block = &ssa->cfg.blocks[block_num];
488 		uint32_t op_num;
489 		zend_op *opline;
490 		zend_ssa_op *op;
491 
492 		while (next_block_num < ssa->cfg.blocks_count
493 			&& !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
494 			next_block_num++;
495 		}
496 
497 		if (block->len) {
498 			if (block->successors_count == 2) {
499 				if (block->successors[0] == block->successors[1]) {
500 					op_num = block->start + block->len - 1;
501 					opline = op_array->opcodes + op_num;
502 					switch (opline->opcode) {
503 						case ZEND_JMPZ:
504 						case ZEND_JMPNZ:
505 						case ZEND_JMPZNZ:
506 							op = ssa->ops + op_num;
507 							if (block->successors[0] == next_block_num) {
508 								if (opline->op1_type & (IS_CV|IS_CONST)) {
509 									zend_ssa_remove_instr(ssa, opline, op);
510 									if (op->op1_use >= 0) {
511 										zend_ssa_unlink_use_chain(ssa, op_num, op->op1_use);
512 										op->op1_use = -1;
513 										op->op1_use_chain = -1;
514 									}
515 									MAKE_NOP(opline);
516 									removed_ops++;
517 								} else {
518 									opline->opcode = ZEND_FREE;
519 									opline->op2.num = 0;
520 								}
521 							} else {
522 								if (opline->op1_type & (IS_CV|IS_CONST)) {
523 									if (op->op1_use >= 0) {
524 										zend_ssa_unlink_use_chain(ssa, op_num, op->op1_use);
525 										op->op1_use = -1;
526 										op->op1_use_chain = -1;
527 									}
528 									opline->opcode = ZEND_JMP;
529 									opline->op1_type = IS_UNUSED;
530 									opline->op1.num = opline->op2.num;
531 								}
532 							}
533 							break;
534 						default:
535 							break;
536 					}
537 				}
538 			} else if (block->successors_count == 1 && block->successors[0] == next_block_num) {
539 				op_num = block->start + block->len - 1;
540 				opline = op_array->opcodes + op_num;
541 				if (opline->opcode == ZEND_JMP) {
542 					MAKE_NOP(opline);
543 					removed_ops++;
544 				}
545 		    }
546 		}
547 
548 		block_num = next_block_num;
549 	}
550 
551 	return removed_ops;
552 }
553 
zend_dfa_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx,zend_ssa * ssa,zend_call_info ** call_map)554 void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
555 {
556 	if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
557 		zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
558 	}
559 
560 	if (ssa->var_info) {
561 		int op_1;
562 		int v;
563 		int remove_nops = 0;
564 		zend_op *opline;
565 		zval tmp;
566 
567 		if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
568 			if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
569 				remove_nops = 1;
570 			}
571 
572 			if (zend_dfa_optimize_jmps(op_array, ssa)) {
573 				remove_nops = 1;
574 			}
575 
576 #if ZEND_DEBUG_DFA
577 			ssa_verify_integrity(op_array, ssa, "after sccp");
578 #endif
579 			if (ZEND_FUNC_INFO(op_array)) {
580 				if (zend_dfa_optimize_calls(op_array, ssa)) {
581 					remove_nops = 1;
582 				}
583 			}
584 			if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_8) {
585 				zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
586 			}
587 #if ZEND_DEBUG_DFA
588 			ssa_verify_integrity(op_array, ssa, "after calls");
589 #endif
590 		}
591 
592 		if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) {
593 			if (dce_optimize_op_array(op_array, ssa, 0)) {
594 				remove_nops = 1;
595 			}
596 			if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) {
597 				zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa);
598 			}
599 #if ZEND_DEBUG_DFA
600 			ssa_verify_integrity(op_array, ssa, "after dce");
601 #endif
602 		}
603 
604 		for (v = op_array->last_var; v < ssa->vars_count; v++) {
605 
606 			op_1 = ssa->vars[v].definition;
607 
608 			if (op_1 < 0) {
609 				continue;
610 			}
611 
612 			opline = op_array->opcodes + op_1;
613 
614 			/* Convert LONG constants to DOUBLE */
615 			if (ssa->var_info[v].use_as_double) {
616 				if (opline->opcode == ZEND_ASSIGN
617 				 && opline->op2_type == IS_CONST
618 				 && ssa->ops[op_1].op1_def == v
619 				 && !RETURN_VALUE_USED(opline)
620 				) {
621 
622 // op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
623 
624 					zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
625 					ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
626 					ZVAL_DOUBLE(&tmp, zval_get_double(zv));
627 					opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
628 
629 				} else if (opline->opcode == ZEND_QM_ASSIGN
630 				 && opline->op1_type == IS_CONST
631 				) {
632 
633 // op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
634 
635 					zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
636 					ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
637 					ZVAL_DOUBLE(&tmp, zval_get_double(zv));
638 					opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
639 				}
640 
641 			} else {
642 				if (opline->opcode == ZEND_ADD
643 				 || opline->opcode == ZEND_SUB
644 				 || opline->opcode == ZEND_MUL
645 				 || opline->opcode == ZEND_IS_EQUAL
646 				 || opline->opcode == ZEND_IS_NOT_EQUAL
647 				 || opline->opcode == ZEND_IS_SMALLER
648 				 || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
649 				) {
650 
651 					if (opline->op1_type == IS_CONST
652 					 && opline->op2_type != IS_CONST
653 					 && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
654 					 && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
655 					) {
656 
657 // op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
658 
659 						zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
660 						ZVAL_DOUBLE(&tmp, zval_get_double(zv));
661 						opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
662 
663 					} else if (opline->op1_type != IS_CONST
664 					 && opline->op2_type == IS_CONST
665 					 && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
666 					 && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
667 					) {
668 
669 // op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
670 
671 						zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
672 						ZVAL_DOUBLE(&tmp, zval_get_double(zv));
673 						opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
674 					}
675 				} else if (opline->opcode == ZEND_CONCAT) {
676 					if (!(OP1_INFO() & MAY_BE_OBJECT)
677 					 && !(OP2_INFO() & MAY_BE_OBJECT)) {
678 						opline->opcode = ZEND_FAST_CONCAT;
679 					}
680 				}
681 			}
682 
683 			if (ssa->vars[v].var >= op_array->last_var) {
684 				/* skip TMP and VAR */
685 				continue;
686 			}
687 
688 			if (opline->opcode == ZEND_ASSIGN
689 			 && ssa->ops[op_1].op1_def == v
690 			 && !RETURN_VALUE_USED(opline)
691 			) {
692 				int orig_var = ssa->ops[op_1].op1_use;
693 
694 				if (orig_var >= 0
695 				 && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
696 				) {
697 
698 					int src_var = ssa->ops[op_1].op2_use;
699 
700 					if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
701 					 && src_var >= 0
702 					 && !(ssa->var_info[src_var].type & MAY_BE_REF)
703 					 && ssa->vars[src_var].definition >= 0
704 					 && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
705 					 && ssa->ops[ssa->vars[src_var].definition].result_use < 0
706 					 && ssa->vars[src_var].use_chain == op_1
707 					 && ssa->ops[op_1].op2_use_chain < 0
708 					 && !ssa->vars[src_var].phi_use_chain
709 					 && !ssa->vars[src_var].sym_use_chain
710 					 && opline_supports_assign_contraction(
711 						 ssa, &op_array->opcodes[ssa->vars[src_var].definition],
712 						 src_var, opline->op1.var)
713 					) {
714 
715 						int op_2 = ssa->vars[src_var].definition;
716 
717 // op_2: #src_var.T = OP ...                                     => #v.CV = OP ...
718 // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T    NOP
719 
720 						if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
721 							/* Reconstruct SSA */
722 							ssa->vars[v].definition = op_2;
723 							ssa->ops[op_2].result_def = v;
724 
725 							ssa->vars[src_var].definition = -1;
726 							ssa->vars[src_var].use_chain = -1;
727 
728 							ssa->ops[op_1].op1_use = -1;
729 							ssa->ops[op_1].op2_use = -1;
730 							ssa->ops[op_1].op1_def = -1;
731 							ssa->ops[op_1].op1_use_chain = -1;
732 
733 							/* Update opcodes */
734 							op_array->opcodes[op_2].result_type = opline->op1_type;
735 							op_array->opcodes[op_2].result.var = opline->op1.var;
736 							MAKE_NOP(opline);
737 							remove_nops = 1;
738 						}
739 					} else if (opline->op2_type == IS_CONST
740 					 || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
741 					     && ssa->ops[op_1].op2_use >= 0
742 					     && ssa->ops[op_1].op2_def < 0)
743 					) {
744 
745 // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
746 
747 						if (ssa->ops[op_1].op1_use != ssa->ops[op_1].op2_use) {
748 							zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
749 						} else {
750 							ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain;
751 						}
752 
753 						/* Reconstruct SSA */
754 						ssa->ops[op_1].result_def = v;
755 						ssa->ops[op_1].op1_def = -1;
756 						ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
757 						ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
758 						ssa->ops[op_1].op2_use = -1;
759 						ssa->ops[op_1].op2_use_chain = -1;
760 
761 						/* Update opcode */
762 						opline->result_type = opline->op1_type;
763 						opline->result.var = opline->op1.var;
764 						opline->op1_type = opline->op2_type;
765 						opline->op1.var = opline->op2.var;
766 						opline->op2_type = IS_UNUSED;
767 						opline->op2.var = 0;
768 						opline->opcode = ZEND_QM_ASSIGN;
769 					}
770 				}
771 
772 			} else if (opline->opcode == ZEND_ASSIGN_ADD
773 			 && opline->extended_value == 0
774 			 && ssa->ops[op_1].op1_def == v
775 			 && opline->op2_type == IS_CONST
776 			 && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
777 			 && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
778 			 && ssa->ops[op_1].op1_use >= 0
779 			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
780 
781 // op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
782 
783 				opline->opcode = ZEND_PRE_INC;
784 				SET_UNUSED(opline->op2);
785 
786 			} else if (opline->opcode == ZEND_ASSIGN_SUB
787 			 && opline->extended_value == 0
788 			 && ssa->ops[op_1].op1_def == v
789 			 && opline->op2_type == IS_CONST
790 			 && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
791 			 && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
792 			 && ssa->ops[op_1].op1_use >= 0
793 			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
794 
795 // op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
796 
797 				opline->opcode = ZEND_PRE_DEC;
798 				SET_UNUSED(opline->op2);
799 
800 			} else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
801 			 && ssa->ops[op_1].op1_def == v
802 			 && ssa->ops[op_1].op1_use >= 0
803 			 && ssa->ops[op_1].op1_use_chain == -1
804 			 && ssa->vars[v].use_chain >= 0
805 			 && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) {
806 
807 // op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP
808 
809 				int orig_var = ssa->ops[op_1].op1_use;
810 				if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
811 
812 					int ret = ssa->vars[v].use_chain;
813 
814 					ssa->ops[ret].op1_use = orig_var;
815 					ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain;
816 					ssa->vars[orig_var].use_chain = ret;
817 
818 					ssa->vars[v].definition = -1;
819 					ssa->vars[v].use_chain = -1;
820 
821 					ssa->ops[op_1].op1_def = -1;
822 					ssa->ops[op_1].op1_use = -1;
823 
824 					MAKE_NOP(opline);
825 					remove_nops = 1;
826 				}
827 
828 			} else if (ssa->ops[op_1].op1_def == v
829 			 && !RETURN_VALUE_USED(opline)
830 			 && ssa->ops[op_1].op1_use >= 0
831 			 && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
832 			 && (opline->opcode == ZEND_ASSIGN_ADD
833 			  || opline->opcode == ZEND_ASSIGN_SUB
834 			  || opline->opcode == ZEND_ASSIGN_MUL
835 			  || opline->opcode == ZEND_ASSIGN_DIV
836 			  || opline->opcode == ZEND_ASSIGN_MOD
837 			  || opline->opcode == ZEND_ASSIGN_SL
838 			  || opline->opcode == ZEND_ASSIGN_SR
839 			  || opline->opcode == ZEND_ASSIGN_BW_OR
840 			  || opline->opcode == ZEND_ASSIGN_BW_AND
841 			  || opline->opcode == ZEND_ASSIGN_BW_XOR)
842 			 && opline->extended_value == 0) {
843 
844 // op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
845 
846 				/* Reconstruct SSA */
847 				ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
848 				ssa->ops[op_1].op1_def = -1;
849 
850 				/* Update opcode */
851 				opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
852 				opline->result_type = opline->op1_type;
853 				opline->result.var = opline->op1.var;
854 
855 			}
856 		}
857 
858 #if ZEND_DEBUG_DFA
859 		ssa_verify_integrity(op_array, ssa, "after dfa");
860 #endif
861 
862 		if (remove_nops) {
863 			zend_ssa_remove_nops(op_array, ssa);
864 #if ZEND_DEBUG_DFA
865 			ssa_verify_integrity(op_array, ssa, "after nop");
866 #endif
867 		}
868 	}
869 
870 	if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
871 		zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
872 	}
873 }
874 
zend_optimize_dfa(zend_op_array * op_array,zend_optimizer_ctx * ctx)875 void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
876 {
877 	void *checkpoint = zend_arena_checkpoint(ctx->arena);
878 	uint32_t flags = 0;
879 	zend_ssa ssa;
880 
881 	if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, &flags) != SUCCESS) {
882 		zend_arena_release(&ctx->arena, checkpoint);
883 		return;
884 	}
885 
886 	zend_dfa_optimize_op_array(op_array, ctx, &ssa, NULL);
887 
888 	/* Destroy SSA */
889 	zend_arena_release(&ctx->arena, checkpoint);
890 }
891 
892 /*
893  * Local variables:
894  * tab-width: 4
895  * c-basic-offset: 4
896  * indent-tabs-mode: t
897  * End:
898  */
899