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