source: trunk/mod_optimizer/xc_optimizer.c

Last change on this file was 1532, checked in by moo, 10 days ago

define XCACHE_DEBUG in phpized

  • Property svn:eol-style set to native
File size: 16.4 KB
Line 
1#include "xc_optimizer.h"
2#include "xcache/xc_extension.h"
3#include "xcache/xc_ini.h"
4#include "xcache/xc_utils.h"
5#include "util/xc_stack.h"
6#include "util/xc_trace.h"
7#include "xcache_globals.h"
8
9#include "ext/standard/info.h"
10
11#ifdef XCACHE_DEBUG
12#   include "xc_processor.h"
13#   include "xcache/xc_const_string.h"
14#   include "xcache/xc_opcode_spec.h"
15#   include "ext/standard/php_var.h"
16#endif
17
18#ifdef IS_CV
19#   define XCACHE_IS_CV IS_CV
20#else
21#   define XCACHE_IS_CV 16
22#endif
23
24#ifdef ZEND_ENGINE_2_4
25#   undef Z_OP_CONSTANT
26/* Z_OP_CONSTANT is used before pass_two is applied */
27#   define Z_OP_CONSTANT(op) op_array->literals[(op).constant].constant
28#endif
29
30typedef zend_uint bbid_t;
31#define BBID_INVALID ((bbid_t) -1)
32/* {{{ basic block */
33typedef struct _bb_t {
34    bbid_t     id;
35    zend_bool  used;
36
37    zend_bool  alloc;
38    zend_op   *opcodes;
39    int        count;
40    int        size;
41
42    bbid_t     fall;
43
44    zend_uint  opnum; /* opnum after joining basic block */
45} bb_t;
46/* }}} */
47
48/* basic blocks */
49typedef xc_stack_t bbs_t;
50
51/* op array helper functions */
52static int op_array_convert_switch(zend_op_array *op_array) /* {{{ */
53{
54    zend_uint i;
55
56    if (op_array->brk_cont_array == NULL) {
57        return SUCCESS;
58    }
59
60    for (i = 0; i < op_array->last; i ++) {
61        zend_op *opline = &op_array->opcodes[i];
62        zend_brk_cont_element *jmp_to;
63        zend_bool can_convert = 1;
64        int array_offset, nest_levels;
65
66        switch (opline->opcode) {
67        case ZEND_BRK:
68        case ZEND_CONT:
69            break;
70
71#ifdef ZEND_GOTO
72        case ZEND_GOTO:
73#endif
74            continue;
75
76        default:
77            continue;
78        }
79
80        if (Z_OP_TYPE(opline->op2) != IS_CONST
81         || Z_OP_CONSTANT(opline->op2).type != IS_LONG) {
82            return FAILURE;
83        }
84
85        nest_levels = Z_OP_CONSTANT(opline->op2).value.lval;
86
87        array_offset = Z_OP(opline->op1).opline_num;
88        do {
89            if (array_offset == -1) {
90                return FAILURE;
91            }
92            jmp_to = &op_array->brk_cont_array[array_offset];
93            if (nest_levels > 1) {
94                zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
95
96                switch (brk_opline->opcode) {
97                case ZEND_SWITCH_FREE:
98                case ZEND_FREE:
99#ifdef EXT_TYPE_FREE_ON_RETURN
100                    if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN))
101#endif
102                    {
103                        can_convert = 0;
104                    }
105                    break;
106                }
107            }
108            array_offset = jmp_to->parent;
109        } while (--nest_levels > 0);
110
111        if (can_convert) {
112            /* rewrite to jmp */
113            switch (opline->opcode) {
114            case ZEND_BRK:
115                Z_OP(opline->op1).opline_num = jmp_to->brk;
116                break;
117
118            case ZEND_CONT:
119                Z_OP(opline->op1).opline_num = jmp_to->cont;
120                break;
121            }
122            Z_OP_TYPE(opline->op2) = IS_UNUSED;
123            opline->opcode = ZEND_JMP;
124        }
125    }
126
127    return SUCCESS;
128}
129/* }}} */
130/* {{{ op_flowinfo helper func */
131enum {
132    XC_OPNUM_INVALID = -1
133};
134typedef struct {
135    int       jmpout_op1;
136    int       jmpout_op2;
137    int       jmpout_ext;
138    zend_bool fall;
139} op_flowinfo_t;
140static void op_flowinfo_init(op_flowinfo_t *fi)
141{
142    fi->jmpout_op1 = fi->jmpout_op2 = fi->jmpout_ext = XC_OPNUM_INVALID;
143    fi->fall = 0;
144}
145/* }}} */
146static int op_get_flowinfo(op_flowinfo_t *fi, zend_op *opline) /* {{{ */
147{
148    op_flowinfo_init(fi);
149
150    switch (opline->opcode) {
151#ifdef ZEND_HANDLE_EXCEPTION
152    case ZEND_HANDLE_EXCEPTION:
153#endif
154    case ZEND_RETURN:
155#ifdef ZEND_FAST_RET
156    case ZEND_FAST_RET:
157#endif
158#ifdef ZEND_GENERATOR_RETURN
159    case ZEND_GENERATOR_RETURN:
160#endif
161    case ZEND_EXIT:
162        return SUCCESS; /* no fall */
163
164#ifdef ZEND_GOTO
165    case ZEND_GOTO:
166#endif
167    case ZEND_JMP:
168        fi->jmpout_op1 = Z_OP(opline->op1).opline_num;
169        return SUCCESS; /* no fall */
170
171#ifdef ZEND_FAST_CALL
172    case ZEND_FAST_CALL:
173        fi->jmpout_op1 = Z_OP(opline->op1).opline_num;
174        if (opline->extended_value) {
175            fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
176        }
177        return SUCCESS; /* no fall */
178#endif
179
180    case ZEND_JMPZNZ:
181        fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
182        fi->jmpout_ext = (int) opline->extended_value;
183        return SUCCESS; /* no fall */
184
185    case ZEND_JMPZ:
186    case ZEND_JMPNZ:
187    case ZEND_JMPZ_EX:
188    case ZEND_JMPNZ_EX:
189#ifdef ZEND_JMP_SET
190    case ZEND_JMP_SET:
191#endif
192#ifdef ZEND_JMP_SET_VAR
193    case ZEND_JMP_SET_VAR:
194#endif
195#ifdef ZEND_JMP_NO_CTOR
196    case ZEND_JMP_NO_CTOR:
197#endif
198#ifdef ZEND_NEW
199    case ZEND_NEW:
200#endif
201#ifdef ZEND_FE_RESET
202    case ZEND_FE_RESET:
203#endif     
204    case ZEND_FE_FETCH:
205        fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
206        fi->fall = 1;
207        return SUCCESS;
208
209#ifdef ZEND_CATCH
210    case ZEND_CATCH:
211        fi->jmpout_ext = (int) opline->extended_value;
212        fi->fall = 1;
213        return SUCCESS;
214#endif
215
216    default:
217        return FAILURE;
218    }
219}
220/* }}} */
221#ifdef XCACHE_DEBUG
222static void op_snprint(zend_op_array *op_array, char *buf, int size, zend_uchar op_type, znode_op *op, xc_op_spec_t op_spec) /* {{{ */
223{
224    switch ((op_spec & OPSPEC_UNUSED) ? IS_UNUSED : op_type) {
225    case IS_CONST:
226        {
227            zval result;
228            zval *zv = &Z_OP_CONSTANT(*op);
229            TSRMLS_FETCH();
230
231            php_output_start_default(TSRMLS_C);
232            php_var_export(&zv, 1 TSRMLS_CC);
233            php_output_get_contents(&result TSRMLS_CC); 
234            php_output_discard(TSRMLS_C);
235
236            snprintf(buf, size, Z_STRVAL(result));
237            zval_dtor(&result);
238        }
239        break;
240
241    case IS_TMP_VAR:
242        snprintf(buf, size, "t@%d", Z_OP(*op).var);
243        break;
244
245    case XCACHE_IS_CV:
246    case IS_VAR:
247        snprintf(buf, size, "v@%d", Z_OP(*op).var);
248        break;
249
250    case IS_UNUSED:
251        if (Z_OP(*op).opline_num) {
252            snprintf(buf, size, "u#%d", Z_OP(*op).opline_num);
253        }
254        else {
255            snprintf(buf, size, "-");
256        }
257        break;
258
259    default:
260        snprintf(buf, size, "%d %d", op_type, Z_OP(*op).var);
261    }
262}
263/* }}} */
264static void op_print(zend_op_array *op_array, int line, zend_op *first, zend_op *end) /* {{{ */
265{
266    zend_op *opline;
267    for (opline = first; opline < end; opline ++) {
268        char buf_r[20];
269        char buf_1[20];
270        char buf_2[20];
271        const xc_opcode_spec_t *opcode_spec = xc_get_opcode_spec(opline->opcode);
272        op_snprint(op_array, buf_r, sizeof(buf_r), Z_OP_TYPE(opline->result), &opline->result, opcode_spec->res);
273        op_snprint(op_array, buf_1, sizeof(buf_1), Z_OP_TYPE(opline->op1),    &opline->op1,    opcode_spec->op1);
274        op_snprint(op_array, buf_2, sizeof(buf_2), Z_OP_TYPE(opline->op2),    &opline->op2,    opcode_spec->op2);
275        fprintf(stderr,
276                "%3d %3lu"
277                " %-25s%-8s%-20s%-20s%5lu\n"
278                , opline->lineno, (long) (opline - first + line)
279                , xc_get_opcode(opline->opcode), buf_r, buf_1, buf_2, opline->extended_value);
280    }
281}
282/* }}} */
283static void op_array_print_try_catch(zend_op_array *op_array TSRMLS_DC) /* {{{ */
284{
285    int i;
286    for (i = 0; i < op_array->last_try_catch; i ++) {
287        zend_try_catch_element *element = &op_array->try_catch_array[i];
288#   ifdef ZEND_ENGINE_2_5
289        fprintf(stderr, "try_catch[%d] = %u, %u, %u, %u\n", i, element->try_op, element->catch_op, element->finally_op, element->finally_end);
290#   else
291        fprintf(stderr, "try_catch[%d] = %u, %u\n", i, element->try_op, element->catch_op);
292#   endif
293    }
294}
295/* }}} */
296#endif
297
298/*
299 * basic block functions
300 */
301
302static bb_t *bb_new_ex(zend_op *opcodes, int count) /* {{{ */
303{
304    bb_t *bb = (bb_t *) ecalloc(sizeof(bb_t), 1);
305
306    bb->fall       = BBID_INVALID;
307
308    if (opcodes) {
309        bb->alloc   = 0;
310        bb->size    = bb->count = count;
311        bb->opcodes = opcodes;
312    }
313    else {
314        bb->alloc   = 1;
315        bb->size    = bb->count = 8;
316        bb->opcodes = ecalloc(sizeof(zend_op), bb->size);
317    }
318
319    return bb;
320}
321/* }}} */
322#define bb_new() bb_new_ex(NULL, 0)
323static void bb_destroy(bb_t *bb) /* {{{ */
324{
325    if (bb->alloc) {
326        efree(bb->opcodes);
327    }
328    efree(bb);
329}
330/* }}} */
331#ifdef XCACHE_DEBUG
332static void bb_print(bb_t *bb, zend_op_array *op_array) /* {{{ */
333{
334    int line = bb->opcodes - op_array->opcodes;
335    op_flowinfo_t fi;
336    zend_op *last = bb->opcodes + bb->count - 1;
337
338    op_get_flowinfo(&fi, last);
339
340    fprintf(stderr,
341            "\n==== #%-3d cnt:%-3d lno:%-3d"
342            " %c%c"
343            " op1:%-3d op2:%-3d ext:%-3d fal:%-3d %s ====\n"
344            , bb->id, bb->count, bb->alloc ? -1 : line
345            , bb->used ? 'U' : ' ', bb->alloc ? 'A' : ' '
346            , fi.jmpout_op1, fi.jmpout_op2, fi.jmpout_ext, bb->fall, xc_get_opcode(last->opcode)
347            );
348    op_print(op_array, line, bb->opcodes, last + 1);
349}
350/* }}} */
351#endif
352
353static bb_t *bbs_get(bbs_t *bbs, int n) /* {{{ */
354{
355    return (bb_t *) xc_stack_get(bbs, n);
356}
357/* }}} */
358static int bbs_count(bbs_t *bbs) /* {{{ */
359{
360    return xc_stack_count(bbs);
361}
362/* }}} */
363static void bbs_destroy(bbs_t *bbs) /* {{{ */
364{
365    bb_t *bb;
366    while (bbs_count(bbs)) {
367        bb = (bb_t *) xc_stack_pop(bbs);
368        bb_destroy(bb);
369    }
370    xc_stack_destroy(bbs);
371}
372/* }}} */
373#ifdef XCACHE_DEBUG
374static void bbs_print(bbs_t *bbs, zend_op_array *op_array) /* {{{ */
375{
376    int i;
377    for (i = 0; i < xc_stack_count(bbs); i ++) {
378        bb_print(bbs_get(bbs, i), op_array);
379    }
380}
381/* }}} */
382#endif
383#define bbs_init(bbs) xc_stack_init_ex(bbs, 16)
384static bb_t *bbs_add_bb(bbs_t *bbs, bb_t *bb) /* {{{ */
385{
386    bb->id = (bbid_t) xc_stack_count(bbs);
387    xc_stack_push(bbs, (void *) bb);
388    return bb;
389}
390/* }}} */
391static bb_t *bbs_new_bb_ex(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */
392{
393    return bbs_add_bb(bbs, bb_new_ex(opcodes, count));
394}
395/* }}} */
396static int bbs_build_from(bbs_t *bbs, zend_op_array *op_array, int count) /* {{{ */
397{
398    int i, start;
399    bb_t *bb;
400    bbid_t id;
401    op_flowinfo_t fi;
402    zend_op *opline;
403    ALLOCA_FLAG(opline_infos_use_heap)
404    typedef struct {
405        zend_bool isbbhead;
406        bbid_t bbid;
407    } oplineinfo_t;
408    oplineinfo_t *oplineinfos = xc_do_alloca(count * sizeof(oplineinfo_t), opline_infos_use_heap);
409
410    memset(oplineinfos, 0, count * sizeof(oplineinfo_t));
411
412    /* {{{ mark jmpin/jumpout */
413    oplineinfos[0].isbbhead = 1;
414    for (i = 0; i < count; i ++) {
415        if (op_get_flowinfo(&fi, &op_array->opcodes[i]) == SUCCESS) {
416            if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
417                oplineinfos[fi.jmpout_op1].isbbhead = 1;
418            }
419            if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
420                oplineinfos[fi.jmpout_op2].isbbhead = 1;
421            }
422            if (fi.jmpout_ext != XC_OPNUM_INVALID) {
423                oplineinfos[fi.jmpout_ext].isbbhead = 1;
424            }
425            if (i + 1 < count) {
426                oplineinfos[i + 1].isbbhead = 1;
427            }
428        }
429    }
430#ifdef ZEND_ENGINE_2
431    /* mark try start */
432    for (i = 0; i < op_array->last_try_catch; i ++) {
433#   define MARK_OP_BB_HEAD(name) \
434        oplineinfos[op_array->try_catch_array[i].name].isbbhead = 1
435        MARK_OP_BB_HEAD(try_op);
436        MARK_OP_BB_HEAD(catch_op);
437#   ifdef ZEND_ENGINE_2_5
438        MARK_OP_BB_HEAD(finally_op);
439#   endif
440#   undef MARK_OP_BB_HEAD
441    }
442#endif
443    /* }}} */
444    /* {{{ fill op lines with newly allocated id */
445    for (i = 0; i < count; i ++) {
446        oplineinfos[i].bbid = BBID_INVALID;
447    }
448
449    id = -1;
450    for (i = 0; i < count; i ++) {
451        if (oplineinfos[i].isbbhead) {
452            id ++;
453        }
454        oplineinfos[i].bbid = id;
455        TRACE("bbids[%d] = %d", i, id);
456    }
457    /* }}} */
458#ifdef ZEND_ENGINE_2
459    /* {{{ convert try_catch_array.* from oplinenum to bbid */
460    for (i = 0; i < op_array->last_try_catch; i ++) {
461#   define OPNUM_TO_BBID(name) \
462        op_array->try_catch_array[i].name = oplineinfos[op_array->try_catch_array[i].name].bbid;
463        OPNUM_TO_BBID(try_op);
464        OPNUM_TO_BBID(catch_op);
465#   ifdef ZEND_ENGINE_2_5
466        OPNUM_TO_BBID(finally_op);
467#   endif
468#   undef OPNUM_TO_BBID
469    }
470    /* }}} */
471#endif
472    /* {{{ create basic blocks */
473    start = 0;
474    id = 0;
475    /* loop over to deal with the last block */
476    for (i = 1; i <= count; i ++) {
477        if (i < count && id == oplineinfos[i].bbid) {
478            continue;
479        }
480
481        opline = op_array->opcodes + start;
482        bb = bbs_new_bb_ex(bbs, opline, i - start);
483
484        /* last */
485        opline = bb->opcodes + bb->count - 1;
486        if (op_get_flowinfo(&fi, opline) == SUCCESS) {
487            if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
488                Z_OP(opline->op1).opline_num = oplineinfos[fi.jmpout_op1].bbid;
489                assert(Z_OP(opline->op1).opline_num != BBID_INVALID);
490            }
491            if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
492                Z_OP(opline->op2).opline_num = oplineinfos[fi.jmpout_op2].bbid;
493                assert(Z_OP(opline->op2).opline_num != BBID_INVALID);
494            }
495            if (fi.jmpout_ext != XC_OPNUM_INVALID) {
496                opline->extended_value = oplineinfos[fi.jmpout_ext].bbid;
497                assert(opline->extended_value != BBID_INVALID);
498            }
499            if (fi.fall && i + 1 < count) {
500                bb->fall = oplineinfos[i + 1].bbid;
501                TRACE("fall %d", bb->fall);
502                assert(bb->fall != BBID_INVALID);
503            }
504        }
505        if (i >= count) {
506            break;
507        }
508        start = i;
509        id = oplineinfos[i].bbid;
510    }
511    /* }}} */
512
513    xc_free_alloca(oplineinfos, opline_infos_use_heap);
514    return SUCCESS;
515}
516/* }}} */
517static void bbs_restore_opnum(bbs_t *bbs, zend_op_array *op_array) /* {{{ */
518{
519    int bbid;
520    int i;
521
522    for (bbid = 0; bbid < bbs_count(bbs); bbid ++) {
523        op_flowinfo_t fi;
524        bb_t *bb = bbs_get(bbs, bbid);
525        zend_op *last = bb->opcodes + bb->count - 1;
526
527        if (op_get_flowinfo(&fi, last) == SUCCESS) {
528            if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
529                Z_OP(last->op1).opline_num = bbs_get(bbs, fi.jmpout_op1)->opnum;
530                assert(Z_OP(last->op1).opline_num != BBID_INVALID);
531            }
532            if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
533                Z_OP(last->op2).opline_num = bbs_get(bbs, fi.jmpout_op2)->opnum;
534                assert(Z_OP(last->op2).opline_num != BBID_INVALID);
535            }
536            if (fi.jmpout_ext != XC_OPNUM_INVALID) {
537                last->extended_value = bbs_get(bbs, fi.jmpout_ext)->opnum;
538                assert(last->extended_value != BBID_INVALID);
539            }
540        }
541    }
542
543#ifdef ZEND_ENGINE_2
544    /* {{{ convert try_catch_array from bbid to oplinenum */
545    for (i = 0; i < op_array->last_try_catch; i ++) {
546#   define BBID_TO_OPNUM(name) \
547        op_array->try_catch_array[i].name = bbs_get(bbs, op_array->try_catch_array[i].name)->opnum;
548        BBID_TO_OPNUM(try_op);
549        BBID_TO_OPNUM(catch_op);
550#   ifdef ZEND_ENGINE_2_5
551        BBID_TO_OPNUM(finally_op);
552#   endif
553#   undef BBID_TO_OPNUM
554    }
555    /* }}} */
556#endif /* ZEND_ENGINE_2 */
557}
558/* }}} */
559
560/*
561 * optimize
562 */
563static int xc_optimize_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
564{
565    bbs_t bbs;
566
567    if (op_array->type != ZEND_USER_FUNCTION) {
568        return 0;
569    }
570
571#ifdef XCACHE_DEBUG
572    TRACE("optimize file: %s", op_array->filename);
573#   if 0 && HAVE_XCACHE_DPRINT
574    xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
575#   endif
576    op_array_print_try_catch(op_array TSRMLS_CC);
577    op_print(op_array, 0, op_array->opcodes, op_array->opcodes + op_array->last);
578#endif
579
580    if (op_array_convert_switch(op_array) == SUCCESS) {
581        bbs_init(&bbs);
582        if (bbs_build_from(&bbs, op_array, op_array->last) == SUCCESS) {
583            int i;
584#ifdef XCACHE_DEBUG
585            bbs_print(&bbs, op_array);
586#endif
587            /* TODO: calc opnum after basic block move */
588            for (i = 0; i < bbs_count(&bbs); i ++) {
589                bb_t *bb = bbs_get(&bbs, i);
590                bb->opnum = bb->opcodes - op_array->opcodes;
591            }
592            bbs_restore_opnum(&bbs, op_array);
593        }
594        bbs_destroy(&bbs);
595    }
596
597#ifdef XCACHE_DEBUG
598    TRACE("%s", "after compiles");
599#   if 0
600    xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
601#   endif
602    op_array_print_try_catch(op_array TSRMLS_CC);
603    op_print(op_array, 0, op_array->opcodes, op_array->opcodes + op_array->last);
604#endif
605    return 0;
606}
607/* }}} */
608static void xc_optimizer_op_array_handler(zend_op_array *op_array) /* {{{ */
609{
610    TSRMLS_FETCH();
611    if (XG(optimizer)) {
612        xc_optimize_op_array(op_array TSRMLS_CC);
613    }
614}
615/* }}} */
616
617static int xc_coverager_zend_startup(zend_extension *extension) /* {{{ */
618{
619    return SUCCESS;
620}
621/* }}} */
622static void xc_coverager_zend_shutdown(zend_extension *extension) /* {{{ */
623{
624}
625/* }}} */
626/* {{{ zend extension definition structure */
627static zend_extension xc_optimizer_zend_extension_entry = {
628    XCACHE_NAME " Optimizer",
629    XCACHE_VERSION,
630    XCACHE_AUTHOR,
631    XCACHE_URL,
632    XCACHE_COPYRIGHT,
633    xc_coverager_zend_startup,
634    xc_coverager_zend_shutdown,
635    NULL,           /* activate_func_t */
636    NULL,           /* deactivate_func_t */
637    NULL,           /* message_handler_func_t */
638    xc_optimizer_op_array_handler,
639    NULL,           /* statement_handler_func_t */
640    NULL,           /* fcall_begin_handler_func_t */
641    NULL,           /* fcall_end_handler_func_t */
642    NULL,           /* op_array_ctor_func_t */
643    NULL,           /* op_array_dtor_func_t */
644    STANDARD_ZEND_EXTENSION_PROPERTIES
645};
646/* }}} */
647
648/* {{{ ini */
649PHP_INI_BEGIN()
650    STD_PHP_INI_BOOLEAN("xcache.optimizer",              "0", PHP_INI_ALL,    OnUpdateBool,        optimizer,         zend_xcache_globals, xcache_globals)
651PHP_INI_END()
652/* }}} */
653static PHP_MINFO_FUNCTION(xcache_optimizer) /* {{{ */
654{
655    php_info_print_table_start();
656    php_info_print_table_row(2, "XCache Optimizer Module", "enabled");
657    php_info_print_table_end();
658
659    DISPLAY_INI_ENTRIES();
660}
661/* }}} */
662static PHP_MINIT_FUNCTION(xcache_optimizer) /* {{{ */
663{
664    REGISTER_INI_ENTRIES();
665    return xcache_zend_extension_add(&xc_optimizer_zend_extension_entry, 0);
666}
667/* }}} */
668static PHP_MSHUTDOWN_FUNCTION(xcache_optimizer) /* {{{ */
669{
670    UNREGISTER_INI_ENTRIES();
671    return xcache_zend_extension_remove(&xc_optimizer_zend_extension_entry);
672}
673/* }}} */
674static zend_module_entry xcache_optimizer_module_entry = { /* {{{ */
675    STANDARD_MODULE_HEADER,
676    XCACHE_NAME " Optimizer",
677    NULL,
678    PHP_MINIT(xcache_optimizer),
679    PHP_MSHUTDOWN(xcache_optimizer),
680    NULL,
681    NULL,
682    PHP_MINFO(xcache_optimizer),
683    XCACHE_VERSION,
684#ifdef PHP_GINIT
685    NO_MODULE_GLOBALS,
686#endif
687#ifdef ZEND_ENGINE_2
688    NULL,
689#else
690    NULL,
691    NULL,
692#endif
693    STANDARD_MODULE_PROPERTIES_EX
694};
695/* }}} */
696int xc_optimizer_startup_module() /* {{{ */
697{
698    return zend_startup_module(&xcache_optimizer_module_entry);
699}
700/* }}} */
Note: See TracBrowser for help on using the repository browser.