static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func) { if (func->type == ZEND_USER_FUNCTION && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS)) && fcall->extended_value >= func->op_array.required_num_args && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) { zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args; if (ret_opline->op1_type == IS_CONST) { uint32_t i, num_args = func->op_array.num_args; num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0; if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) { /* TODO: we can't inlne methods, because $this may be used * not in object context ??? */ return; } for (i = 0; i < num_args; i++) { /* Don't inline functions with by-reference arguments. This would require * correct handling of INDIRECT arguments. */ if (func->op_array.arg_info[i].pass_by_reference) { return; } } if (fcall->extended_value < func->op_array.num_args) { /* don't inline funcions with named constants in default arguments */ i = fcall->extended_value; do { if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) { return; } i++; } while (i < func->op_array.num_args); } if (RETURN_VALUE_USED(opline)) { zval zv; ZVAL_DUP(&zv, RT_CONSTANT(&func->op_array, ret_opline->op1)); opline->opcode = ZEND_QM_ASSIGN; opline->op1_type = IS_CONST; opline->op1.constant = zend_optimizer_add_literal(op_array, &zv); SET_UNUSED(opline->op2); } else { MAKE_NOP(opline); } zend_delete_call_instructions(opline-1); } } }
static inline char *phpdbg_decode_op( zend_op_array *ops, const znode_op *op, uint32_t type) /* {{{ */ { char *decode = NULL; switch (type) { case IS_CV: { zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)]; spprintf(&decode, 0, "$%.*s%c", ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18, ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+'); } break; case IS_VAR: spprintf(&decode, 0, "@%u", EX_VAR_TO_NUM(op->var) - ops->last_var); break; case IS_TMP_VAR: spprintf(&decode, 0, "~%u", EX_VAR_TO_NUM(op->var) - ops->last_var); break; case IS_CONST: { zval *literal = RT_CONSTANT(ops, *op); decode = phpdbg_short_zval_print(literal, 20); } break; } return decode; } /* }}} */
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */ { char *decode = NULL; switch (type &~ EXT_TYPE_UNUSED) { case IS_CV: { zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)]; asprintf(&decode, "$%.*s%c", ZSTR_LEN(var) <= 19 ? (int) ZSTR_LEN(var) : 18, ZSTR_VAL(var), ZSTR_LEN(var) <= 19 ? 0 : '+'); } break; case IS_VAR: case IS_TMP_VAR: { zend_ulong id = 0, *pid = NULL; if (vars != NULL) { if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) { id = *pid; } else { id = zend_hash_num_elements(vars); zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong)); } } asprintf(&decode, "@" ZEND_ULONG_FMT, id); } break; case IS_CONST: { zval *literal = RT_CONSTANT(ops, *op); decode = phpdbg_short_zval_print(literal, 20); } break; } return decode; } /* }}} */
static int coro_exit_handler(zend_execute_data *execute_data) { zval ex; zend_object *obj; zend_long flags = 0; if (sw_get_current_cid() != -1) { flags |= SW_EXIT_IN_COROUTINE; } if (SwooleG.serv && SwooleG.serv->gs->start) { flags |= SW_EXIT_IN_SERVER; } if (flags) { const zend_op *opline = EX(opline); zval _exit_status; zval *exit_status = NULL; if (opline->op1_type != IS_UNUSED) { if (opline->op1_type == IS_CONST) { // see: https://github.com/php/php-src/commit/e70618aff6f447a298605d07648f2ce9e5a284f5 #ifdef EX_CONSTANT exit_status = EX_CONSTANT(opline->op1); #else exit_status = RT_CONSTANT(opline, opline->op1); #endif } else { exit_status = EX_VAR(opline->op1.var); } if (Z_ISREF_P(exit_status)) { exit_status = Z_REFVAL_P(exit_status); } } else { exit_status = &_exit_status; ZVAL_NULL(exit_status); } obj = zend_throw_error_exception(swoole_exit_exception_class_entry_ptr, "swoole exit.", 0, E_ERROR TSRMLS_CC); ZVAL_OBJ(&ex, obj); zend_update_property_long(swoole_exit_exception_class_entry_ptr, &ex, ZEND_STRL("flags"), flags); Z_TRY_ADDREF_P(exit_status); zend_update_property(swoole_exit_exception_class_entry_ptr, &ex, ZEND_STRL("status"), exit_status); } return ZEND_USER_OPCODE_DISPATCH; }
static void zend_accel_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_function *func; zend_op *opline, *end; opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { if (opline->opcode == ZEND_INIT_FCALL) { func = zend_hash_find_ptr( &ctx->script->function_table, Z_STR_P(RT_CONSTANT(op_array, opline->op2))); if (func) { opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func); } } opline++; } }
static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type, HashTable *vars) /* {{{ */ { char *decode = NULL; switch (type &~ EXT_TYPE_UNUSED) { case IS_CV: { zend_string *var = ops->vars[EX_VAR_TO_NUM(op->var)]; asprintf(&decode, "$%.*s%c", var->len <= 19 ? (int) var->len : 18, var->val, var->len <= 19 ? 0 : '+'); } break; case IS_VAR: case IS_TMP_VAR: { zend_ulong id = 0, *pid = NULL; if (vars != NULL) { if ((pid = zend_hash_index_find_ptr(vars, (zend_ulong) ops->vars - op->var))) { id = *pid; } else { id = zend_hash_num_elements(vars); zend_hash_index_update_mem(vars, (zend_ulong) ops->vars - op->var, &id, sizeof(zend_ulong)); } } asprintf(&decode, "@" ZEND_ULONG_FMT, id); } break; case IS_CONST: { zval *literal = RT_CONSTANT(ops, *op); switch (Z_TYPE_P(literal)) { case IS_UNDEF: decode = zend_strndup("", 0); break; case IS_NULL: decode = zend_strndup(ZEND_STRL("null")); break; case IS_FALSE: decode = zend_strndup(ZEND_STRL("false")); break; case IS_TRUE: decode = zend_strndup(ZEND_STRL("true")); break; case IS_LONG: asprintf(&decode, "%lld", Z_LVAL_P(literal)); break; case IS_DOUBLE: asprintf(&decode, "%.*G", 14, Z_DVAL_P(literal)); break; case IS_STRING: { int i; zend_string *str = php_addcslashes(Z_STR_P(literal), 0, "\\\"", 2); for (i = 0; i < str->len; i++) { if (str->val[i] < 32) { str->val[i] = ' '; } } asprintf(&decode, "\"%.*s\"%c", str->len <= 18 ? (int) str->len : 17, str->val, str->len <= 18 ? 0 : '+'); zend_string_release(str); } break; case IS_RESOURCE: asprintf(&decode, "Rsrc #%d", Z_RES_HANDLE_P(literal)); break; case IS_ARRAY: asprintf(&decode, "array(%d)", zend_hash_num_elements(Z_ARR_P(literal))); break; case IS_OBJECT: { zend_string *str = Z_OBJCE_P(literal)->name; asprintf(&decode, "%.*s%c", str->len <= 18 ? (int) str->len : 18, str->val, str->len <= 18 ? 0 : '+'); } break; case IS_CONSTANT: decode = zend_strndup(ZEND_STRL("<constant>")); break; case IS_CONSTANT_AST: decode = zend_strndup(ZEND_STRL("<ast>")); break; default: asprintf(&decode, "unknown type: %d", Z_TYPE_P(literal)); break; } } break; case IS_UNUSED: asprintf(&decode, "<unused>"); break; } return decode; } /* }}} */
int zend_build_cfg(zend_arena **arena, zend_op_array *op_array, int rt_constants, int stackless, zend_cfg *cfg, uint32_t *func_flags) /* {{{ */ { uint32_t flags = 0; uint32_t i; int j; uint32_t *block_map; zend_function *fn; int blocks_count = 0; zend_basic_block *blocks; zval *zv; cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); if (!block_map) { return FAILURE; } /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */ BB_START(0); if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->static_variables) { // FIXME: Really we should try to perform variable initialization flags |= ZEND_FUNC_TOO_DYNAMIC; } for (i = 0; i < op_array->last; i++) { zend_op *opline = op_array->opcodes + i; switch(opline->opcode) { case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_INCLUDE_OR_EVAL: case ZEND_YIELD: case ZEND_YIELD_FROM: flags |= ZEND_FUNC_TOO_DYNAMIC; if (stackless) { BB_START(i + 1); } break; case ZEND_DO_FCALL: case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: flags |= ZEND_FUNC_HAS_CALLS; if (stackless) { BB_START(i + 1); } break; case ZEND_DO_ICALL: flags |= ZEND_FUNC_HAS_CALLS; break; case ZEND_INIT_FCALL: if (rt_constants) { zv = RT_CONSTANT(op_array, opline->op2); } else { zv = CT_CONSTANT_EX(op_array, opline->op2.constant); } if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) { if (fn->type == ZEND_INTERNAL_FUNCTION) { if (Z_STRLEN_P(zv) == sizeof("extract")-1 && memcmp(Z_STRVAL_P(zv), "extract", sizeof("extract")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("compact")-1 && memcmp(Z_STRVAL_P(zv), "compact", sizeof("compact")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("parse_str")-1 && memcmp(Z_STRVAL_P(zv), "parse_str", sizeof("parse_str")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("mb_parse_str")-1 && memcmp(Z_STRVAL_P(zv), "mb_parse_str", sizeof("mb_parse_str")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("get_defined_vars")-1 && memcmp(Z_STRVAL_P(zv), "get_defined_vars", sizeof("get_defined_vars")-1) == 0) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (Z_STRLEN_P(zv) == sizeof("func_num_args")-1 && memcmp(Z_STRVAL_P(zv), "func_num_args", sizeof("func_num_args")-1) == 0) { flags |= ZEND_FUNC_VARARG; } else if (Z_STRLEN_P(zv) == sizeof("func_get_arg")-1 && memcmp(Z_STRVAL_P(zv), "func_get_arg", sizeof("func_get_arg")-1) == 0) { flags |= ZEND_FUNC_VARARG; } else if (Z_STRLEN_P(zv) == sizeof("func_get_args")-1 && memcmp(Z_STRVAL_P(zv), "func_get_args", sizeof("func_get_args")-1) == 0) { flags |= ZEND_FUNC_VARARG; } } } break; case ZEND_FAST_CALL: flags |= ZEND_FUNC_TOO_DYNAMIC; BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); BB_START(i + 1); break; case ZEND_FAST_RET: flags |= ZEND_FUNC_TOO_DYNAMIC; if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); BB_START(i + 1); break; case ZEND_JMP: BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZNZ: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); if (i + 1 < op_array->last) { BB_START(i + 1); } break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_CATCH: flags |= ZEND_FUNC_TOO_DYNAMIC; if (!opline->result.num) { BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); } BB_START(i + 1); break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); BB_START(i + 1); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); BB_START(i + 1); break; case ZEND_DECLARE_LAMBDA_FUNCTION: { //??? zend_op_array *lambda_op_array; //??? //??? if (rt_constants) { //??? zv = RT_CONSTANT(op_array, opline->op1); //??? } else { //??? zv = CT_CONSTANT_EX(op_array, opline->op1.constant); //??? } //??? if (ctx->main_script && //??? (lambda_op_array = zend_hash_find_ptr(&ctx->main_script->function_table, Z_STR_P(zv))) != NULL) { //??? if (lambda_op_array->type == ZEND_USER_FUNCTION && //??? lambda_op_array->static_variables) { //??? // FIXME: Really we should try to perform alias //??? // analysis on variables used by the closure //??? info->flags |= ZEND_FUNC_TOO_DYNAMIC; //??? } //??? } else { //??? // FIXME: how to find the lambda function? flags |= ZEND_FUNC_TOO_DYNAMIC; //??? } } break; case ZEND_UNSET_VAR: if (!(opline->extended_value & ZEND_QUICK_SET)) { flags |= ZEND_FUNC_TOO_DYNAMIC; } break; case ZEND_FETCH_R: case ZEND_FETCH_W: case ZEND_FETCH_RW: case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_IS: case ZEND_FETCH_UNSET: if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) { flags |= ZEND_FUNC_TOO_DYNAMIC; } else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL || (opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) && !op_array->function_name) { flags |= ZEND_FUNC_TOO_DYNAMIC; } break; } } for (j = 0; j < op_array->last_live_range; j++) { BB_START(op_array->live_range[j].start); BB_START(op_array->live_range[j].end); } if (op_array->last_try_catch) { for (j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); if (op_array->try_catch_array[j].catch_op) { BB_START(op_array->try_catch_array[j].catch_op); } if (op_array->try_catch_array[j].finally_op) { BB_START(op_array->try_catch_array[j].finally_op); } if (op_array->try_catch_array[j].finally_end) { BB_START(op_array->try_catch_array[j].finally_end); } } } cfg->blocks_count = blocks_count; /* Build CFG, Step 2: Build Array of Basic Blocks */ cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count); if (!blocks) { return FAILURE; } for (i = 0, blocks_count = -1; i < op_array->last; i++) { if (block_map[i]) { if (blocks_count >= 0) { blocks[blocks_count].end = i - 1; } blocks_count++; blocks[blocks_count].flags = 0; blocks[blocks_count].start = i; blocks[blocks_count].successors[0] = -1; blocks[blocks_count].successors[1] = -1; blocks[blocks_count].predecessors_count = 0; blocks[blocks_count].predecessor_offset = -1; blocks[blocks_count].idom = -1; blocks[blocks_count].loop_header = -1; blocks[blocks_count].level = -1; blocks[blocks_count].children = -1; blocks[blocks_count].next_child = -1; block_map[i] = blocks_count; } else { block_map[i] = (uint32_t)-1; } } blocks[blocks_count].end = i - 1; blocks_count++; /* Build CFG, Step 3: Calculate successors */ for (j = 0; j < blocks_count; j++) { zend_op *opline = op_array->opcodes + blocks[j].end; switch(opline->opcode) { case ZEND_FAST_RET: case ZEND_RETURN: case ZEND_RETURN_BY_REF: case ZEND_GENERATOR_RETURN: case ZEND_EXIT: case ZEND_THROW: break; case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_ANON_INHERITED_CLASS: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_JMP: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); break; case ZEND_JMPZNZ: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_ASSERT_CHECK: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_CATCH: if (!opline->result.num) { record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); record_successor(blocks, j, 1, j + 1); } else { record_successor(blocks, j, 0, j + 1); } break; case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]); record_successor(blocks, j, 1, j + 1); break; case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: case ZEND_NEW: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; case ZEND_FAST_CALL: record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]); record_successor(blocks, j, 1, j + 1); break; default: record_successor(blocks, j, 0, j + 1); break; } } /* Build CFG, Step 4, Mark Reachable Basic Blocks */ zend_mark_reachable_blocks(op_array, cfg, 0); if (func_flags) { *func_flags = flags; } return SUCCESS; }