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