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