source: trunk/optimizer.c @ 316

Last change on this file since 316 was 316, checked in by moo, 8 years ago

optimizer: don't crash on compiler error

File size: 8.0 KB
Line 
1#if 0
2#define DEBUG
3#endif
4
5#include "utils.h"
6#include "optimizer.h"
7/* the "vector" stack */
8#include "stack.h"
9
10#ifdef DEBUG
11#   include "processor.h"
12#endif
13
14typedef int bbid_t;
15enum {
16    BBID_INVALID = -1,
17};
18/* {{{ basic block */
19typedef struct _bb_t {
20    bbid_t     id;
21    zend_bool  used;
22
23    zend_bool  alloc;
24    zend_op   *opcodes;
25    int        count;
26    int        size;
27
28    bbid_t     jmpout_op1;
29    bbid_t     jmpout_op2;
30    bbid_t     jmpout_ext;
31    bbid_t     fall;
32} bb_t;
33/* }}} */
34
35/* basic blocks */
36typedef xc_stack_t bbs_t;
37
38/* oparray help functions */
39static int op_array_convert_switch(zend_op_array *op_array) /* {{{ */
40{
41    int i;
42
43    if (op_array->brk_cont_array == NULL) {
44        return SUCCESS;
45    }
46
47    for (i = 0; i < op_array->last; i ++) {
48        zend_op *opline = &op_array->opcodes[i];
49        zend_brk_cont_element *jmp_to;
50        int array_offset, nest_levels, original_nest_levels;
51
52        if (opline->opcode != ZEND_BRK && opline->opcode != ZEND_CONT) {
53            continue;
54        }
55        if (opline->op2.op_type != IS_CONST
56         || opline->op2.u.constant.type != IS_LONG) {
57            return FAILURE;
58        }
59
60        nest_levels = opline->op2.u.constant.value.lval;
61        original_nest_levels = nest_levels;
62
63        array_offset = opline->op1.u.opline_num;
64        do {
65            if (array_offset == -1) {
66                /* this is a runtime error in ZE
67                zend_error(E_ERROR, "Cannot break/continue %d level%s", original_nest_levels, (original_nest_levels == 1) ? "" : "s");
68                */
69                return FAILURE;
70            }
71            jmp_to = &op_array->brk_cont_array[array_offset];
72            if (nest_levels > 1) {
73                zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
74
75                switch (brk_opline->opcode) {
76                case ZEND_SWITCH_FREE:
77                    break;
78                case ZEND_FREE:
79                    break;
80                }
81            }
82            array_offset = jmp_to->parent;
83        } while (--nest_levels > 0);
84
85        /* rewrite to jmp */
86        if (opline->opcode == ZEND_BRK) {
87            opline->op1.u.opline_num = jmp_to->brk;
88        }
89        else {
90            opline->op1.u.opline_num = jmp_to->cont;
91        }
92        opline->op2.op_type = IS_UNUSED;
93        opline->opcode = ZEND_JMP;
94    }
95
96    if (op_array->brk_cont_array != NULL) {
97        efree(op_array->brk_cont_array);
98        op_array->brk_cont_array = NULL;
99    }
100    op_array->last_brk_cont = 0;
101    return SUCCESS;
102}
103/* }}} */
104static int op_get_jmpout(int *jmpout_op1, int *jmpout_op2, int *jmpout_ext, zend_bool *fall, zend_op *opline) /* {{{ */
105{
106    /* break=will fall */
107    switch (opline->opcode) {
108    case ZEND_RETURN:
109    case ZEND_EXIT:
110        return SUCCESS; /* no fall */
111
112    case ZEND_JMP:
113        *jmpout_op1 = opline->op1.u.opline_num;
114        return SUCCESS; /* no fall */
115
116    case ZEND_JMPZNZ:
117        *jmpout_ext = opline->extended_value;
118        *jmpout_op2 = opline->op2.u.opline_num;
119        break;
120
121    case ZEND_JMPZ:
122    case ZEND_JMPNZ:
123    case ZEND_JMPZ_EX:
124    case ZEND_JMPNZ_EX:
125#ifndef ZEND_ENGINE_2_1
126        /* Pre-PHP 5.1 only */
127    case ZEND_JMP_NO_CTOR:
128#else       
129    case ZEND_NEW:
130    case ZEND_FE_RESET:
131#endif     
132    case ZEND_FE_FETCH:
133        *jmpout_op2 = opline->op2.u.opline_num;
134        break;
135
136    default:
137        return FAILURE;
138    }
139
140    *fall = 1;
141    return SUCCESS;
142}
143/* }}} */
144
145/*
146 * basic block functions
147 */
148
149static bb_t *bb_new_ex(zend_op *opcodes, int count) /* {{{ */
150{
151    bb_t *bb = (bb_t *) ecalloc(sizeof(bb_t), 1);
152
153    bb->jmpout_op1 = BBID_INVALID;
154    bb->jmpout_op2 = BBID_INVALID;
155    bb->jmpout_ext = BBID_INVALID;
156    bb->fall       = BBID_INVALID;
157
158    if (opcodes) {
159        bb->alloc   = 0;
160        bb->size    = bb->count = count;
161        bb->opcodes = opcodes;
162    }
163    else {
164        bb->alloc   = 1;
165        bb->size    = bb->count = 8;
166        bb->opcodes = ecalloc(sizeof(zend_op), bb->size);
167    }
168
169    return bb;
170}
171/* }}} */
172#define bb_new() bb_new_ex(NULL, 0)
173static void bb_destroy(bb_t *bb) /* {{{ */
174{
175    if (bb->alloc) {
176        efree(bb->opcodes);
177    }
178    efree(bb);
179}
180/* }}} */
181#ifdef DEBUG
182static void bb_print(bb_t *bb, zend_op *opcodes) /* {{{ */
183{
184    fprintf(stderr,
185            "%3d %3d %3d"
186            " %c%c"
187            " %3d %3d %3d %3d\r\n"
188            , bb->id, bb->count, bb->opcodes - opcodes
189            , bb->used ? 'U' : ' ', bb->alloc ? 'A' : ' '
190            , bb->jmpout_op1, bb->jmpout_op2, bb->jmpout_ext, bb->fall
191            );
192}
193/* }}} */
194#endif
195
196#define bbs_get(bbs, n) xc_stack_get(bbs, n)
197static void bbs_destroy(bbs_t *bbs) /* {{{ */
198{
199    bb_t *bb;
200    while (xc_stack_count(bbs)) {
201        bb = (bb_t *) xc_stack_pop(bbs);
202        bb_destroy(bb);
203    }
204    xc_stack_destroy(bbs);
205}
206/* }}} */
207#ifdef DEBUG
208static void bbs_print(bbs_t *bbs, zend_op *opcodes) /* {{{ */
209{
210    int i;
211    for (i = 0; i < xc_stack_count(bbs); i ++) {
212        bb_print(bbs_get(bbs, i), opcodes);
213    }
214}
215/* }}} */
216#endif
217#define bbs_init(bbs) xc_stack_init_ex(bbs, 16)
218static bb_t *bbs_add_bb(bbs_t *bbs, bb_t *bb) /* {{{ */
219{
220    bb->id = (bbid_t) xc_stack_count(bbs);
221    xc_stack_push(bbs, (void *) bb);
222    return bb;
223}
224/* }}} */
225static bb_t *bbs_new_bb_ex(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */
226{
227    return bbs_add_bb(bbs, bb_new_ex(opcodes, count));
228}
229/* }}} */
230static int bbs_build_from(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */
231{
232    int i, prev;
233    bb_t *pbb;
234    bbid_t id;
235    int jmpout_op1, jmpout_op2, jmpout_ext;
236    zend_bool fall;
237    bbid_t *bbids          = do_alloca(count * sizeof(bbid_t));
238    zend_bool *markjmpins  = do_alloca(count * sizeof(zend_bool));
239    zend_bool *markjmpouts = do_alloca(count * sizeof(zend_bool));
240
241    /* {{{ mark jmpin/jumpout */
242    memset(markjmpins,  0, count * sizeof(zend_bool));
243    memset(markjmpouts, 0, count * sizeof(zend_bool));
244
245    for (i = 0; i < count; i ++) {
246        jmpout_op1 = jmpout_op2 = jmpout_ext = -1;
247        fall = 0;
248        if (op_get_jmpout(&jmpout_op1, &jmpout_op2, &jmpout_ext, &fall, &opcodes[i]) == SUCCESS) {
249            markjmpouts[i] = 1;
250
251            if (jmpout_op1 != -1) {
252                markjmpins[jmpout_op1] = 1;
253            }
254            if (jmpout_op2 != -1) {
255                markjmpins[jmpout_op2] = 1;
256            }
257            if (jmpout_ext != -1) {
258                markjmpins[jmpout_ext] = 1;
259            }
260        }
261    }
262    /* }}} */
263    /* {{{ fill opcodes with newly allocated id */
264    for (i = 0; i < count; i ++) {
265        bbids[i] = BBID_INVALID;
266    }
267
268    prev = 0;
269    id = 0;
270    for (i = 1; i < count; i ++) {
271        if (markjmpins[i] || markjmpouts[i - 1]) {
272            for (; prev < i; prev ++) {
273                bbids[prev] = id;
274            }
275            id ++;
276            prev = i;
277        }
278    }
279
280    if (prev < count) {
281        for (; prev < i; prev ++) {
282            bbids[prev] = id;
283        }
284    }
285    /* }}} */
286    /* {{{ create basic blocks */
287    prev = 0;
288    id = 0;
289    for (i = 1; i < count; i ++) {
290        if (id == bbids[i]) {
291            continue;
292        }
293        id = bbids[i];
294
295        pbb = bbs_new_bb_ex(bbs, opcodes + prev, i - prev);
296        jmpout_op1 = jmpout_op2 = jmpout_ext = -1;
297        fall = 0;
298        if (op_get_jmpout(&jmpout_op1, &jmpout_op2, &jmpout_ext, &fall, &opcodes[prev]) == SUCCESS) {
299            if (jmpout_op1 != -1) {
300                pbb->jmpout_op1 = bbids[jmpout_op1];
301                assert(pbb->jmpout_op1 != BBID_INVALID);
302            }
303            if (jmpout_op2 != -1) {
304                pbb->jmpout_op2 = bbids[jmpout_op2];
305                assert(pbb->jmpout_op2 != BBID_INVALID);
306            }
307            if (jmpout_ext != -1) {
308                pbb->jmpout_ext = bbids[jmpout_ext];
309                assert(pbb->jmpout_ext != BBID_INVALID);
310            }
311            if (fall && i + 1 < count) {
312                pbb->fall = bbids[i + 1];
313                assert(pbb->fall != BBID_INVALID);
314            }
315        }
316        prev = i + 1;
317    }
318    assert(prev == count);
319    /* }}} */
320
321    free_alloca(bbids);
322    free_alloca(markjmpins);
323    free_alloca(markjmpouts);
324    return SUCCESS;
325}
326/* }}} */
327
328/*
329 * optimize
330 */
331static int xc_optimize_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
332{
333    bbs_t bbs;
334
335    if (op_array->type != ZEND_USER_FUNCTION) {
336        return 0;
337    }
338    xc_undo_pass_two(op_array TSRMLS_CC);
339#ifdef DEBUG
340#   if 0
341    TRACE("optimize file: %s", op_array->filename);
342    xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
343#   endif
344#endif
345
346    if (op_array_convert_switch(op_array) == SUCCESS) {
347        bbs_init(&bbs);
348        if (bbs_build_from(&bbs, op_array->opcodes, op_array->last) == SUCCESS) {
349#ifdef DEBUG
350            bbs_print(&bbs, op_array->opcodes);
351#endif
352        }
353        bbs_destroy(&bbs);
354    }
355
356#ifdef DEBUG
357#   if 0
358    TRACE("%s", "after compiles");
359    xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
360#   endif
361#endif
362    xc_redo_pass_two(op_array TSRMLS_CC);
363    return 0;
364}
365/* }}} */
366void xc_optimize(zend_op_array *op_array TSRMLS_DC) /* {{{ */
367{
368    xc_compile_result_t cr;
369
370    if (!op_array) {
371        return;
372    }
373
374    xc_compile_result_init_cur(&cr, op_array TSRMLS_CC);
375
376    xc_apply_op_array(&cr, (apply_func_t) xc_undo_pass_two TSRMLS_CC);
377    xc_apply_op_array(&cr, (apply_func_t) xc_optimize_op_array TSRMLS_CC);
378    xc_apply_op_array(&cr, (apply_func_t) xc_redo_pass_two TSRMLS_CC);
379
380    xc_compile_result_free(&cr);
381}
382/* }}} */
Note: See TracBrowser for help on using the repository browser.