xref: /PHP-5.5/ext/opcache/Optimizer/block_pass.c (revision 80260019)
1 #define DEBUG_BLOCKPASS 0
2 
3 /* Checks if a constant (like "true") may be replaced by its value */
zend_get_persistent_constant(char * name,uint name_len,zval * result,int copy TSRMLS_DC ELS_DC)4 static int zend_get_persistent_constant(char *name, uint name_len, zval *result, int copy TSRMLS_DC ELS_DC)
5 {
6 	zend_constant *c;
7 	char *lookup_name;
8 	int retval = 1;
9 	ALLOCA_FLAG(use_heap);
10 
11 	if (zend_hash_find(EG(zend_constants), name, name_len + 1, (void **) &c) == FAILURE) {
12 		lookup_name = DO_ALLOCA(name_len + 1);
13 		memcpy(lookup_name, name, name_len + 1);
14 		zend_str_tolower(lookup_name, name_len);
15 
16 		if (zend_hash_find(EG(zend_constants), lookup_name, name_len + 1, (void **) &c) == SUCCESS) {
17 			if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) {
18 				retval = 0;
19 			}
20 		} else {
21 			retval = 0;
22 		}
23 		FREE_ALLOCA(lookup_name);
24 	}
25 
26 	if (retval) {
27 		if (c->flags & CONST_PERSISTENT) {
28 			*result = c->value;
29 			if (copy) {
30 				zval_copy_ctor(result);
31 			}
32 		} else {
33 			retval = 0;
34 		}
35 	}
36 
37 	return retval;
38 }
39 
40 #if DEBUG_BLOCKPASS
41 # define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1
42 
print_block(zend_code_block * block,zend_op * opcodes,char * txt)43 static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt)
44 {
45 	fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes + block->len - 1, block->len);
46 	if (!block->access) {
47 		fprintf(stderr, " unused");
48 	}
49 	if (block->op1_to) {
50 		fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes);
51 	}
52 	if (block->op2_to) {
53 		fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes);
54 	}
55 	if (block->ext_to) {
56 		fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes);
57 	}
58 	if (block->follow_to) {
59 		fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes);
60 	}
61 
62 	if (block->sources) {
63 		zend_block_source *bs = block->sources;
64 		fprintf(stderr, " s:");
65 		while (bs) {
66 			fprintf(stderr, " %d", bs->from->start_opline - opcodes);
67 			bs = bs->next;
68 		}
69 	}
70 
71 	fprintf(stderr, "\n");
72 	fflush(stderr);
73 }
74 #else
75 #define print_block(a,b,c)
76 #endif
77 
78 #define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1
79 
80 /* find code blocks in op_array
81    code block is a set of opcodes with single flow of control, i.e. without jmps,
82    branches, etc. */
find_code_blocks(zend_op_array * op_array,zend_cfg * cfg)83 static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg)
84 {
85 	zend_op *opline;
86 	zend_op *end = op_array->opcodes + op_array->last;
87 	zend_code_block *blocks, *cur_block;
88 	zend_uint opno = 0;
89 
90 	memset(cfg, 0, sizeof(zend_cfg));
91 	blocks = cfg->blocks = ecalloc(op_array->last + 2, sizeof(zend_code_block));
92 	opline = op_array->opcodes;
93 	blocks[0].start_opline = opline;
94 	blocks[0].start_opline_no = 0;
95 	while (opline < end) {
96 		switch((unsigned)opline->opcode) {
97 			case ZEND_BRK:
98 			case ZEND_CONT:
99 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
100 			case ZEND_GOTO:
101 #endif
102 				/* would not optimize non-optimized BRK/CONTs - we cannot
103 				 really know where it jumps, so these optimizations are
104 				too dangerous */
105 				efree(blocks);
106 				return 0;
107 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
108 			case ZEND_FAST_CALL:
109 				START_BLOCK_OP(ZEND_OP1(opline).opline_num);
110 				if (opline->extended_value) {
111 					START_BLOCK_OP(ZEND_OP2(opline).opline_num);
112 				}
113 				START_BLOCK_OP(opno + 1);
114 				break;
115 			case ZEND_FAST_RET:
116 				if (opline->extended_value) {
117 					START_BLOCK_OP(ZEND_OP2(opline).opline_num);
118 				}
119 				START_BLOCK_OP(opno + 1);
120 				break;
121 #endif
122 			case ZEND_JMP:
123 				START_BLOCK_OP(ZEND_OP1(opline).opline_num);
124 				/* break missing intentionally */
125 			case ZEND_RETURN:
126 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
127 			case ZEND_RETURN_BY_REF:
128 #endif
129 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
130 			case ZEND_GENERATOR_RETURN:
131 #endif
132 			case ZEND_EXIT:
133 			case ZEND_THROW:
134 				/* start new block from this+1 */
135 				START_BLOCK_OP(opno + 1);
136 				break;
137 				/* TODO: if conditional jmp depends on constant,
138 						 don't start block that won't be executed */
139 			case ZEND_CATCH:
140 				START_BLOCK_OP(opline->extended_value);
141 				START_BLOCK_OP(opno + 1);
142 				break;
143 			case ZEND_JMPZNZ:
144 				START_BLOCK_OP(opline->extended_value);
145 			case ZEND_JMPZ:
146 			case ZEND_JMPNZ:
147 			case ZEND_JMPZ_EX:
148 			case ZEND_JMPNZ_EX:
149 			case ZEND_FE_RESET:
150 			case ZEND_NEW:
151 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
152 			case ZEND_JMP_SET:
153 #endif
154 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
155 			case ZEND_JMP_SET_VAR:
156 #endif
157 				START_BLOCK_OP(ZEND_OP2(opline).opline_num);
158 				START_BLOCK_OP(opno + 1);
159 				break;
160 			case ZEND_FE_FETCH:
161 				START_BLOCK_OP(ZEND_OP2(opline).opline_num);
162 				START_BLOCK_OP(opno + 2);
163 				break;
164 		}
165 		opno++;
166 		opline++;
167 	}
168 
169 	/* first find block start points */
170 	if (op_array->last_try_catch) {
171 		int i;
172 		cfg->try = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *));
173 		cfg->catch = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *));
174 		for (i = 0; i< op_array->last_try_catch; i++) {
175 			cfg->try[i] = &blocks[op_array->try_catch_array[i].try_op];
176 			cfg->catch[i] = &blocks[op_array->try_catch_array[i].catch_op];
177 			START_BLOCK_OP(op_array->try_catch_array[i].try_op);
178 			START_BLOCK_OP(op_array->try_catch_array[i].catch_op);
179 			blocks[op_array->try_catch_array[i].try_op].protected = 1;
180 		}
181 	}
182 	/* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
183 	 * but, we have to keep brk_cont_array to avoid memory leaks during
184 	 * exception handling */
185 	if (op_array->last_brk_cont) {
186 		int i, j;
187 
188 		j = 0;
189 		for (i = 0; i< op_array->last_brk_cont; i++) {
190 			if (op_array->brk_cont_array[i].start >= 0 &&
191 			    (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
192 			     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_SWITCH_FREE)) {
193 				int parent = op_array->brk_cont_array[i].parent;
194 
195 				while (parent >= 0 &&
196 				       op_array->brk_cont_array[parent].start < 0 &&
197 				       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE &&
198 				       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_SWITCH_FREE) {
199 					parent = op_array->brk_cont_array[parent].parent;
200 				}
201 				op_array->brk_cont_array[i].parent = parent;
202 				j++;
203 			}
204 		}
205 		if (j) {
206 			cfg->loop_start = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *));
207 			cfg->loop_cont  = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *));
208 			cfg->loop_brk   = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *));
209 			j = 0;
210 			for (i = 0; i< op_array->last_brk_cont; i++) {
211 				if (op_array->brk_cont_array[i].start >= 0 &&
212 				    (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
213 				     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_SWITCH_FREE)) {
214 					if (i != j) {
215 						op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
216 					}
217 					cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
218 					cfg->loop_cont[j]  = &blocks[op_array->brk_cont_array[j].cont];
219 					cfg->loop_brk[j]   = &blocks[op_array->brk_cont_array[j].brk];
220 					START_BLOCK_OP(op_array->brk_cont_array[j].start);
221 					START_BLOCK_OP(op_array->brk_cont_array[j].cont);
222 					START_BLOCK_OP(op_array->brk_cont_array[j].brk);
223 					blocks[op_array->brk_cont_array[j].start].protected = 1;
224 					blocks[op_array->brk_cont_array[j].brk].protected = 1;
225 					j++;
226 				}
227 			}
228 			op_array->last_brk_cont = j;
229 		} else {
230 			efree(op_array->brk_cont_array);
231 			op_array->brk_cont_array = NULL;
232 			op_array->last_brk_cont = 0;
233 		}
234 	}
235 
236 	/* Build CFG (Control Flow Graph) */
237 	cur_block = blocks;
238 	for (opno = 1; opno < op_array->last; opno++) {
239 		if (blocks[opno].start_opline) {
240 			/* found new block start */
241 			cur_block->len = blocks[opno].start_opline - cur_block->start_opline;
242 			cur_block->next = &blocks[opno];
243 			/* what is the last OP of previous block? */
244 			opline = blocks[opno].start_opline - 1;
245 			if (opline->opcode == ZEND_OP_DATA) {
246 				opline--;
247 			}
248 			switch((unsigned)opline->opcode) {
249 				case ZEND_RETURN:
250 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
251 				case ZEND_RETURN_BY_REF:
252 #endif
253 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
254 				case ZEND_GENERATOR_RETURN:
255 #endif
256 				case ZEND_EXIT:
257 				case ZEND_THROW:
258 					break;
259 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
260 				case ZEND_FAST_CALL:
261 					if (opline->extended_value) {
262 						cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
263 					}
264 					cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
265 					break;
266 				case ZEND_FAST_RET:
267 					if (opline->extended_value) {
268 						cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
269 					}
270 					break;
271 #endif
272 				case ZEND_JMP:
273 					cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num];
274 					break;
275 				case ZEND_JMPZNZ:
276 					cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
277 					cur_block->ext_to = &blocks[opline->extended_value];
278 					break;
279 				case ZEND_CATCH:
280 					cur_block->ext_to = &blocks[opline->extended_value];
281 					cur_block->follow_to = &blocks[opno];
282 					break;
283 				case ZEND_JMPZ:
284 				case ZEND_JMPNZ:
285 				case ZEND_JMPZ_EX:
286 				case ZEND_JMPNZ_EX:
287 				case ZEND_FE_RESET:
288 				case ZEND_NEW:
289 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
290 				case ZEND_JMP_SET:
291 #endif
292 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
293 				case ZEND_JMP_SET_VAR:
294 #endif
295 				case ZEND_FE_FETCH:
296 					cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num];
297 					/* break missing intentionally */
298 				default:
299 					/* next block follows this */
300 					cur_block->follow_to = &blocks[opno];
301 					break;
302 			}
303 			print_block(cur_block, op_array->opcodes, "");
304 			cur_block = cur_block->next;
305 		}
306 	}
307 	cur_block->len = end - cur_block->start_opline;
308 	cur_block->next = &blocks[op_array->last + 1];
309 	print_block(cur_block, op_array->opcodes, "");
310 
311 	return 1;
312 }
313 
314 /* CFG back references management */
315 
316 #define ADD_SOURCE(fromb, tob) { \
317 	zend_block_source *__s = tob->sources; \
318     while (__s && __s->from != fromb) __s = __s->next; \
319 	if (__s == NULL) { \
320 		zend_block_source *__t = emalloc(sizeof(zend_block_source)); \
321 		__t->next = tob->sources; \
322 		tob->sources = __t; \
323 		__t->from = fromb; \
324 	} \
325 }
326 
327 #define DEL_SOURCE(cs) { \
328 	zend_block_source *__ns = (*cs)->next; \
329 	efree(*cs); \
330 	*cs = __ns; \
331 }
332 
333 
replace_source(zend_block_source * list,zend_code_block * old,zend_code_block * new)334 static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new)
335 {
336 	/* replace all references to 'old' in 'list' with 'new' */
337 	zend_block_source **cs;
338 	int found = 0;
339 
340 	for (cs = &list; *cs; cs = &((*cs)->next)) {
341 		if ((*cs)->from == new) {
342 			if (found) {
343 				DEL_SOURCE(cs);
344 			} else {
345 				found = 1;
346 			}
347 		}
348 
349 		if ((*cs)->from == old) {
350 			if (found) {
351 				DEL_SOURCE(cs);
352 			} else {
353 				(*cs)->from = new;
354 				found = 1;
355 			}
356 		}
357 	}
358 }
359 
del_source(zend_code_block * from,zend_code_block * to)360 static inline void del_source(zend_code_block *from, zend_code_block *to)
361 {
362 	/* delete source 'from' from 'to'-s sources list */
363 	zend_block_source **cs = &to->sources;
364 
365 	if (to->sources == NULL) {
366 		to->access = 0;
367 		return;
368 	}
369 
370 	while (*cs) {
371 		if ((*cs)->from == from) {
372 		 	DEL_SOURCE(cs);
373 			break;
374 		}
375 		cs = &((*cs)->next);
376 	}
377 
378 	if (to->sources == NULL) {
379 		/* 'to' has no more sources - it's unused, will be stripped */
380 		to->access = 0;
381 		return;
382 	}
383 
384 	if (!to->protected && to->sources->next == NULL) {
385 		/* source to only one block */
386 		zend_code_block *from_block = to->sources->from;
387 
388 		if (from_block->access && from_block->follow_to == to &&
389 		    from_block->op1_to == NULL &&
390 		    from_block->op2_to == NULL &&
391 		    from_block->ext_to == NULL) {
392 			/* this block follows it's only predecessor - we can join them */
393 			zend_op *new_to = from_block->start_opline + from_block->len;
394 			if (new_to != to->start_opline) {
395 				/* move block to new location */
396 				memmove(new_to, to->start_opline, sizeof(zend_op)*to->len);
397 			}
398 			/* join blocks' lengths */
399 			from_block->len += to->len;
400 			/* move 'to'`s references to 'from' */
401 			to->start_opline = NULL;
402 			to->access = 0;
403 			efree(to->sources);
404 			to->sources = NULL;
405 			from_block->follow_to = to->follow_to;
406 			if (to->op1_to) {
407 				from_block->op1_to = to->op1_to;
408 				replace_source(to->op1_to->sources, to, from_block);
409 			}
410 			if (to->op2_to) {
411 				from_block->op2_to = to->op2_to;
412 				replace_source(to->op2_to->sources, to, from_block);
413 			}
414 			if (to->ext_to) {
415 				from_block->ext_to = to->ext_to;
416 				replace_source(to->ext_to->sources, to, from_block);
417 			}
418 			if (to->follow_to) {
419 				replace_source(to->follow_to->sources, to, from_block);
420 			}
421 			/* remove "to" from list */
422 		}
423 	}
424 }
425 
delete_code_block(zend_code_block * block)426 static void delete_code_block(zend_code_block *block)
427 {
428 	if (block->protected) {
429 		return;
430 	}
431 	if (block->follow_to) {
432 		zend_block_source *bs = block->sources;
433 		while (bs) {
434 			zend_code_block *from_block = bs->from;
435 			zend_code_block *to = block->follow_to;
436 			if (from_block->op1_to == block) {
437 				from_block->op1_to = to;
438 				ADD_SOURCE(from_block, to);
439 			}
440 			if (from_block->op2_to == block) {
441 				from_block->op2_to = to;
442 				ADD_SOURCE(from_block, to);
443 			}
444 			if (from_block->ext_to == block) {
445 				from_block->ext_to = to;
446 				ADD_SOURCE(from_block, to);
447 			}
448 			if (from_block->follow_to == block) {
449 				from_block->follow_to = to;
450 				ADD_SOURCE(from_block, to);
451 			}
452 			bs = bs->next;
453 		}
454 	}
455 	block->access = 0;
456 }
457 
zend_access_path(zend_code_block * block)458 static void zend_access_path(zend_code_block *block)
459 {
460 	if (block->access) {
461 		return;
462 	}
463 
464 	block->access = 1;
465 	if (block->op1_to) {
466 		zend_access_path(block->op1_to);
467 		ADD_SOURCE(block, block->op1_to);
468 	}
469 	if (block->op2_to) {
470 		zend_access_path(block->op2_to);
471 		ADD_SOURCE(block, block->op2_to);
472 	}
473 	if (block->ext_to) {
474 		zend_access_path(block->ext_to);
475 		ADD_SOURCE(block, block->ext_to);
476 	}
477 	if (block->follow_to) {
478 		zend_access_path(block->follow_to);
479 		ADD_SOURCE(block, block->follow_to);
480 	}
481 }
482 
483 /* Traverse CFG, mark reachable basic blocks and build back references */
zend_rebuild_access_path(zend_cfg * cfg,zend_op_array * op_array,int find_start)484 static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int find_start)
485 {
486 	zend_code_block *blocks = cfg->blocks;
487 	zend_code_block *start = find_start? NULL : blocks;
488 	zend_code_block *b;
489 
490 	/* Mark all blocks as unaccessible and destroy back references */
491 	b = blocks;
492 	while (b != NULL) {
493 		zend_block_source *cs;
494 		if (!start && b->access) {
495 			start = b;
496 		}
497 		b->access = 0;
498 		cs = b->sources;
499 		while (cs) {
500 			zend_block_source *n = cs->next;
501 			efree(cs);
502 			cs = n;
503 		}
504 		b->sources = NULL;
505 		b = b->next;
506 	}
507 
508 	/* Walk thorough all paths */
509 	zend_access_path(start);
510 
511 	/* Add brk/cont paths */
512 	if (op_array->last_brk_cont) {
513 		int i;
514 		for (i=0; i< op_array->last_brk_cont; i++) {
515 			zend_access_path(cfg->loop_start[i]);
516 			zend_access_path(cfg->loop_cont[i]);
517 			zend_access_path(cfg->loop_brk[i]);
518 		}
519 	}
520 
521 	/* Add exception paths */
522 	if (op_array->last_try_catch) {
523 		int i;
524 		for (i=0; i< op_array->last_try_catch; i++) {
525 			if (!cfg->catch[i]->access) {
526 				zend_access_path(cfg->catch[i]);
527 			}
528 		}
529 	}
530 }
531 
532 /* Data dependencies macros */
533 
534 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
535 
536 # define VAR_NUM_EX(op) ((op ## _type & (IS_TMP_VAR|IS_VAR))?VAR_NUM((op).var):(op).var)
537 
538 # define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
539 # define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
540 
541 # define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0)
542 
543 #else
544 
545 # define VAR_NUM_EX(op) ((op).op_type == IS_TMP_VAR || (op).op_type == IS_VAR? VAR_NUM((op).u.var) : (op).u.var)
546 
547 # define VAR_SOURCE(op) Tsource[VAR_NUM(op.u.var)]
548 # define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(ZEND_RESULT(opline).var)] = opline
549 
550 # define VAR_UNSET(op) do { if ((op).op_type == IS_TMP_VAR || (op).op_type == IS_VAR) {VAR_SOURCE(op) = NULL;}} while (0)
551 
552 #endif
553 
554 #define convert_to_string_safe(v) \
555 	if (Z_TYPE_P((v)) == IS_NULL) { \
556 		ZVAL_STRINGL((v), "", 0, 1); \
557 	} else { \
558 		convert_to_string((v)); \
559 	}
560 
strip_nop(zend_code_block * block)561 static void strip_nop(zend_code_block *block)
562 {
563 	zend_op *opline = block->start_opline;
564 	zend_op *end, *new_end;
565 
566 	/* remove leading NOPs */
567 	while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
568 		if (block->len == 1) {
569 			/* this block is all NOPs, join with following block */
570 			if (block->follow_to) {
571 				delete_code_block(block);
572 			}
573 			return;
574 		}
575 		block->start_opline++;
576 		block->start_opline_no++;
577 		block->len--;
578 	}
579 
580 	/* strip the inside NOPs */
581 	opline = new_end = block->start_opline;
582 	end = opline + block->len;
583 
584 	while (opline < end) {
585 		zend_op *src;
586 		int len = 0;
587 
588 		while (opline < end && opline->opcode == ZEND_NOP) {
589 			opline++;
590 		}
591 		src = opline;
592 
593 		while (opline < end && opline->opcode != ZEND_NOP) {
594 			opline++;
595 		}
596 		len = opline - src;
597 
598 		/* move up non-NOP opcodes */
599 		memmove(new_end, src, len*sizeof(zend_op));
600 
601 		new_end += len;
602 	}
603 	block->len = new_end - block->start_opline;
604 }
605 
zend_optimize_block(zend_code_block * block,zend_op_array * op_array,char * used_ext TSRMLS_DC)606 static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, char *used_ext TSRMLS_DC)
607 {
608 	zend_op *opline = block->start_opline;
609 	zend_op *end, *last_op = NULL;
610 	zend_op **Tsource = NULL;
611 
612 	print_block(block, op_array->opcodes, "Opt ");
613 
614 	/* remove leading NOPs */
615 	while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) {
616 		if (block->len == 1) {
617 			/* this block is all NOPs, join with following block */
618 			if (block->follow_to) {
619 				delete_code_block(block);
620 			}
621 			return;
622 		}
623 		block->start_opline++;
624 		block->start_opline_no++;
625 		block->len--;
626 	}
627 
628 	/* we track data dependencies only insight a single basic block */
629 	if (op_array->T) {
630 		Tsource = ecalloc(op_array->T, sizeof(zend_op *));
631 	}
632 	opline = block->start_opline;
633 	end = opline + block->len;
634 	while ((op_array->T) && (opline < end)) {
635 		/* strip X = QM_ASSIGN(const) */
636 		if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
637 			VAR_SOURCE(opline->op1) &&
638 			VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN &&
639 			ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
640 			opline->opcode != ZEND_CASE &&         /* CASE _always_ expects variable */
641 			opline->opcode != ZEND_FETCH_DIM_TMP_VAR &&   /* in 5.1, FETCH_DIM_TMP_VAR expects T */
642 			opline->opcode != ZEND_FE_RESET &&
643 			opline->opcode != ZEND_FREE
644 			) {
645 			zend_op *src = VAR_SOURCE(opline->op1);
646 			zval c = ZEND_OP1_LITERAL(src);
647 			VAR_UNSET(opline->op1);
648 			zval_copy_ctor(&c);
649 			update_op1_const(op_array, opline, &c TSRMLS_CC);
650 			literal_dtor(&ZEND_OP1_LITERAL(src));
651 			MAKE_NOP(src);
652 		}
653 
654 		/* T = QM_ASSIGN(C), F(T) => NOP, F(C) */
655 		if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR &&
656 			VAR_SOURCE(opline->op2) &&
657 			VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN &&
658 			ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) {
659 			zend_op *src = VAR_SOURCE(opline->op2);
660 			zval c = ZEND_OP1_LITERAL(src);
661 			VAR_UNSET(opline->op2);
662 			zval_copy_ctor(&c);
663 			update_op2_const(op_array, opline, &c TSRMLS_CC);
664 			literal_dtor(&ZEND_OP1_LITERAL(src));
665 			MAKE_NOP(src);
666 		}
667 
668 		/* T = PRINT(X), F(T) => ECHO(X), F(1) */
669 		if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
670 			VAR_SOURCE(opline->op1) &&
671 			VAR_SOURCE(opline->op1)->opcode == ZEND_PRINT &&
672 			opline->opcode != ZEND_CASE && opline->opcode != ZEND_FREE) {
673 			ZEND_OP1_TYPE(opline) = IS_CONST;
674 			LITERAL_LONG(opline->op1, 1);
675 		}
676 
677 		if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR &&
678 			VAR_SOURCE(opline->op2) &&
679 			VAR_SOURCE(opline->op2)->opcode == ZEND_PRINT) {
680 			ZEND_OP2_TYPE(opline) = IS_CONST;
681 			LITERAL_LONG(opline->op2, 1);
682 		}
683 
684 		/* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
685 		if ((opline->opcode == ZEND_ECHO || opline->opcode == ZEND_PRINT) &&
686 			ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
687 			VAR_SOURCE(opline->op1) &&
688 			VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
689 			VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
690 			zend_op *src = VAR_SOURCE(opline->op1);
691 			COPY_NODE(opline->op1, src->op1);
692 			MAKE_NOP(src);
693 		}
694 
695 		/* T = PRINT(X), FREE(T) => ECHO(X) */
696 		if (opline->opcode == ZEND_FREE &&
697 			ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
698 			VAR_SOURCE(opline->op1)) {
699 			zend_op *src = VAR_SOURCE(opline->op1);
700 			if (src->opcode == ZEND_PRINT) {
701 				src->opcode = ZEND_ECHO;
702 				ZEND_RESULT_TYPE(src) = IS_UNUSED;
703 				MAKE_NOP(opline);
704 			}
705 		}
706 
707        /* T = BOOL(X), FREE(T) => NOP */
708 		if (opline->opcode == ZEND_FREE &&
709 			ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
710 			VAR_SOURCE(opline->op1)) {
711 			zend_op *src = VAR_SOURCE(opline->op1);
712 			if (src->opcode == ZEND_BOOL) {
713 				if (ZEND_OP1_TYPE(src) == IS_CONST) {
714 					literal_dtor(&ZEND_OP1_LITERAL(src));
715 				}
716 				MAKE_NOP(src);
717 				MAKE_NOP(opline);
718 			}
719 		}
720 
721 #if 0
722 		/* pre-evaluate functions:
723 		   constant(x)
724 		   defined(x)
725 		   function_exists(x)
726 		   extension_loaded(x)
727 		   BAD: interacts badly with Accelerator
728 		*/
729 		if((ZEND_OP1_TYPE(opline) & IS_VAR) &&
730 		   VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
731 		   VAR_SOURCE(opline->op1)->extended_value == 1) {
732 			zend_op *fcall = VAR_SOURCE(opline->op1);
733 			zend_op *sv = fcall-1;
734 			if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
735 			   ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
736 			   Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
737 			   ) {
738 				zval *arg = &OPLINE_OP1_LITERAL(sv);
739 				char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
740 				int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
741 				if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) {
742 					zval c;
743 					if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 0 TSRMLS_CC ELS_CC) != 0) {
744 						literal_dtor(arg);
745 						MAKE_NOP(sv);
746 						MAKE_NOP(fcall);
747 						LITERAL_BOOL(opline->op1, 1);
748 						ZEND_OP1_TYPE(opline) = IS_CONST;
749 					}
750 				} else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
751 						  (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
752 						  ) {
753 					zend_function *function;
754 					if(zend_hash_find(EG(function_table), Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1, (void **)&function) == SUCCESS) {
755 						literal_dtor(arg);
756 						MAKE_NOP(sv);
757 						MAKE_NOP(fcall);
758 						LITERAL_BOOL(opline->op1, 1);
759 						ZEND_OP1_TYPE(opline) = IS_CONST;
760 					}
761 				} else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
762 					zval c;
763 					if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 1 TSRMLS_CC ELS_CC) != 0) {
764 						literal_dtor(arg);
765 						MAKE_NOP(sv);
766 						MAKE_NOP(fcall);
767 						ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c TSRMLS_CC);
768 						/* no copy ctor - get already copied it */
769 						ZEND_OP1_TYPE(opline) = IS_CONST;
770 					}
771 				} else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
772 					if(zend_hash_exists(&module_registry, Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1)) {
773 						literal_dtor(arg);
774 						MAKE_NOP(sv);
775 						MAKE_NOP(fcall);
776 						LITERAL_BOOL(opline->op1, 1);
777 						ZEND_OP1_TYPE(opline) = IS_CONST;
778 					}
779 				}
780 			}
781 		}
782 #endif
783 
784         /* IS_EQ(TRUE, X)      => BOOL(X)
785          * IS_EQ(FALSE, X)     => BOOL_NOT(X)
786          * IS_NOT_EQ(TRUE, X)  => BOOL_NOT(X)
787          * IS_NOT_EQ(FALSE, X) => BOOL(X)
788          */
789 		if (opline->opcode == ZEND_IS_EQUAL ||
790 			opline->opcode == ZEND_IS_NOT_EQUAL) {
791 			if (ZEND_OP1_TYPE(opline) == IS_CONST &&
792 				Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_BOOL) {
793 				opline->opcode =
794 					((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP1_LITERAL(opline)))?
795 					ZEND_BOOL : ZEND_BOOL_NOT;
796 				COPY_NODE(opline->op1, opline->op2);
797 				SET_UNUSED(opline->op2);
798 			} else if (ZEND_OP2_TYPE(opline) == IS_CONST &&
799 					   Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_BOOL) {
800 				opline->opcode =
801 					((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP2_LITERAL(opline)))?
802 					ZEND_BOOL : ZEND_BOOL_NOT;
803 				SET_UNUSED(opline->op2);
804 			}
805 		}
806 
807 		if ((opline->opcode == ZEND_BOOL ||
808 			opline->opcode == ZEND_BOOL_NOT ||
809 			opline->opcode == ZEND_JMPZ ||
810 			opline->opcode == ZEND_JMPNZ ||
811 			opline->opcode == ZEND_JMPZNZ) &&
812 			ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
813 			VAR_SOURCE(opline->op1) != NULL &&
814 			!used_ext[VAR_NUM(ZEND_OP1(opline).var)] &&
815 			VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) {
816 			/* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
817 			zend_op *src = VAR_SOURCE(opline->op1);
818 
819 			COPY_NODE(opline->op1, src->op1);
820 
821 			switch (opline->opcode) {
822 				case ZEND_BOOL:
823 					/* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
824 					opline->opcode = ZEND_BOOL_NOT;
825 					break;
826 				case ZEND_BOOL_NOT:
827 					/* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */
828 					opline->opcode = ZEND_BOOL;
829 					break;
830 				case ZEND_JMPZ:
831 					/* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */
832 					opline->opcode = ZEND_JMPNZ;
833 					break;
834 				case ZEND_JMPNZ:
835 					/* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */
836 					opline->opcode = ZEND_JMPZ;
837 					break;
838 				case ZEND_JMPZNZ:
839 				{
840 					/* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
841 					int op_t;
842 					zend_code_block *op_b;
843 
844 					op_t = opline->extended_value;
845 					opline->extended_value = ZEND_OP2(opline).opline_num;
846 					ZEND_OP2(opline).opline_num = op_t;
847 
848 					op_b = block->ext_to;
849 					block->ext_to = block->op2_to;
850 					block->op2_to = op_b;
851 				}
852 				break;
853 			}
854 
855 			VAR_UNSET(opline->op1);
856 			MAKE_NOP(src);
857 			continue;
858 		} else
859 #if 0
860 		/* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */
861 		if(0 && (opline->opcode == ZEND_JMPZ_EX ||
862 			opline->opcode == ZEND_JMPNZ_EX) &&
863 		   ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
864 		   VAR_SOURCE(opline->op1) != NULL &&
865 		   VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT &&
866 		   ZEND_OP1(opline).var == ZEND_RESULT(opline).var
867 		   ) {
868 			zend_op *src = VAR_SOURCE(opline->op1);
869 			if(opline->opcode == ZEND_JMPZ_EX) {
870 				opline->opcode = ZEND_JMPNZ;
871 			} else {
872 				opline->opcode = ZEND_JMPZ;
873 			}
874 			COPY_NODE(opline->op1, src->op1);
875 			SET_UNUSED(opline->result);
876 			continue;
877 		} else
878 #endif
879 		/* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */
880 		if ((opline->opcode == ZEND_BOOL ||
881 			opline->opcode == ZEND_BOOL_NOT ||
882 			opline->opcode == ZEND_JMPZ ||
883 			opline->opcode == ZEND_JMPZ_EX ||
884 			opline->opcode == ZEND_JMPNZ_EX ||
885 			opline->opcode == ZEND_JMPNZ ||
886 			opline->opcode == ZEND_JMPZNZ) &&
887 			ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
888 			VAR_SOURCE(opline->op1) != NULL &&
889 			(!used_ext[VAR_NUM(ZEND_OP1(opline).var)] ||
890 			(ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
891 			 ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) &&
892 			(VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
893 			VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) {
894 			zend_op *src = VAR_SOURCE(opline->op1);
895 			COPY_NODE(opline->op1, src->op1);
896 
897 			VAR_UNSET(opline->op1);
898 			MAKE_NOP(src);
899 			continue;
900 		} else if (last_op && opline->opcode == ZEND_ECHO &&
901 				  last_op->opcode == ZEND_ECHO &&
902 				  ZEND_OP1_TYPE(opline) == IS_CONST &&
903 				  Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
904 				  ZEND_OP1_TYPE(last_op) == IS_CONST &&
905 				  Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
906 			/* compress consecutive ECHO's.
907 			 * Float to string conversion may be affected by current
908 			 * locale setting.
909 			 */
910 			int l;
911 
912 			if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
913 				convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
914 			}
915 			if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
916 				convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
917 			}
918 			l = Z_STRLEN(ZEND_OP1_LITERAL(opline)) + Z_STRLEN(ZEND_OP1_LITERAL(last_op));
919 			if (IS_INTERNED(Z_STRVAL(ZEND_OP1_LITERAL(last_op)))) {
920 				char *tmp = emalloc(l + 1);
921 				memcpy(tmp, Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1);
922 				Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = tmp;
923 			} else {
924 				Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = erealloc(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1);
925 			}
926 			memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op))+Z_STRLEN(ZEND_OP1_LITERAL(last_op)), Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
927 			Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
928 			zval_dtor(&ZEND_OP1_LITERAL(opline));
929 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
930 			Z_STRVAL(ZEND_OP1_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1, 1 TSRMLS_CC);
931 			Z_TYPE(ZEND_OP1_LITERAL(last_op)) = IS_NULL;
932 #else
933 			Z_STRVAL(ZEND_OP1_LITERAL(opline)) = Z_STRVAL(ZEND_OP1_LITERAL(last_op));
934 #endif
935 			Z_STRLEN(ZEND_OP1_LITERAL(opline)) = l;
936 			MAKE_NOP(last_op);
937 		} else if (opline->opcode == ZEND_CONCAT &&
938 				  ZEND_OP2_TYPE(opline) == IS_CONST &&
939 				  ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
940 				  VAR_SOURCE(opline->op1) &&
941 				  (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT ||
942 				   VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_STRING) &&
943 				  ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
944 				  ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) {
945 			/* compress consecutive CONCATs */
946 			zend_op *src = VAR_SOURCE(opline->op1);
947 			int l;
948 
949 			if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
950 				convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
951 			}
952 			if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
953 				convert_to_string_safe(&ZEND_OP2_LITERAL(src));
954 			}
955 
956 			VAR_UNSET(opline->op1);
957 			if (ZEND_OP1_TYPE(src) == IS_UNUSED) {
958 				/* 5.3 may use IS_UNUSED as first argument to ZEND_ADD_... */
959 				opline->opcode = ZEND_ADD_STRING;
960 			}
961 			COPY_NODE(opline->op1, src->op1);
962 			l = Z_STRLEN(ZEND_OP2_LITERAL(opline)) + Z_STRLEN(ZEND_OP2_LITERAL(src));
963 			if (IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(src)))) {
964 				char *tmp = emalloc(l + 1);
965 				memcpy(tmp, Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1);
966 				Z_STRVAL(ZEND_OP2_LITERAL(src)) = tmp;
967 			} else {
968 				Z_STRVAL(ZEND_OP2_LITERAL(src)) = erealloc(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1);
969 			}
970 			memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src))+Z_STRLEN(ZEND_OP2_LITERAL(src)), Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
971 			Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
972 			if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) {
973 				efree(Z_STRVAL(ZEND_OP2_LITERAL(opline)));
974 			}
975 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
976 			Z_STRVAL(ZEND_OP2_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1, 1 TSRMLS_CC);
977 			Z_TYPE(ZEND_OP2_LITERAL(src)) = IS_NULL;
978 #else
979 			Z_STRVAL(ZEND_OP2_LITERAL(opline)) = Z_STRVAL(ZEND_OP2_LITERAL(src));
980 #endif
981 			Z_STRLEN(ZEND_OP2_LITERAL(opline)) = l;
982 			MAKE_NOP(src);
983 		} else if ((opline->opcode == ZEND_ADD_STRING || opline->opcode == ZEND_ADD_VAR) && ZEND_OP1_TYPE(opline) == IS_CONST) {
984 			/* convert ADD_STRING(C1, C2) to CONCAT(C1, C2) */
985 			opline->opcode = ZEND_CONCAT;
986 			continue;
987 		} else if (opline->opcode == ZEND_ADD_CHAR && ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP2_TYPE(opline) == IS_CONST) {
988             /* convert ADD_CHAR(C1, C2) to CONCAT(C1, C2) */
989 			char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
990 			ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
991 			opline->opcode = ZEND_CONCAT;
992 			continue;
993 		} else if ((opline->opcode == ZEND_ADD ||
994 					opline->opcode == ZEND_SUB ||
995 					opline->opcode == ZEND_MUL ||
996 					opline->opcode == ZEND_DIV ||
997 					opline->opcode == ZEND_MOD ||
998 					opline->opcode == ZEND_SL ||
999 					opline->opcode == ZEND_SR ||
1000 					opline->opcode == ZEND_CONCAT ||
1001 					opline->opcode == ZEND_IS_EQUAL ||
1002 					opline->opcode == ZEND_IS_NOT_EQUAL ||
1003 					opline->opcode == ZEND_IS_SMALLER ||
1004 					opline->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
1005 					opline->opcode == ZEND_IS_IDENTICAL ||
1006 					opline->opcode == ZEND_IS_NOT_IDENTICAL ||
1007 					opline->opcode == ZEND_BOOL_XOR ||
1008 					opline->opcode == ZEND_BW_OR ||
1009 					opline->opcode == ZEND_BW_AND ||
1010 					opline->opcode == ZEND_BW_XOR) &&
1011 					ZEND_OP1_TYPE(opline)==IS_CONST &&
1012 					ZEND_OP2_TYPE(opline)==IS_CONST) {
1013 			/* evaluate constant expressions */
1014 			int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode);
1015 			zval result;
1016 			int er;
1017 
1018             if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
1019                 ((Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG &&
1020                   Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) ||
1021                  (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_DOUBLE &&
1022                   Z_DVAL(ZEND_OP2_LITERAL(opline)) == 0.0))) {
1023 				if (RESULT_USED(opline)) {
1024 					SET_VAR_SOURCE(opline);
1025 				}
1026                 opline++;
1027 				continue;
1028 			}
1029 			er = EG(error_reporting);
1030 			EG(error_reporting) = 0;
1031 			if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) == SUCCESS) {
1032 				PZ_SET_REFCOUNT_P(&result, 1);
1033 				PZ_UNSET_ISREF_P(&result);
1034 
1035 				literal_dtor(&ZEND_OP1_LITERAL(opline));
1036 				literal_dtor(&ZEND_OP2_LITERAL(opline));
1037 				opline->opcode = ZEND_QM_ASSIGN;
1038 				SET_UNUSED(opline->op2);
1039 				update_op1_const(op_array, opline, &result TSRMLS_CC);
1040 			}
1041 			EG(error_reporting) = er;
1042 		} else if ((opline->opcode == ZEND_BOOL ||
1043 				   	opline->opcode == ZEND_BOOL_NOT ||
1044 				  	opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) {
1045 			/* evaluate constant unary ops */
1046 			unary_op_type unary_op = get_unary_op(opline->opcode);
1047 			zval result;
1048 
1049 			if (unary_op) {
1050 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1051 				unary_op(&result, &ZEND_OP1_LITERAL(opline));
1052 #else
1053 				unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC);
1054 #endif
1055 				literal_dtor(&ZEND_OP1_LITERAL(opline));
1056 			} else {
1057 				/* BOOL */
1058 				result = ZEND_OP1_LITERAL(opline);
1059 				convert_to_boolean(&result);
1060 				Z_TYPE(ZEND_OP1_LITERAL(opline)) = IS_NULL;
1061 			}
1062 			PZ_SET_REFCOUNT_P(&result, 1);
1063 			PZ_UNSET_ISREF_P(&result);
1064 			opline->opcode = ZEND_QM_ASSIGN;
1065 			update_op1_const(op_array, opline, &result TSRMLS_CC);
1066 		} else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) &&
1067 					ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1068 				   	VAR_SOURCE(opline->op1) &&
1069 				   	VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) {
1070 			/* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */
1071 			zend_op *src = VAR_SOURCE(opline->op1);
1072 			VAR_UNSET(opline->op1);
1073 			COPY_NODE(opline->op1, src->op1);
1074 			MAKE_NOP(src);
1075 		} else if ((opline->opcode == ZEND_ADD_STRING ||
1076 					opline->opcode == ZEND_ADD_CHAR) &&
1077 				  	ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1078 				  	VAR_SOURCE(opline->op1) &&
1079 				  	VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) {
1080 			/* convert T = INIT_STRING(), T = ADD_STRING(T, X) to T = QM_ASSIGN(X) */
1081 			/* CHECKME: Remove ZEND_ADD_VAR optimization, since some conversions -
1082 			   namely, BOOL(false)->string - don't allocate memory but use empty_string
1083 			   and ADD_CHAR fails */
1084 			zend_op *src = VAR_SOURCE(opline->op1);
1085 			VAR_UNSET(opline->op1);
1086 			COPY_NODE(opline->op1, opline->op2);
1087 			if (opline->opcode == ZEND_ADD_CHAR) {
1088 				char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
1089 				ZVAL_STRINGL(&ZEND_OP1_LITERAL(opline), &c, 1, 1);
1090 			}
1091 			SET_UNUSED(opline->op2);
1092 			MAKE_NOP(src);
1093 			opline->opcode = ZEND_QM_ASSIGN;
1094 		} else if ((opline->opcode == ZEND_ADD_STRING ||
1095 				   	opline->opcode == ZEND_ADD_CHAR ||
1096 				   	opline->opcode == ZEND_ADD_VAR ||
1097 				   	opline->opcode == ZEND_CONCAT) &&
1098 				  	ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1099 				  	VAR_SOURCE(opline->op1) &&
1100 				  	VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT &&
1101 				  	ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST &&
1102 				  	Z_TYPE(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == IS_STRING &&
1103 				  	Z_STRLEN(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == 0) {
1104 			/* convert T = CONCAT(X,''), T = ADD_STRING(T, Y) to T = CONCAT(X,Y) */
1105 			zend_op *src = VAR_SOURCE(opline->op1);
1106 			VAR_UNSET(opline->op1);
1107 			COPY_NODE(opline->op1, src->op1);
1108 			if (opline->opcode == ZEND_ADD_CHAR) {
1109 				char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
1110 				ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
1111 			}
1112 			opline->opcode = ZEND_CONCAT;
1113 			literal_dtor(&ZEND_OP2_LITERAL(src)); /* will take care of empty_string too */
1114 			MAKE_NOP(src);
1115 		} else if (opline->opcode == ZEND_ADD_VAR &&
1116 					ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1117 					VAR_SOURCE(opline->op1) &&
1118 					VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) {
1119 			/* convert T = INIT_STRING(), T = ADD_VAR(T, X) to T = CAST(STRING, X) */
1120 			zend_op *src = VAR_SOURCE(opline->op1);
1121 			VAR_UNSET(opline->op1);
1122 			COPY_NODE(opline->op1, opline->op2);
1123 			SET_UNUSED(opline->op2);
1124 			MAKE_NOP(src);
1125 			opline->opcode = ZEND_CAST;
1126 			opline->extended_value = IS_STRING;
1127 		} else if ((opline->opcode == ZEND_ADD_STRING ||
1128 					opline->opcode == ZEND_ADD_CHAR ||
1129 					opline->opcode == ZEND_ADD_VAR ||
1130 					opline->opcode == ZEND_CONCAT) &&
1131 					ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1132 					VAR_SOURCE(opline->op1) &&
1133 					VAR_SOURCE(opline->op1)->opcode == ZEND_CAST &&
1134 					VAR_SOURCE(opline->op1)->extended_value == IS_STRING) {
1135 			/* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
1136 			zend_op *src = VAR_SOURCE(opline->op1);
1137 			VAR_UNSET(opline->op1);
1138 			COPY_NODE(opline->op1, src->op1);
1139 			if (opline->opcode == ZEND_ADD_CHAR) {
1140 				char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline));
1141 				ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1);
1142 			}
1143 			opline->opcode = ZEND_CONCAT;
1144 			MAKE_NOP(src);
1145 		} else if (opline->opcode == ZEND_QM_ASSIGN &&
1146 					ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1147 					ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
1148 					ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
1149 			/* strip T = QM_ASSIGN(T) */
1150 			MAKE_NOP(opline);
1151 		} else if (opline->opcode == ZEND_BOOL &&
1152 					ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
1153 					VAR_SOURCE(opline->op1) &&
1154 					(VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL ||
1155 					VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL ||
1156 					VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER ||
1157 					VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL ||
1158 					VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL ||
1159 					VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL ||
1160 					VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
1161 					VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR ||
1162 					VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) &&
1163 					!used_ext[VAR_NUM(ZEND_OP1(opline).var)]) {
1164 			/* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */
1165 			zend_op *src = VAR_SOURCE(opline->op1);
1166 			COPY_NODE(src->result, opline->result);
1167 			SET_VAR_SOURCE(src);
1168 			MAKE_NOP(opline);
1169 		}
1170 		/* get variable source */
1171 		if (RESULT_USED(opline)) {
1172 			SET_VAR_SOURCE(opline);
1173 		}
1174 		if (opline->opcode != ZEND_NOP) {
1175 			last_op = opline;
1176 		}
1177 		opline++;
1178 	}
1179 
1180 	strip_nop(block);
1181 
1182 	if (op_array->T) {
1183 		efree(Tsource);
1184 	}
1185 }
1186 
1187 /* Rebuild plain (optimized) op_array from CFG */
assemble_code_blocks(zend_cfg * cfg,zend_op_array * op_array)1188 static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
1189 {
1190 	zend_code_block *blocks = cfg->blocks;
1191 	zend_op *new_opcodes = emalloc(op_array->last * sizeof(zend_op));
1192 	zend_op *opline = new_opcodes;
1193 	zend_code_block *cur_block = blocks;
1194 
1195 	/* Copy code of reachable blocks into a single buffer */
1196 	while (cur_block) {
1197 		if (cur_block->access) {
1198 			memcpy(opline, cur_block->start_opline, cur_block->len * sizeof(zend_op));
1199 			cur_block->start_opline = opline;
1200 			opline += cur_block->len;
1201 			if ((opline - 1)->opcode == ZEND_JMP) {
1202 				zend_code_block *next;
1203 				next = cur_block->next;
1204 				while (next && !next->access) {
1205 					next = next->next;
1206 				}
1207 				if (next && next == cur_block->op1_to) {
1208 					/* JMP to the next block - strip it */
1209 					cur_block->follow_to = cur_block->op1_to;
1210 					cur_block->op1_to = NULL;
1211 					MAKE_NOP((opline - 1));
1212 					opline--;
1213 					cur_block->len--;
1214 				}
1215 			}
1216 		} else {
1217 			/* this block will not be used, delete all constants there */
1218 			zend_op *_opl;
1219 			zend_op *end = cur_block->start_opline + cur_block->len;
1220 			for (_opl = cur_block->start_opline; _opl && _opl < end; _opl++) {
1221 				if (ZEND_OP1_TYPE(_opl) == IS_CONST) {
1222 					literal_dtor(&ZEND_OP1_LITERAL(_opl));
1223 				}
1224 				if (ZEND_OP2_TYPE(_opl) == IS_CONST) {
1225 					literal_dtor(&ZEND_OP2_LITERAL(_opl));
1226 				}
1227 			}
1228 		}
1229 		cur_block = cur_block->next;
1230 	}
1231 
1232 	if ((opline-1)->opcode == ZEND_THROW) {
1233 		/* if we finished with THROW, we need to add space between THROW and HANDLE to not confuse
1234 		   zend_throw_internal */
1235 		MAKE_NOP(opline);
1236 		opline->lineno = opline[-1].lineno;
1237 		opline++;
1238 	}
1239 #if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1240 	MAKE_NOP(opline);
1241 	opline->opcode = ZEND_HANDLE_EXCEPTION;
1242 	opline->lineno = opline[-1].lineno;
1243 	opline++;
1244 #endif
1245 
1246 	op_array->last = opline-new_opcodes;
1247 
1248 	/* adjust exception jump targets */
1249 	if (op_array->last_try_catch) {
1250 		int i, j;
1251 		for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
1252 			if (cfg->try[i]->access) {
1253 				op_array->try_catch_array[j].try_op = cfg->try[i]->start_opline - new_opcodes;
1254 				op_array->try_catch_array[j].catch_op = cfg->catch[i]->start_opline - new_opcodes;
1255 				j++;
1256 			}
1257 		}
1258 		op_array->last_try_catch = j;
1259 		efree(cfg->try);
1260 		efree(cfg->catch);
1261 	}
1262 
1263 	/* adjust loop jump targets */
1264 	if (op_array->last_brk_cont) {
1265 		int i;
1266 		for (i = 0; i< op_array->last_brk_cont; i++) {
1267 			op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
1268 			op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
1269 			op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
1270 		}
1271 		efree(cfg->loop_start);
1272 		efree(cfg->loop_cont);
1273 		efree(cfg->loop_brk);
1274 	}
1275 
1276     /* adjust jump targets */
1277 	for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
1278 		if (!cur_block->access) {
1279 			continue;
1280 		}
1281 		opline = cur_block->start_opline + cur_block->len - 1;
1282 		if (opline->opcode == ZEND_OP_DATA) {
1283 			opline--;
1284 		}
1285 		if (cur_block->op1_to) {
1286 			ZEND_OP1(opline).opline_num = cur_block->op1_to->start_opline - new_opcodes;
1287 		}
1288 		if (cur_block->op2_to) {
1289 			ZEND_OP2(opline).opline_num = cur_block->op2_to->start_opline - new_opcodes;
1290 		}
1291 		if (cur_block->ext_to) {
1292 			opline->extended_value = cur_block->ext_to->start_opline - new_opcodes;
1293 		}
1294 		print_block(cur_block, new_opcodes, "Out ");
1295 	}
1296 	efree(op_array->opcodes);
1297 	op_array->opcodes = erealloc(new_opcodes, op_array->last * sizeof(zend_op));
1298 
1299 #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1300 	/* adjust early binding list */
1301 	if (op_array->early_binding != (zend_uint)-1) {
1302 		zend_uint *opline_num = &op_array->early_binding;
1303 		zend_op *end;
1304 
1305 		opline = op_array->opcodes;
1306 		end = opline + op_array->last;
1307 		while (opline < end) {
1308 			if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
1309 				*opline_num = opline - op_array->opcodes;
1310 				opline_num = &ZEND_RESULT(opline).opline_num;
1311 			}
1312 			++opline;
1313 		}
1314 		*opline_num = -1;
1315 	}
1316 #endif
1317 }
1318 
zend_jmp_optimization(zend_code_block * block,zend_op_array * op_array,zend_code_block * blocks TSRMLS_DC)1319 static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks TSRMLS_DC)
1320 {
1321 	/* last_op is the last opcode of the current block */
1322 	zend_op *last_op = (block->start_opline + block->len - 1);
1323 
1324 	if (!block->len) {
1325 		return;
1326 	}
1327 	switch (last_op->opcode) {
1328 		case ZEND_JMP:
1329 			{
1330 				zend_op *target = block->op1_to->start_opline;
1331 				zend_code_block *next = block->next;
1332 
1333 				while (next && !next->access) {
1334 					/* find used one */
1335 					next = next->next;
1336 				}
1337 
1338 				/* JMP(next) -> NOP */
1339 				if (block->op1_to == next) {
1340 					block->follow_to = block->op1_to;
1341 					block->op1_to = NULL;
1342 					MAKE_NOP(last_op);
1343 					block->len--;
1344 					if (block->len == 0) {
1345 						/* this block is nothing but NOP now */
1346 						delete_code_block(block);
1347 					}
1348 					break;
1349 				}
1350 
1351 				if (((target->opcode == ZEND_JMP &&
1352 					block->op1_to != block->op1_to->op1_to) ||
1353 					target->opcode == ZEND_JMPZNZ) &&
1354 					!block->op1_to->protected) {
1355 					/* JMP L, L: JMP L1 -> JMP L1 */
1356 					/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
1357 					*last_op = *target;
1358 #if ZEND_EXTENSION_API_NO < PHP_5_4_X_API_NO
1359 					if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1360 						zval_copy_ctor(&ZEND_OP1_LITERAL(last_op));
1361 					}
1362 #else
1363 					if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1364 						zval zv = ZEND_OP1_LITERAL(last_op);
1365 						zval_copy_ctor(&zv);
1366 						last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv TSRMLS_CC);
1367 					}
1368 #endif
1369 					del_source(block, block->op1_to);
1370 					if (block->op1_to->op2_to) {
1371 						block->op2_to = block->op1_to->op2_to;
1372 						ADD_SOURCE(block, block->op2_to);
1373 					}
1374 					if (block->op1_to->ext_to) {
1375 						block->ext_to = block->op1_to->ext_to;
1376 						ADD_SOURCE(block, block->ext_to);
1377 					}
1378 					if (block->op1_to->op1_to) {
1379 						block->op1_to = block->op1_to->op1_to;
1380 						ADD_SOURCE(block, block->op1_to);
1381 					} else {
1382 						block->op1_to = NULL;
1383 					}
1384 				} else if (target->opcode == ZEND_RETURN ||
1385 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1386 				          target->opcode == ZEND_RETURN_BY_REF ||
1387 #endif
1388 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
1389             	          target->opcode == ZEND_FAST_RET ||
1390 #endif
1391 			    	      target->opcode == ZEND_EXIT) {
1392 					/* JMP L, L: RETURN to immediate RETURN */
1393 					*last_op = *target;
1394 #if ZEND_EXTENSION_API_NO < PHP_5_4_X_API_NO
1395 					if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1396 						zval_copy_ctor(&ZEND_OP1_LITERAL(last_op));
1397 					}
1398 #else
1399 					if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1400 						zval zv = ZEND_OP1_LITERAL(last_op);
1401 						zval_copy_ctor(&zv);
1402 						last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv TSRMLS_CC);
1403 					}
1404 #endif
1405 					del_source(block, block->op1_to);
1406 					block->op1_to = NULL;
1407 #if 0
1408 				/* Temporarily disabled - see bug #0025274 */
1409 				} else if (0&& block->op1_to != block &&
1410 			           block->op1_to != blocks &&
1411 						   op_array->last_try_catch == 0 &&
1412 				           target->opcode != ZEND_FREE &&
1413 				           target->opcode != ZEND_SWITCH_FREE) {
1414 				    /* Block Reordering (saves one JMP on each "for" loop iteration)
1415 				     * It is disabled for some cases (ZEND_FREE/ZEND_SWITCH_FREE)
1416 				     * which may break register allocation.
1417             	     */
1418 					zend_bool can_reorder = 0;
1419 					zend_block_source *cs = block->op1_to->sources;
1420 
1421 					/* the "target" block doesn't had any followed block */
1422 					while(cs) {
1423 						if (cs->from->follow_to == block->op1_to) {
1424 							can_reorder = 0;
1425 							break;
1426 						}
1427 						cs = cs->next;
1428 					}
1429 					if (can_reorder) {
1430 						next = block->op1_to;
1431 						/* the "target" block is not followed by current "block" */
1432 						while (next->follow_to != NULL) {
1433 							if (next->follow_to == block) {
1434 								can_reorder = 0;
1435 								break;
1436 							}
1437 							next = next->follow_to;
1438 						}
1439 						if (can_reorder) {
1440 							zend_code_block *prev = blocks;
1441 
1442 							while (prev->next != block->op1_to) {
1443 								prev = prev->next;
1444 							}
1445 							prev->next = next->next;
1446 							next->next = block->next;
1447 							block->next = block->op1_to;
1448 
1449 							block->follow_to = block->op1_to;
1450 							block->op1_to = NULL;
1451 							MAKE_NOP(last_op);
1452 							block->len--;
1453 							if(block->len == 0) {
1454 								/* this block is nothing but NOP now */
1455 								delete_code_block(block);
1456 							}
1457 							break;
1458 						}
1459 					}
1460 #endif
1461 				}
1462 			}
1463 			break;
1464 
1465 		case ZEND_JMPZ:
1466 		case ZEND_JMPNZ:
1467 			/* constant conditional JMPs */
1468 			if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1469 				int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1470 				if (last_op->opcode == ZEND_JMPZ) {
1471 					should_jmp = !should_jmp;
1472 				}
1473 				literal_dtor(&ZEND_OP1_LITERAL(last_op));
1474 				ZEND_OP1_TYPE(last_op) = IS_UNUSED;
1475 				if (should_jmp) {
1476 					/* JMPNZ(true) -> JMP */
1477 					last_op->opcode = ZEND_JMP;
1478 					COPY_NODE(last_op->op1, last_op->op2);
1479 					block->op1_to = block->op2_to;
1480 					del_source(block, block->follow_to);
1481 					block->op2_to = NULL;
1482 					block->follow_to = NULL;
1483 				} else {
1484 					/* JMPNZ(false) -> NOP */
1485 					MAKE_NOP(last_op);
1486 					del_source(block, block->op2_to);
1487 					block->op2_to = NULL;
1488 				}
1489 				break;
1490 			}
1491 
1492 			if (block->op2_to) {
1493 				zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1494 				zend_uint same_var = VAR_NUM_EX(last_op->op1);
1495 				zend_op *target;
1496 				zend_op *target_end;
1497 				zend_code_block *target_block = block->op2_to;;
1498 
1499 next_target:
1500 				target = target_block->start_opline;
1501 				target_end = target_block->start_opline + target_block->len;
1502 				while (target < target_end && target->opcode == ZEND_NOP) {
1503 					target++;
1504 				}
1505 
1506 				/* next block is only NOP's */
1507 				if (target == target_end) {
1508 					target_block = target_block->follow_to;
1509 					goto next_target;
1510 				} else if (target->opcode == INV_COND(last_op->opcode) &&
1511 					/* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
1512 				   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1513 				   same_type == ZEND_OP1_TYPE(target) &&
1514 				   same_var == VAR_NUM_EX(target->op1) &&
1515 				   target_block->follow_to &&
1516 				   !target_block->protected
1517 				   ) {
1518 					del_source(block, block->op2_to);
1519 					block->op2_to = target_block->follow_to;
1520 					ADD_SOURCE(block, block->op2_to);
1521 				} else if (target->opcode == INV_COND_EX(last_op->opcode) &&
1522 							(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1523 				    		same_type == ZEND_OP1_TYPE(target) &&
1524 				    		same_var == VAR_NUM_EX(target->op1) &&
1525 							target_block->follow_to &&
1526 							!target_block->protected) {
1527 					/* JMPZ(X, L), L: X = JMPNZ_EX(X, L2) -> JMPZ(X, L+1) */
1528 					last_op->opcode += 3;
1529 					last_op->result = target->result;
1530 					del_source(block, block->op2_to);
1531 					block->op2_to = target_block->follow_to;
1532 					ADD_SOURCE(block, block->op2_to);
1533 				} else if (target_block->op2_to &&
1534 						   target->opcode == last_op->opcode &&
1535 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1536 						   same_type == ZEND_OP1_TYPE(target) &&
1537 						   same_var == VAR_NUM_EX(target->op1) &&
1538 						   !target_block->protected) {
1539 					/* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
1540 					del_source(block, block->op2_to);
1541 					block->op2_to = target_block->op2_to;
1542 					ADD_SOURCE(block, block->op2_to);
1543 				} else if (target_block->op1_to &&
1544 							target->opcode == ZEND_JMP &&
1545 							!target_block->protected) {
1546 					/* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
1547 					del_source(block, block->op2_to);
1548 					block->op2_to = target_block->op1_to;
1549 					ADD_SOURCE(block, block->op2_to);
1550 				} else if (target_block->op2_to &&
1551 							target_block->ext_to &&
1552 							target->opcode == ZEND_JMPZNZ &&
1553 							(ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1554 				        	same_type == ZEND_OP1_TYPE(target) &&
1555 				        	same_var == VAR_NUM_EX(target->op1) &&
1556 							!target_block->protected) {
1557 					/* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
1558 					del_source(block, block->op2_to);
1559 					if (last_op->opcode == ZEND_JMPZ) {
1560 						block->op2_to = target_block->op2_to;
1561 					} else {
1562 						block->op2_to = target_block->ext_to;
1563 					}
1564 					ADD_SOURCE(block, block->op2_to);
1565 				}
1566 			}
1567 
1568 			if (block->follow_to &&
1569 			    (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
1570 				zend_op *target;
1571 				zend_op *target_end;
1572 
1573 				while (1) {
1574 					target = block->follow_to->start_opline;
1575 					target_end = block->follow_to->start_opline + block->follow_to->len;
1576 					while (target < target_end && target->opcode == ZEND_NOP) {
1577 						target++;
1578 					}
1579 
1580 					/* next block is only NOP's */
1581 					if (target == target_end) {
1582 						del_source(block, block->follow_to);
1583 						block->follow_to = block->follow_to->follow_to;
1584 						ADD_SOURCE(block, block->follow_to);
1585 					} else {
1586 						break;
1587 					}
1588 				}
1589 				/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
1590 				if (target->opcode == ZEND_JMP &&
1591 					block->follow_to->op1_to &&
1592 					!block->follow_to->protected) {
1593 					del_source(block, block->follow_to);
1594 					if (last_op->opcode == ZEND_JMPZ) {
1595 						block->ext_to = block->follow_to->op1_to;
1596 						ADD_SOURCE(block, block->ext_to);
1597 					} else {
1598 						block->ext_to = block->op2_to;
1599 						block->op2_to = block->follow_to->op1_to;
1600 						ADD_SOURCE(block, block->op2_to);
1601 					}
1602 					block->follow_to = NULL;
1603 					last_op->opcode = ZEND_JMPZNZ;
1604 				}
1605 			}
1606 			break;
1607 
1608 		case ZEND_JMPNZ_EX:
1609 		case ZEND_JMPZ_EX:
1610 			/* constant conditional JMPs */
1611 			if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1612 				int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1613 				if (last_op->opcode == ZEND_JMPZ_EX) {
1614 					should_jmp = !should_jmp;
1615 				}
1616 				if (!should_jmp) {
1617 					/* T = JMPZ_EX(true,L)   -> T = QM_ASSIGN(true)
1618 					 * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
1619 					 */
1620 					last_op->opcode = ZEND_QM_ASSIGN;
1621 					SET_UNUSED(last_op->op2);
1622 					del_source(block, block->op2_to);
1623 					block->op2_to = NULL;
1624 				}
1625 				break;
1626 			}
1627 
1628 			if (block->op2_to) {
1629 				zend_op *target, *target_end;
1630 				char *same_t=NULL;
1631 				zend_code_block *target_block;
1632 				int var_num = 0;
1633 				if (op_array->T >= (zend_uint)op_array->last_var) {
1634 					var_num = op_array->T;
1635 				} else {
1636 					var_num = op_array->last_var;
1637 				}
1638 				if (var_num <= 0) {
1639    					return;
1640 				}
1641 				same_t = ecalloc(var_num, sizeof(char));
1642 				if (same_t == NULL) {
1643 					return;
1644 				}
1645 				same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
1646 				same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
1647 				target_block = block->op2_to;
1648 next_target_ex:
1649 				target = target_block->start_opline;
1650 				target_end = target_block->start_opline + target_block->len;
1651 				while (target < target_end && target->opcode == ZEND_NOP) {
1652 					target++;
1653 				}
1654  				/* next block is only NOP's */
1655 				if (target == target_end) {
1656 					target_block = target_block->follow_to;
1657 					goto next_target_ex;
1658 				} else if (target_block->op2_to &&
1659 						   target->opcode == last_op->opcode-3 &&
1660 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1661 						   (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1662 						   !target_block->protected) {
1663 					/* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1664 					del_source(block, block->op2_to);
1665 					block->op2_to = target_block->op2_to;
1666 					ADD_SOURCE(block, block->op2_to);
1667 				} else if (target_block->op2_to &&
1668 						   target->opcode == INV_EX_COND(last_op->opcode) &&
1669 					   	   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1670 						   (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1671 						   !target_block->protected) {
1672 					/* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
1673 					del_source(block, block->op2_to);
1674 					block->op2_to = target_block->follow_to;
1675 					ADD_SOURCE(block, block->op2_to);
1676 				} else if (target_block->op2_to &&
1677 						   target->opcode == INV_EX_COND_EX(last_op->opcode) &&
1678 					       (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1679 						   (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1680 						   (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
1681 						   !target_block->protected) {
1682 					/* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
1683 					del_source(block, block->op2_to);
1684 					block->op2_to = target_block->follow_to;
1685 					ADD_SOURCE(block, block->op2_to);
1686 				} else if (target_block->op2_to &&
1687 						   target->opcode == last_op->opcode &&
1688 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1689 						   (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1690 						   (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
1691 						   !target_block->protected) {
1692 					/* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1693 					del_source(block, block->op2_to);
1694 					block->op2_to = target_block->op2_to;
1695 					ADD_SOURCE(block, block->op2_to);
1696 				} else if (target_block->op1_to &&
1697 						   target->opcode == ZEND_JMP &&
1698 						   !target_block->protected) {
1699 					/* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
1700 					del_source(block, block->op2_to);
1701 					block->op2_to = target_block->op1_to;
1702 					ADD_SOURCE(block, block->op2_to);
1703 				} else if (target_block->op2_to &&
1704 						   target_block->ext_to &&
1705 						   target->opcode == ZEND_JMPZNZ &&
1706 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1707 						   (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1708 						   !target_block->protected) {
1709 					/* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
1710 					del_source(block, block->op2_to);
1711 					if (last_op->opcode == ZEND_JMPZ_EX) {
1712 						block->op2_to = target_block->op2_to;
1713 					} else {
1714 						block->op2_to = target_block->ext_to;
1715 					}
1716 					ADD_SOURCE(block, block->op2_to);
1717 				}
1718 				if (same_t != NULL) {
1719 					efree(same_t);
1720 				}
1721 			}
1722 			break;
1723 
1724 		case ZEND_JMPZNZ: {
1725 			zend_code_block *next = block->next;
1726 
1727 			while (next && !next->access) {
1728 				/* find first accessed one */
1729 				next = next->next;
1730 			}
1731 
1732 			if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1733 				if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
1734 					/* JMPZNZ(false,L1,L2) -> JMP(L1) */
1735 					zend_code_block *todel;
1736 
1737 					literal_dtor(&ZEND_OP1_LITERAL(last_op));
1738 					last_op->opcode = ZEND_JMP;
1739 					SET_UNUSED(last_op->op1);
1740 					SET_UNUSED(last_op->op2);
1741 					block->op1_to = block->op2_to;
1742 					todel = block->ext_to;
1743 					block->op2_to = NULL;
1744 					block->ext_to = NULL;
1745 					del_source(block, todel);
1746 				} else {
1747 					/* JMPZNZ(true,L1,L2) -> JMP(L2) */
1748 					zend_code_block *todel;
1749 
1750 					literal_dtor(&ZEND_OP1_LITERAL(last_op));
1751 					last_op->opcode = ZEND_JMP;
1752 					SET_UNUSED(last_op->op1);
1753 					SET_UNUSED(last_op->op2);
1754 					block->op1_to = block->ext_to;
1755 					todel =  block->op2_to;
1756 					block->op2_to = NULL;
1757 					block->ext_to = NULL;
1758 					del_source(block, todel);
1759 				}
1760 			} else if (block->op2_to == block->ext_to) {
1761 				/* both goto the same one - it's JMP */
1762 				if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
1763 					/* JMPZNZ(?,L,L) -> JMP(L) */
1764 					last_op->opcode = ZEND_JMP;
1765 					SET_UNUSED(last_op->op1);
1766 					SET_UNUSED(last_op->op2);
1767 					block->op1_to = block->op2_to;
1768 					block->op2_to = NULL;
1769 					block->ext_to = NULL;
1770 				}
1771 			} else if (block->op2_to == next) {
1772 				/* jumping to next on Z - can follow to it and jump only on NZ */
1773 				/* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
1774 				last_op->opcode = ZEND_JMPNZ;
1775 				block->op2_to = block->ext_to;
1776 				block->follow_to = next;
1777 				block->ext_to = NULL;
1778 				/* no need to add source - it's block->op2_to */
1779 			} else if (block->ext_to == next) {
1780 				/* jumping to next on NZ - can follow to it and jump only on Z */
1781 				/* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
1782 				last_op->opcode = ZEND_JMPZ;
1783 				block->follow_to = next;
1784 				block->ext_to = NULL;
1785 				/* no need to add source - it's block->ext_to */
1786 			}
1787 
1788 			if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
1789 				zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1790 				zend_uchar same_var = VAR_NUM_EX(last_op->op1);
1791 				zend_op *target;
1792 				zend_op *target_end;
1793 				zend_code_block *target_block = block->op2_to;
1794 
1795 next_target_znz:
1796 				target = target_block->start_opline;
1797 				target_end = target_block->start_opline + target_block->len;
1798 				while (target < target_end && target->opcode == ZEND_NOP) {
1799 					target++;
1800 				}
1801 				/* next block is only NOP's */
1802 				if (target == target_end) {
1803 					target_block = target_block->follow_to;
1804 					goto next_target_znz;
1805 				} else if (target_block->op2_to &&
1806 						   (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
1807 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1808 						   same_type == ZEND_OP1_TYPE(target) &&
1809 						   same_var == VAR_NUM_EX(target->op1) &&
1810 						   !target_block->protected) {
1811 				    /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
1812 					del_source(block, block->op2_to);
1813 					block->op2_to = target_block->op2_to;
1814 					ADD_SOURCE(block, block->op2_to);
1815 				} else if (target->opcode == ZEND_JMPNZ &&
1816 						   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1817 						   same_type == ZEND_OP1_TYPE(target) &&
1818 						   same_var == VAR_NUM_EX(target->op1) &&
1819 						   target_block->follow_to &&
1820 						   !target_block->protected) {
1821                     /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
1822 					del_source(block, block->op2_to);
1823 					block->op2_to = target_block->follow_to;
1824 					ADD_SOURCE(block, block->op2_to);
1825 				} else if (target_block->op1_to &&
1826 					       target->opcode == ZEND_JMP &&
1827 					       !target_block->protected) {
1828                     /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
1829 					del_source(block, block->op2_to);
1830 					block->op2_to = target_block->op1_to;
1831 					ADD_SOURCE(block, block->op2_to);
1832 				}
1833 			}
1834 			break;
1835 		}
1836 	}
1837 }
1838 
1839 /* Global data dependencies */
1840 
1841 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1842 
1843 # define T_USAGE(op) do { \
1844 		if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
1845 		   !defined_here[VAR_NUM(op.var)] && !used_ext[VAR_NUM(op.var)]) {	\
1846 			used_ext[VAR_NUM(op.var)] = 1;									\
1847 		} \
1848 	} while (0)
1849 
1850 # define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !usage[VAR_NUM(op.var)]) /* !used_ext[op.var] && */
1851 # define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
1852 
1853 #else
1854 
1855 # define T_USAGE(op) do { \
1856 		if ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && \
1857 		   !defined_here[VAR_NUM(op.u.var)] && !used_ext[VAR_NUM(op.u.var)]) {	\
1858 			used_ext[VAR_NUM(op.u.var)] = 1;									\
1859 		} \
1860 	} while (0)
1861 
1862 # define NEVER_USED(op) ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && !usage[VAR_NUM(op.u.var)]) /* !used_ext[op.u.var] && */
1863 # define RES_NEVER_USED(opline) (ZEND_RESULT_TYPE(opline) == IS_UNUSED || NEVER_USED(opline->result))
1864 
1865 #endif
1866 
1867 /* Find a set of variables which are used outside of the block where they are
1868  * defined. We won't apply some optimization patterns for sush variables. */
zend_t_usage(zend_code_block * block,zend_op_array * op_array,char * used_ext)1869 static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, char *used_ext)
1870 {
1871 	zend_code_block *next_block = block->next;
1872 	char *usage;
1873 	char *defined_here;
1874 
1875 	if (op_array->T == 0) {
1876 		/* shortcut - if no Ts, nothing to do */
1877 		return;
1878 	}
1879 
1880 	usage = ecalloc(op_array->T, 1);
1881 	defined_here = emalloc(op_array->T);
1882 
1883 	while (next_block) {
1884 		zend_op *opline = next_block->start_opline;
1885 		zend_op *end = opline + next_block->len;
1886 
1887 		if (!next_block->access) {
1888 			next_block = next_block->next;
1889 			continue;
1890 		}
1891 		memset(defined_here, 0, op_array->T);
1892 
1893 		while (opline<end) {
1894 			T_USAGE(opline->op1);
1895 			T_USAGE(opline->op2);
1896 
1897 			if (RESULT_USED(opline)) {
1898 				if (!defined_here[VAR_NUM(ZEND_RESULT(opline).var)] && !used_ext[VAR_NUM(ZEND_RESULT(opline).var)] &&
1899 				    (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT ||
1900 					(opline->opcode == ZEND_OP_DATA && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR) ||
1901 					opline->opcode == ZEND_ADD_ARRAY_ELEMENT)) {
1902 					/* these opcodes use the result as argument */
1903 					used_ext[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
1904 				}
1905 				defined_here[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
1906 			}
1907 			opline++;
1908 		}
1909 		next_block = next_block->next;
1910 	}
1911 
1912 #if DEBUG_BLOCKPASS
1913 	{
1914 		int i;
1915 		for (i = 0; i< op_array->T; i++) {
1916 			fprintf(stderr, "T%d: %c\n", i, used_ext[i] + '0');
1917 		}
1918 	}
1919 #endif
1920 
1921 	while (block) {
1922 		zend_op *opline = block->start_opline + block->len - 1;
1923 
1924 		if (!block->access) {
1925 			block = block->next;
1926 			continue;
1927 		}
1928 
1929 		memcpy(usage, used_ext, op_array->T);
1930 
1931 		while (opline >= block->start_opline) {
1932 			/* usage checks */
1933 			if (RES_NEVER_USED(opline)) {
1934 				switch (opline->opcode) {
1935 					case ZEND_ASSIGN_ADD:
1936 					case ZEND_ASSIGN_SUB:
1937 					case ZEND_ASSIGN_MUL:
1938 					case ZEND_ASSIGN_DIV:
1939 					case ZEND_ASSIGN_MOD:
1940 					case ZEND_ASSIGN_SL:
1941 					case ZEND_ASSIGN_SR:
1942 					case ZEND_ASSIGN_CONCAT:
1943 					case ZEND_ASSIGN_BW_OR:
1944 					case ZEND_ASSIGN_BW_AND:
1945 					case ZEND_ASSIGN_BW_XOR:
1946 					case ZEND_PRE_INC:
1947 					case ZEND_PRE_DEC:
1948 					case ZEND_POST_INC:
1949 					case ZEND_POST_DEC:
1950 					case ZEND_ASSIGN:
1951 					case ZEND_ASSIGN_REF:
1952 					case ZEND_DO_FCALL:
1953 					case ZEND_DO_FCALL_BY_NAME:
1954 						if (ZEND_RESULT_TYPE(opline) == IS_VAR) {
1955 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1956 							ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
1957 #else
1958 							ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED;
1959 #endif
1960 						}
1961 						break;
1962 					case ZEND_QM_ASSIGN:
1963 					case ZEND_BOOL:
1964 					case ZEND_BOOL_NOT:
1965 						if (ZEND_OP1_TYPE(opline) == IS_CONST) {
1966 							literal_dtor(&ZEND_OP1_LITERAL(opline));
1967 						}
1968 						MAKE_NOP(opline);
1969 						break;
1970 					case ZEND_PRINT:
1971 						opline->opcode = ZEND_ECHO;
1972 						ZEND_RESULT_TYPE(opline) = IS_UNUSED;
1973 						break;
1974 					case ZEND_JMPZ_EX:
1975 					case ZEND_JMPNZ_EX:
1976 						opline->opcode -= 3;
1977 						SET_UNUSED(opline->result);
1978 						break;
1979 				}
1980 			}
1981 
1982 			if (opline->opcode == ZEND_RECV ||
1983                 opline->opcode == ZEND_RECV_INIT ||
1984                 opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1985 				if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
1986 					usage[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
1987 				}
1988 			} else {
1989 				if (RESULT_USED(opline)) {
1990 					usage[VAR_NUM(ZEND_RESULT(opline).var)] = 0;
1991 				}
1992 			}
1993 
1994 			if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
1995 				usage[VAR_NUM(ZEND_OP1(opline).var)] = 1;
1996 			}
1997 			if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
1998 				usage[VAR_NUM(ZEND_OP2(opline).var)] = 1;
1999 			}
2000 
2001 
2002 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2003 			if ((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
2004                 (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
2005                 usage[VAR_NUM(ZEND_RESULT(opline).var)]) {
2006 				ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
2007  			}
2008 #else
2009 			if (ZEND_RESULT_TYPE(opline) == IS_VAR &&
2010 			    usage[VAR_NUM(ZEND_RESULT(opline).var)] &&
2011 			    (ZEND_RESULT(opline).EA.type & EXT_TYPE_UNUSED) != 0) {
2012 				ZEND_RESULT(opline).EA.type &= ~EXT_TYPE_UNUSED;
2013 			}
2014 #endif
2015 
2016 			opline--;
2017 		}
2018 		block = block->next;
2019 	} /* end blocks */
2020 
2021 	efree(defined_here);
2022 	efree(usage);
2023 }
2024 
2025 #define PASSES 3
2026 
zend_block_optimization(zend_op_array * op_array TSRMLS_DC)2027 static void zend_block_optimization(zend_op_array *op_array TSRMLS_DC)
2028 {
2029 	zend_cfg cfg;
2030 	zend_code_block *cur_block;
2031 	int pass;
2032 	char *usage;
2033 
2034 #if DEBUG_BLOCKPASS
2035 	fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main");
2036 	fflush(stderr);
2037 #endif
2038 
2039 #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
2040 	if (op_array->has_finally_block) {
2041 		return;
2042 	}
2043 #endif
2044 
2045     /* Build CFG */
2046 	if (!find_code_blocks(op_array, &cfg)) {
2047 		return;
2048 	}
2049 
2050 	zend_rebuild_access_path(&cfg, op_array, 0);
2051 	/* full rebuild here to produce correct sources! */
2052 	usage = emalloc(op_array->T);
2053 	for (pass = 0; pass < PASSES; pass++) {
2054 		/* Compute data dependencies */
2055 		memset(usage, 0, op_array->T);
2056 		zend_t_usage(cfg.blocks, op_array, usage);
2057 
2058 		/* optimize each basic block separately */
2059 		for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2060 			if (!cur_block->access) {
2061 				continue;
2062 			}
2063 			zend_optimize_block(cur_block, op_array, usage TSRMLS_CC);
2064 		}
2065 
2066 		/* Jump optimization for each block */
2067 		for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2068 			if (!cur_block->access) {
2069 				continue;
2070 			}
2071 			zend_jmp_optimization(cur_block, op_array, cfg.blocks TSRMLS_CC);
2072 		}
2073 
2074 		/* Eliminate unreachable basic blocks */
2075 		zend_rebuild_access_path(&cfg, op_array, 1);
2076 	}
2077 
2078 	memset(usage, 0, op_array->T);
2079 	zend_t_usage(cfg.blocks, op_array, usage);
2080 	assemble_code_blocks(&cfg, op_array);
2081 	efree(usage);
2082 
2083 	/* Destroy CFG */
2084 	for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2085 		zend_block_source *cs = cur_block->sources;
2086 		while (cs) {
2087 			zend_block_source *n = cs->next;
2088 			efree(cs);
2089 			cs = n;
2090 		}
2091 	}
2092 	efree(cfg.blocks);
2093 }
2094