1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2008 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.0 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_0.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: Ilia Alshanetsky <iliaa@php.net>                            |
16  |          Graham Kelly <graham@php.net>                               |
17  |          Dmitry Stogov (Truck MMcache)                               |
18  |          Derick Rethans <derick@php.net>                             |
19  +----------------------------------------------------------------------+
20*/
21/*
22Portions of the code below are based on the Turck MMCache by Dmitry Stogov
23
24The components derived from Turck MMCache are licensed under the BSD license
25for the purposes of this optimizer. All other components of Turck MMCache
26retain their existing GPL license.
27*/
28
29/* $Id: optimize.c 281559 2009-06-02 19:19:02Z graham $ */
30
31#include "zend.h"
32#include "zend_API.h"
33#include "zend_constants.h"
34#include "zend_compile.h"
35#include "zend_extensions.h"
36#include "zend_ini.h"
37#include "zend_exceptions.h"
38
39#include "SAPI.h"
40#include "php.h"
41
42#include "ext/standard/basic_functions.h"
43#include "ext/standard/pageinfo.h"
44#include "ext/standard/info.h"
45#include "ext/standard/md5.h"
46#include "ext/standard/sha1.h"
47#include "ext/standard/crc32.h"
48#include "ext/standard/php_string.h"
49#include "ext/standard/php_math.h"
50#include "ext/standard/php_rand.h"
51#include "ext/standard/html.h"
52#include "ext/standard/php_versioning.h"
53
54#include "php_optimizer.h"
55#include "optimize.h"
56#include "optimizer_debug.h"
57#include "optimize_fcr.h"
58
59
60
61unsigned int zops[] = {
62/* ZEND_NOP*/ EXT_UNUSED | OP1_UNUSED | OP2_UNUSED | RES_UNUSED,
63/* ZEND_ADD*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
64/* ZEND_SUB*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
65/* ZEND_MUL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
66/* ZEND_DIV*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
67/* ZEND_MOD*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
68/* ZEND_SL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
69/* ZEND_SR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
70/* ZEND_CONCAT*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
71/* ZEND_BW_OR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
72/* ZEND_BW_AND*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
73/* ZEND_BW_XOR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
74/* ZEND_BW_NOT*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_TMP,
75/* ZEND_BOOL_NOT*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
76/* ZEND_BOOL_XOR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
77/* ZEND_IS_IDENTICAL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
78/* ZEND_IS_NOT_IDENTICAL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
79/* ZEND_IS_EQUAL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
80/* ZEND_IS_NOT_EQUAL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
81/* ZEND_IS_SMALLER*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
82/* ZEND_IS_SMALLER_OR_EQUAL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
83/* ZEND_CAST*/ EXT_CAST| OP1_STD| OP2_UNUSED | RES_TMP,
84/* ZEND_QM_ASSIGN*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_TMP,
85/* ZEND_ASSIGN_ADD*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
86/* ZEND_ASSIGN_SUB*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
87/* ZEND_ASSIGN_MUL*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
88/* ZEND_ASSIGN_DIV*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
89/* ZEND_ASSIGN_MOD*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
90/* ZEND_ASSIGN_SL*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
91/* ZEND_ASSIGN_SR*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
92/* ZEND_ASSIGN_CONCAT*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
93/* ZEND_ASSIGN_BW_OR*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
94/* ZEND_ASSIGN_BW_AND*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
95/* ZEND_ASSIGN_BW_XOR*/ EXT_ASSIGN | OP1_STD| OP2_STD| RES_VAR,
96/* ZEND_PRE_INC*/ EXT_UNUSED | OP1_VAR| OP2_UNUSED | RES_VAR,
97/* ZEND_PRE_DEC*/ EXT_UNUSED | OP1_VAR| OP2_UNUSED | RES_VAR,
98/* ZEND_POST_INC*/ EXT_UNUSED | OP1_VAR| OP2_UNUSED | RES_TMP,
99/* ZEND_POST_DEC*/ EXT_UNUSED | OP1_VAR| OP2_UNUSED | RES_TMP,
100/* ZEND_ASSIGN*/ EXT_UNUSED | OP1_VAR| OP2_STD| RES_VAR,
101/* ZEND_ASSIGN_REF*/ EXT_UNUSED | OP1_VAR| OP2_VAR| RES_VAR,
102/* ZEND_ECHO*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
103/* ZEND_PRINT*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_TMP,
104/* ZEND_JMP*/ EXT_UNUSED | OP1_JMPADDR| OP2_UNUSED | RES_UNUSED,
105/* ZEND_JMPZ*/ EXT_UNUSED | OP1_STD| OP2_JMPADDR| RES_UNUSED,
106/* ZEND_JMPNZ*/ EXT_UNUSED | OP1_STD| OP2_JMPADDR| RES_UNUSED,
107/* ZEND_JMPZNZ*/ EXT_OPLINE | OP1_STD| OP2_OPLINE | RES_UNUSED,
108/* ZEND_JMPZ_EX*/ EXT_UNUSED | OP1_STD| OP2_JMPADDR| RES_TMP,
109/* ZEND_JMPNZ_EX*/ EXT_UNUSED | OP1_STD| OP2_JMPADDR| RES_TMP,
110/* ZEND_CASE*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
111/* ZEND_SWITCH_FREE*/ EXT_BIT| OP1_STD| OP2_UNUSED | RES_UNUSED,
112/* ZEND_BRK*/ EXT_UNUSED | OP1_BRK| OP2_STD| RES_UNUSED,
113/* ZEND_CONT*/ EXT_UNUSED | OP1_CONT| OP2_STD| RES_UNUSED,
114/* ZEND_BOOL*/ EXT_UNUSED | OPS_STD| OP2_UNUSED | RES_TMP,
115/* ZEND_INIT_STRING*/ EXT_UNUSED | OP1_UNUSED | OP2_UNUSED | RES_TMP,
116/* ZEND_ADD_CHAR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
117/* ZEND_ADD_STRING*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
118/* ZEND_ADD_VAR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
119/* ZEND_BEGIN_SILENCE*/ EXT_UNUSED | OP1_UNUSED | OP2_UNUSED | RES_TMP,
120/* ZEND_END_SILENCE*/ EXT_UNUSED | OP1_TMP| OP2_UNUSED | RES_UNUSED,
121/* ZEND_INIT_FCALL_BY_NAME*/ EXT_INIT_FCALL | OP1_STD| OP2_STD| RES_UNUSED,
122/* ZEND_DO_FCALL*/ EXT_FCALL | OP1_STD| OP2_OPLINE | RES_VAR,
123/* ZEND_DO_FCALL_BY_NAME*/ EXT_FCALL | OP1_STD| OP2_OPLINE | RES_VAR,
124/* ZEND_RETURN*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
125/* ZEND_RECV*/ EXT_UNUSED | OP1_ARG| OP2_UNUSED | RES_VAR,
126/* ZEND_RECV_INIT*/ EXT_UNUSED | OP1_ARG| OP2_STD| RES_VAR,
127/* ZEND_SEND_VAL*/ EXT_SEND| OP1_STD| OP2_ARG| RES_UNUSED,
128/* ZEND_SEND_VAR*/ EXT_SEND| OP1_VAR| OP2_ARG| RES_UNUSED,
129/* ZEND_SEND_REF*/ EXT_SEND| OP1_VAR| OP2_ARG| RES_UNUSED,
130/* ZEND_NEW*/ EXT_UNUSED | OP1_CLASS | OP2_UNUSED | RES_VAR,
131/* ZEND_JMP_NO_CTOR*/ EXT_UNUSED | OP1_STD| OP2_OPLINE | RES_UNUSED,
132/* ZEND_FREE*/ EXT_UNUSED | OP1_TMP| OP2_UNUSED | RES_UNUSED,
133/* ZEND_INIT_ARRAY*/ EXT_BIT| OP1_STD| OP2_STD| RES_TMP,
134/* ZEND_ADD_ARRAY_ELEMENT*/ EXT_BIT| OP1_STD| OP2_STD| RES_TMP,
135/* ZEND_INCLUDE_OR_EVAL*/ EXT_UNUSED | OP1_STD| OP2_INCLUDE| RES_VAR,
136/* ZEND_UNSET_VAR*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_UNUSED,
137/* ZEND_UNSET_DIM*/ EXT_STD| OP1_STD| OP2_STD| RES_UNUSED,
138/* ZEND_UNSET_OBJ*/ EXT_STD| OP1_STD| OP2_STD| RES_UNUSED,
139/* ZEND_FE_RESET*/ EXT_BIT| OP1_STD| OP2_OPLINE | RES_VAR,
140/* ZEND_FE_FETCH*/ EXT_FE | OP1_STD| OP2_OPLINE | RES_TMP,
141/* ZEND_EXIT*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
142/* ZEND_FETCH_R*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_VAR,
143/* ZEND_FETCH_DIM_R*/ EXT_FETCH | OP1_VAR| OP2_STD| RES_VAR,
144/* ZEND_FETCH_OBJ_R*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
145/* ZEND_FETCH_W*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_VAR,
146/* ZEND_FETCH_DIM_W*/ EXT_UNUSED | OP1_VAR| OP2_STD| RES_VAR,
147/* ZEND_FETCH_OBJ_W*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
148/* ZEND_FETCH_RW*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_VAR,
149/* ZEND_FETCH_DIM_RW*/ EXT_UNUSED | OP1_VAR| OP2_STD| RES_VAR,
150/* ZEND_FETCH_OBJ_RW*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
151/* ZEND_FETCH_IS*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_VAR,
152/* ZEND_FETCH_DIM_IS*/ EXT_UNUSED | OP1_VAR| OP2_STD| RES_VAR,
153/* ZEND_FETCH_OBJ_IS*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
154/* ZEND_FETCH_FUNC_ARG*/ EXT_ARG| OP1_STD| OP2_FETCH | RES_VAR,
155/* ZEND_FETCH_DIM_FUNC_ARG*/ EXT_ARG| OP1_VAR| OP2_STD| RES_VAR,
156/* ZEND_FETCH_OBJ_FUNC_ARG*/ EXT_ARG| OP1_STD| OP2_STD| RES_VAR,
157/* ZEND_FETCH_UNSET*/ EXT_UNUSED | OP1_STD| OP2_FETCH | RES_VAR,
158/* ZEND_FETCH_DIM_UNSET*/ EXT_UNUSED | OP1_VAR| OP2_STD| RES_VAR,
159/* ZEND_FETCH_OBJ_UNSET*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
160/* ZEND_FETCH_DIM_TMP_VAR*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
161/* ZEND_FETCH_CONSTANT*/ EXT_UNUSED | OP1_UCLASS | OP2_STD| RES_TMP,
162/* ZEND_DECLARE_FUNCTION_OR_CLASS*/ EXT_DECLARE| OP1_STD| OP2_STD| RES_UNUSED,
163/* ZEND_EXT_STMT*/ EXT_STD| OP1_STD| OP2_STD| RES_STD,
164/* ZEND_EXT_FCALL_BEGIN*/ EXT_STD| OP1_STD| OP2_STD| RES_STD,
165/* ZEND_EXT_FCALL_END*/ EXT_STD| OP1_STD| OP2_STD| RES_STD,
166/* ZEND_EXT_NOP*/ EXT_UNUSED | OP1_UNUSED | OP2_UNUSED | RES_UNUSED,
167/* ZEND_TICKS*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
168/* ZEND_SEND_VAR_NO_REF*/ EXT_SEND_NOREF| OP1_VAR | OP2_ARG| RES_UNUSED,
169/* ZEND_CATCH*/ EXT_OPLINE | OP1_CLASS | OP2_STD| RES_UNUSED,
170/* ZEND_THROW*/ EXT_UNUSED | OP1_STD| OP2_OPLINE | RES_UNUSED,
171/* ZEND_FETCH_CLASS*/ EXT_FCLASS | OP1_STD| OP2_STD| RES_CLASS,
172/* ZEND_CLONE*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_VAR,
173/* ZEND_INIT_CTOR_CALL*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
174/* ZEND_INIT_METHOD_CALL*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
175/* ZEND_INIT_STATIC_METHOD_CALL*/ EXT_UNUSED | OP1_UCLASS | OP2_STD| RES_UNUSED,
176/* ZEND_ISSET_ISEMPTY_VAR*/ EXT_ISSET | OP1_STD| OP2_FETCH | RES_TMP,
177/* ZEND_ISSET_ISEMPTY_DIM_OBJ*/ EXT_ISSET | OP1_STD| OP2_STD| RES_TMP,
178/* ZEND_IMPORT_FUNCTION*/ EXT_UNUSED | OP1_CLASS | OP2_STD| RES_UNUSED,
179/* ZEND_IMPORT_CLASS*/ EXT_UNUSED | OP1_CLASS | OP2_STD| RES_UNUSED,
180/* ZEND_IMPORT_CONST*/ EXT_UNUSED | OP1_CLASS | OP2_STD| RES_UNUSED,
181/* ZEND_OP_119*/ EXT_STD| OP1_STD| OP2_STD| RES_STD,
182/* ZEND_OP_120*/ EXT_STD| OP1_STD| OP2_STD| RES_STD,
183/* ZEND_ASSIGN_ADD_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
184/* ZEND_ASSIGN_SUB_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
185/* ZEND_ASSIGN_MUL_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
186/* ZEND_ASSIGN_DIV_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
187/* ZEND_ASSIGN_MOD_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
188/* ZEND_ASSIGN_SL_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
189/* ZEND_ASSIGN_SR_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
190/* ZEND_ASSIGN_CONCAT_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
191/* ZEND_ASSIGN_BW_OR_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
192/* ZEND_ASSIGN_BW_AND_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
193/* ZEND_ASSIGN_BW_XOR_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
194/* ZEND_PRE_INC_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
195/* ZEND_PRE_DEC_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
196/* ZEND_POST_INC_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
197/* ZEND_POST_DEC_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_TMP,
198/* ZEND_ASSIGN_OBJ*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
199/* ZEND_OP_DATA*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_STD,
200/* ZEND_INSTANCEOF*/ EXT_UNUSED | OP1_STD| OP2_CLASS | RES_TMP,
201/* ZEND_DECLARE_CLASS*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_CLASS,
202/* ZEND_DECLARE_INHERITED_CLASS*/ EXT_CLASS | OP1_STD| OP2_STD| RES_CLASS,
203/* ZEND_DECLARE_FUNCTION*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_UNUSED,
204/* ZEND_RAISE_ABSTRACT_ERROR*/ EXT_UNUSED | OP1_UNUSED | OP2_UNUSED | RES_UNUSED,
205/* ZEND_START_NAMESPACE*/ EXT_UNUSED | OP1_STD| OP2_UNUSED | RES_UNUSED,
206/* ZEND_ADD_INTERFACE*/ EXT_IFACE | OP1_CLASS | OP2_CLASS | RES_UNUSED,
207/* ZEND_VERIFY_INSTANCEOF*/ EXT_UNUSED | OP1_CLASS | OP2_STD| RES_UNUSED,
208/* ZEND_VERIFY_ABSTRACT_CLASS*/ EXT_UNUSED | OP1_CLASS | OP2_UNUSED | RES_UNUSED,
209/* ZEND_ASSIGN_DIM*/ EXT_UNUSED | OP1_STD| OP2_STD| RES_VAR,
210/* ZEND_ISSET_ISEMPTY_PROP_OBJ*/ EXT_ISSET | OP1_STD| OP2_STD| RES_TMP,
211/* ZEND_HANDLE_EXCEPTION*/ EXT_STD| OP1_UNUSED | OP2_UNUSED | RES_STD,
212/* ZEND_USER_OPCODE*/ EXT_STD| OP1_UNUSED | OP2_UNUSED | RES_STD,
213};
214
215
216
217zend_op *optimize_next_op(zend_op *op)
218{
219    zend_op *x = op + 1;
220
221    while (x && x->opcode == ZEND_NOP) {
222        x++;
223    }
224
225    return x;
226}
227
228
229zend_op *optimize_prev_op(zend_op *op)
230{
231  TSRMLS_FETCH();
232    zend_op* x = op-1;
233    zend_op_array *op_array = OPTIMIZER_G(op_array);
234    zend_op* start_op = op_array->opcodes;
235
236    while (x && x >= start_op && x->opcode == ZEND_NOP) {
237        x--;
238    }
239    if (x == start_op && x->opcode == ZEND_NOP) {
240        return NULL;
241    } else {
242        return x;
243    }
244}
245
246
247zend_uint optimizer_fetch_free_tmp_var(zend_op_array* op_array)
248{
249    return (sizeof(temp_variable) * op_array->T++);
250}
251
252void optimizer_parse_level(TSRMLS_D)
253{
254    zend_uint level = OPTIMIZER_G(optimization_level);
255
256    if (level > 0) {
257        level = (level > 6 ? 6 : level);
258
259        switch (level) {
260            case 1:
261                /* BASIC optimizations ONLY */
262                /*
263                 * 2 passes of the peephole optimizer
264                 * variable optimization
265                 * jump optimization (done in peephole)
266                 */
267                 OPTIMIZER_G(peephole_passes) = 2;
268                break;
269            case 2:
270                /* BASIC with Data Flow Analysis */
271                /*
272                 * 2 passes of the peephole optimizer WITH data flow analysis on
273                 * variable optimization
274                 * jump optimization (done with peephole)
275                 */
276                OPTIMIZER_G(peephole_passes) = 2;
277                break;
278            case 3:
279                /* BASIC, DFA and global optimizations */
280                /*
281                 * Once the optimizer is able to gain information about an
282                 *   entire sorce file level 3 might be where this should
283                 *   actually happen... Basically at this point things like
284                 *   inling and whatnot can start to happen as we will gather
285                 *   better global info about the scope of a op-array
286                 *
287                 */
288                OPTIMIZER_G(peephole_passes) = 2;
289                break;
290            case 4:
291                /* BASIC, DFA, global, CSE and Loop? */
292                /* Not sure yet... but basically a lot of optimizations... */
293                OPTIMIZER_G(peephole_passes) = 2;
294                break;
295            case 5:
296                /* Full out optimization */
297                OPTIMIZER_G(peephole_passes) = 3;
298                /* Possible future settings */
299                /* OPTIMIZER_G(do_dfa) = 1; */
300                /* OPTIMIZER_G(do_cse) = 1; */
301                /* OPTIMIZER_G(do_scheduling) = 1; */
302                /* OPTIMIZER_G(do_loops) = 1; */
303                break;
304            case 6:
305                /* EVERYTHING AND experimental optimizations */
306                /*
307                 * Everything from level 6 PLUS any experimental optimizations
308                 *   that *might* not be finished or have all the bugs and
309                 *   quarks worked out.
310                 */
311                OPTIMIZER_G(peephole_passes) = 3;
312                break;
313        }
314    }
315}
316
317static int optimize_is_disabled(zend_op_array* op_array TSRMLS_DC)
318{
319    char mangled_name[1024];
320    int len;
321    char *disabled_functions = OPTIMIZER_G(disabled_functions);
322    char *p;
323
324    if (disabled_functions == NULL) {
325        return 0;
326    }
327
328    /* Build the function name. There are three kinds of names:
329     * (1) standalone function, for example: foo
330     * (2) class member function, for example: a.b
331     * (3) code of a file, the file path
332     */
333    if (op_array->scope) {
334        OPTIMIZE_ASSERT(op_array->scope->name && op_array->function_name);
335        len = snprintf(mangled_name, sizeof(mangled_name), "%s.%s", op_array->scope->name, op_array->function_name);
336    } else if (op_array->function_name) {
337        len = snprintf(mangled_name, sizeof(mangled_name), "%s", op_array->function_name);
338    } else {
339        len = snprintf(mangled_name, sizeof(mangled_name), "%s", op_array->filename);
340    }
341    p = strstr(disabled_functions, mangled_name);
342    if (!p) {
343        return 0;
344    }
345    if (p > disabled_functions && *(p - 1) != ' ') {
346        return 0;
347    }
348    if (*(p + len) && *(p + len) != ' ') {
349        return 0;
350    }
351    return 1;
352}
353
354static void get_var(CB* cb, zend_op_array* op_array, char* global)
355{
356    CB* p = cb;
357    OPTIMIZER_ALLOCA_FLAG(used_use_heap)
358    char *used = optimizer_do_alloca(op_array->T * sizeof(char), used_use_heap);
359
360    if (!used) {
361        return;
362    }
363
364    memset(global, 0, op_array->T * sizeof(char));
365
366    if (p != NULL && p->next != NULL) {
367        int cb_count = 0;
368        OPTIMIZER_ALLOCA_FLAG(def_use_heap)
369        char *def = optimizer_do_alloca(op_array->T * sizeof(char), def_use_heap);
370
371        if (!def) {
372            return;
373        }
374
375        while (p != NULL) {
376            zend_op* op = p->start;
377            zend_op* end = op + p->len;
378
379            memset(def, 0, op_array->T * sizeof(char));
380
381            while (op < end) {
382                if ((OP1_TYPE(op) == IS_VAR || OP1_TYPE(op) == IS_TMP_VAR) && !def[VAR_NUM(OP1_VR(op))] && !global[VAR_NUM(OP1_VR(op))]) {
383                    global[VAR_NUM(OP1_VR(op))] = 1;
384                }
385                if ((OP2_TYPE(op) == IS_VAR || OP2_TYPE(op) == IS_TMP_VAR) && !def[VAR_NUM(OP2_VR(op))] && !global[VAR_NUM(OP2_VR(op))]) {
386                    if (op->opcode != ZEND_OP_DATA) {
387                        global[VAR_NUM(OP2_VR(op))] = 1;
388                    }
389                }
390                if (op->opcode == ZEND_DECLARE_INHERITED_CLASS && !def[VAR_NUM(op->extended_value)] && !global[VAR_NUM(op->extended_value)]) {
391                    global[VAR_NUM(op->extended_value)] = 1;
392                }
393                if ((RES_TYPE(op) == IS_VAR && (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT || !USED_RESULT(op))) || (RES_TYPE(op) == IS_TMP_VAR)) {
394                    if (!def[VAR_NUM(RES_VR(op))] && !global[VAR_NUM(RES_VR(op))]) {
395                        switch (op->opcode) {
396                            case ZEND_RECV:
397                            case ZEND_RECV_INIT:
398                            case ZEND_ADD_ARRAY_ELEMENT:
399                                global[VAR_NUM(RES_VR(op))] = 1;
400                                break;
401                        }
402                    }
403                    def[VAR_NUM(RES_VR(op))] = 1;
404                }
405                op++;
406            }
407            p = p->next;
408            cb_count++;
409        }
410        optimizer_free_alloca(def, def_use_heap);
411    }
412
413    p = cb;
414    while (p != NULL) {
415        zend_op *op = p->start;
416        zend_op *end = op + p->len;
417
418        memset(used, 0, op_array->T * sizeof(char));
419
420        while (op < end) {
421            end--;
422
423            if (((RES_TYPE(end) == IS_VAR && (end->opcode == ZEND_RECV || end->opcode == ZEND_RECV_INIT || !USED_RESULT(end))) || (RES_TYPE(end) == IS_TMP_VAR)) && !global[VAR_NUM(RES_VR(end))] && !used[VAR_NUM(RES_VR(end))]) {
424                switch(end->opcode) {
425                    case ZEND_JMPZ_EX:
426                        end->opcode = ZEND_JMPZ;
427                        RES_TYPE(end) = IS_UNUSED;
428                        break;
429                    case ZEND_JMPNZ_EX:
430                        end->opcode = ZEND_JMPNZ;
431                        RES_TYPE(end) = IS_UNUSED;
432                        break;
433                    case ZEND_ASSIGN_ADD:
434                    case ZEND_ASSIGN_SUB:
435                    case ZEND_ASSIGN_MUL:
436                    case ZEND_ASSIGN_DIV:
437                    case ZEND_ASSIGN_MOD:
438                    case ZEND_ASSIGN_SL:
439                    case ZEND_ASSIGN_SR:
440                    case ZEND_ASSIGN_CONCAT:
441                    case ZEND_ASSIGN_BW_OR:
442                    case ZEND_ASSIGN_BW_AND:
443                    case ZEND_ASSIGN_BW_XOR:
444                    case ZEND_PRE_INC:
445                    case ZEND_PRE_DEC:
446                    case ZEND_POST_INC:
447                    case ZEND_POST_DEC:
448                    case ZEND_ASSIGN:
449                    case ZEND_ASSIGN_REF:
450                    case ZEND_DO_FCALL:
451                    case ZEND_DO_FCALL_BY_NAME:
452                        if (RES_TYPE(end) == IS_VAR) {
453                            RESULT_TYPE(end) |= EXT_TYPE_UNUSED;
454                        }
455                        break;
456                    case ZEND_UNSET_VAR:
457                    case ZEND_UNSET_DIM:
458                    case ZEND_UNSET_OBJ:
459                        RES_TYPE(end) = IS_UNUSED;
460                        break;
461                    case ZEND_RECV:
462                    case ZEND_RECV_INIT:
463                    case ZEND_INCLUDE_OR_EVAL:
464                    case ZEND_NEW:
465                    case ZEND_FE_FETCH:
466                    case ZEND_PRINT:
467                    case ZEND_INIT_METHOD_CALL:
468                    case ZEND_INIT_STATIC_METHOD_CALL:
469                    case ZEND_ASSIGN_DIM:
470                    case ZEND_ASSIGN_OBJ:
471                    case ZEND_DECLARE_CLASS:
472                    case ZEND_DECLARE_INHERITED_CLASS:
473                        break;
474                    default:
475                        if (OP1_TYPE(end) == IS_CONST) {
476                            zval_dtor(&__OP1_VAL(end));
477                        }
478                        if (OP2_TYPE(end) == IS_CONST) {
479                            zval_dtor(&__OP2_VAL(end));
480                        }
481                        SET_TO_NOP(end);
482                }
483            } else if (RES_TYPE(end) == IS_VAR && USED_RESULT(end) && end->opcode != ZEND_RECV && end->opcode != ZEND_RECV_INIT && used[VAR_NUM(RES_VR(end))]) {
484                RESULT_TYPE(end) &= ~EXT_TYPE_UNUSED;
485            }
486
487            if ((RES_TYPE(end) == IS_VAR && (end->opcode == ZEND_RECV || end->opcode == ZEND_RECV_INIT || !USED_RESULT(end))) || (RES_TYPE(end) == IS_TMP_VAR)) {
488                switch (end->opcode) {
489                    case ZEND_RECV:
490                    case ZEND_RECV_INIT:
491                    case ZEND_ADD_ARRAY_ELEMENT:
492                        used[VAR_NUM(RES_VR(end))] = 1;
493                        break;
494                    default:
495                        used[VAR_NUM(RES_VR(end))] = 0;
496                }
497            }
498
499            if (OP1_TYPE(end) == IS_VAR || OP1_TYPE(end) == IS_TMP_VAR) {
500                used[VAR_NUM(OP1_VR(end))] = 1;
501            }
502
503            if (OP2_TYPE(end) == IS_VAR || OP2_TYPE(end) == IS_TMP_VAR) {
504                used[VAR_NUM(OP2_VR(end))] = 1;
505            }
506
507            if (end->opcode == ZEND_DECLARE_INHERITED_CLASS) {
508                used[VAR_NUM(end->extended_value)] = 1;
509            }
510        }
511        p = p->next;
512    }
513    optimizer_free_alloca(used, used_use_heap);
514}
515
516
517/* Set FROM as the parent of TO */
518#define CB_ADD_PRED(TO,FROM) {      \
519    CBchain *c = (TO)->pchain;      \
520    while (c != NULL) {             \
521        if (c->cb == (FROM)) {      \
522            break;                  \
523        }                           \
524        c = c->next;                \
525    }                               \
526    if (c == NULL) {                \
527        c = emalloc(sizeof(*c));    \
528        c->cb = (FROM);             \
529        c->next = (TO)->pchain;     \
530        (TO)->pchain = c;           \
531    }                               \
532}                                   \
533
534
535/* Remove FROM from parent of TO */
536#define CB_DEL_PRED(TO,FROM) {                \
537    CBchain *c = (TO)->pchain;                \
538    if (c != NULL) {                          \
539        if (c->cb == (FROM)) {                \
540            (TO)->pchain = c->next;           \
541            efree(c);                         \
542        } else {                              \
543            while (c->next != NULL) {         \
544                if (c->next->cb == (FROM)) {  \
545                    CBchain *cc = c->next;    \
546                    c->next = c->next->next;  \
547                    efree(cc);                \
548                    break;                    \
549                }                             \
550                c = c->next;                  \
551            }                                 \
552        }                                     \
553    }                                         \
554}                                             \
555
556
557
558#define RM_CB(p) do {                    \
559    if (p->pchain == NULL && p != cb) {  \
560        rm_cb(p);                        \
561    }                                    \
562} while (0)                              \
563
564
565
566static void mark_used_cb(CB* cb)
567{
568    if (cb->used) {
569        return;
570    }
571
572    cb->used = 1;
573
574    if (cb->jmp_1 != NULL) {
575        mark_used_cb(cb->jmp_1);
576        CB_ADD_PRED(cb->jmp_1, cb);
577    }
578    if (cb->jmp_2 != NULL) {
579        mark_used_cb(cb->jmp_2);
580        CB_ADD_PRED(cb->jmp_2, cb);
581    }
582    if (cb->jmp_ext != NULL) {
583        mark_used_cb(cb->jmp_ext);
584        CB_ADD_PRED(cb->jmp_ext, cb);
585    }
586    if (cb->jmp_tc != NULL) {
587        mark_used_cb(cb->jmp_tc);
588        CB_ADD_PRED(cb->jmp_tc, cb);
589    }
590    if (cb->follow != NULL) {
591        mark_used_cb(cb->follow);
592        CB_ADD_PRED(cb->follow, cb);
593    }
594}
595
596
597static void mark_used_cb2(CB* cb)
598{
599    if (cb->used) {
600        return;
601    }
602
603    cb->used = 1;
604
605    if (cb->jmp_1 != NULL) {
606        mark_used_cb2(cb->jmp_1);
607    }
608    if (cb->jmp_2 != NULL) {
609        mark_used_cb2(cb->jmp_2);
610    }
611    if (cb->jmp_ext != NULL) {
612        mark_used_cb2(cb->jmp_ext);
613    }
614    if (cb->jmp_tc != NULL) {
615        mark_used_cb2(cb->jmp_tc);
616    }
617    if (cb->follow != NULL) {
618        mark_used_cb2(cb->follow);
619    }
620}
621
622
623static void rm_cb(CB* cb)
624{
625    if (cb->used == 0) {
626        return;
627    }
628
629    cb->used = 0;
630
631    if (cb->jmp_1 != NULL) {
632        CB_DEL_PRED(cb->jmp_1, cb);
633    }
634    if (cb->jmp_2 != NULL) {
635        CB_DEL_PRED(cb->jmp_2, cb);
636    }
637    if (cb->jmp_ext != NULL) {
638        CB_DEL_PRED(cb->jmp_ext, cb);
639    }
640    if (cb->jmp_tc != NULL) {
641        CB_DEL_PRED(cb->jmp_tc, cb);
642    }
643    if (cb->follow != NULL) {
644        CB_DEL_PRED(cb->follow, cb);
645    }
646}
647
648
649static void del_cb(CB* cb)
650{
651    zend_op* op = cb->start;
652    zend_op* end = op + cb->len;
653
654    rm_cb(cb);
655    while (op < end) {
656        --end;
657        if (OP1_TYPE(end) == IS_CONST) {
658            zval_dtor(&__OP1_VAL(end));
659        }
660        if (OP2_TYPE(end) == IS_CONST) {
661            zval_dtor(&__OP2_VAL(end));
662        }
663        SET_TO_NOP(end);
664    }
665    cb->len = 0;
666    cb->used = 0;
667}
668
669
670static void replace_cb(CB* src, CB* dst)
671{
672    CBchain* p = src->pchain;
673    while (p != NULL) {
674        CBchain* q = p->next;
675        if (p->cb->jmp_1 == src) {
676            p->cb->jmp_1 = dst;
677            CB_ADD_PRED(dst,p->cb);
678        }
679        if (p->cb->jmp_2 == src) {
680            p->cb->jmp_2 = dst;
681            CB_ADD_PRED(dst,p->cb);
682        }
683        if (p->cb->jmp_ext == src) {
684            p->cb->jmp_ext = dst;
685            CB_ADD_PRED(dst,p->cb);
686        }
687        if (p->cb->jmp_tc == src) {
688            p->cb->jmp_tc = dst;
689            CB_ADD_PRED(dst,p->cb);
690        }
691        if (p->cb->follow == src) {
692            p->cb->follow = dst;
693            CB_ADD_PRED(dst,p->cb);
694        }
695        efree(p);
696        p = q;
697    }
698    src->pchain = NULL;
699}
700
701
702
703static void optimize_jmp(CB* cb, zend_op_array* op_array, int *jmp_lines)
704{
705    CB *p;
706    zend_op *op, *oe;
707    int i, line_num;
708
709    while(1) {
710        zend_bool ok = 1;
711
712        /* Remove Unused Blocks */
713        p = cb;
714        while (p->next != NULL) {
715            if (p->next->used && p->next->pchain) {
716                p = p->next;
717            } else {
718                del_cb(p->next);
719                p->next = p->next->next;
720                ok = 0;
721            }
722        }
723
724        /* JMP optimization */
725        p = cb;
726        while (p != NULL) {
727            while (p->next != NULL && (!p->next->used || p->next->pchain == NULL)) {
728                del_cb(p->next);
729                p->next = p->next->next;
730                ok = 0;
731            }
732            if (p->used && p->len > 0) {
733                zend_op* op = &p->start[p->len-1];
734                switch (op->opcode) {
735                    case ZEND_JMP:
736jmp:
737
738                        /* L1: JMP L1+1  => NOP */
739                        if (p->jmp_1 == p->next) {
740                            if (p->follow) { /* this is needed for optimized out BRK/JMP that already have a follow */
741                                CB_DEL_PRED(p->follow, p);
742                            }
743                            p->follow = p->jmp_1;
744                            p->jmp_1 = NULL;
745                            SET_TO_NOP(op);
746                            --(p->len);
747                            ok = 0;
748                            break;
749                        }
750                        /*  JMP L1  =>  JMP L2
751                            ...         ...
752                            L1: JMP L2      JMP L2
753                        */
754                        while (p->jmp_1->len == 1 && p->jmp_1->start->opcode == ZEND_JMP && p->jmp_1 != p) {
755                            CB* x_p = p->jmp_1;
756                            CB_DEL_PRED(p->jmp_1, p);
757                            RM_CB(x_p);
758                            p->jmp_1 = x_p->jmp_1;
759                            CB_ADD_PRED(p->jmp_1, p);
760                            ok = 0;
761                        }
762                        break;
763                    case ZEND_JMPZNZ:
764jmp_znz:
765                        /* JMPZNZ  ?,L1,L1  =>  JMP L1 */
766                        if (p->jmp_ext == p->jmp_2) {
767                            op->opcode = ZEND_JMP;
768                            op->extended_value = 0;
769                            OP1_TYPE(op) = IS_UNUSED;
770                            OP2_TYPE(op) = IS_UNUSED;
771                            p->jmp_1 = p->jmp_2;
772                            p->jmp_2 = NULL;
773                            p->jmp_ext = NULL;
774                            ok = 0;
775                            goto jmp;
776                        } else if (OP1_TYPE(op) == IS_CONST) {
777                            /* JMPZNZ  0,L1,L2  =>  JMP L1 */
778                            if (!zend_is_true(&__OP1_VAL(op))) {
779                                op->opcode = ZEND_JMP;
780                                op->extended_value = 0;
781                                OP1_TYPE(op) = IS_UNUSED;
782                                OP2_TYPE(op) = IS_UNUSED;
783                                if (p->jmp_ext != p->jmp_2) {
784                                    CB_DEL_PRED(p->jmp_ext, p);
785                                    RM_CB(p->jmp_ext);
786                                }
787                                p->jmp_1 = p->jmp_2;
788                                p->jmp_2 = NULL;
789                                p->jmp_ext = NULL;
790                                p->follow  = NULL;
791                                ok = 0;
792                                goto jmp;
793                                /* JMPZNZ  1,L1,L2  =>  JMP L2 */
794                            } else {
795                                op->opcode = ZEND_JMP;
796                                op->extended_value = 0;
797                                OP1_TYPE(op) = IS_UNUSED;
798                                OP2_TYPE(op) = IS_UNUSED;
799                                if (p->jmp_ext != p->jmp_2) {
800                                    CB_DEL_PRED(p->jmp_2, p);
801                                    RM_CB(p->jmp_2);
802                                }
803                                p->jmp_1 = p->jmp_ext;
804                                p->jmp_2 = NULL;
805                                p->jmp_ext = NULL;
806                                p->follow = NULL;
807                                ok = 0;
808                                goto jmp;
809                            }
810                            /* L1: JMPZNZ ?,L2,L1+1  => JMPZ ?,L2 */
811                        } else if (p->jmp_ext == p->next) {
812                            op->opcode = ZEND_JMPZ;
813                            op->extended_value = 0;
814                            p->follow = p->jmp_ext;
815                            p->jmp_ext = NULL;
816                            ok = 0;
817                            goto jmp_z;
818                            /* L1: JMPZNZ ?,L1+1,L2  => JMPNZ ?,L2 */
819                        } else if (p->jmp_2 == p->next) {
820                            op->opcode = ZEND_JMPNZ;
821                            op->extended_value = 0;
822                            p->follow = p->jmp_2;
823                            p->jmp_2  = p->jmp_ext;
824                            p->jmp_ext = NULL;
825                            ok = 0;
826                            goto jmp_nz;
827                        } else if (p->jmp_2->len == 1 && OP1_TYPE(op) == IS_TMP_VAR) {
828                            /*  JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L3,L2
829                                ...                  ...
830                                L1: JMPZ   $x,L3         JMPZ   $x,L3
831                            */
832                            /*  JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L3,L2
833                                ...                  ...
834                                L1: JMPZNZ $x,L3,L4      JMPZNZ $x,L3,L4
835                            */
836                            if ((p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
837                                if (p->jmp_2 != p->jmp_ext) {
838                                    CB_DEL_PRED(p->jmp_2, p);
839                                    RM_CB(p->jmp_2);
840                                }
841                                p->jmp_2 = p->jmp_2->jmp_2;
842                                CB_ADD_PRED(p->jmp_2, p);
843                                ok = 0;
844                                goto jmp_znz;
845                                /*     JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L1+1,L2
846                                    ...                  ...
847                                    L1: JMPNZ  $x,L3         JMPNZ  $x,L3
848                                */
849                            } else if (p->jmp_2->start->opcode == ZEND_JMPNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
850                                if (p->jmp_2 != p->jmp_ext) {
851                                    CB_DEL_PRED(p->jmp_2, p);
852                                    RM_CB(p->jmp_2);
853                                }
854                                p->jmp_2 = p->jmp_2->follow;
855                                CB_ADD_PRED(p->jmp_2, p);
856                                ok = 0;
857                                goto jmp_znz;
858                                /*     JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L1,L3
859                                    ...                  ...
860                                    L2: JMPNZ  $x,L3         JMPNZ  $x,L3
861                                */
862                            } else if (p->jmp_ext->start->opcode == ZEND_JMPNZ && OP1_TYPE(p->jmp_ext->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_ext->start)) {
863                                if (p->jmp_2 != p->jmp_ext) {
864                                    CB_DEL_PRED(p->jmp_ext, p);
865                                    RM_CB(p->jmp_ext);
866                                }
867                                p->jmp_ext = p->jmp_ext->jmp_2;
868                                CB_ADD_PRED(p->jmp_ext, p);
869                                ok = 0;
870                                goto jmp_znz;
871                                /*     JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L1,L4
872                                    ...                  ...
873                                    L2: JMPZNZ $x,L3,L4      JMPZNZ $x,L3,L4
874                                */
875                            } else if (p->jmp_ext->start->opcode == ZEND_JMPZNZ && OP1_TYPE(p->jmp_ext->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_ext->start)) {
876                                if (p->jmp_2 != p->jmp_ext) {
877                                    CB_DEL_PRED(p->jmp_ext, p);
878                                    RM_CB(p->jmp_ext);
879                                }
880                                p->jmp_ext = p->jmp_ext->jmp_ext;
881                                CB_ADD_PRED(p->jmp_ext, p);
882                                ok = 0;
883                                goto jmp_znz;
884                                /*     JMPZNZ $x,L1,L2  =>  JMPZNZ $x,L1,L2+1
885                                    ...                  ...
886                                    L2: JMPZ   $x,L3         JMPZ   $x,L3
887                                */
888                            } else if (p->jmp_ext->start->opcode == ZEND_JMPZ && OP1_TYPE(p->jmp_ext->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_ext->start)) {
889                                if (p->jmp_2 != p->jmp_ext) {
890                                    CB_DEL_PRED(p->jmp_ext, p);
891                                    RM_CB(p->jmp_ext);
892                                }
893                                p->jmp_ext = p->jmp_ext->follow;
894                                CB_ADD_PRED(p->jmp_ext, p);
895                                ok = 0;
896                                goto jmp_znz;
897                            }
898                        }
899                        while (p->jmp_2->len == 1 && p->jmp_2->start->opcode == ZEND_JMP) {
900                            CB* x_p = p->jmp_2;
901                            if (p->jmp_2 != p->jmp_ext) {
902                                CB_DEL_PRED(p->jmp_2, p);
903                                RM_CB(x_p);
904                            }
905                            p->jmp_2 = x_p->jmp_1;
906                            CB_ADD_PRED(p->jmp_2, p);
907                            ok = 0;
908                        }
909                        while (p->jmp_ext->len == 1 && p->jmp_ext->start->opcode == ZEND_JMP) {
910                            CB* x_p = p->jmp_ext;
911                            if (p->jmp_2 != p->jmp_ext) {
912                                CB_DEL_PRED(p->jmp_ext, p);
913                                RM_CB(x_p);
914                            }
915                            p->jmp_ext = x_p->jmp_1;
916                            CB_ADD_PRED(p->jmp_ext, p);
917                            ok = 0;
918                        }
919                        break;
920                    case ZEND_JMPZ:
921jmp_z:
922                        /* L1: JMPZ  ?,L1+1  =>  NOP */
923                        if (p->follow == p->jmp_2) {
924                            p->jmp_2   = NULL;
925                            SET_TO_NOP(op);
926                            --(p->len);
927                            ok = 0;
928                            break;
929                        } else if (OP1_TYPE(op) == IS_CONST) {
930                            /* JMPZ  0,L1  =>  JMP L1 */
931                            if (!zend_is_true(&__OP1_VAL(op))) {
932                                op->opcode = ZEND_JMP;
933                                OP1_TYPE(op) = IS_UNUSED;
934                                OP2_TYPE(op) = IS_UNUSED;
935
936                                if (p->follow != p->jmp_2) {
937                                    CB_DEL_PRED(p->follow, p);
938                                    RM_CB(p->follow);
939                                }
940                                p->jmp_1  = p->jmp_2;
941                                p->jmp_2  = NULL;
942                                p->follow = NULL;
943                                ok = 0;
944                                goto jmp;
945                                /* JMPZ  1,L1  =>  NOP */
946                            } else {
947                                if (p->follow != p->jmp_2) {
948                                    CB_DEL_PRED(p->jmp_2, p);
949                                    RM_CB(p->jmp_2);
950                                }
951                                p->jmp_2   = NULL;
952                                SET_TO_NOP(op);
953                                --(p->len);
954                                ok = 0;
955                                break;
956                            }
957                            /*  JMPZ ?,L1  =>  JMPZNZ  ?,L1,L2
958                                JMP  L2        JMP     L2
959                            */
960                        } else if (p->follow->len == 1 && p->follow->start->opcode == ZEND_JMP) {
961                            CB* x_p = p->follow;
962
963                            /* handler for when JMPZ and JMP have the same destination address */
964                            if (x_p->jmp_1 == p->jmp_2) {
965                                CB_DEL_PRED(p->jmp_2, p);
966                                RM_CB(p->jmp_2);
967                                p->jmp_2   = NULL;
968                                SET_TO_NOP(op);
969                                --(p->len);
970                                ok = 0;
971                                break;
972                            }
973
974                            op->opcode = ZEND_JMPZNZ;
975                            if (p->jmp_2 != p->follow) {
976                                CB_DEL_PRED(p->follow, p);
977                                RM_CB(x_p);
978                            }
979                            p->follow = NULL;
980                            p->jmp_ext = x_p->jmp_1;
981                            CB_ADD_PRED(p->jmp_ext, p);
982                            ok = 0;
983                            goto jmp_znz;
984                        } else if (p->jmp_2->len == 1 && OP1_TYPE(op) == IS_TMP_VAR) {
985                            /*  JMPZ $x,L1  =>  JMPZ $x,L2
986                                ...             ...
987                                L1: JMPZ $x,L2      JMPZ $x,L2
988                                ----------------------------------------
989                                JMPZ   $x,L1     =>  JMPZ  $x,L2
990                                ...                   ...
991                                L1: JMPZNZ $x,L2,L3      JMPZNZ $x,L2,L3
992                            */
993                            if ((p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == p->jmp_2->start->op1.u.var) {
994                                if (p->jmp_2 != p->follow) {
995                                    CB_DEL_PRED(p->jmp_2, p);
996                                    RM_CB(p->jmp_2);
997                                }
998                                p->jmp_2 = p->jmp_2->jmp_2;
999                                CB_ADD_PRED(p->jmp_2, p);
1000                                ok = 0;
1001                                goto jmp_z;
1002                                /*  JMPZ  $x,L1  =>  JMPZ  $x,L1+1
1003                                    ...              ...
1004                                    L1: JMPNZ $x,L2      JMPNZ $x,L2
1005                                */
1006                            } else if (p->jmp_2->start->opcode == ZEND_JMPNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
1007                                if (p->jmp_2 != p->follow) {
1008                                    CB_DEL_PRED(p->jmp_2, p);
1009                                    RM_CB(p->jmp_2);
1010                                }
1011                                p->jmp_2 = p->jmp_2->follow;
1012                                CB_ADD_PRED(p->jmp_2, p);
1013                                ok = 0;
1014                                goto jmp_z;
1015                            }
1016                        }
1017                        goto jmp_2;
1018                    case ZEND_JMPNZ:
1019jmp_nz:
1020                        /* L1: JMPNZ  ?,L1+1  =>  NOP */
1021                        if (p->follow == p->jmp_2) {
1022                            p->jmp_2 = NULL;
1023                            SET_TO_NOP(op);
1024                            --(p->len);
1025                            ok = 0;
1026                            break;
1027                        } else if (OP1_TYPE(op) == IS_CONST) {
1028                            /* JMPNZ  1,L1  =>  JMP L1 */
1029                            if (zend_is_true(&__OP1_VAL(op))) {
1030                                op->opcode = ZEND_JMP;
1031                                OP1_TYPE(op) = IS_UNUSED;
1032                                OP2_TYPE(op) = IS_UNUSED;
1033                                if (p->follow != p->jmp_2) {
1034                                    CB_DEL_PRED(p->follow, p);
1035                                    RM_CB(p->follow);
1036                                }
1037                                p->jmp_1 = p->jmp_2;
1038                                p->jmp_2 = NULL;
1039                                p->follow = NULL;
1040                                ok = 0;
1041                                goto jmp;
1042                                /* JMPNZ  0,L1  =>  NOP */
1043                            } else {
1044                                if (p->follow != p->jmp_2) {
1045                                    CB_DEL_PRED(p->jmp_2, p);
1046                                    RM_CB(p->jmp_2);
1047                                }
1048                                p->jmp_2 = NULL;
1049                                SET_TO_NOP(op);
1050                                --(p->len);
1051                                ok = 0;
1052                                break;
1053                            }
1054                            /*  JMPNZ ?,L1  =>  JMPZNZ  ?,L2,L1
1055                                JMP   L2        JMP     L2
1056                            */
1057                        } else if (p->follow->len == 1 && p->follow->start->opcode == ZEND_JMP) {
1058                            CB* x_p = p->follow;
1059
1060                            op->opcode = ZEND_JMPZNZ;
1061                            if (p->jmp_2 != p->follow) {
1062                                CB_DEL_PRED(p->follow, p);
1063                                RM_CB(p->follow);
1064                            }
1065                            p->follow = NULL;
1066                            p->jmp_ext = p->jmp_2;
1067                            p->jmp_2 = x_p->jmp_1;
1068                            CB_ADD_PRED(p->jmp_2, p);
1069                            ok = 0;
1070                            goto jmp_znz;
1071                            /*  JMPNZ $x,L1  =>  JMPNZ $x,L2
1072                                ...              ...
1073                                L1: JMPNZ $x,L2      JMPNZ $x,L2
1074                            */
1075                        } else if (p->jmp_2->len == 1 && OP1_TYPE(op) == IS_TMP_VAR) {
1076                            if (p->jmp_2->start->opcode == ZEND_JMPNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
1077                                if (p->jmp_2 != p->follow) {
1078                                    CB_DEL_PRED(p->jmp_2, p);
1079                                    RM_CB(p->jmp_2);
1080                                }
1081                                p->jmp_2 = p->jmp_2->jmp_2;
1082                                CB_ADD_PRED(p->jmp_2, p);
1083                                ok = 0;
1084                                goto jmp_nz;
1085                                /*  JMPNZ  $x,L1  =>  JMPNZ  $x,L1+1
1086                                    ...               ...
1087                                    L1: JMPZ   $x,L2      JMPZ $x,L2
1088                                */
1089                            } else if (p->jmp_2->start->opcode == ZEND_JMPZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
1090                                if (p->jmp_2 != p->follow) {
1091                                    CB_DEL_PRED(p->jmp_2, p);
1092                                    RM_CB(p->jmp_2);
1093                                }
1094                                p->jmp_2 = p->jmp_2->follow;
1095                                CB_ADD_PRED(p->jmp_2, p);
1096                                ok = 0;
1097                                goto jmp_nz;
1098                                /*  JMPNZ  $x,L1     =>  JMPNZ  $x,L3
1099                                    ...                   ...
1100                                    L1: JMPZNZ $x,L2,L3      JMPZNZ $x,L2,L3
1101                                */
1102                            } else if (p->jmp_2->start->opcode == ZEND_JMPZNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && OP1_VR(op) == OP1_VR(p->jmp_2->start)) {
1103                                if (p->jmp_2 != p->follow) {
1104                                    CB_DEL_PRED(p->jmp_2, p);
1105                                    RM_CB(p->jmp_2);
1106                                }
1107                                p->jmp_2 = p->jmp_2->jmp_ext;
1108                                CB_ADD_PRED(p->jmp_2, p);
1109                                ok = 0;
1110                                goto jmp_nz;
1111                            }
1112                        }
1113                        goto jmp_2;
1114                    case ZEND_JMPZ_EX:
1115jmp_z_ex:
1116                        /* L1: JMPZ_EX  $x,L1+1,$x  =>  NOP */
1117                        if (p->follow == p->jmp_2 && OP1_TYPE(op) == IS_TMP_VAR && RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op)) {
1118                            p->jmp_2   = NULL;
1119                            SET_TO_NOP(op);
1120                            --(p->len);
1121                            ok = 0;
1122                            break;
1123                            /* L1: JMPZ_EX  $x,L1+1,$y  =>  BOOL $x,$y */
1124                        } else if (p->follow == p->jmp_2) {
1125                            p->jmp_2   = NULL;
1126                            op->opcode = ZEND_BOOL;
1127                            OP2_TYPE(op) = IS_UNUSED;
1128                            ok = 0;
1129                            break;
1130                        } else if (p->jmp_2->len == 1 && RES_TYPE(op) == IS_TMP_VAR) {
1131                            /*  JMPZ_EX ?,L1,$x  =>  JMPZ_EX ?,L2,$x
1132                                ...                  ...
1133                                L1: JMPZ    $x,L2        JMPZ    $x,L2
1134                                ------------------------------------------
1135                                JMPZ_EX ?,L1,$x  =>  JMPZ_EX ?,L2,$x
1136                                ...                  ...
1137                                L1: JMPZNZ  $x,L2,L3     JMPZNZ  $x,L2,L3
1138                                ------------------------------------------
1139                                JMPZ_EX ?,L1,$x  =>  JMPZ_EX ?,L2,$x
1140                                ...                  ...
1141                                L1: JMPZ_EX $x,L2,$x     JMPZ_EX $x,L2,$x
1142                            */
1143                            if ((
1144                                    (p->jmp_2->start->opcode == ZEND_JMPZ || p->jmp_2->start->opcode == ZEND_JMPZNZ) &&
1145                                    OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) ||
1146                                    (p->jmp_2->start->opcode == ZEND_JMPZ_EX && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1147                                    RES_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start) &&
1148                                    RES_VR(op) == p->jmp_2->start->result.u.var)) {
1149
1150                                if (p->jmp_2 != p->follow) {
1151                                    CB_DEL_PRED(p->jmp_2, p);
1152                                    RM_CB(p->jmp_2);
1153                                }
1154                                p->jmp_2 = p->jmp_2->jmp_2;
1155                                CB_ADD_PRED(p->jmp_2, p);
1156                                ok = 0;
1157                                goto jmp_z_ex;
1158                                /*  JMPZ_EX ?,L1,$x   =>  JMPZ_EX ?,L2+1,$x
1159                                            ...                   ...
1160                                    L1: JMPNZ    $x,L2        JMPNZ    $x,L2
1161                                    ------------------------------------------
1162                                    JMPZ_EX ?,L1,$x   =>  JMPZ_EX  ?,L2+1,$x
1163                                    ...                   ...
1164                                    L1: JMPNZ_EX $x,L2,$x     JMPNZ_EX $x,L2,$x
1165                                */
1166                            } else if ((p->jmp_2->start->opcode == ZEND_JMPNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1167                                    RES_VR(op) == OP1_VR(p->jmp_2->start)) ||
1168                                    (p->jmp_2->start->opcode == ZEND_JMPNZ_EX && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1169                                    RES_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start) &&
1170                                    RES_VR(op) == p->jmp_2->start->result.u.var)) {
1171
1172                                if (p->jmp_2 != p->follow) {
1173                                    CB_DEL_PRED(p->jmp_2, p);
1174                                    RM_CB(p->jmp_2);
1175                                }
1176                                p->jmp_2 = p->jmp_2->follow;
1177                                CB_ADD_PRED(p->jmp_2, p);
1178                                ok = 0;
1179                                goto jmp_z_ex;
1180                                /*  JMPZ_EX ?,L1,$x   =>  JMPZ_EX ?,L1+1,$y
1181                                    ...                   ...
1182                                    L1: BOOL    $x,$y         BOOL    $x,$y
1183                                */
1184                            } else if (p->jmp_2->start->opcode == ZEND_BOOL && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1185                                memcpy(&op->result, &p->jmp_2->start->result, sizeof(zval));
1186                                if (p->jmp_2 != p->follow) {
1187                                    CB_DEL_PRED(p->jmp_2, p);
1188                                    RM_CB(p->jmp_2);
1189                                }
1190                                p->jmp_2 = p->jmp_2->follow;
1191                                CB_ADD_PRED(p->jmp_2, p);
1192                                ok = 0;
1193                                goto jmp_z_ex;
1194                                /*  JMPZ_EX ?,L1,$x   =>  JMPZ    ?,L1+1
1195                                    ...                   ...
1196                                    L1: FREE    $x            FREE    $x
1197                                */
1198                            } else if (p->jmp_2->start->opcode == ZEND_FREE && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1199                                op->opcode = ZEND_JMPZ;
1200                                RES_TYPE(op) = IS_UNUSED;
1201                                if (p->jmp_2 != p->follow) {
1202                                    CB_DEL_PRED(p->jmp_2, p);
1203                                    RM_CB(p->jmp_2);
1204                                }
1205                                p->jmp_2 = p->jmp_2->follow;
1206                                CB_ADD_PRED(p->jmp_2, p);
1207                                ok = 0;
1208                                goto jmp_z;
1209                            }
1210                            /*  JMPZ_EX ?,L1,$x   =>  JMPZ ?,L1+1
1211                                ...                   ...
1212                                L1: FREE    $x            FREE $x
1213                            */
1214                        } else if (RES_TYPE(op) == IS_TMP_VAR && p->jmp_2->start->opcode == ZEND_FREE && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1215                            if (p->jmp_2->len > 1) {
1216                                /* splitting */
1217                                CB* new_cb = (p->jmp_2+1);
1218                                new_cb->used = 1;
1219                                new_cb->start = p->jmp_2->start+1;
1220                                new_cb->len = p->jmp_2->len-1;
1221                                p->jmp_2->len = 1;
1222                                new_cb->next = p->jmp_2->next;
1223                                p->jmp_2->next = new_cb;
1224                                new_cb->pchain = NULL;
1225
1226                                if (p->jmp_2->jmp_1) {
1227                                    new_cb->jmp_1 = p->jmp_2->jmp_1;
1228                                    CB_ADD_PRED(new_cb->jmp_1, new_cb);
1229                                    CB_DEL_PRED(new_cb->jmp_1, p->jmp_2);
1230                                    p->jmp_2->jmp_1 = NULL;
1231                                }
1232                                if (p->jmp_2->jmp_2) {
1233                                    new_cb->jmp_2 = p->jmp_2->jmp_2;
1234                                    CB_ADD_PRED(new_cb->jmp_2, new_cb);
1235                                    CB_DEL_PRED(new_cb->jmp_2, p->jmp_2);
1236                                    p->jmp_2->jmp_2 = NULL;
1237                                }
1238                                if (p->jmp_2->jmp_ext) {
1239                                    new_cb->jmp_ext = p->jmp_2->jmp_ext;
1240                                    CB_ADD_PRED(new_cb->jmp_ext, new_cb);
1241                                    CB_DEL_PRED(new_cb->jmp_ext, p->jmp_2);
1242                                    p->jmp_2->jmp_ext   = NULL;
1243                                }
1244                                op->opcode = ZEND_JMPZ;
1245                                RES_TYPE(op) = IS_UNUSED;
1246
1247                                if (p->jmp_2->follow) {
1248                                    new_cb->follow = p->jmp_2->follow;
1249                                    CB_ADD_PRED(new_cb->follow, new_cb);
1250                                    CB_DEL_PRED(new_cb->follow, p->jmp_2);
1251                                    p->jmp_2->follow = NULL;
1252                                }
1253                                p->jmp_2->follow = new_cb;
1254                                CB_ADD_PRED(p->jmp_2->follow, p->jmp_2);
1255                            }
1256                            if (p->jmp_2 != p->follow) {
1257                                CB_DEL_PRED(p->jmp_2, p);
1258                                RM_CB(p->jmp_2);
1259                            }
1260                            p->jmp_2 = p->jmp_2->follow;
1261                            CB_ADD_PRED(p->jmp_2, p);
1262                            ok = 0;
1263                            goto jmp_z;
1264                        }
1265                        goto jmp_2;
1266                    case ZEND_JMPNZ_EX:
1267jmp_nz_ex:
1268                            /* L1: JMPNZ_EX  $x,L1+1,$x  =>  NOP */
1269                        if (p->follow == p->jmp_2 && OP1_TYPE(op) == IS_TMP_VAR && RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op)) {
1270                            p->jmp_2   = NULL;
1271                            SET_TO_NOP(op);
1272                            --(p->len);
1273                            ok = 0;
1274                            break;
1275                            /* L1: JMPNZ_EX  $x,L1+1,$y  =>  BOOL $x,$y */
1276                        } else if (p->follow == p->jmp_2) {
1277                            p->jmp_2   = NULL;
1278                            op->opcode = ZEND_BOOL;
1279                            OP2_TYPE(op) = IS_UNUSED;
1280                            ok = 0;
1281                            break;
1282                        } else if (p->jmp_2->len == 1 && RES_TYPE(op) == IS_TMP_VAR) {
1283                            /*  JMPNZ_EX ?,L1,$x  =>  JMPNZ_EX ?,L2,$x
1284                                ...                   ...
1285                                L1: JMPNZ    $x,L2        JMPNZ    $x,L2
1286                                ------------------------------------------
1287                                JMPNZ_EX ?,L1,$x  =>  JMPNZ_EX ?,L2,$x
1288                                ...                   ...
1289                                L1: JMPNZ_EX $x,L2,$x     JMPNZ_EX $x,L2,$x
1290                            */
1291                            if ((p->jmp_2->start->opcode == ZEND_JMPNZ &&
1292                                    OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1293                                    RES_VR(op) == OP1_VR(p->jmp_2->start)) ||
1294                                    (p->jmp_2->start->opcode == ZEND_JMPNZ_EX &&
1295                                    OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1296                                    RES_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1297                                    RES_VR(op) == OP1_VR(p->jmp_2->start) &&
1298                                    RES_VR(op) == p->jmp_2->start->result.u.var)) {
1299                                if (p->jmp_2 != p->follow) {
1300                                    CB_DEL_PRED(p->jmp_2, p);
1301                                    RM_CB(p->jmp_2);
1302                                }
1303                                p->jmp_2 = p->jmp_2->jmp_2;
1304                                CB_ADD_PRED(p->jmp_2, p);
1305                                ok = 0;
1306                                goto jmp_nz_ex;
1307                            /*  JMPNZ_EX ?,L1,$x   =>  JMPNZ_EX ?,L3,$x
1308                                ...                    ...
1309                                       L1: JMPZNZ   $x,L2,L3      JMPZNZ   $x,L2,L3
1310                            */
1311                            } else if (p->jmp_2->start->opcode == ZEND_JMPZNZ && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1312                                if (p->jmp_2 != p->follow) {
1313                                    CB_DEL_PRED(p->jmp_2, p);
1314                                    RM_CB(p->jmp_2);
1315                                }
1316                                p->jmp_2 = p->jmp_2->jmp_ext;
1317                                CB_ADD_PRED(p->jmp_2, p);
1318                                ok = 0;
1319                                goto jmp_nz_ex;
1320                                /*  JMPNZ_EX ?,L1,$x   =>  JMPNZ_EX ?,L1+1,$x
1321                                    ...                    ...
1322                                    L1: JMPZ    $x,L2          JMPZ    $x,L2
1323                                    ------------------------------------------
1324                                    JMPNZ_EX ?,L1,$x   =>  JMPNZ_EX  ?,L1+1,$x
1325                                    ...                    ...
1326                                    L1: JMPZ_EX $x,L2,$x      JMPZ_EX $x,L2,$x
1327                                */
1328                            } else if ((p->jmp_2->start->opcode == ZEND_JMPZ &&
1329                                        OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1330                                        RES_VR(op) == OP1_VR(p->jmp_2->start)) ||
1331                                        (p->jmp_2->start->opcode == ZEND_JMPZ_EX &&
1332                                        OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1333                                        RES_TYPE(p->jmp_2->start) == IS_TMP_VAR &&
1334                                        RES_VR(op) == OP1_VR(p->jmp_2->start) &&
1335                                        RES_VR(op) == p->jmp_2->start->result.u.var)) {
1336                                if (p->jmp_2 != p->follow) {
1337                                    CB_DEL_PRED(p->jmp_2, p);
1338                                    RM_CB(p->jmp_2);
1339                                }
1340                                    p->jmp_2 = p->jmp_2->follow;
1341                                CB_ADD_PRED(p->jmp_2, p);
1342                                ok = 0;
1343                                goto jmp_nz_ex;
1344                                /*  JMPNZ_EX ?,L1,$x   =>  JMPNZ_EX ?,L1+1,$y
1345                                    ...                   ...
1346                                    L1: BOOL    $x,$y         BOOL    $x,$y
1347                                */
1348                            } else if (p->jmp_2->start->opcode == ZEND_BOOL && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1349                                memcpy(&op->result, &p->jmp_2->start->result, sizeof(zval));
1350                                if (p->jmp_2 != p->follow) {
1351                                    CB_DEL_PRED(p->jmp_2, p);
1352                                    RM_CB(p->jmp_2);
1353                                }
1354                                p->jmp_2 = p->jmp_2->follow;
1355                                CB_ADD_PRED(p->jmp_2, p);
1356                                ok = 0;
1357                                goto jmp_nz_ex;
1358                                /*  JMPNZ_EX ?,L1,$x   =>  JMPNZ ?,L1+1
1359                                    ...                    ...
1360                                    L1: FREE    $x             FREE    $x
1361                                */
1362                            } else if (p->jmp_2->start->opcode == ZEND_FREE && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1363                                op->opcode = ZEND_JMPNZ;
1364                                RES_TYPE(op) = IS_UNUSED;
1365                                if (p->jmp_2 != p->follow) {
1366                                    CB_DEL_PRED(p->jmp_2, p);
1367                                    RM_CB(p->jmp_2);
1368                                }
1369                                p->jmp_2 = p->jmp_2->follow;
1370                                CB_ADD_PRED(p->jmp_2, p);
1371                                ok = 0;
1372                                goto jmp_nz;
1373                            }
1374                            /*  JMPNZ_EX ?,L1,$x   =>  JMPNZ_EX ?,L1+1,$x
1375                                ...                    ...
1376                                L1: FREE    $x             FREE    $x
1377                            */
1378                        } else if (RES_TYPE(op) == IS_TMP_VAR && p->jmp_2->start->opcode == ZEND_FREE && OP1_TYPE(p->jmp_2->start) == IS_TMP_VAR && RES_VR(op) == OP1_VR(p->jmp_2->start)) {
1379                            if (p->jmp_2->len > 1) {
1380                                /* splitting */
1381                                CB* new_cb = (p->jmp_2+1);
1382                                new_cb->used = 1;
1383                                new_cb->start = p->jmp_2->start+1;
1384                                new_cb->len = p->jmp_2->len-1;
1385                                p->jmp_2->len = 1;
1386                                new_cb->next = p->jmp_2->next;
1387                                p->jmp_2->next = new_cb;
1388                                new_cb->pchain   = NULL;
1389
1390                                if (p->jmp_2->jmp_1) {
1391                                    new_cb->jmp_1 = p->jmp_2->jmp_1;
1392                                    CB_ADD_PRED(new_cb->jmp_1, new_cb);
1393                                    CB_DEL_PRED(new_cb->jmp_1, p->jmp_2);
1394                                    p->jmp_2->jmp_1 = NULL;
1395                                }
1396                                if (p->jmp_2->jmp_2) {
1397                                    new_cb->jmp_2 = p->jmp_2->jmp_2;
1398                                    CB_ADD_PRED(new_cb->jmp_2, new_cb);
1399                                    CB_DEL_PRED(new_cb->jmp_2, p->jmp_2);
1400                                    p->jmp_2->jmp_2 = NULL;
1401                                }
1402                                if (p->jmp_2->jmp_ext) {
1403                                    new_cb->jmp_ext = p->jmp_2->jmp_ext;
1404                                    CB_ADD_PRED(new_cb->jmp_ext, new_cb);
1405                                    CB_DEL_PRED(new_cb->jmp_ext, p->jmp_2);
1406                                    p->jmp_2->jmp_ext = NULL;
1407                                }
1408                                if (p->jmp_2->follow) {
1409                                    new_cb->follow = p->jmp_2->follow;
1410                                    CB_ADD_PRED(new_cb->follow, new_cb);
1411                                    CB_DEL_PRED(new_cb->follow, p->jmp_2);
1412                                    p->jmp_2->follow = NULL;
1413                                }
1414                                p->jmp_2->follow = new_cb;
1415                                CB_ADD_PRED(p->jmp_2->follow, p->jmp_2);
1416                            }
1417                            op->opcode = ZEND_JMPNZ;
1418                            RES_TYPE(op) = IS_UNUSED;
1419                            if (p->jmp_2 != p->follow) {
1420                                CB_DEL_PRED(p->jmp_2, p);
1421                                RM_CB(p->jmp_2);
1422                            }
1423                            p->jmp_2 = p->jmp_2->follow;
1424                            CB_ADD_PRED(p->jmp_2, p);
1425                            ok = 0;
1426                            goto jmp_nz;
1427                        }
1428                        goto jmp_2;
1429                    case ZEND_NEW:
1430                    case ZEND_FE_FETCH:
1431jmp_2:
1432                        while (p->jmp_2->len == 1 && p->jmp_2->start->opcode == ZEND_JMP) {
1433                            CB* x_p = p->jmp_2;
1434                            if (p->jmp_2 != p->follow) {
1435                                CB_DEL_PRED(p->jmp_2, p);
1436                                RM_CB(x_p);
1437                            }
1438                            p->jmp_2 = x_p->jmp_1;
1439                            CB_ADD_PRED(p->jmp_2, p);
1440                            ok = 0;
1441                        }
1442                }
1443            }
1444
1445
1446            /* Merge Blocks */
1447            if (
1448                p->used && p->pchain != NULL && p->pchain->cb->used && p->pchain->next == NULL &&
1449                p->pchain->cb->follow == p && p->pchain->cb->next == p && p->pchain->cb->jmp_1 == NULL &&
1450                p->pchain->cb->jmp_2 == NULL && p->pchain->cb->jmp_ext == NULL && !p->exn
1451            ) {
1452                CB* x = p->pchain->cb;
1453                CB_DEL_PRED(p, x);
1454                x->len = &p->start[p->len] - x->start;
1455
1456                if (p->jmp_1 != NULL) {
1457                    x->jmp_1 = p->jmp_1;
1458                    CB_DEL_PRED(p->jmp_1, p);
1459                    CB_ADD_PRED(p->jmp_1, x);
1460                }
1461
1462                if (p->jmp_2 != NULL) {
1463                    x->jmp_2 = p->jmp_2;
1464                    CB_DEL_PRED(p->jmp_2, p);
1465                    CB_ADD_PRED(p->jmp_2, x);
1466                }
1467
1468                if (p->jmp_ext != NULL) {
1469                    x->jmp_ext = p->jmp_ext;
1470                    CB_DEL_PRED(p->jmp_ext, p);
1471                    CB_ADD_PRED(p->jmp_ext, x);
1472                }
1473                x->follow = p->follow;
1474
1475                if (p->follow != NULL) {
1476                    CB_DEL_PRED(p->follow, p);
1477                    CB_ADD_PRED(p->follow, x);
1478                }
1479                p->used = 0;
1480                p->len = 0;
1481                ok = 0;
1482            }
1483
1484            p = p->next;
1485        }
1486
1487        if (ok) {
1488            /* Eliminate JMP to RETURN or EXIT */
1489            p = cb;
1490            while (p != NULL) {
1491                if (p->used && p->len > 0) {
1492                    zend_op* op = &p->start[p->len-1];
1493
1494                    if (op->opcode == ZEND_JMP && p->jmp_1->len == 1 && (p->jmp_1->start->opcode == ZEND_RETURN || p->jmp_1->start->opcode == ZEND_EXIT)) {
1495                        CB_DEL_PRED(p->jmp_1, p);
1496                        RM_CB(p->jmp_1);
1497                        memcpy(op, p->jmp_1->start, sizeof(zend_op));
1498
1499                        if (OP1_TYPE(op) == IS_CONST)  {
1500                            zval_copy_ctor(&__OP1_VAL(op));
1501                        }
1502                        p->jmp_1 = NULL;
1503                        ok = 0;
1504                    }
1505                }
1506                p = p->next;
1507            }
1508        }
1509        if (ok) {
1510            break;
1511        }
1512    }
1513
1514
1515    op = op_array->opcodes;
1516    oe = op + op_array->last;
1517
1518    memset(jmp_lines, 0, sizeof(int) * op_array->last);
1519
1520    /*
1521    int _debug_i = 0;
1522    for (line_num = 0; op < oe; op++, line_num++, _debug_i++) {
1523        printf("%d: jmp op_array 4: %p\n", _debug_i, op_array);
1524        switch (op->opcode) {
1525            case ZEND_JMP:
1526                for (i = line_num; i < op->op1.u.opline_num; i++) {
1527                    jmp_lines[i] = 1;
1528                }
1529                break;
1530            case ZEND_JMPZ:
1531            case ZEND_JMPNZ:
1532            case ZEND_JMPZ_EX:
1533                for (i = line_num; i < op->op2.u.opline_num; i++) {
1534                    jmp_lines[i] = 1;
1535                }
1536                break;
1537        }
1538    }
1539    */
1540
1541    /* Safe jmp_lines (this one has bounds checking for jump locations...) */
1542    /*
1543     * Were setting any line within a JMP instruction to 1 in this case. This
1544     *   will indicate later on in the program that something is nested within
1545     *   a conditional block (or anything else with a JMP).
1546     * This is a VERY simple system that will exclude a LOT of information from
1547     *   being optimized. A better system might need to be devised.
1548     *
1549     */
1550    for (line_num = 0; op < oe; op++, line_num++) {
1551        switch(op->opcode) {
1552            case ZEND_JMP:
1553                for (i = line_num; i < op->op1.u.opline_num && i < op_array->last; i++) {
1554                    jmp_lines[i] = 1;
1555                }
1556                break;
1557            case ZEND_JMPZ:
1558            case ZEND_JMPNZ:
1559            case ZEND_JMPZ_EX:
1560                for (i = line_num; i < op->op2.u.opline_num && i < op_array->last; i++) {
1561                    jmp_lines[i] = 1;
1562                }
1563                break;
1564        }
1565    }
1566}
1567
1568/*
1569 * Returns 1 if the zerver var in key can be optimized out
1570 *
1571 */
1572static zend_bool optimizeable_server_var(char *ops, char *key, int key_len)
1573{
1574    char *e = ops + strlen(ops);
1575    char *p = php_memnstr(ops, key, key_len, e);
1576
1577    if (!p) {
1578        return 0;
1579    }
1580    if (p > ops && *(p - 1) != ' ') {
1581        return 0;
1582    }
1583    if (p + key_len < e && *(p + key_len) != ' ') {
1584        return 0;
1585    }
1586    return 1;
1587}
1588
1589
1590int optimize_get_constant(const zend_op *op, const char *name, int name_len, const char *cname, int cname_len, zval *r TSRMLS_DC)
1591{
1592    int retval = 0;
1593
1594    if (cname == NULL) {
1595        /* basic constant */
1596        zend_constant *c = NULL;
1597
1598        if (zend_hash_find(EG(zend_constants), (char *)name, name_len + 1, (void **)&c) == SUCCESS) {
1599            if (c->module_number != PHP_USER_CONSTANT) {
1600                if (r) {
1601                    *r = c->value;
1602                }
1603                retval = 1;
1604            }
1605        } else {
1606            OPTIMIZER_ALLOCA_FLAG(lookup_name_use_heap)
1607            char *lookup_name = optimizer_do_alloca(name_len + 1, lookup_name_use_heap);
1608
1609            memcpy(lookup_name, name, name_len);
1610            lookup_name[name_len] = '\0';
1611
1612            zend_str_tolower(lookup_name, name_len);
1613
1614            if (zend_hash_find(EG(zend_constants), lookup_name, name_len + 1, (void **)&c) == SUCCESS) {
1615                if ((c->flags & CONST_CS) && c->module_number != PHP_USER_CONSTANT && (memcmp(c->name, name, name_len) != 0)) {
1616                    retval = 0;
1617                } else {
1618                    if (r) {
1619                        *r = c->value;
1620                    }
1621                    retval = 1;
1622                }
1623            }
1624            optimizer_free_alloca(lookup_name, lookup_name_use_heap);
1625        }
1626    } else {
1627        /* class constant */
1628        zend_class_entry **ce = NULL;
1629        zval **c;
1630        OPTIMIZER_ALLOCA_FLAG(tcname_use_heap)
1631        char *tcname = optimizer_do_alloca(cname_len + 1, tcname_use_heap);
1632
1633        memcpy(tcname, cname, cname_len);
1634        tcname[cname_len] = '\0';
1635        zend_str_tolower(tcname, cname_len);
1636
1637        if (zend_hash_find(EG(class_table), (char *)tcname, cname_len + 1, (void **)&ce) == FAILURE) {
1638            retval = 0;
1639        } else if ((*ce)->type != ZEND_INTERNAL_CLASS || zend_hash_find(&(*ce)->constants_table, (char *)name, name_len + 1, (void **)&c) == FAILURE) {
1640            retval = 0;
1641        } else {
1642            if (r) {
1643                zend_class_entry *old_scope = CG(active_class_entry);
1644                zend_bool saved_in_compilation = CG(in_compilation);
1645                char *saved_compiled_filename = CG(compiled_filename);
1646                int saved_zend_lineno = CG(zend_lineno);
1647
1648                CG(in_compilation) = 1;
1649                CG(compiled_filename) = OPTIMIZER_G(op_array)->filename;
1650                CG(zend_lineno) = (int)op->lineno;
1651                CG(active_class_entry) = *ce;
1652
1653                zval_update_constant(c, (void *) 1 TSRMLS_CC);
1654
1655                CG(active_class_entry) = old_scope;
1656                CG(in_compilation) = saved_in_compilation;
1657                CG(compiled_filename) = saved_compiled_filename;
1658                CG(zend_lineno) = saved_zend_lineno;
1659
1660                *r = **c;
1661            }
1662            retval = 1;
1663        }
1664        optimizer_free_alloca(tcname, tcname_use_heap);
1665    }
1666
1667    return retval;
1668}
1669
1670#define UNDEFINE_OP(op) Ts[VAR_NUM((op).u.var)] = NULL;
1671#define DEFINE_OP(op)   Ts[VAR_NUM((op)->result.u.var)] = (op);
1672#define IS_DEFINED(op)    (Ts[VAR_NUM((op).u.var)] != NULL)
1673#define DEFINED_OP(op)    (Ts[VAR_NUM((op).u.var)])
1674
1675
1676void optimize_fullpath(zend_op *op, char *base, zend_bool use_include_path TSRMLS_DC)
1677{
1678    char *realpath;
1679
1680    if (
1681        OP1_VALT(op) != IS_STRING ||
1682        IS_ABSOLUTE_PATH(OP1_VAL_S(op), OP1_VAL_SL(op)) ||
1683        php_memnstr(OP1_VAL_S(op), "://", sizeof("://") - 1, OP1_VAL_S(op) + OP1_VAL_SL(op))
1684    ) {
1685        return;
1686    }
1687
1688    switch (use_include_path) {
1689        case ZEND_INCLUDE_ONCE:
1690        case ZEND_REQUIRE_ONCE:
1691        case ZEND_INCLUDE:
1692        case ZEND_REQUIRE:
1693            {
1694                char *opath, *pathbuf;
1695                FILE *fp;
1696                char *exec_fname = base;
1697                int exec_fname_length = strlen(exec_fname);
1698
1699                while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
1700                if ((exec_fname && exec_fname[0] == '[') || exec_fname_length<=0) {
1701                    /* [no active file] or no path */
1702                    pathbuf = estrdup(OP1_VAL_S(op));
1703                } else {
1704                    pathbuf = (char *) emalloc(exec_fname_length + OP1_VAL_SL(op) +1 +1);
1705                    memcpy(pathbuf, OP1_VAL_S(op), OP1_VAL_SL(op));
1706                    pathbuf[OP1_VAL_SL(op)] = DEFAULT_DIR_SEPARATOR;
1707                    memcpy(pathbuf+OP1_VAL_SL(op)+1, exec_fname, exec_fname_length);
1708                    pathbuf[OP1_VAL_SL(op) + exec_fname_length +1] = '\0';
1709                }
1710                fp = php_fopen_with_path(OP1_VAL_S(op), "rb", (const char *)pathbuf, &opath TSRMLS_CC);
1711                efree(pathbuf);
1712                if (fp) {
1713                    fclose(fp);
1714                    efree(OP1_VAL_S(op));
1715                    OP1_VAL_S(op) = opath;
1716                    OP1_VAL_SL(op) = strlen(opath);
1717                } else {
1718                    return;
1719                }
1720            }
1721            break;
1722        default:
1723            return;
1724    }
1725
1726    if ((realpath = expand_filepath(OP1_VAL_S(op), NULL TSRMLS_CC)) != NULL) {
1727        efree(OP1_VAL_S(op));
1728        OP1_VAL_S(op) = realpath;
1729        OP1_VAL_SL(op) = strlen(realpath);
1730    }
1731}
1732
1733
1734/*
1735 * Attempt to optimize the defining of a constant
1736 *
1737 */
1738static void optimize_define(zend_op** a, zend_op** b, CB* cb, zend_op** Ts TSRMLS_DC)
1739{
1740    zend_op *prev = *a;
1741    zend_op *op = *b;
1742    zend_constant c;
1743    zend_op *val, *name;
1744
1745    if (op->extended_value == 3) {
1746        convert_to_long(&__OP1_VAL(prev));
1747        c.flags = OP1_VAL_L(prev) ? 0 : CONST_CS;
1748        val = op -   2;
1749        name = op - 3;
1750    } else {
1751        c.flags = CONST_CS;
1752        val = prev;
1753        name = op - 2;
1754    }
1755    convert_to_string(&__OP1_VAL(name));
1756
1757    c.name = zend_strndup(OP1_VAL_S(name), OP1_VAL_SL(name));
1758    c.name_len = OP1_VAL_SL(name) + 1;
1759    c.module_number = 0;
1760    c.value.type = OP1_VAL_L(val);
1761    c.value = __OP1_VAL(val);
1762
1763    /* need this hack to avoid crashes due to hackish implementation of STD* constants in CLI */
1764    if (strcmp(sapi_module.name, "cli")) {
1765        c.flags |= CONST_PERSISTENT;
1766    }
1767    zval_copy_ctor(&c.value);
1768
1769    if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {
1770        int k = 0;
1771
1772        if (!USED_RESULT(op)) {
1773            optimize_to_true(&prev, &op, cb, Ts);
1774            k += 2;
1775        } else {
1776            CB_DEL_PRED(cb->jmp_2, cb);
1777            cb->jmp_2 = NULL;
1778        }
1779    } else if (c.flags & CONST_PERSISTENT) {
1780        zval_dtor(&c.value);
1781    }
1782}
1783
1784
1785
1786int get_next_znode_pos(zend_op* op, zend_bool var_only)
1787{
1788    zend_op *x;
1789
1790    x = NEXT_OP(op);
1791
1792    switch (x->opcode) {
1793        case ZEND_ADD:
1794        case ZEND_SUB:
1795        case ZEND_MUL:
1796        case ZEND_DIV:
1797        case ZEND_MOD:
1798        case ZEND_BOOL_NOT:
1799        case ZEND_BOOL_XOR:
1800        case ZEND_BOOL:
1801        case ZEND_IS_IDENTICAL:
1802        case ZEND_IS_NOT_IDENTICAL:
1803        case ZEND_IS_EQUAL:
1804        case ZEND_IS_NOT_EQUAL:
1805        case ZEND_IS_SMALLER:
1806        case ZEND_IS_SMALLER_OR_EQUAL:
1807        case ZEND_CONCAT:
1808            if (OP1_TYPE(x) == IS_VAR && OP1_VR(x) == RES_VR(op)) {
1809                return 1;
1810            } else if (OP2_TYPE(x) == IS_VAR && OP2_VR(x) == RES_VR(op)) {
1811                return 2;
1812            } else {
1813                return 0;
1814            }
1815            break;
1816        case ZEND_QM_ASSIGN:
1817        case ZEND_SEND_VAR:
1818        case ZEND_SEND_VAL:
1819        case ZEND_ECHO:
1820        case ZEND_JMPZ:
1821        case ZEND_JMPNZ:
1822        case ZEND_RETURN:
1823            if (OP1_VR(x) == RES_VR(op) && ((var_only && OP1_TYPE(x) == IS_VAR) || (!var_only && OP1_IS_VART(x)))) {
1824            //if ((OP1_TYPE(x) == IS_VAR || OP1_TYPE(x) == IS_CV || OP1_TYPE(x) == IS_TMP_VAR) && OP1_VR(x) == RES_VR(op)) {
1825                return 1;
1826            } else {
1827                return 0;
1828            }
1829        case ZEND_ASSIGN:
1830        case ZEND_ASSIGN_CONCAT:
1831            if (OP2_VR(x) == RES_VR(op) && ((var_only && OP2_TYPE(x) == IS_VAR) || (!var_only && OP2_IS_VART(x)))) {
1832            //if ((OP2_TYPE(x) == IS_VAR || OP2_TYPE(x) == IS_CV || OP2_TYPE(x) == IS_TMP_VAR) && OP2_VR(x) == RES_VR(op)) {
1833                return 2;
1834            } else {
1835                return 0;
1836            }
1837        case ZEND_ASSIGN_DIM:
1838            if (NEXT_OP(x)->opcode == ZEND_OP_DATA && OP1_VR(NEXT_OP(x)) == RES_VR(op)) {
1839                return 3;
1840            }
1841        case ZEND_FETCH_W:
1842            if (
1843                    OP1_TYPE(x) == IS_CONST && OP1_VALT(x) == IS_STRING &&
1844                    !strcmp(OP1_VAL_S(x), "GLOBALS") &&
1845                    NEXT_OP(NEXT_OP(x))->opcode == ZEND_OP_DATA &&
1846                    OP1_VR(NEXT_OP(NEXT_OP(x))) == RES_VR(op)
1847            ) {
1848                return 4;
1849            }
1850        case ZEND_SEND_VAR_NO_REF:
1851            if ((x->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && !(x->extended_value & ZEND_ARG_SEND_BY_REF)) {
1852                x->opcode = ZEND_SEND_VAR;
1853                x->extended_value = ZEND_DO_FCALL;
1854
1855                if (OP1_VR(x) == RES_VR(op) && ((var_only && OP1_TYPE(x) == IS_VAR) || (!var_only && OP1_IS_VART(x)))) {
1856                //if ((OP1_TYPE(x) == IS_VAR || OP1_TYPE(x) == IS_CV || OP1_TYPE(x) == IS_TMP_VAR) && OP1_VR(x) == RES_VR(op)) {
1857                    return 1;
1858                } else {
1859                    return 0;
1860                }
1861            } else {
1862                return 0;
1863            }
1864        default:
1865            return 0;
1866    }
1867}
1868
1869#define OPTIMIZE_TO_HEADER                                \
1870    zend_op* prev = NULL;                                 \
1871    if (a == NULL) {                                      \
1872        flags = flags & (0xFF ^ OPTIMIZE_TO_DEL_PREV);    \
1873    } else {                                              \
1874        prev = *a;               \
1875    }                                                     \
1876    zend_op* op   = *b;                 \
1877    zend_op* next = NEXT_OP(*b);                          \
1878    znode* n;                                             \
1879    int nn;                                               \
1880    if (flags & OPTIMIZE_TO_FCALL) {                      \
1881        nn = can_optimize_fcall(*b);                      \
1882    } else {                                              \
1883        nn = get_next_znode_pos(*b, 0);                   \
1884    }                                                     \
1885    switch(nn) {                                          \
1886        case 1: n = &next->op1; break;                    \
1887        case 2: n = &next->op2; break;                    \
1888        case 3: n = &NEXT_OP(next)->op1; break;           \
1889        case 4: n = &NEXT_OP(NEXT_OP(next))->op1; break;  \
1890        default: return;                                  \
1891    }                                                     \
1892    if (next->opcode == ZEND_ASSIGN) {                    \
1893        UNDEFINE_OP(next->op2);                           \
1894    } else if (next->opcode == ZEND_SEND_VAR) {           \
1895        next->opcode = ZEND_SEND_VAL;                     \
1896    }                                                     \
1897
1898
1899#define OPTIMIZE_TO_FOOTER                               \
1900    if (flags & OPTIMIZE_TO_DEL_PREV) {                  \
1901        SET_TO_NOP_EX(prev);                             \
1902    }                                                    \
1903    if (flags & OPTIMIZE_TO_DEL_OP) {                    \
1904        if (op && op->opcode == ZEND_FETCH_DIM_R) {      \
1905            if (op) {                                    \
1906                zval_dtor(&__OP2_VAL(op));               \
1907                SET_TO_NOP(op);                          \
1908            }                                            \
1909        } else {                                         \
1910            if (op) {                                    \
1911                zval_dtor(&__OP1_VAL(op));               \
1912                SET_TO_NOP(op);                          \
1913            }                                            \
1914            CB_DEL_PRED(cbl->jmp_2, cbl);                \
1915            cbl->jmp_2 = NULL;                           \
1916        }                                                \
1917    }                                                    \
1918
1919
1920
1921void optimize_to_bool_ex(zend_constant* c, zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1922{
1923    OPTIMIZE_TO_HEADER
1924
1925    n->op_type = IS_CONST;
1926    n->u.constant = c->value;
1927    zval_copy_ctor(&n->u.constant);
1928    INIT_PZVAL(&n->u.constant);
1929
1930    OPTIMIZE_TO_FOOTER
1931}
1932
1933
1934void optimize_to_true_ex(zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1935{
1936    zend_constant *c = NULL;
1937
1938    /* we shouldn't have PHP without "true" constant */
1939    if (zend_hash_find(EG(zend_constants), "true", sizeof("true"), (void **) &c) != SUCCESS) {
1940        assert(0);
1941    }
1942
1943    optimize_to_bool_ex(c, a, b, cbl, Ts, flags TSRMLS_CC);
1944}
1945
1946
1947void optimize_to_false_ex(zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1948{
1949    zend_constant *c = NULL;
1950
1951    /* we shouldn't have PHP without "false" constant */
1952    if (zend_hash_find(EG(zend_constants), "false", sizeof("false"), (void **) &c) != SUCCESS) {
1953        assert(0);
1954    }
1955
1956    optimize_to_bool_ex(c, a, b, cbl, Ts, flags TSRMLS_CC);
1957}
1958
1959
1960void optimize_to_null_ex(zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1961{
1962    zend_constant *c = NULL;
1963
1964    /* we shouldn't have PHP without "null" constant */
1965    if (zend_hash_find(EG(zend_constants), "null", sizeof("null"), (void **) &c) != SUCCESS) {
1966        assert(0);
1967    }
1968
1969    optimize_to_bool_ex(c, a, b, cbl, Ts, flags TSRMLS_CC);
1970}
1971
1972
1973void optimize_to_string_ex(char* str, int str_len, zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1974{
1975    OPTIMIZE_TO_HEADER
1976
1977    n->op_type = IS_CONST;
1978    INIT_PZVAL(&n->u.constant);
1979    ZVAL_STRINGL(&n->u.constant, str, str_len, 1);
1980
1981    OPTIMIZE_TO_FOOTER
1982}
1983
1984
1985void optimize_to_long_ex(long val, zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1986{
1987    OPTIMIZE_TO_HEADER
1988
1989    n->op_type = IS_CONST;
1990    INIT_PZVAL(&n->u.constant);
1991    ZVAL_LONG(&n->u.constant, val);
1992
1993    OPTIMIZE_TO_FOOTER
1994}
1995
1996
1997void optimize_to_double_ex(double val, zend_op** a, zend_op** b, CB* cbl, zend_op** Ts, zend_uchar flags TSRMLS_DC)
1998{
1999    OPTIMIZE_TO_HEADER
2000
2001    n->op_type = IS_CONST;
2002    INIT_PZVAL(&n->u.constant);
2003    ZVAL_DOUBLE(&n->u.constant, val);
2004
2005    OPTIMIZE_TO_FOOTER
2006}
2007
2008
2009void optimize_to_var_ex(zend_uint var, int var_type,  zend_op **a, zend_op **b, CB *cbl, zend_op **Ts, zend_uchar flags TSRMLS_DC)
2010{
2011    OPTIMIZE_TO_HEADER
2012
2013    /* Undo ZEND_SEND_VAR -> ZEND_SEND_VAL */
2014    if (next->opcode == ZEND_SEND_VAL) {
2015        next->opcode = ZEND_SEND_VAR;
2016    }
2017
2018    n->op_type = var_type;
2019    n->u.var = var;
2020
2021    OPTIMIZE_TO_FOOTER
2022}
2023
2024
2025/*
2026 * Returns 1 if the op is known to return a numeric result, 0 otherwise
2027 *
2028 */
2029static int is_numeric_result(zend_op* x)
2030{
2031    switch (x->opcode) {
2032        case ZEND_ADD:
2033        case ZEND_SUB:
2034        case ZEND_MUL:
2035        case ZEND_DIV:
2036        case ZEND_MOD:
2037        case ZEND_SL:
2038        case ZEND_SR:
2039
2040        case ZEND_ASSIGN_ADD:
2041        case ZEND_ASSIGN_SUB:
2042        case ZEND_ASSIGN_MUL:
2043        case ZEND_ASSIGN_DIV:
2044        case ZEND_ASSIGN_MOD:
2045        case ZEND_ASSIGN_SL:
2046        case ZEND_ASSIGN_SR:
2047
2048        case ZEND_BOOL_NOT:
2049        case ZEND_BOOL_XOR:
2050        case ZEND_BOOL:
2051
2052        case ZEND_IS_IDENTICAL:
2053        case ZEND_IS_NOT_IDENTICAL:
2054        case ZEND_IS_EQUAL:
2055        case ZEND_IS_NOT_EQUAL:
2056        case ZEND_IS_SMALLER:
2057        case ZEND_IS_SMALLER_OR_EQUAL:
2058
2059        case ZEND_PRE_DEC:
2060        case ZEND_PRE_INC:
2061        case ZEND_POST_INC:
2062        case ZEND_POST_DEC:
2063            return 1;
2064
2065        case ZEND_CAST:
2066            return (x->extended_value == IS_BOOL || x->extended_value == IS_LONG || x->extended_value == IS_DOUBLE);
2067
2068        case ZEND_DO_FCALL:
2069
2070#define OPTIMIZER_INT_FUNC_CHECK(name) !strcmp(OP1_VAL_S(x), name)
2071
2072            if (OP1_TYPE(x) == IS_CONST && OP1_VALT(x) == IS_STRING &&
2073                (
2074                    OPTIMIZER_INT_FUNC_CHECK("count") ||
2075                    OPTIMIZER_INT_FUNC_CHECK("extract") ||
2076                    OPTIMIZER_INT_FUNC_CHECK("array_push") ||
2077                    OPTIMIZER_INT_FUNC_CHECK("array_unshift") ||
2078                    OPTIMIZER_INT_FUNC_CHECK("assert") ||
2079                    OPTIMIZER_INT_FUNC_CHECK("ip2long") ||
2080                    OPTIMIZER_INT_FUNC_CHECK("get_magic_quotes_runtime") ||
2081                    OPTIMIZER_INT_FUNC_CHECK("get_magic_quotes_gpc") ||
2082                    OPTIMIZER_INT_FUNC_CHECK("connection_aborted") ||
2083                    OPTIMIZER_INT_FUNC_CHECK("connection_status") ||
2084                    OPTIMIZER_INT_FUNC_CHECK("ignore_user_abort") ||
2085                    OPTIMIZER_INT_FUNC_CHECK("getservbyname") ||
2086                    OPTIMIZER_INT_FUNC_CHECK("getprotobyname") ||
2087                    OPTIMIZER_INT_FUNC_CHECK("dl") ||
2088                    OPTIMIZER_INT_FUNC_CHECK("dns_check_record") ||
2089                    OPTIMIZER_INT_FUNC_CHECK("system") ||
2090                    OPTIMIZER_INT_FUNC_CHECK("file_put_contents") ||
2091                    OPTIMIZER_INT_FUNC_CHECK("pclose") ||
2092                    OPTIMIZER_INT_FUNC_CHECK("fwrite") ||
2093                    OPTIMIZER_INT_FUNC_CHECK("ftell") ||
2094                    OPTIMIZER_INT_FUNC_CHECK("fseek") ||
2095                    OPTIMIZER_INT_FUNC_CHECK("mkdir") ||
2096                    OPTIMIZER_INT_FUNC_CHECK("readfile") ||
2097                    OPTIMIZER_INT_FUNC_CHECK("umask") ||
2098                    OPTIMIZER_INT_FUNC_CHECK("fpassthru") ||
2099                    OPTIMIZER_INT_FUNC_CHECK("fputcsv") ||
2100                    OPTIMIZER_INT_FUNC_CHECK("fileperms") ||
2101                    OPTIMIZER_INT_FUNC_CHECK("fileinode") ||
2102                    OPTIMIZER_INT_FUNC_CHECK("filesize") ||
2103                    OPTIMIZER_INT_FUNC_CHECK("fileowner") ||
2104                    OPTIMIZER_INT_FUNC_CHECK("filegroup") ||
2105                    OPTIMIZER_INT_FUNC_CHECK("fileatime") ||
2106                    OPTIMIZER_INT_FUNC_CHECK("filemtime") ||
2107                    OPTIMIZER_INT_FUNC_CHECK("filectime") ||
2108                    OPTIMIZER_INT_FUNC_CHECK("printf") ||
2109                    OPTIMIZER_INT_FUNC_CHECK("vprintf") ||
2110                    OPTIMIZER_INT_FUNC_CHECK("fprintf") ||
2111                    OPTIMIZER_INT_FUNC_CHECK("vfprintf") ||
2112                    OPTIMIZER_INT_FUNC_CHECK("ftok") ||
2113                    OPTIMIZER_INT_FUNC_CHECK("levenshtein") ||
2114                    OPTIMIZER_INT_FUNC_CHECK("linkinfo") ||
2115                    OPTIMIZER_INT_FUNC_CHECK("symlink") ||
2116                    OPTIMIZER_INT_FUNC_CHECK("link") ||
2117                    OPTIMIZER_INT_FUNC_CHECK("ezmlm_hash") ||
2118                    OPTIMIZER_INT_FUNC_CHECK("mail") ||
2119                    OPTIMIZER_INT_FUNC_CHECK("abs") ||
2120                    OPTIMIZER_INT_FUNC_CHECK("bindec") ||
2121                    OPTIMIZER_INT_FUNC_CHECK("hexdec") ||
2122                    OPTIMIZER_INT_FUNC_CHECK("octdec") ||
2123                    OPTIMIZER_INT_FUNC_CHECK("getmyuid") ||
2124                    OPTIMIZER_INT_FUNC_CHECK("getmygid") ||
2125                    OPTIMIZER_INT_FUNC_CHECK("getmypid") ||
2126                    OPTIMIZER_INT_FUNC_CHECK("getmyinode") ||
2127                    OPTIMIZER_INT_FUNC_CHECK("getlastmod") ||
2128                    OPTIMIZER_INT_FUNC_CHECK("proc_terminate") ||
2129                    OPTIMIZER_INT_FUNC_CHECK("proc_close") ||
2130                    OPTIMIZER_INT_FUNC_CHECK("rand") ||
2131                    OPTIMIZER_INT_FUNC_CHECK("mt_rand") ||
2132                    OPTIMIZER_INT_FUNC_CHECK("getrandmax") ||
2133                    OPTIMIZER_INT_FUNC_CHECK("mt_getrandmax") ||
2134                    OPTIMIZER_INT_FUNC_CHECK("ereg") ||
2135                    OPTIMIZER_INT_FUNC_CHECK("eregi") ||
2136                    OPTIMIZER_INT_FUNC_CHECK("stream_select") ||
2137                    OPTIMIZER_INT_FUNC_CHECK("stream_set_write_buffer") ||
2138                    OPTIMIZER_INT_FUNC_CHECK("stream_socket_enable_crypto") ||
2139                    OPTIMIZER_INT_FUNC_CHECK("strspn") ||
2140                    OPTIMIZER_INT_FUNC_CHECK("strcspn") ||
2141                    OPTIMIZER_INT_FUNC_CHECK("strcoll") ||
2142                    OPTIMIZER_INT_FUNC_CHECK("strpos") ||
2143                    OPTIMIZER_INT_FUNC_CHECK("stripos") ||
2144                    OPTIMIZER_INT_FUNC_CHECK("strrpos") ||
2145                    OPTIMIZER_INT_FUNC_CHECK("strripos") ||
2146                    OPTIMIZER_INT_FUNC_CHECK("ord") ||
2147                    OPTIMIZER_INT_FUNC_CHECK("similar_text") ||
2148                    OPTIMIZER_INT_FUNC_CHECK("strnatcmp") ||
2149                    OPTIMIZER_INT_FUNC_CHECK("strnatcasecmp") ||
2150                    OPTIMIZER_INT_FUNC_CHECK("substr_count") ||
2151                    OPTIMIZER_INT_FUNC_CHECK("substr_compare") ||
2152                    OPTIMIZER_INT_FUNC_CHECK("intval") ||
2153                    OPTIMIZER_INT_FUNC_CHECK("memory_get_usage") ||
2154                    OPTIMIZER_INT_FUNC_CHECK("memory_get_peak_usage") ||
2155                    OPTIMIZER_INT_FUNC_CHECK("version_compare")
2156                )
2157            ) {
2158                return 1;
2159            }
2160            return 0;
2161    }
2162    return 0;
2163}
2164
2165
2166/*
2167 * Optimize binary (math) operation where one of the opperands is a constant
2168 *   value and the other is a variable. The are basic math identities being
2169 *   optimized.
2170 */
2171static void optimize_binary_op_1const(zend_op** a, zend_op** b, CB* cb, zend_op** Ts OPTIMIZER_STATS_PARAM TSRMLS_DC)
2172{
2173    zend_op* op = *b;
2174    zend_op* prev = *a;
2175    int rl = -1;
2176    long v;
2177    zend_uint var;
2178    int var_type;
2179
2180    if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_LONG && (OP1_TYPE(op) == IS_TMP_VAR || OP1_TYPE(op) == IS_CV || OP1_TYPE(op) == IS_VAR)) {
2181        /* right opperand (op2) */
2182        convert_scalar_to_number(&__OP2_VAL(op) TSRMLS_CC);
2183
2184        if (OP2_VALT(op) == IS_LONG && (OP2_VAL_L(op) == 0 || OP2_VAL_L(op) == 1)) {
2185            v = OP2_VAL_L(op);
2186            var = op->op1.u.var;
2187            var_type = OP1_TYPE(op);
2188            rl = 0;
2189        }
2190    } else if (OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_LONG && (OP2_TYPE(op) == IS_TMP_VAR || OP2_TYPE(op) == IS_CV || OP2_TYPE(op) == IS_VAR)) {
2191        /* left opperand (op1) */
2192        convert_scalar_to_number(&__OP1_VAL(op) TSRMLS_CC);
2193
2194        if (OP1_VALT(op) == IS_LONG && (OP1_VAL_L(op) == 0 || OP1_VAL_L(op) == 1)) {
2195            v = OP1_VAL_L(op);
2196            var = op->op2.u.var;
2197            var_type = OP2_TYPE(op);
2198            rl = 1;
2199        }
2200    }
2201
2202    if (rl >= 0) {
2203        switch (op->opcode) {
2204            case ZEND_ADD:
2205            case ZEND_BW_OR:
2206                if (v == 0) {
2207                    /* ZEND_ADD: $a+0 -> $a or 0+$a -> $a*/
2208                    /* ZEND_BW_OR: $a|0 -> $a or 0|$a -> $a */
2209                    optimize_to_var(var, var_type, &prev, &op, cb, Ts);
2210                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2211                }
2212                break;
2213            case ZEND_SUB:
2214                if (rl == 0 && v == 0) {
2215                    /* $a-0 -> $a */
2216                    optimize_to_var(var, var_type, &prev, &op, cb, Ts);
2217                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2218                }
2219                break;
2220            case ZEND_MUL:
2221                if (v == 0) {
2222                    /* $a*0 -> 0 or 0*$a -> 0 */
2223                    optimize_to_long(0, &prev, &op, cb, Ts);
2224                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2225                } else if (v == 1) {
2226                    /* $a*1 -> $a or 1*$a -> $a */
2227                    optimize_to_var(var, var_type, &prev, &op, cb, Ts);
2228                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2229                }
2230                break;
2231            case ZEND_DIV:
2232                if (rl == 0 && v == 1) {
2233                    /* $a/1 -> $a */
2234                    optimize_to_var(var, var_type, &prev, &op, cb, Ts);
2235                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2236                } else if (rl == 1 && v == 0) {
2237                    /* 0/$a -> 0 */
2238                    optimize_to_long(0, &prev, &op, cb, Ts);
2239                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2240                }
2241                break;
2242            case ZEND_MOD:
2243                if (rl == 1 && v == 0) {
2244                    /* 0%$a -> 0 */
2245                    optimize_to_long(0, &prev, &op, cb, Ts);
2246                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2247                } else if (rl == 0 && v == 1) {
2248                    /* $a%1 -> 0 */
2249                    optimize_to_long(0, &prev, &op, cb, Ts);
2250                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2251                }
2252                break;
2253            case ZEND_BW_AND:
2254                if (v == 0) {
2255                    /* $a&0 -> 0 or 0&$a -> 0 */
2256                    optimize_to_long(0, &prev, &op, cb, Ts);
2257                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2258                }
2259                break;
2260            case ZEND_SR:
2261            case ZEND_SL:
2262                if (rl == 0 && v == 0) {
2263                    /* ZEND_SR: $a >> 0 -> $a */
2264                    /* ZEND_SL: $a << 0 -> $a */
2265                    optimize_to_var(var, var_type, &prev, &op, cb, Ts);
2266                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2267                } else if (rl == 1 && v == 0) {
2268                    /* ZEND_SR: 0 >> $a -> 0 */
2269                    /* ZEND_SL: 0 << $a -> 0 */
2270                    optimize_to_long(0, &prev, &op, cb, Ts);
2271                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2272                }
2273                break;
2274            case ZEND_ASSIGN_ADD:
2275            case ZEND_ASSIGN_SUB:
2276            case ZEND_ASSIGN_BW_OR:
2277            case ZEND_ASSIGN_SR:
2278            case ZEND_ASSIGN_SL:
2279                if (v == 0) {
2280                    /* ZEND_ASSIGN_ADD: $a += 0 -> NOP */
2281                    /* ZEND_ASSIGN_SUB: $a -= 0 -> NOP */
2282                    /* ZEND_ASSIGN_BW_OR: $a |= 0 -> NOP */
2283                    /* ZEND_ASSIGN_SR: $a >>= 0 -> NOP */
2284                    /* ZEND_ASSIGN_SL: $a <<= 0 -> NOP */
2285                    SET_TO_NOP(op);
2286                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2287                }
2288                break;
2289            case ZEND_ASSIGN_MUL:
2290                if (v == 0) {
2291                    /* $a *= 0 -> $a=0 */
2292                    op->opcode = ZEND_ASSIGN;
2293                    OP2_VALT(op) = IS_LONG;
2294                    OP2_VAL_L(op) = 0;
2295                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2296                } else if (v == 1) {
2297                    /* $a *= 1 -> NOP */
2298                    SET_TO_NOP(op);
2299                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2300                }
2301                break;
2302            case ZEND_ASSIGN_DIV:
2303                if (v == 1) {
2304                    /* $a /= 1 -> NOP */
2305                    SET_TO_NOP(op);
2306                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2307                }
2308                break;
2309            case ZEND_ASSIGN_MOD:
2310                if (v == 1) {
2311                    /* $a %= 1 -> 0 */
2312                    op->opcode = ZEND_ASSIGN;
2313                    OP2_VALT(op) = IS_LONG;
2314                    OP2_VAL_L(op) = 0;
2315                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2316                }
2317                break;
2318            case ZEND_ASSIGN_BW_AND:
2319                if (v == 0) {
2320                    /* $a &= 0 -> 0 */
2321                    op->opcode = ZEND_ASSIGN;
2322                    OP2_VALT(op) = IS_LONG;
2323                    OP2_VAL_L(op) = 0;
2324                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2325                }
2326                break;
2327        }
2328    }
2329}
2330
2331
2332/*
2333 * Performs peephole optimization on a code block.
2334 *
2335 */
2336static void optimize_code_block(CB *cb, zend_op_array *op_array, char *global, int pass, optimizer_source_file* sf, int *jmp_lines OPTIMIZER_STATS_PARAM TSRMLS_DC)
2337{
2338    zend_op* prev = NULL;
2339    zend_op* op = cb->start;
2340    zend_op* end = op + cb->len;
2341    char *server_op = NULL;
2342
2343    HashTable assigns;
2344    HashTable fetch_dim;
2345
2346    OPTIMIZER_ALLOCA_FLAG(Ts_use_heap)
2347    zend_op** Ts = optimizer_do_alloca(sizeof(zend_op*) * op_array->T, Ts_use_heap);
2348    memset(Ts, 0, sizeof(zend_op*) * op_array->T);
2349
2350    zend_hash_init(&assigns, 0, NULL, NULL, 0);
2351    zend_hash_init(&fetch_dim, 0, NULL, NULL, 0);
2352
2353    server_op = OPTIMIZER_G(optimize_inline_server);
2354    if (*server_op == '\0') {
2355        server_op = NULL;
2356    }
2357
2358    while (op < end) {
2359        zend_bool saved_in_compilation;
2360        char *saved_compiled_filename;
2361        int saved_zend_lineno;
2362        OPTIMIZE_SAVE(op_array, op);
2363        /* Constant Folding */
2364        if (OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_QM_ASSIGN && OP1_TYPE(DEFINED_OP(op->op1)) == IS_CONST) {
2365            zend_op *x = DEFINED_OP(op->op1);
2366            if (op->opcode != ZEND_CASE) {
2367                UNDEFINE_OP(op->op1);
2368                memcpy(&op->op1, &x->op1, sizeof(znode));
2369                SET_TO_NOP(x);
2370                OPTIMIZER_STATS_ADD_PEEPHOLE();
2371            }
2372        }
2373
2374        if (OP2_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op2) && DEFINED_OP(op->op2)->opcode == ZEND_QM_ASSIGN && OP1_TYPE(DEFINED_OP(op->op2)) == IS_CONST) {
2375            zend_op *x = DEFINED_OP(op->op2);
2376            UNDEFINE_OP(op->op2);
2377            memcpy(&op->op2, &x->op1, sizeof(znode));
2378            SET_TO_NOP(x);
2379            OPTIMIZER_STATS_ADD_PEEPHOLE();
2380        }
2381
2382
2383        /* simplify binary operations */
2384        if (op->opcode == ZEND_IS_EQUAL) {
2385            if (OP1_TYPE(op) == IS_CONST && (OP1_VALT(op) == IS_BOOL && OP1_VAL_L(op) == 0)) {
2386                op->opcode = ZEND_BOOL_NOT;
2387                memcpy(&op->op1, &op->op2, sizeof(znode));
2388                OP2_TYPE(op) = IS_UNUSED;
2389                OPTIMIZER_STATS_ADD_PEEPHOLE();
2390            } else if (OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_BOOL && OP1_VAL_L(op) == 1) {
2391                op->opcode = ZEND_BOOL;
2392                memcpy(&op->op1, &op->op2, sizeof(znode));
2393                OP2_TYPE(op) = IS_UNUSED;
2394                OPTIMIZER_STATS_ADD_PEEPHOLE();
2395            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_BOOL && OP2_VAL_L(op) == 0) {
2396                op->opcode = ZEND_BOOL_NOT;
2397                OP2_TYPE(op) = IS_UNUSED;
2398                OPTIMIZER_STATS_ADD_PEEPHOLE();
2399            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_BOOL && OP2_VAL_L(op) == 1) {
2400                op->opcode = ZEND_BOOL;
2401                OP2_TYPE(op) = IS_UNUSED;
2402                OPTIMIZER_STATS_ADD_PEEPHOLE();
2403            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_LONG && OP2_VAL_L(op) == 0 && (OP1_TYPE(op) == IS_TMP_VAR || OP1_TYPE(op) == IS_VAR) && IS_DEFINED(op->op1) && is_numeric_result(DEFINED_OP(op->op1))) {
2404                op->opcode = ZEND_BOOL_NOT;
2405                OP2_TYPE(op) = IS_UNUSED;
2406                OPTIMIZER_STATS_ADD_PEEPHOLE();
2407            }
2408        } else if (op->opcode == ZEND_IS_NOT_EQUAL) {
2409            if (OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_BOOL && OP1_VAL_L(op) == 0) {
2410                op->opcode = ZEND_BOOL;
2411                memcpy(&op->op1, &op->op2, sizeof(znode));
2412                OP2_TYPE(op) = IS_UNUSED;
2413                OPTIMIZER_STATS_ADD_PEEPHOLE();
2414            } else if (OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_BOOL && OP1_VAL_L(op) == 1) {
2415                op->opcode = ZEND_BOOL_NOT;
2416                memcpy(&op->op1, &op->op2, sizeof(znode));
2417                OP2_TYPE(op) = IS_UNUSED;
2418                OPTIMIZER_STATS_ADD_PEEPHOLE();
2419            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_BOOL && OP2_VAL_L(op) == 0) {
2420                op->opcode = ZEND_BOOL;
2421                OP2_TYPE(op) = IS_UNUSED;
2422                OPTIMIZER_STATS_ADD_PEEPHOLE();
2423            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_BOOL && OP2_VAL_L(op) == 1) {
2424                op->opcode = ZEND_BOOL_NOT;
2425                OP2_TYPE(op) = IS_UNUSED;
2426                OPTIMIZER_STATS_ADD_PEEPHOLE();
2427            } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_LONG && OP2_VAL_L(op) == 0 && (OP1_TYPE(op) == IS_TMP_VAR || OP1_TYPE(op) == IS_VAR) && IS_DEFINED(op->op1) && is_numeric_result(DEFINED_OP(op->op1))) {
2428                op->opcode = ZEND_BOOL;
2429                OP2_TYPE(op) = IS_UNUSED;
2430                OPTIMIZER_STATS_ADD_PEEPHOLE();
2431            }
2432        }
2433
2434
2435        /* Optimize binary ops with one constant operand and one variable operand using some basic identities */
2436        if ((op->opcode == ZEND_ADD || op->opcode == ZEND_SUB || op->opcode == ZEND_MUL ||
2437            op->opcode == ZEND_DIV || op->opcode == ZEND_MOD || op->opcode == ZEND_BW_AND ||
2438            op->opcode == ZEND_BW_OR || op->opcode == ZEND_ASSIGN_ADD || op->opcode == ZEND_ASSIGN_SUB ||
2439            op->opcode == ZEND_ASSIGN_MUL || op->opcode == ZEND_ASSIGN_DIV || op->opcode == ZEND_ASSIGN_MOD ||
2440            op->opcode == ZEND_SL || op->opcode == ZEND_SR || op->opcode == ZEND_ASSIGN_BW_AND ||
2441            op->opcode == ZEND_ASSIGN_BW_OR || op->opcode == ZEND_ASSIGN_SR || op->opcode == ZEND_ASSIGN_SL) &&
2442            ((OP2_TYPE(op) == IS_CONST && (OP1_TYPE(op) == IS_TMP_VAR || OP1_TYPE(op) == IS_CV || OP1_TYPE(op) == IS_VAR)) ||
2443            (OP1_TYPE(op) == IS_CONST && (OP2_TYPE(op) == IS_TMP_VAR || OP2_TYPE(op) == IS_CV || OP2_TYPE(op) == IS_VAR))) &&
2444            NEXT_OP(op)->opcode != ZEND_OP_DATA)
2445        {
2446            optimize_binary_op_1const(&prev, &op, cb, Ts OPTIMIZER_STATS_PARAM_PASS TSRMLS_CC);
2447        }
2448
2449
2450        /* output block optimization */
2451        if (op->opcode == ZEND_INIT_STRING && RES_TYPE(op) == IS_TMP_VAR) {
2452            zend_op *x = NEXT_OP(op);
2453            /* validation check */
2454            while (x->opcode == ZEND_ADD_STRING || x->opcode == ZEND_ADD_VAR) {
2455                x++;
2456            }
2457            if (x->opcode == ZEND_ECHO) {
2458                zend_op *s = NEXT_OP(op);
2459                UNDEFINE_OP(op->op1);
2460                SET_TO_NOP(op);
2461
2462                while (s < x) {
2463                    s->opcode = ZEND_ECHO;
2464                    RES_TYPE(s) = IS_UNUSED;
2465                    memcpy(&s->op1, &s->op2, sizeof(s->op2));
2466                    OP2_TYPE(s) = IS_UNUSED;
2467                    s++;
2468                }
2469
2470                UNDEFINE_OP(x->op1);
2471                SET_TO_NOP(x);
2472            }
2473        }
2474
2475
2476        /* Optimize function calls */
2477        if (op->opcode == ZEND_DO_FCALL && OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_STRING) {
2478            optimize_fcall(&op, &prev, cb, op_array, Ts, &sf->fh OPTIMIZER_STATS_PARAM_PASS TSRMLS_CC);
2479        }
2480
2481
2482        /* optimize out echo ''; lines (common issue with Smarty templates) */
2483        if (op->opcode == ZEND_ECHO && OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_STRING && OP1_VAL_SL(op) == 0) {
2484            efree(OP1_VAL_S(op));
2485            SET_TO_NOP(op);
2486            OPTIMIZER_STATS_ADD_PEEPHOLE();
2487        }
2488        /* optimize out known constants */
2489        else if (op->opcode == ZEND_FETCH_CONSTANT && RES_TYPE(op) == IS_TMP_VAR && OP1_TYPE(op) == IS_UNUSED && OP2_TYPE(op) == IS_CONST && !op->extended_value) {
2490            zval c;
2491            if (optimize_get_constant(op, OP2_VAL_S(op), OP2_VAL_SL(op), NULL, 0, &c TSRMLS_CC)) {
2492                UNDEFINE_OP(op->op1);
2493                zval_dtor(&__OP2_VAL(op));
2494                op->opcode = ZEND_QM_ASSIGN;
2495                OP1_TYPE(op) = IS_CONST;
2496                OP2_TYPE(op) = IS_UNUSED;
2497                __OP1_VAL(op) = c;
2498                zval_copy_ctor(&__OP1_VAL(op));
2499                OPTIMIZER_STATS_ADD_PEEPHOLE();
2500            }
2501        }
2502        /* optimize out known class constants */
2503        else if (prev != NULL &&
2504            prev->opcode == ZEND_FETCH_CLASS && OP2_TYPE(prev) == IS_CONST && prev->extended_value == ZEND_FETCH_CLASS_GLOBAL &&
2505            RES_TYPE(op) == IS_TMP_VAR && op->opcode == ZEND_FETCH_CONSTANT && OP2_TYPE(op) == IS_CONST && OP1_VR(op) == RES_VR(prev) &&
2506            (
2507                (OP1_TYPE(NEXT_OP(op)) == IS_TMP_VAR && OP1_VR(NEXT_OP(op)) == RES_VR(op)) ||
2508                (OP2_TYPE(NEXT_OP(op)) == IS_TMP_VAR && OP2_VR(NEXT_OP(op)) == RES_VR(op))
2509            )
2510        ) {
2511            zval c;
2512            if (optimize_get_constant(op, OP2_VAL_S(op), OP2_VAL_SL(op), OP2_VAL_S(prev), OP2_VAL_SL(prev), &c TSRMLS_CC)) {
2513                if (OP1_TYPE(NEXT_OP(op)) == IS_TMP_VAR && OP1_VR(NEXT_OP(op)) == RES_VR(op)) {
2514                    zend_op *x = NEXT_OP(op);
2515
2516                    UNDEFINE_OP(x->op1);
2517                    OP1_TYPE(x) = IS_CONST;
2518                    __OP1_VAL(x) = c;
2519                    zval_copy_ctor(&__OP1_VAL(x));
2520                    INIT_PZVAL(&__OP1_VAL(x));
2521                }
2522                if (OP2_TYPE(NEXT_OP(op)) == IS_TMP_VAR && OP2_VR(NEXT_OP(op)) == RES_VR(op)) {
2523                    zend_op *x = NEXT_OP(op);
2524
2525                    UNDEFINE_OP(x->op2);
2526                    OP2_TYPE(x) = IS_CONST;
2527                    __OP2_VAL(x) = c;
2528                    zval_copy_ctor(&__OP2_VAL(x));
2529                    INIT_PZVAL(&__OP2_VAL(x));
2530                }
2531                UNDEFINE_OP(prev->op1);
2532                zval_dtor(&__OP2_VAL(prev));
2533                SET_TO_NOP(prev);
2534                UNDEFINE_OP(op->op1);
2535                zval_dtor(&__OP2_VAL(op));
2536                SET_TO_NOP(op);
2537            }
2538        }
2539        /* try to resolve partial file paths for include/require statements */
2540        else if (op->opcode == ZEND_INCLUDE_OR_EVAL && OP1_TYPE(op) == IS_CONST &&
2541            (OP2_VAL_L(op) & (0x2|0x4|0x8|0x10)) && /* include/require/include_once/require_once */
2542            OP1_VALT(op) == IS_STRING &&
2543            !IS_ABSOLUTE_PATH(OP1_VAL_S(op), OP1_VAL_SL(op)) &&
2544            !php_memnstr(OP1_VAL_S(op), "://", sizeof("://") - 1, OP1_VAL_S(op) + OP1_VAL_SL(op))
2545        ) {
2546            optimize_fullpath(op, op_array->filename, OP2_VAL_L(op) TSRMLS_CC);
2547        }
2548        /* try to optimize out allows $_SERVER['key'] calls */
2549        else if (server_op && op->opcode == ZEND_FETCH_R && FETCH_TYPE(op) == ZEND_FETCH_GLOBAL &&
2550            OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_STRING &&
2551            !strcmp(OP1_VAL_S(op), "_SERVER") &&
2552            NEXT_OP(op)->opcode == ZEND_FETCH_DIM_R && OP1_VR(NEXT_OP(op)) == RES_VR(op) &&
2553            OP2_TYPE(NEXT_OP(op)) == IS_CONST && Z_TYPE(__OP2_VAL(NEXT_OP(op))) == IS_STRING &&
2554            optimizeable_server_var(server_op, OP2_VAL_S(NEXT_OP(op)), OP2_VAL_SL(NEXT_OP(op))) &&
2555            PG(http_globals)[TRACK_VARS_SERVER] &&
2556            zend_hash_exists(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), OP2_VAL_S(NEXT_OP(op)), OP2_VAL_SL(NEXT_OP(op))+1)
2557        ) {
2558            zval **tmp;
2559
2560            if (zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), OP2_VAL_S(NEXT_OP(op)), OP2_VAL_SL(NEXT_OP(op))+1, (void **) &tmp) == SUCCESS) {
2561                zend_op *x = NEXT_OP(op);
2562
2563                switch (Z_TYPE_PP(tmp)) {
2564                    case IS_STRING:
2565                        optimize_to_string_ex(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), &op, &x, cb, Ts, OPTIMIZE_TO_FULL TSRMLS_CC);
2566                        OPTIMIZER_STATS_ADD_PEEPHOLE();
2567                        break;
2568                    case IS_LONG:
2569                        optimize_to_long_ex(Z_LVAL_PP(tmp), &op, &x, cb, Ts, OPTIMIZE_TO_FULL TSRMLS_CC);
2570                        OPTIMIZER_STATS_ADD_PEEPHOLE();
2571                        break;
2572                    case IS_DOUBLE:
2573                        optimize_to_double_ex(Z_DVAL_PP(tmp), &op, &x, cb, Ts, OPTIMIZE_TO_FULL TSRMLS_CC);
2574                        OPTIMIZER_STATS_ADD_PEEPHOLE();
2575                        break;
2576                    case IS_BOOL:
2577                        if (Z_LVAL_PP(tmp)) {
2578                            optimize_to_true_ex(&op, &x, cb, Ts, OPTIMIZE_TO_FULL TSRMLS_CC);
2579                        } else {
2580                            optimize_to_false_ex(&op, &x, cb, Ts, OPTIMIZE_TO_FULL TSRMLS_CC);
2581                        }
2582                        OPTIMIZER_STATS_ADD_PEEPHOLE();
2583                        break;
2584                    default: /* shouldn't happen, but can if ARRAY/NULL/RESOURCE/OBJECT types are being used */
2585                        break;
2586                }
2587            }
2588        }
2589        /* if allowed optimize our assert() calls */
2590        else if (prev != NULL && op->opcode == ZEND_DO_FCALL && USED_RESULT(op) &&
2591                op->extended_value == 1 && !strcmp(OP1_VAL_S(op),"assert") &&
2592                ((prev->opcode == ZEND_SEND_VAL && OP1_TYPE(prev) == IS_CONST) || prev->opcode == ZEND_SEND_VAR)
2593                && OPTIMIZER_G(optimize_assert)
2594        ) {
2595            zval_dtor(&__OP1_VAL(op));
2596            SET_TO_NOP(op);
2597            if (prev->opcode != ZEND_SEND_VAR) {
2598                zval_dtor(&__OP1_VAL(prev));
2599                SET_TO_NOP(prev);
2600            }
2601            OPTIMIZER_STATS_ADD_PEEPHOLE();
2602        }
2603        /* try to optimize out define() calls */
2604        else if (
2605            prev != NULL && op->opcode == ZEND_DO_FCALL && OP1_TYPE(op) == IS_CONST &&
2606            OP1_VALT(op) == IS_STRING && (op->extended_value == 2 || op->extended_value == 3) &&
2607            !strcmp(OP1_VAL_S(op),"define") && prev->opcode == ZEND_SEND_VAL && OP1_TYPE(prev) == IS_CONST &&
2608            (op-2)->opcode == ZEND_SEND_VAL && OP1_TYPE((op-2)) == IS_CONST &&
2609            (op->extended_value < 3 || ((op-3)->opcode == ZEND_SEND_VAL && OP1_TYPE((op-3)) == IS_CONST)) &&
2610            (!EG(in_execution) && OPTIMIZER_G(optimize_define)) && /* not inside an include */
2611            !jmp_lines[(op - op_array->opcodes)] /* non-conditional */
2612        ) {
2613            optimize_define(&prev, &op, cb, Ts TSRMLS_CC);
2614        }
2615        /* ternary operator to if() {} else {} optimization */
2616        else if (
2617            op->opcode == ZEND_JMPZ && NEXT_OP(op)->opcode == ZEND_QM_ASSIGN &&
2618            NEXT_OP(NEXT_OP(op))->opcode == ZEND_JMP && NEXT_OP(NEXT_OP(NEXT_OP(op)))->opcode == ZEND_QM_ASSIGN
2619        ) {
2620            zend_op *x = NEXT_OP(NEXT_OP(NEXT_OP(NEXT_OP(op))));
2621
2622            if (x->opcode == ZEND_ASSIGN && RES_TYPE(x) == IS_UNUSED) { /* $var = $foo ? a : b */
2623                NEXT_OP(op)->opcode = NEXT_OP(NEXT_OP(NEXT_OP(op)))->opcode = x->opcode;
2624
2625                memcpy(&NEXT_OP(op)->op2, &NEXT_OP(op)->op1, sizeof(znode));
2626                memcpy(&NEXT_OP(NEXT_OP(NEXT_OP(op)))->op2, &NEXT_OP(NEXT_OP(NEXT_OP(op)))->op1, sizeof(znode));
2627
2628                RESULT_TYPE(NEXT_OP(op)) |= EXT_TYPE_UNUSED;
2629                RESULT_TYPE(NEXT_OP(NEXT_OP(NEXT_OP(op)))) |= EXT_TYPE_UNUSED;
2630
2631                UNDEFINE_OP(NEXT_OP(op)->result);
2632                UNDEFINE_OP(NEXT_OP(NEXT_OP(NEXT_OP(op)))->result);
2633                UNDEFINE_OP(x->op2);
2634
2635                RES_TYPE(NEXT_OP(op)) = RES_TYPE(NEXT_OP(NEXT_OP(NEXT_OP(op)))) = IS_UNUSED;
2636
2637                memcpy(&NEXT_OP(op)->op1, &x->op1, sizeof(znode));
2638                memcpy(&NEXT_OP(NEXT_OP(NEXT_OP(op)))->op1, &x->op1, sizeof(znode));
2639
2640                SET_TO_NOP(x);
2641            } else if (x->opcode == ZEND_ECHO) { /* echo $foo ? a : b */
2642                NEXT_OP(op)->opcode = NEXT_OP(NEXT_OP(NEXT_OP(op)))->opcode = x->opcode;
2643
2644                UNDEFINE_OP(NEXT_OP(op)->result);
2645                UNDEFINE_OP(NEXT_OP(NEXT_OP(NEXT_OP(op)))->result);
2646                UNDEFINE_OP(x->op2);
2647
2648                RESULT_TYPE(NEXT_OP(op)) |= EXT_TYPE_UNUSED;
2649                RESULT_TYPE(NEXT_OP(NEXT_OP(NEXT_OP(op)))) |= EXT_TYPE_UNUSED;
2650
2651                RES_TYPE(NEXT_OP(op)) = RES_TYPE(NEXT_OP(NEXT_OP(NEXT_OP(op)))) = IS_UNUSED;
2652                SET_TO_NOP(x);
2653            }
2654
2655            OPTIMIZER_STATS_ADD_PEEPHOLE();
2656        }
2657        /* optimize out mathematical operations on constant values */
2658        else if (
2659            (
2660                op->opcode == ZEND_ADD || op->opcode == ZEND_SUB || op->opcode == ZEND_MUL || op->opcode == ZEND_DIV ||
2661                op->opcode == ZEND_MOD || op->opcode == ZEND_SL || op->opcode == ZEND_SR || op->opcode == ZEND_CONCAT ||
2662                op->opcode == ZEND_BW_OR || op->opcode == ZEND_BW_AND || op->opcode == ZEND_BW_XOR ||
2663                op->opcode == ZEND_BOOL_XOR || op->opcode == ZEND_IS_IDENTICAL || op->opcode == ZEND_IS_NOT_IDENTICAL ||
2664                op->opcode == ZEND_IS_EQUAL || op->opcode == ZEND_IS_NOT_EQUAL || op->opcode == ZEND_IS_SMALLER ||
2665                op->opcode == ZEND_IS_SMALLER_OR_EQUAL
2666            ) && OP1_TYPE(op) == IS_CONST && OP2_TYPE(op) == IS_CONST && RES_TYPE(op) == IS_TMP_VAR
2667        ) {
2668            typedef int (*binary_op_type)(zval *, zval *, zval* TSRMLS_DC);
2669
2670            binary_op_type binary_op = (binary_op_type)get_binary_op(op->opcode);
2671
2672            if (binary_op != NULL) {
2673                zval res;
2674
2675                if (binary_op(&res, &__OP1_VAL(op), &__OP2_VAL(op) TSRMLS_CC) != FAILURE) {
2676                    zval_dtor(&__OP1_VAL(op));
2677                    zval_dtor(&__OP2_VAL(op));
2678                    op->opcode = ZEND_QM_ASSIGN;
2679                    op->extended_value = 0;
2680                    OP1_TYPE(op) = IS_CONST;
2681                    memcpy(&__OP1_VAL(op), &res, sizeof(zval));
2682                    OP2_TYPE(op) = IS_UNUSED;
2683
2684                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2685                }
2686            }
2687        }
2688        /* optimize unary ops with static constants on both sides */
2689        else if ((op->opcode == ZEND_BW_NOT || op->opcode == ZEND_BOOL_NOT) && OP1_TYPE(op) == IS_CONST && RES_TYPE(op) == IS_TMP_VAR) {
2690            int (*unary_op)(zval *result, zval *op1) = unary_op = get_unary_op(op->opcode);
2691
2692            if (unary_op != NULL) {
2693                zval res;
2694
2695                if (unary_op(&res, &__OP1_VAL(op)) != FAILURE) {
2696                    zval_dtor(&__OP1_VAL(op));
2697                    op->opcode = ZEND_QM_ASSIGN;
2698                    op->extended_value = 0;
2699                    OP1_TYPE(op) = IS_CONST;
2700                    memcpy(&__OP1_VAL(op), &res, sizeof(zval));
2701                    OP2_TYPE(op) = IS_UNUSED;
2702
2703                    OPTIMIZER_STATS_ADD_PEEPHOLE();
2704                }
2705            }
2706        }
2707        /* optimize if (const_value) situations */
2708        else if ((op->opcode == ZEND_BOOL) && OP1_TYPE(op) == IS_CONST && RES_TYPE(op) == IS_TMP_VAR) {
2709            zval res;
2710
2711            ZVAL_BOOL(&res, zend_is_true(&__OP1_VAL(op)));
2712            zval_dtor(&__OP1_VAL(op));
2713            op->opcode = ZEND_QM_ASSIGN;
2714            op->extended_value = 0;
2715            OP1_TYPE(op) = IS_CONST;
2716            memcpy(&__OP1_VAL(op), &res, sizeof(zval));
2717            OP2_TYPE(op) = IS_UNUSED;
2718
2719            OPTIMIZER_STATS_ADD_PEEPHOLE();
2720        }
2721        /* optimize (cast) operations for const values */
2722        else if (
2723            op->opcode == ZEND_CAST && OP1_TYPE(op) == IS_CONST && OP1_VALT(op) != IS_STRING && RES_TYPE(op) == IS_TMP_VAR &&
2724            op->extended_value != IS_ARRAY && op->extended_value != IS_OBJECT && op->extended_value != IS_RESOURCE
2725        ) {
2726            zval res;
2727            memcpy(&res, &__OP1_VAL(op), sizeof(zval));
2728            zval_copy_ctor(&res);
2729
2730            switch (op->extended_value) {
2731                case IS_NULL:
2732                    convert_to_null(&res);
2733                    break;
2734                case IS_BOOL:
2735                    convert_to_boolean(&res);
2736                    break;
2737                case IS_LONG:
2738                    convert_to_long(&res);
2739                    break;
2740                case IS_DOUBLE:
2741                    convert_to_double(&res);
2742                    break;
2743                case IS_STRING:
2744                    convert_to_string(&res);
2745                    break;
2746                case IS_ARRAY:
2747                    convert_to_array(&res);
2748                    break;
2749                case IS_OBJECT:
2750                    convert_to_object(&res);
2751                    break;
2752            }
2753            zval_dtor(&__OP1_VAL(op));
2754            op->opcode = ZEND_QM_ASSIGN;
2755            op->extended_value = 0;
2756            OP1_TYPE(op) = IS_CONST;
2757            memcpy(&__OP1_VAL(op), &res, sizeof(zval));
2758            OP2_TYPE(op) = IS_UNUSED;
2759            OPTIMIZER_STATS_ADD_PEEPHOLE();
2760        }
2761        /* string creation folding, relating to leftover ops from INIT_STRING */
2762        else if (op->opcode == ZEND_FREE && OP1_TYPE(op) == IS_CONST) {
2763            zval_dtor(&__OP1_VAL(op));
2764            SET_TO_NOP(op);
2765            OPTIMIZER_STATS_ADD_PEEPHOLE();
2766        }
2767        /* get rid of INIT_STRING */
2768        else if (op->opcode == ZEND_INIT_STRING) {
2769            op->opcode = ZEND_QM_ASSIGN;
2770            OP1_TYPE(op) = IS_CONST;
2771            OP2_TYPE(op) = IS_UNUSED;
2772            OP1_VALT(op) = IS_STRING;
2773            OP1_VAL_SL(op) = 0;
2774            OP1_VAL_S(op) = STR_EMPTY_ALLOC();
2775            OPTIMIZER_STATS_ADD_PEEPHOLE();
2776        }
2777        /* optimize ADD_CHAR TO QM_ASSIGN to be later inlined if possible */
2778        else if (op->opcode == ZEND_ADD_CHAR && OP1_TYPE(op) == IS_CONST) {
2779            size_t len;
2780
2781            op->opcode = ZEND_QM_ASSIGN;
2782            OP1_TYPE(op) = IS_CONST;
2783            OP2_TYPE(op) = IS_UNUSED;
2784            convert_to_string(&__OP1_VAL(op));
2785            len = OP1_VAL_SL(op) + 1;
2786            STR_REALLOC(OP1_VAL_S(op),len+1);
2787            OP1_VAL_S(op)[len-1] = (char) OP2_VAL_L(op);
2788            OP1_VAL_S(op)[len] = 0;
2789            OP1_VAL_SL(op) = len;
2790
2791            OPTIMIZER_STATS_ADD_PEEPHOLE();
2792        }
2793        /* optimize ADD_STRING TO QM_ASSIGN to be later inlined if possible */
2794        else if (op->opcode == ZEND_ADD_STRING && OP1_TYPE(op) == IS_CONST) {
2795            size_t len;
2796
2797            op->opcode = ZEND_QM_ASSIGN;
2798            OP1_TYPE(op) = IS_CONST;
2799            OP2_TYPE(op) = IS_UNUSED;
2800            convert_to_string(&__OP1_VAL(op));
2801            convert_to_string(&__OP2_VAL(op));
2802            len = OP1_VAL_SL(op) + OP2_VAL_SL(op);
2803            STR_REALLOC(OP1_VAL_S(op),len+1);
2804            memcpy(OP1_VAL_S(op) + OP1_VAL_SL(op), OP2_VAL_S(op), OP2_VAL_SL(op));
2805            OP1_VAL_S(op)[len] = 0;
2806            OP1_VAL_SL(op) = len;
2807            STR_FREE(OP2_VAL_S(op));
2808
2809            OPTIMIZER_STATS_ADD_PEEPHOLE();
2810        }
2811        /* convert addition of constant to concat */
2812        else if (op->opcode == ZEND_ADD_VAR && OP1_TYPE(op) == IS_CONST) {
2813            op->opcode = ZEND_CONCAT;
2814            OPTIMIZER_STATS_ADD_PEEPHOLE();
2815        }
2816        /* optimize out addition of empty character */
2817        else if (
2818            op->opcode == ZEND_ADD_CHAR && OP1_TYPE(op) == IS_TMP_VAR &&
2819            IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_CONCAT &&
2820            OP1_TYPE(DEFINED_OP(op->op1)) == IS_CONST && OP1_VALT(DEFINED_OP(op->op1)) == IS_STRING
2821            && !OP1_VAL_SL(DEFINED_OP(op->op1))
2822        ) {
2823            char ch = (char) OP2_VAL_L(op);
2824            zend_op *x = DEFINED_OP(op->op1);
2825
2826            UNDEFINE_OP(op->op1);
2827            memcpy(&op->op1, &x->op2, sizeof(znode));
2828            op->opcode = ZEND_CONCAT;
2829            ZVAL_STRINGL(&__OP2_VAL(op), &ch, 1, 1);
2830            STR_FREE(OP1_VAL_S(x));
2831            SET_TO_NOP(x);
2832            OPTIMIZER_STATS_ADD_PEEPHOLE();
2833        }
2834        /* optimize out addition, concatination and adding of vars to expressions with empty strings */
2835        else if (
2836            (op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT || op->opcode == ZEND_ADD_VAR) &&
2837            OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_CONCAT &&
2838            OP1_TYPE(DEFINED_OP(op->op1)) == IS_CONST && OP1_VALT(DEFINED_OP(op->op1)) == IS_STRING &&
2839            !OP1_VAL_SL(DEFINED_OP(op->op1))
2840        ) {
2841            zend_op *x = DEFINED_OP(op->op1);
2842
2843            UNDEFINE_OP(op->op1);
2844            op->opcode = ZEND_CONCAT;
2845            memcpy(&op->op1, &x->op2, sizeof(znode));
2846            STR_FREE(OP1_VAL_S(x));
2847            SET_TO_NOP(x);
2848            OPTIMIZER_STATS_ADD_PEEPHOLE();
2849        }
2850        /* optimize out addition of 2 constant chars into a single add string */
2851        else if (op->opcode == ZEND_ADD_CHAR && OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_ADD_CHAR) {
2852            char ch1 = (char) OP2_VAL_L(DEFINED_OP(op->op1));
2853            char ch2 = (char) OP2_VAL_L(op);
2854
2855            OP2_VALT(DEFINED_OP(op->op1)) = IS_STRING;
2856            OP2_VAL_S(DEFINED_OP(op->op1)) = emalloc(3);
2857            OP2_VAL_S(DEFINED_OP(op->op1))[0] = ch1;
2858            OP2_VAL_S(DEFINED_OP(op->op1))[1] = ch2;
2859            OP2_VAL_S(DEFINED_OP(op->op1))[2] = '\0';
2860            OP2_VAL_SL(DEFINED_OP(op->op1)) = 2;
2861            memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(znode));
2862            DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING;
2863            DEFINE_OP(DEFINED_OP(op->op1));
2864            SET_TO_NOP(op);
2865            OPTIMIZER_STATS_ADD_PEEPHOLE();
2866        }
2867        /* optimize out addition of const chars to strings or variables */
2868        else if (
2869            op->opcode == ZEND_ADD_CHAR && OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) &&
2870            (DEFINED_OP(op->op1)->opcode == ZEND_CONCAT || DEFINED_OP(op->op1)->opcode == ZEND_ADD_STRING) &&
2871            OP2_TYPE(DEFINED_OP(op->op1)) == IS_CONST
2872        ) {
2873            size_t len;
2874
2875            convert_to_string(&__OP2_VAL(DEFINED_OP(op->op1)));
2876            len = OP2_VAL_SL(DEFINED_OP(op->op1)) + 1;
2877            STR_REALLOC(OP2_VAL_S(DEFINED_OP(op->op1)),len+1);
2878            OP2_VAL_S(DEFINED_OP(op->op1))[len-1] = (char) OP2_VAL_L(op);
2879            OP2_VAL_S(DEFINED_OP(op->op1))[len] = 0;
2880            OP2_VAL_SL(DEFINED_OP(op->op1)) = len;
2881            memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(znode));
2882            if (OP1_TYPE(DEFINED_OP(op->op1)) == RES_TYPE(DEFINED_OP(op->op1)) && OP1_VR(DEFINED_OP(op->op1)) == RES_VR(DEFINED_OP(op->op1))) {
2883                DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING;
2884            }
2885            DEFINE_OP(DEFINED_OP(op->op1));
2886            SET_TO_NOP(op);
2887            OPTIMIZER_STATS_ADD_PEEPHOLE();
2888        }
2889        /* optimize instances such as $a = 'foo'; $a .= 'bar'; */
2890        else if (
2891            (op->opcode == ZEND_ASSIGN || op->opcode == ZEND_ASSIGN_CONCAT) && OP2_TYPE(op) == IS_CONST &&
2892            NEXT_OP(op)->opcode == ZEND_ASSIGN_CONCAT && OP2_TYPE(NEXT_OP(op)) == IS_CONST && OP1_VR(op) == OP1_VR(NEXT_OP(op)) &&
2893            NEXT_OP(op) < end
2894        ) {
2895            zend_op *x = NEXT_OP(op);
2896            size_t len;
2897
2898            convert_to_string(&__OP2_VAL(op));
2899            convert_to_string(&__OP2_VAL(x));
2900
2901            len = OP2_VAL_SL(op);
2902
2903            STR_REALLOC(OP2_VAL_S(op), len + OP2_VAL_SL(x) + 1);
2904            memcpy(OP2_VAL_S(op) + len, OP2_VAL_S(x), OP2_VAL_SL(x) + 1);
2905            OP2_VAL_SL(op) += OP2_VAL_SL(x);
2906            STR_FREE(OP2_VAL_S(x));
2907            SET_TO_NOP(x);
2908            OPTIMIZER_STATS_ADD_PEEPHOLE();
2909        }
2910        /* optimie out concatination of single chars */
2911        else if (
2912            (op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT) && OP2_TYPE(op) == IS_CONST &&
2913            OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_ADD_CHAR
2914        ) {
2915            char ch = (char) OP2_VAL_L(DEFINED_OP(op->op1));
2916            size_t len;
2917
2918            convert_to_string(&__OP2_VAL(op));
2919            len = OP2_VAL_SL(op) + 1;
2920            OP2_VALT(DEFINED_OP(op->op1)) = IS_STRING;
2921            OP2_VAL_S(DEFINED_OP(op->op1)) = emalloc(len+1);
2922            OP2_VAL_S(DEFINED_OP(op->op1))[0] = ch;
2923            memcpy(OP2_VAL_S(DEFINED_OP(op->op1))+1, OP2_VAL_S(op), OP2_VAL_SL(op));
2924            OP2_VAL_S(DEFINED_OP(op->op1))[len] = 0;
2925            OP2_VAL_SL(DEFINED_OP(op->op1)) = len;
2926            STR_FREE(OP2_VAL_S(op));
2927            memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(znode));
2928            DEFINED_OP(op->op1)->opcode = op->opcode;
2929
2930            if (OP1_TYPE(DEFINED_OP(op->op1)) == RES_TYPE(DEFINED_OP(op->op1)) && OP1_VR(DEFINED_OP(op->op1)) == RES_VR(DEFINED_OP(op->op1))) {
2931                DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING;
2932            }
2933            DEFINE_OP(DEFINED_OP(op->op1));
2934            SET_TO_NOP(op);
2935            OPTIMIZER_STATS_ADD_PEEPHOLE();
2936        }
2937        /* static string addition & concatination optimization */
2938        else if (
2939            (op->opcode == ZEND_ADD_STRING || op->opcode == ZEND_CONCAT) && OP2_TYPE(op) == IS_CONST &&
2940            OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) &&
2941            (DEFINED_OP(op->op1)->opcode == ZEND_CONCAT || DEFINED_OP(op->op1)->opcode == ZEND_ADD_STRING) &&
2942            OP2_TYPE(DEFINED_OP(op->op1)) == IS_CONST
2943        ) {
2944            size_t len;
2945
2946            convert_to_string(&__OP2_VAL(DEFINED_OP(op->op1)));
2947            convert_to_string(&__OP2_VAL(op));
2948
2949            len = OP2_VAL_SL(DEFINED_OP(op->op1)) + OP2_VAL_SL(op);
2950            STR_REALLOC(OP2_VAL_S(DEFINED_OP(op->op1)),len + 1);
2951            memcpy(OP2_VAL_S(DEFINED_OP(op->op1)) + OP2_VAL_SL(DEFINED_OP(op->op1)), OP2_VAL_S(op), OP2_VAL_SL(op));
2952            OP2_VAL_S(DEFINED_OP(op->op1))[len] = 0;
2953            OP2_VAL_SL(DEFINED_OP(op->op1)) = len;
2954            STR_FREE(OP2_VAL_S(op));
2955            memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(znode));
2956
2957            if (op->opcode == ZEND_CONCAT) {
2958                DEFINED_OP(op->op1)->opcode = ZEND_CONCAT;
2959            }
2960
2961            if (OP1_TYPE(DEFINED_OP(op->op1)) == RES_TYPE(DEFINED_OP(op->op1)) && OP1_VR(DEFINED_OP(op->op1)) == RES_VR(DEFINED_OP(op->op1))) {
2962                DEFINED_OP(op->op1)->opcode = ZEND_ADD_STRING;
2963            }
2964            DEFINE_OP(DEFINED_OP(op->op1));
2965            SET_TO_NOP(op);
2966
2967            OPTIMIZER_STATS_ADD_PEEPHOLE();
2968        }
2969        /* optimie out GLOBALS["foo"] into global $foo */
2970        else if (
2971            (
2972                (op->opcode == ZEND_FETCH_DIM_R && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_R) ||
2973                (op->opcode == ZEND_FETCH_DIM_W && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_W) ||
2974                (op->opcode == ZEND_FETCH_DIM_RW && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_RW) ||
2975                (op->opcode == ZEND_FETCH_DIM_IS && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_IS) ||
2976                (op->opcode == ZEND_FETCH_DIM_FUNC_ARG && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_FUNC_ARG) ||
2977                (op->opcode == ZEND_FETCH_DIM_UNSET && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_UNSET)
2978            ) && FETCH_TYPE(DEFINED_OP(op->op1)) == ZEND_FETCH_GLOBAL &&
2979            OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_STRING &&
2980            !is_numeric_string(OP2_VAL_S(op), OP2_VAL_SL(op), NULL, NULL, 1) &&
2981            OP1_TYPE(DEFINED_OP(op->op1)) == IS_CONST && OP1_VALT(DEFINED_OP(op->op1)) == IS_STRING &&
2982            OP1_VAL_SL(DEFINED_OP(op->op1)) == (sizeof("GLOBALS")-1) &&
2983            !memcmp(OP1_VAL_S(DEFINED_OP(op->op1)), "GLOBALS", sizeof("GLOBALS")-1)
2984        ) {
2985            zend_op *x = DEFINED_OP(op->op1);
2986            UNDEFINE_OP(op->op1);
2987            STR_FREE(OP1_VAL_S(x));
2988            FETCH_TYPE(x) = ZEND_FETCH_GLOBAL;
2989            memcpy(&x->op1,&op->op2,sizeof(znode));
2990            memcpy(&x->result,&op->result,sizeof(znode));
2991            DEFINE_OP(x);
2992            SET_TO_NOP(op);
2993            OPTIMIZER_STATS_ADD_PEEPHOLE();
2994        }
2995        /* optimize out isset()/empty() ops on GLOBALS['foo'] into $foo */
2996        else if (
2997            op->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ && OP1_TYPE(op) == IS_VAR &&
2998            IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_FETCH_IS &&
2999            FETCH_TYPE(DEFINED_OP(op->op1)) == ZEND_FETCH_GLOBAL &&
3000            OP1_TYPE(DEFINED_OP(op->op1)) == IS_CONST && OP1_VALT(DEFINED_OP(op->op1)) == IS_STRING &&
3001            OP1_VAL_SL(DEFINED_OP(op->op1)) == (sizeof("GLOBALS")-1) &&
3002            !memcmp(OP1_VAL_S(DEFINED_OP(op->op1)), "GLOBALS", sizeof("GLOBALS")-1)
3003        ) {
3004            zend_op* x = DEFINED_OP(op->op1);
3005            STR_FREE(OP1_VAL_S(x));
3006            x->opcode = ZEND_ISSET_ISEMPTY_VAR;
3007            x->extended_value = op->extended_value;
3008            FETCH_TYPE(x) = ZEND_FETCH_GLOBAL;
3009            memcpy(&x->op1, &op->op2, sizeof(znode));
3010            memcpy(&x->result, &op->result, sizeof(znode));
3011            DEFINE_OP(x);
3012            SET_TO_NOP(op);
3013            OPTIMIZER_STATS_ADD_PEEPHOLE();
3014        }
3015        else if (op->opcode == ZEND_FREE && OP1_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1)) {
3016            /* convert post increment to pre increment */
3017            if (DEFINED_OP(op->op1)->opcode == ZEND_POST_INC && PREV_OP(DEFINED_OP(op->op1)) && PREV_OP(DEFINED_OP(op->op1))->opcode != ZEND_JMPZNZ) {
3018                DEFINED_OP(op->op1)->opcode = ZEND_PRE_INC;
3019                RES_TYPE(DEFINED_OP(op->op1)) = IS_VAR;
3020                RESULT_TYPE(DEFINED_OP(op->op1)) |= EXT_TYPE_UNUSED;
3021                UNDEFINE_OP(op->op1);
3022                SET_TO_NOP(op);
3023                OPTIMIZER_STATS_ADD_PEEPHOLE();
3024            }
3025            /* convert post decrement to pre decrement */
3026            else if (DEFINED_OP(op->op1)->opcode == ZEND_POST_DEC && PREV_OP(DEFINED_OP(op->op1)) && PREV_OP(DEFINED_OP(op->op1))->opcode != ZEND_JMPZNZ) {
3027                DEFINED_OP(op->op1)->opcode = ZEND_PRE_DEC;
3028                RES_TYPE(DEFINED_OP(op->op1)) = IS_VAR;
3029                RESULT_TYPE(DEFINED_OP(op->op1)) |= EXT_TYPE_UNUSED;
3030                UNDEFINE_OP(op->op1);
3031                SET_TO_NOP(op);
3032                OPTIMIZER_STATS_ADD_PEEPHOLE();
3033            }
3034            /* convert print where return value is unused to echo */
3035            else if (DEFINED_OP(op->op1)->opcode == ZEND_PRINT) {
3036                DEFINED_OP(op->op1)->opcode = ZEND_ECHO;
3037                RES_TYPE(DEFINED_OP(op->op1)) = IS_UNUSED;
3038                UNDEFINE_OP(op->op1);
3039                SET_TO_NOP(op);
3040                OPTIMIZER_STATS_ADD_PEEPHOLE();
3041            }
3042            /* convert left over bool into NOP */
3043            else if (DEFINED_OP(op->op1)->opcode == ZEND_BOOL) {
3044                SET_TO_NOP(DEFINED_OP(op->op1));
3045                UNDEFINE_OP(op->op1);
3046                SET_TO_NOP(op);
3047                OPTIMIZER_STATS_ADD_PEEPHOLE();
3048            }
3049        }
3050        /* convert conditional bool to just bool */
3051        else if (
3052            op->opcode == ZEND_BOOL && OP1_TYPE(op) == IS_TMP_VAR &&
3053            (!global[VAR_NUM(OP1_VR(op))] || (RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op))) &&
3054            IS_DEFINED(op->op1) && (
3055                DEFINED_OP(op->op1)->opcode == ZEND_IS_IDENTICAL ||
3056                DEFINED_OP(op->op1)->opcode == ZEND_IS_NOT_IDENTICAL ||
3057                DEFINED_OP(op->op1)->opcode == ZEND_IS_EQUAL ||
3058                DEFINED_OP(op->op1)->opcode == ZEND_IS_NOT_EQUAL ||
3059                DEFINED_OP(op->op1)->opcode == ZEND_IS_SMALLER ||
3060                DEFINED_OP(op->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL
3061            )
3062        ) {
3063            memcpy(&DEFINED_OP(op->op1)->result, &op->result, sizeof(znode));
3064            DEFINE_OP(DEFINED_OP(op->op1));
3065            SET_TO_NOP(op);
3066            OPTIMIZER_STATS_ADD_PEEPHOLE();
3067        }
3068        /* optimize boolean ops and booleans & basic jmps */
3069        else if (
3070            (
3071                op->opcode == ZEND_BOOL || op->opcode == ZEND_BOOL_NOT || op->opcode == ZEND_JMPZ ||
3072                op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPZNZ || op->opcode == ZEND_JMPZ_EX ||
3073                op->opcode == ZEND_JMPNZ_EX
3074            ) &&
3075            OP1_TYPE(op) == IS_TMP_VAR &&
3076            (!global[VAR_NUM(OP1_VR(op))] || (RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op))) &&
3077            IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_BOOL
3078        ) {
3079            zend_op *x = DEFINED_OP(op->op1);
3080            UNDEFINE_OP(op->op1);
3081            memcpy(&op->op1, &x->op1, sizeof(znode));
3082            SET_TO_NOP(x);
3083            OPTIMIZER_STATS_ADD_PEEPHOLE();
3084        }
3085        /* optimize boolean NOT ops and booleans & basic jmps */
3086        else if (
3087            (op->opcode == ZEND_BOOL || op->opcode == ZEND_BOOL_NOT || op->opcode == ZEND_JMPZ || op->opcode == ZEND_JMPNZ) &&
3088            OP1_TYPE(op) == IS_TMP_VAR &&
3089            (!global[VAR_NUM(OP1_VR(op))] || (RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op))) &&
3090            IS_DEFINED(op->op1) && DEFINED_OP(op->op1)->opcode == ZEND_BOOL_NOT
3091        ) {
3092            zend_op *x = DEFINED_OP(op->op1);
3093
3094            switch (op->opcode) {
3095                case ZEND_BOOL:
3096                    op->opcode = ZEND_BOOL_NOT;
3097                    break;
3098                case ZEND_BOOL_NOT:
3099                    op->opcode = ZEND_BOOL;
3100                    break;
3101                case ZEND_JMPZ:
3102                    op->opcode = ZEND_JMPNZ;
3103                    break;
3104                case ZEND_JMPNZ:
3105                    op->opcode = ZEND_JMPZ;
3106                    break;
3107            }
3108            UNDEFINE_OP(op->op1);
3109            memcpy(&op->op1, &x->op1, sizeof(znode));
3110            SET_TO_NOP(x);
3111            OPTIMIZER_STATS_ADD_PEEPHOLE();
3112        }
3113        /* optimize out pointless assignment */
3114        else if (op->opcode == ZEND_QM_ASSIGN && OP1_TYPE(op) == IS_TMP_VAR && RES_TYPE(op) == IS_TMP_VAR && OP1_VR(op) == RES_VR(op)) {
3115            SET_TO_NOP(op);
3116            OPTIMIZER_STATS_ADD_PEEPHOLE();
3117        }
3118        /* optimize array breakdown of known values */
3119        else if (
3120            op->opcode == ZEND_QM_ASSIGN && OP1_TYPE(op) == IS_TMP_VAR && !global[VAR_NUM(OP1_VR(op))] &&
3121            OP1_VR(op) != RES_VR(op) && IS_DEFINED(op->op1)
3122        ) {
3123            zend_op *x = DEFINED_OP(op->op1);
3124
3125            switch (x->opcode) {
3126                case ZEND_ADD_ARRAY_ELEMENT:
3127                case ZEND_ADD_STRING:
3128                case ZEND_ADD_CHAR:
3129                case ZEND_ADD_VAR:
3130                    break;
3131                default:
3132                    UNDEFINE_OP(op->op1);
3133                    memcpy(&x->result, &op->result, sizeof(znode));
3134                    DEFINE_OP(x);
3135                    SET_TO_NOP(op);
3136            }
3137            OPTIMIZER_STATS_ADD_PEEPHOLE();
3138        }
3139        /* combine multiple echo calls with const params into a single echo */
3140        else if (prev != NULL && op->opcode == ZEND_ECHO && OP1_TYPE(op) == IS_CONST && prev->opcode == ZEND_ECHO && OP1_TYPE(prev) == IS_CONST) {
3141            int len;
3142
3143            convert_to_string(&__OP1_VAL(op));
3144            //convert_to_string(&__OP1_VAL(op));
3145            convert_to_string(&__OP1_VAL(prev));
3146
3147            len = OP1_VAL_SL(prev) + OP1_VAL_SL(op);
3148            STR_REALLOC(OP1_VAL_S(prev), len + 1);
3149            memcpy(OP1_VAL_S(prev) + OP1_VAL_SL(prev), OP1_VAL_S(op), OP1_VAL_SL(op));
3150            OP1_VAL_S(prev)[len] = 0;
3151            OP1_VAL_SL(prev) = len;
3152            STR_FREE(OP1_VAL_S(op));
3153            SET_TO_NOP(op);
3154            OPTIMIZER_STATS_ADD_PEEPHOLE();
3155        }
3156        /* convert empty silence blocks to NOP */
3157        else if (prev != NULL && prev->opcode == ZEND_END_SILENCE && op->opcode == ZEND_BEGIN_SILENCE) {
3158            zend_op *x = op+1;
3159
3160            while (x < end) {
3161                if (x->opcode == ZEND_END_SILENCE && OP1_VR(x) == RES_VR(op)) {
3162                    OP1_VR(x) = OP1_VR(prev);
3163                    SET_TO_NOP(prev);
3164                    SET_TO_NOP(op);
3165                    break;
3166                }
3167                x++;
3168            }
3169            OPTIMIZER_STATS_ADD_PEEPHOLE();
3170        }
3171        /* convert empty silence blocks to NOP */
3172        else if (prev != NULL && prev->opcode == ZEND_BEGIN_SILENCE && op->opcode == ZEND_END_SILENCE && RES_VR(prev) == OP1_VR(op)) {
3173            SET_TO_NOP(prev);
3174            SET_TO_NOP(op);
3175            OPTIMIZER_STATS_ADD_PEEPHOLE();
3176        }
3177        /* optimize out pointless send by-ref calls to ops that take args by value */
3178        else if (op->opcode == ZEND_SEND_VAR_NO_REF && (op->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && !(op->extended_value & ZEND_ARG_SEND_BY_REF)) {
3179            op->opcode = ZEND_SEND_VAR;
3180            op->extended_value = ZEND_DO_FCALL;
3181            OPTIMIZER_STATS_ADD_PEEPHOLE();
3182        }
3183        /* convert INIT_FCALL_BY_NAME and DO_FCALL_BY_NAME to DO_FCALL */
3184        else if (
3185            prev != NULL && op->opcode == ZEND_DO_FCALL_BY_NAME && op->extended_value == 0 &&
3186            OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_STRING && prev->opcode == ZEND_INIT_FCALL_BY_NAME &&
3187            OP1_TYPE(prev) == IS_UNUSED && OP2_TYPE(prev) == IS_CONST && OP2_VALT(prev) == IS_STRING &&
3188            OP1_VAL_SL(op) == OP2_VAL_SL(prev) && !memcmp(OP1_VAL_S(op),OP2_VAL_S(prev),OP1_VAL_SL(op))
3189        ) {
3190            op->opcode = ZEND_DO_FCALL;
3191            STR_FREE(OP2_VAL_S(prev));
3192            SET_TO_NOP(prev);
3193            OPTIMIZER_STATS_ADD_PEEPHOLE();
3194        }
3195        /* optimize array init such as $a = array(); $a[] = 'foo'; */
3196        else if (
3197            op->opcode == ZEND_INIT_ARRAY && NEXT_OP(op)->opcode == ZEND_ASSIGN &&
3198            OP1_TYPE(op) == IS_UNUSED && OP2_TYPE(op) == IS_UNUSED && NEXT_OP(NEXT_OP(op))->opcode == ZEND_ASSIGN_DIM &&
3199            OP1_VR(NEXT_OP(NEXT_OP(op))) == OP1_VR(NEXT_OP(op)) && NEXT_OP(NEXT_OP(NEXT_OP(op)))->opcode == ZEND_OP_DATA &&
3200            (cb->start + cb->len) > NEXT_OP(NEXT_OP(op))
3201        ) {
3202            zend_op *x = NEXT_OP(op);
3203
3204            zend_op *key = NEXT_OP(NEXT_OP(op));
3205            zend_op *val = NEXT_OP(NEXT_OP(NEXT_OP(op)));
3206            zend_bool move = 0;
3207            if (!(OP1_TYPE(x) == IS_CV && OP1_TYPE(val) == IS_CV &&
3208                  OP1_VR(x) == OP1_VR(val))) {
3209
3210                memcpy(&op->op2, &key->op2, sizeof(znode));
3211                memcpy(&op->op1, &val->op1, sizeof(znode));
3212
3213                SET_TO_NOP(key);
3214                SET_TO_NOP(val);
3215
3216                /* let's see if we have more array elements */
3217                while (NEXT_OP(val)->opcode == ZEND_ASSIGN_DIM && OP1_VR(NEXT_OP(val)) == OP1_VR(x) && NEXT_OP(NEXT_OP(val))->opcode == ZEND_OP_DATA) {
3218                    SET_TO_NOP(val);
3219
3220                    key = NEXT_OP(val);
3221                    val = NEXT_OP(NEXT_OP(val));
3222
3223                    key->opcode = ZEND_ADD_ARRAY_ELEMENT;
3224                    memcpy(&key->op1, &val->op1, sizeof(znode));
3225                    memcpy(&key->result, &op->result, sizeof(znode));
3226                    move = 1;
3227                }
3228
3229                /* move ASSIGN below ADD_ARRAY_ELEMENT */
3230                if (move) {
3231                    zend_op *x = NEXT_OP(op);
3232                    memcpy(val, x, sizeof(zend_op));
3233                    SET_TO_NOP(x);
3234                }
3235
3236                OPTIMIZER_STATS_ADD_PEEPHOLE();
3237            }
3238        }
3239
3240        /* optimize operations where a math operation is done on the var itself, $a = $a [op] val; > $a [op]= val; */
3241        if (op->opcode == ZEND_ASSIGN && OP1_TYPE(op) == IS_VAR && OP2_TYPE(op) == IS_TMP_VAR && IS_DEFINED(op->op1) && IS_DEFINED(op->op2)) {
3242            zend_op* l = DEFINED_OP(op->op1);
3243            zend_op* r = DEFINED_OP(op->op2);
3244
3245            if (l->opcode == ZEND_FETCH_W && OP1_TYPE(l) == IS_CONST && OP1_VALT(l) == IS_STRING &&
3246                    (
3247                        r->opcode == ZEND_ADD || r->opcode == ZEND_SUB || r->opcode == ZEND_MUL ||
3248                        r->opcode == ZEND_DIV || r->opcode == ZEND_MOD || r->opcode == ZEND_SL ||
3249                        r->opcode == ZEND_SR || r->opcode == ZEND_CONCAT || r->opcode == ZEND_BW_OR ||
3250                        r->opcode == ZEND_BW_AND || r->opcode == ZEND_BW_XOR
3251                    ) && OP1_TYPE(r) == IS_VAR && IS_DEFINED(r->op1))
3252            {
3253                zend_op* rl = DEFINED_OP(r->op1);
3254
3255                if (rl->opcode == ZEND_FETCH_R && OP1_TYPE(rl) == IS_CONST && OP1_VALT(rl) == IS_STRING &&
3256                    FETCH_TYPE(rl) == FETCH_TYPE(l) && OP1_VAL_SL(l) == OP1_VAL_SL(rl) &&
3257                    !memcmp(OP1_VAL_S(l), OP1_VAL_S(rl), OP1_VAL_SL(l))
3258                ) {
3259                    switch (r->opcode) {
3260                        case ZEND_ADD:
3261                            op->opcode = ZEND_ASSIGN_ADD;
3262                            break;
3263                        case ZEND_SUB:
3264                            op->opcode = ZEND_ASSIGN_SUB;
3265                            break;
3266                        case ZEND_MUL:
3267                            op->opcode = ZEND_ASSIGN_MUL;
3268                            break;
3269                        case ZEND_DIV:
3270                            op->opcode = ZEND_ASSIGN_DIV;
3271                            break;
3272                        case ZEND_MOD:
3273                            op->opcode = ZEND_ASSIGN_MOD;
3274                            break;
3275                        case ZEND_SL:
3276                            op->opcode = ZEND_ASSIGN_SL;
3277                            break;
3278                        case ZEND_SR:
3279                            op->opcode = ZEND_ASSIGN_SR;
3280                            break;
3281                        case ZEND_CONCAT:
3282                            op->opcode = ZEND_ASSIGN_CONCAT;
3283                            break;
3284                        case ZEND_BW_OR:
3285                            op->opcode = ZEND_ASSIGN_BW_OR;
3286                            break;
3287                        case ZEND_BW_AND:
3288                            op->opcode = ZEND_ASSIGN_BW_AND;
3289                            break;
3290                        case ZEND_BW_XOR:
3291                            op->opcode = ZEND_ASSIGN_BW_XOR;
3292                            break;
3293                    }
3294                    memcpy(&op->op2, &r->op2, sizeof(znode));
3295                    l->opcode = ZEND_FETCH_RW;
3296                    SET_TO_NOP(r);
3297                    STR_FREE(OP1_VAL_S(rl));
3298                    SET_TO_NOP(rl);
3299                }
3300
3301                OPTIMIZER_STATS_ADD_PEEPHOLE();
3302            }
3303        }
3304
3305        /* one time optimizations that the 1st pass should always resolved, so we only try to run them once */
3306        if (pass == 1) {
3307            /* FETCH_W var,$x + ASSIGN $x,?,_  + FETCH_R var,$y => FETCH_W var,$x + ASSIGN $x,?,$y */
3308            if (
3309                op->opcode == ZEND_UNSET_VAR || op->opcode == ZEND_DO_FCALL || op->opcode == ZEND_DO_FCALL_BY_NAME ||
3310                op->opcode == ZEND_POST_INC || op->opcode == ZEND_POST_DEC || op->opcode == ZEND_UNSET_DIM ||
3311                op->opcode == ZEND_UNSET_OBJ || op->opcode == ZEND_INCLUDE_OR_EVAL || op->opcode == ZEND_ASSIGN_DIM ||
3312                op->opcode == ZEND_ASSIGN_OBJ
3313            ) {
3314                zend_hash_clean(&assigns);
3315                zend_hash_clean(&fetch_dim);
3316                OPTIMIZER_STATS_ADD_PEEPHOLE();
3317            } else if (
3318                op->opcode == ZEND_ASSIGN_REF || op->opcode == ZEND_ASSIGN || op->opcode == ZEND_PRE_INC ||
3319                op->opcode == ZEND_PRE_DEC || op->opcode == ZEND_ASSIGN_ADD || op->opcode == ZEND_ASSIGN_SUB ||
3320                op->opcode == ZEND_ASSIGN_MUL || op->opcode == ZEND_ASSIGN_DIV || op->opcode == ZEND_ASSIGN_MOD ||
3321                op->opcode == ZEND_ASSIGN_SL || op->opcode == ZEND_ASSIGN_SR || op->opcode == ZEND_ASSIGN_CONCAT ||
3322                op->opcode == ZEND_ASSIGN_BW_OR || op->opcode == ZEND_ASSIGN_BW_AND || op->opcode == ZEND_ASSIGN_BW_XOR
3323            ) {
3324                zend_hash_clean(&assigns);
3325                zend_hash_clean(&fetch_dim);
3326
3327                if (USED_RESULT(op) && OP1_TYPE(op) == IS_VAR && op->extended_value != ZEND_ASSIGN_DIM && op->extended_value != ZEND_ASSIGN_OBJ && IS_DEFINED(op->op1)) {
3328                    zend_op *x = DEFINED_OP(op->op1);
3329
3330                    if ((x->opcode == ZEND_FETCH_W || x->opcode == ZEND_FETCH_RW) && OP1_TYPE(x) == IS_CONST && OP1_VALT(x) == IS_STRING) {
3331                        union {
3332                            zend_op *v;
3333                            void *ptr;
3334                        } op_copy;
3335                        zend_op *y = DEFINED_OP(x->op2);
3336
3337                        if (y) {
3338                            uint nKeyLength = OP1_VAL_SL(x) + 2;
3339                            if (y->opcode == ZEND_FETCH_CLASS &&
3340                                OP2_TYPE(y) == IS_CONST &&
3341                                OP2_VALT(y) == IS_STRING) {
3342                                nKeyLength += OP2_VAL_SL(y);
3343                            }
3344                            char *s = emalloc(nKeyLength);
3345
3346                            op_copy.v = op;
3347                            memcpy(s, OP1_VAL_S(x), OP1_VAL_SL(x));
3348                            s[OP1_VAL_SL(x)] = (char)FETCH_TYPE(x);
3349                            if (y->opcode == ZEND_FETCH_CLASS) {
3350                                memcpy(&s[OP1_VAL_SL(x) + 1], OP2_VAL_S(y), OP2_VAL_SL(y));
3351                            }
3352                            s[nKeyLength-1] = '\0';
3353                            zend_hash_update(&assigns, s, nKeyLength, &op_copy.ptr, sizeof(void*), NULL);
3354                            efree(s);
3355                        }
3356                    }
3357
3358                    OPTIMIZER_STATS_ADD_PEEPHOLE();
3359                }
3360            } else if ((op->opcode == ZEND_FETCH_R || op->opcode == ZEND_FETCH_IS) && !global[VAR_NUM(RES_VR(op))] && OP1_TYPE(op) == IS_CONST && OP1_VALT(op) == IS_STRING) {
3361                union {
3362                    zend_op *v;
3363                    void *ptr;
3364                } x;
3365                zend_op *y = DEFINED_OP(op->op2);
3366                if (y) {
3367                    uint nKeyLength = OP1_VAL_SL(op) + 2;
3368                    if (y->opcode == ZEND_FETCH_CLASS &&
3369                        OP2_TYPE(y) == IS_CONST && OP2_VALT(y) == IS_STRING) {
3370                        nKeyLength += OP2_VAL_SL(y);
3371                    }
3372                    char *s = emalloc(nKeyLength);
3373                    memcpy(s, OP1_VAL_S(op), OP1_VAL_SL(op));
3374                    s[OP1_VAL_SL(op)] = (char)FETCH_TYPE(op);
3375                    if (y->opcode == ZEND_FETCH_CLASS) {
3376                        memcpy(&s[OP1_VAL_SL(op) + 1], OP2_VAL_S(y), OP2_VAL_SL(y));
3377                    }
3378                    s[nKeyLength-1] = '\0';
3379
3380                    if (zend_hash_find(&assigns, s, nKeyLength, &x.ptr) == SUCCESS) {
3381                        x.v = *(zend_op**)x.v;
3382
3383                        memcpy(&x.v->result, &op->result, sizeof(znode));
3384                        RESULT_TYPE(x.v) = 0;
3385                        DEFINE_OP(x.v);
3386                        zend_hash_del(&assigns, s, OP1_VAL_SL(op) + 2);
3387                        STR_FREE(OP1_VAL_S(op));
3388                        SET_TO_NOP(op);
3389                        OPTIMIZER_STATS_ADD_PEEPHOLE();
3390                    }
3391                    efree(s);
3392                }
3393            } else if (op->opcode == ZEND_FETCH_DIM_R && op->extended_value != ZEND_FETCH_ADD_LOCK && OP1_TYPE(op) == IS_VAR && IS_DEFINED(op->op1)) {
3394                zend_op *x = DEFINED_OP(op->op1);
3395
3396                while ((x->opcode == ZEND_ASSIGN_REF ||
3397                        x->opcode == ZEND_ASSIGN ||
3398                        x->opcode == ZEND_PRE_INC ||
3399                        x->opcode == ZEND_PRE_DEC ||
3400                        x->opcode == ZEND_ASSIGN_ADD ||
3401                        x->opcode == ZEND_ASSIGN_SUB ||
3402                        x->opcode == ZEND_ASSIGN_MUL ||
3403                        x->opcode == ZEND_ASSIGN_DIV ||
3404                        x->opcode == ZEND_ASSIGN_MOD ||
3405                        x->opcode == ZEND_ASSIGN_SL ||
3406                        x->opcode == ZEND_ASSIGN_SR ||
3407                        x->opcode == ZEND_ASSIGN_CONCAT ||
3408                        x->opcode == ZEND_ASSIGN_BW_OR ||
3409                        x->opcode == ZEND_ASSIGN_BW_AND ||
3410                        x->opcode == ZEND_ASSIGN_BW_XOR) &&
3411                        OP1_TYPE(x) == IS_VAR &&
3412                        IS_DEFINED(x->op1))
3413                {
3414                    x = DEFINED_OP(x->op1);
3415                }
3416                if ((x->opcode == ZEND_FETCH_R || x->opcode == ZEND_FETCH_W || x->opcode == ZEND_FETCH_RW) && OP1_TYPE(x) == IS_CONST && OP1_VALT(x) == IS_STRING) {
3417                    union {
3418                        zend_op *v;
3419                        void *ptr;
3420                    } y;
3421                    union {
3422                        zend_op *v;
3423                        void *ptr;
3424                    } op_copy;
3425
3426                    char *s = emalloc(OP1_VAL_SL(x) + 2);
3427
3428                    op_copy.v = op;
3429                    memcpy(s, OP1_VAL_S(x), OP1_VAL_SL(x));
3430                    s[OP1_VAL_SL(x)] = (char)FETCH_TYPE(x);
3431                    s[OP1_VAL_SL(x) + 1] = '\0';
3432
3433                    if (zend_hash_find(&fetch_dim, s, OP1_VAL_SL(x) + 2, &y.ptr) == SUCCESS) {
3434                        y.v = *(zend_op**)y.v;
3435                        y.v->extended_value = ZEND_FETCH_ADD_LOCK;
3436                        zend_hash_update(&fetch_dim, s, OP1_VAL_SL(x) + 2, &op_copy.ptr, sizeof(void*), NULL);
3437                        UNDEFINE_OP(x->result);
3438                        STR_FREE(OP1_VAL_S(x));
3439                        SET_TO_NOP(x);
3440                        memcpy(&op->op1, &y.v->op1, sizeof(znode));
3441                    } else {
3442                        zend_hash_update(&fetch_dim, s, OP1_VAL_SL(x) + 2, &op_copy.ptr, sizeof(void*), NULL);
3443                    }
3444                    efree(s);
3445                }
3446            }
3447        }
3448
3449        if (op->opcode != ZEND_NOP) {
3450            prev = op;
3451        }
3452
3453        if ((RES_TYPE(op) == IS_VAR && (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT || !USED_RESULT(op))) || (RES_TYPE(op) == IS_TMP_VAR)) {
3454            if (op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT) {
3455                UNDEFINE_OP(op->result);
3456            } else {
3457                DEFINE_OP(op);
3458            }
3459        }
3460        OPTIMIZE_RESTORE(op_array, op);
3461        ++op;
3462    }
3463
3464    /* Remove left over NOP opcodes */
3465    op = cb->start;
3466    end = op + cb->len;
3467    while (op < end) {
3468        if (op->opcode == ZEND_NOP) {
3469            zend_op *next = op + 1;
3470            while (next < end && next->opcode == ZEND_NOP) {
3471                next++;
3472            }
3473            if (next < end) {
3474                memmove(op, next,(end - next) * sizeof(zend_op));
3475                while (next > op) {
3476                    --end;
3477                    SET_TO_NOP(end);
3478                    --next;
3479                }
3480            } else {
3481                end -= (next-op);
3482            }
3483        } else {
3484            ++op;
3485        }
3486    }
3487    cb->len = end - cb->start;
3488
3489    zend_hash_destroy(&fetch_dim);
3490    zend_hash_destroy(&assigns);
3491    optimizer_free_alloca(Ts, Ts_use_heap);
3492}
3493
3494
3495
3496/*
3497 * This is a direct copy from old optimizer, APC makes a mess of oplines, let is restore them back to sane values
3498 */
3499static void apc_restore_opline_num(zend_op_array *op_array)
3500{
3501    zend_op *opline, *end;
3502    opline = op_array->opcodes;
3503    end = opline + op_array->last;
3504
3505    while (opline < end) {
3506        switch (opline->opcode) {
3507            case ZEND_JMP:
3508                opline->op1.u.opline_num = opline->op1.u.jmp_addr - op_array->opcodes;
3509                break;
3510            case ZEND_JMPZ:
3511            case ZEND_JMPNZ:
3512            case ZEND_JMPZ_EX:
3513            case ZEND_JMPNZ_EX:
3514                opline->op2.u.opline_num = opline->op2.u.jmp_addr - op_array->opcodes;
3515                break;
3516        }
3517        opline++;
3518    }
3519}
3520
3521/*
3522 * Find the total number of NOP oplines needed to disambiguate
3523 * nested try/catch blocks that share the same starting opline number.
3524 */
3525static int get_num_try_catch_nops(zend_op_array *op_array)
3526{
3527    int i;
3528    int j;
3529    int total = 0;
3530
3531    i = 0;
3532    do {
3533        for (j = i + 1; j < op_array->last_try_catch; j++) {
3534            if (op_array->try_catch_array[j].try_op !=
3535                op_array->try_catch_array[i].try_op) {
3536                break;
3537            }
3538        }
3539        total += j - i - 1;
3540        i = j;
3541    } while (i < op_array->last_try_catch);
3542    return total;
3543}
3544
3545/*
3546 * Adjust the target of various jump oplines after inserting NOPs.
3547 */
3548static void adjust_opline_num(zend_op_array *op_array, int insert_offset, int n_nops)
3549{
3550    zend_op *opline, *end;
3551    opline = op_array->opcodes;
3552    end = opline + op_array->last;
3553
3554    while (opline < end) {
3555        switch (opline->opcode) {
3556            case ZEND_JMP:
3557                if (opline->op1.u.opline_num > insert_offset) {
3558                    opline->op1.u.opline_num += n_nops;
3559                }
3560                break;
3561            case ZEND_JMPZ:
3562            case ZEND_JMPNZ:
3563            case ZEND_JMPZNZ:
3564            case ZEND_JMPZ_EX:
3565            case ZEND_JMPNZ_EX:
3566            case ZEND_FE_RESET:
3567            case ZEND_FE_FETCH:
3568            case ZEND_NEW:
3569                if (opline->op2.u.opline_num > insert_offset) {
3570                    opline->op2.u.opline_num += n_nops;
3571                }
3572                if (opline->opcode == ZEND_JMPZNZ) {
3573                    if (opline->extended_value > insert_offset) {
3574                        opline->extended_value += n_nops;
3575                    }
3576                }
3577                break;
3578            case ZEND_CATCH:
3579                if (opline->extended_value > insert_offset) {
3580                    opline->extended_value += n_nops;
3581                }
3582                break;
3583        }
3584        opline++;
3585    }
3586}
3587
3588/*
3589 * Insert NOP oplines to disambiguate nested try/catch blocks that share
3590 * the same starting opline number.
3591 */
3592static void insert_try_catch_nops(zend_op_array *op_array)
3593{
3594    zend_op* op;
3595    int i;
3596    int j;
3597    zend_try_catch_element* tc_element;
3598    zend_brk_cont_element* bc_element;
3599
3600    /* For directly nested try/catch blocks, they share the same starting
3601       opline #. Insert n NOP oplines for n nested try/catch levels and
3602       adjust the starting opline # for each try_op.
3603     */
3604
3605    /* First figure out how many artificial NOP oplines are needed */
3606    int total_nops = get_num_try_catch_nops(op_array);
3607
3608    if (total_nops == 0) {
3609        return;
3610    }
3611
3612    /* Allocate space for the extra NOP oplines */
3613    if (op_array->last + total_nops > op_array->size) {
3614        op_array->size = op_array->last + total_nops;
3615        op_array->opcodes = erealloc(op_array->opcodes,
3616                                     (op_array->size)*sizeof(zend_op));
3617    }
3618
3619    j = 0;
3620    do {
3621        int n_nops;
3622
3623        i = j;
3624        for (j = i + 1; j < op_array->last_try_catch; j++) {
3625            if (op_array->try_catch_array[j].try_op !=
3626                op_array->try_catch_array[i].try_op) {
3627                break;
3628            }
3629        }
3630        n_nops = j - i - 1;
3631
3632        if (n_nops == 0) {
3633            continue;
3634        }
3635
3636        /* Insert j - i - 1 NOP oplines before the opline at
3637           op_array->try_catch_array[i].try_op.
3638         */
3639        zend_uint insert_offset = op_array->try_catch_array[i].try_op;
3640        zend_op* start_op = &op_array->opcodes[insert_offset];
3641        zend_op* end = op_array->opcodes + op_array->last;
3642
3643        memmove(start_op + n_nops, start_op, sizeof(zend_op) * (end - start_op));
3644        for (op = start_op; op < start_op + n_nops; op++) {
3645            SET_TO_NOP(op);
3646        }
3647        op_array->last += n_nops;
3648
3649        /* Adjust try/catch opline. */
3650        for (j = 0, tc_element = op_array->try_catch_array;
3651             j < op_array->last_try_catch; j++, tc_element++) {
3652            if (i <= j && j < i + n_nops) {
3653                tc_element->try_op += j - i;
3654            } else if (j >= i + n_nops) {
3655                tc_element->try_op += n_nops;
3656            }
3657            tc_element->catch_op += n_nops;
3658        }
3659        for (j = 0, bc_element = op_array->brk_cont_array;
3660             j < op_array->last_brk_cont; j++, bc_element++) {
3661            if (i <= j && j < i + n_nops) {
3662                bc_element->brk += j - i;
3663            } else if (j >= i + n_nops) {
3664                bc_element->brk += n_nops;
3665            }
3666            bc_element->brk += n_nops;
3667        }
3668
3669        /* Adjust the opline number to observe the newly added NOPs. */
3670        adjust_opline_num(op_array, insert_offset, n_nops);
3671    } while (j < op_array->last_try_catch);
3672}
3673
3674/*
3675 * Find All Blocks in op_array and build Control Flow Graph (CFG)
3676 */
3677static zend_bool build_cfg(zend_op_array *op_array, CB* cb)
3678{
3679    if (op_array->last_try_catch > 0) {
3680        int i;
3681        zend_try_catch_element* tc_element = op_array->try_catch_array;
3682
3683        for (i = 0; i < op_array->last_try_catch; i++, tc_element++) {
3684            cb[tc_element->try_op].start = &op_array->opcodes[tc_element->try_op];
3685            cb[tc_element->try_op].exn = 1;
3686
3687            cb[tc_element->catch_op].start = &op_array->opcodes[tc_element->catch_op];
3688            cb[tc_element->catch_op].exn = 1;
3689
3690            cb[tc_element->try_op].jmp_tc = &cb[tc_element->catch_op];
3691        }
3692    }
3693
3694    zend_op* op = op_array->opcodes;
3695    int len = op_array->last;
3696    int line_num;
3697    CB* p;
3698    zend_bool remove_brk_cont_array = 1;
3699    zend_uint deep_catch;
3700    int first_switch_free = -1;
3701
3702    /* Find the start of all the blocks */
3703    cb[0].start = op;
3704    for (line_num = 0; line_num < len; op++,line_num++) {
3705        const unsigned int mask = op->opcode < MAX_ZOPS ? zops[op->opcode] : -1;
3706        if (mask != -1) {
3707            if ((mask & OP1_MASK) == OP1_UCLASS) {
3708                if (OP1_TYPE(op) != IS_UNUSED) {
3709                    OP1_TYPE(op) = IS_VAR;
3710                }
3711            } else if ((mask & OP1_MASK) == OP1_CLASS) {
3712                OP1_TYPE(op) = IS_VAR;
3713            } else if ((mask & OP1_MASK) == OP1_UNUSED) {
3714                OP1_TYPE(op) = IS_UNUSED;
3715            }
3716
3717            if ((mask & OP2_MASK) == OP2_CLASS) {
3718                OP2_TYPE(op) = IS_VAR;
3719            } else if ((mask & OP2_MASK) == OP2_UNUSED) {
3720                OP2_TYPE(op) = IS_UNUSED;
3721            } else if ((mask & OP2_MASK) == OP2_FETCH && FETCH_TYPE(op) == ZEND_FETCH_STATIC_MEMBER) {
3722                OP2_TYPE(op) = IS_VAR;
3723            }
3724
3725            if ((mask & RES_MASK) == RES_CLASS) {
3726                RES_TYPE(op) = IS_VAR;
3727                RESULT_TYPE(op) &= ~EXT_TYPE_UNUSED;
3728            } else if ((mask & RES_MASK) == RES_UNUSED) {
3729                RES_TYPE(op) = IS_UNUSED;
3730            }
3731        }
3732
3733        switch (op->opcode) {
3734            case ZEND_RETURN:
3735            case ZEND_EXIT:
3736                cb[line_num+1].start = op+1;
3737                break;
3738            case ZEND_JMP:
3739                cb[op->op1.u.opline_num].start = &op_array->opcodes[op->op1.u.opline_num];
3740                cb[line_num+1].start = op+1;
3741                break;
3742            case ZEND_JMPZNZ:
3743                cb[op->extended_value].start = &op_array->opcodes[op->extended_value];
3744                cb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num];
3745                cb[line_num+1].start = op+1;
3746                break;
3747            case ZEND_JMPZ:
3748            case ZEND_JMPNZ:
3749            case ZEND_JMPZ_EX:
3750            case ZEND_JMPNZ_EX:
3751            case ZEND_NEW:
3752            case ZEND_FE_RESET:
3753            case ZEND_FE_FETCH:
3754                cb[line_num+1].start = op+1;
3755                cb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num];
3756                break;
3757            case ZEND_BRK:
3758                /* Replace BRK by JMP */
3759                if (op->op1.u.opline_num == -1) {
3760                    /* nothing to do here */
3761                } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_LONG) {
3762                    int level = OP2_VAL_L(op);
3763                    int offset = op->op1.u.opline_num;
3764                    zend_brk_cont_element *jmp_to;
3765
3766                    do {
3767                        if (offset == -1) {
3768                            /* invalid brk */
3769                            goto brk_failed;
3770                        }
3771                        jmp_to = &op_array->brk_cont_array[offset];
3772                        offset = jmp_to->parent;
3773                    } while (--level > 0);
3774
3775                    op->opcode = ZEND_JMP;
3776                    op->op1.u.opline_num = jmp_to->brk;
3777                    OP2_TYPE(op) = IS_UNUSED;
3778                    op->extended_value = ZEND_BRK; /* Mark the opcode as former ZEND_BRK */
3779                    cb[op->op1.u.opline_num].start = &op_array->opcodes[jmp_to->brk];
3780                } else {
3781brk_failed:
3782                    remove_brk_cont_array = 0;
3783                }
3784                cb[line_num+1].start = op+1;
3785                break;
3786            case ZEND_CONT:
3787                /* Replace CONT by JMP */
3788                if (op->op1.u.opline_num == -1) {
3789                    /* nothing to do here */
3790                } else if (OP2_TYPE(op) == IS_CONST && OP2_VALT(op) == IS_LONG) {
3791                    int level = OP2_VAL_L(op);
3792                    int offset = op->op1.u.opline_num;
3793                    zend_brk_cont_element *jmp_to;
3794
3795                    do {
3796                        if (offset == -1) {
3797                            /* invalid cont */
3798                            goto cont_failed;
3799                        }
3800                        jmp_to = &op_array->brk_cont_array[offset];
3801                        offset = jmp_to->parent;
3802                    } while (--level > 0);
3803
3804                    op->opcode = ZEND_JMP;
3805                    op->op1.u.opline_num = jmp_to->cont;
3806                    OP2_TYPE(op) = IS_UNUSED;
3807                    op->extended_value = ZEND_CONT; /* Mark the opcode as former ZEND_CONT */
3808                    cb[op->op1.u.opline_num].start = &op_array->opcodes[jmp_to->cont];
3809                } else {
3810cont_failed:
3811                    remove_brk_cont_array = 0;
3812                }
3813                cb[line_num+1].start = op+1;
3814                break;
3815            case ZEND_CATCH:
3816                cb[op->extended_value].start = &op_array->opcodes[op->extended_value];
3817                cb[line_num+1].start = op+1;
3818                break;
3819            case ZEND_THROW:
3820                if (op->op2.u.opline_num != -1) {
3821                    cb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num];
3822                }
3823                cb[line_num+1].start = op+1;
3824                break;
3825            case ZEND_DO_FCALL:
3826            case ZEND_DO_FCALL_BY_NAME:
3827                if (op->op2.u.opline_num != -1) {
3828                    cb[op->op2.u.opline_num].start = &op_array->opcodes[op->op2.u.opline_num];
3829                    cb[line_num+1].start = op+1;
3830                }
3831                break;
3832            case ZEND_UNSET_VAR:
3833            case ZEND_UNSET_DIM:
3834            case ZEND_UNSET_OBJ:
3835                RES_TYPE(op) = IS_UNUSED;
3836                break;
3837            case ZEND_SWITCH_FREE:
3838                if (first_switch_free == -1) {
3839                    first_switch_free = line_num;
3840                }
3841                break;
3842            default:
3843                break;
3844        }
3845    }
3846
3847    /* Find lengths of the found blocks and build CFG */
3848    p = cb;
3849    for (line_num = 1; line_num < len; line_num++) {
3850        /* Calculate innermost CATCH op */
3851        deep_catch = 0;
3852        if (op_array->last_try_catch > 0) {
3853            int i;
3854            zend_try_catch_element* tc_element = op_array->try_catch_array;
3855            for (i = 0; i<op_array->last_try_catch; i++, tc_element++) {
3856                /* silence compile warnings, line_num can't be negative here so casting is safe. */
3857                if (tc_element->try_op <= (zend_uint)line_num-1 && (zend_uint)line_num-1 < tc_element->catch_op && (deep_catch == 0 || deep_catch > tc_element->catch_op)) {
3858                    deep_catch = tc_element->catch_op;
3859                }
3860            }
3861        }
3862        if (cb[line_num].start != NULL) {
3863            p->len = cb[line_num].start - p->start;
3864            p->next = &cb[line_num];
3865            op = &p->start[p->len-1];
3866            switch (op->opcode) {
3867                case ZEND_JMP:
3868                    p->jmp_1 = &cb[op->op1.u.opline_num];
3869                    if (op->extended_value == ZEND_BRK || op->extended_value == ZEND_CONT) {
3870                        /* Converted ZEND_BRK or ZEND_CONT opcode */
3871                        p->follow = (deep_catch > 0) ? &cb[deep_catch] : &cb[len-1];
3872                        op->extended_value = 0;
3873                    }
3874                    break;
3875                case ZEND_JMPZNZ:
3876                    p->jmp_2 = &cb[op->op2.u.opline_num];
3877                    p->jmp_ext = &cb[op->extended_value];
3878                    break;
3879                case ZEND_JMPZ:
3880                case ZEND_JMPNZ:
3881                case ZEND_JMPZ_EX:
3882                case ZEND_JMPNZ_EX:
3883                case ZEND_NEW:
3884                case ZEND_FE_RESET:
3885                case ZEND_FE_FETCH:
3886                    p->jmp_2 = &cb[op->op2.u.opline_num];
3887                    p->follow = &cb[line_num];
3888                    break;
3889                case ZEND_BRK:
3890                case ZEND_RETURN:
3891                case ZEND_EXIT:
3892                case ZEND_CONT:
3893                    p->follow = (deep_catch > 0) ? &cb[deep_catch] : &cb[len-1];
3894                    break;
3895                case ZEND_DO_FCALL:
3896                case ZEND_DO_FCALL_BY_NAME:
3897                    if (op->op2.u.opline_num != -1) {
3898                        p->jmp_2 = &cb[op->op2.u.opline_num];
3899                    }
3900                    p->follow = &cb[line_num];
3901                    break;
3902                case ZEND_CATCH:
3903                    p->jmp_ext = &cb[op->extended_value];
3904                    p->follow = &cb[line_num];
3905                    break;
3906                case ZEND_THROW:
3907                    if (op->op2.u.opline_num != -1) {
3908                        p->jmp_2 = &cb[op->op2.u.opline_num];
3909                    }
3910                    p->follow = &cb[line_num];
3911                    break;
3912                default:
3913                    p->follow = &cb[line_num];
3914            }
3915            p = &cb[line_num];
3916        }
3917    }
3918    p->len = (op_array->opcodes + op_array->last) - p->start;
3919
3920    /* Remove Unused brk_cont_array (BRK and CONT instructions replaced by JMP)*/
3921/*
3922    if (remove_brk_cont_array) {
3923        if (first_switch_free >= 0) {
3924            op = op_array->opcodes+first_switch_free;
3925            for (line_num = first_switch_free; line_num < len; line_num++, op++) {
3926                if (op->opcode == ZEND_SWITCH_FREE) {
3927                    SET_TO_NOP_EX(op);
3928                }
3929            }
3930        }
3931
3932        if (op_array->brk_cont_array != NULL) {
3933            efree(op_array->brk_cont_array);
3934            op_array->brk_cont_array = NULL;
3935        }
3936        op_array->last_brk_cont = 0;
3937    }
3938*/
3939    return remove_brk_cont_array;
3940}
3941
3942/*
3943 * Emits Optimized Code
3944 */
3945static void generate_cfg(zend_op_array *op_array, CB* cb)
3946{
3947    /* Compacting Optimized Code */
3948    CB* p = cb;
3949    zend_op* start = op_array->opcodes;
3950    zend_op* op = start;
3951    zend_op* end = op + op_array->last;
3952    zend_brk_cont_element *bc_element;
3953    int i;
3954
3955    while (p != NULL) {
3956        if (p->used) {
3957            if (p->len > 0 && op != p->start) {
3958                memmove(op, p->start, p->len * sizeof(zend_op));
3959                for (i = 0, bc_element = op_array->brk_cont_array;
3960                     i < op_array->last_brk_cont; i++, bc_element++) {
3961                    if (start + bc_element->start >= p->start &&
3962                        start + bc_element->start < p->start + p->len) {
3963                        bc_element->start -= (p->start - op);
3964                    }
3965                    if (start + bc_element->brk >= p->start &&
3966                        start + bc_element->brk < p->start + p->len) {
3967                        bc_element->brk -= (p->start - op);
3968                    }
3969                    if (start + bc_element->cont >= p->start &&
3970                        start + bc_element->cont < p->start + p->len) {
3971                        bc_element->cont -= (p->start - op);
3972                    }
3973                    if (bc_element->parent != -1 &&
3974                        start + bc_element->parent >= p->start &&
3975                        start + bc_element->parent < p->start + p->len) {
3976                        bc_element->parent -= (p->start - op);
3977                    }
3978                }
3979            }
3980            p->start = op;
3981            op += p->len;
3982        }
3983        p = p->next;
3984    }
3985
3986    op_array->last = op - start;
3987    op_array->start_op = NULL;
3988
3989    while (op < end) {
3990        SET_TO_NOP(op);
3991        op++;
3992    }
3993
3994
3995//vld_dump_oparray(op_array);
3996    /* Set Branch Targets */
3997    p = cb;
3998    while (p != NULL) {
3999        if (p->used && p->len > 0) {
4000            if (p->jmp_1 != NULL) {
4001                p->start[p->len - 1].op1.u.opline_num = p->jmp_1->start - start;
4002            }
4003            if (p->jmp_2 != NULL) {
4004                p->start[p->len - 1].op2.u.opline_num = p->jmp_2->start - start;
4005            }
4006            if (p->jmp_ext != NULL) {
4007                p->start[p->len - 1].extended_value = p->jmp_ext->start - start;
4008            }
4009        }
4010        p = p->next;
4011//vld_dump_oparray(op_array);
4012    }
4013
4014
4015    if (op_array->last_try_catch > 0) {
4016        int i;
4017        int last_try_catch = op_array->last_try_catch;
4018        zend_try_catch_element *old_tc_element = op_array->try_catch_array;
4019
4020        for (i = 0; i < op_array->last_try_catch; i++, old_tc_element++) {
4021            while (!cb[old_tc_element->try_op].used && cb[old_tc_element->try_op].follow) {
4022                old_tc_element->try_op += cb[old_tc_element->try_op].start - cb[old_tc_element->try_op].follow->start;
4023            }
4024            while (!cb[old_tc_element->catch_op].used && cb[old_tc_element->catch_op].follow) {
4025                old_tc_element->catch_op += cb[old_tc_element->catch_op].start - cb[old_tc_element->catch_op].follow->start;
4026            }
4027
4028            if (cb[old_tc_element->try_op].used && cb[old_tc_element->catch_op].used) {
4029                old_tc_element->try_op = cb[old_tc_element->try_op].start - start;
4030                old_tc_element->catch_op = cb[old_tc_element->catch_op].start - start;
4031            } else {
4032                old_tc_element->try_op = 0;
4033                old_tc_element->catch_op = 0;
4034                last_try_catch--;
4035            }
4036        }
4037
4038        if (op_array->last_try_catch > last_try_catch) {
4039            zend_try_catch_element* new_tc_array = NULL;
4040            if (last_try_catch > 0) {
4041                /* Lost some try & catch blocks */
4042                zend_try_catch_element* new_tc_element = safe_emalloc(sizeof(zend_try_catch_element), last_try_catch, 0);
4043                new_tc_array = new_tc_element;
4044                old_tc_element = op_array->try_catch_array;
4045                for (i = 0; i < op_array->last_try_catch; i++, old_tc_element++) {
4046                    if (old_tc_element->try_op != old_tc_element->catch_op) {
4047                        new_tc_element->try_op = old_tc_element->try_op;
4048                        new_tc_element->catch_op = old_tc_element->catch_op;
4049                        new_tc_element++;
4050                    }
4051                }
4052            }
4053
4054            /* Otherwise lost all try & catch blocks */
4055            efree(op_array->try_catch_array);
4056            op_array->try_catch_array = new_tc_array;
4057            op_array->last_try_catch = last_try_catch;
4058        }
4059    }
4060}
4061
4062#define GET_REG(R) {                                                       \
4063    if (assigned[(R)] < 0) {                                               \
4064        zend_uint j = 0;                                                   \
4065        while (j < op_array->T) {                                          \
4066            if (reg_pool[j] == 0 && (global[(R)] == 0 || used[j] == 0)) {  \
4067                reg_pool[j] = 1;                                           \
4068                assigned[(R)] = j;                                         \
4069                if (j + 1 > n) {                                           \
4070                    n = j + 1;                                             \
4071                }                                                          \
4072                break;                                                     \
4073            }                                                              \
4074            j++;                                                           \
4075        }                                                                  \
4076    }                                                                      \
4077    used[assigned[(R)]] = 1;                                               \
4078}                                                                          \
4079
4080
4081
4082static void rebuild_var_list(zend_op_array *op_array, CB* p, char *global)
4083{
4084    zend_uint i, n = 0;
4085
4086    OPTIMIZER_ALLOCA_FLAG(assigned_use_heap)
4087    OPTIMIZER_ALLOCA_FLAG(reg_pool_use_heap)
4088    OPTIMIZER_ALLOCA_FLAG(used_use_heap)
4089
4090    int *assigned  = optimizer_do_alloca(op_array->T * sizeof(int), assigned_use_heap);
4091    char *reg_pool = optimizer_do_alloca(op_array->T * sizeof(char), reg_pool_use_heap);
4092    char *used     = optimizer_do_alloca(op_array->T * sizeof(char), used_use_heap);
4093
4094    memset(assigned, -1, op_array->T * sizeof(int));
4095    memset(reg_pool, 0, op_array->T * sizeof(char));
4096    memset(used, 0, op_array->T * sizeof(char));
4097
4098    while (p != NULL) {
4099        if (p->used && p->len > 0) {
4100            zend_op* start = p->start;
4101            zend_op* op = start + p->len;
4102            zend_op* op_data;
4103
4104            for (i = 0; i < op_array->T; i++) {
4105                if (!global[i]) {
4106                    if (assigned[i] >= 0) {
4107                        reg_pool[assigned[i]] = 0;
4108                    }
4109                    assigned[i] = -1;
4110                }
4111            }
4112
4113            while (start < op) {
4114                --op;
4115                op_data = NULL;
4116
4117                if (op->opcode == ZEND_DO_FCALL_BY_NAME && OP1_TYPE(op) == IS_CONST) {
4118                    zval_dtor(&__OP1_VAL(op));
4119                    OP1_TYPE(op) = IS_UNUSED;
4120                }
4121                if (OP1_TYPE(op) == IS_VAR || OP1_TYPE(op) == IS_TMP_VAR) {
4122                    int r = VAR_NUM(OP1_VR(op));
4123                    GET_REG(r);
4124
4125                    if (op->opcode == ZEND_DO_FCALL_BY_NAME) {
4126                        OP1_TYPE(op) = IS_UNUSED;
4127                    } else if (op->opcode == ZEND_FETCH_CONSTANT && OP1_TYPE(op) == IS_VAR) {
4128                        OP1_VR(op) = VAR_VAL(assigned[r]);
4129                        /* restore op1 type from VAR to CONST (the opcode handler expects this or bombs out with invalid opcode) */
4130                        OP1_TYPE(op) = IS_CONST;
4131                    } else {
4132                        OP1_VR(op) = VAR_VAL(assigned[r]);
4133                    }
4134                }
4135                if (OP2_TYPE(op) == IS_VAR || OP2_TYPE(op) == IS_TMP_VAR) {
4136                    int r = VAR_NUM(OP2_VR(op));
4137                    GET_REG(r);
4138                    OP2_VR(op) = VAR_VAL(assigned[r]);
4139                }
4140                if (op->opcode == ZEND_DECLARE_INHERITED_CLASS) {
4141                    int r = VAR_NUM(op->extended_value);
4142                    GET_REG(r);
4143                    op->extended_value = VAR_VAL(assigned[r]);
4144                }
4145                if (RES_TYPE(op) == IS_VAR || RES_TYPE(op) == IS_TMP_VAR) {
4146                    int r = VAR_NUM(RES_VR(op));
4147
4148                    GET_REG(r);
4149                    RES_VR(op) = VAR_VAL(assigned[r]);
4150
4151                    if (
4152                        op->opcode != ZEND_RECV && op->opcode != ZEND_RECV_INIT &&
4153                        (
4154                            (RES_TYPE(op) == IS_VAR && USED_RESULT(op)) ||
4155                            (
4156                                !(OP1_TYPE(op) == RES_TYPE(op) && OP1_VR(op) == RES_VR(op)) &&
4157                                !(OP2_TYPE(op) == RES_TYPE(op) && OP2_VR(op) == RES_VR(op)) &&
4158                                !global[r] && op->opcode != ZEND_ADD_ARRAY_ELEMENT
4159                            )
4160                        )
4161                    ) {
4162                        reg_pool[VAR_NUM(RES_VR(op))] = 0;
4163                    }
4164                }
4165            }
4166        }
4167        p = p->next;
4168    }
4169    op_array->T = n;
4170
4171    optimizer_free_alloca(used, used_use_heap);
4172    optimizer_free_alloca(reg_pool, reg_pool_use_heap);
4173    optimizer_free_alloca(assigned, assigned_use_heap);
4174}
4175
4176static int fixup_inherited_method_helper(zend_class_entry **pce, zend_op_array *parent_method, char *lowercase_method_name TSRMLS_DC)
4177{
4178    zend_class_entry *ce = *pce;
4179    zend_class_entry *parent_class = parent_method->scope;
4180    zend_class_entry *tmp_ce;
4181    zend_function *inherited_method;
4182
4183    /* Stop when we see parent_class. */
4184    if (ce == parent_class) {
4185        return ZEND_HASH_APPLY_STOP;
4186    }
4187
4188    /* Skip non-user class. */
4189    if (ce->type != ZEND_USER_CLASS) {
4190        return ZEND_HASH_APPLY_KEEP;
4191    }
4192
4193    /* Check whether ce is a subclass of parent_class */
4194    for (tmp_ce = ce->parent; tmp_ce; tmp_ce = tmp_ce->parent) {
4195        if (tmp_ce == parent_class) {
4196            break;
4197        }
4198    }
4199
4200    if (!tmp_ce) {
4201        /* Not a subclass, skip it. */
4202        return ZEND_HASH_APPLY_KEEP;
4203    }
4204
4205    /* ce is a subclass of parent_class, check whether the parent_method is
4206       inherited by ce.
4207     */
4208    if (zend_hash_find(&ce->function_table, lowercase_method_name,
4209                        strlen(lowercase_method_name) + 1,
4210                        (void **)&inherited_method) == FAILURE) {
4211        /* Not exist, this should not occur! */
4212        OPTIMIZE_ASSERT(0);
4213        return ZEND_HASH_APPLY_KEEP;
4214    }
4215
4216    /* Skip this class if the inherited method gets overridden. */
4217    if (inherited_method->op_array.opcodes != parent_method->opcodes) {
4218        return ZEND_HASH_APPLY_KEEP;
4219    }
4220
4221    /* Now do the fixup. We only need to save the static_variables,
4222       prototype and fn_flags.
4223       See static_variables allocated by function_add_ref (invoked from
4224       do_inherit_method) and do_inherit_method_check.
4225     */
4226    zend_op_array *child_method = &inherited_method->op_array;
4227    HashTable *saved_static_variables = child_method->static_variables;
4228    zend_function *saved_prototype = child_method->prototype;
4229    zend_uint saved_fn_flags = child_method->fn_flags;
4230    *child_method = *parent_method;
4231    child_method->static_variables = saved_static_variables;
4232    child_method->prototype = child_method->prototype;
4233    child_method->fn_flags = saved_fn_flags;
4234    return ZEND_HASH_APPLY_KEEP;
4235}
4236
4237typedef int (*apply_func_2_args_t)(void *pDest, void *arg1, void *arg2 TSRMLS_DC);
4238
4239/*
4240 * Fixup all the copies of this method that have been inherited by
4241 * subclasses at compile time. All these copies share the same opcodes
4242 * pointer as this method. However after optimization attributes of this
4243 * method such as op_array->last may have changed. This needs to be fixed
4244 * up because op_array->last is used to locate the exception handling
4245 * instruction ZEND_HANDLE_EXCEPTION at run time. If an inherited method
4246 * has op_array->last out of sync with the new value, on an exception the
4247 * control will go beyond the end of the opcodes array and try to fetch
4248 * an invalid instruction.
4249 */
4250static void fixup_inherited_method(zend_op_array *op_array, apply_func_2_args_t apply_func TSRMLS_DC)
4251{
4252    HashTable *ht = EG(class_table);
4253    char *lowercase_method_name = zend_str_tolower_dup(op_array->function_name, strlen(op_array->function_name));
4254
4255    /* Scan the class table backwards for subclasses until we see this
4256       op_array's class. Classes appearing earlier cannot have done an early
4257       inheritance of the current class at compile time.
4258     */
4259
4260    /* Adapted from zend_hash_reverse_apply */
4261    Bucket *p, *q;
4262
4263    p = ht->pListTail;
4264    while (p != NULL) {
4265        int result = apply_func(p->pData, op_array, lowercase_method_name TSRMLS_CC);
4266
4267        q = p;
4268        p = p->pListLast;
4269        if (result & ZEND_HASH_APPLY_STOP) {
4270            break;
4271        }
4272    }
4273    OPTIMIZE_ASSERT(p != NULL);
4274    efree(lowercase_method_name);
4275}
4276
4277#define OPTIMIZER_STATS_CALC(stats_struct, op_array)   \
4278    stats_struct.op_row_count = op_array->last;        \
4279    stats_struct.var_count    = op_array->last_var;    \
4280    stats_struct.temp_count   = op_array->T;           \
4281
4282
4283
4284/*
4285 * Main Optimization Routine
4286 */
4287zend_op_array *optimize_op_array(zend_op_array *op_array, optimizer_source_file* sf TSRMLS_DC)
4288{
4289    CB *p, *cb;
4290    int i;
4291    int *jmp_lines;
4292    OPTIMIZER_ALLOCA_FLAG(jmp_lines_use_heap)
4293    OPTIMIZER_ALLOCA_FLAG(cb_use_heap)
4294    zval **tmp;
4295
4296#if OPTIMIZER_TEST_COMPILE
4297    if (OPTIMIZER_G(optimizer_test)) {
4298        php_printf("PRE OPTIMIZATION\n");
4299        optimizer_test_dump_op_array(op_array TSRMLS_CC);
4300    }
4301#endif
4302    if (optimize_is_disabled(op_array TSRMLS_CC)) {
4303        return op_array;
4304    }
4305    optimizer_parse_level(TSRMLS_C);
4306
4307#if OPTIMIZER_STATS_COMPILE
4308    optimizer_stats _op_array_stats = {0};
4309    optimizer_stats* op_array_stats = &_op_array_stats;
4310    op_array_stats->optimization_count = 0;
4311    OPTIMIZER_STATS_CALC(op_array_stats->pre, op_array);
4312#endif
4313
4314
4315    if (op_array->type != ZEND_USER_FUNCTION) { /* eval() is scary */
4316        return op_array;
4317    }
4318
4319    if ((jmp_lines = do_alloca(sizeof(int) * op_array->last, jmp_lines_use_heap)) == NULL) {
4320        return NULL;
4321    }
4322
4323
4324    /* APC specific cleanup */
4325    apc_restore_opline_num(op_array);
4326
4327    /* When building the CFG, the catch clause may not be reachable and the
4328       dead code elimination would remove the code for the catch clause
4329       entirely. To preserve the catch clause, we set up a "special edge"
4330       from the head block of the try clause to the head block of the catch
4331       clause. However, this won't work for immediately nested try clauses
4332       because they would shared the same head block and there can be only
4333       one "special edge" out of each basic block.
4334        try {
4335            try {
4336                ...
4337            } catch (...) {
4338                ...
4339            }
4340        } catch (...) {
4341            ...
4342        }
4343       We need different head blocks, one for each nested try clause. Insert
4344       NOPs as needed for immediately nested try clauses so that each NOP will
4345       start at a different opline. Later these NOPs will be optimized away.
4346     */
4347    if (op_array->last_try_catch > 0) {
4348        insert_try_catch_nops(op_array);
4349    }
4350
4351    /* Allocate memory for CFG */
4352    if ((cb = do_alloca(sizeof(CB)*(op_array->last+1), cb_use_heap)) == NULL) {
4353        free_alloca(jmp_lines, jmp_lines_use_heap);
4354        return NULL;
4355    }
4356    memset(cb, 0, sizeof(CB) * (op_array->last + 1));
4357    OPTIMIZER_G(op_array) = op_array;
4358
4359#if OPTIMIZER_DEBUG_COMPILE
4360    //vld_dump_oparray(op_array);
4361    //optimizer_dump_brk_cont_table(op_array);
4362    optimizer_dump_op_array(op_array);
4363#endif
4364
4365    /* Find all blocks and build CFG */
4366    if (build_cfg(op_array, cb)) {
4367        OPTIMIZER_ALLOCA_FLAG(global_use_heap)
4368        char *global = do_alloca(op_array->T * sizeof(char), global_use_heap);
4369
4370        //optimizer_dump_cfg_graphviz(cb, op_array);
4371
4372        if (global == NULL) {
4373            free_alloca(cb, cb_use_heap);
4374            free_alloca(jmp_lines, jmp_lines_use_heap);
4375            return NULL;
4376        }
4377
4378        int peephole_passes = OPTIMIZER_G(peephole_passes);
4379        //peephole_passes = 1;
4380        for (i = 0; i < peephole_passes; i++) {
4381            /* Determine used blocks and its parent chain */
4382            mark_used_cb(cb);
4383
4384            /* JMP Optimization */
4385            optimize_jmp(cb, op_array, jmp_lines);
4386
4387            get_var(cb, op_array, global);
4388
4389            /* Optimize Each block section */
4390            p = cb;
4391            while (p != NULL) {
4392                optimize_code_block(p, op_array, global, i, sf, jmp_lines OPTIMIZER_STATS_PARAM_PASS TSRMLS_CC);
4393                p = p->next;
4394            }
4395
4396            /* Mark All Blocks as unused and destroy and parent chain */
4397            p = cb;
4398            while (p != NULL) {
4399                rm_cb(p);
4400                p = p->next;
4401            }
4402        }
4403
4404        /* Last jump optimization */
4405        mark_used_cb(cb);
4406        optimize_jmp(cb, op_array, jmp_lines);
4407        p = cb;
4408        while (p != NULL) {
4409            rm_cb(p);
4410            p = p->next;
4411        }
4412
4413        /* Mark used blocks */
4414        mark_used_cb2(cb);
4415
4416        /* Remove unused blocks */
4417        p = cb;
4418        while (p->next != NULL) {
4419            if (p->next->used) {
4420                p = p->next;
4421            } else {
4422                del_cb(p->next);
4423                p->next = p->next->next;
4424            }
4425        }
4426
4427
4428#if OPTIMIZER_DEBUG_COMPILE
4429        //optimizer_dump_cfg_graphviz(cb, op_array);
4430#endif
4431
4432
4433        /* Store Optimized Code */
4434        generate_cfg(op_array, cb);
4435        rebuild_var_list(op_array, cb, global);
4436        free_alloca(global, global_use_heap);
4437    }
4438    free_alloca(cb, cb_use_heap);
4439    free_alloca(jmp_lines, jmp_lines_use_heap);
4440
4441    if (zend_hash_find(EG(active_symbol_table), "php_errormsg",
4442                       sizeof("php_errormsg"), (void **)&tmp) == SUCCESS) {
4443        if (zend_hash_del(EG(active_symbol_table), "php_errormsg",
4444                          sizeof("php_errormsg")) != SUCCESS) {
4445            OPTIMIZE_ASSERT(0);
4446        }
4447    }
4448
4449    /*
4450     * part of zend_opcode.c...
4451     */
4452    pass_two(op_array TSRMLS_CC);
4453
4454#if OPTIMIZER_STATS_COMPILE
4455    OPTIMIZER_STATS_CALC(op_array_stats->post, op_array);
4456    // Replace with a call to a debug function that will dump the info for us
4457    //php_printf("optimization_count=%d\n\n", op_array_stats->optimization_count);
4458    //php_printf("op-row count (pre): %d\n\n", op_array_stats->op_row_count_pre);
4459    //php_printf("STATS DUMP\n");
4460    optimizer_dump_op_array_stats(op_array_stats, op_array TSRMLS_CC);
4461    //php_printf("END STATS DUMP\n");
4462#endif
4463
4464#if OPTIMIZER_DEBUG_COMPILE
4465    //vld_dump_oparray(op_array);
4466    //optimizer_dump_brk_cont_table(op_array);
4467    optimizer_dump_op_array(op_array);
4468#endif
4469
4470#if OPTIMIZER_TEST_COMPILE
4471    if (OPTIMIZER_G(optimizer_test)) {
4472        php_printf("POST OPTIMIZATION\n");
4473        optimizer_test_dump_op_array(op_array TSRMLS_CC);
4474    }
4475#endif
4476
4477    /* If necessary identify all function calls with static parameter */
4478    if(OPTIMIZER_G(optimize_detect_static_function_calls) || OPTIMIZER_G(optimize_report_constants)) {
4479        zend_op* op = op_array->opcodes;
4480        int i = 0, j;
4481        zend_bool ok;
4482        int len = op_array->last;
4483
4484        int only_native = OPTIMIZER_G(optimize_report_only_native_func);
4485        int const_check = OPTIMIZER_G(optimize_report_constants);
4486        int func_check  = OPTIMIZER_G(optimize_detect_static_function_calls);
4487
4488
4489        while (i++ < len) {
4490            if (func_check && op->opcode == ZEND_DO_FCALL && op->extended_value > 0 && (op-1)->opcode == ZEND_SEND_VAL && OP1_TYPE((op-1)) == IS_CONST) {
4491                if (only_native) {
4492                    zend_function *func;
4493                    if (zend_hash_find(EG(function_table), OP1_VAL_S(op), OP1_VAL_SL(op) + 1, (void **)&func) == SUCCESS) {
4494                        if (func->type != ZEND_INTERNAL_FUNCTION) {
4495                            op++;
4496                            continue;
4497                        }
4498                    }
4499                }
4500
4501                ok = j = 1;
4502                while (j <= op->extended_value) {
4503                    if ((op-j)->opcode != ZEND_SEND_VAL || OP1_TYPE((op-j)) != IS_CONST) {
4504                        ok = 0;
4505                        break;
4506                    }
4507                    j++;
4508                }
4509                if (ok) {
4510                    fprintf(stderr, "Function %s(argc=%ld) on %s:%d\n", OP1_VAL_S(op), op->extended_value, op_array->filename, op->lineno);
4511                }
4512            } else if (const_check && op->opcode == ZEND_FETCH_CONSTANT && OP2_TYPE(op) == IS_CONST) {
4513                if (RES_TYPE(op) == IS_TMP_VAR && OP1_TYPE(op) == IS_UNUSED && !op->extended_value) {
4514                    fprintf(stderr, "Constant %s on %s:%d\n", OP2_VAL_S(op), op_array->filename, op->lineno);
4515                } else if (i && (op-1)->opcode == ZEND_FETCH_CLASS && OP2_TYPE((op-1)) == IS_CONST && (op-1)->extended_value == ZEND_FETCH_CLASS_GLOBAL) {
4516                    fprintf(stderr, "Class Constant %s::%s on %s:%d\n", OP2_VAL_S(op), OP2_VAL_S((op-1)), op_array->filename, op->lineno);
4517                }
4518            }
4519            op++;
4520        }
4521    }
4522    /* If this is a class method, fixup all inherited methods in those
4523       subclasses that have done inheritance at compile time.
4524     */
4525    if (op_array->scope) {
4526        fixup_inherited_method(op_array, (apply_func_2_args_t) fixup_inherited_method_helper TSRMLS_CC);
4527    }
4528    return op_array;
4529}
4530
4531#if 0
4532static void _optimizer_dump_hashtable_keys(HashTable* ht)
4533{
4534    if (ht != NULL) {
4535        zend_uint ele_c = zend_hash_num_elements(ht);
4536        printf("    ele_c: %d\n", ele_c);
4537
4538        if (ele_c > 0) {
4539            zend_hash_internal_pointer_reset(ht);
4540            zend_uint i;
4541            char* key;
4542            uint key_size;
4543
4544            for (i = 0; i < ele_c; i++) {
4545                zend_hash_get_current_key_ex(ht, &key, &key_size, NULL, 0, NULL);
4546                printf("    key: %s\n", key);
4547                printf("    key_size: %d\n", key_size);
4548                zend_hash_move_forward(ht);
4549            }
4550        }
4551    }
4552}
4553#endif
4554
4555zend_op_array* optimizer_compile_file(zend_file_handle* h, int type TSRMLS_DC)
4556{
4557    int i;
4558    int function_count_before, function_count_after;
4559    int class_count_before, class_count_after;
4560    optimizer_source_file sf;
4561    zend_op_array* op_array;
4562
4563    memset(&sf, 0, sizeof(optimizer_source_file));
4564
4565    function_count_before = zend_hash_num_elements(CG(function_table));
4566    class_count_before = zend_hash_num_elements(CG(class_table));
4567
4568    op_array = optimizer_old_compile_file(h, type TSRMLS_CC);
4569
4570    if (op_array == NULL || OPTIMIZER_G(optimization_level) == 0) {
4571        return op_array;
4572    }
4573
4574    function_count_after = zend_hash_num_elements(CG(function_table));
4575    class_count_after = zend_hash_num_elements(CG(class_table));
4576
4577    sf.fc= function_count_after-function_count_before;
4578    sf.class_count = class_count_after-class_count_before;
4579
4580    fcr_table_ctor(&sf.fh);
4581
4582
4583    /* Handle classes */
4584    if (sf.class_count > 0) {
4585        zend_hash_internal_pointer_reset(CG(class_table));
4586        for (i = 0; i < class_count_before; i++) {
4587            zend_hash_move_forward(CG(class_table));
4588        }
4589
4590        if ((sf.class_table = emalloc(sf.class_count*sizeof(optimizer_class))) == NULL) {
4591            fcr_table_dtor(&sf.fh);
4592            return op_array;
4593        }
4594        memset(sf.class_table, 0, sf.class_count*sizeof(optimizer_class));
4595
4596        optimizer_class* oce = sf.class_table;
4597        for (i = 0; i < sf.class_count; i++) {
4598            zend_class_entry** ce;
4599            zend_hash_get_current_data(CG(class_table), (void**)&ce);
4600
4601            oce->ce = *ce;
4602
4603            oce->fc = zend_hash_num_elements(&(*ce)->function_table);
4604            sf.fc_class += oce->fc;
4605
4606            oce->constants_count = zend_hash_num_elements(&(*ce)->constants_table);
4607
4608            oce->function_table = NULL;
4609
4610            zend_hash_move_forward(CG(class_table));
4611        }
4612
4613        if (sf.fc_class > 0) {
4614            if ((sf._class_function_table = emalloc(sf.fc_class*sizeof(optimizer_function*))) == NULL) {
4615                efree(sf.class_table);
4616                fcr_table_dtor(&sf.fh);
4617                return op_array;
4618            }
4619            memset(sf._class_function_table, 0, sf.fc_class*sizeof(optimizer_function*));
4620        }
4621    }
4622
4623    if ((sf.function_table = emalloc((sf.fc_class+sf.fc+1)*sizeof(optimizer_function))) == NULL) {
4624        if (sf.class_count > 0) {
4625            if (sf.fc_class > 0) {
4626                efree(sf._class_function_table);
4627            }
4628            efree(sf.class_table);
4629        }
4630        fcr_table_dtor(&sf.fh);
4631        return op_array;
4632    }
4633    memset(sf.function_table, 0, (sf.fc_class+sf.fc+1)*sizeof(optimizer_function));
4634
4635    optimizer_function* ft_pos = sf.function_table;
4636
4637    /* Handle main() */
4638    ft_pos->fe = NULL;
4639    ft_pos->op_array = op_array;
4640    ft_pos->class_entry = NULL;
4641    sf.total_op_count = OPARRAY_LEN(op_array);
4642    ft_pos++;
4643
4644    /* Handle functions */
4645    if (sf.fc > 0) {
4646        zend_hash_internal_pointer_reset(CG(function_table));
4647        for (i = 0; i < function_count_before; i++) {
4648            zend_hash_move_forward(CG(function_table));
4649        }
4650
4651        for (i = 0; i < sf.fc; i++) {
4652            zend_function* fe;
4653            zend_hash_get_current_data(CG(function_table), (void**)&fe);
4654
4655            if (fe->type == ZEND_USER_FUNCTION) {
4656                ft_pos->fe = fe;
4657                ft_pos->op_array = &fe->op_array;
4658                ft_pos->class_entry = NULL;
4659
4660                if (EG(autoload_func) == fe) {
4661                    ft_pos->fe_type = OFE_TYPE___AUTOLOAD;
4662                } else {
4663                    ft_pos->fe_type = 0;
4664                }
4665
4666                sf.total_op_count += OPARRAY_LEN(&fe->op_array);
4667                ft_pos++;
4668            }
4669            zend_hash_move_forward(CG(function_table));
4670        }
4671    }
4672
4673    /* Handle class functions */
4674    if (sf.fc_class > 0) {
4675        optimizer_function** c_ft_pos = sf._class_function_table;
4676        optimizer_class* ct_pos = sf.class_table;
4677
4678        for (i = 0; i < sf.class_count; i++) {
4679            if (ct_pos->fc > 0) {
4680                zend_uint j;
4681                zend_function* fe;
4682                zend_class_entry* ce = ct_pos->ce;
4683
4684                zend_hash_internal_pointer_reset(&ce->function_table);
4685
4686                ct_pos->function_table = c_ft_pos;
4687
4688                for (j = 0; j < ct_pos->fc; j++) {
4689                    zend_hash_get_current_data(&ce->function_table, (void**)&fe);
4690
4691                    if (fe->type == ZEND_USER_FUNCTION) {
4692                        *c_ft_pos = ft_pos;
4693                        (*c_ft_pos)->fe = fe;
4694                        (*c_ft_pos)->class_entry = ct_pos;
4695                        (*c_ft_pos)->op_array = &fe->op_array;
4696
4697                        if (ce->constructor == fe) {
4698                            ct_pos->constructor = *c_ft_pos;
4699                            (*c_ft_pos)->fe_type = OFE_TYPE_CONSTRUCTOR;
4700                        } else if (ce->destructor == fe) {
4701                            ct_pos->destructor = *c_ft_pos;
4702                            (*c_ft_pos)->fe_type = OFE_TYPE_DESTRUCTOR;
4703                        } else if (ce->clone == fe) {
4704                            ct_pos->clone = *c_ft_pos;
4705                            (*c_ft_pos)->fe_type = OFE_TYPE_CLONE;
4706                        } else if (ce->__get == fe) {
4707                            ct_pos->__get = *c_ft_pos;
4708                            (*c_ft_pos)->fe_type = OFE_TYPE___GET;
4709                        } else if (ce->__set == fe) {
4710                            ct_pos->__set = *c_ft_pos;
4711                            (*c_ft_pos)->fe_type = OFE_TYPE___SET;
4712                        } else if (ce->__unset == fe) {
4713                            ct_pos->__unset = *c_ft_pos;
4714                            (*c_ft_pos)->fe_type = OFE_TYPE___UNSET;
4715                        } else if (ce->__isset == fe) {
4716                            ct_pos->__isset = *c_ft_pos;
4717                            (*c_ft_pos)->fe_type = OFE_TYPE___ISSET;
4718                        } else if (ce->__call == fe) {
4719                            ct_pos->__call = *c_ft_pos;
4720                            (*c_ft_pos)->fe_type = OFE_TYPE___CALL;
4721#if PHP_VERSION_ID >= 50300
4722                        } else if (ce->__callstatic == fe) {
4723                            ct_pos->__callstatic = *c_ft_pos;
4724                            (*c_ft_pos)->fe_type = OFE_TYPE___CALLSTATIC;
4725#endif
4726                        } else if (ce->__tostring == fe) {
4727                            ct_pos->__tostring = *c_ft_pos;
4728                            (*c_ft_pos)->fe_type = OFE_TYPE___TOSTRING;
4729                        } else if (ce->serialize_func == fe) {
4730                            ct_pos->serialize_func = *c_ft_pos;
4731                            (*c_ft_pos)->fe_type = OFE_TYPE_SERIALIZE_FUNC;
4732                        } else if (ce->unserialize_func == fe) {
4733                            ct_pos->unserialize_func = *c_ft_pos;
4734                            (*c_ft_pos)->fe_type = OFE_TYPE_UNSERIALIZE_FUNC;
4735                        } else {
4736                            (*c_ft_pos)->fe_type = 0;
4737                        }
4738
4739                        sf.total_op_count += OPARRAY_LEN(&fe->op_array);
4740                        c_ft_pos++;
4741                        ft_pos++;
4742                    }
4743
4744                    zend_hash_move_forward(&ce->function_table);
4745                }
4746            }
4747
4748            ct_pos++;
4749        }
4750    }
4751
4752
4753    /* At this point we have built our function, class, and class function
4754       tables... all we need to do now is to optimize! */
4755    ft_pos = sf.function_table;
4756    for (i = 0; i < (sf.fc+sf.fc_class+1); i++) {
4757        zend_op_array* o = optimize_op_array(ft_pos->op_array, &sf TSRMLS_CC);
4758        ft_pos++;
4759    }
4760
4761    if (sf.class_count > 0) {
4762        if (sf.fc_class > 0) {
4763            efree(sf._class_function_table);
4764        }
4765        efree(sf.class_table);
4766    }
4767    efree(sf.function_table);
4768    fcr_table_dtor(&sf.fh);
4769
4770    return op_array;
4771}
4772
4773/*
4774 * Local variables:
4775 * tab-width: 4
4776 * c-basic-offset: 4
4777 * End:
4778 * vim600: noet sw=4 ts=4 fdm=marker
4779 * vim<600: noet sw=4 ts=4
4780 */
4781