1#define DEBUG_BLOCKPASS 0
2
3/* Checks if a constant (like "true") may be replaced by its value */
4static 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
43static 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. */
83static 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
334static 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
360static 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->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
426static 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
458static 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 */
484static 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
561static 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
606static 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 */
1188static 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
1319static 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#if ZEND_EXTENSION_API_NO > PHP_5_6_X_API_NO
1470                int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op) TSRMLS_CC);
1471#else
1472                int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1473#endif
1474
1475                if (last_op->opcode == ZEND_JMPZ) {
1476                    should_jmp = !should_jmp;
1477                }
1478                literal_dtor(&ZEND_OP1_LITERAL(last_op));
1479                ZEND_OP1_TYPE(last_op) = IS_UNUSED;
1480                if (should_jmp) {
1481                    /* JMPNZ(true) -> JMP */
1482                    last_op->opcode = ZEND_JMP;
1483                    COPY_NODE(last_op->op1, last_op->op2);
1484                    block->op1_to = block->op2_to;
1485                    del_source(block, block->follow_to);
1486                    block->op2_to = NULL;
1487                    block->follow_to = NULL;
1488                } else {
1489                    /* JMPNZ(false) -> NOP */
1490                    MAKE_NOP(last_op);
1491                    del_source(block, block->op2_to);
1492                    block->op2_to = NULL;
1493                }
1494                break;
1495            }
1496
1497            if (block->op2_to) {
1498                zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1499                zend_uint same_var = VAR_NUM_EX(last_op->op1);
1500                zend_op *target;
1501                zend_op *target_end;
1502                zend_code_block *target_block = block->op2_to;;
1503
1504next_target:
1505                target = target_block->start_opline;
1506                target_end = target_block->start_opline + target_block->len;
1507                while (target < target_end && target->opcode == ZEND_NOP) {
1508                    target++;
1509                }
1510
1511                /* next block is only NOP's */
1512                if (target == target_end) {
1513                    target_block = target_block->follow_to;
1514                    goto next_target;
1515                } else if (target->opcode == INV_COND(last_op->opcode) &&
1516                    /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
1517                   (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1518                   same_type == ZEND_OP1_TYPE(target) &&
1519                   same_var == VAR_NUM_EX(target->op1) &&
1520                   target_block->follow_to &&
1521                   !target_block->protected
1522                   ) {
1523                    del_source(block, block->op2_to);
1524                    block->op2_to = target_block->follow_to;
1525                    ADD_SOURCE(block, block->op2_to);
1526                } else if (target->opcode == INV_COND_EX(last_op->opcode) &&
1527                            (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1528                            same_type == ZEND_OP1_TYPE(target) &&
1529                            same_var == VAR_NUM_EX(target->op1) &&
1530                            target_block->follow_to &&
1531                            !target_block->protected) {
1532                    /* JMPZ(X, L), L: X = JMPNZ_EX(X, L2) -> JMPZ(X, L+1) */
1533                    last_op->opcode += 3;
1534                    last_op->result = target->result;
1535                    del_source(block, block->op2_to);
1536                    block->op2_to = target_block->follow_to;
1537                    ADD_SOURCE(block, block->op2_to);
1538                } else if (target_block->op2_to &&
1539                           target->opcode == last_op->opcode &&
1540                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1541                           same_type == ZEND_OP1_TYPE(target) &&
1542                           same_var == VAR_NUM_EX(target->op1) &&
1543                           !target_block->protected) {
1544                    /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
1545                    del_source(block, block->op2_to);
1546                    block->op2_to = target_block->op2_to;
1547                    ADD_SOURCE(block, block->op2_to);
1548                } else if (target_block->op1_to &&
1549                            target->opcode == ZEND_JMP &&
1550                            !target_block->protected) {
1551                    /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
1552                    del_source(block, block->op2_to);
1553                    block->op2_to = target_block->op1_to;
1554                    ADD_SOURCE(block, block->op2_to);
1555                } else if (target_block->op2_to &&
1556                            target_block->ext_to &&
1557                            target->opcode == ZEND_JMPZNZ &&
1558                            (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1559                            same_type == ZEND_OP1_TYPE(target) &&
1560                            same_var == VAR_NUM_EX(target->op1) &&
1561                            !target_block->protected) {
1562                    /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
1563                    del_source(block, block->op2_to);
1564                    if (last_op->opcode == ZEND_JMPZ) {
1565                        block->op2_to = target_block->op2_to;
1566                    } else {
1567                        block->op2_to = target_block->ext_to;
1568                    }
1569                    ADD_SOURCE(block, block->op2_to);
1570                }
1571            }
1572
1573            if (block->follow_to &&
1574                (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) {
1575                zend_op *target;
1576                zend_op *target_end;
1577
1578                while (1) {
1579                    target = block->follow_to->start_opline;
1580                    target_end = block->follow_to->start_opline + block->follow_to->len;
1581                    while (target < target_end && target->opcode == ZEND_NOP) {
1582                        target++;
1583                    }
1584
1585                    /* next block is only NOP's */
1586                    if (target == target_end) {
1587                        del_source(block, block->follow_to);
1588                        block->follow_to = block->follow_to->follow_to;
1589                        ADD_SOURCE(block, block->follow_to);
1590                    } else {
1591                        break;
1592                    }
1593                }
1594                /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
1595                if (target->opcode == ZEND_JMP &&
1596                    block->follow_to->op1_to &&
1597                    !block->follow_to->protected) {
1598                    del_source(block, block->follow_to);
1599                    if (last_op->opcode == ZEND_JMPZ) {
1600                        block->ext_to = block->follow_to->op1_to;
1601                        ADD_SOURCE(block, block->ext_to);
1602                    } else {
1603                        block->ext_to = block->op2_to;
1604                        block->op2_to = block->follow_to->op1_to;
1605                        ADD_SOURCE(block, block->op2_to);
1606                    }
1607                    block->follow_to = NULL;
1608                    last_op->opcode = ZEND_JMPZNZ;
1609                }
1610            }
1611            break;
1612
1613        case ZEND_JMPNZ_EX:
1614        case ZEND_JMPZ_EX:
1615            /* constant conditional JMPs */
1616            if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1617#if ZEND_EXTENSION_API_NO > PHP_5_6_X_API_NO
1618                int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op) TSRMLS_CC);
1619#else
1620                int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
1621#endif
1622
1623                if (last_op->opcode == ZEND_JMPZ_EX) {
1624                    should_jmp = !should_jmp;
1625                }
1626                if (!should_jmp) {
1627                    /* T = JMPZ_EX(true,L)   -> T = QM_ASSIGN(true)
1628                     * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
1629                     */
1630                    last_op->opcode = ZEND_QM_ASSIGN;
1631                    SET_UNUSED(last_op->op2);
1632                    del_source(block, block->op2_to);
1633                    block->op2_to = NULL;
1634                }
1635                break;
1636            }
1637
1638            if (block->op2_to) {
1639                zend_op *target, *target_end;
1640                char *same_t=NULL;
1641                zend_code_block *target_block;
1642                int var_num = 0;
1643                if (op_array->T >= (zend_uint)op_array->last_var) {
1644                    var_num = op_array->T;
1645                } else {
1646                    var_num = op_array->last_var;
1647                }
1648                if (var_num <= 0) {
1649                    return;
1650                }
1651                same_t = ecalloc(var_num, sizeof(char));
1652                if (same_t == NULL) {
1653                    return;
1654                }
1655                same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
1656                same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
1657                target_block = block->op2_to;
1658next_target_ex:
1659                target = target_block->start_opline;
1660                target_end = target_block->start_opline + target_block->len;
1661                while (target < target_end && target->opcode == ZEND_NOP) {
1662                    target++;
1663                }
1664                /* next block is only NOP's */
1665                if (target == target_end) {
1666                    target_block = target_block->follow_to;
1667                    goto next_target_ex;
1668                } else if (target_block->op2_to &&
1669                           target->opcode == last_op->opcode-3 &&
1670                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1671                           (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1672                           !target_block->protected) {
1673                    /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1674                    del_source(block, block->op2_to);
1675                    block->op2_to = target_block->op2_to;
1676                    ADD_SOURCE(block, block->op2_to);
1677                } else if (target_block->op2_to &&
1678                           target->opcode == INV_EX_COND(last_op->opcode) &&
1679                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1680                           (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1681                           !target_block->protected) {
1682                    /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, 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 == INV_EX_COND_EX(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 = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
1693                    del_source(block, block->op2_to);
1694                    block->op2_to = target_block->follow_to;
1695                    ADD_SOURCE(block, block->op2_to);
1696                } else if (target_block->op2_to &&
1697                           target->opcode == last_op->opcode &&
1698                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1699                           (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1700                           (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
1701                           !target_block->protected) {
1702                    /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1703                    del_source(block, block->op2_to);
1704                    block->op2_to = target_block->op2_to;
1705                    ADD_SOURCE(block, block->op2_to);
1706                } else if (target_block->op1_to &&
1707                           target->opcode == ZEND_JMP &&
1708                           !target_block->protected) {
1709                    /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
1710                    del_source(block, block->op2_to);
1711                    block->op2_to = target_block->op1_to;
1712                    ADD_SOURCE(block, block->op2_to);
1713                } else if (target_block->op2_to &&
1714                           target_block->ext_to &&
1715                           target->opcode == ZEND_JMPZNZ &&
1716                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1717                           (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
1718                           !target_block->protected) {
1719                    /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
1720                    del_source(block, block->op2_to);
1721                    if (last_op->opcode == ZEND_JMPZ_EX) {
1722                        block->op2_to = target_block->op2_to;
1723                    } else {
1724                        block->op2_to = target_block->ext_to;
1725                    }
1726                    ADD_SOURCE(block, block->op2_to);
1727                }
1728                if (same_t != NULL) {
1729                    efree(same_t);
1730                }
1731            }
1732            break;
1733
1734        case ZEND_JMPZNZ: {
1735            zend_code_block *next = block->next;
1736
1737            while (next && !next->access) {
1738                /* find first accessed one */
1739                next = next->next;
1740            }
1741
1742            if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
1743#if ZEND_EXTENSION_API_NO > PHP_5_6_X_API_NO
1744                if (!zend_is_true(&ZEND_OP1_LITERAL(last_op) TSRMLS_CC)) {
1745#else
1746                if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
1747#endif
1748                    /* JMPZNZ(false,L1,L2) -> JMP(L1) */
1749                    zend_code_block *todel;
1750
1751                    literal_dtor(&ZEND_OP1_LITERAL(last_op));
1752                    last_op->opcode = ZEND_JMP;
1753                    SET_UNUSED(last_op->op1);
1754                    SET_UNUSED(last_op->op2);
1755                    block->op1_to = block->op2_to;
1756                    todel = block->ext_to;
1757                    block->op2_to = NULL;
1758                    block->ext_to = NULL;
1759                    del_source(block, todel);
1760                } else {
1761                    /* JMPZNZ(true,L1,L2) -> JMP(L2) */
1762                    zend_code_block *todel;
1763
1764                    literal_dtor(&ZEND_OP1_LITERAL(last_op));
1765                    last_op->opcode = ZEND_JMP;
1766                    SET_UNUSED(last_op->op1);
1767                    SET_UNUSED(last_op->op2);
1768                    block->op1_to = block->ext_to;
1769                    todel =  block->op2_to;
1770                    block->op2_to = NULL;
1771                    block->ext_to = NULL;
1772                    del_source(block, todel);
1773                }
1774            } else if (block->op2_to == block->ext_to) {
1775                /* both goto the same one - it's JMP */
1776                /* JMPZNZ(?,L,L) -> JMP(L) */
1777                last_op->opcode = ZEND_JMP;
1778                SET_UNUSED(last_op->op1);
1779                SET_UNUSED(last_op->op2);
1780                block->op1_to = block->op2_to;
1781                block->op2_to = NULL;
1782                block->ext_to = NULL;
1783            } else if (block->op2_to == next) {
1784                /* jumping to next on Z - can follow to it and jump only on NZ */
1785                /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
1786                last_op->opcode = ZEND_JMPNZ;
1787                block->op2_to = block->ext_to;
1788                block->follow_to = next;
1789                block->ext_to = NULL;
1790                /* no need to add source - it's block->op2_to */
1791            } else if (block->ext_to == next) {
1792                /* jumping to next on NZ - can follow to it and jump only on Z */
1793                /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
1794                last_op->opcode = ZEND_JMPZ;
1795                block->follow_to = next;
1796                block->ext_to = NULL;
1797                /* no need to add source - it's block->ext_to */
1798            }
1799
1800            if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) {
1801                zend_uchar same_type = ZEND_OP1_TYPE(last_op);
1802                zend_uchar same_var = VAR_NUM_EX(last_op->op1);
1803                zend_op *target;
1804                zend_op *target_end;
1805                zend_code_block *target_block = block->op2_to;
1806
1807next_target_znz:
1808                target = target_block->start_opline;
1809                target_end = target_block->start_opline + target_block->len;
1810                while (target < target_end && target->opcode == ZEND_NOP) {
1811                    target++;
1812                }
1813                /* next block is only NOP's */
1814                if (target == target_end) {
1815                    target_block = target_block->follow_to;
1816                    goto next_target_znz;
1817                } else if (target_block->op2_to &&
1818                           (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
1819                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1820                           same_type == ZEND_OP1_TYPE(target) &&
1821                           same_var == VAR_NUM_EX(target->op1) &&
1822                           !target_block->protected) {
1823                    /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
1824                    del_source(block, block->op2_to);
1825                    block->op2_to = target_block->op2_to;
1826                    ADD_SOURCE(block, block->op2_to);
1827                } else if (target->opcode == ZEND_JMPNZ &&
1828                           (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
1829                           same_type == ZEND_OP1_TYPE(target) &&
1830                           same_var == VAR_NUM_EX(target->op1) &&
1831                           target_block->follow_to &&
1832                           !target_block->protected) {
1833                    /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
1834                    del_source(block, block->op2_to);
1835                    block->op2_to = target_block->follow_to;
1836                    ADD_SOURCE(block, block->op2_to);
1837                } else if (target_block->op1_to &&
1838                           target->opcode == ZEND_JMP &&
1839                           !target_block->protected) {
1840                    /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
1841                    del_source(block, block->op2_to);
1842                    block->op2_to = target_block->op1_to;
1843                    ADD_SOURCE(block, block->op2_to);
1844                }
1845            }
1846            break;
1847        }
1848    }
1849}
1850
1851/* Global data dependencies */
1852
1853#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1854
1855# define T_USAGE(op) do { \
1856        if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \
1857           !defined_here[VAR_NUM(op.var)] && !used_ext[VAR_NUM(op.var)]) {  \
1858            used_ext[VAR_NUM(op.var)] = 1;                                  \
1859        } \
1860    } while (0)
1861
1862# define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !usage[VAR_NUM(op.var)]) /* !used_ext[op.var] && */
1863# define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result))
1864
1865#else
1866
1867# define T_USAGE(op) do { \
1868        if ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && \
1869           !defined_here[VAR_NUM(op.u.var)] && !used_ext[VAR_NUM(op.u.var)]) {  \
1870            used_ext[VAR_NUM(op.u.var)] = 1;                                    \
1871        } \
1872    } while (0)
1873
1874# 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] && */
1875# define RES_NEVER_USED(opline) (ZEND_RESULT_TYPE(opline) == IS_UNUSED || NEVER_USED(opline->result))
1876
1877#endif
1878
1879/* Find a set of variables which are used outside of the block where they are
1880 * defined. We won't apply some optimization patterns for such variables. */
1881static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, char *used_ext)
1882{
1883    zend_code_block *next_block = block->next;
1884    char *usage;
1885    char *defined_here;
1886
1887    if (op_array->T == 0) {
1888        /* shortcut - if no Ts, nothing to do */
1889        return;
1890    }
1891
1892    usage = ecalloc(op_array->T, 1);
1893    defined_here = emalloc(op_array->T);
1894
1895    while (next_block) {
1896        zend_op *opline = next_block->start_opline;
1897        zend_op *end = opline + next_block->len;
1898
1899        if (!next_block->access) {
1900            next_block = next_block->next;
1901            continue;
1902        }
1903        memset(defined_here, 0, op_array->T);
1904
1905        while (opline<end) {
1906            T_USAGE(opline->op1);
1907            T_USAGE(opline->op2);
1908
1909            if (RESULT_USED(opline)) {
1910                if (!defined_here[VAR_NUM(ZEND_RESULT(opline).var)] && !used_ext[VAR_NUM(ZEND_RESULT(opline).var)] &&
1911                    (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT ||
1912#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
1913                     opline->opcode == ZEND_RECV_VARIADIC ||
1914#endif
1915                    (opline->opcode == ZEND_OP_DATA && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR) ||
1916                    opline->opcode == ZEND_ADD_ARRAY_ELEMENT)) {
1917                    /* these opcodes use the result as argument */
1918                    used_ext[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
1919                }
1920                defined_here[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
1921            }
1922            opline++;
1923        }
1924        next_block = next_block->next;
1925    }
1926
1927#if DEBUG_BLOCKPASS
1928    {
1929        int i;
1930        for (i = 0; i< op_array->T; i++) {
1931            fprintf(stderr, "T%d: %c\n", i, used_ext[i] + '0');
1932        }
1933    }
1934#endif
1935
1936    while (block) {
1937        zend_op *opline = block->start_opline + block->len - 1;
1938
1939        if (!block->access) {
1940            block = block->next;
1941            continue;
1942        }
1943
1944        memcpy(usage, used_ext, op_array->T);
1945
1946        while (opline >= block->start_opline) {
1947            /* usage checks */
1948            if (RES_NEVER_USED(opline)) {
1949                switch (opline->opcode) {
1950                    case ZEND_ASSIGN_ADD:
1951                    case ZEND_ASSIGN_SUB:
1952                    case ZEND_ASSIGN_MUL:
1953                    case ZEND_ASSIGN_DIV:
1954                    case ZEND_ASSIGN_MOD:
1955                    case ZEND_ASSIGN_SL:
1956                    case ZEND_ASSIGN_SR:
1957                    case ZEND_ASSIGN_CONCAT:
1958                    case ZEND_ASSIGN_BW_OR:
1959                    case ZEND_ASSIGN_BW_AND:
1960                    case ZEND_ASSIGN_BW_XOR:
1961                    case ZEND_PRE_INC:
1962                    case ZEND_PRE_DEC:
1963                    case ZEND_POST_INC:
1964                    case ZEND_POST_DEC:
1965                    case ZEND_ASSIGN:
1966                    case ZEND_ASSIGN_REF:
1967                    case ZEND_DO_FCALL:
1968                    case ZEND_DO_FCALL_BY_NAME:
1969                        if (ZEND_RESULT_TYPE(opline) == IS_VAR) {
1970#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1971                            ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED;
1972#else
1973                            ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED;
1974#endif
1975                        }
1976                        break;
1977                    case ZEND_QM_ASSIGN:
1978                    case ZEND_BOOL:
1979                    case ZEND_BOOL_NOT:
1980                        if (ZEND_OP1_TYPE(opline) == IS_CONST) {
1981                            literal_dtor(&ZEND_OP1_LITERAL(opline));
1982                        }
1983                        MAKE_NOP(opline);
1984                        break;
1985                    case ZEND_PRINT:
1986                        opline->opcode = ZEND_ECHO;
1987                        ZEND_RESULT_TYPE(opline) = IS_UNUSED;
1988                        break;
1989                    case ZEND_JMPZ_EX:
1990                    case ZEND_JMPNZ_EX:
1991                        opline->opcode -= 3;
1992                        SET_UNUSED(opline->result);
1993                        break;
1994                }
1995            }
1996
1997            if (opline->opcode == ZEND_RECV ||
1998                opline->opcode == ZEND_RECV_INIT ||
1999#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
2000                opline->opcode == ZEND_RECV_VARIADIC ||
2001#endif
2002                opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
2003                if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
2004                    usage[VAR_NUM(ZEND_RESULT(opline).var)] = 1;
2005                }
2006            } else {
2007                if (RESULT_USED(opline)) {
2008                    usage[VAR_NUM(ZEND_RESULT(opline).var)] = 0;
2009                }
2010            }
2011
2012            if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) {
2013                usage[VAR_NUM(ZEND_OP1(opline).var)] = 1;
2014            }
2015            if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) {
2016                usage[VAR_NUM(ZEND_OP2(opline).var)] = 1;
2017            }
2018
2019
2020#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2021            if ((ZEND_RESULT_TYPE(opline) & IS_VAR) &&
2022                (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) &&
2023                usage[VAR_NUM(ZEND_RESULT(opline).var)]) {
2024                ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED;
2025            }
2026#else
2027            if (ZEND_RESULT_TYPE(opline) == IS_VAR &&
2028                usage[VAR_NUM(ZEND_RESULT(opline).var)] &&
2029                (ZEND_RESULT(opline).EA.type & EXT_TYPE_UNUSED) != 0) {
2030                ZEND_RESULT(opline).EA.type &= ~EXT_TYPE_UNUSED;
2031            }
2032#endif
2033
2034            opline--;
2035        }
2036        block = block->next;
2037    } /* end blocks */
2038
2039    efree(defined_here);
2040    efree(usage);
2041}
2042
2043#define PASSES 3
2044
2045static void zend_block_optimization(zend_op_array *op_array TSRMLS_DC)
2046{
2047    zend_cfg cfg;
2048    zend_code_block *cur_block;
2049    int pass;
2050    char *usage;
2051
2052#if DEBUG_BLOCKPASS
2053    fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main");
2054    fflush(stderr);
2055#endif
2056
2057#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
2058    if (op_array->has_finally_block) {
2059        return;
2060    }
2061#endif
2062
2063    /* Build CFG */
2064    if (!find_code_blocks(op_array, &cfg)) {
2065        return;
2066    }
2067
2068    zend_rebuild_access_path(&cfg, op_array, 0);
2069    /* full rebuild here to produce correct sources! */
2070    usage = emalloc(op_array->T);
2071    for (pass = 0; pass < PASSES; pass++) {
2072        /* Compute data dependencies */
2073        memset(usage, 0, op_array->T);
2074        zend_t_usage(cfg.blocks, op_array, usage);
2075
2076        /* optimize each basic block separately */
2077        for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2078            if (!cur_block->access) {
2079                continue;
2080            }
2081            zend_optimize_block(cur_block, op_array, usage TSRMLS_CC);
2082        }
2083
2084        /* Jump optimization for each block */
2085        for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2086            if (!cur_block->access) {
2087                continue;
2088            }
2089            zend_jmp_optimization(cur_block, op_array, cfg.blocks TSRMLS_CC);
2090        }
2091
2092        /* Eliminate unreachable basic blocks */
2093        zend_rebuild_access_path(&cfg, op_array, 1);
2094    }
2095
2096    memset(usage, 0, op_array->T);
2097    zend_t_usage(cfg.blocks, op_array, usage);
2098    assemble_code_blocks(&cfg, op_array);
2099    efree(usage);
2100
2101    /* Destroy CFG */
2102    for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) {
2103        zend_block_source *cs = cur_block->sources;
2104        while (cs) {
2105            zend_block_source *n = cs->next;
2106            efree(cs);
2107            cs = n;
2108        }
2109    }
2110    efree(cfg.blocks);
2111}
2112