From 528006a3b49fd45d6b2803c7b7843b2e7d6929d6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 13 Feb 2013 16:26:47 +0400 Subject: [PATCH] Open Source Release --- .gitignore | 87 + Optimizer/block_pass.c | 1967 +++++++++++++++++++++ Optimizer/nop_removal.c | 126 ++ Optimizer/optimize_temp_vars_5.c | 222 +++ Optimizer/pass10.c | 3 + Optimizer/pass1_5.c | 391 +++++ Optimizer/pass2.c | 210 +++ Optimizer/pass3.c | 443 +++++ Optimizer/pass5.c | 3 + Optimizer/pass9.c | 8 + Optimizer/zend_optimizer.c | 139 ++ Optimizer/zend_optimizer.h | 49 + Optimizer/zend_optimizer_internal.h | 76 + README | 171 ++ ZendAccelerator.c | 2510 +++++++++++++++++++++++++++ ZendAccelerator.h | 364 ++++ config.m4 | 345 ++++ config.w32 | 28 + shared_alloc_mmap.c | 70 + shared_alloc_posix.c | 90 + shared_alloc_shm.c | 137 ++ shared_alloc_win32.c | 315 ++++ zend_accelerator_blacklist.c | 241 +++ zend_accelerator_blacklist.h | 49 + zend_accelerator_debug.c | 101 ++ zend_accelerator_debug.h | 33 + zend_accelerator_hash.c | 222 +++ zend_accelerator_hash.h | 98 ++ zend_accelerator_module.c | 574 ++++++ zend_accelerator_module.h | 28 + zend_accelerator_util_funcs.c | 1079 ++++++++++++ zend_accelerator_util_funcs.h | 49 + zend_persist.c | 680 ++++++++ zend_persist.h | 29 + zend_persist_calc.c | 343 ++++ zend_shared_alloc.c | 473 +++++ zend_shared_alloc.h | 166 ++ 37 files changed, 11919 insertions(+) create mode 100644 .gitignore create mode 100644 Optimizer/block_pass.c create mode 100644 Optimizer/nop_removal.c create mode 100644 Optimizer/optimize_temp_vars_5.c create mode 100644 Optimizer/pass10.c create mode 100644 Optimizer/pass1_5.c create mode 100644 Optimizer/pass2.c create mode 100644 Optimizer/pass3.c create mode 100644 Optimizer/pass5.c create mode 100644 Optimizer/pass9.c create mode 100644 Optimizer/zend_optimizer.c create mode 100644 Optimizer/zend_optimizer.h create mode 100644 Optimizer/zend_optimizer_internal.h create mode 100644 README create mode 100644 ZendAccelerator.c create mode 100644 ZendAccelerator.h create mode 100644 config.m4 create mode 100644 config.w32 create mode 100644 shared_alloc_mmap.c create mode 100644 shared_alloc_posix.c create mode 100644 shared_alloc_shm.c create mode 100644 shared_alloc_win32.c create mode 100644 zend_accelerator_blacklist.c create mode 100644 zend_accelerator_blacklist.h create mode 100644 zend_accelerator_debug.c create mode 100644 zend_accelerator_debug.h create mode 100644 zend_accelerator_hash.c create mode 100644 zend_accelerator_hash.h create mode 100644 zend_accelerator_module.c create mode 100644 zend_accelerator_module.h create mode 100644 zend_accelerator_util_funcs.c create mode 100644 zend_accelerator_util_funcs.h create mode 100644 zend_persist.c create mode 100644 zend_persist.h create mode 100644 zend_persist_calc.c create mode 100644 zend_shared_alloc.c create mode 100644 zend_shared_alloc.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..8f2bec9d3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,87 @@ +# General Ignores +*~ +.#* +*. +*.slo +*.mk +*.mem +*.gcda +*.gcno +*.la +*.lo +*.o +*.a +*.ncb +*.opt +*.plg +*swp +*.patch +*.tgz +*.tar.gz +*.tar.bz2 +.FBCIndex +.FBCLockFolder +.deps +.libs +phpt.* +core +dynlib.m4 +Debug +Debug_TS +Makefile +Makefile.fragments +Makefile.objects +Makefile.global +Release +Release_TS +Release_TSDbg +Release_TS_inline +Release_inline +ZendEngine1 +_libs +acconfig.h +aclocal.m4 +acinclude.m4 +autom4te.cache +bsd_converted +buildconf.stamp +buildmk.stamp +confdefs.h +config.h +config.guess +config.cache +config.h.in +config.log +config.nice +config.nice.bat +config.status +config.sub +config_vars.mk +configuration-parser.c +configuration-parser.h +configuration-parser.output +configuration-scanner.c +configure +configure.in +configure.bat +configure.js +conftest +conftest.c +debug.log +diff +generated_lists +include +install-sh +internal_functions.c +lcov_data +lcov_html +libs +libtool +ltmain.sh +meta_cc +meta_ccld +missing +mkinstalldirs +modules +build +run-tests.php diff --git a/Optimizer/block_pass.c b/Optimizer/block_pass.c new file mode 100644 index 0000000000..2481b25475 --- /dev/null +++ b/Optimizer/block_pass.c @@ -0,0 +1,1967 @@ +#define DEBUG_BLOCKPASS 0 + +/* Checks if a constant (like "true") may be relaced by its value */ +static int zend_get_persistent_constant(char *name, uint name_len, zval *result, int copy TSRMLS_DC ELS_DC) +{ + zend_constant *c; + char *lookup_name; + int retval = 1; + ALLOCA_FLAG(use_heap); + + if (zend_hash_find(EG(zend_constants), name, name_len+1, (void **) &c) == FAILURE) { + lookup_name = DO_ALLOCA(name_len+1); + memcpy(lookup_name, name, name_len+1); + zend_str_tolower(lookup_name, name_len); + + if (zend_hash_find(EG(zend_constants), lookup_name, name_len+1, (void **) &c)==SUCCESS) { + if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) { + retval=0; + } + } else { + retval=0; + } + FREE_ALLOCA(lookup_name); + } + + if (retval) { + if(c->flags & CONST_PERSISTENT) { + *result = c->value; + if(copy) { + zval_copy_ctor(result); + } + } else { + retval = 0; + } + } + + return retval; +} + +#if DEBUG_BLOCKPASS +# define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1 + +static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt) +{ + fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes+block->len-1, block->len); + if(!block->access) { + fprintf(stderr, " unused"); + } + if(block->op1_to) { + fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes); + } + if(block->op2_to) { + fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes); + } + if(block->ext_to) { + fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes); + } + if(block->follow_to) { + fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes); + } + + if(block->sources) { + zend_block_source *bs = block->sources; + fprintf(stderr, " s:"); + while(bs) { + fprintf(stderr, " %d", bs->from->start_opline - opcodes); + bs = bs->next; + } + } + + fprintf(stderr, "\n"); + fflush(stderr); +} +#else +#define print_block(a,b,c) +#endif + +#define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1 + +/* find code blocks in op_array + code block is a set of opcodes with single flow of control, i.e. without jmps, + branches, etc. */ +static zend_code_block *find_code_blocks(zend_op_array *op_array) +{ + zend_op *opline; + zend_op *end = op_array->opcodes+op_array->last; + zend_code_block *blocks = ecalloc(op_array->last+2, sizeof(zend_code_block)); + zend_code_block *cur_block; + zend_uint opno = 0; + + opline = op_array->opcodes; + blocks[0].start_opline = opline; + blocks[0].start_opline_no = 0; + /* first find block start points */ + if(op_array->last_try_catch) { + int i; + blocks->try = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *)); + blocks->catch = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *)); + for(i=0; i< op_array->last_try_catch; i++) { + blocks->try[i] = &blocks[op_array->try_catch_array[i].try_op]; + blocks->catch[i] = &blocks[op_array->try_catch_array[i].catch_op]; + START_BLOCK_OP(op_array->try_catch_array[i].try_op); + START_BLOCK_OP(op_array->try_catch_array[i].catch_op); + blocks[op_array->try_catch_array[i].try_op].is_try = 1; + } + } + while (oplineopcode) { + case ZEND_BRK: + case ZEND_CONT: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_GOTO: +#endif + /* would not optimize non-optimized BRK/CONTs - we cannot + really know where it jumps, so these optimizations are + too dangerous */ + efree(blocks); + return NULL; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + break; +#endif + case ZEND_JMP: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + /* break missing intentionally */ + case ZEND_RETURN: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_RETURN_BY_REF: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_RET: +#endif + case ZEND_EXIT: + case ZEND_THROW: + /* start new block from this+1 */ + START_BLOCK_OP(opno+1); + break; + /* TODO: if conditional jmp depends on constant, + don't start block that won't be executed */ + case ZEND_CATCH: + START_BLOCK_OP(opline->extended_value); + START_BLOCK_OP(opno+1); + break; + case ZEND_JMPZNZ: + START_BLOCK_OP(opline->extended_value); + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_FETCH: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + START_BLOCK_OP(ZEND_OP2(opline).opline_num); + START_BLOCK_OP(opno+1); + break; + + } + opno++; + opline++; + } + + /* Build CFG (Control Flow Graph) */ + cur_block = blocks; + for(opno = 1; opno < op_array->last; opno++) { + if(blocks[opno].start_opline) { + /* found new block start */ + cur_block->len = blocks[opno].start_opline - cur_block->start_opline; + cur_block->next = &blocks[opno]; + /* what is the last OP of previous block? */ + opline = blocks[opno].start_opline-1; + switch((unsigned)opline->opcode) { + case ZEND_RETURN: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_RETURN_BY_REF: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_RET: +#endif + case ZEND_EXIT: + case ZEND_THROW: + break; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + case ZEND_JMP: + cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; + break; + case ZEND_JMPZNZ: + cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; + cur_block->ext_to = &blocks[opline->extended_value]; + break; + case ZEND_CATCH: + cur_block->ext_to = &blocks[opline->extended_value]; + cur_block->follow_to = &blocks[opno]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + case ZEND_FE_FETCH: + cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; + /* break missing intentionally */ + default: + /* next block follows this */ + cur_block->follow_to = &blocks[opno]; + break; + } + print_block(cur_block, op_array->opcodes, ""); + cur_block = cur_block->next; + } + } + cur_block->len = end - cur_block->start_opline; + cur_block->next = &blocks[op_array->last+1]; + print_block(cur_block, op_array->opcodes, ""); + + /* The op_array desn't have BRK, CONT, GOTO opcodes anyway */ + if(op_array->brk_cont_array) { + efree(op_array->brk_cont_array); + } + op_array->brk_cont_array = NULL; + op_array->last_brk_cont = 0; + + return blocks; +} + +/* CFG back references management */ + +#define ADD_SOURCE(fromb, tob) { \ + zend_block_source *__s = tob->sources; \ + while(__s && __s->from != fromb) __s = __s->next; \ + if(__s == NULL) { \ + zend_block_source *__t = emalloc(sizeof(zend_block_source)); \ + __t->next = tob->sources; \ + tob->sources = __t; \ + __t->from = fromb; \ + } \ +} + +#define DEL_SOURCE(cs) { \ + zend_block_source *__ns = (*cs)->next; \ + efree(*cs); \ + *cs = __ns; \ +} + + +static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new) +{ + /* replace all references to 'old' in 'list' with 'new' */ + zend_block_source **cs; + int found = 0; + + for(cs = &list; *cs; cs = &((*cs)->next)) { + if((*cs)->from == new) { + if(found) { + DEL_SOURCE(cs); + } else { + found = 1; + } + } + + if((*cs)->from == old) { + if(found) { + DEL_SOURCE(cs); + } else { + (*cs)->from = new; + found = 1; + } + } + } +} + +static inline void del_source(zend_code_block *from, zend_code_block *to) +{ + /* delete source 'from' from 'to'-s sources list */ + zend_block_source **cs = &to->sources; + + if(to->sources == NULL) { + to->access = 0; + return; + } + + while(*cs) { + if((*cs)->from == from) { + DEL_SOURCE(cs); + break; + } + cs = &((*cs)->next); + } + + if(to->sources == NULL) { + /* 'to' has no more sources - it's unused, will be stripped */ + to->access = 0; + return; + } + + if(to->sources->next == NULL) { + /* source to only one block */ + zend_code_block *from_block = to->sources->from; + + if(from_block->access && from_block->follow_to == to && + from_block->op1_to == NULL && + from_block->op2_to == NULL && + from_block->ext_to == NULL + ) { + /* this block follows it's only predecessor - we can join them */ + zend_op *new_to = from_block->start_opline + from_block->len; + if(new_to != to->start_opline) { + /* move block to new location */ + memmove(new_to, to->start_opline, sizeof(zend_op)*to->len); + } + /* join blocks' lengthes */ + from_block->len += to->len; + /* move 'to'`s references to 'from' */ + to->start_opline = NULL; + to->access = 0; + efree(to->sources); + to->sources = NULL; + from_block->follow_to = to->follow_to; + if(to->op1_to) { + from_block->op1_to = to->op1_to; + replace_source(to->op1_to->sources, to, from_block); + } + if(to->op2_to) { + from_block->op2_to = to->op2_to; + replace_source(to->op2_to->sources, to, from_block); + } + if(to->ext_to) { + from_block->ext_to = to->ext_to; + replace_source(to->ext_to->sources, to, from_block); + } + if(to->follow_to) { + replace_source(to->follow_to->sources, to, from_block); + } + /* remove "to" from list */ + } + } +} + +static void delete_code_block(zend_code_block *block) +{ + if(block->follow_to) { + zend_block_source *bs = block->sources; + while(bs) { + zend_code_block *from_block = bs->from; + zend_code_block *to = block->follow_to; + if(from_block->op1_to == block) { + from_block->op1_to = to; + ADD_SOURCE(from_block, to); + } + if(from_block->op2_to == block) { + from_block->op2_to = to; + ADD_SOURCE(from_block, to); + } + if(from_block->ext_to == block) { + from_block->ext_to = to; + ADD_SOURCE(from_block, to); + } + if(from_block->follow_to == block) { + from_block->follow_to = to; + ADD_SOURCE(from_block, to); + } + bs = bs->next; + } + } + block->access = 0; +} + +static void zend_access_path(zend_code_block *block) +{ + if(block->access) { + return; + } + + block->access = 1; + if(block->op1_to) { + zend_access_path(block->op1_to); + ADD_SOURCE(block, block->op1_to); + } + if(block->op2_to) { + zend_access_path(block->op2_to); + ADD_SOURCE(block, block->op2_to); + } + if(block->ext_to) { + zend_access_path(block->ext_to); + ADD_SOURCE(block, block->ext_to); + } + if(block->follow_to) { + zend_access_path(block->follow_to); + ADD_SOURCE(block, block->follow_to); + } +} + +/* Traverse CFG, mark reachable basic blocks and build back references */ +static void zend_rebuild_access_path(zend_code_block *blocks, zend_op_array *op_array, int find_start) +{ + zend_code_block *start = find_start?NULL:blocks; + zend_code_block *b; + + /* Mark all blocks as unaccessable and destroy back references */ + b = blocks; + while (b != NULL) { + zend_block_source *cs; + if (!start && b->access) { + start = b; + } + b->access = 0; + cs = b->sources; + while(cs) { + zend_block_source *n = cs->next; + efree(cs); + cs = n; + } + b->sources = NULL; + b = b->next; + } + + /* Walk thorough all pathes */ + zend_access_path(start); + + /* Add exception pathes */ + if (op_array->last_try_catch) { + int i; + for(i=0; i< op_array->last_try_catch; i++) { + if(!blocks->catch[i]->access) { + zend_access_path(blocks->catch[i]); + } + } + } +} + +/* Data dependencies macros */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# define VAR_NUM_EX(op) ((op ## _type & (IS_TMP_VAR|IS_VAR))?VAR_NUM((op).var):(op).var) + +# define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)] +# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline + +# define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0) + +#else + +# define VAR_NUM_EX(op) ((op).op_type==IS_TMP_VAR||(op).op_type==IS_VAR?VAR_NUM((op).u.var):(op).u.var) + +# define VAR_SOURCE(op) Tsource[VAR_NUM(op.u.var)] +# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(ZEND_RESULT(opline).var)] = opline + +# define VAR_UNSET(op) do { if ((op).op_type==IS_TMP_VAR||(op).op_type==IS_VAR) {VAR_SOURCE(op) = NULL;}} while (0) + +#endif + +#define convert_to_string_safe(v) \ + if(Z_TYPE_P((v)) == IS_NULL) { \ + ZVAL_STRINGL((v), "", 0, 1); \ + } else { \ + convert_to_string((v)); \ + } + +static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, char *used_ext TSRMLS_DC) +{ + zend_op *opline = block->start_opline; + zend_op *end, *last_op = NULL; + zend_op **Tsource = NULL; + + print_block(block, op_array->opcodes, "Opt "); + + /* remove leading NOPs */ + while(block->start_opline->opcode == ZEND_NOP) { + if(block->len == 1) { + /* this block is all NOPs, join with following block */ + if(block->follow_to) { + delete_code_block(block); + } + return; + } + block->start_opline++; + block->start_opline_no++; + block->len--; + } + + /* we track data dependencies only insight a single basic block */ + if(op_array->T){ + Tsource = ecalloc(op_array->T, sizeof(zend_op *)); + } + opline = block->start_opline; + end = opline+block->len; + while((op_array->T)&&(oplineop1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */ + opline->opcode != ZEND_FETCH_DIM_TMP_VAR && /* in 5.1, FETCH_DIM_TMP_VAR expects T */ + opline->opcode != ZEND_FE_RESET && + opline->opcode != ZEND_FREE + ) { + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + + /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */ + if(ZEND_OP2_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) { + zend_op *src = VAR_SOURCE(opline->op2); + VAR_UNSET(opline->op2); + COPY_NODE(opline->op2, src->op1); + MAKE_NOP(src); + } + + /* T = PRINT(X), F(T) => ECHO(X), F(1) */ + if(ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_PRINT && + opline->opcode != ZEND_CASE && opline->opcode != ZEND_FREE) { + ZEND_OP1_TYPE(opline) = IS_CONST; + LITERAL_LONG(opline->op1, 1); + } + + if(ZEND_OP2_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + VAR_SOURCE(opline->op2)->opcode == ZEND_PRINT) { + ZEND_OP2_TYPE(opline) = IS_CONST; + LITERAL_LONG(opline->op2, 1); + } + + /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ + if((opline->opcode == ZEND_ECHO || opline->opcode == ZEND_PRINT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && + VAR_SOURCE(opline->op1)->extended_value == IS_STRING) { + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + + /* T = PRINT(X), FREE(T) => ECHO(X) */ + if(opline->opcode == ZEND_FREE && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1)) { + zend_op *src = VAR_SOURCE(opline->op1); + if(src->opcode == ZEND_PRINT) { + src->opcode = ZEND_ECHO; + ZEND_RESULT_TYPE(src) = IS_UNUSED; + MAKE_NOP(opline); + } + } + + /* T = BOOL(X), FREE(T) => NOP */ + if(opline->opcode == ZEND_FREE && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1)) { + zend_op *src = VAR_SOURCE(opline->op1); + if (src->opcode == ZEND_BOOL) { + if (ZEND_OP1_TYPE(src) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(src)); + } + MAKE_NOP(src); + MAKE_NOP(opline); + } + } + +#if 0 + /* pre-evaluate functions: + constant(x) + defined(x) + function_exists(x) + extension_loaded(x) + BAD: interacts badly with Accelerator + */ + if((ZEND_OP1_TYPE(opline) & IS_VAR) && + VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL && + VAR_SOURCE(opline->op1)->extended_value == 1) { + zend_op *fcall = VAR_SOURCE(opline->op1); + zend_op *sv = fcall-1; + if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL && + ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING && + Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1 + ) { + zval *arg = &OPLINE_OP1_LITERAL(sv); + char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name; + int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len; + if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) { + zval c; + if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 0 TSRMLS_CC ELS_CC) != 0) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) || + (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0) + ) { + zend_function *function; + if(zend_hash_find(EG(function_table), Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1, (void **)&function) == SUCCESS) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) { + zval c; + if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 1 TSRMLS_CC ELS_CC) != 0) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + ZEND_OP1_LITERAL(opline) = zend_add_literal(op_array, &c TSRMLS_CC); + /* no copy ctor - get already copied it */ + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) { + if(zend_hash_exists(&module_registry, Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1)) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } + } + } +#endif + + /* IS_EQ(TRUE, X) => BOOL(X) + * IS_EQ(FALSE, X) => BOOL_NOT(X) + * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) + * IS_NOT_EQ(FALSE, X) => BOOL(X) + */ + if(opline->opcode == ZEND_IS_EQUAL || + opline->opcode == ZEND_IS_NOT_EQUAL) { + if (ZEND_OP1_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_BOOL) { + opline->opcode = + ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP1_LITERAL(opline)))? + ZEND_BOOL:ZEND_BOOL_NOT; + COPY_NODE(opline->op1, opline->op2); + SET_UNUSED(opline->op2); + } else if (ZEND_OP2_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_BOOL) { + opline->opcode = + ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP2_LITERAL(opline)))? + ZEND_BOOL:ZEND_BOOL_NOT; + SET_UNUSED(opline->op2); + } + } + + if((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_JMPZ || + opline->opcode == ZEND_JMPNZ || + opline->opcode == ZEND_JMPZNZ) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + !used_ext[VAR_NUM(ZEND_OP1(opline).var)] && + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT + ) { + /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */ + zend_op *src = VAR_SOURCE(opline->op1); + + COPY_NODE(opline->op1, src->op1); + + switch(opline->opcode) { + case ZEND_BOOL: + /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */ + opline->opcode = ZEND_BOOL_NOT; + break; + case ZEND_BOOL_NOT: + /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */ + opline->opcode = ZEND_BOOL; + break; + case ZEND_JMPZ: + /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */ + opline->opcode = ZEND_JMPNZ; + break; + case ZEND_JMPNZ: + /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */ + opline->opcode = ZEND_JMPZ; + break; + case ZEND_JMPZNZ: + { + /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */ + int op_t; + zend_code_block *op_b; + + op_t = opline->extended_value; + opline->extended_value = ZEND_OP2(opline).opline_num; + ZEND_OP2(opline).opline_num = op_t; + + op_b = block->ext_to; + block->ext_to = block->op2_to; + block->op2_to = op_b; + } + break; + } + + VAR_UNSET(opline->op1); + MAKE_NOP(src); + continue; + } else +#if 0 + /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */ + if(0 && (opline->opcode == ZEND_JMPZ_EX || + opline->opcode == ZEND_JMPNZ_EX) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var + ) { + zend_op *src = VAR_SOURCE(opline->op1); + if(opline->opcode == ZEND_JMPZ_EX) { + opline->opcode = ZEND_JMPNZ; + } else { + opline->opcode = ZEND_JMPZ; + } + COPY_NODE(opline->op1, src->op1); + SET_UNUSED(opline->result); + continue; + } else +#endif + /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */ + if((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_JMPZ || + opline->opcode == ZEND_JMPZ_EX || + opline->opcode == ZEND_JMPNZ_EX || + opline->opcode == ZEND_JMPNZ || + opline->opcode == ZEND_JMPZNZ) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + (!used_ext[VAR_NUM(ZEND_OP1(opline).var)] || + (ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) + ) { + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(opline->op1, src->op1); + + VAR_UNSET(opline->op1); + MAKE_NOP(src); + continue; + } else if(last_op && opline->opcode == ZEND_ECHO && + last_op->opcode == ZEND_ECHO && + ZEND_OP1_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE && + ZEND_OP1_TYPE(last_op) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) { + /* compress consecutive ECHO's. + * Float to string conversion may be affected by current + * locale setting. + */ + int l; + + if(Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP1_LITERAL(opline)); + } + if(Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP1_LITERAL(last_op)); + } + l = Z_STRLEN(ZEND_OP1_LITERAL(opline)) + Z_STRLEN(ZEND_OP1_LITERAL(last_op)); + if (IS_INTERNED(Z_STRVAL(ZEND_OP1_LITERAL(last_op)))) { + char *tmp = emalloc(l + 1); + memcpy(tmp, Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1); + Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = tmp; + } else { + Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = erealloc(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l+1); + } + memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op))+Z_STRLEN(ZEND_OP1_LITERAL(last_op)), Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline))); + Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0'; + zval_dtor(&ZEND_OP1_LITERAL(opline)); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_STRVAL(ZEND_OP1_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1, 1 TSRMLS_CC); + Z_TYPE(ZEND_OP1_LITERAL(last_op)) = IS_NULL; +#else + Z_STRVAL(ZEND_OP1_LITERAL(opline)) = Z_STRVAL(ZEND_OP1_LITERAL(last_op)); +#endif + Z_STRLEN(ZEND_OP1_LITERAL(opline)) = l; + MAKE_NOP(last_op); + } else if(opline->opcode == ZEND_CONCAT && + ZEND_OP2_TYPE(opline) == IS_CONST && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT || + VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_STRING) && + ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var + ) { + /* compress consecutive CONCATs */ + zend_op *src = VAR_SOURCE(opline->op1); + int l; + + if(Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); + } + if(Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(src)); + } + + VAR_UNSET(opline->op1); + if (ZEND_OP1_TYPE(src) == IS_UNUSED) { + /* 5.3 may use IS_UNUSED as first argument to ZEND_ADD_... */ + opline->opcode = ZEND_ADD_STRING; + } + COPY_NODE(opline->op1, src->op1); + l = Z_STRLEN(ZEND_OP2_LITERAL(opline)) + Z_STRLEN(ZEND_OP2_LITERAL(src)); + if (IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(src)))) { + char *tmp = emalloc(l + 1); + memcpy(tmp, Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1); + Z_STRVAL(ZEND_OP2_LITERAL(src)) = tmp; + } else { + Z_STRVAL(ZEND_OP2_LITERAL(src)) = erealloc(Z_STRVAL(ZEND_OP2_LITERAL(src)), l+1); + } + memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src))+Z_STRLEN(ZEND_OP2_LITERAL(src)), Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); + Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0'; + if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) { + efree(Z_STRVAL(ZEND_OP2_LITERAL(opline))); + } +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1, 1 TSRMLS_CC); + Z_TYPE(ZEND_OP2_LITERAL(src)) = IS_NULL; +#else + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = Z_STRVAL(ZEND_OP2_LITERAL(src)); +#endif + Z_STRLEN(ZEND_OP2_LITERAL(opline)) = l; + MAKE_NOP(src); + } else if((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_VAR) && + ZEND_OP1_TYPE(opline) == IS_CONST) { + /* convert ADD_STRING(C1, C2) to CONCAT(C1, C2) */ + opline->opcode = ZEND_CONCAT; + continue; + } else if(opline->opcode == ZEND_ADD_CHAR && ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP2_TYPE(opline) == IS_CONST) { + /* convert ADD_CHAR(C1, C2) to CONCAT(C1, C2) */ + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + opline->opcode = ZEND_CONCAT; + continue; + } else if( + (opline->opcode == ZEND_ADD || + opline->opcode == ZEND_SUB || + opline->opcode == ZEND_MUL || + opline->opcode == ZEND_DIV || + opline->opcode == ZEND_MOD || + opline->opcode == ZEND_SL || + opline->opcode == ZEND_SR || + opline->opcode == ZEND_CONCAT || + opline->opcode == ZEND_IS_EQUAL || + opline->opcode == ZEND_IS_NOT_EQUAL || + opline->opcode == ZEND_IS_SMALLER || + opline->opcode == ZEND_IS_SMALLER_OR_EQUAL || + opline->opcode == ZEND_IS_IDENTICAL || + opline->opcode == ZEND_IS_NOT_IDENTICAL || + opline->opcode == ZEND_BOOL_XOR || + opline->opcode == ZEND_BW_OR || + opline->opcode == ZEND_BW_AND || + opline->opcode == ZEND_BW_XOR) && + ZEND_OP1_TYPE(opline)==IS_CONST && + ZEND_OP2_TYPE(opline)==IS_CONST) { + /* evaluate constant expressions */ + int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode); + zval result; + int er; + + if ((opline->opcode == ZEND_DIV || + opline->opcode == ZEND_MOD) && + ((Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG && + Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) || + (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_DOUBLE && + Z_DVAL(ZEND_OP2_LITERAL(opline)) == 0.0))) { + if(RESULT_USED(opline)) { + SET_VAR_SOURCE(opline); + } + opline++; + continue; + } + er = EG(error_reporting); + EG(error_reporting) = 0; + if(binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) == SUCCESS) { + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_LITERAL(opline) = result; + SET_UNUSED(opline->op2); + + opline->opcode = ZEND_QM_ASSIGN; + } + EG(error_reporting) = er; + } else if((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline)==IS_CONST + ) { + /* evaluate constant unary ops */ + unary_op_type unary_op = get_unary_op(opline->opcode); + zval result; + + if(unary_op) { + unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC); + literal_dtor(&ZEND_OP1_LITERAL(opline)); + } else { + /* BOOL */ + result = ZEND_OP1_LITERAL(opline); + convert_to_boolean(&result); + } + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + ZEND_OP1_LITERAL(opline) = result; + opline->opcode = ZEND_QM_ASSIGN; + } else if((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN + ) { + /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } else if((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) { + /* convert T = INIT_STRING(), T = ADD_STRING(T, X) to T = QM_ASSIGN(X) */ + /* CHECKME: Remove ZEND_ADD_VAR optimizsation, since some conversions - + namely, BOOL(false)->string - don't allocate memory but use empty_string + and ADD_CHAR fails */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, opline->op2); + if(opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP1_LITERAL(opline), &c, 1, 1); + } + SET_UNUSED(opline->op2); + MAKE_NOP(src); + opline->opcode = ZEND_QM_ASSIGN; + } else if((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR || + opline->opcode == ZEND_ADD_VAR || + opline->opcode == ZEND_CONCAT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT && + ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == IS_STRING && + Z_STRLEN(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == 0) { + /* convert T = CONCAT(X,''), T = ADD_STRING(T, Y) to T = CONCAT(X,Y) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + if(opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + } + opline->opcode = ZEND_CONCAT; + literal_dtor(&ZEND_OP2_LITERAL(src)); /* will take care of empty_string too */ + MAKE_NOP(src); + } else if(opline->opcode == ZEND_ADD_VAR && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) { + /* convert T = INIT_STRING(), T = ADD_VAR(T, X) to T = CAST(STRING, X) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, opline->op2); + SET_UNUSED(opline->op2); + MAKE_NOP(src); + opline->opcode = ZEND_CAST; + opline->extended_value = IS_STRING; + } else if((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR || + opline->opcode == ZEND_ADD_VAR || + opline->opcode == ZEND_CONCAT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && + VAR_SOURCE(opline->op1)->extended_value == IS_STRING) { + /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + if(opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + } + opline->opcode = ZEND_CONCAT; + MAKE_NOP(src); + } else if(opline->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + /* strip T = QM_ASSIGN(T) */ + MAKE_NOP(opline); + } else if(opline->opcode == ZEND_BOOL && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR || + VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ + ) && !used_ext[VAR_NUM(ZEND_OP1(opline).var)] + ) { + /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */ + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + } + /* get variable source */ + if(RESULT_USED(opline)) { + SET_VAR_SOURCE(opline); + } + if(opline->opcode != ZEND_NOP) { + last_op = opline; + } + opline++; + } + + /* remove leading NOPs */ + while(block->start_opline->opcode == ZEND_NOP) { + if(block->len == 1) { + /* this block is all NOPs, join with following block */ + if(block->follow_to) { + delete_code_block(block); + } + if(op_array->T){ + efree(Tsource); + } + return; + } + block->start_opline++; + block->start_opline_no++; + block->len--; + } + + /* strip the inside NOPs */ + opline = block->start_opline; + end = opline+block->len; + while(oplineopcode == ZEND_NOP) { + zend_op *nop = opline+1; + int noplen; + while(nopopcode == ZEND_NOP) { + nop++; + } + noplen = nop-opline; + if(noplen -= noplen; + end = block->start_opline + block->len; + } + opline++; + } + + if(op_array->T){ + efree(Tsource); + } +} + +/* Rebuild plain (optimized) op_array from CFG */ +static void assemble_code_blocks(zend_code_block *blocks, zend_op_array *op_array) +{ + zend_op *new_opcodes = emalloc(op_array->last*sizeof(zend_op)); + zend_op *opline = new_opcodes; + zend_code_block *cur_block = blocks; + + /* Copy code of reachable blocks into a single buffer */ + while(cur_block) { + if(cur_block->access) { + memcpy(opline, cur_block->start_opline, cur_block->len*sizeof(zend_op)); + cur_block->start_opline = opline; + opline += cur_block->len; + if((opline-1)->opcode == ZEND_JMP) { + zend_code_block *next; + next = cur_block->next; + while(next && !next->access) { + next = next->next; + } + if(next && next == cur_block->op1_to) { + /* JMP to the next block - strip it */ + cur_block->follow_to = cur_block->op1_to; + cur_block->op1_to = NULL; + MAKE_NOP((opline-1)); + opline--; + cur_block->len--; + } + } + } else { + /* this block will not be used, delete all constants there */ + zend_op *_opl; + zend_op *end = cur_block->start_opline+cur_block->len; + for(_opl = cur_block->start_opline; _opl && _opl < end; _opl++) { + if(ZEND_OP1_TYPE(_opl) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(_opl)); + } + if(ZEND_OP2_TYPE(_opl) == IS_CONST) { + literal_dtor(&ZEND_OP2_LITERAL(_opl)); + } + } + } + cur_block = cur_block->next; + } +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if(opline[-1].opcode == ZEND_THROW) { + /* if we finished with THROW, we need to add space between THROW and HANDLE to not confuse + zend_throw_internal */ + MAKE_NOP(opline); + opline->lineno = opline[-1].lineno; + opline++; + } + MAKE_NOP(opline); + opline->opcode = ZEND_HANDLE_EXCEPTION; + opline->lineno = opline[-1].lineno; + opline++; +#endif + + op_array->last = opline-new_opcodes; + + /* adjust exception jump targets */ + if(op_array->last_try_catch) { + int i; + for(i=0; i< op_array->last_try_catch; i++) { + op_array->try_catch_array[i].try_op = blocks->try[i]->start_opline - new_opcodes; + op_array->try_catch_array[i].catch_op = blocks->catch[i]->start_opline - new_opcodes; + } + efree(blocks->try); + efree(blocks->catch); + } + + /* adjust jump targets */ + for(cur_block = blocks; cur_block; cur_block = cur_block->next) { + if(!cur_block->access) { + continue; + } + if(cur_block->op1_to) { + ZEND_OP1(&cur_block->start_opline[cur_block->len-1]).opline_num = cur_block->op1_to->start_opline - new_opcodes; + } + if(cur_block->op2_to) { + ZEND_OP2(&cur_block->start_opline[cur_block->len-1]).opline_num = cur_block->op2_to->start_opline - new_opcodes; + } + if(cur_block->ext_to) { + cur_block->start_opline[cur_block->len-1].extended_value = cur_block->ext_to->start_opline - new_opcodes; + } + print_block(cur_block, new_opcodes, "Out "); + } + efree(op_array->opcodes); + op_array->opcodes = erealloc(new_opcodes, op_array->last*sizeof(zend_op)); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* adjust early binding list */ + if (op_array->early_binding != -1) { + zend_uint *opline_num = &op_array->early_binding; + zend_op *end; + + opline = op_array->opcodes; + end = opline + op_array->last; + while (opline < end) { + if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { + *opline_num = opline - op_array->opcodes; + opline_num = &ZEND_RESULT(opline).opline_num; + } + ++opline; + } + *opline_num = -1; + } +#endif +} + +static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks) +{ + /* last_op is the last opcode of the current block */ + zend_op *last_op = (block->start_opline+block->len-1); + + switch(last_op->opcode) { + case ZEND_JMP: + { + zend_op *target = block->op1_to->start_opline; + zend_code_block *next = block->next; + + while (next && !next->access) { + /* find used one */ + next = next->next; + } + + /* JMP(next) -> NOP */ + if(block->op1_to == next) { + block->follow_to = block->op1_to; + block->op1_to = NULL; + MAKE_NOP(last_op); + block->len--; + if(block->len == 0) { + /* this block is nothing but NOP now */ + delete_code_block(block); + } + break; + } + + if(((target->opcode == ZEND_JMP && + block->op1_to != block->op1_to->op1_to) || + target->opcode == ZEND_JMPZNZ) && + !block->op1_to->is_try) { + /* JMP L, L: JMP L1 -> JMP L1 */ + /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */ + *last_op = *target; + if(ZEND_OP1_TYPE(last_op) == IS_CONST) { + zval_copy_ctor(&ZEND_OP1_LITERAL(last_op)); + } + del_source(block, block->op1_to); + if (block->op1_to->op2_to) { + block->op2_to = block->op1_to->op2_to; + ADD_SOURCE(block, block->op2_to); + } + if (block->op1_to->ext_to) { + block->ext_to = block->op1_to->ext_to; + ADD_SOURCE(block, block->ext_to); + } + if (block->op1_to->op1_to) { + block->op1_to = block->op1_to->op1_to; + ADD_SOURCE(block, block->op1_to); + } else { + block->op1_to = NULL; + } + } else if(target->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + target->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + target->opcode == ZEND_FAST_RET || +#endif + target->opcode == ZEND_EXIT) { + /* JMP L, L: RETURN to immediate RETURN */ + *last_op = *target; + if(ZEND_OP1_TYPE(last_op) == IS_CONST) { + zval_copy_ctor(&ZEND_OP1_LITERAL(last_op)); + } + del_source(block, block->op1_to); + block->op1_to = NULL; +#if 0 + /* Temporarily disabled - see bug #0025274 */ + } else if (0&& block->op1_to != block && + block->op1_to != blocks && + op_array->last_try_catch == 0 && + target->opcode != ZEND_FREE && + target->opcode != ZEND_SWITCH_FREE) { + /* Block Reordering (saves one JMP on each "for" loop iteration) + * It is disabled for some cases (ZEND_FREE/ZEND_SWITCH_FREE) + * which may break register allocation. + */ + zend_bool can_reorder = 0; + zend_block_source *cs = block->op1_to->sources; + + /* the "target" block doesn't had any followed block */ + while(cs) { + if (cs->from->follow_to == block->op1_to) { + can_reorder = 0; + break; + } + cs = cs->next; + } + if (can_reorder) { + next = block->op1_to; + /* the "target" block is not followed by current "block" */ + while (next->follow_to != NULL) { + if (next->follow_to == block) { + can_reorder = 0; + break; + } + next = next->follow_to; + } + if (can_reorder) { + zend_code_block *prev = blocks; + + while (prev->next != block->op1_to) { + prev = prev->next; + } + prev->next = next->next; + next->next = block->next; + block->next = block->op1_to; + + block->follow_to = block->op1_to; + block->op1_to = NULL; + MAKE_NOP(last_op); + block->len--; + if(block->len == 0) { + /* this block is nothing but NOP now */ + delete_code_block(block); + } + break; + } + } +#endif + } + } + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + /* constant conditional JMPs */ + if(ZEND_OP1_TYPE(last_op) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); + if (last_op->opcode == ZEND_JMPZ) { + should_jmp = !should_jmp; + } + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + ZEND_OP1_TYPE(last_op) = IS_UNUSED; + if (should_jmp) { + /* JMPNZ(true) -> JMP */ + last_op->opcode = ZEND_JMP; + COPY_NODE(last_op->op1, last_op->op2); + block->op1_to = block->op2_to; + del_source(block, block->follow_to); + block->op2_to = NULL; + block->follow_to = NULL; + } else { + /* JMPNZ(false) -> NOP */ + MAKE_NOP(last_op); + del_source(block, block->op2_to); + block->op2_to = NULL; + } + break; + } + + if(block->op2_to) { + zend_uchar same_type = ZEND_OP1_TYPE(last_op); + zend_uint same_var = VAR_NUM_EX(last_op->op1); + zend_op *target; + zend_op *target_end; + zend_code_block *target_block = block->op2_to;; + +next_target: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while(target < target_end && target->opcode == ZEND_NOP) { + target++; + } + + /* next block is only NOP's */ + if(target == target_end) { + target_block = target_block->follow_to; + goto next_target; + } else + /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */ + if(target->opcode == INV_COND(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->is_try + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZ(X, L), L: X = JMPNZ_EX(X, L2) -> JMPZ(X, L+1) */ + else if(target->opcode == INV_COND_EX(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->is_try + ) { + last_op->opcode += 3; + last_op->result = target->result; + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */ + else if(target_block->op2_to && + target->opcode == last_op->opcode && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + !target_block->is_try + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */ + else if(target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->is_try + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */ + else if(target_block->op2_to && + target_block->ext_to && + target->opcode == ZEND_JMPZNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + ! target_block->is_try + ) { + del_source(block, block->op2_to); + if (last_op->opcode == ZEND_JMPZ) { + block->op2_to = target_block->op2_to; + } else { + block->op2_to = target_block->ext_to; + } + ADD_SOURCE(block, block->op2_to); + } + } + + if (block->follow_to && + (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) { + zend_op *target; + zend_op *target_end; + + while (1) { + target = block->follow_to->start_opline; + target_end = block->follow_to->start_opline + block->follow_to->len; + while (target < target_end && target->opcode == ZEND_NOP) { + target++; + } + + /* next block is only NOP's */ + if(target == target_end) { + del_source(block, block->follow_to); + block->follow_to = block->follow_to->follow_to; + ADD_SOURCE(block, block->follow_to); + } else { + break; + } + } + /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ + if (target->opcode == ZEND_JMP && + block->follow_to->op1_to && + !block->follow_to->is_try) { + del_source(block, block->follow_to); + if (last_op->opcode == ZEND_JMPZ) { + block->ext_to = block->follow_to->op1_to; + ADD_SOURCE(block, block->ext_to); + } else { + block->ext_to = block->op2_to; + block->op2_to = block->follow_to->op1_to; + ADD_SOURCE(block, block->op2_to); + } + block->follow_to = NULL; + last_op->opcode = ZEND_JMPZNZ; + } + } + break; + + case ZEND_JMPNZ_EX: + case ZEND_JMPZ_EX: + /* constant conditional JMPs */ + if(ZEND_OP1_TYPE(last_op) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); + if (last_op->opcode == ZEND_JMPZ_EX) { + should_jmp = !should_jmp; + } + if (!should_jmp) { + /* T = JMPZ_EX(true,L) -> T = QM_ASSIGN(true) + * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false) + */ + last_op->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(last_op->op2); + del_source(block, block->op2_to); + block->op2_to = NULL; + } + break; + } + + if(block->op2_to) { + zend_op *target, *target_end; + char *same_t=NULL; + zend_code_block *target_block; + int var_num = 0; + if(op_array->T >= (zend_uint)op_array->last_var){ + var_num = op_array->T; + } else { + var_num = op_array->last_var; + } + if( var_num <= 0 ) { + return; + } + same_t = ecalloc(var_num, sizeof(char)); + if(same_t == NULL){ + return; + } + same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op); + same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op); + target_block = block->op2_to; +next_target_ex: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while(target < target_end && target->opcode == ZEND_NOP) { + target++; + } + /* next block is only NOP's */ + if(target == target_end) { + target_block = target_block->follow_to; + goto next_target_ex; + } else + /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ + if(target_block->op2_to && + target->opcode == last_op->opcode-3 && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->is_try + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */ + } else if(target_block->op2_to && + target->opcode == INV_EX_COND(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->is_try + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */ + } else if(target_block->op2_to && + target->opcode == INV_EX_COND_EX(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ + } else if(target_block->op2_to && + target->opcode == last_op->opcode && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */ + } else if(target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */ + } else if(target_block->op2_to && + target_block->ext_to && + target->opcode == ZEND_JMPZNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->is_try + ) { + del_source(block, block->op2_to); + if (last_op->opcode == ZEND_JMPZ_EX) { + block->op2_to = target_block->op2_to; + } else { + block->op2_to = target_block->ext_to; + } + ADD_SOURCE(block, block->op2_to); + } + if(same_t != NULL){ + efree(same_t); + } + } + break; + + case ZEND_JMPZNZ: { + zend_code_block *next = block->next; + + while (next && !next->access) { + /* find first accessed one */ + next = next->next; + } + + if(ZEND_OP1_TYPE(last_op) == IS_CONST) { + if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) { + /* JMPZNZ(false,L1,L2) -> JMP(L1) */ + zend_code_block *todel; + + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->op2_to; + todel = block->ext_to; + block->op2_to = NULL; + block->ext_to = NULL; + del_source(block, todel); + } else { + /* JMPZNZ(true,L1,L2) -> JMP(L2) */ + zend_code_block *todel; + + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->ext_to; + todel = block->op2_to; + block->op2_to = NULL; + block->ext_to = NULL; + del_source(block, todel); + } + } else if (block->op2_to == block->ext_to) { + /* both goto the same one - it's JMP */ + /* JMPZNZ(?,L,L) -> JMP(L) */ + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->op2_to; + block->op2_to = NULL; + block->ext_to = NULL; + } else if (block->op2_to == next) { + /* jumping to next on Z - can follow to it and jump only on NZ */ + /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */ + last_op->opcode = ZEND_JMPNZ; + block->op2_to = block->ext_to; + block->follow_to = next; + block->ext_to = NULL; + /* no need to add source - it's block->op2_to */ + } else if (block->ext_to == next) { + /* jumping to next on NZ - can follow to it and jump only on Z */ + /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */ + last_op->opcode = ZEND_JMPZ; + block->follow_to = next; + block->ext_to = NULL; + /* no need to add source - it's block->ext_to */ + } + + if(last_op->opcode == ZEND_JMPZNZ && block->op2_to) { + zend_uchar same_type = ZEND_OP1_TYPE(last_op); + zend_uchar same_var = VAR_NUM_EX(last_op->op1); + zend_op *target; + zend_op *target_end; + zend_code_block *target_block = block->op2_to; + +next_target_znz: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while(target < target_end && target->opcode == ZEND_NOP) { + target++; + } + /* next block is only NOP's */ + if(target == target_end) { + target_block = target_block->follow_to; + goto next_target_znz; + /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ + } else if(target_block->op2_to && + (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ + else if(target->opcode == ZEND_JMPNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } + /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */ + else if(target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->is_try) { + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + } + } + break; + } + } +} + +/* Global data dependencies */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# define T_USAGE(op) do { \ + if((op ## _type & (IS_VAR | IS_TMP_VAR)) && \ + !defined_here[VAR_NUM(op.var)] && !used_ext[VAR_NUM(op.var)]) { \ + used_ext[VAR_NUM(op.var)] = 1; \ + } \ + } while (0) + +# define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !usage[VAR_NUM(op.var)]) /* !used_ext[op.var] && */ +# define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result)) + +#else + +# define T_USAGE(op) do { \ + if((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && \ + !defined_here[VAR_NUM(op.u.var)] && !used_ext[VAR_NUM(op.u.var)]) { \ + used_ext[VAR_NUM(op.u.var)] = 1; \ + } \ + } while (0) + +# define NEVER_USED(op) ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && !usage[VAR_NUM(op.u.var)]) /* !used_ext[op.u.var] && */ +# define RES_NEVER_USED(opline) (ZEND_RESULT_TYPE(opline) == IS_UNUSED || NEVER_USED(opline->result)) + +#endif + +/* Find a set of variables which are used outside of the block where they are + * defined. We won't apply some optimization patterns for sush variables. */ +static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, char *used_ext) +{ + zend_code_block *next_block = block->next; + char *usage; + char *defined_here; + + if(op_array->T == 0) { + /* shortcut - if no Ts, nothing to do */ + return; + } + + usage = ecalloc(op_array->T, 1); + defined_here = emalloc(op_array->T); + + while(next_block) { + zend_op *opline = next_block->start_opline; + zend_op *end = opline+next_block->len; + + if(!next_block->access) { + next_block = next_block->next; + continue; + } + memset(defined_here, 0, op_array->T); + + while(oplineop1); + T_USAGE(opline->op2); + + if(RESULT_USED(opline)) { + if(!defined_here[VAR_NUM(ZEND_RESULT(opline).var)] && !used_ext[VAR_NUM(ZEND_RESULT(opline).var)] && + (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT || + (opline->opcode == ZEND_OP_DATA && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR) || + opline->opcode == ZEND_ADD_ARRAY_ELEMENT)) { + /* these opcodes use the result as argument */ + used_ext[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + defined_here[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + opline++; + } + next_block = next_block->next; + } + +#if DEBUG_BLOCKPASS + { + int i; + for(i=0; i< op_array->T; i++) { + fprintf(stderr, "T%d: %c\n", i, used_ext[i]+'0'); + } + } +#endif + + while(block) { + zend_op *opline = block->start_opline+block->len-1; + + if(!block->access) { + block = block->next; + continue; + } + + memcpy(usage, used_ext, op_array->T); + + while(opline>=block->start_opline) { + /* usage checks */ + if(RES_NEVER_USED(opline)) { + switch(opline->opcode) { + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: + case ZEND_ASSIGN: + case ZEND_ASSIGN_REF: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + if(ZEND_RESULT_TYPE(opline) == IS_VAR) { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED; +#else + ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED; +#endif + } + break; + case ZEND_QM_ASSIGN: + case ZEND_BOOL: + case ZEND_BOOL_NOT: + if(ZEND_OP1_TYPE(opline) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + } + MAKE_NOP(opline); + break; + case ZEND_PRINT: + opline->opcode = ZEND_ECHO; + ZEND_RESULT_TYPE(opline) = IS_UNUSED; + break; + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + opline->opcode -= 3; + SET_UNUSED(opline->result); + break; + } + } + + if(opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT || + opline->opcode == ZEND_ADD_ARRAY_ELEMENT) { + if(ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + } else { + if(RESULT_USED(opline)) { + usage[VAR_NUM(ZEND_RESULT(opline).var)] = 0; + } + } + + if(ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_OP1(opline).var)] = 1; + } + if(ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_OP2(opline).var)] = 1; + } + + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if((ZEND_RESULT_TYPE(opline) & IS_VAR) && + (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) && + usage[VAR_NUM(ZEND_RESULT(opline).var)]) { + ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED; + } +#else + if(ZEND_RESULT_TYPE(opline) == IS_VAR && + usage[VAR_NUM(ZEND_RESULT(opline).var)] && + (ZEND_RESULT(opline).EA.type & EXT_TYPE_UNUSED) != 0) { + ZEND_RESULT(opline).EA.type &= ~EXT_TYPE_UNUSED; + } +#endif + + opline--; + } + block = block->next; + } /* end blocks */ + efree(defined_here); + efree(usage); +} + +#define PASSES 3 + +static void zend_block_optimization(zend_op_array *op_array TSRMLS_DC) +{ + zend_code_block *blocks, *cur_block; + int pass; + char *usage; + +#if DEBUG_BLOCKPASS + fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name?op_array->function_name:"main"); + fflush(stderr); +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + return; + } +#endif + + /* Build CFG */ + blocks = find_code_blocks(op_array); + if(!blocks) { + return; + } + + zend_rebuild_access_path(blocks, op_array, 0); + /* full rebuild here to produce correct sources! */ + usage = emalloc(op_array->T); + for(pass=0; pass< PASSES; pass++) { + /* Compute data dependencies */ + memset(usage, 0, op_array->T); + zend_t_usage(blocks, op_array, usage); + + /* optimize each basic block separately */ + for(cur_block = blocks; cur_block; cur_block = cur_block->next) { + if(!cur_block->access) { + continue; + } + zend_optimize_block(cur_block, op_array, usage TSRMLS_CC); + } + + /* Jump optimization for each block */ + for(cur_block = blocks; cur_block; cur_block = cur_block->next) { + if(!cur_block->access) { + continue; + } + zend_jmp_optimization(cur_block, op_array, blocks); + } + + /* Eliminate unreachable basic blocks */ + zend_rebuild_access_path(blocks, op_array, 1); + } + + assemble_code_blocks(blocks, op_array); + efree(usage); + + /* Destroy CFG */ + for(cur_block = blocks; cur_block; cur_block = cur_block->next) { + zend_block_source *cs = cur_block->sources; + while(cs) { + zend_block_source *n = cs->next; + efree(cs); + cs = n; + } + } + efree(blocks); +} diff --git a/Optimizer/nop_removal.c b/Optimizer/nop_removal.c new file mode 100644 index 0000000000..49ffe0e6e5 --- /dev/null +++ b/Optimizer/nop_removal.c @@ -0,0 +1,126 @@ +/* pass 10: + * - remove NOPs + */ + +static void nop_removal(zend_op_array *op_array) +{ + zend_op *end, *opline; + zend_uint new_count, i, shift; + int j; + zend_uint *shiftlist; + ALLOCA_FLAG(use_heap); + + shiftlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint) * op_array->last); + i = new_count = shift = 0; + end = op_array->opcodes+op_array->last; + for (opline = op_array->opcodes; opline < end; opline++) { + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* GOTO target is unresolved yet. We can't optimize. */ + if (opline->opcode == ZEND_GOTO && + Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { + /* TODO: in general we can avoid this restriction */ + FREE_ALLOCA(shiftlist); + return; + } +#endif + + /* Kill JMP-over-NOP-s */ + if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) { + /* check if there are only NOPs under the branch */ + zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1; + + while (target->opcode == ZEND_NOP) { + target--; + } + if (target == opline) { + /* only NOPs */ + opline->opcode = ZEND_NOP; + } + } + + shiftlist[i++] = shift; + if (opline->opcode == ZEND_NOP) { + shift++; + } else { + if (shift) { + op_array->opcodes[new_count] = *opline; + } + new_count++; + } + } + + if (shift) { + op_array->last = new_count; + end = op_array->opcodes + op_array->last; + + /* update JMPs */ + for (opline = op_array->opcodes; oplineopcode) { + case ZEND_JMP: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_GOTO: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_FETCH: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; + break; + case ZEND_JMPZNZ: + ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; + opline->extended_value -= shiftlist[opline->extended_value]; + break; + case ZEND_CATCH: + opline->extended_value -= shiftlist[opline->extended_value]; + break; + } + } + + /* update brk/cont array */ + for (i=0; ilast_brk_cont; i++) { + op_array->brk_cont_array[i].brk -= shiftlist[op_array->brk_cont_array[i].brk]; + op_array->brk_cont_array[i].cont -= shiftlist[op_array->brk_cont_array[i].cont]; + op_array->brk_cont_array[i].start -= shiftlist[op_array->brk_cont_array[i].start]; + } + + /* update try/catch array */ + for (j=0; jlast_try_catch; j++) { + op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; + op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->try_catch_array[j].finally_op) { + op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; + op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; + } +#endif + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* update early binding list */ + if (op_array->early_binding != -1) { + zend_uint *opline_num = &op_array->early_binding; + + do { + *opline_num -= shiftlist[*opline_num]; + opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num; + } while (*opline_num != -1); + } +#endif + } + FREE_ALLOCA(shiftlist); +} diff --git a/Optimizer/optimize_temp_vars_5.c b/Optimizer/optimize_temp_vars_5.c new file mode 100644 index 0000000000..4e2865ead4 --- /dev/null +++ b/Optimizer/optimize_temp_vars_5.c @@ -0,0 +1,222 @@ +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +/* ops that use CLs: +op1: +ZEND_FETCH_CONSTANT: +ZEND_INIT_CTOR_CALL: +ZEND_INIT_STATIC_METHOD_CALL: +ZEND_INIT_METHOD_CALL: +ZEND_IMPORT_CLASS: +ZEND_IMPORT_FUNCTION: +ZEND_IMPORT_CONST: +ZEND_ADD_INTERFACE: +ZEND_VERIFY_ABSTRACT_CLASS: +ZEND_NEW: +ZEND_CATCH: +ZEND_INIT_FCALL_BY_NAME: + +op2: +ZEND_UNSET_VAR: +ZEND_ISSET_ISEMPTY_VAR: +ZEND_FETCH_UNSET: +ZEND_FETCH_IS: +ZEND_FETCH_R: +ZEND_FETCH_W: +ZEND_FETCH_RW: +ZEND_FETCH_FUNC_ARG: +ZEND_ADD_INTERFACE: +ZEND_INSTANCEOF: + +extended_value: +ZEND_DECLARE_INHERITED_CLASS: + +ignore result +INIT_METHOD_CALL: +*/ + +#define OP1_CONST_IS_CLASS 1 +#define OP2_CONST_IS_CLASS 2 +#define EXT_CONST_IS_CLASS 4 +#define RESULT_IS_UNUSED 8 + +static const char op_const_means_class[256] = { + /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 32 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + /* 64 */ + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, + /* 96 */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 128 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 160 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 192 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 224 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + +#define GET_AVAILABLE_T() \ + for (i=0; i max) { \ + max = i; \ + } + +static void optimize_temporary_variables(zend_op_array *op_array) +{ + int T = op_array->T; + char *taken_T; /* T index in use */ + zend_op **start_of_T; /* opline where T is first used */ + char *valid_T; /* Is the map_T valid */ + int *map_T; /* Map's the T to its new index */ + zend_op *opline, *end; + int currT; + int i; + int max = -1; + int var_to_free = -1; + + taken_T = (char *) emalloc(T); + start_of_T = (zend_op **) emalloc(T*sizeof(zend_op *)); + valid_T = (char *) emalloc(T); + map_T = (int *) emalloc(T*sizeof(int)); + + end = op_array->opcodes; + opline = &op_array->opcodes[op_array->last-1]; + + /* Find T definition points */ + while (opline >= end) { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) { + if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) { + start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline; + } + } +#else + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { + start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline; + } +#endif + opline--; + } + + memset(valid_T, 0, T); + memset(taken_T, 0, T); + + end = op_array->opcodes; + opline = &op_array->opcodes[op_array->last-1]; + + while (opline >= end) { + if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + || ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST) +#endif + ) { + currT = VAR_NUM(ZEND_OP1(opline).var); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT]); + } + + /* Skip OP_DATA */ + if (opline->opcode == ZEND_OP_DATA && + (opline-1)->opcode == ZEND_ASSIGN_DIM) { + opline--; + continue; + } + + if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + || ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST) +#endif + ) { + currT = VAR_NUM(ZEND_OP2(opline).var); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP2(opline).var = NUM_VAR(map_T[currT]); + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) { +#else + if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS || + opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { +#endif + currT = VAR_NUM(opline->extended_value); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + opline->extended_value = NUM_VAR(map_T[currT]); + } + + /* Allocate OP_DATA->op2 after "opernds", but before "result" */ + if (opline->opcode == ZEND_ASSIGN_DIM && + (opline+1)->opcode == ZEND_OP_DATA && + ZEND_OP2_TYPE(opline+1) & (IS_VAR | IS_TMP_VAR)) { + currT = VAR_NUM(ZEND_OP2(opline+1).var); + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + taken_T[i] = 0; + ZEND_OP2(opline+1).var = NUM_VAR(i); + var_to_free = i; + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) { + if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) { +#else + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { +#endif + currT = VAR_NUM(ZEND_RESULT(opline).var); + if (valid_T[currT]) { + if (start_of_T[currT] == opline) { + taken_T[map_T[currT]] = 0; + } + ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]); + } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */ + GET_AVAILABLE_T(); + + if (RESULT_UNUSED(opline)) { + taken_T[i] = 0; + } else { + /* Code which gets here is using a wrongly built opcode such as RECV() */ + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_RESULT(opline).var = NUM_VAR(i); + } +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + } +#endif + } + + if (var_to_free >= 0) { + taken_T[var_to_free] = 0; + var_to_free = -1; + } + + opline--; + } + + efree(taken_T); + efree(start_of_T); + efree(valid_T); + efree(map_T); + op_array->T = max+1; +} diff --git a/Optimizer/pass10.c b/Optimizer/pass10.c new file mode 100644 index 0000000000..3bfcec643a --- /dev/null +++ b/Optimizer/pass10.c @@ -0,0 +1,3 @@ +if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) { + nop_removal(op_array); +} diff --git a/Optimizer/pass1_5.c b/Optimizer/pass1_5.c new file mode 100644 index 0000000000..247b1b8632 --- /dev/null +++ b/Optimizer/pass1_5.c @@ -0,0 +1,391 @@ +/* pass 1 + * - substitute persistent constants (true, false, null, etc) + * - perform compile-time evaluation of constant binary and unary operations + * - optimize series of ADD_STRING and/or ADD_CHAR + * - convert CAST(IS_BOOL,x) into BOOL(x) + * - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL + */ + +if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { + int i=0; + zend_op *opline = op_array->opcodes; + zend_op *end = opline + op_array->last; + + while (oplineopcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + case ZEND_CONCAT: + case ZEND_IS_EQUAL: + case ZEND_IS_NOT_EQUAL: + case ZEND_IS_SMALLER: + case ZEND_IS_SMALLER_OR_EQUAL: + case ZEND_IS_IDENTICAL: + case ZEND_IS_NOT_IDENTICAL: + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + case ZEND_BOOL_XOR: + if (ZEND_OP1_TYPE(opline)==IS_CONST + && ZEND_OP2_TYPE(opline)==IS_CONST) { + /* binary operation wheit constant operands */ + int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode); + zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ + zval result; + zend_op *tmp_opline; + int er; + + if (opline->opcode == ZEND_DIV && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG && + Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) { + /* div by 0 */ + break; + } + er = EG(error_reporting); + EG(error_reporting) = 0; + /* evaluate constant expression */ + if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) != SUCCESS) { + EG(error_reporting) = er; + break; + } + EG(error_reporting) = er; + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + + /* substitute the following TMP_VAR usage with constant */ + for (tmp_opline=opline+1; tmp_oplineopcode==ZEND_FREE) { + MAKE_NOP(tmp_opline); + zval_dtor(&result); + } else { + ZEND_OP1_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC); + if (Z_TYPE(result) == IS_STRING) { + Z_HASH_P(&ZEND_OP1_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP1_LITERAL(tmp_opline))+1); + if (tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || + tmp_opline->opcode == ZEND_DO_FCALL || + tmp_opline->opcode == ZEND_CATCH || + tmp_opline->opcode == ZEND_FETCH_CONSTANT) { + op_array->literals[tmp_opline->op1.constant].cache_slot = op_array->last_cache_slot++; + } + } +#else + ZEND_OP1_LITERAL(tmp_opline) = result; +#endif + } + /* TMP_VAR my be used only once */ + break; + } + if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR + && ZEND_OP2(tmp_opline).var == tv) { + ZEND_OP2_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC); + if (Z_TYPE(result) == IS_STRING) { + Z_HASH_P(&ZEND_OP2_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP2_LITERAL(tmp_opline))+1); + if (tmp_opline->opcode == ZEND_FETCH_R || + tmp_opline->opcode == ZEND_FETCH_W || + tmp_opline->opcode == ZEND_FETCH_RW || + tmp_opline->opcode == ZEND_FETCH_IS || + tmp_opline->opcode == ZEND_FETCH_UNSET || + tmp_opline->opcode == ZEND_FETCH_FUNC_ARG || + tmp_opline->opcode == ZEND_FETCH_CLASS || + tmp_opline->opcode == ZEND_INIT_FCALL_BY_NAME || + tmp_opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || + tmp_opline->opcode == ZEND_UNSET_VAR || + tmp_opline->opcode == ZEND_ISSET_ISEMPTY_VAR || + tmp_opline->opcode == ZEND_ADD_INTERFACE || + tmp_opline->opcode == ZEND_ADD_TRAIT) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot++; + } else if (tmp_opline->opcode == ZEND_INIT_METHOD_CALL || + tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || + tmp_opline->opcode == ZEND_FETCH_CONSTANT || + tmp_opline->opcode == ZEND_ASSIGN_OBJ || + tmp_opline->opcode == ZEND_FETCH_OBJ_R || + tmp_opline->opcode == ZEND_FETCH_OBJ_W || + tmp_opline->opcode == ZEND_FETCH_OBJ_RW || + tmp_opline->opcode == ZEND_FETCH_OBJ_IS || + tmp_opline->opcode == ZEND_FETCH_OBJ_UNSET || + tmp_opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || + tmp_opline->opcode == ZEND_UNSET_OBJ || + tmp_opline->opcode == ZEND_PRE_INC_OBJ || + tmp_opline->opcode == ZEND_PRE_DEC_OBJ || + tmp_opline->opcode == ZEND_POST_INC_OBJ || + tmp_opline->opcode == ZEND_POST_DEC_OBJ || + tmp_opline->opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } else if (tmp_opline->opcode == ZEND_ASSIGN_ADD || + tmp_opline->opcode == ZEND_ASSIGN_SUB || + tmp_opline->opcode == ZEND_ASSIGN_MUL || + tmp_opline->opcode == ZEND_ASSIGN_DIV || + tmp_opline->opcode == ZEND_ASSIGN_MOD || + tmp_opline->opcode == ZEND_ASSIGN_SL || + tmp_opline->opcode == ZEND_ASSIGN_SR || + tmp_opline->opcode == ZEND_ASSIGN_CONCAT || + tmp_opline->opcode == ZEND_ASSIGN_BW_OR || + tmp_opline->opcode == ZEND_ASSIGN_BW_AND || + tmp_opline->opcode == ZEND_ASSIGN_BW_XOR) { + if (tmp_opline->extended_value == ZEND_ASSIGN_OBJ) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } + } + } +#else + ZEND_OP2_LITERAL(tmp_opline) = result; +#endif + break; + } + } + } + break; + + case ZEND_CAST: + if (ZEND_OP1_TYPE(opline)==IS_CONST && + opline->extended_value != IS_ARRAY && + opline->extended_value != IS_OBJECT && + opline->extended_value != IS_RESOURCE) { + /* cast of constant operand */ + zval res; + res = ZEND_OP1_LITERAL(opline); + zval_copy_ctor(&res); + switch (opline->extended_value) { + case IS_NULL: + convert_to_null(&res); + break; + case IS_BOOL: + convert_to_boolean(&res); + break; + case IS_LONG: + convert_to_long(&res); + break; + case IS_DOUBLE: + convert_to_double(&res); + break; + case IS_STRING: + convert_to_string(&res); + break; + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + opline->opcode = ZEND_QM_ASSIGN; + opline->extended_value = 0; + ZEND_OP1_LITERAL(opline) = res; + SET_UNUSED(opline->op2); + } else if (opline->extended_value == IS_BOOL) { + /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ + opline->opcode = ZEND_BOOL; + opline->extended_value = 0; + } + break; + + case ZEND_BW_NOT: + case ZEND_BOOL_NOT: + if (ZEND_OP1_TYPE(opline)==IS_CONST) { + /* unary operation on constant operand */ + unary_op_type unary_op = get_unary_op(opline->opcode); + zval result; + zend_op *tmp_opline; + zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ + int er; + + er = EG(error_reporting); + EG(error_reporting) = 0; + if (unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC) != SUCCESS) { + EG(error_reporting) = er; + break; + } + EG(error_reporting) = er; + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + + /* substitute the following TMP_VAR usage with constant */ + for (tmp_opline=opline+1; tmp_oplineopcode==ZEND_FREE) { + MAKE_NOP(tmp_opline); + zval_dtor(&result); + } else { + ZEND_OP1_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC); +#else + ZEND_OP1_LITERAL(tmp_opline) = result; +#endif + } + break; + } + if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR + && ZEND_OP2(tmp_opline).var == tv) { + ZEND_OP2_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC); +#else + ZEND_OP2_LITERAL(tmp_opline) = result; +#endif + break; + } + } + } + break; + + case ZEND_ADD_STRING: + case ZEND_ADD_CHAR: + { + zend_op *next_op = opline+1; + int requires_conversion = (opline->opcode==ZEND_ADD_CHAR?1:0); + size_t final_length = 0; + char *ptr; + zend_op *last_op; + + /* There is always a ZEND_RETURN at the end + if (next_op>=end) { + break; + } + */ + while (next_op->opcode == ZEND_ADD_STRING || next_op->opcode == ZEND_ADD_CHAR) { + if (ZEND_RESULT(opline).var != ZEND_RESULT(next_op).var) { + break; + } + if (next_op->opcode == ZEND_ADD_CHAR) { + final_length += 1; + } else { /* ZEND_ADD_STRING */ + final_length += ZEND_OP2_LITERAL(next_op).value.str.len; + } + next_op++; + } + if (final_length == 0) { + break; + } + last_op = next_op; + final_length += (requires_conversion ? 1 : ZEND_OP2_LITERAL(opline).value.str.len); + ptr = (char *) emalloc(final_length+1); + ptr[final_length] = '\0'; + if (requires_conversion) { /* ZEND_ADD_CHAR */ + char chval = (char)ZEND_OP2_LITERAL(opline).value.lval; + + ZEND_OP2_LITERAL(opline).value.str.val = ptr; + ptr[0] = chval; + ZEND_OP2_LITERAL(opline).type = IS_STRING; + opline->opcode = ZEND_ADD_STRING; + ptr++; + } else { /* ZEND_ADD_STRING */ + memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); + if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) { + efree(Z_STRVAL(ZEND_OP2_LITERAL(opline))); + } + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = ptr; + ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline)); + } + ZEND_OP2_LITERAL(opline).value.str.len = final_length; + next_op = opline+1; + while (next_op < last_op) { + if (next_op->opcode == ZEND_ADD_STRING) { + memcpy(ptr, ZEND_OP2_LITERAL(next_op).value.str.val, ZEND_OP2_LITERAL(next_op).value.str.len); + ptr += ZEND_OP2_LITERAL(next_op).value.str.len; + literal_dtor(&ZEND_OP2_LITERAL(next_op)); + } else { /* ZEND_ADD_CHAR */ + *ptr = (char)ZEND_OP2_LITERAL(next_op).value.lval; + ptr++; + } + MAKE_NOP(next_op); + next_op++; + } + if (!((ZEND_OPTIMIZER_PASS_5|ZEND_OPTIMIZER_PASS_10) & OPTIMIZATION_LEVEL)) { + /* NOP removel is disabled => insert JMP over NOPs */ + if (last_op-opline >= 3) { /* If we have more than 2 NOPS then JMP over them */ + (opline+1)->opcode = ZEND_JMP; + ZEND_OP1(opline+1).opline_num = last_op - op_array->opcodes; /* that's OK even for ZE2, since opline_num's are resolved in pass 2 later */ + } + } + } + break; + + case ZEND_FETCH_CONSTANT: + if (ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && + Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__")-1 && + memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1) == 0) { + /* substitute __COMPILER_HALT_OFFSET__ constant */ + zend_bool orig_in_execution = EG(in_execution); + zend_op_array *orig_op_array = EG(active_op_array); + zval offset; + + EG(in_execution) = 1; + EG(active_op_array) = op_array; + if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) { + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op1.constant = zend_add_literal(op_array, &offset TSRMLS_CC); +#else + ZEND_OP1_LITERAL(opline) = offset; +#endif + SET_UNUSED(opline->op2); + opline->opcode = ZEND_QM_ASSIGN; + } + EG(active_op_array) = orig_op_array; + EG(in_execution) = orig_in_execution; + break; + } + + if (ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST && + ZEND_OP2_LITERAL(opline).type == IS_STRING) { + /* substitute persistent constants */ + zval c; + + if(zend_get_persistent_constant(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), &c, 1 TSRMLS_CC) == 0) { + break; + } + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op1.constant = zend_add_literal(op_array, &c TSRMLS_CC); +#else + ZEND_OP1_LITERAL(opline) = c; +#endif + SET_UNUSED(opline->op2); + opline->opcode = ZEND_QM_ASSIGN; + } + break; + + case ZEND_INIT_FCALL_BY_NAME: + if(opline->extended_value == 0 /* not method */ && + ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST) { + if((opline+1)->opcode == ZEND_DO_FCALL_BY_NAME && + (opline+1)->extended_value == 0) { + (opline+1)->opcode = ZEND_DO_FCALL; + COPY_NODE((opline+1)->op1, opline->op2); + zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1))); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_HASH_P(&ZEND_OP1_LITERAL(opline+1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1))+1); + op_array->literals[(opline+1)->op1.constant].cache_slot = op_array->last_cache_slot++; +#endif + MAKE_NOP(opline); + } + } + break; + } + opline++; + i++; + } +} diff --git a/Optimizer/pass2.c b/Optimizer/pass2.c new file mode 100644 index 0000000000..ef6c5ac0c3 --- /dev/null +++ b/Optimizer/pass2.c @@ -0,0 +1,210 @@ +/* pass 2: + * - convert non-numeric constants to numeric constants in numeric operators + * - optimize constant conditional JMPs + * - optimize static BRKs and CONTs + */ + +if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) { + zend_op *opline; + zend_op *end = op_array->opcodes+op_array->last; + + opline = op_array->opcodes; + while (oplineopcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type == IS_STRING) { + convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC); + } + } + /* break missing *intentionally* - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + if(opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type == IS_STRING) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC); + } + } + break; + + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type != IS_LONG) { + convert_to_long(&ZEND_OP1_LITERAL(opline)); + } + } + /* break missing *intentionally - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + if(opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type != IS_LONG) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } + } + break; + + case ZEND_CONCAT: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type != IS_STRING) { + convert_to_string(&ZEND_OP1_LITERAL(opline)); + } + } + /* break missing *intentionally - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_CONCAT: + if(opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); + } + } + break; + + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ + if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + opline->opcode -= 3; + /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) + in case we know it wouldn't jump */ + } else if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); + if (opline->opcode == ZEND_JMPZ_EX) { + should_jmp = !should_jmp; + } + if (!should_jmp) { + opline->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(opline->op2); + } + } + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); + + if (opline->opcode == ZEND_JMPZ) { + should_jmp = !should_jmp; + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_UNUSED; + if (should_jmp) { + opline->opcode = ZEND_JMP; + COPY_NODE(opline->op1, opline->op2); + } else { + MAKE_NOP(opline); + } + break; + } + if ((opline+1)->opcode == ZEND_JMP) { + /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ + /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ + if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline+1).opline_num) { + /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ + MAKE_NOP(opline); + } else { + if (opline->opcode == ZEND_JMPZ) { + opline->extended_value = ZEND_OP1(opline+1).opline_num; + } else { + opline->extended_value = ZEND_OP2(opline).opline_num; + COPY_NODE(opline->op2, (opline+1)->op1); + } + opline->opcode = ZEND_JMPZNZ; + } + } + break; + + case ZEND_JMPZNZ: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int opline_num; + + if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { + opline_num = opline->extended_value; /* JMPNZ */ + } else { + opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */ + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + ZEND_OP1(opline).opline_num = opline_num; + ZEND_OP1_TYPE(opline) = IS_UNUSED; + opline->opcode = ZEND_JMP; + } + break; + + case ZEND_BRK: + case ZEND_CONT: + { + zend_brk_cont_element *jmp_to; + int array_offset; + int nest_levels; + int dont_optimize=0; + + if (ZEND_OP2_TYPE(opline) != IS_CONST) { + break; + } + convert_to_long(&ZEND_OP2_LITERAL(opline)); + nest_levels = ZEND_OP2_LITERAL(opline).value.lval; + + array_offset = ZEND_OP1(opline).opline_num; + while (1) { + if (array_offset==-1) { + dont_optimize=1; /* don't optimize this bogus break/continue, let the executor shout */ + break; + } + jmp_to = &op_array->brk_cont_array[array_offset]; + array_offset = jmp_to->parent; + if (--nest_levels > 0) { + if (opline->opcode == ZEND_BRK && + (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE)) { + dont_optimize=1; + break; + } + } else { + break; + } + } + + if (dont_optimize) { + break; + } + + /* optimize - convert to a JMP */ + switch (opline->opcode) { + case ZEND_BRK: + MAKE_NOP(opline); + ZEND_OP1(opline).opline_num = jmp_to->brk; + break; + case ZEND_CONT: + MAKE_NOP(opline); + ZEND_OP1(opline).opline_num = jmp_to->cont; + break; + } + opline->opcode = ZEND_JMP; + /* MAKE_NOP() already set op1 and op2 to IS_UNUSED */ + } + break; + } + opline++; + } +} diff --git a/Optimizer/pass3.c b/Optimizer/pass3.c new file mode 100644 index 0000000000..95a6aad9bf --- /dev/null +++ b/Optimizer/pass3.c @@ -0,0 +1,443 @@ +/* pass 3: + * - optimize $i = $i+expr to $i+=expr + * - optimize series of JMPs + * - change $i++ to ++$i where possible + */ + +/* compares opcodes with allowing oc1 be _EX of oc2 */ +#define SAME_OPCODE_EX(oc1, oc2) ((oc1 == oc2) || (oc1 == ZEND_JMPZ_EX && oc2 == ZEND_JMPZ) || (oc1 == ZEND_JMPNZ_EX && oc2 == ZEND_JMPNZ)) + +/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ +#define CHECK_JMP(target, label) \ + for (i=0; iopcodes[target]).opline_num) { \ + goto label; \ + } \ + } \ + jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num; + +#define CHECK_JMP2(target, label) \ + for (i=0; iopcodes[target]).opline_num) { \ + goto label; \ + } \ + } \ + jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num; + +if(ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) { + zend_op *opline; + zend_op *end = op_array->opcodes+op_array->last; + zend_uint *jmp_hitlist; + int jmp_hitlist_count; + int i; + zend_uint opline_num = 0; + ALLOCA_FLAG(use_heap); + + jmp_hitlist = (zend_uint *) DO_ALLOCA(sizeof(zend_uint)*op_array->last); + opline = op_array->opcodes; + + while (oplineopcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_MOD: + case ZEND_CONCAT: + case ZEND_SL: + case ZEND_SR: + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + { + zend_op *next_opline = opline+1; + + while (next_opline < end && next_opline->opcode == ZEND_NOP) { + ++next_opline; + } + + if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { + break; + } + + if ((ZEND_OP2_TYPE(opline)==IS_VAR || ZEND_OP2_TYPE(opline)==IS_CV) + && ZEND_OP2(opline).var == ZEND_OP1(next_opline).var && + (opline->opcode == ZEND_ADD || + opline->opcode == ZEND_MUL || + opline->opcode == ZEND_BW_OR || + opline->opcode == ZEND_BW_AND || + opline->opcode == ZEND_BW_XOR)) { + /* change $i=expr+$i to $i=$i+expr so that the next + * optimization works on it + */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_uchar tmp_type=opline->op1_type; + znode_op tmp=opline->op1; +#else + znode tmp=opline->op1; +#endif + + if(opline->opcode != ZEND_ADD || ZEND_OP1_TYPE(opline) == IS_CONST) { + /* protection from array add: $a = array + $a is not commutative! */ + COPY_NODE(opline->op1, opline->op2); + COPY_NODE(opline->op2, tmp); + } + } + if ((ZEND_OP1_TYPE(opline)==IS_VAR || ZEND_OP1_TYPE(opline)==IS_CV) + && ZEND_OP1(opline).var == ZEND_OP1(next_opline).var + && ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) { + switch (opline->opcode) { + case ZEND_ADD: + opline->opcode = ZEND_ASSIGN_ADD; + break; + case ZEND_SUB: + opline->opcode = ZEND_ASSIGN_SUB; + break; + case ZEND_MUL: + opline->opcode = ZEND_ASSIGN_MUL; + break; + case ZEND_DIV: + opline->opcode = ZEND_ASSIGN_DIV; + break; + case ZEND_MOD: + opline->opcode = ZEND_ASSIGN_MOD; + break; + case ZEND_CONCAT: + opline->opcode = ZEND_ASSIGN_CONCAT; + break; + case ZEND_SL: + opline->opcode = ZEND_ASSIGN_SL; + break; + case ZEND_SR: + opline->opcode = ZEND_ASSIGN_SR; + break; + case ZEND_BW_OR: + opline->opcode = ZEND_ASSIGN_BW_OR; + break; + case ZEND_BW_AND: + opline->opcode = ZEND_ASSIGN_BW_AND; + break; + case ZEND_BW_XOR: + opline->opcode = ZEND_ASSIGN_BW_XOR; + break; + } + COPY_NODE(opline->result, next_opline->result); + MAKE_NOP(next_opline); + opline++; + opline_num++; + } + } + break; + + case ZEND_JMP: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + /* convert L: JMP L+1 to NOP */ + if(ZEND_OP1(opline).opline_num == opline_num + 1) { + MAKE_NOP(opline); + goto done_jmp_optimization; + } + + /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ + while (ZEND_OP1(opline).opline_numlast + && op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) { + int target = ZEND_OP1(opline).opline_num; + CHECK_JMP(target, done_jmp_optimization); + ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } + break; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + while (ZEND_OP2(opline).opline_numlast) { + int target = ZEND_OP2(opline).opline_num; + if (op_array->opcodes[target].opcode == ZEND_JMP) { + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } else { + break; + } + } + break; +#endif + + case ZEND_JMPZ: + case ZEND_JMPNZ: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + /* convert L: JMPZ L+1 to NOP */ + if(ZEND_OP2(opline).opline_num == opline_num + 1) { + MAKE_NOP(opline); + goto done_jmp_optimization; + } + + while (ZEND_OP2(opline).opline_numlast) { + int target = ZEND_OP2(opline).opline_num; + + if (op_array->opcodes[target].opcode == ZEND_JMP) { + /* plain JMP */ + /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ + CHECK_JMP(target, done_jmp_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } else if (op_array->opcodes[target].opcode == opline->opcode && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* same opcode and same var as this opcode */ + /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */ + CHECK_JMP2(target, done_jmp_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else if (op_array->opcodes[target].opcode == opline->opcode+3 && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to + T = JMPZ_EX(X, L2) */ + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3; + COPY_NODE(opline->result, op_array->opcodes[target].result); + break; + } else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to + JMPZ(X,L1+1) */ + ZEND_OP2(opline).opline_num = target+1; + break; + } else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to + T = JMPZ_EX(X,L1+1) */ + ZEND_OP2(opline).opline_num = target+1; + opline->opcode += 3; + COPY_NODE(opline->result, op_array->opcodes[target].result); + break; + } else { + break; + } + } + break; + + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_uchar T_type = opline->result_type; + znode_op T = opline->result; +#else + znode T = opline->result; +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ + /* convert L: T = JMPZ_EX T,L+1 to NOP */ + if(ZEND_OP2(opline).opline_num == opline_num + 1) { + if(ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + MAKE_NOP(opline); + } else { + opline->opcode = ZEND_BOOL; + SET_UNUSED(opline->op2); + } + goto done_jmp_optimization; + } + + while (ZEND_OP2(opline).opline_numlast) { + int target = ZEND_OP2(opline).opline_num; + if(SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) && + SAME_VAR(op_array->opcodes[target].op1, T)) { + /* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */ + if(op_array->opcodes[target].opcode == opline->opcode) { + /* change T only if we have _EX opcode there */ + COPY_NODE(T, op_array->opcodes[target].result); + } + CHECK_JMP2(target, continue_jmp_ex_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else if(op_array->opcodes[target].opcode == ZEND_JMPZNZ && + SAME_VAR(op_array->opcodes[target].op1, T)) { + /* Check for JMPZNZ with same cond variable */ + int new_target; + CHECK_JMP2(target, continue_jmp_ex_optimization); + if(opline->opcode == ZEND_JMPZ_EX) { + new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else { + /* JMPNZ_EX */ + new_target = op_array->opcodes[target].extended_value; + } + ZEND_OP2(opline).opline_num = new_target; + } else if((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) || + op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode) + ) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to + JMPZ_EX(X,L1+1) */ + ZEND_OP2(opline).opline_num = target+1; + break; + } else { + break; + } + } /* while */ +continue_jmp_ex_optimization: + break; +#if 0 + /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */ + { + zend_op *op; + for(op = opline+1; opopcode == ZEND_JMP || + op->opcode == ZEND_JMPZ || + op->opcode == ZEND_JMPZ_EX || + op->opcode == ZEND_JMPNZ || + op->opcode == ZEND_JMPNZ_EX || + op->opcode == ZEND_JMPZNZ || + op->opcode == ZEND_BRK || + op->opcode == ZEND_CONT || + op->opcode == ZEND_CASE || + op->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + op->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + op->opcode == ZEND_FAST_RET || +#endif + op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_EXIT) { + break; + } + + if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && + ZEND_OP1(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + + if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && + ZEND_OP2(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + } /* for */ + + for(op = &op_array->opcodes[ZEND_OP2(opline).opline_num]; opopcode == ZEND_JMP || + op->opcode == ZEND_JMPZ || + op->opcode == ZEND_JMPZ_EX || + op->opcode == ZEND_JMPNZ || + op->opcode == ZEND_JMPNZ_EX || + op->opcode == ZEND_JMPZNZ || + op->opcode == ZEND_BRK || + op->opcode == ZEND_CONT || + op->opcode == ZEND_CASE || + op->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + op->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + op->opcode == ZEND_FAST_RET || +#endif + op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_EXIT) { + break; + } + + if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && + ZEND_OP1(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + + if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && + ZEND_OP2(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + } + + opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ + SET_UNUSED(opline->result); + break; + } +#endif + } + break; + + case ZEND_JMPZNZ: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ + while (ZEND_OP2(opline).opline_num < op_array->last + && op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) { + int target = ZEND_OP2(opline).opline_num; + CHECK_JMP(target, continue_jmpznz_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } +continue_jmpznz_optimization: + /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ + while (opline->extended_value < op_array->last + && op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) { + int target = opline->extended_value; + CHECK_JMP(target, done_jmp_optimization); + opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } + break; + + case ZEND_POST_INC: + case ZEND_POST_DEC: { + /* POST_INC, FREE => PRE_INC */ + zend_op *next_op = opline+1; + + if (next_op>=end) { + break; + } + if (next_op->opcode == ZEND_FREE + && ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) { + MAKE_NOP(next_op); + switch (opline->opcode) { + case ZEND_POST_INC: + opline->opcode = ZEND_PRE_INC; + break; + case ZEND_POST_DEC: + opline->opcode = ZEND_PRE_DEC; + break; + } +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED; +#else + ZEND_RESULT_TYPE(opline) = IS_VAR; + ZEND_RESULT(opline).EA.type = 0; + ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED; +#endif + } + } + break; + } +done_jmp_optimization: + opline++; + opline_num++; + } + FREE_ALLOCA(jmp_hitlist); +} diff --git a/Optimizer/pass5.c b/Optimizer/pass5.c new file mode 100644 index 0000000000..b0d651a5fc --- /dev/null +++ b/Optimizer/pass5.c @@ -0,0 +1,3 @@ +if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) { + zend_block_optimization(op_array TSRMLS_CC); +} diff --git a/Optimizer/pass9.c b/Optimizer/pass9.c new file mode 100644 index 0000000000..586160c14d --- /dev/null +++ b/Optimizer/pass9.c @@ -0,0 +1,8 @@ +/* pass 9 + * + * - optimize usage of temporary variables + */ + +if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) { + optimize_temporary_variables(op_array); +} diff --git a/Optimizer/zend_optimizer.c b/Optimizer/zend_optimizer.c new file mode 100644 index 0000000000..b2b0c8878c --- /dev/null +++ b/Optimizer/zend_optimizer.c @@ -0,0 +1,139 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "Optimizer/zend_optimizer.h" +#include "Optimizer/zend_optimizer_internal.h" +#include "zend_API.h" +#include "zend_constants.h" +#include "zend_execute.h" + +#define OPTIMIZATION_LEVEL \ + ZCG(accel_directives).optimization_level + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC) +{ + int i = op_array->last_literal; + op_array->last_literal++; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + { + if (i >= CG(context).literals_size) { + CG(context).literals_size += 16; /* FIXME */ + op_array->literals = (zend_literal*)erealloc(op_array->literals, CG(context).literals_size * sizeof(zend_literal)); + } + } +#else + if (i >= op_array->size_literal) { + op_array->size_literal += 16; /* FIXME */ + op_array->literals = (zend_literal*)erealloc(op_array->literals, op_array->size_literal * sizeof(zend_literal)); + } +#endif + op_array->literals[i].constant = *zv; + Z_SET_REFCOUNT(op_array->literals[i].constant, 2); + Z_SET_ISREF(op_array->literals[i].constant); + return i; +} + +# define LITERAL_LONG(op, val) do { \ + zval _c; \ + ZVAL_LONG(&_c, val); \ + op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \ + } while (0) + +# define LITERAL_BOOL(op, val) do { \ + zval _c; \ + ZVAL_BOOL(&_c, val); \ + op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \ + } while (0) + +# define literal_dtor(zv) do { \ + zval_dtor(zv); \ + Z_TYPE_P(zv) = IS_NULL; \ + } while (0) + +#define COPY_NODE(target, src) do { \ + target ## _type = src ## _type; \ + target = src; \ + } while (0) + +#else + +# define LITERAL_LONG(op, val) ZVAL_LONG(&op.u.constant, val) + +# define LITERAL_BOOL(op, val) ZVAL_BOOL(&op.u.constant, val) + +# define literal_dtor(zv) zval_dtor(zv) + +#define COPY_NODE(target, src) do { \ + target = src; \ + } while (0) + +#endif + +#include "Optimizer/nop_removal.c" +#include "Optimizer/block_pass.c" +#include "Optimizer/optimize_temp_vars_5.c" + +void zend_optimizer(zend_op_array *op_array TSRMLS_DC) +{ + if (op_array->type == ZEND_EVAL_CODE || + (op_array->fn_flags & ZEND_ACC_INTERACTIVE)) { + return; + } + + /* pass 1 + * - substitute persistent constants (true, false, null, etc) + * - perform compile-time evaluation of constant binary and unary operations + * - optimize series of ADD_STRING and/or ADD_CHAR + * - convert CAST(IS_BOOL,x) into BOOL(x) + * - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL + */ +#include "Optimizer/pass1_5.c" + + /* pass 2: + * - convert non-numeric constants to numeric constants in numeric operators + * - optimize constant conditional JMPs + * - optimize static BRKs and CONTs + */ +#include "Optimizer/pass2.c" + + /* pass 3: + * - optimize $i = $i+expr to $i+=expr + * - optimize series of JMPs + * - change $i++ to ++$i where possible + */ +#include "Optimizer/pass3.c" + + /* pass 5: + * - CFG optimization + */ +#include "Optimizer/pass5.c" + + /* pass 9: + * - Optimize temp variables usage + */ +#include "Optimizer/pass9.c" + + /* pass 10: + * - remove NOPs + */ +#include "Optimizer/pass10.c" +} diff --git a/Optimizer/zend_optimizer.h b/Optimizer/zend_optimizer.h new file mode 100644 index 0000000000..0c06916874 --- /dev/null +++ b/Optimizer/zend_optimizer.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_OPTIMIZER_H +#define ZEND_OPTIMIZER_H + +#include "zend.h" +#include "zend_compile.h" + +#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */ +#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jums */ +#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */ +#define ZEND_OPTIMIZER_PASS_4 (1<<3) +#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */ +#define ZEND_OPTIMIZER_PASS_6 (1<<5) +#define ZEND_OPTIMIZER_PASS_7 (1<<6) +#define ZEND_OPTIMIZER_PASS_8 (1<<7) +#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */ +#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */ +#define ZEND_OPTIMIZER_PASS_11 (1<<10) +#define ZEND_OPTIMIZER_PASS_12 (1<<11) +#define ZEND_OPTIMIZER_PASS_13 (1<<12) +#define ZEND_OPTIMIZER_PASS_14 (1<<13) + +#define ZEND_OPTIMIZER_ALL_PASSES 0xFFFFFFFF + +#define DEFAULT_OPTIMIZATION_LEVEL "0xFFFFFFFF" + +void zend_optimizer(zend_op_array *op_array TSRMLS_DC); + +#endif diff --git a/Optimizer/zend_optimizer_internal.h b/Optimizer/zend_optimizer_internal.h new file mode 100644 index 0000000000..113cbe9826 --- /dev/null +++ b/Optimizer/zend_optimizer_internal.h @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_OPTIMIZER_INTERNAL_H +#define ZEND_OPTIMIZER_INTERNAL_H + +#include "ZendAccelerator.h" + +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO +# define VAR_NUM(v) (EX_TMP_VAR_NUM(0, 0) - EX_TMP_VAR(0, v)) +# define NUM_VAR(v) ((zend_uint)EX_TMP_VAR_NUM(0, v)) +#else +# define VAR_NUM(v) ((v)/(sizeof(temp_variable))) +# define NUM_VAR(v) ((v)*(sizeof(temp_variable))) +#endif + +#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ) +#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ) +#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX) +#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX) + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(opline->result)); memset(&opline->op1,0,sizeof(opline->op1)); memset(&opline->op2,0,sizeof(opline->op2)); opline->result_type=opline->op1_type=opline->op2_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; } +# define RESULT_USED(op) (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR) +# define RESULT_UNUSED(op) ((op->result_type & EXT_TYPE_UNUSED) != 0) +# define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var) +#else +# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(znode)); memset(&opline->op1,0,sizeof(znode)); memset(&opline->op2,0,sizeof(znode)); opline->result.op_type=opline->op1.op_type=opline->op2.op_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; } +# define RESULT_USED(op) ((op->result.op_type == IS_VAR && (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || (op->result.op_type == IS_TMP_VAR)) +# define RESULT_UNUSED(op) ((op->result.op_type == IS_VAR) && (op->result.u.EA.type == EXT_TYPE_UNUSED)) +# define SAME_VAR(op1, op2) (((op1.op_type == IS_VAR && op2.op_type == IS_VAR) || (op1.op_type == IS_TMP_VAR && op2.op_type == IS_TMP_VAR)) && op1.u.var == op2.u.var) +#endif + +typedef struct _zend_code_block zend_code_block; +typedef struct _zend_block_source zend_block_source; + +struct _zend_code_block { + int access; + zend_op *start_opline; + int start_opline_no; + int len; + zend_code_block *op1_to; + zend_code_block *op2_to; + zend_code_block *ext_to; + zend_code_block *follow_to; + zend_code_block *next; + zend_block_source *sources; + zend_code_block **try; + zend_code_block **catch; + zend_bool is_try; +}; + +struct _zend_block_source { + zend_code_block *from; + zend_block_source *next; +}; + +#endif diff --git a/README b/README new file mode 100644 index 0000000000..69273d0624 --- /dev/null +++ b/README @@ -0,0 +1,171 @@ +The Zend Optimizer+ +=================== + +The Zend Optimizer+ provides faster PHP execution through opcode caching and +optimization. It improves PHP performance by storing precompiled script +bytecode in the shared memory. This eliminates the stages of reading code from +the disk and compiling it on future access. In addition it applies a few +bytecode optimization patterns that make code execution faster. + +Compatibility +------------- + +This version of Zend Optimizer+ is compatible with PHP 5.2.*, 5.3.*, 5.4.* +and PHP-5.5 development branch. PHP 5.2 support may be removed in the future. + +Quick Install +------------- + +- Compile + +export PHP_DIR=/usr/local/php5.5 +PHP_AUTOCONF=autoconf $PHP_DIR/bin/phpize +./configure \ + --enable-optimizer-plus \ + --with-php-config=$PHP_DIR/bin/php-config +make + +- Install + +cp .libs/ZendOptimizerPlus.so $PHP_DIR/lib/ZendOptimizerPlus.so + +- Edit php.ini + +zend_extensin=/...full path.../ZendOptimizerPlus.so + +- Restart PHP + +Speed Tunning +------------- + +We reccomend the following configuration options for best performance. + +zend_optimizerplus.memory_consumption=128 +zend_optimizerplus.interned_strings_buffer=8 +zend_optimizerplus.max_accelerated_files=4000 +zend_optimizerplus.revalidate_freq=60 +zend_optimizerplus.save_comments=0 +zend_optimizerplus.fast_shutdown=1 +zend_optimizerplus.enable_file_override=1 +zend_optimizerplus.enable_cli=1 + +In some cases you may like to prefer enabling/disabling some features +to avoid incompatibilities at the cost of some performance degradation. + +Configuration Directives +------------------------ + +zend_optimizerplus.enable (default "1") + Optimizer+ On/Off switch. When set to Off, code is not optimized. + +zend_optimizerplus.memory_consumption (default "64") + The Optimizer+ shared memory storage size. The amount of memory for storing + precompiled PHP code in Mbytes. + +zend_optimizerplus.interned_strings_buffer (default "4") + The amount of memory for interned strings in Mbytes. + +zend_optimizerplus.max_accelerated_files (default "2000") + The maximum number of keys (scripts) in the Optimizer+ hash table. + The number is actually the the first one in the following set of prime + numbers that is bigger than the one supplied: { 223, 463, 983, 1979, 3907, + 7963, 16229, 32531, 65407, 130987 }. Only numbers between 200 and 100000 + are allowed. + +zend_optimizerplus.max_wasted_percentage (default "5") + The maximum percentage of "wasted" memory until a restart is scheduled + +zend_optimizerplus.use_cwd (default "1") + When this directive is enabled, the Optimizer+ appends the current working + directory to the script key, thus elminating possible collisions between + files with the same name (basename). Disablingthe directive improves + performance, but may break existing applications. + +zend_optimizerplus.validate_timestamps (default "1") + When disabled, you must reset the Optimizer+ manually or restart the + webserver for changes to the filesystem to take effect. + The frequancy of the check is controlled by the directive + "zend_optimizerplus.revalidate_freq" + +zend_optimizerplus.revalidate_freq (default "2") + How often (in seconds) to check file timestamps for changes to the shared + memory storage allocation. + +zend_optimizerplus.revalidate_path (default "0") + Enables or disables file search in include_path optimization + If the file search is disabled and a cached file is found that uses + the same include_path, the file is not searched again. Thus, if a file + with the same name appears somewhere else in include_path, it + won't be found. Enable this directive if this optimization has an effect on + your applications. The default for this directive is disabled, which means + that optimization is active. + +zend_optimizerplus.save_comments (default "1") + If disabled, all PHPDoc comments are dropped from the code to reduce the + size of the optimized code. + +zend_optimizerplus.fast_shutdown (default "0") + If enabled, a fast shutdown sequence is used for the accelerated code + The fast shutdown sequence doesn't free each allocated block, but lets + the Zend Engine Memory Manager do the work. + +zend_optimizerplus.enable_file_override (default "0") + Allow file existance override (file_exists, etc.) performance feature + +zend_optimizerplus.optimization_level (default "0xffffffff") + A bitmask, where each bit enables or disables the appropriate Optimizer+ + passes + +zend_optimizerplus.inherited_hack (default "1") + Enable this hack as a workaround for "can't redeclare class" errors. + The Optimizer+ stores the places where DECLARE_CLASS opcodes use + inheritance (These are the only opcodes that can be executed by PHP, + but which may not be executed because the parent class is missing due to + optimization). When the file is loaded, Optimizer+ tries to bind the + inherited classes by using the current environment. The problem with this + scenario is that, while the DECLARE_CLASS opcode may not be needed for the + current script, if the script requires that the opcode at least be defined, + it may not run. The default for this directive is disabled, which means + that optimization is active. In php-5.3 and above this hack is not needed + anymore and this setting has no effect. + +zend_optimizerplus.dups_fix (default "0") + Enable this hack as a workaround for "duplicate definition" errors + +zend_optimizerplus.blacklist_filename + The location of the Optimizer+ blacklist file + The Optimizer+ blacklist file is a text file that holds the names of files + that should not be accelerated. The file format is to add each filename + to a new line. The filename may be a full path or just a file prefix + (i.e., /var/www/x blacklists all the files and directories in /var/www + that start with 'x'). Files are usually triggered by one of the following + three reasons: + 1) Directories that contain auto generated code, like Smarty or ZFW cache. + 2) Code that does not work well when accelerated, due to some delayed + compile time evaluation. + 3) Code that triggers an Optimizer+ bug. + +zend_optimizerplus.consistency_checks (default "0") + Check the cache checksum each N requests. + The default value of "0" means that the checks are disabled. + Because calculating the checksum impairs performance, this directive should + be enabled only as part of a debugging process. + +zend_optimizerplus.force_restart_timeout (default "180") + How long to wait (in seconds) for a scheduled restart to begin if the cache + is not being accessed. + The Optimizer+ uses this directive to identify a situation where there may + be a problem with a process. After this time period has passed, the + Optimizer+ assumes that something has happened and starts killing the + processes that still hold the locks that are preventing a restart. + If the log level is 3 or above, a "killed locker" error is recorded + in the Apache logs when this happens. + +zend_optimizerplus.error_log + Optimizer+ error_log file name. Empty string assumes "stderr" + +zend_optimizerplus.log_verbosity_level (default "1") + Alll Optimizer+ errors go to the Web server log. + By default, only fatal errors (level 0) or errors (level 1) are logged. + You can also enable warnings (level 2), info messages (level 3) or + debug messesges (level 4). diff --git a/ZendAccelerator.c b/ZendAccelerator.c new file mode 100644 index 0000000000..8b2a6ae48e --- /dev/null +++ b/ZendAccelerator.c @@ -0,0 +1,2510 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "main/php.h" +#include "main/php_globals.h" +#include "zend.h" +#include "zend_extensions.h" +#include "zend_compile.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_module.h" +#include "zend_accelerator_blacklist.h" +#include "zend_list.h" +#include "zend_execute.h" +#include "main/SAPI.h" +#include "main/php_streams.h" +#include "main/php_open_temporary_file.h" +#include "zend_API.h" +#include "zend_ini.h" +#include "TSRM/tsrm_virtual_cwd.h" +#include "zend_accelerator_util_funcs.h" +#include "zend_accelerator_hash.h" + +#ifndef ZEND_WIN32 +#include +#endif + +#ifdef ZEND_WIN32 +typedef int uid_t; +typedef int gid_t; +#include +#endif + +#ifndef ZEND_WIN32 +# include +#else +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include + +#ifndef ZEND_WIN32 +# include +# include +#endif + +#include +#include + +#define MIN_FREE_MEMORY 64*1024 + +#define SHM_PROTECT() \ + do { \ + if (ZCG(accel_directives).protect_memory) { \ + zend_accel_shared_protect(1 TSRMLS_CC); \ + } \ + } while (0) +#define SHM_UNPROTECT() \ + do { \ + if (ZCG(accel_directives).protect_memory) { \ + zend_accel_shared_protect(0 TSRMLS_CC); \ + } \ + } while (0) + +ZEND_EXTENSION(); + +#ifndef ZTS +zend_accel_globals accel_globals; +#else +int accel_globals_id; +#endif + +/* Points to the structure shared across all PHP processes */ +zend_accel_shared_globals *accel_shared_globals = NULL; + +/* true globals, no need for thread safety */ +static char *zps_failure_reason = NULL; +char *zps_api_failure_reason = NULL; + +static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC); +static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +static char *(*accelerator_orig_zend_resolve_path)(const char *filename, int filename_len TSRMLS_DC); +#endif +static void (*orig_chdir)(INTERNAL_FUNCTION_PARAMETERS) = NULL; +static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL; + +#ifdef ZEND_WIN32 +# define INCREMENT(v) InterlockedIncrement(&ZCSG(v)) +# define DECREMENT(v) InterlockedDecrement(&ZCSG(v)) +# define LOCKVAL(v) (ZCSG(v)) +#endif + +#ifdef ZEND_WIN32 +static time_t zend_accel_get_time(void) +{ + FILETIME now; + GetSystemTimeAsFileTime(&now); + + return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000); +} +#else +# define zend_accel_get_time() time(NULL) +#endif + +static inline int is_stream_path(const char *filename) +{ + const char *p; + + for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + return ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')); +} + +/* O+ overrides PHP chdir() function and remembers the current working directory + * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and + * avoid getcwd() call. + */ +static ZEND_FUNCTION(accel_chdir) +{ + char cwd[MAXPATHLEN]; + + orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + ZCG(cwd_len) = strlen(cwd); + ZCG(cwd) = estrndup(cwd, ZCG(cwd_len)); + } else { + if (ZCG(cwd)) { + efree(ZCG(cwd)); + ZCG(cwd) = NULL; + } + } +} + +static inline char* accel_getcwd(int *cwd_len TSRMLS_DC) +{ + if (ZCG(cwd)) { + *cwd_len = ZCG(cwd_len); + return ZCG(cwd); + } else { + char cwd[MAXPATHLEN + 1]; + + if (!VCWD_GETCWD(cwd, MAXPATHLEN)) { + return NULL; + } + *cwd_len = ZCG(cwd_len) = strlen(cwd); + ZCG(cwd) = estrndup(cwd, ZCG(cwd_len)); + return ZCG(cwd); + } +} + +/* O+ traks changes of "include_path" directive. It stores all the requested + * values in ZCG(include_paths) shared hash table, current value in + * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in + * ZCG(include_path_key). + */ +static ZEND_INI_MH(accel_include_path_on_modify) +{ + int ret = orig_include_path_on_modify(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + + ZCG(include_path_key) = NULL; + if (ret == SUCCESS) { + ZCG(include_path) = new_value; + if (ZCG(include_path) && *ZCG(include_path)) { + ZCG(include_path_len) = new_value_length; + + if (ZCG(startup_ok) && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + + ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1); + if (!ZCG(include_path_key) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + } + } + + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } else { + ZCG(include_path_check) = 1; + } + } else { + ZCG(include_path) = ""; + ZCG(include_path_len) = 0; + } + } + return ret; +} + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +/* Interned strings support */ +static char *orig_interned_strings_start; +static char *orig_interned_strings_end; +static const char *(*orig_new_interned_string)(const char *str, int len, int free_src TSRMLS_DC); +static void (*orig_interned_strings_snapshot)(TSRMLS_D); +static void (*orig_interned_strings_restore)(TSRMLS_D); + +/* O+ disables creation of interned strings by regular PHP compiler, instead, + * it creates interned strings in shared memory when saves a script. + * Such interned strings are shatred across all PHP processes + */ +static const char *accel_new_interned_string_for_php(const char *str, int len, int free_src TSRMLS_DC) +{ + return str; +} + +static void accel_interned_strings_snapshot_for_php(TSRMLS_D) +{ +} + +static void accel_interned_strings_restore_for_php(TSRMLS_D) +{ +} + +#ifndef ZTS +static void accel_interned_strings_restore_state(TSRMLS_D) +{ + unsigned int i; + + for (i = 0; i < ZCSG(interned_strings).nTableSize; i++) { + ZCSG(interned_strings).arBuckets[i] = ZCSG(interned_strings_saved_state).arBuckets[i]; + if (ZCSG(interned_strings).arBuckets[i]) { + ZCSG(interned_strings).arBuckets[i]->pLast = NULL; + } + } + ZCSG(interned_strings).pListHead = ZCSG(interned_strings_saved_state).pListHead; + ZCSG(interned_strings).pListTail = ZCSG(interned_strings_saved_state).pListTail; + if (ZCSG(interned_strings).pListHead) { + ZCSG(interned_strings).pListHead->pListLast = NULL; + } + if (ZCSG(interned_strings).pListTail) { + ZCSG(interned_strings).pListTail->pListNext = NULL; + } + ZCSG(interned_strings_top) = ZCSG(interned_strings_saved_state).top; +} + +static void accel_interned_strings_save_state(TSRMLS_D) +{ + ZCSG(interned_strings_saved_state).arBuckets = (Bucket**)zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + if (!ZCSG(interned_strings_saved_state).arBuckets) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + } + memcpy(ZCSG(interned_strings_saved_state).arBuckets, ZCSG(interned_strings).arBuckets, ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + ZCSG(interned_strings_saved_state).pListHead = ZCSG(interned_strings).pListHead; + ZCSG(interned_strings_saved_state).pListTail = ZCSG(interned_strings).pListTail; + ZCSG(interned_strings_saved_state).top = ZCSG(interned_strings_top); +} +#endif + +const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC) +{ +/* for now interned strings are supported only for non-ZTS build */ +#ifndef ZTS + ulong h; + uint nIndex; + Bucket *p; + + if (arKey >= ZCSG(interned_strings_start) && arKey < ZCSG(interned_strings_end)) { + /* this is already an interned string */ + return arKey; + } + + h = zend_inline_hash_func(arKey, nKeyLength); + nIndex = h & ZCSG(interned_strings).nTableMask; + + /* check for existing interned string */ + p = ZCSG(interned_strings).arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == (uint)nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + if (free_src) { + efree((char*)arKey); + } + return p->arKey; + } + } + p = p->pNext; + } + + if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >= + ZCSG(interned_strings_end)) { + /* no memory, return the same non-interned string */ + return arKey; + } + + /* create new interning string in shared interned strings buffer */ + p = (Bucket *) ZCSG(interned_strings_top); + ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength); + + p->arKey = (char*)(p+1); + memcpy((char*)p->arKey, arKey, nKeyLength); + p->nKeyLength = nKeyLength; + p->h = h; + p->pData = &p->pDataPtr; + p->pDataPtr = p; + + p->pNext = ZCSG(interned_strings).arBuckets[nIndex]; + p->pLast = NULL; + if (p->pNext) { + p->pNext->pLast = p; + } + ZCSG(interned_strings).arBuckets[nIndex] = p; + + p->pListLast = ZCSG(interned_strings).pListTail; + ZCSG(interned_strings).pListTail = p; + p->pListNext = NULL; + if (p->pListLast != NULL) { + p->pListLast->pListNext = p; + } + if (!ZCSG(interned_strings).pListHead) { + ZCSG(interned_strings).pListHead = p; + } + + ZCSG(interned_strings).nNumOfElements++; + + if (free_src) { + efree((char*)arKey); + } + + return p->arKey; +#else + return arKey; +#endif +} + +#ifndef ZTS +/* Copy PHP interned strings from PHP process memory into the shared memory */ +static void accel_use_shm_interned_strings(TSRMLS_D) +{ + Bucket *p, *q; + + /* function table hash keys */ + p = CG(function_table)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } + + /* class table hash keys, class names, properties, methods, constants, etc */ + p = CG(class_table)->pListHead; + while (p) { + zend_class_entry *ce = (zend_class_entry*)(p->pDataPtr); + + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + + if (ce->name) { + ce->name = accel_new_interned_string(ce->name, ce->name_length+1, 0 TSRMLS_CC); + } + + q = ce->properties_info.pListHead; + while (q) { + zend_property_info *info = (zend_property_info*)(q->pData); + + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + + if (info->name) { + info->name = accel_new_interned_string(info->name, info->name_length+1, 0 TSRMLS_CC); + } + + q = q->pListNext; + } + + q = ce->function_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + q = q->pListNext; + } + + q = ce->constants_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + q = q->pListNext; + } + + p = p->pListNext; + } + + /* constant hash keys */ + p = EG(zend_constants)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } + + /* auto globals hash keys and names */ + p = CG(auto_globals)->pListHead; + while (p) { + zend_auto_global *auto_global = (zend_auto_global*)p->pData; + + auto_global->name = accel_new_interned_string(auto_global->name, auto_global->name_len + 1, 0 TSRMLS_CC); + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } +} +#endif +#endif + +static inline void accel_restart_enter(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + INCREMENT(restart_in); +#else + static const FLOCK_STRUCTURE(restart_in_progress, F_WRLCK, SEEK_SET, 2, 1); + + if (fcntl(lock_file, F_SETLK, &restart_in_progress)==-1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1): %s (%d)", strerror(errno), errno); + } +#endif + ZCSG(restart_in_progress) = 1; +} + +static inline void accel_restart_leave(TSRMLS_D) +{ + ZCSG(restart_in_progress) = 0; +#ifdef ZEND_WIN32 + DECREMENT(restart_in); +#else + static const FLOCK_STRUCTURE(restart_finished, F_UNLCK, SEEK_SET, 2, 1); + + if (fcntl(lock_file, F_SETLK, &restart_finished)==-1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1): %s (%d)", strerror(errno), errno); + } +#endif +} + +static inline int accel_restart_is_active(TSRMLS_D) +{ + if(ZCSG(restart_in_progress)) { +#ifndef ZEND_WIN32 + FLOCK_STRUCTURE(restart_check, F_WRLCK, SEEK_SET, 2, 1); + + if (fcntl(lock_file, F_GETLK, &restart_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC: %s (%d)", strerror(errno), errno); + return FAILURE; + } + if (restart_check.l_type == F_UNLCK) { + ZCSG(restart_in_progress) = 0; + return 0; + } else { + return 1; + } +#else + return LOCKVAL(restart_in) != 0; +#endif + } + return 0; +} + +/* Creates a read lock for SHM access */ +static inline void accel_activate_add(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + INCREMENT(mem_usage); +#else + static const FLOCK_STRUCTURE(mem_usage_lock, F_RDLCK, SEEK_SET, 1, 1); + + if (fcntl(lock_file, F_SETLK, &mem_usage_lock)==-1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1): %s (%d)", strerror(errno), errno); + } +#endif +} + +/* Releases a lock for SHM access */ +static inline void accel_deactivate_sub(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + if (ZCG(counted)) { + DECREMENT(mem_usage); + ZCG(counted) = 0; + } +#else + static const FLOCK_STRUCTURE(mem_usage_unlock, F_UNLCK, SEEK_SET, 1, 1); + + if (fcntl(lock_file, F_SETLK, &mem_usage_unlock)==-1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1): %s (%d)", strerror(errno), errno); + } +#endif +} + +static inline void accel_unlock_all(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + accel_deactivate_sub(TSRMLS_C); +#else + static const FLOCK_STRUCTURE(mem_usage_unlock_all, F_UNLCK, SEEK_SET, 0, 0); + + if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all)==-1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll: %s (%d)", strerror(errno), errno); + } +#endif +} + +#ifndef ZEND_WIN32 +static inline void kill_all_lockers(struct flock *mem_usage_check) +{ + int tries = 10; + + /* so that other process won't try to force while we are busy cleaning up */ + ZCSG(force_restart_time) = 0; + while (mem_usage_check->l_pid > 0) { + while (tries--) { + zend_accel_error(ACCEL_LOG_INFO, "Killed locker %d", mem_usage_check->l_pid); + if (kill(mem_usage_check->l_pid, SIGKILL)) { + break; + } + /* give it a chance to die */ + usleep(20000); + if (kill(mem_usage_check->l_pid, 0)) { + /* can't kill it */ + break; + } + usleep(10000); + } + if (!tries) { + zend_accel_error(ACCEL_LOG_INFO, "Can't kill %d after 20 tries!", mem_usage_check->l_pid); + ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */ + } + + mem_usage_check->l_type = F_WRLCK; + mem_usage_check->l_whence = SEEK_SET; + mem_usage_check->l_start = 1; + mem_usage_check->l_len = 1; + mem_usage_check->l_pid = -1; + if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "KLockers: %s (%d)", strerror(errno), errno); + break; + } + + if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) { + break; + } + } +} +#endif + +static inline int accel_is_inactive(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + if (LOCKVAL(mem_usage) == 0) { + return SUCCESS; + } +#else + FLOCK_STRUCTURE(mem_usage_check, F_WRLCK, SEEK_SET, 1, 1); + + mem_usage_check.l_pid = -1; + if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC: %s (%d)", strerror(errno), errno); + return FAILURE; + } + if (mem_usage_check.l_type == F_UNLCK) { + return SUCCESS; + } + + if (ZCG(accel_directives).force_restart_timeout + && ZCSG(force_restart_time) + && time(NULL)>=ZCSG(force_restart_time)) { + zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid); + kill_all_lockers(&mem_usage_check); + + return FAILURE; /* next request should be able to restart it */ + } +#endif + + return FAILURE; +} + +static int zend_get_stream_timestamp(const char *filename, struct stat *statbuf TSRMLS_DC) +{ + php_stream_wrapper *wrapper; + php_stream_statbuf stream_statbuf; + + if(!filename) { + return FAILURE; + } + + wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC); + if(!wrapper) { + + return FAILURE; + } + if(!wrapper->wops || !wrapper->wops->url_stat) { + + statbuf->st_mtime = 1; + return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */ + } + + if(wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL TSRMLS_CC) != 0) { + return FAILURE; + } + *statbuf = stream_statbuf.sb; + return SUCCESS; +} + +#if ZEND_WIN32 +static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle) +{ + static unsigned __int64 utc_base = 0; + static FILETIME utc_base_ft; + WIN32_FILE_ATTRIBUTE_DATA fdata; + + if(!file_handle->opened_path) { + return 0; + } + + if (!utc_base) { + SYSTEMTIME st; + + st.wYear = 1970; + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + + SystemTimeToFileTime (&st, &utc_base_ft); + utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime; + } + + if(GetFileAttributesEx(file_handle->opened_path, GetFileExInfoStandard, &fdata) != 0) { + unsigned __int64 ftime; + + if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) + return 0; + + ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base; + ftime /= 10000000L; + + return (accel_time_t)ftime; + } + return 0; +} +#endif + +static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle TSRMLS_DC) +{ + struct stat statbuf; + +#ifdef ZEND_WIN32 + accel_time_t res; + + res = zend_get_file_handle_timestamp_win(file_handle); + if(res) { + return res; + } +#endif + + switch (file_handle->type) { + case ZEND_HANDLE_FD: + if (fstat(file_handle->handle.fd, &statbuf)==-1) { + return 0; + } + break; + case ZEND_HANDLE_FP: + if (fstat(fileno(file_handle->handle.fp), &statbuf) == -1) { + if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) { + return 0; + } + } + break; + case ZEND_HANDLE_FILENAME: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_HANDLE_MAPPED: +#endif + { + char *file_path = file_handle->opened_path; + + if(file_path) { + if (is_stream_path(file_path)) { + if (zend_get_stream_timestamp(file_path, &statbuf TSRMLS_CC) == SUCCESS) { + break; + } + } + if (VCWD_STAT(file_path, &statbuf) !=-1) { + break; + } + } + + if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) { + return 0; + } + break; + } + case ZEND_HANDLE_STREAM: + { + php_stream *stream = (php_stream *)file_handle->handle.stream.handle; + php_stream_statbuf sb; + int er = EG(error_reporting); + + EG(error_reporting) = 0; + if (!stream || + !stream->ops || + !stream->ops->stat || + stream->ops->stat(stream, &sb TSRMLS_CC) != 0) { + EG(error_reporting) = er; + return 0; + } + EG(error_reporting) = er; + statbuf = sb.sb; + } + break; + + default: + return 0; + } + + return statbuf.st_mtime; +} + +static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC) +{ + zend_file_handle ps_handle; + + /** check that the persistant script is indeed the same file we cached + * (if part of the path is a symlink than it possible that the user will change it) + * See bug #15140 + */ + if (file_handle->opened_path) { + if (strcmp(persistent_script->full_path,file_handle->opened_path)!=0) { + return FAILURE; + } + } else { + char actualpath [MAXPATHLEN+1]; + char *full_path_ptr; + + full_path_ptr = VCWD_REALPATH(file_handle->filename, actualpath); + if (full_path_ptr && strcmp(persistent_script->full_path,full_path_ptr)!=0) { + return FAILURE; + } + } + + if (persistent_script->timestamp == 0) { + return FAILURE; + } + + if (zend_get_file_handle_timestamp(file_handle TSRMLS_CC) == persistent_script->timestamp) { + return SUCCESS; + } + + ps_handle.type = ZEND_HANDLE_FILENAME; + ps_handle.filename = persistent_script->full_path; + ps_handle.opened_path = persistent_script->full_path; + + if (zend_get_file_handle_timestamp(&ps_handle TSRMLS_CC) == persistent_script->timestamp) { + return SUCCESS; + } + + return FAILURE; +} + +static void zend_accel_schedule_restart_if_necessary(TSRMLS_D) +{ + if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) { + zend_accel_schedule_restart(TSRMLS_C); + } +} + +static inline int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC) +{ + if (persistent_script->dynamic_members.revalidate >= ZCSG(revalidate_at)) { + return SUCCESS; + } else if (do_validate_timestamps(persistent_script, file_handle TSRMLS_CC)==FAILURE) { + return FAILURE; + } else { + persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at); + return SUCCESS; + } +} + +static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script) +{ + signed char *mem = (signed char*)persistent_script->mem; + size_t size = persistent_script->size; + size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script; + unsigned int checksum = ADLER32_INIT; + + if (mem < (signed char*)persistent_script) { + checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem); + size -= (signed char*)persistent_script - mem; + mem += (signed char*)persistent_script - mem; + } + + zend_adler32(checksum, mem, persistent_script_check_block_size); + mem += sizeof(*persistent_script); + size -= sizeof(*persistent_script); + + if (size > 0) { + checksum = zend_adler32(checksum, mem, size); + } + return checksum; +} + +/* Instead of resolving full real path name each time we need to identify file, + * we create a key that consist from requested file name, current working + * directory, current include_path, etc */ +char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC) +{ + int key_length; + + /* CWD and include_path don't matter for absolute file names and streams */ + if (ZCG(accel_directives).use_cwd && + !IS_ABSOLUTE_PATH(file_handle->filename, path_length) && + !is_stream_path(file_handle->filename)) { + char *include_path=NULL; + int include_path_len=0; + const char *parent_script = NULL; + int parent_script_len=0; + int cur_len=0; + int cwd_len; + char *cwd; + + if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) == NULL) { + /* we don't handle this well for now. */ + zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set zend_optimizerplus.use_cwd to 0 in ini file", file_handle->filename, errno); + if(file_handle->opened_path) { + cwd = file_handle->opened_path; + cwd_len = strlen(cwd); + } else { + ZCG(key_len) = 0; + return NULL; + } + } + + if (ZCG(include_path_key)) { + include_path = ZCG(include_path_key); + include_path_len = 1; + } else { + include_path = ZCG(include_path); + include_path_len = ZCG(include_path_len); + if (ZCG(include_path_check) && + ZCG(startup_ok) && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + + ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1); + if (ZCG(include_path_key)) { + include_path = ZCG(include_path_key); + include_path_len = 1; + } else if (!zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + include_path = ZCG(include_path_key); + include_path_len = 1; + } + } + + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + } + + /* Here we add to the key the parent script directory, + since fopen_wrappers from version 4.0.7 use current script's path + in include path too. + */ + if (EG(in_execution) && + (parent_script = zend_get_executed_filename(TSRMLS_C)) != NULL && + parent_script[0] != '[') { + + parent_script_len = strlen(parent_script); + while ((--parent_script_len > 0) && !IS_SLASH(parent_script[parent_script_len])); + } + + /* Calculate key length */ + key_length = cwd_len+path_length+include_path_len+2; + if (parent_script_len) { + key_length += parent_script_len+1; + } + + /* Generate key + * Note - the include_path must be the last element in the key, + * since in itself, it may include colons (which we use to separate + * different components of the key) + */ + if ((size_t)key_length >= sizeof(ZCG(key))) { + ZCG(key_len) = 0; + return NULL; + } + memcpy(ZCG(key), cwd, cwd_len); + ZCG(key)[cwd_len] = ':'; + + memcpy(ZCG(key)+cwd_len+1, file_handle->filename, path_length); + + ZCG(key)[cwd_len+1+path_length] = ':'; + + cur_len = cwd_len+1+path_length+1; + + if (parent_script_len) { + memcpy(ZCG(key)+cur_len, parent_script, parent_script_len); + cur_len += parent_script_len; + ZCG(key)[cur_len] = ':'; + cur_len++; + } + memcpy(ZCG(key)+cur_len, include_path, include_path_len); + ZCG(key)[key_length] = '\0'; + } else { + /* not use_cwd */ + key_length = path_length; + if ((size_t)key_length >= sizeof(ZCG(key))) { + ZCG(key_len) = 0; + return NULL; + } + memcpy(ZCG(key), file_handle->filename, key_length+1); + } + + *key_len = ZCG(key_len) = key_length; + return ZCG(key); +} + +static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int *key_len TSRMLS_DC) +{ + return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC); +} + +/* Adds another key for existing cached script */ +static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC) +{ + if (!zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) { + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_DEBUG,"No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + } else { + char *new_key = zend_shared_alloc(key_length+1); + if (new_key) { + memcpy(new_key, key, key_length+1); + zend_accel_hash_update(&ZCSG(hash), new_key, key_length+1, 1, bucket); + } else { + zend_accel_error(ACCEL_LOG_DEBUG,"No more memory!"); + ZSMMG(memory_exhausted) = 1; + } + } + } +} + +static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory TSRMLS_DC) +{ + zend_accel_hash_entry *bucket; + uint memory_used; + + /* Check if script may be stored in shared memory */ + if(!zend_accel_script_persistable(new_persistent_script)) { + return new_persistent_script; + } + + /* exclusive lock */ + zend_shared_alloc_lock(TSRMLS_C); + + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_DEBUG,"No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + zend_accel_schedule_restart_if_necessary(TSRMLS_C); + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + + /* Check if we still need to put the file into the cache (may be it was + * already stored by another process. This final check is done under + * exclusive lock) */ + bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len+1); + if (bucket) { + zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data; + + if (!existing_persistent_script->corrupted) { + if (!ZCG(accel_directives).validate_timestamps || + (new_persistent_script->timestamp == existing_persistent_script->timestamp)) { + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + } + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + } + + /* Calculate the required memory size */ + memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length TSRMLS_CC); + + /* Allocate shared memory */ + ZCG(mem) = zend_shared_alloc(memory_used); + if (!ZCG(mem)) { + zend_accel_error(ACCEL_LOG_DEBUG, "No more memory!"); + zend_accel_schedule_restart_if_necessary(TSRMLS_C); + if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { + ZSMMG(memory_exhausted) = 1; + } + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + + /* cleanup after calculation */ + new_persistent_script->mem = ZCG(mem); + new_persistent_script->size = memory_used; + + /* Copy into shared memory */ + new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length TSRMLS_CC); + + /* Consistency check */ + if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) { + zend_accel_error( + ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING, + "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n", + new_persistent_script->full_path, + new_persistent_script->mem, + (char *)new_persistent_script->mem + new_persistent_script->size, + ZCG(mem)); + } + + new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script); + + /* store script structure in the hash table */ + bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len+1, 0, new_persistent_script); + if (bucket && + (new_persistent_script->full_path_len != key_length || + memcmp(new_persistent_script->full_path, key, key_length) != 0)) { + /* link key to the same persistent script in hash table */ + if (!zend_accel_hash_update(&ZCSG(hash), key, key_length+1, 1, bucket)) { + zend_accel_error(ACCEL_LOG_DEBUG,"No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + } + } + + new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size); + + zend_shared_alloc_unlock(TSRMLS_C); + + *from_shared_memory = 1; + return new_persistent_script; +} + +static const struct jit_auto_global_info +{ + const char *name; + size_t len; +} jit_auto_globals_info[] = { + { "_SERVER", sizeof("_SERVER")}, + { "_ENV", sizeof("_ENV")}, + { "_REQUEST", sizeof("_REQUEST")}, +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + { "GLOBALS", sizeof("GLOBALS")}, +#endif +}; + +static int zend_accel_get_auto_globals(TSRMLS_D) +{ + int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0])); + int n = 1; + zval **res; + int mask = 0; + + for (i = 0; i < ag_size ; i++) { + if (zend_hash_find(&EG(symbol_table), jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, (void *)&res) == SUCCESS) { + mask |= n; + } + n += n; + } + return mask; +} + +static void zend_accel_set_auto_globals(int mask TSRMLS_DC) +{ + int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0])); + int n = 1; + + for (i = 0; i < ag_size ; i++) { + if (mask & n) { + zend_is_auto_global(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len - 1 TSRMLS_CC); + } + n += n; + } +} + +static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p, int *from_shared_memory TSRMLS_DC) +{ + zend_persistent_script *new_persistent_script; + zend_op_array *orig_active_op_array; + HashTable *orig_function_table, *orig_class_table; + zval *orig_user_error_handler; + zend_op_array *op_array; + int do_bailout = 0; +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + zend_uint orig_compiler_options = 0; +#endif + + /* Try to open file */ + if (file_handle->type == ZEND_HANDLE_FILENAME) { + if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == SUCCESS) { + /* key may be changed by zend_stream_open_function() */ + if (key == ZCG(key)) { + key_length = ZCG(key_len); + } + } else { + *op_array_p = NULL; + if (type==ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); + } + return NULL; + } + } + + /* check blacklist right after ensuring that file was opened */ + if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path)) { + ZCSG(blacklist_misses)++; + *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + return NULL; + } + + new_persistent_script = create_persistent_script(); + + /* Save the original values for the op_array, function table and class table */ + orig_active_op_array = CG(active_op_array); + orig_function_table = CG(function_table); + orig_class_table = CG(class_table); + orig_user_error_handler = EG(user_error_handler); + + /* Override them with ours */ + CG(function_table) = &ZCG(function_table); + EG(class_table) = CG(class_table) = &new_persistent_script->class_table; + EG(user_error_handler) = NULL; + + zend_try { +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + orig_compiler_options = CG(compiler_options); + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; +#endif + op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + CG(compiler_options) = orig_compiler_options; +#endif + } zend_catch { + op_array = NULL; + do_bailout = 1; +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + CG(compiler_options) = orig_compiler_options; +#endif + } zend_end_try(); + + /* Restore originals */ + CG(active_op_array) = orig_active_op_array; + CG(function_table) = orig_function_table; + EG(class_table) = CG(class_table) = orig_class_table; + EG(user_error_handler) = orig_user_error_handler; + + if (!op_array) { + /* compilation failed */ + free_persistent_script(new_persistent_script, 1); + zend_accel_free_user_functions(&ZCG(function_table) TSRMLS_CC); + if(do_bailout) { + zend_bailout(); + } + return NULL; + } + + /* Build the persistent_script structure. + Here we aren't sure we would store it, but we will need it + further anyway. + */ + zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table TSRMLS_CC); + new_persistent_script->main_op_array = *op_array; + + efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */ + + /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we + will have to ping the used auto global varaibles before execution */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (PG(auto_globals_jit)) { +#else + if ((PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays))) { +#endif + new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C); + } + + if (ZCG(accel_directives).validate_timestamps) { + /* Obtain the file timestamps, *before* actually compiling them, + * otherwise we have a race-condition. + */ + new_persistent_script->timestamp = zend_get_file_handle_timestamp(file_handle TSRMLS_CC); + + /* If we can't obtain a timestamp (that means file is possibly socket) + * we return it but do not persist it + */ + if (new_persistent_script->timestamp == 0) { + return new_persistent_script; + } + new_persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at); + } + + if (file_handle->opened_path) { + new_persistent_script->full_path_len = strlen(file_handle->opened_path); + new_persistent_script->full_path = estrndup(file_handle->opened_path, new_persistent_script->full_path_len); + } else { + new_persistent_script->full_path_len = strlen(file_handle->filename); + new_persistent_script->full_path = estrndup(file_handle->filename, new_persistent_script->full_path_len); + } + new_persistent_script->hash_value = zend_hash_func(new_persistent_script->full_path, new_persistent_script->full_path_len + 1); + + /* Now persistent_script structure is ready in process memory */ + return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory TSRMLS_CC); +} + +/* zend_compile() replacement */ +static zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) +{ + zend_persistent_script *persistent_script = NULL; + char *key = NULL; + int key_length; + int from_shared_memory; /* if the script we've got is stored in SHM */ + + if (!file_handle->filename || + !ZCG(startup_ok) || + (!ZCG(counted) && !ZCSG(accelerator_enabled)) || + CG(interactive) || + (ZCSG(restart_in_progress) && accel_restart_is_active(TSRMLS_C))) { + /* The Accelerator is disabled, act as if without the Accelerator */ + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + + /* Make sure we only increase the currently running processes semaphore + * once each execution (this function can be called more than once on + * each execution) + */ + if (!ZCG(counted)) { + ZCG(counted) = 1; + accel_activate_add(TSRMLS_C); + } + + /* In case this callback is called from include_once, require_once or it's + * a main FastCGI request, the key must be already calculated, and cached + * persistent script already found */ + if ((EG(opline_ptr) == NULL && + ZCG(cache_opline) == NULL && + file_handle->filename == SG(request_info).path_translated && + ZCG(cache_persistent_script)) || + (EG(opline_ptr) && *EG(opline_ptr) && + *EG(opline_ptr) == ZCG(cache_opline) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + if (!ZCG(key_len)) { + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + /* persistent script was already found by overrifen open() or + * resolve_path() callbacks */ + persistent_script = ZCG(cache_persistent_script); + key = ZCG(key); + key_length = ZCG(key_len); + } else { + /* try to find cached script by key */ + if ((key = accel_make_persistent_key(file_handle, &key_length TSRMLS_CC)) == NULL) { + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1); + if (!persistent_script) { + /* try to find cached script by full real path */ + zend_accel_hash_entry *bucket; + + /* open file to resolve the path */ + if (file_handle->type == ZEND_HANDLE_FILENAME && +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { +#else + zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { +#endif + if (type==ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); + } + return NULL; + } + + if (file_handle->opened_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path, strlen(file_handle->opened_path)+1)) != NULL) { + + persistent_script = (zend_persistent_script *)bucket->data; + if (!ZCG(accel_directives).revalidate_path && + !persistent_script->corrupted) { + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + } + } + } + + /* clear cache */ + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + + if (persistent_script && persistent_script->corrupted) { + persistent_script = NULL; + } + + SHM_UNPROTECT(); + + /* If script is found then validate_timestamps if option is enabled */ + if (persistent_script && ZCG(accel_directives).validate_timestamps) { + if (validate_timestamp_and_record(persistent_script, file_handle TSRMLS_CC)==FAILURE) { + zend_shared_alloc_lock(TSRMLS_C); + if (!persistent_script->corrupted) { + persistent_script->corrupted = 1; + persistent_script->timestamp = 0; + ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption; + } + zend_shared_alloc_unlock(TSRMLS_C); + persistent_script = NULL; + } + } + + /* if turned on - check the compiled script ADLER32 checksum */ + if (persistent_script && ZCG(accel_directives).consistency_checks + && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) { + + unsigned int checksum = zend_accel_script_checksum(persistent_script); + if (checksum != persistent_script->dynamic_members.checksum ) { + /* The checksum is wrong */ + zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%0.8X, found=0x%0.8X", + persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum); + zend_shared_alloc_lock(TSRMLS_C); + if (!persistent_script->corrupted) { + persistent_script->corrupted = 1; + persistent_script->timestamp = 0; + ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption; + } + zend_shared_alloc_unlock(TSRMLS_C); + persistent_script = NULL; + } + } + + /* If script was not found or invalidated by validate_timestamps */ + if (!persistent_script) { + zend_op_array *op_array; + + /* Cache miss.. */ + ZCSG(misses)++; + + /* No memory left. Behave like without the Accelerator */ + if (ZSMMG(memory_exhausted)) { + SHM_PROTECT(); + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + + /* Try and cache the script and assume that it is returned from_shared_memory. + * If it isn't compile_and_cache_file() changes the flag to 0 + */ + from_shared_memory = 0; + persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory TSRMLS_CC); + + /* Something went wrong during compilation, returning NULL */ + if (!persistent_script) { + SHM_PROTECT(); + return op_array; /* Presently always NULL, but not necessary in the future */ + } + } else { + +#if !ZEND_WIN32 + ZCSG(hits)++; /* TBFixed: may lose one hit */ + persistent_script->dynamic_members.hits++; /* see above */ +#else + InterlockedIncrement(&ZCSG(hits)); + InterlockedIncrement(&persistent_script->dynamic_members.hits); +#endif + + /* see bug #15471 (old BTS) */ + if (persistent_script->full_path) { + if (!EG(opline_ptr) || !*EG(opline_ptr) || + (*EG(opline_ptr))->opcode != ZEND_INCLUDE_OR_EVAL || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value != ZEND_INCLUDE_ONCE && + (*EG(opline_ptr))->extended_value != ZEND_REQUIRE_ONCE)) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_INCLUDE_ONCE && + (*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_REQUIRE_ONCE)) { +#endif + void *dummy = (void *) 1; + + zend_hash_quick_add(&EG(included_files), persistent_script->full_path, persistent_script->full_path_len+1, persistent_script->hash_value, &dummy, sizeof(void *), NULL); + } + } + zend_file_handle_dtor(file_handle TSRMLS_CC); + from_shared_memory = 1; + } + + persistent_script->dynamic_members.last_used = ZCG(request_time); + + SHM_PROTECT(); + + /* Fetch jit auto globals used in the script before execution */ + if (persistent_script->ping_auto_globals_mask) { + zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask TSRMLS_CC); + } + + return zend_accel_load_script(persistent_script, from_shared_memory TSRMLS_CC); +} + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +/* Taken from php-5.2.5 because early versions don't have correct version */ +static char *accel_tsrm_realpath(const char *path, int path_len TSRMLS_DC) +{ + cwd_state new_state; + char *real_path; + char *cwd; + int cwd_len; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, path_len) && + (cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) { + new_state.cwd = zend_strndup(cwd, cwd_len); + new_state.cwd_length = cwd_len; + } else { + new_state.cwd = (char*)malloc(1); + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + +#ifndef CWD_REALPATH +# define CWD_REALPATH 2 +#endif + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) { + free(new_state.cwd); + return NULL; + } + + real_path = emalloc(new_state.cwd_length+1); + memcpy(real_path, new_state.cwd, new_state.cwd_length+1); + free(new_state.cwd); + return real_path; +} + +static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC) +{ + char *resolved_path; + char trypath[MAXPATHLEN]; + const char *ptr, *end; + int len; + + if (!filename) { + return NULL; + } + + if (*filename == '.' || + IS_ABSOLUTE_PATH(filename, filename_length) || + !path || + !*path) { + return accel_tsrm_realpath(filename, filename_length TSRMLS_CC); + } + + ptr = path; + while (*ptr) { + for (end = ptr; *end && *end != DEFAULT_DIR_SEPARATOR; end++); + len = end-ptr; + if (*end) end++; + if (len + 1 + filename_length + 1 >= MAXPATHLEN) { + ptr = end; + continue; + } + memcpy(trypath, ptr, len); + trypath[len] = '/'; + memcpy(trypath+len+1, filename, filename_length+1); + ptr = end; + if ((resolved_path = accel_tsrm_realpath(trypath, len+1+filename_length TSRMLS_CC)) != NULL) { + return resolved_path; + } + } /* end provided path */ + + /* check in calling scripts' current working directory as a fall back case + */ + if (EG(in_execution)) { + char *exec_fname = zend_get_executed_filename(TSRMLS_C); + int exec_fname_length = strlen(exec_fname); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) { + memcpy(trypath, exec_fname, exec_fname_length + 1); + memcpy(trypath+exec_fname_length + 1, filename, filename_length+1); + return accel_tsrm_realpath(trypath, exec_fname_length+1+filename_length TSRMLS_CC); + } + } + + return NULL; +} + +/* zend_stream_open_function() replacement for PHP 5.2 */ +static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC) +{ + if (ZCG(startup_ok) && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + if (EG(opline_ptr) && *EG(opline_ptr)) { + zend_op *opline = *EG(opline_ptr); + + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + (opline->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + opline->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE)) { + /* we are in include_once */ + char *key = NULL; + int key_length; + char *resolved_path; + zend_accel_hash_entry *bucket; + zend_persistent_script *persistent_script; + int filename_len; + + if (opline->op1.op_type == IS_CONST) { + filename_len = Z_STRLEN(opline->op1.u.constant); + } else { + filename_len = strlen(filename); + } + handle->filename = (char*)filename; + handle->free_filename = 0; + handle->opened_path = NULL; + + /* Check if requestd file already cached (by full name) */ + if (IS_ABSOLUTE_PATH(filename, filename_len) && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + + /* Check if requestd file already cached (by key) */ + key = accel_make_persistent_key_ex(handle, filename_len, &key_length TSRMLS_CC); + if (!ZCG(accel_directives).revalidate_path && + key && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + + /* find the full real path */ + resolved_path = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC); + + /* Check if requestd file already cached (by real name) */ + if (resolved_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path)+1)) != NULL) { + + persistent_script = (zend_persistent_script *)bucket->data; + if (persistent_script && !persistent_script->corrupted) { + handle->opened_path = resolved_path; + handle->type = ZEND_HANDLE_FILENAME; + if (key && !ZCG(accel_directives).revalidate_path) { + /* add another "key" for the same bucket */ + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + } + if (resolved_path) { + efree(resolved_path); + } + } + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC); +} + +#else + +/* zend_stream_open_function() replacement for PHP 5.3 and above */ +static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC) +{ + if (ZCG(startup_ok) && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + /* check if callback is called from include_once or it's a main request */ + if ((!EG(opline_ptr) && + filename == SG(request_info).path_translated) || + (EG(opline_ptr) && + *EG(opline_ptr) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + + /* we are in include_once or FastCGI request */ + zend_persistent_script *persistent_script; + + handle->filename = (char*)filename; + handle->free_filename = 0; + + /* check if cached script was already found by resolve_path() */ + if ((EG(opline_ptr) == NULL && + ZCG(cache_opline) == NULL && + ZCG(cache_persistent_script) != NULL) || + (EG(opline_ptr) && + (ZCG(cache_opline) == *EG(opline_ptr)))) { + persistent_script = ZCG(cache_persistent_script); + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + return SUCCESS; +#if 0 + } else { + /* FIXME: It looks like this part is not needed any more */ + int filename_len = strlen(filename); + + if ((IS_ABSOLUTE_PATH(filename, filename_len) || + is_stream_path(filename)) && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = EG(opline_ptr) ? persistent_script : NULL; + return SUCCESS; + } +#endif + } + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC); +} + +/* zend_resolve_path() replacement for PHP 5.3 and above */ +static char* persistent_zend_resolve_path(const char *filename, int filename_len TSRMLS_DC) +{ + if (ZCG(startup_ok) && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + /* check if callback is called from include_once or it's a main request */ + if ((!EG(opline_ptr) && + filename == SG(request_info).path_translated) || + (EG(opline_ptr) && + *EG(opline_ptr) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + + /* we are in include_once or FastCGI request */ + zend_file_handle handle; + char *key = NULL; + int key_length; + char *resolved_path; + zend_accel_hash_entry *bucket; + zend_persistent_script *persistent_script; + + /* Check if requestd file already cached (by full name) */ + if ((IS_ABSOLUTE_PATH(filename, filename_len) || + is_stream_path(filename)) && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), (char*)filename, filename_len+1)) != NULL) { + persistent_script = (zend_persistent_script *)bucket->data; + if (persistent_script && !persistent_script->corrupted) { + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len+1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = persistent_script; + return estrndup(persistent_script->full_path, persistent_script->full_path_len); + } + } + + /* Check if requestd file already cached (by key) */ + handle.filename = (char*)filename; + handle.free_filename = 0; + handle.opened_path = NULL; + key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC); + if (!ZCG(accel_directives).revalidate_path && + key && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1)) != NULL && + !persistent_script->corrupted) { + + /* we have persistent script */ + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = persistent_script; + return estrndup(persistent_script->full_path, persistent_script->full_path_len); + } + + /* find the full real path */ + resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC); + + /* Check if requestd file already cached (by real path) */ + if (resolved_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path)+1)) != NULL) { + persistent_script = (zend_persistent_script *)bucket->data; + + if (persistent_script && !persistent_script->corrupted) { + if (key && !ZCG(accel_directives).revalidate_path) { + /* add another "key" for the same bucket */ + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + ZCG(cache_opline) = (EG(opline_ptr) && key) ? *EG(opline_ptr): NULL; + ZCG(cache_persistent_script) = key ? persistent_script : NULL; + return resolved_path; + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return resolved_path; + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC); +} + +#endif + +static void zend_reset_cache_vars(TSRMLS_D) +{ + ZSMMG(memory_exhausted) = 0; + ZCSG(hits) = 0; + ZCSG(misses) = 0; + ZCSG(blacklist_misses) = 0; + ZSMMG(wasted_shared_memory) = 0; + ZCSG(restart_pending) = 0; + ZCSG(force_restart_time) = 0; +} + +static void accel_activate(void) +{ + TSRMLS_FETCH(); + + if (!ZCG(startup_ok)) { + return; + } + + SHM_UNPROTECT(); + ZCG(request_time) = sapi_get_request_time(TSRMLS_C); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_check) = !ZCG(include_path_key); + + if (ZCG(counted)) { +#ifdef ZTS + zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id()); +#else + zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid()); +#endif + accel_unlock_all(TSRMLS_C); + ZCG(counted) = 0; + } + + if (ZCSG(restart_pending)) { + zend_shared_alloc_lock(TSRMLS_C); + if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */ + if (accel_is_inactive(TSRMLS_C) == SUCCESS) { + zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!"); + ZCSG(restart_pending) = 0; + accel_restart_enter(TSRMLS_C); + + zend_reset_cache_vars(TSRMLS_C); + zend_accel_hash_clean(&ZCSG(hash)); + + /* include_paths keeps only the first path */ + if (ZCSG(include_paths).num_entries > 1) { + ZCSG(include_paths).num_entries = 1; + ZCSG(include_paths).num_direct_entries = 1; + memset(ZCSG(include_paths).hash_table, 0, sizeof(zend_accel_hash_entry*) * ZCSG(include_paths).max_num_entries); + ZCSG(include_paths).hash_table[zend_inline_hash_func(ZCSG(include_paths).hash_entries[0].key, ZCSG(include_paths).hash_entries[0].key_length) % ZCSG(include_paths).max_num_entries] = &ZCSG(include_paths).hash_entries[0]; + } + +#if (ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO) && !defined(ZTS) + accel_interned_strings_restore_state(TSRMLS_C); +#endif + + zend_shared_alloc_restore_state(); + ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart); + ZCSG(last_restart_time) = ZCG(request_time); + accel_restart_leave(TSRMLS_C); + } + } + zend_shared_alloc_unlock(TSRMLS_C); + } + + /* check if ZCG(function_table) wasn't somehow polluted on the way */ + if(ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) { + zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table))); + } + + if (ZCG(accel_directives).validate_timestamps) { + time_t now = ZCG(request_time); + if (now > ZCSG(revalidate_at) + (time_t)ZCG(accel_directives).revalidate_freq) { + ZCSG(revalidate_at) = now; + } + } + + ZCG(cwd) = NULL; + + SHM_PROTECT(); +} + +#if !ZEND_DEBUG + +/* Fast Request Shutdown + * ===================== + * Zend Memory Manager frees memory by its own. We don't have to free each + * allocated block separately, but we like to call all the destructors and + * callbacks in exactly the same order. + */ + +static void accel_fast_hash_destroy(HashTable *ht) +{ + Bucket *p = ht->pListHead; + + while (p != NULL) { + ht->pDestructor(p->pData); + p = p->pListNext; + } +} + +static void accel_fast_zval_ptr_dtor(zval **zval_ptr) +{ + zval *zvalue = *zval_ptr; + + if (Z_DELREF_P(zvalue)==0) { +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) { +#else + switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) { +#endif + case IS_ARRAY: + case IS_CONSTANT_ARRAY: { + TSRMLS_FETCH(); + + if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) { + zvalue->value.ht->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(zvalue->value.ht); + } + } + break; + case IS_OBJECT: + { + TSRMLS_FETCH(); + + Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC); + } + break; + case IS_RESOURCE: + { + TSRMLS_FETCH(); + + /* destroy resource */ + zend_list_delete(zvalue->value.lval); + } + break; + case IS_LONG: + case IS_DOUBLE: + case IS_BOOL: + case IS_NULL: + case IS_STRING: + case IS_CONSTANT: + default: + return; + break; + } + } +} + +static int accel_clean_non_persistent_function(zend_function *function TSRMLS_DC) +{ + if (function->type == ZEND_INTERNAL_FUNCTION) { + return ZEND_HASH_APPLY_STOP; + } else { + if (function->op_array.static_variables) { + function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + return (--(*function->op_array.refcount) <= 0) ? + ZEND_HASH_APPLY_REMOVE : + ZEND_HASH_APPLY_KEEP; + } +} + +static int accel_cleanup_function_data(zend_function *function TSRMLS_DC) +{ + if (function->type == ZEND_USER_FUNCTION) { + if (function->op_array.static_variables) { + function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + } + return 0; +} + +static int accel_clean_non_persistent_class(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->type == ZEND_INTERNAL_CLASS) { + return ZEND_HASH_APPLY_STOP; + } else { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC); + } + if (ce->static_members_table) { + int i; + + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->static_members_table[i]) { + accel_fast_zval_ptr_dtor(&ce->static_members_table[i]); + ce->static_members_table[i] = NULL; + } + } + ce->static_members_table = NULL; + } +#else + zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC); + if (ce->static_members) { + ce->static_members->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(ce->static_members); + ce->static_members = NULL; + } +#endif + return ZEND_HASH_APPLY_REMOVE; + } +} + +static int accel_clean_non_persistent_constant(zend_constant *c TSRMLS_DC) +{ + if (c->flags & CONST_PERSISTENT) { + return ZEND_HASH_APPLY_STOP; + } else { + interned_free(c->name); + return ZEND_HASH_APPLY_REMOVE; + } +} + +static void zend_accel_fast_shutdown(TSRMLS_D) +{ + if (EG(full_tables_cleanup)) { + EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + } else { + dtor_func_t old_destructor; + + if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) { + /* We don't have to destroy all zvals if they cannot call any destructors */ + + old_destructor = EG(symbol_table).pDestructor; + EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + zend_try { + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + } zend_end_try(); + EG(symbol_table).pDestructor = old_destructor; + } + zend_hash_init(&EG(symbol_table), 0, NULL, NULL, 0); + old_destructor = EG(function_table)->pDestructor; + EG(function_table)->pDestructor = NULL; + zend_hash_reverse_apply(EG(function_table), (apply_func_t) accel_clean_non_persistent_function TSRMLS_CC); + EG(function_table)->pDestructor = old_destructor; + old_destructor = EG(class_table)->pDestructor; + EG(class_table)->pDestructor = NULL; + zend_hash_reverse_apply(EG(class_table), (apply_func_t) accel_clean_non_persistent_class TSRMLS_CC); + EG(class_table)->pDestructor = old_destructor; + old_destructor = EG(zend_constants)->pDestructor; + EG(zend_constants)->pDestructor = NULL; + zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) accel_clean_non_persistent_constant TSRMLS_CC); + EG(zend_constants)->pDestructor = old_destructor; + } + CG(unclean_shutdown) = 1; +} +#endif + +static void accel_deactivate(void) +{ + /* ensure that we restore function_table and class_table + * In general, they're restored by persistent_compile_file(), but in case + * the script is aborted abnormally, they may become messed up. + */ + TSRMLS_FETCH(); + + if (!ZCG(startup_ok)) { + return; + } + + zend_shared_alloc_safe_unlock(TSRMLS_C); /* be sure we didn't leave cache locked */ + accel_unlock_all(TSRMLS_C); + ZCG(counted) = 0; + +#if !ZEND_DEBUG + if (ZCG(accel_directives).fast_shutdown) { + zend_accel_fast_shutdown(TSRMLS_C); + } +#endif + + if (ZCG(cwd)) { + efree(ZCG(cwd)); + ZCG(cwd) = NULL; + } + +} + +static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2) +{ + (void)element2; /* keep the compiler happy */ + + if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) { + element1->startup = NULL; +#if 0 + /* We have to call shutdown callback it to free TS resources */ + element1->shutdown = NULL; +#endif + element1->activate = NULL; + element1->deactivate = NULL; + element1->op_array_handler = NULL; + +#ifdef __DEBUG_MESSAGES__ + fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error")); + fflush(stderr); +#endif + } + + return 0; +} + +static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *) TSRMLS_DC) +{ + ZCG(startup_ok) = 0; + zps_failure_reason = reason; + zps_api_failure_reason = api_reason?api_reason:reason; + zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb); +} + +static inline int accel_find_sapi(TSRMLS_D) +{ + static const char *supported_sapis[] = { + "apache", + "fastcgi", + "cgi-fcgi", + "fpm-fcgi", + "isapi", + "apache2filter", + "apache2handler", + NULL + }; + const char **sapi_name; + + if (sapi_module.name) { + for (sapi_name=supported_sapis; *sapi_name; sapi_name++) { + if (strcmp(sapi_module.name, *sapi_name)==0) { + return SUCCESS; + } + } + if (ZCG(accel_directives).enable_cli && + strcmp(sapi_module.name, "cli")==0) { + return SUCCESS; + } + } + + return FAILURE; +} + +static void zend_accel_init_shm(TSRMLS_D) +{ + zend_shared_alloc_lock(TSRMLS_C); + + accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals)); + if (!accel_shared_globals) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + } + ZSMMG(app_shared_globals) = accel_shared_globals; + + zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files); + zend_accel_hash_init(&ZCSG(include_paths), 32); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# ifndef ZTS + zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1); + ZCSG(interned_strings).nTableMask = ZCSG(interned_strings).nTableSize - 1; + ZCSG(interned_strings).arBuckets = zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024)); + if (!ZCSG(interned_strings).arBuckets || !ZCSG(interned_strings_start)) { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings"); + } + ZCSG(interned_strings_end) = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024); + ZCSG(interned_strings_top) = ZCSG(interned_strings_start); +# else + ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL; +# endif + + orig_interned_strings_start = CG(interned_strings_start); + orig_interned_strings_end = CG(interned_strings_end); + orig_new_interned_string = zend_new_interned_string; + orig_interned_strings_snapshot = zend_interned_strings_snapshot; + orig_interned_strings_restore = zend_interned_strings_restore; + + CG(interned_strings_start) = ZCSG(interned_strings_start); + CG(interned_strings_end) = ZCSG(interned_strings_end); + zend_new_interned_string = accel_new_interned_string_for_php; + zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php; + zend_interned_strings_restore = accel_interned_strings_restore_for_php; + +# ifndef ZTS + accel_use_shm_interned_strings(TSRMLS_C); + accel_interned_strings_save_state(TSRMLS_C); +# endif + +#endif + + zend_reset_cache_vars(TSRMLS_C); + + ZCSG(accelerator_enabled) = 1; + ZCSG(last_restart_time) = 0; + ZCSG(restart_in_progress) = 0; + + zend_shared_alloc_unlock(TSRMLS_C); +} + +static void accel_globals_ctor(zend_accel_globals *accel_globals TSRMLS_DC) +{ + memset(accel_globals, 0, sizeof(zend_accel_globals)); + zend_hash_init(&accel_globals->function_table, zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1); + zend_accel_copy_internal_functions(TSRMLS_C); +} + +static void accel_globals_dtor(zend_accel_globals *accel_globals TSRMLS_DC) +{ + accel_globals->function_table.pDestructor = NULL; + zend_hash_destroy(&accel_globals->function_table); +} + +static int accel_startup(zend_extension *extension) +{ + zend_function *func; + zend_ini_entry *ini_entry; + TSRMLS_FETCH(); + +#ifdef ZTS + accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor); +#else + accel_globals_ctor(&accel_globals); +#endif + +#ifdef ZEND_WIN32 + _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */ +#endif + + if (start_accel_module(0) == FAILURE) { + ZCG(startup_ok) = 0; + zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!"); + return FAILURE; + } + + /* no supported SAPI found - disable acceleration and stop initalization */ + if( accel_find_sapi(TSRMLS_C) == FAILURE ){ + ZCG(startup_ok) = 0; + zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI and FastCGI SAPIs", NULL, accelerator_remove_cb TSRMLS_CC); + return SUCCESS; + } + + if (ZCG(enabled) == 0) { + return SUCCESS ; + } +/********************************************/ +/* End of non-SHM dependent initializations */ +/********************************************/ + switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) { + case ALLOC_SUCCESS: + zend_accel_init_shm(TSRMLS_C); + break; + case ALLOC_FAILURE: + ZCG(startup_ok) = 0; + zend_accel_error(ACCEL_LOG_FATAL,"Failure to initialize shared memory structures - probably not enough shared memory."); + return SUCCESS; + case SUCCESSFULLY_REATTACHED: + accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_shared_alloc_lock(TSRMLS_C); + orig_interned_strings_start = CG(interned_strings_start); + orig_interned_strings_end = CG(interned_strings_end); + orig_new_interned_string = zend_new_interned_string; + orig_interned_strings_snapshot = zend_interned_strings_snapshot; + orig_interned_strings_restore = zend_interned_strings_restore; + + CG(interned_strings_start) = ZCSG(interned_strings_start); + CG(interned_strings_end) = ZCSG(interned_strings_end); + zend_new_interned_string = accel_new_interned_string_for_php; + zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php; + zend_interned_strings_restore = accel_interned_strings_restore_for_php; + +# ifndef ZTS + accel_use_shm_interned_strings(TSRMLS_C); +# endif + + zend_shared_alloc_unlock(TSRMLS_C); +#endif + + break; + case FAILED_REATTACHED: + ZCG(startup_ok) = 0; + zend_accel_error(ACCEL_LOG_FATAL,"Failure to initialize shared memory structures - can not reattach to exiting shared memory."); + return SUCCESS; + break; + } + + /* from this point further, shared memory is supposed to be OK */ + + /* Override compiler */ + accelerator_orig_compile_file = zend_compile_file; + zend_compile_file = persistent_compile_file; + + /* Override stream opener function (to eliminate open() call caused by + * include/require statements ) */ + accelerator_orig_zend_stream_open_function = zend_stream_open_function; + zend_stream_open_function = persistent_stream_open_function; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* Override path resolver function (to eliminate stat() calls caused by + * include_once/require_once statements */ + accelerator_orig_zend_resolve_path = zend_resolve_path; + zend_resolve_path = persistent_zend_resolve_path; +#endif + + if(ZCG(accel_directives).validate_timestamps) { + ZCSG(revalidate_at) = zend_accel_get_time() + ZCG(accel_directives).revalidate_freq; + } + + /* Override chdir() function */ + if (zend_hash_find(CG(function_table), "chdir", sizeof("chdir"), (void**)&func) == SUCCESS && + func->type == ZEND_INTERNAL_FUNCTION) { + orig_chdir = func->internal_function.handler; + func->internal_function.handler = ZEND_FN(accel_chdir); + } + ZCG(cwd) = NULL; + + /* Override "include_path" modifier callback */ + if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry)==SUCCESS) { + ZCG(include_path) = INI_STR("include_path"); + ZCG(include_path_key) = NULL; + if (ZCG(include_path) && *ZCG(include_path)) { + ZCG(include_path_len) = strlen(ZCG(include_path)); + if (!zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + zend_shared_alloc_lock(TSRMLS_C); + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + } + zend_shared_alloc_unlock(TSRMLS_C); + } + } else { + ZCG(include_path) = ""; + ZCG(include_path_len) = 0; + } + orig_include_path_on_modify = ini_entry->on_modify; + ini_entry->on_modify = accel_include_path_on_modify; + } + + zend_shared_alloc_lock(TSRMLS_C); + zend_shared_alloc_save_state(); + zend_shared_alloc_unlock(TSRMLS_C); + + SHM_PROTECT(); + + ZCG(startup_ok) = 1; + + /* Override file_exists(), is_file() and is_readable() */ + zend_accel_override_file_functions(TSRMLS_C); + +#if 0 + /* FIXME: We probably don't need it here */ + zend_accel_copy_internal_functions(TSRMLS_C); +#endif + + return SUCCESS; +} + +static void accel_free_ts_resources() +{ +#ifndef ZTS + accel_globals_dtor(&accel_globals); +#else + ts_free_id(accel_globals_id); +#endif +} + +static void accel_shutdown(zend_extension *extension) +{ + zend_ini_entry *ini_entry; + TSRMLS_FETCH(); + + (void)extension; /* keep the compiler happy */ + + zend_accel_blacklist_shutdown(&accel_blacklist); + + if (!ZCG(startup_ok)) { + accel_free_ts_resources(); + return; + } + + accel_free_ts_resources(); + zend_shared_alloc_shutdown(); + zend_compile_file = accelerator_orig_compile_file; + + if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry)==SUCCESS) { + ini_entry->on_modify = orig_include_path_on_modify; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + CG(interned_strings_start) = orig_interned_strings_start; + CG(interned_strings_end) = orig_interned_strings_end; + zend_new_interned_string = orig_new_interned_string; + zend_interned_strings_snapshot = orig_interned_strings_snapshot; + zend_interned_strings_restore = orig_interned_strings_restore; +#endif + +} + +void zend_accel_schedule_restart(TSRMLS_D) +{ + if (ZCSG(restart_pending)) { + /* don't schedule twice */ + return; + } + zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!"); + + ZCSG(restart_pending) = 1; + ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled); + ZCSG(accelerator_enabled) = 0; + + if (ZCG(accel_directives).force_restart_timeout) { + ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout; + } else { + ZCSG(force_restart_time) = 0; + } +} + +/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */ +#ifdef ZEND_WIN32 +#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub(TSRMLS_C) +#else +#define accel_deactivate_now() accel_deactivate_sub(TSRMLS_C) +#endif + +/* ensures it is OK to read SHM + if it's not OK (restart in progress) returns FAILURE + if OK returns SUCCESS + MUST call accelerator_shm_read_unlock after done lock operationss +*/ +int accelerator_shm_read_lock(TSRMLS_D) +{ + if(ZCG(counted)) { + /* counted means we are holding read lock for SHM, so that nothing bad can happen */ + return SUCCESS; + } else { + /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled + or is in progress now */ + accel_activate_add(TSRMLS_C); /* acquire usage lock */ + /* Now if we weren't inside restart, restart would not begin until we remove usage lock */ + if(ZCSG(restart_in_progress)) { + /* we already were inside restart this means it's not safe to touch shm */ + accel_deactivate_now(); /* drop usage lock */ + return FAILURE; + } + } + return SUCCESS; +} + +/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */ +void accelerator_shm_read_unlock(TSRMLS_D) +{ + if(!ZCG(counted)) { + /* counted is 0 - meaning we had to readlock manually, release readlock now */ + accel_deactivate_now(); + } +} + +static void accel_op_array_handler(zend_op_array *op_array) +{ + TSRMLS_FETCH(); + + if (ZCG(startup_ok) && ZCSG(accelerator_enabled)) { + zend_optimizer(op_array TSRMLS_CC); + } +} + +ZEND_EXT_API zend_extension zend_extension_entry = { + ACCELERATOR_PRODUCT_NAME, /* name */ + ACCELERATOR_VERSION, /* version */ + "Zend Technologies", /* author */ + "http://www.zend.com/", /* URL */ + "Copyright (c) 1999-2013", /* copyright */ + accel_startup, /* startup */ + accel_shutdown, /* shutdown */ + accel_activate, /* per-script activation */ + accel_deactivate, /* per-script deactivation */ + NULL, /* message handler */ + accel_op_array_handler, /* op_array handler */ + NULL, /* extended statement handler */ + NULL, /* extended fcall begin handler */ + NULL, /* extended fcall end handler */ + NULL, /* op_array ctor */ + NULL, /* op_array dtor */ + STANDARD_ZEND_EXTENSION_PROPERTIES +}; diff --git a/ZendAccelerator.h b/ZendAccelerator.h new file mode 100644 index 0000000000..68afad38f4 --- /dev/null +++ b/ZendAccelerator.h @@ -0,0 +1,364 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_H +#define ZEND_ACCELERATOR_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define ACCELERATOR_PRODUCT_NAME "Zend Optimizer+" +#define ACCELERATOR_VERSION "7.0.0" +/* 2 - added Profiler support, on 20010712 */ +/* 3 - added support for Optimizer's encoded-only-files mode */ +/* 4 - works with the new Optimizer, that supports the file format with licenses */ +/* 5 - API 4 didn't really work with the license-enabled file format. v5 does. */ +/* 6 - Monitor was removed from ZendPlatform.so, to a module of its own */ +/* 7 - Optimizer was embeded into Accelerator */ +/* 8 - Standalone Open Source OptimizerPlus */ +#define ACCELERATOR_API_NO 8 + +#if ZEND_WIN32 +# include "zend_config.w32.h" +#else +#include "zend_config.h" +# include +# include +#endif + +#if HAVE_UNISTD_H +# include "unistd.h" +#endif + +#include "zend_extensions.h" +#include "zend_compile.h" + +#include "Optimizer/zend_optimizer.h" +#include "zend_accelerator_hash.h" +#include "zend_accelerator_debug.h" + +#ifndef PHPAPI +# ifdef ZEND_WIN32 +# define PHPAPI __declspec(dllimport) +# else +# define PHPAPI +# endif +#endif + +#ifndef ZEND_EXT_API +# if WIN32|WINNT +# define ZEND_EXT_API __declspec(dllexport) +# else +# define ZEND_EXT_API +# endif +#endif + +#ifdef ZEND_WIN32 +# ifndef MAXPATHLEN +# define MAXPATHLEN _MAX_PATH +# endif +# include +#else +# include +#endif + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 + +/*** file locking ***/ +#ifndef ZEND_WIN32 +extern int lock_file; + +# if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)/* Darwin */) || defined(__OpenBSD__) || defined(__NetBSD__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {start, len, -1, type, whence} +# elif defined(__svr4__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len} +# elif defined(__linux__) || defined(__hpux) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len, 0} +# elif defined(_AIX) +# if defined(_LARGE_FILES) || defined(__64BIT__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, 0, 0, 0, start, len } +# else +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len} +# endif +# else +# error "Don't know how to define struct flock" +# endif +#endif + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + #define Z_REFCOUNT_P(pz) (pz)->refcount + #define Z_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v) + #define Z_ADDREF_P(pz) ++((pz)->refcount) + #define Z_DELREF_P(pz) --((pz)->refcount) + #define Z_ISREF_P(pz) (pz)->is_ref + #define Z_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define Z_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref) + #define PZ_REFCOUNT_P(pz) (pz)->refcount + #define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v) + #define PZ_ADDREF_P(pz) ++((pz)->refcount) + #define PZ_DELREF_P(pz) --((pz)->refcount) + #define PZ_ISREF_P(pz) (pz)->is_ref + #define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref) +#else + #define PZ_REFCOUNT_P(pz) (pz)->refcount__gc + #define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount__gc = (v) + #define PZ_ADDREF_P(pz) ++((pz)->refcount__gc) + #define PZ_DELREF_P(pz) --((pz)->refcount__gc) + #define PZ_ISREF_P(pz) (pz)->is_ref__gc + #define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref__gc = (isref) +#endif + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO +# ifdef ALLOCA_FLAG + #define DO_ALLOCA(x) do_alloca_with_limit(x,use_heap) + #define FREE_ALLOCA(x) free_alloca_with_limit(x,use_heap) +# else + #define ALLOCA_FLAG(x) + #define DO_ALLOCA(x) do_alloca(x) + #define FREE_ALLOCA(x) free_alloca(x) +# endif +#else + #define DO_ALLOCA(x) do_alloca(x,use_heap) + #define FREE_ALLOCA(x) free_alloca(x,use_heap) +#endif + + +#if ZEND_WIN32 +typedef unsigned __int64 accel_time_t; +#else +typedef time_t accel_time_t; +#endif + +typedef struct _zend_persistent_script { + ulong hash_value; + char *full_path; /* full real path with resolved symlinks */ + int full_path_len; + zend_op_array main_op_array; + HashTable function_table; + HashTable class_table; + long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */ + int ping_auto_globals_mask; /* which autoglobals are used by the script */ + accel_time_t timestamp; /* the script modification time */ + zend_bool corrupted; +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_uint early_binding; /* the linked list of delayed declarations */ +#endif + + void *mem; /* shared memory area used by script structures */ + size_t size; /* size of used shared memory */ + + /* All entries that shouldn't be counted in the ADLER32 + * checksum must be declared in this struct + */ + struct zend_persistent_script_dynamic_members { + time_t last_used; + ulong hits; + unsigned int memory_consumption; + unsigned int checksum; + time_t revalidate; + } dynamic_members; +} zend_persistent_script; + +typedef struct _zend_accel_directives { + long memory_consumption; + long max_accelerated_files; + double max_wasted_percentage; + char *user_blacklist_filename; + long consistency_checks; + long force_restart_timeout; + zend_bool use_cwd; + zend_bool ignore_dups; + zend_bool validate_timestamps; + zend_bool revalidate_path; + zend_bool save_comments; + zend_bool fast_shutdown; + zend_bool protect_memory; + zend_bool file_override_enabled; + zend_bool inherited_hack; + zend_bool enable_cli; + unsigned long revalidate_freq; + char *error_log; +#ifdef ZEND_WIN32 + char *mmap_base; +#endif + char *memory_model; + long log_verbosity_level; + + long optimization_level; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + long interned_strings_buffer; +#endif +} zend_accel_directives; + +typedef struct _zend_accel_globals { + /* copy of CG(function_table) used for compilation scripts into cashe */ + /* imitially it contains only internal functions */ + HashTable function_table; + int internal_functions_count; + int counted; /* the process uses shatred memory */ + zend_bool enabled; + HashTable bind_hash; /* prototype and zval lookup table */ + zend_accel_directives accel_directives; + char *cwd; /* current working directory or NULL */ + int cwd_len; /* "cwd" string lenght */ + char *include_path_key; /* one letter key of current "include_path" */ + char *include_path; /* current settion of "include_path" directive */ + int include_path_len; /* "include_path" string lenght */ + int include_path_check; + time_t request_time; + zend_bool startup_ok; + /* preallocated shared-memory block to save current script */ + void *mem; + /* cache to save hash lookup on the same INCLUDE opcode */ + zend_op *cache_opline; + zend_persistent_script *cache_persistent_script; + /* preallocated buffer for keys */ + int key_len; + char key[MAXPATHLEN * 8]; +} zend_accel_globals; + +typedef struct _zend_accel_shared_globals { + /* Cache Data Structures */ + unsigned long hits; + unsigned long misses; + unsigned long blacklist_misses; + zend_accel_hash hash; /* hash table for cached scripts */ + zend_accel_hash include_paths; /* used "include_path" values */ + + /* Directives & Maintenance */ + time_t last_restart_time; + time_t force_restart_time; + zend_bool accelerator_enabled; + zend_bool restart_pending; + zend_bool cache_status_before_restart; +#ifdef ZEND_WIN32 + unsigned long mem_usage; + unsigned long restart_in; +#endif + zend_bool restart_in_progress; + time_t revalidate_at; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + /* Interned Strings Support */ + char *interned_strings_start; + char *interned_strings_top; + char *interned_strings_end; + HashTable interned_strings; + struct { + Bucket **arBuckets; + Bucket *pListHead; + Bucket *pListTail; + char *top; + } interned_strings_saved_state; +#endif +} zend_accel_shared_globals; + +extern zend_accel_shared_globals *accel_shared_globals; +#define ZCSG(element) (accel_shared_globals->element) + +#ifdef ZTS +# define ZCG(v) TSRMG(accel_globals_id, zend_accel_globals *, v) +extern int accel_globals_id; +#else +# define ZCG(v) (accel_globals.v) +extern zend_accel_globals accel_globals; +#endif + +extern char *zps_api_failure_reason; + +void zend_accel_schedule_restart(TSRMLS_D); +int accelerator_shm_read_lock(TSRMLS_D); +void accelerator_shm_read_unlock(TSRMLS_D); + +char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC); + +#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED) +# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145 +#endif + +#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80 + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC); + +# define interned_free(s) do { \ + if (!IS_INTERNED(s)) { \ + free(s); \ + } \ + } while (0) +# define interned_efree(s) do { \ + if (!IS_INTERNED(s)) { \ + efree(s); \ + } \ + } while (0) +# define interned_estrndup(s, n) \ + (IS_INTERNED(s) ? (s) : estrndup(s, n)) +# define ZEND_RESULT_TYPE(opline) (opline)->result_type +# define ZEND_RESULT(opline) (opline)->result +# define ZEND_OP1_TYPE(opline) (opline)->op1_type +# define ZEND_OP1(opline) (opline)->op1 +# define ZEND_OP1_CONST(opline) (*(opline)->op1.zv) +# define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant].constant +# define ZEND_OP2_TYPE(opline) (opline)->op2_type +# define ZEND_OP2(opline) (opline)->op2 +# define ZEND_OP2_CONST(opline) (*(opline)->op2.zv) +# define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant].constant +# define ZEND_DONE_PASS_TWO(op_array) (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0) +# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len +#else +# define IS_INTERNED(s) 0 +# define interned_free(s) free(s) +# define interned_efree(s) efree(s) +# define interned_estrndup(s, n) estrndup(s, n) +# define ZEND_RESULT_TYPE(opline) (opline)->result.op_type +# define ZEND_RESULT(opline) (opline)->result.u +# define ZEND_OP1_TYPE(opline) (opline)->op1.op_type +# define ZEND_OP1(opline) (opline)->op1.u +# define ZEND_OP1_CONST(opline) (opline)->op1.u.constant +# define ZEND_OP1_LITERAL(opline) (opline)->op1.u.constant +# define ZEND_OP2_TYPE(opline) (opline)->op2.op_type +# define ZEND_OP2(opline) (opline)->op2.u +# define ZEND_OP2_CONST(opline) (opline)->op2.u.constant +# define ZEND_OP2_LITERAL(opline) (opline)->op2.u.constant +# define ZEND_DONE_PASS_TWO(op_array) ((op_array)->done_pass_two != 0) +# define ZEND_CE_FILENAME(ce) (ce)->filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->doc_comment_len +#endif + +#endif /* ZEND_ACCELERATOR_H */ diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000000..6e178c1123 --- /dev/null +++ b/config.m4 @@ -0,0 +1,345 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(optimizer-plus, whether to enable Zend OptimizerPlus support, +[ --enable-optimizer-plus Enable Zend OptimizerPlus support]) + +if test "$PHP_OPTIMIZER_PLUS" != "no"; then + AC_DEFINE(HAVE_OPTIMIZER_PLUS, 1, [ ]) + + AC_CHECK_FUNC(mprotect,[ + AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function]) + ]) + + AC_MSG_CHECKING(for sysvipc shared memory support) + AC_TRY_RUN([ +#include +#include +#include +#include +#include +#include + +int main() { + pid_t pid; + int status; + int ipc_id; + char *shm; + struct shmid_ds shmbuf; + + ipc_id = shmget(IPC_PRIVATE, 4096, (IPC_CREAT | SHM_R | SHM_W)); + if (ipc_id == -1) { + return 1; + } + + shm = shmat(ipc_id, NULL, 0); + if (shm == (void *)-1) { + shmctl(ipc_id, IPC_RMID, NULL); + return 2; + } + + if (shmctl(ipc_id, IPC_STAT, &shmbuf) != 0) { + shmdt(shm); + shmctl(ipc_id, IPC_RMID, NULL); + return 3; + } + + shmbuf.shm_perm.uid = getuid(); + shmbuf.shm_perm.gid = getgid(); + shmbuf.shm_perm.mode = 0600; + + if (shmctl(ipc_id, IPC_SET, &shmbuf) != 0) { + shmdt(shm); + shmctl(ipc_id, IPC_RMID, NULL); + return 4; + } + + shmctl(ipc_id, IPC_RMID, NULL); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_IPC, 1, [Define if you have SysV IPC SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using MAP_ANON shared memory support) + AC_TRY_RUN([ +#include +#include +#include +#include +#include + +#ifndef MAP_ANON +# ifdef MAP_ANONYMOUS +# define MAP_ANON MAP_ANONYMOUS +# endif +#endif +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + char *shm; + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (shm == MAP_FAILED) { + return 1; + } + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_ANON, 1, [Define if you have mmap(MAP_ANON) SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using /dev/zero shared memory support) + AC_TRY_RUN([ +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + + fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 1; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 2; + } + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_ZERO, 1, [Define if you have mmap("/dev/zero") SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using shm_open() shared memory support) + AC_TRY_RUN([ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + char tmpname[4096]; + + sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); + if (mktemp(tmpname) == NULL) { + return 1; + } + fd = shm_open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 2; + } + if (ftruncate(fd, 4096) < 0) { + close(fd); + shm_unlink(tmpname); + return 3; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 4; + } + shm_unlink(tmpname); + close(fd); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_POSIX, 1, [Define if you have POSIX mmap() SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using regular file shared memory support) + AC_TRY_RUN([ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + char tmpname[4096]; + + sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); + if (mktemp(tmpname) == NULL) { + return 1; + } + fd = open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 2; + } + if (ftruncate(fd, 4096) < 0) { + close(fd); + unlink(tmpname); + return 3; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 4; + } + unlink(tmpname); + close(fd); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_FILE, 1, [Define if you have mmap() SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + PHP_NEW_EXTENSION(ZendOptimizerPlus, + ZendAccelerator.c \ + zend_accelerator_blacklist.c \ + zend_accelerator_debug.c \ + zend_accelerator_hash.c \ + zend_accelerator_module.c \ + zend_persist.c \ + zend_persist_calc.c \ + zend_shared_alloc.c \ + zend_accelerator_util_funcs.c \ + shared_alloc_shm.c \ + shared_alloc_mmap.c \ + shared_alloc_posix.c \ + Optimizer/zend_optimizer.c, + $ext_shared) +fi diff --git a/config.w32 b/config.w32 new file mode 100644 index 0000000000..6f3c89c135 --- /dev/null +++ b/config.w32 @@ -0,0 +1,28 @@ +ARG_ENABLE("optimizer-plus", "whether to enable Zend OptimizerPlus support", "yes"); + +if (PHP_OPTIMIZER_PLUS != "no") { + + PHP_PGI = "no"; // workaround + PHP_PGO = "no"; // workaround + + EXTENSION('ZendOptimizerPlus', "\ + ZendAccelerator.c \ + zend_accelerator_blacklist.c \ + zend_accelerator_debug.c \ + zend_accelerator_hash.c \ + zend_accelerator_module.c \ + zend_accelerator_util_funcs.c \ + zend_persist.c \ + zend_persist_calc.c \ + zend_shared_alloc.c \ + shared_alloc_win32.c", true); + + ADD_SOURCES("Optimizer", "zend_optimizer.c", "ZendOptimizerPlus", "OptimizerObj"); + + + ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/I ."); + ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/D HAVE_OPTIMIZER_PLUS=1"); + + ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/Dregexec=php_regexec /Dregerror=php_regerror /Dregfree=php_regfree /Dregcomp=php_regcomp /Iext/ereg/regex"); + +} diff --git a/shared_alloc_mmap.c b/shared_alloc_mmap.c new file mode 100644 index 0000000000..7e23d4ea8d --- /dev/null +++ b/shared_alloc_mmap.c @@ -0,0 +1,70 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#include + +#include "zend_shared_alloc.h" + +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif + +static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + zend_shared_segment *shared_segment; + + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *)); + shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + shared_segment->p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if(shared_segment->p == MAP_FAILED) { + *error_in = "mmap"; + return ALLOC_FAILURE; + } + + shared_segment->pos = 0; + shared_segment->size = requested_size; + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment *shared_segment) +{ + munmap(shared_segment->p, shared_segment->size); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment); +} + +zend_shared_memory_handlers zend_alloc_mmap_handlers = { + create_segments, + detach_segment, + segment_type_size +}; diff --git a/shared_alloc_posix.c b/shared_alloc_posix.c new file mode 100644 index 0000000000..5a0111c797 --- /dev/null +++ b/shared_alloc_posix.c @@ -0,0 +1,90 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "zend_shared_alloc.h" + +typedef struct { + zend_shared_segment common; + int shm_fd; +} zend_shared_segment_posix; + +static int create_segments(size_t requested_size, zend_shared_segment_posix ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + zend_shared_segment_posix *shared_segment; + char shared_segment_name[sizeof("/ZendAccelerator.")+20]; + + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment_posix **) calloc(1, sizeof(zend_shared_segment_posix)+sizeof(void *)); + shared_segment = (zend_shared_segment_posix *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + sprintf(shared_segment_name, "/ZendAccelerator.%d", getpid()); + shared_segment->shm_fd = shm_open(shared_segment_name, O_RDWR|O_CREAT|O_TRUNC, 0600); + if(shared_segment->shm_fd == -1) { + *error_in = "shm_open"; + return ALLOC_FAILURE; + } + + if(ftruncate(shared_segment->shm_fd, requested_size) != 0) { + *error_in = "ftruncate"; + shm_unlink(shared_segment_name); + return ALLOC_FAILURE; + } + + shared_segment->common.p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_segment->shm_fd, 0); + if(shared_segment->common.p == MAP_FAILED) { + *error_in = "mmap"; + shm_unlink(shared_segment_name); + return ALLOC_FAILURE; + } + shm_unlink(shared_segment_name); + + shared_segment->common.pos = 0; + shared_segment->common.size = requested_size; + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment_posix *shared_segment) +{ + munmap(shared_segment->common.p, shared_segment->common.size); + close(shared_segment->shm_fd); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment_posix); +} + +zend_shared_memory_handlers zend_alloc_posix_handlers = { + (create_segments_t)create_segments, + (detach_segment_t)detach_segment, + segment_type_size +}; diff --git a/shared_alloc_shm.c b/shared_alloc_shm.c new file mode 100644 index 0000000000..45fab7c4a3 --- /dev/null +++ b/shared_alloc_shm.c @@ -0,0 +1,137 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#if defined(__FreeBSD__) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "zend_shared_alloc.h" + +#ifndef MIN +# define MIN(x, y) ((x) > (y)? (y) : (x)) +#endif + +#define SEG_ALLOC_SIZE_MAX 32*1024*1024 +#define SEG_ALLOC_SIZE_MIN 2*1024*1024 + +typedef struct { + zend_shared_segment common; + int shm_id; +} zend_shared_segment_shm; + +static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + int i; + unsigned int allocate_size=0, remaining_bytes=requested_size, seg_allocate_size; + int first_segment_id=-1; + key_t first_segment_key = -1; + struct shmid_ds sds; + int shmget_flags; + zend_shared_segment_shm *shared_segments; + + seg_allocate_size = SEG_ALLOC_SIZE_MAX; + /* determine segment size we _really_ need: + * no more than to include requested_size + */ + while (requested_size*2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) { + seg_allocate_size >>= 1; + } + + shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL; + + /* try allocating this much, if not - try shrinking */ + while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) { + allocate_size = MIN(requested_size, seg_allocate_size); + first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags); + if (first_segment_id != -1) { + break; + } + seg_allocate_size >>= 1; /* shrink the allocated block */ + } + + if (first_segment_id == -1) { + *error_in = "shmget"; + return ALLOC_FAILURE; + } + + *shared_segments_count = ((requested_size-1)/seg_allocate_size) + 1; + *shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count)*sizeof(zend_shared_segment_shm)+sizeof(void *)*(*shared_segments_count)); + shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *)*(*shared_segments_count)); + for(i=0; i<*shared_segments_count; i++) { + (*shared_segments_p)[i] = shared_segments+i; + } + + remaining_bytes = requested_size; + for (i=0; i<*shared_segments_count; i++) { + allocate_size = MIN(remaining_bytes, seg_allocate_size); + if (i != 0) { + shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags); + } else { + shared_segments[i].shm_id = first_segment_id; + } + + if (shared_segments[i].shm_id==-1) { + return ALLOC_FAILURE; + } + + shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0); + if (((int) shared_segments[i].common.p) == -1) { + *error_in = "shmat"; + shmctl(shared_segments[i].shm_id, IPC_RMID, &sds); + return ALLOC_FAILURE; + } + shmctl(shared_segments[i].shm_id, IPC_RMID, &sds); + + shared_segments[i].common.pos = 0; + shared_segments[i].common.size = allocate_size; + remaining_bytes -= allocate_size; + } + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment_shm *shared_segment) +{ + shmdt(shared_segment->common.p); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment_shm); +} + +zend_shared_memory_handlers zend_alloc_shm_handlers = { + (create_segments_t)create_segments, + (detach_segment_t)detach_segment, + segment_type_size +}; diff --git a/shared_alloc_win32.c b/shared_alloc_win32.c new file mode 100644 index 0000000000..5388ce1ec5 --- /dev/null +++ b/shared_alloc_win32.c @@ -0,0 +1,315 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_util_funcs.h" +#include +#include +#include + +#define ACCEL_FILEMAP_NAME "ZendOptimizer+.SharedMemoryArea" +#define ACCEL_MUTEX_NAME "ZendOptimizer+.SharedMemoryMutex" +#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000 +#define ACCEL_FILEMAP_BASE "ZendOptimizer+.MemoryBase" +#define ACCEL_EVENT_SOURCE "Zend Optimizer+" + +static HANDLE memfile = NULL, memory_mutex = NULL; +static void *mapping_base; + +#define MAX_MAP_RETRIES 25 + +static void zend_win_error_message(int type, char *msg, int err) +{ + LPVOID lpMsgBuf; + FILE *fp; + HANDLE h; + char *ev_msgs[2]; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE)); + ev_msgs[0] = msg; + ev_msgs[1] = lpMsgBuf; + ReportEvent(h, // event log handle + EVENTLOG_ERROR_TYPE, // event type + 0, // category zero + err, // event identifier + NULL, // no user security identifier + 2, // one substitution string + 0, // no data + ev_msgs, // pointer to string array + NULL); // pointer to data + DeregisterEventSource(h); + + LocalFree( lpMsgBuf ); + + zend_accel_error(type, msg); +} + +static char *create_name_with_username(char *name) +{ + static char newname[MAXPATHLEN+UNLEN+4]; + char uname[UNLEN+1]; + DWORD unsize = UNLEN; + + GetUserName(uname, &unsize); + snprintf(newname, sizeof(newname)-1, "%s@%s", name, uname); + return newname; +} + +static char *get_mmap_base_file() +{ + static char windir[MAXPATHLEN+UNLEN+3+sizeof("\\\\@")]; + char uname[UNLEN+1]; + DWORD unsize = UNLEN; + int l; + + GetTempPath(MAXPATHLEN, windir); + GetUserName(uname, &unsize); + l = strlen(windir); + snprintf(windir+l, sizeof(windir)-l-1, "\\%s@%s", ACCEL_FILEMAP_BASE, uname); + return windir; +} + +void zend_shared_alloc_create_lock(void) +{ + memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME)); + ReleaseMutex(memory_mutex); +} + +void zend_shared_alloc_lock_win32() +{ + DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE); + + if(waitRes == WAIT_FAILED) { + zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex"); + } +} + +void zend_shared_alloc_unlock_win32(TSRMLS_D) +{ + ReleaseMutex(memory_mutex); +} + +static int zend_shared_alloc_reattach(size_t requested_size, char **error_in) +{ + int err; + void *wanted_mapping_base; + char *mmap_base_file = get_mmap_base_file(); + FILE *fp = fopen(mmap_base_file, "r"); + MEMORY_BASIC_INFORMATION info; + + err = GetLastError(); + if(!fp) { + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err); + *error_in="fopen"; + return ALLOC_FAILURE; + } + if(!fscanf(fp, "%p", &wanted_mapping_base)) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err); + *error_in="read mapping base"; + return ALLOC_FAILURE; + } + fclose(fp); + + /* Check if the requested address space is free */ + if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 || + info.State != MEM_FREE || + info.RegionSize < requested_size) { + err = ERROR_INVALID_ADDRESS; + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err); + return ALLOC_FAILURE; + } + + mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base); + err = GetLastError(); + + if(mapping_base == NULL) { + if (err == ERROR_INVALID_ADDRESS) { + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err); + return ALLOC_FAILURE; + } + return ALLOC_FAIL_MAPPING; + } + smm_shared_globals = (zend_smm_shared_globals *) (((char *) mapping_base) + sizeof(zend_shared_memory_block_header)); + + return SUCCESSFULLY_REATTACHED; +} + +static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + int err, ret; + zend_shared_segment *shared_segment; + int map_retries = 0; + void *default_mapping_base_set[] = { 0, 0 }; + void *vista_mapping_base_set[] = { (void *)0x20000000, (void *)0x21000000, (void *)0x30000000, (void *)0x31000000, (void *)0x50000000, 0 }; + void **wanted_mapping_base = default_mapping_base_set; + TSRMLS_FETCH(); + + /* Mapping retries: When Apache2 restarts, the parent process startup routine + can be called before the child process is killed. In this case, the map will fail + and we have to sleep some time (until the child releases the mapping object) and retry.*/ + do { + memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME)); + err = GetLastError(); + if (memfile == NULL) + break; + + ret = zend_shared_alloc_reattach(requested_size, error_in); + err = GetLastError(); + if (ret == ALLOC_FAIL_MAPPING) { + /* Mapping failed, wait for mapping object to get freed and retry */ + CloseHandle(memfile); + memfile = NULL; + Sleep(1000*(map_retries+1)); + } else { + return ret; + } + } while(++map_retries < MAX_MAP_RETRIES); + + if(map_retries == MAX_MAP_RETRIES) { + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err); + *error_in = "OpenFileMapping"; + return ALLOC_FAILURE; + } + + /* creating segment here */ + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *)); + shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size, + create_name_with_username(ACCEL_FILEMAP_NAME)); + err = GetLastError(); + if(memfile == NULL) { + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err); + *error_in = "CreateFileMapping"; + return ALLOC_FAILURE; + } + + /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to + be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses + in high memory. */ + if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) { + do { + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + + ZeroMemory(&si, sizeof(SYSTEM_INFO)); + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if (! GetVersionEx ((OSVERSIONINFO *) &osvi)) { + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) + break; + } + + GetSystemInfo(&si); + + /* Are we running Vista ? */ + if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 6 ) { + /* Assert that platform is 32 bit (for 64 bit we need to test a different set */ + if(si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) + DebugBreak(); + + wanted_mapping_base = vista_mapping_base_set; + } + } while (0); + } else { + char *s = ZCG(accel_directives).mmap_base; + + /* skip leading 0x, %p assumes hexdeciaml format anyway */ + if (*s == '0' && *(s+1) == 'x') { + s += 2; + } + if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) { + zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in zend_optimizerplus.mmap_base", err); + return ALLOC_FAILURE; + } + } + + do { + shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base); + if(*wanted_mapping_base == NULL) /* Auto address (NULL) is the last option on the array */ + break; + wanted_mapping_base++; + } while (!mapping_base); + + err = GetLastError(); + if(mapping_base == NULL) { + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err); + *error_in = "MapViewOfFile"; + return ALLOC_FAILURE; + } else { + char *mmap_base_file = get_mmap_base_file(); + FILE *fp = fopen(mmap_base_file, "w"); + err = GetLastError(); + if(!fp) { + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err); + } + fprintf(fp, "%p\n", mapping_base); + fclose(fp); + } + + shared_segment->pos = 0; + shared_segment->size = requested_size; + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment *shared_segment) +{ + if(mapping_base) { + UnmapViewOfFile(mapping_base); + } + CloseHandle(memfile); + ReleaseMutex(memory_mutex); + CloseHandle(memory_mutex); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment); +} + +zend_shared_memory_handlers zend_alloc_win32_handlers = { + create_segments, + detach_segment, + segment_type_size +}; diff --git a/zend_accelerator_blacklist.c b/zend_accelerator_blacklist.c new file mode 100644 index 0000000000..7ceadeacf3 --- /dev/null +++ b/zend_accelerator_blacklist.c @@ -0,0 +1,241 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "main/php.h" +#include "main/fopen_wrappers.h" +#include "ZendAccelerator.h" +#include "zend_accelerator_blacklist.h" + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +# include "ext/ereg/php_regex.h" +#else +# include "main/php_regex.h" +#endif + +#ifdef ZEND_WIN32 +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE) +#else +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB) +#endif + +#define ZEND_BLACKLIST_BLOCK_SIZE 32 + +struct _zend_regexp_list { + regex_t comp_regex; + zend_regexp_list *next; +}; + +zend_blacklist accel_blacklist; + +void zend_accel_blacklist_init(zend_blacklist *blacklist) +{ + blacklist->pos = 0; + blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE; + + if( blacklist->entries != NULL ){ + zend_accel_blacklist_shutdown(blacklist); + } + + blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size); + blacklist->regexp_list = NULL; +} + +static void blacklist_report_regexp_error(regex_t *comp_regex, int reg_err) +{ + char *errbuf; + int errsize = regerror(reg_err, comp_regex, NULL, 0); + errbuf = malloc(errsize); + + regerror(reg_err, comp_regex, errbuf, errsize); + zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: %s\n", errbuf); + free(errbuf); +} + +static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist) +{ + int i, end=0, j, rlen=6, clen, reg_err; + char *regexp; + zend_regexp_list **regexp_list_it; + + if (blacklist->pos == 0) { + /* we have no blacklist to talk about */ + return; + } + + regexp_list_it = &(blacklist->regexp_list); + for (i=0; ipos; i++) { + rlen += blacklist->entries[i].path_length*2+2; + + /* don't create a regexp buffer bigger than 12K)*/ + if((i+1 == blacklist->pos) || ((rlen+blacklist->entries[i+1].path_length*2+2)>(12*1024) ) ) { + regexp = (char *)malloc(rlen); + regexp[0] = '^'; + regexp[1] = '('; + + clen=2; + for (j=end; j<=i ;j++) { + + int c; + if (j!=end) { + regexp[clen++] = '|'; + } + /* copy mangled filename */ + for(c=0; centries[j].path_length; c++) { + if(strchr("^.[]$()|*+?{}\\", blacklist->entries[j].path[c])) { + regexp[clen++] = '\\'; + } + regexp[clen++] = blacklist->entries[j].path[c]; + } + } + regexp[clen++] = ')'; + regexp[clen] = '\0'; + + (*regexp_list_it) = malloc(sizeof(zend_regexp_list)); + (*regexp_list_it)->next = NULL; + + if ((reg_err = regcomp(&((*regexp_list_it)->comp_regex), regexp, REGEX_MODE)) != 0) { + blacklist_report_regexp_error(&((*regexp_list_it)->comp_regex), reg_err); + } + /* prepare for the next iteration */ + free(regexp); + end = i+1; + rlen = 6; + regexp_list_it = &((*regexp_list_it)->next); + } + } +} + +void zend_accel_blacklist_shutdown(zend_blacklist *blacklist) +{ + zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos; + + while (ppath); + p++; + } + free(blacklist->entries); + blacklist->entries = NULL; + if (blacklist->regexp_list) { + zend_regexp_list *temp, *it = blacklist->regexp_list; + while( it ){ + regfree(&it->comp_regex); + temp = it; + it = it->next; + free(temp); + } + } +} + +static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) +{ + if (blacklist->pos==blacklist->size) { + blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE; + blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size); + } +} + +void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) +{ + char buf[MAXPATHLEN+1], real_path[MAXPATHLEN+1]; + FILE *fp; + int path_length; + TSRMLS_FETCH(); + + if ((fp=fopen(filename, "r"))==NULL) { + zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename); + return; + } + + zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file: '%s'", filename); + + memset(buf, 0, sizeof(buf)); + memset(real_path, 0, sizeof(real_path)); + + while (fgets(buf, MAXPATHLEN, fp)!=NULL) { + char *path_dup, *pbuf; + path_length = strlen(buf); + if (buf[path_length-1]=='\n') { + buf[--path_length] = 0; + if (buf[path_length-1]=='\r') { + buf[--path_length] = 0; + } + } + + /* Strip ctrl-m prefix */ + pbuf = &buf[0]; + while(*pbuf == '\r') { + *pbuf++ = 0; + path_length--; + } + + /* strip \" */ + if( pbuf[0] == '\"' && pbuf[path_length-1]== '\"' ){ + *pbuf++ = 0; + path_length-=2; + } + + if (path_length==0) { + continue; + } + + path_dup = zend_strndup(pbuf, path_length); + expand_filepath(path_dup, real_path TSRMLS_CC); + path_length = strlen(real_path); + + free(path_dup); + + zend_accel_blacklist_allocate(blacklist); + blacklist->entries[blacklist->pos].path_length = path_length; + blacklist->entries[blacklist->pos].path = (char *) malloc(path_length+1); + blacklist->entries[blacklist->pos].id = blacklist->pos; + memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length+1); + blacklist->pos++; + } + fclose(fp); + zend_accel_blacklist_update_regexp(blacklist); +} + +zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path) +{ + int ret = 0; + zend_regexp_list *regexp_list_it = blacklist->regexp_list; + + if (regexp_list_it == NULL) { + return 0; + } + while (regexp_list_it != NULL) { + if (regexec(&(regexp_list_it->comp_regex), verify_path, 0, NULL, 0) == 0) { + ret = 1; + break; + } + regexp_list_it = regexp_list_it->next; + } + return ret; +} + +void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC) +{ + int i; + + for(i=0; ipos; i++) { + func(&blacklist->entries[i], argument TSRMLS_CC); + } +} diff --git a/zend_accelerator_blacklist.h b/zend_accelerator_blacklist.h new file mode 100644 index 0000000000..5d7730f470 --- /dev/null +++ b/zend_accelerator_blacklist.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_BLACKLIST_H +#define ZEND_ACCELERATOR_BLACKLIST_H + +typedef struct _zend_regexp_list zend_regexp_list; + +typedef struct _zend_blacklist_entry { + char *path; + int path_length; + int id; +} zend_blacklist_entry; + +typedef struct _zend_blacklist { + zend_blacklist_entry *entries; + int size; + int pos; + zend_regexp_list *regexp_list; +} zend_blacklist; + +extern zend_blacklist accel_blacklist; + +void zend_accel_blacklist_init(zend_blacklist *blacklist); +void zend_accel_blacklist_shutdown(zend_blacklist *blacklist); + +void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename); +zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path); +void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC); + +#endif /* ZEND_ACCELERATOR_BLACKLIST_H */ diff --git a/zend_accelerator_debug.c b/zend_accelerator_debug.c new file mode 100644 index 0000000000..8b2bd6241c --- /dev/null +++ b/zend_accelerator_debug.c @@ -0,0 +1,101 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#ifdef ZEND_WIN32 +# include +#endif +#include "ZendAccelerator.h" + +void zend_accel_error(int type, const char *format, ...) +{ + va_list args; + time_t timestamp; + char *time_string; + FILE * fLog = NULL; + TSRMLS_FETCH(); + + if (type > ZCG(accel_directives).log_verbosity_level) { + return; + } + + timestamp = time(NULL); + time_string = asctime(localtime(×tamp)); + time_string[24] = 0; + + if (!ZCG(accel_directives).error_log || + !*ZCG(accel_directives).error_log || + strcmp(ZCG(accel_directives).error_log, "stderr") == 0) { + + fLog = stderr; + } else { + fLog = fopen(ZCG(accel_directives).error_log, "a+"); + if (!fLog) { + fLog = stderr; + } + } + + fprintf(fLog, "%s (%d): ", time_string, +#ifdef ZTS + tsrm_thread_id() +#else + getpid() +#endif + ); + + switch (type) { + case ACCEL_LOG_FATAL: + fprintf(fLog, "Fatal Error "); + break; + case ACCEL_LOG_ERROR: + fprintf(fLog, "Error "); + break; + case ACCEL_LOG_WARNING: + fprintf(fLog, "Warning "); + break; + case ACCEL_LOG_INFO: + fprintf(fLog, "Message "); + break; + case ACCEL_LOG_DEBUG: + fprintf(fLog, "Debug "); + break; + } + + va_start(args, format); + vfprintf(fLog, format, args); + va_end(args); + fprintf(fLog, "\n"); + switch (type) { + case ACCEL_LOG_ERROR: + zend_bailout(); + break; + case ACCEL_LOG_FATAL: + exit(-2); + break; + } + fflush(fLog); + if (fLog != stderr) { + fclose(fLog); + } +} diff --git a/zend_accelerator_debug.h b/zend_accelerator_debug.h new file mode 100644 index 0000000000..8d0d7320bf --- /dev/null +++ b/zend_accelerator_debug.h @@ -0,0 +1,33 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_DEBUG_H +#define ZEND_ACCELERATOR_DEBUG_H + +#define ACCEL_LOG_FATAL 0 +#define ACCEL_LOG_ERROR 1 +#define ACCEL_LOG_WARNING 2 +#define ACCEL_LOG_INFO 3 +#define ACCEL_LOG_DEBUG 4 + +void zend_accel_error(int type, const char *format, ...); + +#endif /* _ZEND_ACCELERATOR_DEBUG_H */ diff --git a/zend_accelerator_hash.c b/zend_accelerator_hash.c new file mode 100644 index 0000000000..8257ac2e52 --- /dev/null +++ b/zend_accelerator_hash.c @@ -0,0 +1,222 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "ZendAccelerator.h" +#include "zend_accelerator_hash.h" +#include "zend_hash.h" +#include "zend_shared_alloc.h" + +/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */ +static uint prime_numbers[] = + {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 }; +static uint num_prime_numbers = sizeof(prime_numbers) / sizeof(uint); + +void zend_accel_hash_clean(zend_accel_hash *accel_hash) +{ + accel_hash->num_entries = 0; + accel_hash->num_direct_entries = 0; + memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); +} + +void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size) +{ + uint i; + + for (i=0; inum_entries = 0; + accel_hash->num_direct_entries = 0; + accel_hash->max_num_entries = hash_size; + + /* set up hash pointers table */ + accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); + if (!accel_hash->hash_table) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + } + + /* set up hash values table */ + accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries); + if (!accel_hash->hash_entries) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + } + memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); +} + +/* Returns NULL if hash is full + * Returns pointer the actual hash entry on success + * key needs to be already allocated as it is not copied + */ +zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, char *key, zend_uint key_length, zend_bool indirect, void *data) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + zend_accel_hash_entry *indirect_bucket = NULL; + + if (indirect) { + indirect_bucket = (zend_accel_hash_entry*)data; + while (indirect_bucket->indirect) { + indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data; + } + } + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + /* try to see if the element already exists in the hash */ + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + + if (entry->indirect) { + if (indirect_bucket) { + entry->data = indirect_bucket; + } else { + ((zend_accel_hash_entry*)entry->data)->data = data; + } + } else { + if (indirect_bucket) { + accel_hash->num_direct_entries--; + entry->data = indirect_bucket; + entry->indirect = 1; + } else { + entry->data = data; + } + } + return entry; + } + entry = entry->next; + } + + /* Does not exist, add a new entry */ + if (accel_hash->num_entries == accel_hash->max_num_entries) { + return NULL; + } + + entry = &accel_hash->hash_entries[accel_hash->num_entries++]; + if (indirect) { + entry->data = indirect_bucket; + entry->indirect = 1; + } else { + accel_hash->num_direct_entries++; + entry->data = data; + entry->indirect = 0; + } + entry->hash_value = hash_value; + entry->key = key; + entry->key_length = key_length; + entry->next = accel_hash->hash_table[index]; + accel_hash->hash_table[index] = entry; + return entry; +} + +/* Returns the data associated with key on success + * Returns NULL if data doesn't exist + */ +void* zend_accel_hash_find(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (entry->indirect) { + return ((zend_accel_hash_entry *) entry->data)->data; + } else { + return entry->data; + } + } + entry = entry->next; + } + return NULL; +} + +/* Returns the hash entry associated with key on success + * Returns NULL if it doesn't exist + */ +zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (entry->indirect) { + return (zend_accel_hash_entry *) entry->data; + } else { + return entry; + } + } + entry = entry->next; + } + return NULL; +} + +int zend_accel_hash_unlink(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry, *last_entry=NULL; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (!entry->indirect) { + accel_hash->num_direct_entries--; + } + if (last_entry) { + last_entry->next = entry->next; + } else { + accel_hash->hash_table[index] = entry->next; + } + return SUCCESS; + } + last_entry = entry; + entry = entry->next; + } + return FAILURE; +} diff --git a/zend_accelerator_hash.h b/zend_accelerator_hash.h new file mode 100644 index 0000000000..4673a5e425 --- /dev/null +++ b/zend_accelerator_hash.h @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_HASH_H +#define ZEND_ACCELERATOR_HASH_H + +#include "zend.h" + +/* + zend_accel_hash - is a hash table allocated in shared memory and + distributed across simultaneously running processes. The hash tables have + fixed sizen selected during construction by zend_accel_hash_init(). All the + hash entries are preallocated in the 'hash_entries' array. 'num_entries' is + initialized by zero and grows when new data is added. + zend_accel_hash_update() just takes the next entry from 'hash_entries' + array and puts it into appropriate place of 'hash_table'. + Hash collisions are resolved by separate chaining with linked lists, + however, entries are still taken from the same 'hash_entries' array. + 'key' and 'data' passed to zend_accel_hash_update() must be already + allocated in shared memory. Few keys may be resolved to the same data. + using 'indirect' emtries, that point to other entries ('data' is actually + a pointer to another zend_accel_hash_entry). + zend_accel_hash_update() requires exclusive lock, however, + zend_accel_hash_find() does not. +*/ + +typedef struct _zend_accel_hash_entry zend_accel_hash_entry; + +struct _zend_accel_hash_entry { + zend_ulong hash_value; + char *key; + zend_uint key_length; + zend_accel_hash_entry *next; + void *data; + zend_bool indirect; +}; + +typedef struct _zend_accel_hash { + zend_accel_hash_entry **hash_table; + zend_accel_hash_entry *hash_entries; + zend_uint num_entries; + zend_uint max_num_entries; + zend_uint num_direct_entries; +} zend_accel_hash; + +void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size); +void zend_accel_hash_clean(zend_accel_hash *accel_hash); + +zend_accel_hash_entry* zend_accel_hash_update( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length, + zend_bool indirect, + void *data); + +void* zend_accel_hash_find( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +zend_accel_hash_entry* zend_accel_hash_find_entry( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +int zend_accel_hash_unlink( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +static inline zend_bool zend_accel_hash_is_full(zend_accel_hash *accel_hash) +{ + if (accel_hash->num_entries == accel_hash->max_num_entries) { + return 1; + } else { + return 0; + } +} + +#endif /* ZEND_ACCELERATOR_HASH_H */ diff --git a/zend_accelerator_module.c b/zend_accelerator_module.c new file mode 100644 index 0000000000..b1c90a0eac --- /dev/null +++ b/zend_accelerator_module.c @@ -0,0 +1,574 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include + +#include "php.h" +#include "ZendAccelerator.h" +#include "zend_API.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_blacklist.h" +#include "php_ini.h" +#include "SAPI.h" +#include "TSRM/tsrm_virtual_cwd.h" +#include "ext/standard/info.h" +#include "ext/standard/php_filestat.h" + +#define STRING_NOT_NULL(s) (NULL == (s)?"":s) +#define MIN_ACCEL_FILES 200 +#define MAX_ACCEL_FILES 100000 +#define TOKENTOSTR(X) #X + +/* User functions */ +static ZEND_FUNCTION(accelerator_reset); + +/* Private functions */ +static ZEND_FUNCTION(accelerator_get_status); +static ZEND_FUNCTION(accelerator_get_configuration); + +static zend_function_entry accel_functions[] = { + /* User functions */ + ZEND_FE(accelerator_reset, NULL) + /* Private functions */ + ZEND_FE(accelerator_get_configuration, NULL) + ZEND_FE(accelerator_get_status, NULL) + { NULL, NULL, NULL, 0, 0 } +}; + +static ZEND_INI_MH(OnUpdateMemoryConsumption) +{ + long *p; + long memsize; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (long *) (base+(size_t) mh_arg1); + memsize = atoi(new_value); + /* sanity check we must use at least 8 MB */ + if (memsize < 8) { + const char *new_new_value = "8"; + zend_ini_entry *ini_entry; + + memsize = 8; + zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.memory_consumption is set below the required 8MB.\n" ); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB cofiguration.\n" ); + + if (zend_hash_find(EG(ini_directives), + "zend_optimizerplus.memory_consumption", + sizeof("zend_optimizerplus.memory_consumption"), + (void *) &ini_entry)==FAILURE) { + return FAILURE; + } + + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = memsize * (1024 * 1024); + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles) +{ + long *p; + long size; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (long *) (base+(size_t) mh_arg1); + size = atoi(new_value); + /* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */ + + if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) { + const char *new_new_value; + zend_ini_entry *ini_entry; + + if(size < MIN_ACCEL_FILES){ + size = MIN_ACCEL_FILES; + new_new_value = TOKENTOSTR(MIN_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES ); + zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use the minimal cofiguration.\n" ); + } + if(size > MAX_ACCEL_FILES){ + size = MAX_ACCEL_FILES; + new_new_value = TOKENTOSTR(MAX_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES ); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal cofiguration.\n" ); + } + if (zend_hash_find(EG(ini_directives), + "zend_optimizerplus.max_accelerated_files", + sizeof("zend_optimizerplus.max_accelerated_files"), + (void *) &ini_entry)==FAILURE) { + return FAILURE; + } + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = size; + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateMaxWastedPercentage) +{ + double *p; + long percentage; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (double *) (base+(size_t) mh_arg1); + percentage = atoi(new_value); + + if (percentage <= 0 || percentage > 50) { + const char *new_new_value = "5"; + zend_ini_entry *ini_entry; + + percentage = 5; + zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_wasted_percentage must be ser netweeb 1 and 50.\n"); + zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use 5%.\n" ); + if (zend_hash_find(EG(ini_directives), + "zend_optimizerplus.max_wasted_percentage", + sizeof("zend_optimizerplus.max_wasted_percentage"), + (void *) &ini_entry)==FAILURE) { + return FAILURE; + } + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = (double)percentage / 100.0; + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateAccelBlacklist) +{ + char **p; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + if (new_value && !new_value[0]) { + return FAILURE; + } + + p = (char **) (base+(size_t) mh_arg1); + *p = new_value; + + zend_accel_blacklist_init(&accel_blacklist); + zend_accel_blacklist_load(&accel_blacklist, *p); + + return SUCCESS; +} + +ZEND_INI_BEGIN() + STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable" ,"1", PHP_INI_SYSTEM, OnUpdateBool, enabled , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.use_cwd" ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.validate_timestamps","1", PHP_INI_ALL , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.inherited_hack" ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.dups_fix" ,"0", PHP_INI_ALL , OnUpdateBool, accel_directives.ignore_dups , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.revalidate_path" ,"0", PHP_INI_ALL , OnUpdateBool, accel_directives.revalidate_path , zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("zend_optimizerplus.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.memory_consumption" , "64" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals) +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + STD_PHP_INI_ENTRY("zend_optimizerplus.interned_strings_buffer", "4" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals) +#endif + STD_PHP_INI_ENTRY("zend_optimizerplus.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.force_restart_timeout, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.revalidate_freq" , "2" , PHP_INI_ALL , OnUpdateLong, accel_directives.revalidate_freq, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.preferred_memory_model", "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.memory_model, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.blacklist_filename" , "" , PHP_INI_SYSTEM, OnUpdateAccelBlacklist, accel_directives.user_blacklist_filename, zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("zend_optimizerplus.protect_memory" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.protect_memory, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.save_comments" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.save_comments, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("zend_optimizerplus.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("zend_optimizerplus.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals) + +#ifdef ZEND_WIN32 + STD_PHP_INI_ENTRY("zend_optimizerplus.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals) +#endif +ZEND_INI_END() + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +#undef EX +#define EX(element) execute_data->element +#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) + +static int ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_class_entry **pce, **pce_orig; + + if (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op2.u.constant), Z_STRLEN(EX(opline)->op2.u.constant)+1, (void **)&pce) == FAILURE || + (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op1.u.constant), Z_STRLEN(EX(opline)->op1.u.constant), (void**)&pce_orig) == SUCCESS && + *pce != *pce_orig)) { + do_bind_inherited_class(EX(opline), EG(class_table), EX_T(EX(opline)->extended_value).class_entry, 0 TSRMLS_CC); + } + EX(opline)++; + return ZEND_USER_OPCODE_CONTINUE; +} +#endif + +static int filename_is_in_cache(char *filename, int filename_len TSRMLS_DC) +{ + char *key; + int key_length; + zend_file_handle handle = {0}; + zend_persistent_script *persistent_script; + + handle.filename = filename; + handle.type = ZEND_HANDLE_FILENAME; + + if (IS_ABSOLUTE_PATH(filename, filename_len)) { + persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len+1); + if (persistent_script) { + return !persistent_script->corrupted; + } + } + + if((key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC)) != NULL) { + persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1); + return persistent_script && !persistent_script->corrupted; + } + + return 0; +} + +static void accel_file_in_cache(int type, INTERNAL_FUNCTION_PARAMETERS) +{ + char *filename; + int filename_len; +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zval **zfilename; + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &zfilename) == FAILURE) { + WRONG_PARAM_COUNT; + } + convert_to_string_ex(zfilename); + filename = Z_STRVAL_PP(zfilename); + filename_len = Z_STRLEN_PP(zfilename); +#else + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { + return; + } +#endif + if(filename_len > 0) { + if(filename_is_in_cache(filename, filename_len TSRMLS_CC)) { + RETURN_TRUE; + } + } + + php_stat(filename, filename_len, type, return_value TSRMLS_CC); +} + +static void accel_file_exists(INTERNAL_FUNCTION_PARAMETERS) +{ + accel_file_in_cache(FS_EXISTS, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +static void accel_is_file(INTERNAL_FUNCTION_PARAMETERS) +{ + accel_file_in_cache(FS_IS_FILE, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +static void accel_is_readable(INTERNAL_FUNCTION_PARAMETERS) +{ + accel_file_in_cache(FS_IS_R, INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +static ZEND_MINIT_FUNCTION(zend_accelerator) +{ + (void)type; /* keep the compiler happy */ + + /* must be 0 before the ini entry OnUpdate function is called */ + accel_blacklist.entries = NULL; + + REGISTER_INI_ENTRIES(); +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_set_user_opcode_handler(ZEND_DECLARE_INHERITED_CLASS_DELAYED, ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER); +#endif + return SUCCESS; +} + +void zend_accel_override_file_functions(TSRMLS_D) +{ + zend_function *old_function; + if(ZCG(startup_ok) && ZCG(accel_directives).file_override_enabled) { + /* override file_exists */ + if(zend_hash_find(CG(function_table), "file_exists", sizeof("file_exists"), (void **)&old_function) == SUCCESS) { + old_function->internal_function.handler = accel_file_exists; + } + if(zend_hash_find(CG(function_table), "is_file", sizeof("is_file"), (void **)&old_function) == SUCCESS) { + old_function->internal_function.handler = accel_is_file; + } + if(zend_hash_find(CG(function_table), "is_readable", sizeof("is_readable"), (void **)&old_function) == SUCCESS) { + old_function->internal_function.handler = accel_is_readable; + } + } +} + +static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator) +{ + (void)type; /* keep the compiler happy */ + + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) +{ + php_info_print_table_start(); + + if (ZCG(startup_ok) && ZCSG(accelerator_enabled)) { + php_info_print_table_row(2, "Opcode Caching", "Up and Running"); + } else { + php_info_print_table_row(2, "Opcode Caching", "Disabled"); + } + if (ZCG(enabled) && ZCG(accel_directives).optimization_level) { + php_info_print_table_row(2, "Optimization", "Enabled"); + } else { + php_info_print_table_row(2, "Optimization", "Disabled"); + } + if (!ZCG(startup_ok) || zps_api_failure_reason) { + php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason); + } else { + php_info_print_table_row(2, "Startup", "OK"); + php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model()); + } + + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} + +static zend_module_entry accel_module_entry = { + STANDARD_MODULE_HEADER, + ACCELERATOR_PRODUCT_NAME, + accel_functions, + ZEND_MINIT(zend_accelerator), + ZEND_MSHUTDOWN(zend_accelerator), + NULL, + NULL, + zend_accel_info, + ACCELERATOR_VERSION "FE", + STANDARD_MODULE_PROPERTIES +}; + +int start_accel_module() +{ + return zend_startup_module(&accel_module_entry); +} + +/* {{{ proto array accelerator_get_scripts() + Get the scripts which are accelerated by ZendAccelerator */ +static zval* accelerator_get_scripts(TSRMLS_D) +{ + uint i; + zval *return_value,*persistent_script_report; + zend_accel_hash_entry *cache_entry; + struct tm *ta; + struct timeval exec_time; + struct timeval fetch_time; + + if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) { + return 0; + } + + MAKE_STD_ZVAL(return_value); + array_init(return_value); + for (i=0; inext) { + zend_persistent_script *script; + + if (cache_entry->indirect) continue; + + script = (zend_persistent_script *)cache_entry->data; + + MAKE_STD_ZVAL(persistent_script_report); + array_init(persistent_script_report); + add_assoc_stringl(persistent_script_report, "full_path", script->full_path, script->full_path_len, 1); + add_assoc_long(persistent_script_report, "hits", script->dynamic_members.hits); + add_assoc_long(persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption); + ta = localtime(&script->dynamic_members.last_used); + add_assoc_string(persistent_script_report, "last_used", asctime(ta), 1); + add_assoc_long(persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used); + if (ZCG(accel_directives).validate_timestamps) { + add_assoc_long(persistent_script_report, "timestamp", (long)script->timestamp); + } + timerclear(&exec_time); + timerclear(&fetch_time); + + zend_hash_update(return_value->value.ht, cache_entry->key, cache_entry->key_length, &persistent_script_report, sizeof(zval *), NULL); + } + } + accelerator_shm_read_unlock(TSRMLS_C); + + return return_value; +} + +/* {{{ proto array accelerator_get_status() + Obtain statistics information regarding code acceleration in the Zend Performance Suite */ +static ZEND_FUNCTION(accelerator_get_status) +{ + long reqs; + zval *memory_usage,*statistics,*scripts; + + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + + if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) { + RETURN_FALSE; + } + + array_init(return_value); + + /* Trivia */ + add_assoc_long(return_value, "accelerator_enabled", ZCG(startup_ok) && ZCSG(accelerator_enabled)); + add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted)); + + /* Memory usage statistics */ + MAKE_STD_ZVAL(memory_usage); + array_init(memory_usage); + add_assoc_long(memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory)); + add_assoc_long(memory_usage, "free_memory", zend_shared_alloc_get_free_memory()); + add_assoc_long(memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory)); + add_assoc_double(memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0); + add_assoc_zval(return_value, "memory_usage",memory_usage); + + /* Accelerator statistics */ + MAKE_STD_ZVAL(statistics); + array_init(statistics); + add_assoc_long(statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries); + add_assoc_long(statistics, "max_cached_scripts", ZCSG(hash).max_num_entries); + add_assoc_long(statistics, "hits", ZCSG(hits)); + add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time)); + add_assoc_long(statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses)); + add_assoc_long(statistics, "blacklist_misses", ZCSG(blacklist_misses)); + reqs = ZCSG(hits)+ZCSG(misses); + add_assoc_double(statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0); + add_assoc_double(statistics, "accelerator_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0); + add_assoc_zval(return_value, "accelerator_statistics",statistics); + + /* acceleratred scripts */ + scripts=accelerator_get_scripts(TSRMLS_C); + if( scripts ){ + add_assoc_zval(return_value, "scripts",scripts); + } +} + +static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value TSRMLS_DC) +{ + add_next_index_stringl(return_value, p->path, p->path_length, 1); + return 0; +} + +/* {{{ proto array accelerator_get_configuration() + Obtain configuration information for the Zend Performance Suite */ +static ZEND_FUNCTION(accelerator_get_configuration) +{ + zval *directives,*version,*blacklist; + + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + + array_init(return_value); + + /* directives */ + MAKE_STD_ZVAL(directives); + array_init(directives); + add_assoc_bool(directives, "zend_optimizerplus.enable", ZCG(enabled)); + add_assoc_bool(directives, "zend_optimizerplus.use_cwd", ZCG(accel_directives).use_cwd); + add_assoc_bool(directives, "zend_optimizerplus.validate_timestamps", ZCG(accel_directives).validate_timestamps); + add_assoc_bool(directives, "zend_optimizerplus.inherited_hack", ZCG(accel_directives).inherited_hack); + add_assoc_bool(directives, "zend_optimizerplus.dups_fix", ZCG(accel_directives).ignore_dups); + add_assoc_bool(directives, "zend_optimizerplus.revalidate_path", ZCG(accel_directives).revalidate_path); + + add_assoc_long(directives, "zend_optimizerplus.log_verbosity_level", ZCG(accel_directives).log_verbosity_level); + add_assoc_long(directives, "zend_optimizerplus.memory_consumption", ZCG(accel_directives).memory_consumption); + add_assoc_long(directives, "zend_optimizerplus.max_accelerated_files", ZCG(accel_directives).max_accelerated_files); + add_assoc_double(directives, "zend_optimizerplus.max_wasted_percentage", ZCG(accel_directives).max_wasted_percentage); + add_assoc_long(directives, "zend_optimizerplus.consistency_checks", ZCG(accel_directives).consistency_checks); + add_assoc_long(directives, "zend_optimizerplus.force_restart_timeout", ZCG(accel_directives).force_restart_timeout); + add_assoc_long(directives, "zend_optimizerplus.revalidate_freq", ZCG(accel_directives).revalidate_freq); + add_assoc_string(directives, "zend_optimizerplus.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model), 1); + add_assoc_string(directives, "zend_optimizerplus.blacklist_filename", STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename), 1); + + add_assoc_bool(directives, "zend_optimizerplus.protect_memory", ZCG(accel_directives).protect_memory); + add_assoc_bool(directives, "zend_optimizerplus.save_comments", ZCG(accel_directives).save_comments); + add_assoc_bool(directives, "zend_optimizerplus.fast_shutdown", ZCG(accel_directives).fast_shutdown); + + add_assoc_long(directives, "zend_optimizerplus.optimization_level", ZCG(accel_directives).optimization_level); + + add_assoc_zval(return_value,"directives",directives); + + /*version */ + MAKE_STD_ZVAL(version); + array_init(version); + add_assoc_string(version, "version", ACCELERATOR_VERSION, 1); + add_assoc_string(version, "accelerator_product_name", ACCELERATOR_PRODUCT_NAME, 1); + add_assoc_zval(return_value,"version",version); + + /* blacklist */ + MAKE_STD_ZVAL(blacklist); + array_init(blacklist); + zend_accel_blacklist_apply(&accel_blacklist, (apply_func_arg_t) add_blacklist_path, blacklist TSRMLS_CC); + add_assoc_zval(return_value,"blacklist",blacklist); +} + +/* {{{ proto void accelerator_reset() + Request that the contents of the Accelerator module in the ZPS be reset */ +static ZEND_FUNCTION(accelerator_reset) +{ + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + + if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) { + RETURN_FALSE; + } + + zend_accel_schedule_restart(TSRMLS_C); + RETURN_TRUE; +} diff --git a/zend_accelerator_module.h b/zend_accelerator_module.h new file mode 100644 index 0000000000..25ad4ae3fa --- /dev/null +++ b/zend_accelerator_module.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERAROR_MODULE_H +#define ZEND_ACCELERATOR_MODULE_H + +int start_accel_module(); +void zend_accel_override_file_functions(TSRMLS_D); + +#endif /* _ZEND_ACCELERATOR_MODULE_H */ diff --git a/zend_accelerator_util_funcs.c b/zend_accelerator_util_funcs.c new file mode 100644 index 0000000000..275642bd21 --- /dev/null +++ b/zend_accelerator_util_funcs.c @@ -0,0 +1,1079 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_API.h" +#include "zend_constants.h" +#include "zend_accelerator_util_funcs.h" +#include "zend_persist.h" +#include "zend_shared_alloc.h" + +#define ZEND_PROTECTED_REFCOUNT (1<<30) + +static zend_uint zend_accel_refcount = ZEND_PROTECTED_REFCOUNT; + +#if SIZEOF_SIZE_T <= SIZEOF_LONG +/* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */ +# define accel_xlat_set(old, new) zend_hash_index_update(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), &(new), sizeof(void*), NULL) +# define accel_xlat_get(old, new) zend_hash_index_find(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), (void**)&(new)) +#else +# define accel_xlat_set(old, new) zend_hash_quick_add(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new), sizeof(void*), NULL) +# define accel_xlat_get(old, new) zend_hash_quick_find(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new)) +#endif + +typedef int (*id_function_t)(void *, void *); +typedef void (*unique_copy_ctor_func_t)(void *pElement); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +static const Bucket *uninitialized_bucket = NULL; +#endif + +static int zend_prepare_function_for_execution(zend_op_array *op_array); +static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind); + +static void zend_accel_destroy_zend_function(zend_function *function) +{ + TSRMLS_FETCH(); + + if (function->type == ZEND_USER_FUNCTION) { + if (function->op_array.static_variables) { + + efree(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + } + + destroy_zend_function(function TSRMLS_CC); +} + +static void zend_accel_destroy_zend_class(zend_class_entry **pce) +{ + zend_class_entry *ce = *pce; + + ce->function_table.pDestructor = (dtor_func_t) zend_accel_destroy_zend_function; + destroy_zend_class(pce); +} + +zend_persistent_script* create_persistent_script(void) +{ + zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script)); + memset(persistent_script, 0, sizeof(zend_persistent_script)); + + zend_hash_init(&persistent_script->function_table, 100, NULL, (dtor_func_t) zend_accel_destroy_zend_function, 0); + /* class_table is usualy destroyed by free_persistent_script() that + * overrides destructor. ZEND_CLASS_DTOR may be used by standard + * PHP compiler + */ + zend_hash_init(&persistent_script->class_table, 10, NULL, ZEND_CLASS_DTOR, 0); + + return persistent_script; +} + +void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements) +{ + if (destroy_elements) { + persistent_script->function_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_function; + persistent_script->class_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_class; + } else { + persistent_script->function_table.pDestructor = NULL; + persistent_script->class_table.pDestructor = NULL; + } + + zend_hash_destroy(&persistent_script->function_table); + zend_hash_destroy(&persistent_script->class_table); + + if (persistent_script->full_path) { + efree(persistent_script->full_path); + } + + efree(persistent_script); +} + +static int is_not_internal_function(zend_function *function) +{ + return(function->type != ZEND_INTERNAL_FUNCTION); +} + +void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC) +{ + dtor_func_t orig_dtor = ht->pDestructor; + + ht->pDestructor = NULL; + zend_hash_apply(ht, (apply_func_t) is_not_internal_function TSRMLS_CC); + ht->pDestructor = orig_dtor; +} + +static int move_user_function(zend_function *function TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + HashTable *function_table = va_arg(args, HashTable *); + (void)num_args; /* keep the compiler happy */ + + if (function->type==ZEND_USER_FUNCTION) { + zend_hash_quick_update(function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, function, sizeof(zend_function), NULL); + return 1; + } else { + return 0; + } +} + +void zend_accel_move_user_functions(HashTable *src, HashTable *dst TSRMLS_DC) +{ + dtor_func_t orig_dtor = src->pDestructor; + + src->pDestructor = NULL; + zend_hash_apply_with_arguments(src TSRMLS_CC, (apply_func_args_t)move_user_function, 1, dst); + src->pDestructor = orig_dtor; +} + +static int copy_internal_function(zend_function *function, HashTable *function_table TSRMLS_DC) +{ + if (function->type==ZEND_INTERNAL_FUNCTION) { + zend_hash_update(function_table, function->common.function_name, strlen(function->common.function_name)+1, function, sizeof(zend_function), NULL); + } + return 0; +} + +void zend_accel_copy_internal_functions(TSRMLS_D) +{ + zend_hash_apply_with_argument(CG(function_table), (apply_func_arg_t)copy_internal_function, &ZCG(function_table) TSRMLS_CC); + ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table)); +} + +static void zend_destroy_property_info(zend_property_info *property_info) +{ + interned_efree((char*)property_info->name); + if(property_info->doc_comment){ + efree((char*)property_info->doc_comment); + } +} + +static inline zval* zend_clone_zval(zval *src, int bind TSRMLS_DC) +{ + zval *ret, **ret_ptr; + + if (!bind) { + ALLOC_ZVAL(ret); + *ret = *src; + INIT_PZVAL(ret); + } else if (Z_REFCOUNT_P(src) == 1) { + ALLOC_ZVAL(ret); + *ret = *src; + } else if (accel_xlat_get(src, ret_ptr) != SUCCESS) { + ALLOC_ZVAL(ret); + *ret = *src; + accel_xlat_set(src, ret); + } else { + return *ret_ptr; + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) { + switch ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK)) { +#else + if ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) { + switch ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX)) { +#endif + case IS_STRING: + case IS_CONSTANT: + Z_STRVAL_P(ret) = (char *) interned_estrndup(Z_STRVAL_P(ret), Z_STRLEN_P(ret)); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + if (ret->value.ht && ret->value.ht != &EG(symbol_table)) { + ALLOC_HASHTABLE(ret->value.ht); + zend_hash_clone_zval(ret->value.ht, src->value.ht, 0); + } + break; + } + } + return ret; +} + +static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zval *ppz; + TSRMLS_FETCH(); + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = ZVAL_PTR_DTOR; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->arBuckets = NULL; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = &q->pDataPtr; + if (!bind) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + INIT_PZVAL(ppz); + } else if (Z_REFCOUNT_P((zval*)p->pDataPtr) == 1) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + } else if (accel_xlat_get(p->pDataPtr, ppz) != SUCCESS) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + accel_xlat_set(p->pDataPtr, ppz); + } else { + q->pDataPtr = *(void**)ppz; + p = p->pListNext; + continue; + } + q->pDataPtr = (void*)ppz; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) { + switch ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK)) { +#else + if ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) { + switch ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX)) { +#endif + case IS_STRING: + case IS_CONSTANT: + Z_STRVAL_P(ppz) = (char *) interned_estrndup(Z_STRVAL_P((zval*)p->pDataPtr), Z_STRLEN_P((zval*)p->pDataPtr)); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + if (((zval*)p->pDataPtr)->value.ht && ((zval*)p->pDataPtr)->value.ht != &EG(symbol_table)) { + ALLOC_HASHTABLE(ppz->value.ht); + zend_hash_clone_zval(ppz->value.ht, ((zval*)p->pDataPtr)->value.ht, 0); + } + break; + } + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zend_class_entry **new_ce; + zend_function** new_prototype; + zend_op_array *new_entry; + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = ZEND_FUNCTION_DTOR; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = (void *) emalloc(sizeof(zend_function)); + new_entry = (zend_op_array*)q->pData; + *new_entry = *(zend_op_array*)p->pData; + q->pDataPtr=NULL; + + /* Copy constructor */ + /* we use refcount to show that op_array is referenced from several places */ + if (new_entry->refcount != NULL) { + accel_xlat_set(p->pData, new_entry); + } + + zend_prepare_function_for_execution(new_entry); + + if(old_ce == new_entry->scope) { + new_entry->scope = ce; + } else { + if(accel_xlat_get(new_entry->scope, new_ce) == SUCCESS) { + new_entry->scope = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name); + } + } + + /* update prototype */ + if (new_entry->prototype ){ + if(accel_xlat_get(new_entry->prototype, new_prototype)==SUCCESS) { + new_entry->prototype = *new_prototype; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name); + } + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zend_class_entry **new_ce; + zend_property_info *prop_info; + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = (dtor_func_t) zend_destroy_property_info; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = (void *) emalloc(sizeof(zend_property_info)); + prop_info = q->pData; + *prop_info = *(zend_property_info*)p->pData; + q->pDataPtr=NULL; + + /* Copy constructor */ + prop_info->name = interned_estrndup(prop_info->name, prop_info->name_length); + if(prop_info->doc_comment) { + prop_info->doc_comment = estrndup(prop_info->doc_comment, prop_info->doc_comment_len); + } + if(prop_info->ce == old_ce) { + prop_info->ce = ce; + } else if(accel_xlat_get(prop_info->ce, new_ce) == SUCCESS) { + prop_info->ce = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s, property %s. Please call Zend Support", ce->name, prop_info->name); + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +/* protects reference count, creates copy of statics */ +static int zend_prepare_function_for_execution(zend_op_array *op_array) +{ + HashTable *shared_statics = op_array->static_variables; + + /* protect reference count */ + op_array->refcount = &zend_accel_refcount; + (*op_array->refcount) = ZEND_PROTECTED_REFCOUNT; + + /* copy statics */ + if (shared_statics) { + ALLOC_HASHTABLE(op_array->static_variables); + zend_hash_clone_zval(op_array->static_variables, shared_statics, 0); + } + + return 0; +} + +#define zend_update_inherited_handler(handler) \ +{ \ + if(ce->handler != NULL) { \ + if(accel_xlat_get(ce->handler, new_func)==SUCCESS) { \ + ce->handler = *new_func; \ + } else { \ + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s. Please call Zend Support", ce->name); \ + } \ + } \ +} + +/* Protects class' refcount, copies default properties, functions and class name */ +static void zend_class_copy_ctor(zend_class_entry **pce) +{ + zend_class_entry *ce = *pce; + zend_class_entry *old_ce = ce; + zend_class_entry **new_ce; + zend_function **new_func; + TSRMLS_FETCH(); + + *pce = ce = emalloc(sizeof(zend_class_entry)); + *ce = *old_ce; + ce->refcount = 1; + + if (old_ce->refcount != 1) { + /* this class is not used as a parent for any other classes */ + accel_xlat_set(old_ce, ce); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (old_ce->default_properties_table) { + int i; + + ce->default_properties_table = emalloc(sizeof(zval*) * old_ce->default_properties_count); + for (i = 0; i < old_ce->default_properties_count; i++) { + if (old_ce->default_properties_table[i]) { + ce->default_properties_table[i] = zend_clone_zval(old_ce->default_properties_table[i], 0 TSRMLS_CC); + } else { + ce->default_properties_table[i] = NULL; + } + } + } +#else + zend_hash_clone_zval(&ce->default_properties, &old_ce->default_properties, 0); +#endif + + zend_hash_clone_methods(&ce->function_table, &old_ce->function_table, old_ce, ce TSRMLS_CC); + + /* static members */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (old_ce->default_static_members_table) { + int i; + + ce->default_static_members_table = emalloc(sizeof(zval*) * old_ce->default_static_members_count); + for (i = 0; i < old_ce->default_static_members_count; i++) { + if (old_ce->default_static_members_table[i]) { + ce->default_static_members_table[i] = zend_clone_zval(old_ce->default_static_members_table[i], 1 TSRMLS_CC); + } else { + ce->default_static_members_table[i] = NULL; + } + } + } + ce->static_members_table = ce->default_static_members_table; +#else + zend_hash_clone_zval(&ce->default_static_members, &old_ce->default_static_members, 1); + ce->static_members = &ce->default_static_members; +#endif + + /* properties_info */ + zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce, ce TSRMLS_CC); + + /* constants table */ + zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 0); + + ce->name = interned_estrndup(ce->name, ce->name_length); + + /* interfaces aren't really implemented, so we create a new table */ + if(ce->num_interfaces) { + ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces); + memset(ce->interfaces, 0, sizeof(zend_class_entry *) * ce->num_interfaces); + } else { + ce->interfaces = NULL; + } + if (ZEND_CE_DOC_COMMENT(ce)) { + ZEND_CE_DOC_COMMENT(ce) = estrndup(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce)); + } + + if(ce->parent) { + if(accel_xlat_get(ce->parent, new_ce)==SUCCESS) { + ce->parent = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s. Please call Zend Support", ce->name); + } + } + + zend_update_inherited_handler(constructor); + zend_update_inherited_handler(destructor); + zend_update_inherited_handler(clone); + zend_update_inherited_handler(__get); + zend_update_inherited_handler(__set); + zend_update_inherited_handler(__call); +/* 5.1 stuff */ + zend_update_inherited_handler(serialize_func); + zend_update_inherited_handler(unserialize_func); + zend_update_inherited_handler(__isset); + zend_update_inherited_handler(__unset); +/* 5.2 stuff */ + zend_update_inherited_handler(__tostring); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +/* 5.3 stuff */ + zend_update_inherited_handler(__callstatic); +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +/* 5.4 traits */ + if (ce->trait_aliases) { + zend_trait_alias **trait_aliases; + int i = 0; + + while (ce->trait_aliases[i]) { + i++; + } + trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i+1)); + i = 0; + while (ce->trait_aliases[i]) { + trait_aliases[i] = emalloc(sizeof(zend_trait_alias)); + memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias)); + trait_aliases[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); + memcpy(trait_aliases[i]->trait_method, ce->trait_aliases[i]->trait_method, sizeof(zend_trait_method_reference)); + if (trait_aliases[i]->trait_method) { + if (trait_aliases[i]->trait_method->method_name) { + trait_aliases[i]->trait_method->method_name = + estrndup(trait_aliases[i]->trait_method->method_name, + trait_aliases[i]->trait_method->mname_len); + } + if (trait_aliases[i]->trait_method->class_name) { + trait_aliases[i]->trait_method->class_name = + estrndup(trait_aliases[i]->trait_method->class_name, + trait_aliases[i]->trait_method->cname_len); + } + } + + if (trait_aliases[i]->alias) { + trait_aliases[i]->alias = + estrndup(trait_aliases[i]->alias, + trait_aliases[i]->alias_len); + } + i++; + } + trait_aliases[i] = NULL; + ce->trait_aliases = trait_aliases; + } + + if (ce->trait_precedences) { + zend_trait_precedence **trait_precedences; + int i = 0; + + while (ce->trait_precedences[i]) { + i++; + } + trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i+1)); + i = 0; + while (ce->trait_precedences[i]) { + trait_precedences[i] = emalloc(sizeof(zend_trait_precedence)); + memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence)); + trait_precedences[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); + memcpy(trait_precedences[i]->trait_method, ce->trait_precedences[i]->trait_method, sizeof(zend_trait_method_reference)); + + trait_precedences[i]->trait_method->method_name = + estrndup(trait_precedences[i]->trait_method->method_name, + trait_precedences[i]->trait_method->mname_len); + trait_precedences[i]->trait_method->class_name = + estrndup(trait_precedences[i]->trait_method->class_name, + trait_precedences[i]->trait_method->cname_len); + + if (trait_precedences[i]->exclude_from_classes) { + zend_class_entry **exclude_from_classes; + int j = 0; + + while (trait_precedences[i]->exclude_from_classes[j]) { + j++; + } + exclude_from_classes = emalloc(sizeof(zend_class_entry*) * (j+1)); + j = 0; + while (trait_precedences[i]->exclude_from_classes[j]) { + exclude_from_classes[j] = (zend_class_entry*)estrndup( + (char*)trait_precedences[i]->exclude_from_classes[j], + strlen((char*)trait_precedences[i]->exclude_from_classes[j])); + j++; + } + exclude_from_classes[j] = NULL; + trait_precedences[i]->exclude_from_classes = exclude_from_classes; + } + i++; + } + trait_precedences[i] = NULL; + ce->trait_precedences = trait_precedences; + } +#endif +} + +static int zend_accel_same_function(void *f1, void *f2) +{ + zend_function *func1 = (zend_function *)f1, *func2 = (zend_function *)f2; + zend_op_array *op1 = (zend_op_array *)func1; + zend_op_array *op2 = (zend_op_array *)func2; + + if(func1->type != ZEND_USER_FUNCTION || func2->type != ZEND_USER_FUNCTION) { + return 0; + } + + if(func1 == func2) { + return 1; + } + + if(op1->function_name != op2->function_name && (!op1->function_name || !op2->function_name || strcmp(op1->function_name, op2->function_name))) { + /* compare filenames */ + return 0; + } + + if(op1->filename != op2->filename && (!op1->filename || !op2->filename || strcmp(op1->filename, op2->filename))) { + /* compare filenames */ + return 0; + } + + if(!op1->opcodes || !op2->opcodes || op1->opcodes[0].lineno != op2->opcodes[0].lineno) { + /* compare first lines */ + return 0; + } + + /* if everything matches - we are OK */ + return 1; +} + +static int zend_accel_same_class(void *f1, void *f2) +{ + zend_class_entry *ce1 = *(zend_class_entry **)f1; + zend_class_entry *ce2 = *(zend_class_entry **)f2; + + if(ce1->type != ZEND_USER_CLASS || ce2->type != ZEND_USER_CLASS) { + return 0; + } + + if(ce1 == ce2) { + return 1; + } + + if(!ce1->name || !ce2->name || zend_binary_strcmp(ce1->name, ce1->name_length, ce2->name, ce2->name_length)) { + return 0; + } + + if (zend_hash_num_elements(&ce1->function_table) != zend_hash_num_elements(&ce2->function_table) +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + || ce1->default_properties_count != ce2->default_properties_count) { +#else + || zend_hash_num_elements(&ce1->default_properties) != zend_hash_num_elements(&ce2->default_properties)) { +#endif + return 0; + } + + if(zend_hash_num_elements(&ce1->function_table)) { + HashPosition pos; + zend_function *func1, *func2; + + zend_hash_internal_pointer_reset_ex(&ce1->function_table, &pos); + zend_hash_get_current_data_ex(&ce1->function_table, (void **)&func1, &pos); + zend_hash_internal_pointer_reset_ex(&ce2->function_table, &pos); + zend_hash_get_current_data_ex(&ce2->function_table, (void **)&func2, &pos); + if(!zend_accel_same_function(func1, func2)) { + return 0; + } + } + + return 1; +} + +static int zend_hash_unique_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor, uint size, const char **key_fail, ulong *key_fail_l, id_function_t pIdFunc) +{ + Bucket *p; + void *t; + + p = source->pListHead; + while (p) { + if (p->nKeyLength>0) { + if (zend_hash_quick_add(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t) ==SUCCESS) { + if(pCopyConstructor) { + pCopyConstructor(t); + } + } else { + if(p->nKeyLength>0 && p->arKey[0]==0) { + /* Mangled key, ignore and wait for runtime */ + } else if(zend_hash_quick_find(target, p->arKey, p->nKeyLength, p->h, &t) == SUCCESS && + (!pIdFunc || pIdFunc(p->pData, t))) { + /* same one, ignore it */ + } else { + if(key_fail) { + *key_fail = p->arKey; + } + if(key_fail_l) { + *key_fail_l = p->nKeyLength; + } + return FAILURE; + } + } + } else { + if (!zend_hash_index_exists(target, p->h) && zend_hash_index_update(target, p->h, p->pData, size, &t) == SUCCESS) { + if(pCopyConstructor) { + pCopyConstructor(t); + } + } else { + if(key_fail) { + *key_fail = NULL; + } + if(key_fail_l) { + *key_fail_l = p->h; + } + return FAILURE; + } + } + p = p->pListNext; + } + target->pInternalPointer = target->pListHead; + + return SUCCESS; +} + +static void zend_accel_function_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor) { + const char *name; + ulong name_len; + zend_function *function; + TSRMLS_FETCH(); + + if(zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_function), &name, &name_len, ZCG(accel_directives).ignore_dups?NULL:zend_accel_same_function) != SUCCESS) { + if (zend_hash_find(target, name, name_len, (void *) &function)==SUCCESS + && function->type==ZEND_USER_FUNCTION + && ((zend_op_array *) function)->last>0) { + zend_error(E_ERROR, "Cannot redeclare function %.*s() (previously declared in %s:%d). If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your ini file", + (int)name_len, name, + ((zend_op_array *) function)->filename, + (int)((zend_op_array *) function)->opcodes[0].lineno); + } else { + + zend_error(E_ERROR, "Cannot redeclare function %.*s(). If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your ini file", (int)name_len, name); + } + } +} + +static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor TSRMLS_DC) { + const char *name; + ulong name_len; + uint size; + + size = sizeof(zend_class_entry*); + if(zend_hash_unique_copy(target, source, pCopyConstructor, size, &name, &name_len, ZCG(accel_directives).ignore_dups?NULL:zend_accel_same_class) != SUCCESS) { + zend_error(E_ERROR, "Cannot redeclare class %.*s. If this code worked without the " ACCELERATOR_PRODUCT_NAME ", please set zend_optimizerplus.dups_fix=1 in your php.ini", (int)name_len, name); + } +} + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO +static void zend_do_delayed_early_binding(zend_op_array *op_array, zend_uint early_binding TSRMLS_DC) +{ + zend_uint opline_num = early_binding; + + if ((int)opline_num != -1) { + zend_bool orig_in_compilation = CG(in_compilation); + char *orig_compiled_filename = zend_set_compiled_filename(op_array->filename TSRMLS_CC); + zend_class_entry **pce; + + CG(in_compilation) = 1; + while ((int)opline_num != -1) { + if (zend_lookup_class(Z_STRVAL(op_array->opcodes[opline_num-1].op2.u.constant), Z_STRLEN(op_array->opcodes[opline_num-1].op2.u.constant), &pce TSRMLS_CC) == SUCCESS) { + do_bind_inherited_class(&op_array->opcodes[opline_num], EG(class_table), *pce, 1 TSRMLS_CC); + } + opline_num = op_array->opcodes[opline_num].result.u.opline_num; + } + zend_restore_compiled_filename(orig_compiled_filename); + CG(in_compilation) = orig_in_compilation; + } +} +#endif + +zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC) +{ + zend_op_array *op_array; + + op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); + *op_array = persistent_script->main_op_array; + + if (from_shared_memory) { + /* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */ + if (zend_hash_num_elements(&persistent_script->class_table) > 0) { + zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0); + zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor TSRMLS_CC); + zend_hash_destroy(&ZCG(bind_hash)); + } + /* we must first to copy all classes and then prepare functions, since functions may try to bind + classes - which depend on pre-bind class entries existant in the class table */ + if (zend_hash_num_elements(&persistent_script->function_table) > 0) { + zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, (unique_copy_ctor_func_t)zend_prepare_function_for_execution); + } + + zend_prepare_function_for_execution(op_array); + + /* Register __COMPILER_HALT_OFFSET__ constant */ + if (persistent_script->compiler_halt_offset != 0 && + persistent_script->full_path) { + char *name, *cfilename; + char haltoff[] = "__COMPILER_HALT_OFFSET__"; + int len, clen; + + cfilename = persistent_script->full_path; + clen = strlen(cfilename); + zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0); + if (!zend_hash_exists(EG(zend_constants), name, len+1)) { + zend_register_long_constant(name, len+1, persistent_script->compiler_halt_offset, CONST_CS, 0 TSRMLS_CC); + } + efree(name); + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if ((int)persistent_script->early_binding != -1) { + zend_do_delayed_early_binding(op_array, persistent_script->early_binding TSRMLS_CC); + } +#endif + + } else /* if (!from_shared_memory) */ { + if (zend_hash_num_elements(&persistent_script->function_table) > 0) { + zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, NULL); + } + if (zend_hash_num_elements(&persistent_script->class_table) > 0) { + zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL TSRMLS_CC); + } + free_persistent_script(persistent_script, 0); /* free only hashes */ + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (op_array->early_binding != (zend_uint)-1) { + char *orig_compiled_filename = CG(compiled_filename); + CG(compiled_filename) = persistent_script->full_path; + zend_do_delayed_early_binding(op_array TSRMLS_CC); + CG(compiled_filename) = orig_compiled_filename; + } +#endif + + return op_array; +} + +/* + * zend_adler32() is based on zlib implementation + * Computes the Adler-32 checksum of a data stream + * + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#define ADLER32_BASE 65521 /* largest prime smaller than 65536 */ +#define ADLER32_NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define ADLER32_DO1(buf) {s1 += *(buf); s2 += s1;} +#define ADLER32_DO2(buf,i) ADLER32_DO1(buf+i); ADLER32_DO1(buf+i+1); +#define ADLER32_DO4(buf,i) ADLER32_DO2(buf,i); ADLER32_DO2(buf,i+2); +#define ADLER32_DO8(buf,i) ADLER32_DO4(buf,i); ADLER32_DO4(buf,i+4); +#define ADLER32_DO16(buf) ADLER32_DO8(buf,0); ADLER32_DO8(buf,8); + +unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len) +{ + unsigned int s1 = checksum & 0xffff; + unsigned int s2 = (checksum >> 16) & 0xffff; + signed char *end; + + while (len >= ADLER32_NMAX) { + len -= ADLER32_NMAX; + end = buf + ADLER32_NMAX; + do { + ADLER32_DO16(buf); + buf += 16; + } while (buf != end); + s1 %= ADLER32_BASE; + s2 %= ADLER32_BASE; + } + + if (len) { + if (len >= 16) { + end = buf + (len & 0xfff0); + len &= 0xf; + do { + ADLER32_DO16(buf); + buf += 16; + } while (buf != end); + } + if (len) { + end = buf + len; + do { + ADLER32_DO1(buf); + buf++; + } while (buf != end); + } + s1 %= ADLER32_BASE; + s2 %= ADLER32_BASE; + } + + return (s2 << 16) | s1; +} diff --git a/zend_accelerator_util_funcs.h b/zend_accelerator_util_funcs.h new file mode 100644 index 0000000000..804e0f786b --- /dev/null +++ b/zend_accelerator_util_funcs.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_UTIL_FUNCS_H +#define ZEND_ACCELERATOR_UTIL_FUNCS_H + +#include "zend.h" +#include "ZendAccelerator.h" + +void zend_accel_copy_internal_functions(TSRMLS_D); + +zend_persistent_script* create_persistent_script(void); +void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements); + +void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC); +void zend_accel_move_user_functions(HashTable *str, HashTable *dst TSRMLS_DC); + +zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC); + +#define ADLER32_INIT 1 /* initial Adler-32 value */ + +unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len); + +#endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/zend_persist.c b/zend_persist.c new file mode 100644 index 0000000000..44a9809322 --- /dev/null +++ b/zend_persist.c @@ -0,0 +1,680 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_extensions.h" +#include "zend_shared_alloc.h" +#include "zend_vm.h" +#include "zend_constants.h" +#include "zend_operators.h" + +#define zend_accel_store(p, size) \ + (p = _zend_shared_memdup((void*)p, size, 1 TSRMLS_CC)) +#define zend_accel_memdup(p, size) \ + _zend_shared_memdup((void*)p, size, 0 TSRMLS_CC) + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +# define zend_accel_memdup_interned_string(str, len) \ + IS_INTERNED(str) ? str : zend_accel_memdup(str, len) + +# define zend_accel_store_interned_string(str, len) do { \ + if (!IS_INTERNED(str)) { zend_accel_store(str, len); } \ + } while (0) +#else +# define zend_accel_memdup_interned_string(str, len) \ + zend_accel_memdup(str, len) + +# define zend_accel_store_interned_string(str, len) \ + zend_accel_store(str, len) +#endif + +typedef void (*zend_persist_func_t)(void * TSRMLS_DC); + +static void zend_persist_zval_ptr(zval **zp TSRMLS_DC); + +static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC) +{ + Bucket *p = ht->pListHead; + uint i; + + while (p) { + Bucket *q = p; + + /* persist bucket and key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + p = zend_accel_memdup(p, sizeof(Bucket)); + if (p->nKeyLength) { + p->arKey = zend_accel_memdup_interned_string(p->arKey, p->nKeyLength); + } +#else + p = zend_accel_memdup(p, sizeof(Bucket) - 1 + p->nKeyLength); +#endif + + /* persist data pointer in bucket */ + if (!p->pDataPtr) { + zend_accel_store(p->pData, el_size); + } else { + /* Update p->pData to point to the new p->pDataPtr address, after the bucket relocation */ + p->pData = &p->pDataPtr; + } + + /* persist the data itself */ + if (pPersistElement) { + pPersistElement(p->pData TSRMLS_CC); + } + + /* update linked lists */ + if (p->pLast) { + p->pLast->pNext = p; + } + if (p->pNext) { + p->pNext->pLast = p; + } + if (p->pListLast) { + p->pListLast->pListNext = p; + } + if (p->pListNext) { + p->pListNext->pListLast = p; + } + + p = p->pListNext; + + /* delete the old non-persistent bucket */ + efree(q); + } + + /* update linked lists */ + if (ht->pListHead) { + ht->pListHead = zend_shared_alloc_get_xlat_entry(ht->pListHead); + } + if (ht->pListTail) { + ht->pListTail = zend_shared_alloc_get_xlat_entry(ht->pListTail); + } + if (ht->pInternalPointer) { + ht->pInternalPointer = zend_shared_alloc_get_xlat_entry(ht->pInternalPointer); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + /* Check if HastTable is initialized */ + if (ht->nTableMask) { +#endif + if (ht->nNumOfElements) { + /* update hash table */ + for (i = 0; i < ht->nTableSize; i++) { + if (ht->arBuckets[i]) { + ht->arBuckets[i] = zend_shared_alloc_get_xlat_entry(ht->arBuckets[i]); + } + } + } + zend_accel_store(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + } else { + ht->arBuckets = NULL; + } +#endif +} + +static void zend_persist_zval(zval *z TSRMLS_DC) +{ +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (z->type & IS_CONSTANT_TYPE_MASK) { +#else + switch (z->type & ~IS_CONSTANT_INDEX) { +#endif + case IS_STRING: + case IS_CONSTANT: + zend_accel_store_interned_string(z->value.str.val, z->value.str.len + 1); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + zend_accel_store(z->value.ht, sizeof(HashTable)); + zend_hash_persist(z->value.ht, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + break; + } +} + +static void zend_persist_zval_ptr(zval **zp TSRMLS_DC) +{ + zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp); + + if (new_ptr) { + *zp = new_ptr; + } else { + /* Attempt to store only if we didn't store this zval_ptr yet */ + zend_accel_store(*zp, sizeof(zval)); + zend_persist_zval(*zp TSRMLS_CC); + } +} + +static void zend_protect_zval(zval *z TSRMLS_DC) +{ + PZ_SET_ISREF_P(z); + PZ_SET_REFCOUNT_P(z,2); +} + +static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script TSRMLS_DC) +{ + zend_op *persist_ptr; +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + int has_jmp = 0; +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_literal *orig_literals = NULL; +#endif + + if (op_array->type != ZEND_USER_FUNCTION) { + return; + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO + op_array->size = op_array->last; +#endif + + if (--(*op_array->refcount) == 0) { + efree(op_array->refcount); + } + op_array->refcount = NULL; + + if (op_array->filename) { + /* do not free! PHP has centralized filename storage, compiler will free it */ + op_array->filename = zend_accel_memdup(op_array->filename, strlen(op_array->filename) + 1); + } + + if (main_persistent_script) { + zend_bool orig_in_execution = EG(in_execution); + zend_op_array *orig_op_array = EG(active_op_array); + zval offset; + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + main_persistent_script->early_binding = -1; +#endif + EG(in_execution) = 1; + EG(active_op_array) = op_array; + if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) { + main_persistent_script->compiler_halt_offset = Z_LVAL(offset); + } + EG(active_op_array) = orig_op_array; + EG(in_execution) = orig_in_execution; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->literals) { + orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals); + if (orig_literals) { + op_array->literals = orig_literals; + } else { + zend_literal *p = zend_accel_memdup(op_array->literals, sizeof(zend_literal) * op_array->last_literal); + zend_literal *end = p + op_array->last_literal; + orig_literals = op_array->literals; + op_array->literals = p; + while (p < end) { + zend_persist_zval(&p->constant TSRMLS_CC); + zend_protect_zval(&p->constant TSRMLS_CC); + p++; + } + efree(orig_literals); + } + } +#endif + + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes))) { + op_array->opcodes = persist_ptr; + } else { + zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last); + zend_op *opline = new_opcodes; + zend_op *end = new_opcodes + op_array->last; + int offset = 0; + + for (;opline PHP_5_3_X_API_NO + opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); +#else + zend_persist_zval(&opline->op1.u.constant TSRMLS_CC); + zend_protect_zval(&opline->op1.u.constant TSRMLS_CC); +#endif + } + if (ZEND_OP2_TYPE(opline)==IS_CONST) { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals)); +#else + zend_persist_zval(&opline->op2.u.constant TSRMLS_CC); + zend_protect_zval(&opline->op2.u.constant TSRMLS_CC); +#endif + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + switch (opline->opcode) { + case ZEND_JMP: + has_jmp = 1; + if (ZEND_DONE_PASS_TWO(op_array)) { + ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes]; + } + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + has_jmp = 1; + if (ZEND_DONE_PASS_TWO(op_array)) { + ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; + } + break; + case ZEND_JMPZNZ: + case ZEND_BRK: + case ZEND_CONT: + has_jmp = 1; + break; + case ZEND_DECLARE_INHERITED_CLASS: + if (main_persistent_script && ZCG(accel_directives).inherited_hack) { + if (!has_jmp && + ((opline+2) >= end || + (opline+1)->opcode != ZEND_FETCH_CLASS || + (opline+2)->opcode != ZEND_ADD_INTERFACE)) { + + zend_uint *opline_num = &main_persistent_script->early_binding; + + while ((int)*opline_num != -1) { + opline_num = &new_opcodes[*opline_num].result.u.opline_num; + } + *opline_num = opline - new_opcodes; + opline->result.op_type = IS_UNUSED; + opline->result.u.opline_num = -1; + opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED; + ZEND_VM_SET_OPCODE_HANDLER(opline); + } + break; + } + } + +#else /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */ + + if (ZEND_DONE_PASS_TWO(op_array)) { + /* fix jmps to point to new array */ + switch (opline->opcode) { + case ZEND_JMP: + case ZEND_GOTO: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_JMP_SET: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; + break; + } + } +#endif /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */ + } + + efree(op_array->opcodes); + op_array->opcodes = new_opcodes; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->run_time_cache) { + efree(op_array->run_time_cache); + op_array->run_time_cache = NULL; + } +#endif + } + + if (op_array->function_name) { + char *new_name; + if ((new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name))) { + op_array->function_name = new_name; + } else { + zend_accel_store(op_array->function_name, strlen(op_array->function_name)+1); + } + } + + if (op_array->arg_info) { + zend_arg_info *new_ptr; + if((new_ptr = zend_shared_alloc_get_xlat_entry(op_array->arg_info))) { + op_array->arg_info = new_ptr; + } else { + zend_uint i; + + zend_accel_store(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args); + for(i=0;inum_args;i++) { + if(op_array->arg_info[i].name) { + zend_accel_store_interned_string(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1); + } + if(op_array->arg_info[i].class_name) { + zend_accel_store_interned_string(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1); + } + } + } + } + + if (op_array->brk_cont_array) { + zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont); + } + + if (op_array->static_variables) { + zend_hash_persist(op_array->static_variables, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + zend_accel_store(op_array->static_variables, sizeof(HashTable)); + } + + if(op_array->scope) { + op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope); + } + + if(op_array->doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(op_array->doc_comment, op_array->doc_comment_len + 1); + } else { + if(!zend_shared_alloc_get_xlat_entry(op_array->doc_comment)) { + zend_shared_alloc_register_xlat_entry(op_array->doc_comment, op_array->doc_comment); + efree((char*)op_array->doc_comment); + } + op_array->doc_comment = NULL; + op_array->doc_comment_len = 0; + } + } + + if(op_array->try_catch_array) { + zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + } + + if(op_array->vars) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars))) { + op_array->vars = (zend_compiled_variable*)persist_ptr; + } else { + int i; + zend_accel_store(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var); + for(i=0; ilast_var; i++) { + zend_accel_store_interned_string(op_array->vars[i].name, op_array->vars[i].name_len + 1); + } + } + } + + /* "prototype" may be undefined if "scope" isn't set */ + if(op_array->scope && op_array->prototype) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { + op_array->prototype = (union _zend_function*)persist_ptr; + /* we use refcount to show that op_array is referenced from several places */ + op_array->prototype->op_array.refcount++; + } + } else { + op_array->prototype = NULL; + } +} + +static void zend_persist_op_array(zend_op_array *op_array TSRMLS_DC) +{ + zend_persist_op_array_ex(op_array, NULL TSRMLS_CC); +} + +static void zend_persist_property_info(zend_property_info *prop TSRMLS_DC) +{ + zend_accel_store_interned_string(prop->name, prop->name_length + 1); + if(prop->doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(prop->doc_comment, prop->doc_comment_len + 1); + } else { + if(!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) { + zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment); + efree((char*)prop->doc_comment); + } + prop->doc_comment = NULL; + prop->doc_comment_len = 0; + } + } +} + +static void zend_persist_class_entry(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->type == ZEND_USER_CLASS) { + *pce = zend_accel_store(ce, sizeof(zend_class_entry)); + zend_accel_store_interned_string(ce->name, ce->name_length + 1); + zend_hash_persist(&ce->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->default_properties_table) { + int i; + + zend_accel_store(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count); + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->default_properties_table[i]) { + zend_persist_zval_ptr(&ce->default_properties_table[i] TSRMLS_CC); + } + } + } + if (ce->default_static_members_table) { + int i; + + zend_accel_store(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count); + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->default_static_members_table[i]) { + zend_persist_zval_ptr(&ce->default_static_members_table[i] TSRMLS_CC); + } + } + } + ce->static_members_table = NULL; +#else + zend_hash_persist(&ce->default_properties, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + zend_hash_persist(&ce->default_static_members, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + ce->static_members = NULL; +#endif + zend_hash_persist(&ce->constants_table, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + + if(ZEND_CE_FILENAME(ce)) { + /* do not free! PHP has centralized filename storage, compiler will free it */ + ZEND_CE_FILENAME(ce) = zend_accel_memdup(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1); + } + if(ZEND_CE_DOC_COMMENT(ce)) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1); + } else { + if(!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) { + zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce)); + efree((char*)ZEND_CE_DOC_COMMENT(ce)); + } + ZEND_CE_DOC_COMMENT(ce) = NULL; + ZEND_CE_DOC_COMMENT_LEN(ce) = 0; + } + } + zend_hash_persist(&ce->properties_info, (zend_persist_func_t) zend_persist_property_info, sizeof(zend_property_info) TSRMLS_CC); + if(ce->num_interfaces && ce->interfaces) { + efree(ce->interfaces); + } + ce->interfaces = NULL; /* will be filled in on fetch */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->num_traits && ce->traits) { + efree(ce->traits); + } + ce->traits = NULL; + + if (ce->trait_aliases) { + int i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method) { + if (ce->trait_aliases[i]->trait_method->method_name) { + zend_accel_store(ce->trait_aliases[i]->trait_method->method_name, + ce->trait_aliases[i]->trait_method->mname_len+1); + } + if (ce->trait_aliases[i]->trait_method->class_name) { + zend_accel_store(ce->trait_aliases[i]->trait_method->class_name, + ce->trait_aliases[i]->trait_method->cname_len+1); + } + ce->trait_aliases[i]->trait_method->ce = NULL; + zend_accel_store(ce->trait_aliases[i]->trait_method, + sizeof(zend_trait_method_reference)); + } + + if (ce->trait_aliases[i]->alias) { + zend_accel_store(ce->trait_aliases[i]->alias, + ce->trait_aliases[i]->alias_len+1); + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO + ce->trait_aliases[i]->function = NULL; +#endif + zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias)); + i++; + } + + zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1)); + } + + if (ce->trait_precedences) { + int i = 0; + + while (ce->trait_precedences[i]) { + zend_accel_store(ce->trait_precedences[i]->trait_method->method_name, + ce->trait_precedences[i]->trait_method->mname_len+1); + zend_accel_store(ce->trait_precedences[i]->trait_method->class_name, + ce->trait_precedences[i]->trait_method->cname_len+1); + ce->trait_precedences[i]->trait_method->ce = NULL; + zend_accel_store(ce->trait_precedences[i]->trait_method, + sizeof(zend_trait_method_reference)); + + if (ce->trait_precedences[i]->exclude_from_classes) { + int j = 0; + + while (ce->trait_precedences[i]->exclude_from_classes[j]) { + zend_accel_store(ce->trait_precedences[i]->exclude_from_classes[j], + strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1); + j++; + } + zend_accel_store(ce->trait_precedences[i]->exclude_from_classes, + sizeof(zend_class_entry*) * (j+1)); + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO + ce->trait_precedences[i]->function = NULL; +#endif + zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence)); + i++; + } + zend_accel_store( + ce->trait_precedences, sizeof(zend_trait_precedence*) * (i+1)); + } +#endif + } +} + +static int zend_update_property_info_ce(zend_property_info *prop TSRMLS_DC) +{ + prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); + return 0; +} + +static int zend_update_parent_ce(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->parent) { + ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent); + /* We use refcount to show if the class is used as a parent */ + ce->parent->refcount++; + } + + /* update methods */ + if(ce->constructor) { + ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor); + /* we use refcount to show that op_array is referenced from several places */ + ce->constructor->op_array.refcount++; + } + if(ce->destructor) { + ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor); + ce->destructor->op_array.refcount++; + } + if(ce->clone) { + ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone); + ce->clone->op_array.refcount++; + } + if(ce->__get) { + ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get); + ce->__get->op_array.refcount++; + } + if(ce->__set) { + ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set); + ce->__set->op_array.refcount++; + } + if(ce->__call) { + ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call); + ce->__call->op_array.refcount++; + } + if(ce->serialize_func) { + ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + ce->serialize_func->op_array.refcount++; + } + if(ce->unserialize_func) { + ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + ce->unserialize_func->op_array.refcount++; + } + if(ce->__isset) { + ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset); + ce->__isset->op_array.refcount++; + } + if(ce->__unset) { + ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset); + ce->__unset->op_array.refcount++; + } + if(ce->__tostring) { + ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring); + ce->__tostring->op_array.refcount++; + } +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if(ce->__callstatic) { + ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + ce->__callstatic->op_array.refcount++; + } +#endif + zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce TSRMLS_CC); + return 0; +} + +static void zend_accel_persist_class_table(HashTable *class_table TSRMLS_DC) +{ + zend_hash_persist(class_table, (zend_persist_func_t) zend_persist_class_entry, sizeof(zend_class_entry*) TSRMLS_CC); + zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce TSRMLS_CC); +} + +zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC) +{ + zend_shared_alloc_clear_xlat_table(); + zend_hash_persist(&script->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC); + zend_accel_persist_class_table(&script->class_table TSRMLS_CC); + zend_persist_op_array_ex(&script->main_op_array, script TSRMLS_CC); + *key = zend_accel_memdup(*key, key_length + 1); + zend_accel_store(script->full_path, script->full_path_len + 1); + zend_accel_store(script, sizeof(zend_persistent_script)); + + return script; +} + +int zend_accel_script_persistable(zend_persistent_script *script) +{ + return 1; +} diff --git a/zend_persist.h b/zend_persist.h new file mode 100644 index 0000000000..ffb990ae75 --- /dev/null +++ b/zend_persist.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_PERSIST_H +#define ZEND_PERSIST_H + +int zend_accel_script_persistable(zend_persistent_script *script); +uint zend_accel_script_persist_calc(zend_persistent_script *script, char *key, unsigned int key_length TSRMLS_DC); +zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC); + +#endif /* ZEND_PERSIST_H */ diff --git a/zend_persist_calc.c b/zend_persist_calc.c new file mode 100644 index 0000000000..36ec05b728 --- /dev/null +++ b/zend_persist_calc.c @@ -0,0 +1,343 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_extensions.h" +#include "zend_shared_alloc.h" +#include "zend_operators.h" + +#define START_SIZE() uint memory_used = 0 +#define ADD_DUP_SIZE(m,s) memory_used += zend_shared_memdup_size((void*)m, s) +#define ADD_SIZE(m) memory_used += ZEND_ALIGNED_SIZE(m) +#define RETURN_SIZE() return memory_used + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +# define ADD_INTERNED_STRING(str, len) do { \ + const char *tmp = accel_new_interned_string((str), (len), !IS_INTERNED((str)) TSRMLS_CC); \ + if (tmp != (str)) { \ + (str) = (char*)tmp; \ + } else { \ + ADD_DUP_SIZE((str), (len)); \ + } \ + } while (0) +#else +# define ADD_INTERNED_STRING(str, len) ADD_DUP_SIZE((str), (len)) +#endif + +static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC); + +static uint zend_hash_persist_calc(HashTable *ht, int (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC) +{ + Bucket *p = ht->pListHead; + START_SIZE(); + + while (p) { + /* persist bucket and key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ADD_DUP_SIZE(p, sizeof(Bucket)); + if (p->nKeyLength) { + const char *tmp = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + if (tmp != p->arKey) { + p->arKey = tmp; + } else { + ADD_DUP_SIZE(p->arKey, p->nKeyLength); + } + } +#else + ADD_DUP_SIZE(p, sizeof(Bucket) - 1 + p->nKeyLength); +#endif + + /* persist data pointer in bucket */ + if (!p->pDataPtr) { + ADD_DUP_SIZE(p->pData, el_size); + } + + /* persist the data itself */ + if (pPersistElement) { + ADD_SIZE(pPersistElement(p->pData TSRMLS_CC)); + } + + p = p->pListNext; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ht->nTableMask) { + ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); + } +#else + ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); +#endif + + RETURN_SIZE(); +} + +static uint zend_persist_zval_calc(zval *z TSRMLS_DC) +{ + START_SIZE(); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (z->type & IS_CONSTANT_TYPE_MASK) { +#else + switch (z->type & ~IS_CONSTANT_INDEX) { +#endif + case IS_STRING: + case IS_CONSTANT: + ADD_INTERNED_STRING(Z_STRVAL_P(z), Z_STRLEN_P(z) + 1); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + ADD_DUP_SIZE(z->value.ht, sizeof(HashTable)); + ADD_SIZE(zend_hash_persist_calc(z->value.ht, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + break; + } + RETURN_SIZE(); +} + +static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC) +{ + START_SIZE(); + zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp); + + if (!new_ptr) { + ADD_DUP_SIZE(*zp, sizeof(zval)); + ADD_SIZE(zend_persist_zval_calc(*zp TSRMLS_CC)); + } + RETURN_SIZE(); +} + +static uint zend_persist_op_array_calc(zend_op_array *op_array TSRMLS_DC) +{ + START_SIZE(); + + if (op_array->type != ZEND_USER_FUNCTION) { + return 0; + } + + if (op_array->filename) { + ADD_DUP_SIZE(op_array->filename, strlen(op_array->filename) + 1); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->literals && !zend_shared_alloc_get_xlat_entry(op_array->literals)) { + zend_literal *p = op_array->literals; + zend_literal *end = p + op_array->last_literal; + ADD_DUP_SIZE(op_array->literals, sizeof(zend_literal) * op_array->last_literal); + while (p < end) { + ADD_SIZE(zend_persist_zval_calc(&p->constant TSRMLS_CC)); + p++; + } + } +#endif + + if (!zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { +#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO + zend_op *opline = op_array->opcodes; + zend_op *end = op_array->opcodes+op_array->last; + + ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last); + while (oplineop1.op_type==IS_CONST) { + ADD_SIZE(zend_persist_zval_calc(&opline->op1.u.constant TSRMLS_CC)); + } + if (opline->op2.op_type==IS_CONST) { + ADD_SIZE(zend_persist_zval_calc(&opline->op2.u.constant TSRMLS_CC)); + } + opline++; + } +#else + ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last); +#endif + } + + if (op_array->function_name) { + ADD_DUP_SIZE(op_array->function_name, strlen(op_array->function_name) + 1); + } + + if (op_array->arg_info && + !zend_shared_alloc_get_xlat_entry(op_array->arg_info)) { + zend_uint i; + + ADD_DUP_SIZE(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args); + for(i=0;inum_args;i++) { + if(op_array->arg_info[i].name) { + ADD_INTERNED_STRING(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1); + } + if(op_array->arg_info[i].class_name) { + ADD_INTERNED_STRING(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1); + } + + } + } + + if (op_array->brk_cont_array) { + ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont); + } + + if (op_array->static_variables) { + ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable)); + ADD_SIZE(zend_hash_persist_calc(op_array->static_variables, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + } + + if(ZCG(accel_directives).save_comments && op_array->doc_comment) { + ADD_DUP_SIZE(op_array->doc_comment, op_array->doc_comment_len + 1); + } + + if(op_array->try_catch_array) { + ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + } + + if(op_array->vars && !zend_shared_alloc_get_xlat_entry(op_array->vars)) { + int i; + + ADD_DUP_SIZE(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var); + for(i=0; ilast_var; i++) { + ADD_INTERNED_STRING(op_array->vars[i].name, op_array->vars[i].name_len + 1); + } + } + + RETURN_SIZE(); +} + +static uint zend_persist_property_info_calc(zend_property_info *prop TSRMLS_DC) +{ + START_SIZE(); + ADD_INTERNED_STRING(prop->name, prop->name_length + 1); + if(ZCG(accel_directives).save_comments && prop->doc_comment) { + ADD_DUP_SIZE(prop->doc_comment, prop->doc_comment_len + 1); + } + RETURN_SIZE(); +} + +static uint zend_persist_class_entry_calc(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + START_SIZE(); + + if (ce->type == ZEND_USER_CLASS) { + ADD_DUP_SIZE(ce, sizeof(zend_class_entry)); + ADD_INTERNED_STRING(ce->name, ce->name_length + 1); + ADD_SIZE(zend_hash_persist_calc(&ce->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC)); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->default_properties_table) { + int i; + + ADD_SIZE(sizeof(zval*) * ce->default_properties_count); + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->default_properties_table[i]) { + ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_properties_table[i] TSRMLS_CC)); + } + } + } + if (ce->default_static_members_table) { + int i; + + ADD_SIZE(sizeof(zval*) * ce->default_static_members_count); + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->default_static_members_table[i]) { + ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_static_members_table[i] TSRMLS_CC)); + } + } + } +#else + ADD_SIZE(zend_hash_persist_calc(&ce->default_properties, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + ADD_SIZE(zend_hash_persist_calc(&ce->default_static_members, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); +#endif + ADD_SIZE(zend_hash_persist_calc(&ce->constants_table, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + + if (ZEND_CE_FILENAME(ce)) { + ADD_DUP_SIZE(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1); + } + if(ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) { + ADD_DUP_SIZE(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1); + } + + ADD_SIZE(zend_hash_persist_calc(&ce->properties_info, (int (*)(void* TSRMLS_DC)) zend_persist_property_info_calc, sizeof(zend_property_info) TSRMLS_CC)); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->trait_aliases) { + int i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method) { + if (ce->trait_aliases[i]->trait_method->method_name) { + ADD_SIZE(ce->trait_aliases[i]->trait_method->mname_len+1); + } + if (ce->trait_aliases[i]->trait_method->class_name) { + ADD_SIZE(ce->trait_aliases[i]->trait_method->cname_len+1); + } + ADD_SIZE(sizeof(zend_trait_method_reference)); + } + + if (ce->trait_aliases[i]->alias) { + ADD_SIZE(ce->trait_aliases[i]->alias_len+1); + } + ADD_SIZE(sizeof(zend_trait_alias)); + i++; + } + ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1)); + } + + if (ce->trait_precedences) { + int i = 0; + + while (ce->trait_precedences[i]) { + ADD_SIZE(ce->trait_precedences[i]->trait_method->mname_len+1); + ADD_SIZE(ce->trait_precedences[i]->trait_method->cname_len+1); + ADD_SIZE(sizeof(zend_trait_method_reference)); + + if (ce->trait_precedences[i]->exclude_from_classes) { + int j = 0; + + while (ce->trait_precedences[i]->exclude_from_classes[j]) { + ADD_SIZE(strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1); + j++; + } + ADD_SIZE(sizeof(zend_class_entry*) * (j+1)); + } + ADD_SIZE(sizeof(zend_trait_precedence)); + i++; + } + ADD_SIZE(sizeof(zend_trait_precedence*) * (i+1)); + } +#endif + } + RETURN_SIZE(); +} + +static uint zend_accel_persist_class_table_calc(HashTable *class_table TSRMLS_DC) +{ + return zend_hash_persist_calc(class_table, (int (*)(void* TSRMLS_DC)) zend_persist_class_entry_calc, sizeof(zend_class_entry*) TSRMLS_CC); +} + +uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length TSRMLS_DC) +{ + START_SIZE(); + + ADD_SIZE(zend_hash_persist_calc(&new_persistent_script->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC)); + ADD_SIZE(zend_accel_persist_class_table_calc(&new_persistent_script->class_table TSRMLS_CC)); + ADD_SIZE(zend_persist_op_array_calc(&new_persistent_script->main_op_array TSRMLS_CC)); + ADD_DUP_SIZE(key, key_length + 1); + ADD_DUP_SIZE(new_persistent_script->full_path, new_persistent_script->full_path_len + 1); + ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script)); + + RETURN_SIZE(); +} diff --git a/zend_shared_alloc.c b/zend_shared_alloc.c new file mode 100644 index 0000000000..c0024c6666 --- /dev/null +++ b/zend_shared_alloc.c @@ -0,0 +1,473 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#ifndef ZEND_WIN32 +# include +# include +# include +# include +# include +#endif + +#ifdef HAVE_MPROTECT +# include "sys/mman.h" +#endif + +#define TMP_DIR "/tmp" +#define SEM_FILENAME_PREFIX ".ZendSem." +#define S_H(s) g_shared_alloc_handler->s + +/* True globals */ +static zend_bool locked; +/* old/new mapping. We can use true global even for ZTS because its usage + is wrapped with exclusive lock anyway */ +static HashTable xlat_table; +static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL; +static const char *g_shared_model; +/* pointer to globals allocated in SHM and shared across processes */ +zend_smm_shared_globals *smm_shared_globals; + +#ifndef ZEND_WIN32 +int lock_file; +static char lockfile_name[sizeof(TMP_DIR)+sizeof(SEM_FILENAME_PREFIX)+8]; +#endif + +static const zend_shared_memory_handler_entry handler_table[] = { +#if USE_MMAP + { "mmap", &zend_alloc_mmap_handlers }, +#endif +#if USE_SHM + { "shm", &zend_alloc_shm_handlers }, +#endif +#if USE_SHM_OPEN + { "posix", &zend_alloc_posix_handlers }, +#endif +#ifdef ZEND_WIN32 + { "win32", &zend_alloc_win32_handlers }, +#endif + { NULL, NULL} +}; + +#ifndef ZEND_WIN32 +void zend_shared_alloc_create_lock(void) +{ + int val; + + sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX); + lock_file = mkstemp(lockfile_name); + fchmod(lock_file, 0666); + + if (lock_file == -1) { + zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno); + } + val = fcntl(lock_file, F_GETFD, 0); + val |= FD_CLOEXEC; + fcntl(lock_file, F_SETFD, val); + + unlink(lockfile_name); +} +#endif + +static void no_memory_bailout(int allocate_size, char *error) +{ + zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %d bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno ); +} + +static void copy_shared_segments(void *to, void *from, int count, int size) +{ + zend_shared_segment **shared_segments_v = (zend_shared_segment **)to; + void *shared_segments_to_p = ((char *)to + count*(sizeof(void *))); + void *shared_segments_from_p = from; + int i; + + for(i=0;ihandler; + g_shared_model = he->name; + ZSMMG(shared_segments) = NULL; + ZSMMG(shared_segments_count) = 0; + + res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in); + + if( res ) { + /* this model works! */ + return res; + } + if(*shared_segments_p) { + int i; + /* cleanup */ + for(i=0;i<*shared_segments_count;i++) { + if((*shared_segments_p)[i]->p && (int)(*shared_segments_p)[i]->p != -1) { + S_H(detach_segment)((*shared_segments_p)[i]); + } + } + free(*shared_segments_p); + *shared_segments_p = NULL; + } + g_shared_alloc_handler = NULL; + return ALLOC_FAILURE; +} + +int zend_shared_alloc_startup(int requested_size) +{ + zend_shared_segment **tmp_shared_segments; + size_t shared_segments_array_size; + zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals; + char *error_in = NULL; + const zend_shared_memory_handler_entry *he; + int res = ALLOC_FAILURE; + + TSRMLS_FETCH(); + + /* shared_free must be valid before we call zend_shared_alloc() + * - make it temporarily point to a local variable + */ + smm_shared_globals = &tmp_shared_globals; + ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */ + + zend_shared_alloc_create_lock(); + + if(ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) { + char* model = ZCG(accel_directives).memory_model; + /* "cgi" is really "shm"... */ + if( strncmp(ZCG(accel_directives).memory_model,"cgi",4) == 0 ){ + model = "shm"; + } + + for(he = handler_table; he->name; he++) { + if(strcmp(model, he->name) == 0) { + res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in); + if( res ) { + /* this model works! */ + } + break; + } + } + } + + if( res == FAILED_REATTACHED ){ + smm_shared_globals = NULL; + return res; + } + + if(!g_shared_alloc_handler) { + /* try memory handlers in order */ + for(he = handler_table; he->name; he++) { + res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in); + if( res ) { + /* this model works! */ + break; + } + } + } + + if(!g_shared_alloc_handler) { + no_memory_bailout(requested_size, error_in); + return ALLOC_FAILURE; + } + + if( res == SUCCESSFULLY_REATTACHED ){ + return res; + } + + shared_segments_array_size = ZSMMG(shared_segments_count)*S_H(segment_type_size)(); + + /* move shared_segments and shared_free to shared memory */ + locked = 1; /* no need to perform a real lock at this point */ + p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals)); + + tmp_shared_segments = zend_shared_alloc(shared_segments_array_size+ZSMMG(shared_segments_count)*sizeof(void *)); + copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)()); + + *p_tmp_shared_globals = tmp_shared_globals; + smm_shared_globals = p_tmp_shared_globals; + + free(ZSMMG(shared_segments)); + ZSMMG(shared_segments) = tmp_shared_segments; + + ZSMMG(shared_memory_state).positions = (int *) zend_shared_alloc(sizeof(int)*ZSMMG(shared_segments_count)); + locked = 0; + + return res; +} + +void zend_shared_alloc_shutdown(void) +{ + zend_shared_segment **tmp_shared_segments; + size_t shared_segments_array_size; + zend_smm_shared_globals tmp_shared_globals; + int i; + + tmp_shared_globals = *smm_shared_globals; + smm_shared_globals = &tmp_shared_globals; + shared_segments_array_size = ZSMMG(shared_segments_count)*(S_H(segment_type_size)()+sizeof(void *)); + tmp_shared_segments = emalloc(shared_segments_array_size); + copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)()); + ZSMMG(shared_segments) = tmp_shared_segments; + + for(i=0; i ZSMMG(shared_free)) { /* No hope to find a big-enough block */ + SHARED_ALLOC_FAILED(); + return NULL; + } + for (i=0; isize-ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */ + zend_shared_memory_block_header *p = (zend_shared_memory_block_header *) (((char *) ZSMMG(shared_segments)[i]->p)+ZSMMG(shared_segments)[i]->pos); + int remainder = block_size % PLATFORM_ALIGNMENT; + void *retval; + + if (remainder!=0) { + size += PLATFORM_ALIGNMENT-remainder; + block_size += PLATFORM_ALIGNMENT-remainder; + } + ZSMMG(shared_segments)[i]->pos += block_size; + ZSMMG(shared_free) -= block_size; + p->size = size; + retval = ((char *) p)+sizeof(zend_shared_memory_block_header); + memset(retval, 0, size); + return retval; + } + } + SHARED_ALLOC_FAILED(); + return NULL; +} + +int zend_shared_memdup_size(void *source, size_t size) +{ + void **old_p; + + if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) { + /* we already duplicated this pointer */ + return 0; + } + zend_shared_alloc_register_xlat_entry(source, source); + return ZEND_ALIGNED_SIZE(size); +} + +void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC) +{ + void **old_p, *retval; + + if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) { + /* we already duplicated this pointer */ + return *old_p; + } + retval = ZCG(mem);; + ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); + memcpy(retval, source, size); + if (free_source) { + interned_efree((char*)source); + } + zend_shared_alloc_register_xlat_entry(source, retval); + return retval; +} + +void zend_shared_alloc_safe_unlock(TSRMLS_D) +{ + if (locked) { + zend_shared_alloc_unlock(TSRMLS_C); + } +} + +#ifndef ZEND_WIN32 +/* name l_type l_whence l_start l_len */ +static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1); +static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1); +#endif + +void zend_shared_alloc_lock(TSRMLS_D) +{ +#ifndef ZEND_WIN32 +#if 0 + /* this will happen once per process, and will un-globalize mem_write_lock */ + if (mem_write_lock.l_pid == -1) { + mem_write_lock.l_pid = getpid(); + } +#endif + + while (1) { + if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) { + if (errno == EINTR) { + continue; + } + zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno); + } + break; + } +#else + zend_shared_alloc_lock_win32(); +#endif + + locked=1; + + /* Prepare translation table + * + * Make it persistent so that it uses malloc() and allocated blocks + * won't be taken from space which is freed by efree in memdup. + * Otherwise it leads to false matches in memdup check. + */ + zend_hash_init(&xlat_table, 100, NULL, NULL, 1); +} + +void zend_shared_alloc_unlock(TSRMLS_D) +{ + /* Destroy translation table */ + zend_hash_destroy(&xlat_table); + + locked=0; + +#ifndef ZEND_WIN32 + if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) { + zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno); + } +#else + zend_shared_alloc_unlock_win32(); +#endif +} + +void zend_shared_alloc_clear_xlat_table(void) +{ + zend_hash_clean(&xlat_table); +} + +void zend_shared_alloc_register_xlat_entry(const void *old, const void *new) +{ + zend_hash_index_update(&xlat_table, (ulong) old, (void*)&new, sizeof(void *), NULL); +} + +void *zend_shared_alloc_get_xlat_entry(const void *old) +{ + void **retval; + + if (zend_hash_index_find(&xlat_table, (ulong) old, (void **) &retval)==FAILURE) { + return NULL; + } + return *retval; +} + +size_t zend_shared_alloc_get_free_memory(void) +{ + return ZSMMG(shared_free); +} + +size_t zend_shared_alloc_get_largest_free_block(void) +{ + int i; + size_t largest_block_size=0; + + for (i=0; isize - ZSMMG(shared_segments)[i]->pos; + + if (block_size>largest_block_size) { + largest_block_size = block_size; + } + } + return largest_block_size; +} + +void zend_shared_alloc_save_state(void) +{ + int i; + + for (i=0; ipos; + } + ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free); +} + +void zend_shared_alloc_restore_state(void) +{ + int i; + + for (i=0; ipos = ZSMMG(shared_memory_state).positions[i]; + } + ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free; + ZSMMG(memory_exhausted) = 0; + ZSMMG(wasted_shared_memory) = 0; +} + +const char *zend_accel_get_shared_model() +{ + return g_shared_model; +} + +void zend_accel_shared_protect(int mode TSRMLS_DC) +{ +#ifdef HAVE_MPROTECT + int i; + + if (mode) { + mode = PROT_READ; + } else { + mode = PROT_READ|PROT_WRITE; + } + + for(i=0; i < ZSMMG(shared_segments_count); i++) { + mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode); + } +#endif +} diff --git a/zend_shared_alloc.h b/zend_shared_alloc.h new file mode 100644 index 0000000000..aa61514452 --- /dev/null +++ b/zend_shared_alloc.h @@ -0,0 +1,166 @@ +/* + +----------------------------------------------------------------------+ + | Zend Optimizer+ | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SHARED_ALLOC_H +#define ZEND_SHARED_ALLOC_H + +#include "zend.h" + +#if defined(__APPLE__) && defined(__MACH__) /* darwin */ +# define USE_SHM_OPEN 1 +# define USE_MMAP 1 +#elif defined(__linux__) || defined(_AIX) +# define USE_SHM 1 +# define USE_MMAP 1 +#elif defined(__FreeBSD__) +# define USE_SHM_OPEN 1 +# define USE_MMAP 1 +# define USE_SHM 1 +#elif defined(__sparc) || defined(__sun) +# define USE_SHM_OPEN 1 +# define USE_SHM 1 +# if defined(__i386) +# define USE_MMAP 1 +# endif +#endif + +#define ALLOC_FAILURE 0 +#define ALLOC_SUCCESS 1 +#define FAILED_REATTACHED 2 +#define SUCCESSFULLY_REATTACHED 4 +#define ALLOC_FAIL_MAPPING 8 + +typedef struct _zend_shared_segment { + size_t size; + size_t pos; /* position for simple stack allocator */ + void *p; +} zend_shared_segment; + +typedef int (*create_segments_t)(size_t requested_size, zend_shared_segment ***shared_segments, int *shared_segment_count, char **error_in); +typedef int (*detach_segment_t)(zend_shared_segment *shared_segment); + +typedef struct { + create_segments_t create_segments; + detach_segment_t detach_segment; + size_t (*segment_type_size)(void); +} zend_shared_memory_handlers; + +typedef struct _handler_entry { + const char *name; + zend_shared_memory_handlers *handler; +} zend_shared_memory_handler_entry; + +typedef struct _zend_shared_memory_block_header { + int size; +} zend_shared_memory_block_header; + +typedef struct _zend_shared_memory_state { + int *positions; /* current positions for each segment */ + int shared_free; /* amount of free shared memory */ +} zend_shared_memory_state; + +typedef struct _zend_smm_shared_globals { + /* Shared Memory Manager */ + zend_shared_segment **shared_segments; + /* Number of allocated shared segments */ + int shared_segments_count; + /* Amount of free shared memory */ + size_t shared_free; + /* Amount of shared memory allocated by garbage */ + int wasted_shared_memory; + /* No more shared memory flag */ + zend_bool memory_exhausted; + /* Saved Shared Allocator State */ + zend_shared_memory_state shared_memory_state; + /* Pointer to the application's shared data structures */ + void *app_shared_globals; +} zend_smm_shared_globals; + +extern zend_smm_shared_globals *smm_shared_globals; + +#define ZSMMG(element) (smm_shared_globals->element) + +#define SHARED_ALLOC_REATTACHED (SUCCESS+1) + +int zend_shared_alloc_startup(int requested_size); +void zend_shared_alloc_shutdown(void); + +/* allocate shared memory block */ +void *zend_shared_alloc(size_t size); + +/* copy into shared memory */ +void *_zend_shared_memdup(void *p, size_t size, zend_bool free_source TSRMLS_DC); +int zend_shared_memdup_size(void *p, size_t size); + +typedef union _align_test { + void *ptr; + double dbl; + long lng; +} align_test; + +#if ZEND_GCC_VERSION >= 2000 +# define PLATFORM_ALIGNMENT (__alignof__ (align_test)) +#else +# define PLATFORM_ALIGNMENT (sizeof(align_test)) +#endif + +#define ZEND_ALIGNED_SIZE(size) \ + ((size + PLATFORM_ALIGNMENT - 1) & ~(PLATFORM_ALIGNMENT - 1)) + +/* exclusive locking */ +void zend_shared_alloc_lock(TSRMLS_D); +void zend_shared_alloc_unlock(TSRMLS_D); /* returns the allocated size during lock..unlock */ +void zend_shared_alloc_safe_unlock(TSRMLS_D); + +/* old/new mapping functions */ +void zend_shared_alloc_clear_xlat_table(void); +void zend_shared_alloc_register_xlat_entry(const void *old, const void *new); +void *zend_shared_alloc_get_xlat_entry(const void *old); + +size_t zend_shared_alloc_get_free_memory(void); +void zend_shared_alloc_save_state(void); +void zend_shared_alloc_restore_state(void); +size_t zend_shared_alloc_get_largest_free_block(void); +const char *zend_accel_get_shared_model(); + +/* memory write protection */ +void zend_accel_shared_protect(int mode TSRMLS_DC); + +#ifdef USE_MMAP +extern zend_shared_memory_handlers zend_alloc_mmap_handlers; +#endif + +#ifdef USE_SHM +extern zend_shared_memory_handlers zend_alloc_shm_handlers; +#endif + +#ifdef USE_SHM_OPEN +extern zend_shared_memory_handlers zend_alloc_posix_handlers; +#endif + +#ifdef ZEND_WIN32 +extern zend_shared_memory_handlers zend_alloc_win32_handlers; +void zend_shared_alloc_create_lock(); +void zend_shared_alloc_lock_win32(); +void zend_shared_alloc_unlock_win32(); +#endif + +#endif /* ZEND_SHARED_ALLOC_H */ -- 2.40.0