static inline zend_constant *zend_get_constant_impl(zend_string *name) { zval *zv; zend_constant *c; ALLOCA_FLAG(use_heap) zv = zend_hash_find(EG(zend_constants), name); if (zv == NULL) { char *lcname = do_alloca(ZSTR_LEN(name) + 1, use_heap); zend_str_tolower_copy(lcname, ZSTR_VAL(name), ZSTR_LEN(name)); zv = zend_hash_str_find(EG(zend_constants), lcname, ZSTR_LEN(name)); if (zv != NULL) { c = Z_PTR_P(zv); if (ZEND_CONSTANT_FLAGS(c) & CONST_CS) { c = NULL; } } else { c = zend_get_special_constant(ZSTR_VAL(name), ZSTR_LEN(name)); } free_alloca(lcname, use_heap); return c; } else { return (zend_constant *) Z_PTR_P(zv); } }
ZEND_API zval *zend_get_constant(zend_string *name) { zval *zv; zend_constant *c; ALLOCA_FLAG(use_heap) zv = zend_hash_find(EG(zend_constants), name); if (zv == NULL) { char *lcname = do_alloca(ZSTR_LEN(name) + 1, use_heap); zend_str_tolower_copy(lcname, ZSTR_VAL(name), ZSTR_LEN(name)); zv = zend_hash_str_find(EG(zend_constants), lcname, ZSTR_LEN(name)); if (zv != NULL) { c = Z_PTR_P(zv); if (c->flags & CONST_CS) { c = NULL; } } else { c = zend_get_special_constant(ZSTR_VAL(name), ZSTR_LEN(name)); } free_alloca(lcname, use_heap); return c ? &c->value : NULL; } else { return &((zend_constant*)Z_PTR_P(zv))->value; } }
void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) { if (op_array->last_live_range) { int i = 0; int j = 0; uint32_t *map; ALLOCA_FLAG(use_heap); map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap); do { if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) { map[i] = j; if (i != j) { op_array->live_range[j] = op_array->live_range[i]; } j++; } i++; } while (i < op_array->last_live_range); if (i != j) { zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; op_array->last_live_range = j; while (opline != end) { if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) && opline->extended_value == ZEND_FREE_ON_RETURN) { opline->op2.num = map[opline->op2.num]; } opline++; } } } }
/* }}} */ static void xcache_mkdirs_ex(char *root, int rootlen, char *path, int pathlen TSRMLS_DC) /* {{{ */ { char *fullpath; struct stat st; ALLOCA_FLAG(use_heap) TRACE("mkdirs %s %d %s %d", root, rootlen, path, pathlen); fullpath = xc_do_alloca(rootlen + pathlen + 1, use_heap); memcpy(fullpath, root, rootlen); memcpy(fullpath + rootlen, path, pathlen); fullpath[rootlen + pathlen] = '\0'; if (stat(fullpath, &st) != 0) { char *chr; chr = strrchr(path, PHP_DIR_SEPARATOR); if (chr && chr != path) { *chr = '\0'; xcache_mkdirs_ex(root, rootlen, path, chr - path TSRMLS_CC); *chr = PHP_DIR_SEPARATOR; } TRACE("mkdir %s", fullpath); #if PHP_MAJOR_VERSION > 5 php_stream_mkdir(fullpath, 0700, REPORT_ERRORS, NULL); #else mkdir(fullpath, 0700); #endif } xc_free_alloca(fullpath, use_heap); }
U_CFUNC zend_function *IntlPartsIterator_get_method(zend_object **object_ptr, zend_string *method, const zval *key) { zend_function *ret; zend_string *lc_method_name; ALLOCA_FLAG(use_heap); if (key == NULL) { STR_ALLOCA_ALLOC(lc_method_name, method->len, use_heap); zend_str_tolower_copy(lc_method_name->val, method->val, method->len); } else { lc_method_name = Z_STR_P(key); } if (method->len == sizeof("getrulestatus") - 1 && memcmp("getrulestatus", lc_method_name->val, lc_method_name->len) == 0) { IntlIterator_object *obj = php_intl_iterator_fetch_object(*object_ptr); if (obj->iterator && !Z_ISUNDEF(obj->iterator->data)) { zval *break_iter_zv = &obj->iterator->data; *object_ptr = Z_OBJ_P(break_iter_zv); ret = Z_OBJ_HANDLER_P(break_iter_zv, get_method)(object_ptr, method, key); goto end; } } ret = std_object_handlers.get_method(object_ptr, method, key); end: if (key == NULL) { STR_ALLOCA_FREE(lc_method_name, use_heap); } return ret; }
static int32_t qb_transfer_operands_function_call(qb_compiler_context *cxt, qb_op_factory *f, uint32_t flags, qb_operand *operands, uint32_t operand_count, qb_operand *result, qb_operand *dest, uint32_t dest_count) { USE_TSRM qb_operand *func = &operands[0], *arguments = &operands[1], *argument_count = &operands[2]; const char *func_name = func->zend_function->common.function_name; qb_external_symbol_type symbol_type = (func->type == QB_OPERAND_STATIC_ZEND_FUNCTION) ? QB_EXT_SYM_STATIC_ZEND_FUNCTION : QB_EXT_SYM_ZEND_FUNCTION; uint32_t func_name_len = (uint32_t) strlen(func_name); uint32_t func_index = qb_import_external_symbol(symbol_type, func_name, func_name_len, func->zend_function TSRMLS_CC); uint32_t *var_indices, ret_index = INVALID_INDEX; uint32_t i; ALLOCA_FLAG(use_heap); var_indices = do_alloca(sizeof(uint32_t *) * argument_count->number, use_heap); for(i = 0; i < (uint32_t) argument_count->number; i++) { qb_operand *arg = &arguments->arguments[i]; var_indices[i] = qb_get_variable_index(cxt, arg->address); } if(result->type == QB_OPERAND_ADDRESS && result->address != cxt->zero_address) { ret_index = qb_get_variable_index(cxt, result->address); qb_mark_as_writable(cxt, result->address); } dest[0].address = qb_obtain_constant_U32(cxt, func_index); dest[0].type = QB_OPERAND_ADDRESS; dest[1].address = qb_obtain_constant_indices(cxt, var_indices, argument_count->number); dest[1].type = QB_OPERAND_ADDRESS; dest[2].address = qb_obtain_constant_U32(cxt, ret_index); dest[2].type = QB_OPERAND_ADDRESS; free_alloca(var_indices, use_heap); return TRUE; }
static zend_string *browscap_intern_str_ci( browscap_parser_ctx *ctx, zend_string *str, zend_bool persistent) { zend_string *lcname; zend_string *interned; ALLOCA_FLAG(use_heap); ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(str), use_heap); zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(str), ZSTR_LEN(str)); interned = zend_hash_find_ptr(&ctx->str_interned, lcname); if (interned) { zend_string_addref(interned); } else { interned = zend_string_dup(lcname, persistent); zend_hash_add_new_ptr(&ctx->str_interned, interned, interned); } ZSTR_ALLOCA_FREE(lcname, use_heap); return interned; }
static zend_class_entry * spl_find_ce_by_name(char *name, int len, zend_bool autoload TSRMLS_DC) { zend_class_entry **ce; int found; if (!autoload) { char *lc_name; ALLOCA_FLAG(use_heap) lc_name = do_alloca(len + 1, use_heap); zend_str_tolower_copy(lc_name, name, len); found = zend_hash_find(EG(class_table), lc_name, len +1, (void **) &ce); free_alloca(lc_name, use_heap); } else { found = zend_lookup_class(name, len, &ce TSRMLS_CC); } if (found != SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Class %s does not exist%s", name, autoload ? " and could not be loaded" : ""); return NULL; } return *ce; }
/* {{{ collator_u_strtod * Taken from PHP6:zend_u_strtod() */ static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */ { const UChar *u = nptr, *nstart; UChar c = *u; int any = 0; ALLOCA_FLAG(use_heap); while (u_isspace(c)) { c = *++u; } nstart = u; if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { c = *++u; } while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { any = 1; c = *++u; } if (c == 0x2E /*'.'*/) { c = *++u; while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { any = 1; c = *++u; } } if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) { const UChar *e = u; int any_exp = 0; c = *++u; if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { c = *++u; } while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { any_exp = 1; c = *++u; } if (!any_exp) { u = e; } } if (any) { char buf[64], *numbuf, *bufpos; size_t length = u - nstart; double value; if (length < sizeof(buf)) { numbuf = buf; } else { numbuf = (char *) do_alloca(length + 1, use_heap); } bufpos = numbuf; while (nstart < u) { *bufpos++ = (char) *nstart++; } *bufpos = '\0'; value = zend_strtod(numbuf, NULL); if (numbuf != buf) { free_alloca(numbuf, use_heap); } if (endptr != NULL) { *endptr = (UChar *)u; } return value; } if (endptr != NULL) { *endptr = (UChar *)nptr; } return 0; }
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */ { int set_size; zend_basic_block *blocks = cfg->blocks; int blocks_count = cfg->blocks_count; zend_bitset tmp, def, use, in, out; int k; uint32_t var_num; int j; set_size = dfg->size; tmp = dfg->tmp; def = dfg->def; use = dfg->use; in = dfg->in; out = dfg->out; /* Collect "def" and "use" sets */ for (j = 0; j < blocks_count; j++) { zend_op *opline, *end; if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } opline = op_array->opcodes + blocks[j].start; end = opline + blocks[j].len; for (; opline < end; opline++) { if (opline->opcode != ZEND_OP_DATA) { zend_op *next = opline + 1; if (next < end && next->opcode == ZEND_OP_DATA) { if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(next->op1.var); if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } } if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(next->op2.var); if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } } } if (opline->op1_type == IS_CV) { var_num = EX_VAR_TO_NUM(opline->op1.var); switch (opline->opcode) { case ZEND_ADD_ARRAY_ELEMENT: case ZEND_INIT_ARRAY: if ((build_flags & ZEND_SSA_RC_INFERENCE) || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { goto op1_def; } goto op1_use; case ZEND_FE_RESET_R: case ZEND_SEND_VAR: case ZEND_CAST: case ZEND_QM_ASSIGN: case ZEND_JMP_SET: case ZEND_COALESCE: if (build_flags & ZEND_SSA_RC_INFERENCE) { goto op1_def; } goto op1_use; case ZEND_YIELD: if ((build_flags & ZEND_SSA_RC_INFERENCE) || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { goto op1_def; } goto op1_use; case ZEND_UNSET_VAR: ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET); /* break missing intentionally */ case ZEND_ASSIGN: case ZEND_ASSIGN_REF: case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_SEND_VAR_EX: case ZEND_SEND_REF: case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_NO_REF_EX: case ZEND_FE_RESET_RW: 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_ASSIGN_POW: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: case ZEND_UNSET_DIM: case ZEND_UNSET_OBJ: case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_OBJ_UNSET: case ZEND_VERIFY_RETURN_TYPE: op1_def: /* `def` always come along with dtor or separation, * thus the origin var info might be also `use`d in the feature(CG) */ DFG_SET(use, set_size, j, var_num); DFG_SET(def, set_size, j, var_num); break; default: op1_use: if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } } } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(opline->op1.var); if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) { DFG_SET(use, set_size, j, var_num); DFG_SET(def, set_size, j, var_num); } else if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } } if (opline->op2_type == IS_CV) { var_num = EX_VAR_TO_NUM(opline->op2.var); switch (opline->opcode) { case ZEND_ASSIGN: if (build_flags & ZEND_SSA_RC_INFERENCE) { goto op2_def; } goto op2_use; case ZEND_BIND_LEXICAL: if ((build_flags & ZEND_SSA_RC_INFERENCE) || opline->extended_value) { goto op2_def; } goto op2_use; case ZEND_ASSIGN_REF: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: op2_def: // FIXME: include into "use" too ...? DFG_SET(use, set_size, j, var_num); DFG_SET(def, set_size, j, var_num); break; default: op2_use: if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } break; } } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(opline->op2.var); if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) { DFG_SET(def, set_size, j, var_num); } else { if (!DFG_ISSET(def, set_size, j, var_num)) { DFG_SET(use, set_size, j, var_num); } } } if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(opline->result.var); if ((build_flags & ZEND_SSA_USE_CV_RESULTS) && opline->result_type == IS_CV) { DFG_SET(use, set_size, j, var_num); } DFG_SET(def, set_size, j, var_num); } } } } /* Calculate "in" and "out" sets */ { uint32_t worklist_len = zend_bitset_len(blocks_count); zend_bitset worklist; ALLOCA_FLAG(use_heap); worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap); memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE); for (j = 0; j < blocks_count; j++) { zend_bitset_incl(worklist, j); } while (!zend_bitset_empty(worklist, worklist_len)) { /* We use the last block on the worklist, because predecessors tend to be located * before the succeeding block, so this converges faster. */ j = zend_bitset_last(worklist, worklist_len); zend_bitset_excl(worklist, j); if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } if (blocks[j].successors_count != 0) { zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size); for (k = 1; k < blocks[j].successors_count; k++) { zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size); } } else { zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size); } zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size); if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) { zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size); /* Add predecessors of changed block to worklist */ { int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset]; for (k = 0; k < blocks[j].predecessors_count; k++) { zend_bitset_incl(worklist, predecessors[k]); } } } } free_alloca(worklist, use_heap); } return SUCCESS; }
void zend_optimizer_nop_removal(zend_op_array *op_array) { zend_op *end, *opline; uint32_t new_count, i, shift; int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)DO_ALLOCA(sizeof(uint32_t) * op_array->last); i = new_count = shift = 0; end = op_array->opcodes + op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { /* 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; opline<end; opline++) { switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: 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_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: opline->extended_value -= shiftlist[opline->extended_value]; 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 try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *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 != (uint32_t)-1); } } FREE_ALLOCA(shiftlist); }
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; opline<end; opline++) { switch (opline->opcode) { 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; i<op_array->last_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; j<op_array->last_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); }
void zend_optimizer_nop_removal(zend_op_array *op_array) { zend_op *end, *opline; uint32_t new_count, i, shift; int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); i = new_count = shift = 0; end = op_array->opcodes + op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { /* Kill JMP-over-NOP-s */ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) { /* check if there are only NOPs under the branch */ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 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) { zend_op *new_opline = op_array->opcodes + new_count; *new_opline = *opline; switch (new_opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); break; case ZEND_JMPZNZ: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); /* break missing intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); break; case ZEND_CATCH: if (!opline->result.num) { new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); } break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); break; } } new_count++; } } if (shift) { op_array->last = new_count; end = op_array->opcodes + op_array->last; /* update JMPs */ for (opline = op_array->opcodes; opline<end; opline++) { switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); break; case ZEND_JMPZNZ: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); /* break missing intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_CATCH: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); break; } } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *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 != (uint32_t)-1); } } free_alloca(shiftlist, use_heap); }
void zend_optimizer_pass3(zend_op_array *op_array) { zend_op *opline; zend_op *end = op_array->opcodes + op_array->last; zend_op **jmp_hitlist; int jmp_hitlist_count; int i; uint32_t opline_num = 0; ALLOCA_FLAG(use_heap); jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); opline = op_array->opcodes; while (opline < end) { jmp_hitlist_count = 0; switch (opline->opcode) { case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: case ZEND_MOD: case ZEND_POW: 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 ((opline->op2_type & (IS_VAR | IS_CV)) && opline->op2.var == next_opline->op1.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 */ zend_uchar tmp_type = opline->op1_type; znode_op tmp = opline->op1; if (opline->opcode != ZEND_ADD || (opline->op1_type == IS_CONST && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) { /* protection from array add: $a = array + $a is not commutative! */ COPY_NODE(opline->op1, opline->op2); COPY_NODE(opline->op2, tmp); } } if ((opline->op1_type & (IS_VAR | IS_CV)) && opline->op1.var == next_opline->op1.var && opline->op1_type == next_opline->op1_type) { 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_POW: opline->opcode = ZEND_ASSIGN_POW; 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 (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* convert L: JMP L+1 to NOP */ if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) { MAKE_NOP(opline); goto done_jmp_optimization; } /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ while (ZEND_OP1_JMP_ADDR(opline) < end && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) { zend_op *target = ZEND_OP1_JMP_ADDR(opline); CHECK_JMP(target, done_jmp_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target)); } break; case ZEND_JMP_SET: case ZEND_COALESCE: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (target->opcode == ZEND_JMP) { ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } else { break; } } break; case ZEND_JMPZ: case ZEND_JMPNZ: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (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_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } else if (target->opcode == opline->opcode && SAME_VAR(opline->op1, 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_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == opline->opcode + 3 && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to T = JMPZ_EX(X, L2) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); opline->opcode += 3; COPY_NODE(opline->result, target->result); break; } else if (target->opcode == INV_COND(opline->opcode) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to JMPZ(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else if (target->opcode == INV_COND_EX(opline->opcode) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to T = JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); opline->opcode += 3; COPY_NODE(opline->result, target->result); break; } else { break; } } break; case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: { zend_uchar T_type = opline->result_type; znode_op T = opline->result; if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* 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_JMP_ADDR(opline) == opline + 1) { if (opline->op1.var == opline->result.var) { MAKE_NOP(opline); } else { opline->opcode = ZEND_BOOL; SET_UNUSED(opline->op2); } goto done_jmp_optimization; } while (ZEND_OP2_JMP_ADDR(opline) < end) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); if (target->opcode == opline->opcode-3 && SAME_VAR(target->op1, T)) { /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to JMPZ_EX(X,L2) */ CHECK_JMP2(target, continue_jmp_ex_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == opline->opcode && SAME_VAR(target->op1, T) && SAME_VAR(target->result, T)) { /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to JMPZ_EX(X,L2) */ CHECK_JMP2(target, continue_jmp_ex_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); } else if (target->opcode == ZEND_JMPZNZ && SAME_VAR(target->op1, T)) { /* Check for JMPZNZ with same cond variable */ zend_op *new_target; CHECK_JMP2(target, continue_jmp_ex_optimization); if (opline->opcode == ZEND_JMPZ_EX) { new_target = ZEND_OP2_JMP_ADDR(target); } else { /* JMPNZ_EX */ new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); } ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target); } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) || target->opcode == INV_EX_COND(opline->opcode)) && SAME_VAR(opline->op1, target->op1)) { /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else if (target->opcode == INV_EX_COND(opline->opcode) && SAME_VAR(target->op1, T)) { /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else if (target->opcode == INV_EX_COND_EX(opline->opcode) && SAME_VAR(target->op1, T) && SAME_VAR(target->result, T)) { /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to JMPZ_EX(X,L1+1) */ ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); break; } else if (target->opcode == ZEND_BOOL && SAME_VAR(opline->result, target->op1)) { /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to Z = JMPZ_EX(X,L1+1) */ opline->result.var = target->result.var; ZEND_SET_OP_JMP_ADDR(opline, opline->op2, 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; op<end; op++) { if(op->result_type == IS_TMP_VAR && op->result.var == opline->result.var) { break; /* can pass to part 2 */ } if(op->opcode == 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_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || op->opcode == ZEND_FE_FETCH_R || op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } if(op->op1_type == IS_TMP_VAR && op->op1.var == opline->result.var) { goto done_jmp_optimization; } if(op->op2_type == IS_TMP_VAR && op->op2.var == opline->result.var) { goto done_jmp_optimization; } } /* for */ for(op = &op_array->opcodes[opline->op2.opline_num]; op<end; op++) { if(op->result_type == IS_TMP_VAR && op->result.var == opline->result.var) { break; /* can pass to optimization */ } if(op->opcode == 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_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || op->opcode == ZEND_FE_FETCH_R || op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } if(op->op1_type == IS_TMP_VAR && op->op1.var == opline->result.var) { goto done_jmp_optimization; } if(op->op2_type == IS_TMP_VAR && op->op2.var == opline->result.var) { goto done_jmp_optimization; } } opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ SET_UNUSED(opline->result); break; } #endif } break; case ZEND_JMPZNZ: if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { break; } /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ while (ZEND_OP2_JMP_ADDR(opline) < end && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) { zend_op *target = ZEND_OP2_JMP_ADDR(opline); CHECK_JMP(target, continue_jmpznz_optimization); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); } continue_jmpznz_optimization: /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) { zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); CHECK_JMP(target, done_jmp_optimization); opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target)); } 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 && next_op->op1.var == opline->result.var) { MAKE_NOP(next_op); opline->opcode -= 2; opline->result_type = IS_UNUSED; } } break; } done_jmp_optimization: opline++; opline_num++; } free_alloca(jmp_hitlist, use_heap); }
ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, zend_ulong flags) { zend_constant *c; const char *colon; zend_class_entry *ce = NULL; zend_string *class_name; const char *name = cname->val; size_t name_len = cname->len; /* Skip leading \\ */ if (name[0] == '\\') { name += 1; name_len -= 1; cname = NULL; } if ((colon = zend_memrchr(name, ':', name_len)) && colon > name && (*(colon - 1) == ':')) { int class_name_len = colon - name - 1; size_t const_name_len = name_len - class_name_len - 2; zend_string *constant_name = zend_string_init(colon + 1, const_name_len, 0); char *lcname; zval *ret_constant = NULL; ALLOCA_FLAG(use_heap) class_name = zend_string_init(name, class_name_len, 0); lcname = do_alloca(class_name_len + 1, use_heap); zend_str_tolower_copy(lcname, name, class_name_len); if (!scope) { if (EG(current_execute_data)) { scope = EG(scope); } else { scope = CG(active_class_entry); } } if (class_name_len == sizeof("self")-1 && !memcmp(lcname, "self", sizeof("self")-1)) { if (UNEXPECTED(!scope)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot access self:: when no class scope is active"); return NULL; } ce = scope; } else if (class_name_len == sizeof("parent")-1 && !memcmp(lcname, "parent", sizeof("parent")-1)) { if (UNEXPECTED(!scope)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot access parent:: when no class scope is active"); return NULL; } else if (UNEXPECTED(!scope->parent)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot access parent:: when current class scope has no parent"); return NULL; } else { ce = scope->parent; } } else if (class_name_len == sizeof("static")-1 && !memcmp(lcname, "static", sizeof("static")-1)) { ce = zend_get_called_scope(EG(current_execute_data)); if (UNEXPECTED(!ce)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot access static:: when no class scope is active"); return NULL; } } else { ce = zend_fetch_class(class_name, flags); } free_alloca(lcname, use_heap); if (ce) { ret_constant = zend_hash_find(&ce->constants_table, constant_name); if (ret_constant == NULL) { if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s::%s'", class_name->val, constant_name->val); zend_string_release(class_name); zend_string_free(constant_name); return NULL; } } else if (Z_ISREF_P(ret_constant)) { ret_constant = Z_REFVAL_P(ret_constant); } } zend_string_release(class_name); zend_string_free(constant_name); if (ret_constant && Z_CONSTANT_P(ret_constant)) { if (UNEXPECTED(zval_update_constant_ex(ret_constant, 1, ce) != SUCCESS)) { return NULL; } } return ret_constant; } /* non-class constant */ if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) { /* compound constant name */ int prefix_len = colon - name; size_t const_name_len = name_len - prefix_len - 1; const char *constant_name = colon + 1; char *lcname; size_t lcname_len; ALLOCA_FLAG(use_heap) lcname_len = prefix_len + 1 + const_name_len; lcname = do_alloca(lcname_len + 1, use_heap); zend_str_tolower_copy(lcname, name, prefix_len); /* Check for namespace constant */ lcname[prefix_len] = '\\'; memcpy(lcname + prefix_len + 1, constant_name, const_name_len + 1); if ((c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len)) == NULL) { /* try lowercase */ zend_str_tolower(lcname + prefix_len + 1, const_name_len); if ((c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len)) != NULL) { if ((c->flags & CONST_CS) != 0) { c = NULL; } } } free_alloca(lcname, use_heap); if (c) { return &c->value; } /* name requires runtime resolution, need to check non-namespaced name */ if ((flags & IS_CONSTANT_UNQUALIFIED) != 0) { return zend_get_constant_str(constant_name, const_name_len); } return NULL; } if (cname) { return zend_get_constant(cname); } else { return zend_get_constant_str(name, name_len); } }
int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */ { int i, j, k; int depth; zend_basic_block *blocks = cfg->blocks; int *dj_spanning_tree; zend_worklist work; int flag = ZEND_FUNC_NO_LOOPS; ALLOCA_FLAG(list_use_heap); ALLOCA_FLAG(tree_use_heap); ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap); dj_spanning_tree = do_alloca(sizeof(int) * cfg->blocks_count, tree_use_heap); for (i = 0; i < cfg->blocks_count; i++) { dj_spanning_tree[i] = -1; } zend_worklist_push(&work, 0); while (zend_worklist_len(&work)) { next: i = zend_worklist_peek(&work); /* Visit blocks immediately dominated by i. */ for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) { if (zend_worklist_push(&work, j)) { dj_spanning_tree[j] = i; goto next; } } /* Visit join edges. */ for (j = 0; j < 2; j++) { int succ = blocks[i].successors[j]; if (succ < 0) { continue; } else if (blocks[succ].idom == i) { continue; } else if (zend_worklist_push(&work, succ)) { dj_spanning_tree[succ] = i; goto next; } } zend_worklist_pop(&work); } /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ Graphs". */ for (i = 0, depth = 0; i < cfg->blocks_count; i++) { if (blocks[i].level > depth) { depth = blocks[i].level; } } for (; depth >= 0; depth--) { for (i = 0; i < cfg->blocks_count; i++) { if (blocks[i].level != depth) { continue; } zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count)); for (j = 0; j < blocks[i].predecessors_count; j++) { int pred = cfg->predecessors[blocks[i].predecessor_offset + j]; /* A join edge is one for which the predecessor does not immediately dominate the successor. */ if (blocks[i].idom == pred) { continue; } /* In a loop back-edge (back-join edge), the successor dominates the predecessor. */ if (dominates(blocks, i, pred)) { blocks[i].flags |= ZEND_BB_LOOP_HEADER; flag &= ~ZEND_FUNC_NO_LOOPS; zend_worklist_push(&work, pred); } else { /* Otherwise it's a cross-join edge. See if it's a branch to an ancestor on the dominator spanning tree. */ int dj_parent = pred; while (dj_parent >= 0) { if (dj_parent == i) { /* An sp-back edge: mark as irreducible. */ blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP; flag |= ZEND_FUNC_IRREDUCIBLE; flag &= ~ZEND_FUNC_NO_LOOPS; break; } else { dj_parent = dj_spanning_tree[dj_parent]; } } } } while (zend_worklist_len(&work)) { j = zend_worklist_pop(&work); if (blocks[j].loop_header < 0 && j != i) { blocks[j].loop_header = i; for (k = 0; k < blocks[j].predecessors_count; k++) { zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]); } } } } } free_alloca(dj_spanning_tree, tree_use_heap); ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap); *flags |= flag; return SUCCESS; }
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx) { zend_basic_block *blocks = ssa->cfg.blocks; zend_basic_block *end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; uint32_t i = 0; uint32_t target = 0; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); memset(shiftlist, 0, sizeof(uint32_t) * op_array->last); /* remove empty callee_info */ func_info = ZEND_FUNC_INFO(op_array); if (func_info) { zend_call_info **call_info = &func_info->callee_info; while ((*call_info)) { if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) { *call_info = (*call_info)->next_callee; } else { call_info = &(*call_info)->next_callee; } } } for (b = blocks; b < end; b++) { if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { uint32_t end; if (b->len) { while (i < b->start) { shiftlist[i] = i - target; i++; } if (b->flags & ZEND_BB_UNREACHABLE_FREE) { /* Only keep the FREE for the loop var */ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE || op_array->opcodes[b->start].opcode == ZEND_FE_FREE); b->len = 1; } end = b->start + b->len; b->start = target; while (i < end) { shiftlist[i] = i - target; if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || /* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of * "i-1" here to check the last non-NOP instruction. */ (target > 0 && i + 1 < op_array->last && (op_array->opcodes[i+1].opcode == ZEND_JMPZ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && zend_is_smart_branch(op_array->opcodes + target - 1))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; ssa->cfg.map[target] = b - blocks; } target++; } i++; } if (target != end) { zend_op *opline; zend_op *new_opline; b->len = target - b->start; opline = op_array->opcodes + end - 1; if (opline->opcode == ZEND_NOP) { continue; } new_opline = op_array->opcodes + target - 1; zend_optimizer_migrate_jump(op_array, new_opline, opline); } } else { b->start = target; } } else { b->start = target; b->len = 0; } } if (target != op_array->last) { /* reset rest opcodes */ for (i = target; i < op_array->last; i++) { MAKE_NOP(op_array->opcodes + i); } /* update SSA variables */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].definition >= 0) { ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition]; } if (ssa->vars[j].use_chain >= 0) { ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain]; } } for (i = 0; i < op_array->last; i++) { if (ssa->ops[i].op1_use_chain >= 0) { ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain]; } if (ssa->ops[i].op2_use_chain >= 0) { ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain]; } if (ssa->ops[i].res_use_chain >= 0) { ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain]; } } /* update branch targets */ for (b = blocks; b < end; b++) { if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) { zend_op *opline = op_array->opcodes + b->start + b->len - 1; zend_optimizer_shift_jump(op_array, opline, shiftlist); } } /* update try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { uint32_t *opline_num = &ctx->script->first_early_binding_opline; ZEND_ASSERT(op_array == &ctx->script->main_op_array); do { *opline_num -= shiftlist[*opline_num]; opline_num = &op_array->opcodes[*opline_num].result.opline_num; } while (*opline_num != (uint32_t)-1); } /* update call graph */ if (func_info) { zend_call_info *call_info = func_info->callee_info; while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; call_info->caller_call_opline -= shiftlist[call_info->caller_call_opline - op_array->opcodes]; call_info = call_info->next_callee; } } op_array->last = target; } free_alloca(shiftlist, use_heap); }
static int browser_reg_compare( zval *entry_zv, int num_args, va_list args, zend_hash_key *key) /* {{{ */ { browscap_entry *entry = Z_PTR_P(entry_zv); zend_string *agent_name = va_arg(args, zend_string *); browscap_entry **found_entry_ptr = va_arg(args, browscap_entry **); browscap_entry *found_entry = *found_entry_ptr; ALLOCA_FLAG(use_heap); zend_string *pattern_lc, *regex; const char *cur; int i; pcre *re; int re_options; pcre_extra *re_extra; /* Agent name too short */ if (ZSTR_LEN(agent_name) < browscap_get_minimum_length(entry)) { return 0; } /* Quickly discard patterns where the prefix doesn't match. */ if (zend_binary_strcasecmp( ZSTR_VAL(agent_name), entry->prefix_len, ZSTR_VAL(entry->pattern), entry->prefix_len) != 0) { return 0; } /* Lowercase the pattern, the agent name is already lowercase */ ZSTR_ALLOCA_ALLOC(pattern_lc, ZSTR_LEN(entry->pattern), use_heap); zend_str_tolower_copy(ZSTR_VAL(pattern_lc), ZSTR_VAL(entry->pattern), ZSTR_LEN(entry->pattern)); /* Check if the agent contains the "contains" portions */ cur = ZSTR_VAL(agent_name) + entry->prefix_len; for (i = 0; i < BROWSCAP_NUM_CONTAINS; i++) { if (entry->contains_len[i] != 0) { cur = zend_memnstr(cur, ZSTR_VAL(pattern_lc) + entry->contains_start[i], entry->contains_len[i], ZSTR_VAL(agent_name) + ZSTR_LEN(agent_name)); if (!cur) { ZSTR_ALLOCA_FREE(pattern_lc, use_heap); return 0; } cur += entry->contains_len[i]; } } /* See if we have an exact match, if so, we're done... */ if (zend_string_equals(agent_name, pattern_lc)) { *found_entry_ptr = entry; ZSTR_ALLOCA_FREE(pattern_lc, use_heap); return ZEND_HASH_APPLY_STOP; } regex = browscap_convert_pattern(entry->pattern, 0); re = pcre_get_compiled_regex(regex, &re_extra, &re_options); if (re == NULL) { ZSTR_ALLOCA_FREE(pattern_lc, use_heap); zend_string_release(regex); return 0; } if (pcre_exec(re, re_extra, ZSTR_VAL(agent_name), ZSTR_LEN(agent_name), 0, re_options, NULL, 0) == 0) { /* If we've found a possible browser, we need to do a comparison of the number of characters changed in the user agent being checked versus the previous match found and the current match. */ if (found_entry) { size_t i, prev_len = 0, curr_len = 0; zend_string *previous_match = found_entry->pattern; zend_string *current_match = entry->pattern; for (i = 0; i < ZSTR_LEN(previous_match); i++) { switch (ZSTR_VAL(previous_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++prev_len; } } for (i = 0; i < ZSTR_LEN(current_match); i++) { switch (ZSTR_VAL(current_match)[i]) { case '?': case '*': /* do nothing, ignore these characters in the count */ break; default: ++curr_len; } } /* Pick which browser pattern replaces the least amount of characters when compared to the original user agent string... */ if (prev_len < curr_len) { *found_entry_ptr = entry; } } else { *found_entry_ptr = entry; } } ZSTR_ALLOCA_FREE(pattern_lc, use_heap); zend_string_release(regex); return 0; }
static zend_string *browscap_convert_pattern(zend_string *pattern, int persistent) /* {{{ */ { size_t i, j=0; char *t; zend_string *res; char *lc_pattern; ALLOCA_FLAG(use_heap); res = zend_string_alloc(browscap_compute_regex_len(pattern), persistent); t = ZSTR_VAL(res); lc_pattern = do_alloca(ZSTR_LEN(pattern) + 1, use_heap); zend_str_tolower_copy(lc_pattern, ZSTR_VAL(pattern), ZSTR_LEN(pattern)); t[j++] = '~'; t[j++] = '^'; for (i = 0; i < ZSTR_LEN(pattern); i++, j++) { switch (lc_pattern[i]) { case '?': t[j] = '.'; break; case '*': t[j++] = '.'; t[j] = '*'; break; case '.': t[j++] = '\\'; t[j] = '.'; break; case '\\': t[j++] = '\\'; t[j] = '\\'; break; case '(': t[j++] = '\\'; t[j] = '('; break; case ')': t[j++] = '\\'; t[j] = ')'; break; case '~': t[j++] = '\\'; t[j] = '~'; break; case '+': t[j++] = '\\'; t[j] = '+'; break; default: t[j] = lc_pattern[i]; break; } } t[j++] = '$'; t[j++] = '~'; t[j]=0; ZSTR_LEN(res) = j; free_alloca(lc_pattern, use_heap); return res; }
ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags) { zend_constant *c; const char *colon; zend_class_entry *ce = NULL; const char *name = ZSTR_VAL(cname); size_t name_len = ZSTR_LEN(cname); /* Skip leading \\ */ if (name[0] == '\\') { name += 1; name_len -= 1; cname = NULL; } if ((colon = zend_memrchr(name, ':', name_len)) && colon > name && (*(colon - 1) == ':')) { int class_name_len = colon - name - 1; size_t const_name_len = name_len - class_name_len - 2; zend_string *constant_name = zend_string_init(colon + 1, const_name_len, 0); zend_string *class_name = zend_string_init(name, class_name_len, 0); zval *ret_constant = NULL; if (zend_string_equals_literal_ci(class_name, "self")) { if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access self:: when no class scope is active"); goto failure; } ce = scope; } else if (zend_string_equals_literal_ci(class_name, "parent")) { if (UNEXPECTED(!scope)) { zend_throw_error(NULL, "Cannot access parent:: when no class scope is active"); goto failure; } else if (UNEXPECTED(!scope->parent)) { zend_throw_error(NULL, "Cannot access parent:: when current class scope has no parent"); goto failure; } else { ce = scope->parent; } } else if (zend_string_equals_literal_ci(class_name, "static")) { ce = zend_get_called_scope(EG(current_execute_data)); if (UNEXPECTED(!ce)) { zend_throw_error(NULL, "Cannot access static:: when no class scope is active"); goto failure; } } else { ce = zend_fetch_class(class_name, flags); } if (ce) { zend_class_constant *c = zend_hash_find_ptr(&ce->constants_table, constant_name); if (c == NULL) { if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_throw_error(NULL, "Undefined class constant '%s::%s'", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); goto failure; } ret_constant = NULL; } else { if (!zend_verify_const_access(c, scope)) { zend_throw_error(NULL, "Cannot access %s const %s::%s", zend_visibility_string(Z_ACCESS_FLAGS(c->value)), ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); goto failure; } ret_constant = &c->value; } } if (ret_constant && Z_CONSTANT_P(ret_constant)) { if (Z_TYPE_P(ret_constant) == IS_CONSTANT_AST) { if (IS_CONSTANT_VISITED(ret_constant)) { zend_throw_error(NULL, "Cannot declare self-referencing constant '%s::%s'", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); ret_constant = NULL; goto failure; } MARK_CONSTANT_VISITED(ret_constant); } if (UNEXPECTED(zval_update_constant_ex(ret_constant, ce) != SUCCESS)) { RESET_CONSTANT_VISITED(ret_constant); ret_constant = NULL; goto failure; } RESET_CONSTANT_VISITED(ret_constant); } failure: zend_string_release(class_name); zend_string_free(constant_name); return ret_constant; } /* non-class constant */ if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) { /* compound constant name */ int prefix_len = colon - name; size_t const_name_len = name_len - prefix_len - 1; const char *constant_name = colon + 1; char *lcname; size_t lcname_len; ALLOCA_FLAG(use_heap) lcname_len = prefix_len + 1 + const_name_len; lcname = do_alloca(lcname_len + 1, use_heap); zend_str_tolower_copy(lcname, name, prefix_len); /* Check for namespace constant */ lcname[prefix_len] = '\\'; memcpy(lcname + prefix_len + 1, constant_name, const_name_len + 1); if ((c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len)) == NULL) { /* try lowercase */ zend_str_tolower(lcname + prefix_len + 1, const_name_len); if ((c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len)) != NULL) { if ((c->flags & CONST_CS) != 0) { c = NULL; } } } free_alloca(lcname, use_heap); if (c) { return &c->value; } /* name requires runtime resolution, need to check non-namespaced name */ if ((flags & IS_CONSTANT_UNQUALIFIED) != 0) { return zend_get_constant_str(constant_name, const_name_len); } return NULL; } if (cname) { return zend_get_constant(cname); } else { return zend_get_constant_str(name, name_len); } }
static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_block *ssa_blocks = ssa->blocks; zend_ssa_op *ssa_ops = ssa->ops; int ssa_vars_count = ssa->vars_count; int i, j; uint32_t k; zend_op *opline; int *tmp = NULL; ALLOCA_FLAG(use_heap); // FIXME: Can we optimize this copying out in some cases? if (blocks[n].next_child >= 0) { tmp = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), use_heap); memcpy(tmp, var, sizeof(int) * (op_array->last_var + op_array->T)); var = tmp; } if (ssa_blocks[n].phis) { zend_ssa_phi *phi = ssa_blocks[n].phis; do { if (phi->ssa_var < 0) { phi->ssa_var = ssa_vars_count; var[phi->var] = ssa_vars_count; ssa_vars_count++; } else { var[phi->var] = phi->ssa_var; } phi = phi->next; } while (phi); } for (k = blocks[n].start; k <= blocks[n].end; k++) { opline = op_array->opcodes + k; if (opline->opcode != ZEND_OP_DATA) { zend_op *next = opline + 1; if (k < blocks[n].end && next->opcode == ZEND_OP_DATA) { if (next->op1_type == IS_CV) { ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)]; //USE_SSA_VAR(next->op1.var); } else if (next->op1_type & (IS_VAR|IS_TMP_VAR)) { ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)]; //USE_SSA_VAR(op_array->last_var + next->op1.var); } if (next->op2_type == IS_CV) { ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)]; //USE_SSA_VAR(next->op2.var); } else if (next->op2_type & (IS_VAR|IS_TMP_VAR)) { ssa_ops[k + 1].op2_use = var[EX_VAR_TO_NUM(next->op2.var)]; //USE_SSA_VAR(op_array->last_var + next->op2.var); } } if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { ssa_ops[k].op1_use = var[EX_VAR_TO_NUM(opline->op1.var)]; //USE_SSA_VAR(op_array->last_var + opline->op1.var) } if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) { if (opline->op2_type == IS_CV) { ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)]; } ssa_ops[k].op2_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op2.var) } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { ssa_ops[k].op2_use = var[EX_VAR_TO_NUM(opline->op2.var)]; //USE_SSA_VAR(op_array->last_var + opline->op2.var) } switch (opline->opcode) { case ZEND_ASSIGN: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) { ssa_ops[k].op2_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op2.var) } break; case ZEND_ASSIGN_REF: //TODO: ??? if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } if (opline->op2_type == IS_CV) { ssa_ops[k].op2_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op2.var) } break; case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } break; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) { ssa_ops[k + 1].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(next->op1.var) } break; case ZEND_ADD_ARRAY_ELEMENT: ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)]; case ZEND_INIT_ARRAY: if (((build_flags & ZEND_SSA_RC_INFERENCE) || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) && opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline+->op1.var) } break; case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_EX: case ZEND_SEND_REF: case ZEND_FE_RESET_RW: //TODO: ??? if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } break; case ZEND_FE_RESET_R: if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } break; 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_ASSIGN_POW: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } break; case ZEND_UNSET_VAR: if (opline->extended_value & ZEND_QUICK_SET) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; } break; case ZEND_UNSET_DIM: case ZEND_UNSET_OBJ: case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_OBJ_UNSET: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->op1.var) } break; case ZEND_BIND_LEXICAL: if (opline->extended_value || (build_flags & ZEND_SSA_RC_INFERENCE)) { ssa_ops[k].op2_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op2.var)] = ssa_vars_count; ssa_vars_count++; } break; case ZEND_YIELD: if (opline->op1_type == IS_CV && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) || (build_flags & ZEND_SSA_RC_INFERENCE))) { ssa_ops[k].op1_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; } default: break; } if (opline->result_type == IS_CV) { ssa_ops[k].result_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(opline->result.var) } else if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { ssa_ops[k].result_def = ssa_vars_count; var[EX_VAR_TO_NUM(opline->result.var)] = ssa_vars_count; ssa_vars_count++; //NEW_SSA_VAR(op_array->last_var + opline->result.var) } } } for (i = 0; i < 2; i++) { int succ = blocks[n].successors[i]; if (succ >= 0) { zend_ssa_phi *p; for (p = ssa_blocks[succ].phis; p; p = p->next) { if (p->pi == n) { /* e-SSA Pi */ if (p->constraint.min_var >= 0) { p->constraint.min_ssa_var = var[p->constraint.min_var]; } if (p->constraint.max_var >= 0) { p->constraint.max_ssa_var = var[p->constraint.max_var]; } for (j = 0; j < blocks[succ].predecessors_count; j++) { p->sources[j] = var[p->var]; } if (p->ssa_var < 0) { p->ssa_var = ssa_vars_count; ssa_vars_count++; } } else if (p->pi < 0) { /* Normal Phi */ for (j = 0; j < blocks[succ].predecessors_count; j++) if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) { break; } ZEND_ASSERT(j < blocks[succ].predecessors_count); p->sources[j] = var[p->var]; } } for (p = ssa_blocks[succ].phis; p && (p->pi >= 0); p = p->next) { if (p->pi == n) { zend_ssa_phi *q = p->next; while (q) { if (q->pi < 0 && q->var == p->var) { for (j = 0; j < blocks[succ].predecessors_count; j++) { if (ssa->cfg.predecessors[blocks[succ].predecessor_offset + j] == n) { break; } } ZEND_ASSERT(j < blocks[succ].predecessors_count); q->sources[j] = p->ssa_var; } q = q->next; } } } } } ssa->vars_count = ssa_vars_count; j = blocks[n].children; while (j >= 0) { // FIXME: Tail call optimization? if (zend_ssa_rename(op_array, build_flags, ssa, var, j) != SUCCESS) return FAILURE; j = blocks[j].next_child; } if (tmp) { free_alloca(tmp, use_heap); } return SUCCESS; }
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *end, *opline; uint32_t new_count, i, shift; int j; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); i = new_count = shift = 0; end = op_array->opcodes + op_array->last; for (opline = op_array->opcodes; opline < end; opline++) { /* Kill JMP-over-NOP-s */ if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) { /* check if there are only NOPs under the branch */ zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 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) { zend_op *new_opline = op_array->opcodes + new_count; *new_opline = *opline; zend_optimizer_migrate_jump(op_array, new_opline, opline); } new_count++; } } if (shift) { op_array->last = new_count; end = op_array->opcodes + op_array->last; /* update JMPs */ for (opline = op_array->opcodes; opline<end; opline++) { zend_optimizer_shift_jump(op_array, opline, shiftlist); } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { uint32_t *opline_num = &ctx->script->first_early_binding_opline; ZEND_ASSERT(op_array == &ctx->script->main_op_array); do { *opline_num -= shiftlist[*opline_num]; opline_num = &op_array->opcodes[*opline_num].result.opline_num; } while (*opline_num != (uint32_t)-1); } } free_alloca(shiftlist, use_heap); }
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg) /* {{{ */ { int set_size; zend_basic_block *blocks = cfg->blocks; int blocks_count = cfg->blocks_count; zend_bitset tmp, gen, def, use, in, out; zend_op *opline; uint32_t k; int j; /* FIXME: can we use "gen" instead of "def" for flow analyzing? */ set_size = dfg->size; tmp = dfg->tmp; gen = dfg->gen; def = dfg->def; use = dfg->use; in = dfg->in; out = dfg->out; /* Collect "gen", "def" and "use" sets */ for (j = 0; j < blocks_count; j++) { if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } for (k = blocks[j].start; k <= blocks[j].end; k++) { opline = op_array->opcodes + k; if (opline->opcode != ZEND_OP_DATA) { zend_op *next = opline + 1; if (k < blocks[j].end && next->opcode == ZEND_OP_DATA) { if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(next->op1.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op1.var)); } } if (next->op2_type == IS_CV) { if (!DFG_ISSET(def, set_size, j,EX_VAR_TO_NUM(next->op2.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op2.var)); } } else if (next->op2_type == IS_VAR || next->op2_type == IS_TMP_VAR) { /* ZEND_ASSIGN_??? use the second operand of the following OP_DATA instruction as a temporary variable */ switch (opline->opcode) { case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: 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_ASSIGN_POW: break; default: if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(next->op2.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(next->op2.var)); } } } } if (opline->op1_type == IS_CV) { switch (opline->opcode) { case ZEND_ASSIGN: case ZEND_ASSIGN_REF: case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_SEND_VAR_EX: case ZEND_SEND_REF: case ZEND_SEND_VAR_NO_REF: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_ADD_ARRAY_ELEMENT: case ZEND_INIT_ARRAY: case ZEND_BIND_LEXICAL: if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) { // FIXME: include into "use" to ...? DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); break; case ZEND_UNSET_VAR: ZEND_ASSERT(opline->extended_value & ZEND_QUICK_SET); /* break missing intentionally */ 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_ASSIGN_POW: case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: case ZEND_UNSET_DIM: case ZEND_UNSET_OBJ: case ZEND_FETCH_DIM_W: case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_OBJ_UNSET: DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); default: if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); } } } else if (opline->op1_type == IS_VAR || opline->op1_type == IS_TMP_VAR) { if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op1.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op1.var)); } } if (opline->op2_type == IS_CV) { switch (opline->opcode) { case ZEND_ASSIGN: case ZEND_ASSIGN_REF: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) { // FIXME: include into "use" to ...? DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); break; default: if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); } break; } } else if (opline->op2_type == IS_VAR || opline->op2_type == IS_TMP_VAR) { if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) { if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) { DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); } else { if (!DFG_ISSET(def, set_size, j, EX_VAR_TO_NUM(opline->op2.var))) { DFG_SET(use, set_size, j, EX_VAR_TO_NUM(opline->op2.var)); } } } if (opline->result_type == IS_CV) { if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->result.var))) { DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->result.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->result.var)); } else if (opline->result_type == IS_VAR || opline->result_type == IS_TMP_VAR) { if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(opline->result.var))) { DFG_SET(def, set_size, j, EX_VAR_TO_NUM(opline->result.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(opline->result.var)); } if ((opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) && opline->result_type == IS_TMP_VAR) { if (!DFG_ISSET(use, set_size, j, EX_VAR_TO_NUM(next->result.var))) { DFG_SET(def, set_size, j, EX_VAR_TO_NUM(next->result.var)); } DFG_SET(gen, set_size, j, EX_VAR_TO_NUM(next->result.var)); } } } } /* Calculate "in" and "out" sets */ { uint32_t worklist_len = zend_bitset_len(blocks_count); ALLOCA_FLAG(use_heap); zend_bitset worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap); memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE); for (j = 0; j < blocks_count; j++) { zend_bitset_incl(worklist, j); } while (!zend_bitset_empty(worklist, worklist_len)) { /* We use the last block on the worklist, because predecessors tend to be located * before the succeeding block, so this converges faster. */ j = zend_bitset_last(worklist, worklist_len); zend_bitset_excl(worklist, j); if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } if (blocks[j].successors[0] >= 0) { zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size); if (blocks[j].successors[1] >= 0) { zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[1]), set_size); } } else { zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size); } zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size); if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) { zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size); /* Add predecessors of changed block to worklist */ { int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset]; for (k = 0; k < blocks[j].predecessors_count; k++) { zend_bitset_incl(worklist, predecessors[k]); } } } } free_alloca(worklist, use_heap); } return SUCCESS; }
/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs. * This pass does not operate on SSA form anymore. */ void zend_optimizer_compact_vars(zend_op_array *op_array) { int i; ALLOCA_FLAG(use_heap1); ALLOCA_FLAG(use_heap2); uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T); zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1); uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2); uint32_t num_cvs, num_tmps; /* Determine which CVs are used */ zend_bitset_clear(used_vars, used_vars_len); for (i = 0; i < op_array->last; i++) { zend_op *opline = &op_array->opcodes[i]; if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var)); } if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var)); } if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { zend_bitset_incl(used_vars, VAR_NUM(opline->result.var)); if (opline->opcode == ZEND_ROPE_INIT) { uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); while (num > 1) { num--; zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num); } } } } num_cvs = 0; for (i = 0; i < op_array->last_var; i++) { if (zend_bitset_in(used_vars, i)) { vars_map[i] = num_cvs++; } else { vars_map[i] = (uint32_t) -1; } } num_tmps = 0; for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) { if (zend_bitset_in(used_vars, i)) { vars_map[i] = num_cvs + num_tmps++; } else { vars_map[i] = (uint32_t) -1; } } free_alloca(used_vars, use_heap1); if (num_cvs == op_array->last_var && num_tmps == op_array->T) { free_alloca(vars_map, use_heap2); return; } ZEND_ASSERT(num_cvs <= op_array->last_var); ZEND_ASSERT(num_tmps <= op_array->T); /* Update CV and TMP references in opcodes */ for (i = 0; i < op_array->last; i++) { zend_op *opline = &op_array->opcodes[i]; if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]); } if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]); } if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]); } } /* Update TMP references in live ranges */ if (op_array->live_range) { for (i = 0; i < op_array->last_live_range; i++) { op_array->live_range[i].var = (op_array->live_range[i].var & ZEND_LIVE_MASK) | NUM_VAR(vars_map[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK)]); } } /* Update CV name table */ if (num_cvs != op_array->last_var) { zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0); for (i = 0; i < op_array->last_var; i++) { if (vars_map[i] != (uint32_t) -1) { names[vars_map[i]] = op_array->vars[i]; } else { zend_string_release(op_array->vars[i]); } } efree(op_array->vars); op_array->vars = names; } op_array->last_var = num_cvs; op_array->T = num_tmps; free_alloca(vars_map, use_heap2); }
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa) { zend_basic_block *blocks = ssa->cfg.blocks; zend_basic_block *end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; uint32_t i; uint32_t target = 0; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); memset(shiftlist, 0, sizeof(uint32_t) * op_array->last); for (b = blocks; b < end; b++) { if (b->flags & ZEND_BB_REACHABLE) { i = b->start; b->start = target; while (i <= b->end) { if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || /*keep NOP to support ZEND_VM_SMART_BRANCH */ (i > 0 && i + 1 < op_array->last && (op_array->opcodes[i+1].opcode == ZEND_JMPZ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && (op_array->opcodes[i-1].opcode == ZEND_IS_IDENTICAL || op_array->opcodes[i-1].opcode == ZEND_IS_NOT_IDENTICAL || op_array->opcodes[i-1].opcode == ZEND_IS_EQUAL || op_array->opcodes[i-1].opcode == ZEND_IS_NOT_EQUAL || op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER || op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER_OR_EQUAL || op_array->opcodes[i-1].opcode == ZEND_CASE || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_VAR || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ || op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ || op_array->opcodes[i-1].opcode == ZEND_INSTANCEOF || op_array->opcodes[i-1].opcode == ZEND_TYPE_CHECK || op_array->opcodes[i-1].opcode == ZEND_DEFINED))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; shiftlist[i] = i - target; } target++; } i++; } if (b->end != target - 1) { zend_op *opline; zend_op *new_opline; opline = op_array->opcodes + b->end; b->end = target - 1; new_opline = op_array->opcodes + b->end; switch (new_opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline)); break; case ZEND_JMPZNZ: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); /* break missing intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); break; case ZEND_CATCH: if (!opline->result.num) { new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); } break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); break; } } } } if (target != op_array->last) { /* reset rest opcodes */ for (i = target; i < op_array->last; i++) { MAKE_NOP(op_array->opcodes + i); } /* update SSA variables */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].definition >= 0) { ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition]; } if (ssa->vars[j].use_chain >= 0) { ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain]; } } for (i = 0; i < op_array->last; i++) { if (ssa->ops[i].op1_use_chain >= 0) { ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain]; } if (ssa->ops[i].op2_use_chain >= 0) { ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain]; } if (ssa->ops[i].res_use_chain >= 0) { ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain]; } } /* update branch targets */ for (b = blocks; b < end; b++) { if (b->flags & ZEND_BB_REACHABLE) { zend_op *opline = op_array->opcodes + b->end; switch (opline->opcode) { case ZEND_JMP: case ZEND_FAST_CALL: ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]); break; case ZEND_JMPZNZ: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); /* break missing intentionally */ case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]); break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: case ZEND_CATCH: opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); break; } } } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *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 != (uint32_t)-1); } /* update call graph */ func_info = ZEND_FUNC_INFO(op_array); if (func_info) { zend_call_info *call_info = func_info->callee_info; while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; call_info->caller_call_opline -= shiftlist[call_info->caller_call_opline - op_array->opcodes]; call_info = call_info->next_callee; } } op_array->last = target; } free_alloca(shiftlist, use_heap); }
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa) { zend_basic_block *blocks = ssa->cfg.blocks; zend_basic_block *end = blocks + ssa->cfg.blocks_count; zend_basic_block *b; zend_func_info *func_info; int j; uint32_t i; uint32_t target = 0; uint32_t *shiftlist; ALLOCA_FLAG(use_heap); shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap); memset(shiftlist, 0, sizeof(uint32_t) * op_array->last); for (b = blocks; b < end; b++) { if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { uint32_t end; if (b->flags & ZEND_BB_UNREACHABLE_FREE) { /* Only keep the FREE for the loop var */ ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE || op_array->opcodes[b->start].opcode == ZEND_FE_FREE); b->len = 1; } end = b->start + b->len; i = b->start; b->start = target; while (i < end) { shiftlist[i] = i - target; if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || /*keep NOP to support ZEND_VM_SMART_BRANCH */ (i > 0 && i + 1 < op_array->last && (op_array->opcodes[i+1].opcode == ZEND_JMPZ || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) && zend_is_smart_branch(op_array->opcodes + i - 1))) { if (i != target) { op_array->opcodes[target] = op_array->opcodes[i]; ssa->ops[target] = ssa->ops[i]; } target++; } i++; } if (target != end && b->len != 0) { zend_op *opline; zend_op *new_opline; b->len = target - b->start; opline = op_array->opcodes + end - 1; if (opline->opcode == ZEND_NOP) { continue; } new_opline = op_array->opcodes + target - 1; zend_optimizer_migrate_jump(op_array, new_opline, opline); } } } if (target != op_array->last) { /* reset rest opcodes */ for (i = target; i < op_array->last; i++) { MAKE_NOP(op_array->opcodes + i); } /* update SSA variables */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].definition >= 0) { ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition]; } if (ssa->vars[j].use_chain >= 0) { ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain]; } } for (i = 0; i < op_array->last; i++) { if (ssa->ops[i].op1_use_chain >= 0) { ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain]; } if (ssa->ops[i].op2_use_chain >= 0) { ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain]; } if (ssa->ops[i].res_use_chain >= 0) { ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain]; } } /* update branch targets */ for (b = blocks; b < end; b++) { if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) { zend_op *opline = op_array->opcodes + b->start + b->len - 1; zend_optimizer_shift_jump(op_array, opline, shiftlist); } } /* update brk/cont array */ for (j = 0; j < op_array->last_live_range; j++) { op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; } /* update try/catch array */ for (j = 0; j < op_array->last_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 (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]; } } /* update early binding list */ if (op_array->early_binding != (uint32_t)-1) { uint32_t *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 != (uint32_t)-1); } /* update call graph */ func_info = ZEND_FUNC_INFO(op_array); if (func_info) { zend_call_info *call_info = func_info->callee_info; while (call_info) { call_info->caller_init_opline -= shiftlist[call_info->caller_init_opline - op_array->opcodes]; call_info->caller_call_opline -= shiftlist[call_info->caller_call_opline - op_array->opcodes]; call_info = call_info->next_callee; } } op_array->last = target; } free_alloca(shiftlist, use_heap); }
int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_block *ssa_blocks; int blocks_count = ssa->cfg.blocks_count; uint32_t set_size; zend_bitset tmp, gen, in; int *var = NULL; int i, j, k, changed; zend_dfg dfg; ALLOCA_FLAG(dfg_use_heap); ALLOCA_FLAG(var_use_heap); ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS); ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block)); if (!ssa_blocks) { return FAILURE; } ssa->blocks = ssa_blocks; /* Compute Variable Liveness */ dfg.vars = op_array->last_var + op_array->T; dfg.size = set_size = zend_bitset_len(dfg.vars); dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1), dfg_use_heap); memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1)); dfg.gen = dfg.tmp + set_size; dfg.def = dfg.gen + set_size * blocks_count; dfg.use = dfg.def + set_size * blocks_count; dfg.in = dfg.use + set_size * blocks_count; dfg.out = dfg.in + set_size * blocks_count; if (zend_build_dfg(op_array, &ssa->cfg, &dfg, build_flags) != SUCCESS) { free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; } if (build_flags & ZEND_SSA_DEBUG_LIVENESS) { zend_dump_dfg(op_array, &ssa->cfg, &dfg); } tmp = dfg.tmp; gen = dfg.gen; in = dfg.in; /* SSA construction, Step 1: Propagate "gen" sets in merge points */ do { changed = 0; for (j = 0; j < blocks_count; j++) { if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } if (j >= 0 && (blocks[j].predecessors_count > 1 || j == 0)) { zend_bitset_copy(tmp, gen + (j * set_size), set_size); for (k = 0; k < blocks[j].predecessors_count; k++) { i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; while (i != -1 && i != blocks[j].idom) { zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size); i = blocks[i].idom; } } if (!zend_bitset_equal(gen + (j * set_size), tmp, set_size)) { zend_bitset_copy(gen + (j * set_size), tmp, set_size); changed = 1; } } } } while (changed); /* SSA construction, Step 2: Phi placement based on Dominance Frontiers */ var = do_alloca(sizeof(int) * (op_array->last_var + op_array->T), var_use_heap); if (!var) { free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; } zend_bitset_clear(tmp, set_size); for (j = 0; j < blocks_count; j++) { if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } if (blocks[j].predecessors_count > 1) { zend_bitset_clear(tmp, set_size); if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) { /* Prevent any values from flowing into irreducible loops by replacing all incoming values with explicit phis. The register allocator depends on this property. */ zend_bitset_copy(tmp, in + (j * set_size), set_size); } else { for (k = 0; k < blocks[j].predecessors_count; k++) { i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; while (i != -1 && i != blocks[j].idom) { zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size); i = blocks[i].idom; } } } if (!zend_bitset_empty(tmp, set_size)) { i = op_array->last_var + op_array->T; while (i > 0) { i--; if (zend_bitset_in(tmp, i)) { zend_ssa_phi *phi = zend_arena_calloc(arena, 1, sizeof(zend_ssa_phi) + sizeof(int) * blocks[j].predecessors_count + sizeof(void*) * blocks[j].predecessors_count); if (!phi) { goto failure; } phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi)); memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count); phi->pi = -1; phi->var = i; phi->ssa_var = -1; phi->next = ssa_blocks[j].phis; ssa_blocks[j].phis = phi; } } } } } place_essa_pis(arena, op_array, build_flags, ssa, &dfg); /* SSA construction, Step ?: Phi after Pi placement based on Dominance Frontiers */ for (j = 0; j < blocks_count; j++) { if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } if (blocks[j].predecessors_count > 1) { zend_bitset_clear(tmp, set_size); if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) { /* Prevent any values from flowing into irreducible loops by replacing all incoming values with explicit phis. The register allocator depends on this property. */ zend_bitset_copy(tmp, in + (j * set_size), set_size); } else { for (k = 0; k < blocks[j].predecessors_count; k++) { i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; while (i != -1 && i != blocks[j].idom) { zend_ssa_phi *p = ssa_blocks[i].phis; while (p) { if (p) { if (p->pi >= 0) { if (zend_bitset_in(in + (j * set_size), p->var) && !zend_bitset_in(gen + (i * set_size), p->var)) { zend_bitset_incl(tmp, p->var); } } else { zend_bitset_excl(tmp, p->var); } } p = p->next; } i = blocks[i].idom; } } } if (!zend_bitset_empty(tmp, set_size)) { i = op_array->last_var + op_array->T; while (i > 0) { i--; if (zend_bitset_in(tmp, i)) { zend_ssa_phi **pp = &ssa_blocks[j].phis; while (*pp) { if ((*pp)->pi <= 0 && (*pp)->var == i) { break; } pp = &(*pp)->next; } if (*pp == NULL) { zend_ssa_phi *phi = zend_arena_calloc(arena, 1, sizeof(zend_ssa_phi) + sizeof(int) * blocks[j].predecessors_count + sizeof(void*) * blocks[j].predecessors_count); if (!phi) { goto failure; } phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi)); memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count); phi->pi = -1; phi->var = i; phi->ssa_var = -1; phi->next = NULL; *pp = phi; } } } } } } if (build_flags & ZEND_SSA_DEBUG_PHI_PLACEMENT) { zend_dump_phi_placement(op_array, ssa); } /* SSA construction, Step 3: Renaming */ ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op)); memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op)); memset(var, 0xff, (op_array->last_var + op_array->T) * sizeof(int)); /* Create uninitialized SSA variables for each CV */ for (j = 0; j < op_array->last_var; j++) { var[j] = j; } ssa->vars_count = op_array->last_var; if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) { failure: free_alloca(var, var_use_heap); free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; } free_alloca(var, var_use_heap); free_alloca(dfg.tmp, dfg_use_heap); return SUCCESS; }
PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array) { char *p = NULL; char *ip = NULL; /* index pointer */ char *index; char *var, *var_orig; size_t var_len, index_len; zval gpc_element, *gpc_element_p; zend_bool is_array = 0; HashTable *symtable1 = NULL; ALLOCA_FLAG(use_heap) assert(var_name != NULL); if (track_vars_array && Z_TYPE_P(track_vars_array) == IS_ARRAY) { symtable1 = Z_ARRVAL_P(track_vars_array); } if (!symtable1) { /* Nothing to do */ zval_dtor(val); return; } /* ignore leading spaces in the variable name */ while (*var_name && *var_name==' ') { var_name++; } /* * Prepare variable name */ var_len = strlen(var_name); var = var_orig = do_alloca(var_len + 1, use_heap); memcpy(var_orig, var_name, var_len + 1); /* ensure that we don't have spaces or dots in the variable name (not binary safe) */ for (p = var; *p; p++) { if (*p == ' ' || *p == '.') { *p='_'; } else if (*p == '[') { is_array = 1; ip = p; *p = 0; break; } } var_len = p - var; if (var_len==0) { /* empty variable name, or variable name with a space in it */ zval_dtor(val); free_alloca(var_orig, use_heap); return; } /* GLOBALS hijack attempt, reject parameter */ if (symtable1 == &EG(symbol_table) && var_len == sizeof("GLOBALS")-1 && !memcmp(var, "GLOBALS", sizeof("GLOBALS")-1)) { zval_dtor(val); free_alloca(var_orig, use_heap); return; } index = var; index_len = var_len; if (is_array) { int nest_level = 0; while (1) { char *index_s; size_t new_idx_len = 0; if(++nest_level > PG(max_input_nesting_level)) { HashTable *ht; /* too many levels of nesting */ if (track_vars_array) { ht = Z_ARRVAL_P(track_vars_array); zend_symtable_str_del(ht, var, var_len); } zval_dtor(val); /* do not output the error message to the screen, this helps us to to avoid "information disclosure" */ if (!PG(display_errors)) { php_error_docref(NULL, E_WARNING, "Input variable nesting level exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_nesting_level in php.ini.", PG(max_input_nesting_level)); } free_alloca(var_orig, use_heap); return; } ip++; index_s = ip; if (isspace(*ip)) { ip++; } if (*ip==']') { index_s = NULL; } else { ip = strchr(ip, ']'); if (!ip) { /* PHP variables cannot contain '[' in their names, so we replace the character with a '_' */ *(index_s - 1) = '_'; index_len = 0; if (index) { index_len = strlen(index); } goto plain_var; return; } *ip = 0; new_idx_len = strlen(index_s); } if (!index) { array_init(&gpc_element); if ((gpc_element_p = zend_hash_next_index_insert(symtable1, &gpc_element)) == NULL) { zval_ptr_dtor(&gpc_element); zval_dtor(val); free_alloca(var_orig, use_heap); return; } } else { gpc_element_p = zend_symtable_str_find(symtable1, index, index_len); if (!gpc_element_p) { zval tmp; array_init(&tmp); gpc_element_p = zend_symtable_str_update_ind(symtable1, index, index_len, &tmp); } else { if (Z_TYPE_P(gpc_element_p) == IS_INDIRECT) { gpc_element_p = Z_INDIRECT_P(gpc_element_p); } if (Z_TYPE_P(gpc_element_p) != IS_ARRAY) { zval_ptr_dtor(gpc_element_p); array_init(gpc_element_p); } } } symtable1 = Z_ARRVAL_P(gpc_element_p); /* ip pointed to the '[' character, now obtain the key */ index = index_s; index_len = new_idx_len; ip++; if (*ip == '[') { is_array = 1; *ip = 0; } else { goto plain_var; } } } else { plain_var: ZVAL_COPY_VALUE(&gpc_element, val); if (!index) { if ((gpc_element_p = zend_hash_next_index_insert(symtable1, &gpc_element)) == NULL) { zval_ptr_dtor(&gpc_element); } } else { /* * According to rfc2965, more specific paths are listed above the less specific ones. * If we encounter a duplicate cookie name, we should skip it, since it is not possible * to have the same (plain text) cookie name for the same path and we should not overwrite * more specific cookies with the less specific ones. */ if (Z_TYPE(PG(http_globals)[TRACK_VARS_COOKIE]) != IS_UNDEF && symtable1 == Z_ARRVAL(PG(http_globals)[TRACK_VARS_COOKIE]) && zend_symtable_str_exists(symtable1, index, index_len)) { zval_ptr_dtor(&gpc_element); } else { gpc_element_p = zend_symtable_str_update_ind(symtable1, index, index_len, &gpc_element); } } } free_alloca(var_orig, use_heap); }