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