diff options
| author | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-26 15:53:49 +0100 |
|---|---|---|
| committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-01-28 10:38:25 +0100 |
| commit | 83be073abe665a219041ca1f54b6578b75643971 (patch) | |
| tree | dd92e86fc1f77033c5afa860e6dfb261b02c3abd /ext | |
| parent | ac561f2c255e4786db4f0726d47f0e84100cd2ea (diff) | |
| download | php-git-83be073abe665a219041ca1f54b6578b75643971.tar.gz | |
Move optimizer into core
This only moves the files, adjusts the build system, exports APIs
and does minor fixups to make sure the code builds.
This does not yet try to make the optimizer usable independently
of opcache.
Closes GH-6642.
Diffstat (limited to 'ext')
36 files changed, 0 insertions, 23571 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c deleted file mode 100644 index e833ff3dbf..0000000000 --- a/ext/opcache/Optimizer/block_pass.c +++ /dev/null @@ -1,1937 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" -#include "zend_bitset.h" -#include "zend_cfg.h" -#include "zend_dump.h" - -/* Checks if a constant (like "true") may be replaced by its value */ -int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy) -{ - zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name); - if (c) { - if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) - && !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) - && (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE) - || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) { - ZVAL_COPY_VALUE(result, &c->value); - if (copy) { - Z_TRY_ADDREF_P(result); - } - return 1; - } else { - return 0; - } - } - - /* Special constants null/true/false can always be substituted. */ - c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)); - if (c) { - ZVAL_COPY_VALUE(result, &c->value); - return 1; - } - return 0; -} - -/* Data dependencies macros */ - -#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)] -#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline - -static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b) -{ - zend_op *opcodes = op_array->opcodes; - - do { - b->start++; - b->len--; - } while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP); -} - -static void strip_nops(zend_op_array *op_array, zend_basic_block *b) -{ - uint32_t i, j; - - if (b->len == 0) { - return; - } - - if (op_array->opcodes[b->start].opcode == ZEND_NOP) { - strip_leading_nops(op_array, b); - } - - if (b->len == 0) { - return; - } - - /* strip the inside NOPs */ - i = j = b->start + 1; - while (i < b->start + b->len) { - if (op_array->opcodes[i].opcode != ZEND_NOP) { - if (i != j) { - op_array->opcodes[j] = op_array->opcodes[i]; - } - j++; - } - i++; - } - b->len = j - b->start; - while (j < i) { - MAKE_NOP(op_array->opcodes + j); - j++; - } -} - -static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) { - HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); - zval *zv; - if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG) - || (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) { - /* fallback to next block */ - return block->successors[block->successors_count - 1]; - } - if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) { - /* always jump to the default arm */ - return block->successors[block->successors_count - 1]; - } - if (Z_TYPE_P(val) == IS_LONG) { - zv = zend_hash_index_find(jumptable, Z_LVAL_P(val)); - } else { - ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING); - zv = zend_hash_find(jumptable, Z_STR_P(val)); - } - if (!zv) { - /* default */ - return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)]; - } - return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]; -} - -static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count) -{ - zend_op *opline, *src; - zend_op *end, *last_op = NULL; - - if (block->len == 0) { - return; - } - - if (op_array->opcodes[block->start].opcode == ZEND_NOP) { - /* remove leading NOPs */ - strip_leading_nops(op_array, block); - } - - opline = op_array->opcodes + block->start; - end = opline + block->len; - while (opline < end) { - /* Constant Propagation: strip X = QM_ASSIGN(const) */ - if (opline->op1_type == IS_TMP_VAR && - opline->opcode != ZEND_FREE) { - src = VAR_SOURCE(opline->op1); - if (src && - src->opcode == ZEND_QM_ASSIGN && - src->op1_type == IS_CONST - ) { - znode_op op1 = opline->op1; - if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) { - COPY_NODE(opline->result, opline->op1); - COPY_NODE(opline->op1, src->op1); - VAR_SOURCE(op1) = NULL; - MAKE_NOP(src); - ++(*opt_count); - } else { - zval c; - ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); - if (zend_optimizer_update_op1_const(op_array, opline, &c)) { - VAR_SOURCE(op1) = NULL; - literal_dtor(&ZEND_OP1_LITERAL(src)); - MAKE_NOP(src); - ++(*opt_count); - } else { - zval_ptr_dtor_nogc(&c); - } - } - } - } - - /* Constant Propagation: strip X = QM_ASSIGN(const) */ - if (opline->op2_type == IS_TMP_VAR) { - src = VAR_SOURCE(opline->op2); - if (src && - src->opcode == ZEND_QM_ASSIGN && - src->op1_type == IS_CONST) { - - znode_op op2 = opline->op2; - zval c; - - ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); - if (zend_optimizer_update_op2_const(op_array, opline, &c)) { - VAR_SOURCE(op2) = NULL; - literal_dtor(&ZEND_OP1_LITERAL(src)); - MAKE_NOP(src); - ++(*opt_count); - } else { - zval_ptr_dtor_nogc(&c); - } - } - } - - switch (opline->opcode) { - case ZEND_ECHO: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - src = VAR_SOURCE(opline->op1); - if (src && - src->opcode == ZEND_CAST && - src->extended_value == IS_STRING) { - /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - } - } else if (opline->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) { - if (last_op == opline - 1) { - /* compress consecutive ECHO's. - * Float to string conversion may be affected by current - * locale setting. - */ - int l, old_len; - - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP1_LITERAL(opline)); - } - if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) { - convert_to_string(&ZEND_OP1_LITERAL(last_op)); - } - old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op)); - l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline)); - if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) { - zend_string *tmp = zend_string_alloc(l, 0); - memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len); - Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp; - } else { - Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0); - } - Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX; - memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline))); - Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0'; - zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); - ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op)))); - ZVAL_NULL(&ZEND_OP1_LITERAL(last_op)); - MAKE_NOP(last_op); - ++(*opt_count); - } - last_op = opline; - } - break; - - case ZEND_FREE: - if (opline->op1_type == IS_TMP_VAR) { - src = VAR_SOURCE(opline->op1); - if (src) { - switch (src->opcode) { - case ZEND_BOOL: - case ZEND_BOOL_NOT: - /* T = BOOL(X), FREE(T) => T = BOOL(X) */ - /* The remaining BOOL is removed by a separate optimization */ - VAR_SOURCE(opline->op1) = NULL; - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_ASSIGN: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_PRE_INC_STATIC_PROP: - case ZEND_PRE_DEC_STATIC_PROP: - src->result_type = IS_UNUSED; - VAR_SOURCE(opline->op1) = NULL; - MAKE_NOP(opline); - ++(*opt_count); - break; - default: - break; - } - } - } else if (opline->op1_type == IS_VAR) { - src = VAR_SOURCE(opline->op1); - /* V = OP, FREE(V) => OP. NOP */ - if (src && - src->opcode != ZEND_FETCH_R && - src->opcode != ZEND_FETCH_STATIC_PROP_R && - src->opcode != ZEND_FETCH_DIM_R && - src->opcode != ZEND_FETCH_OBJ_R && - src->opcode != ZEND_NEW) { - src->result_type = IS_UNUSED; - MAKE_NOP(opline); - ++(*opt_count); - if (src->opcode == ZEND_QM_ASSIGN) { - if (src->op1_type & (IS_VAR|IS_TMP_VAR)) { - src->opcode = ZEND_FREE; - } else { - MAKE_NOP(src); - } - } - } - } - break; - -#if 0 - /* pre-evaluate functions: - constant(x) - function_exists(x) - extension_loaded(x) - BAD: interacts badly with Accelerator - */ - if((opline->op1_type & IS_VAR) && - VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL && - VAR_SOURCE(opline->op1)->extended_value == 1) { - zend_op *fcall = VAR_SOURCE(opline->op1); - zend_op *sv = fcall-1; - if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL && - sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING && - Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1 - ) { - zval *arg = &OPLINE_OP1_LITERAL(sv); - char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name; - int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len; - if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) || - (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0) - ) { - zend_function *function; - if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - LITERAL_BOOL(opline->op1, 1); - opline->op1_type = IS_CONST; - } - } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) { - zval c; - if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c); - /* no copy ctor - get already copied it */ - opline->op1_type = IS_CONST; - } - } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) { - if(zend_hash_exists(&module_registry, Z_STR_P(arg))) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - LITERAL_BOOL(opline->op1, 1); - opline->op1_type = IS_CONST; - } - } - } - } -#endif - - case ZEND_FETCH_LIST_R: - case ZEND_FETCH_LIST_W: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - /* LIST variable will be deleted later by FREE */ - Tsource[VAR_NUM(opline->op1.var)] = NULL; - } - break; - - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - /* SWITCH variable will be deleted later by FREE, so we can't optimize it */ - Tsource[VAR_NUM(opline->op1.var)] = NULL; - break; - } - if (opline->op1_type == IS_CONST) { - int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP2_LITERAL(opline)); - opline->opcode = ZEND_JMP; - opline->op1_type = IS_UNUSED; - opline->op2_type = IS_UNUSED; - block->successors_count = 1; - block->successors[0] = target; - } - break; - - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_COPY_TMP: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - /* Variable will be deleted later by FREE, so we can't optimize it */ - Tsource[VAR_NUM(opline->op1.var)] = NULL; - break; - } - /* break missing intentionally */ - - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - if (opline->op1_type == IS_CONST && - opline->op2_type == IS_CONST) { - goto optimize_constant_binary_op; - } - /* IS_EQ(TRUE, X) => BOOL(X) - * IS_EQ(FALSE, X) => BOOL_NOT(X) - * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) - * IS_NOT_EQ(FALSE, X) => BOOL(X) - * CASE(TRUE, X) => BOOL(X) - * CASE(FALSE, X) => BOOL_NOT(X) - */ - if (opline->op1_type == IS_CONST && - (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE || - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) { - /* Optimization of comparison with "null" is not safe, - * because ("0" == null) is not equal to !("0") - */ - opline->opcode = - ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ? - ZEND_BOOL : ZEND_BOOL_NOT; - COPY_NODE(opline->op1, opline->op2); - SET_UNUSED(opline->op2); - ++(*opt_count); - goto optimize_bool; - } else if (opline->op2_type == IS_CONST && - (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE || - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) { - /* Optimization of comparison with "null" is not safe, - * because ("0" == null) is not equal to !("0") - */ - opline->opcode = - ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ? - ZEND_BOOL : ZEND_BOOL_NOT; - SET_UNUSED(opline->op2); - ++(*opt_count); - goto optimize_bool; - } - break; - - case ZEND_BOOL: - case ZEND_BOOL_NOT: - optimize_bool: - if (opline->op1_type == IS_CONST) { - goto optimize_const_unary_op; - } - if (opline->op1_type == IS_TMP_VAR && - !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { - src = VAR_SOURCE(opline->op1); - if (src) { - switch (src->opcode) { - case ZEND_BOOL_NOT: - /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */ - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL; - MAKE_NOP(src); - ++(*opt_count); - goto optimize_bool; - case ZEND_BOOL: - /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */ - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - goto optimize_bool; - case ZEND_IS_EQUAL: - if (opline->opcode == ZEND_BOOL_NOT) { - src->opcode = ZEND_IS_NOT_EQUAL; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_IS_NOT_EQUAL: - if (opline->opcode == ZEND_BOOL_NOT) { - src->opcode = ZEND_IS_EQUAL; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_IS_IDENTICAL: - if (opline->opcode == ZEND_BOOL_NOT) { - src->opcode = ZEND_IS_NOT_IDENTICAL; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_IS_NOT_IDENTICAL: - if (opline->opcode == ZEND_BOOL_NOT) { - src->opcode = ZEND_IS_IDENTICAL; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_IS_SMALLER: - if (opline->opcode == ZEND_BOOL_NOT) { - zend_uchar tmp_type; - uint32_t tmp; - - src->opcode = ZEND_IS_SMALLER_OR_EQUAL; - tmp_type = src->op1_type; - src->op1_type = src->op2_type; - src->op2_type = tmp_type; - tmp = src->op1.num; - src->op1.num = src->op2.num; - src->op2.num = tmp; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_IS_SMALLER_OR_EQUAL: - if (opline->opcode == ZEND_BOOL_NOT) { - zend_uchar tmp_type; - uint32_t tmp; - - src->opcode = ZEND_IS_SMALLER; - tmp_type = src->op1_type; - src->op1_type = src->op2_type; - src->op2_type = tmp_type; - tmp = src->op1.num; - src->op1.num = src->op2.num; - src->op2.num = tmp; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_INSTANCEOF: - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - case ZEND_IN_ARRAY: - case ZEND_ARRAY_KEY_EXISTS: - if (opline->opcode == ZEND_BOOL_NOT) { - break; - } - COPY_NODE(src->result, opline->result); - SET_VAR_SOURCE(src); - MAKE_NOP(opline); - ++(*opt_count); - break; - } - } - } - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - while (1) { - if (opline->op1_type == IS_CONST) { - ++(*opt_count); - block->successors_count = 1; - if (zend_is_true(&ZEND_OP1_LITERAL(opline)) == - (opline->opcode == ZEND_JMPZ)) { - - MAKE_NOP(opline); - block->successors[0] = block->successors[1]; - block->len--; - cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW; - break; - } else { - zend_basic_block *next = cfg->blocks + block->successors[1]; - - next->flags &= ~ZEND_BB_FOLLOW; - if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) { - next->flags &= ~ZEND_BB_REACHABLE; - } - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - break; - } - } else if (opline->op1_type == IS_TMP_VAR && - !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { - src = VAR_SOURCE(opline->op1); - if (src) { - if (src->opcode == ZEND_BOOL_NOT) { - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */ - opline->opcode = INV_COND(opline->opcode); - MAKE_NOP(src); - ++(*opt_count); - continue; - } else if (src->opcode == ZEND_BOOL || - src->opcode == ZEND_QM_ASSIGN) { - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - continue; - } - } - } - break; - } - break; - - case ZEND_JMPZNZ: - while (1) { - if (opline->op1_type == IS_CONST) { - ++(*opt_count); - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - block->successors[0] = block->successors[1]; - } else { - zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - } - block->successors_count = 1; - opline->op1_type = IS_UNUSED; - opline->extended_value = 0; - opline->opcode = ZEND_JMP; - break; - } else if (opline->op1_type == IS_TMP_VAR && - !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { - src = VAR_SOURCE(opline->op1); - if (src) { - if (src->opcode == ZEND_BOOL_NOT) { - /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */ - uint32_t tmp; - - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - tmp = block->successors[0]; - block->successors[0] = block->successors[1]; - block->successors[1] = tmp; - MAKE_NOP(src); - ++(*opt_count); - continue; - } else if (src->opcode == ZEND_BOOL || - src->opcode == ZEND_QM_ASSIGN) { - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - continue; - } - } - } - break; - } - break; - - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - while (1) { - if (opline->op1_type == IS_CONST) { - if (zend_is_true(&ZEND_OP1_LITERAL(opline)) == - (opline->opcode == ZEND_JMPZ_EX)) { - - ++(*opt_count); - opline->opcode = ZEND_QM_ASSIGN; - zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); - ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), opline->opcode == ZEND_JMPZ_EX); - opline->op2.num = 0; - block->successors_count = 1; - block->successors[0] = block->successors[1]; - cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW; - break; - } - } else if (opline->op1_type == IS_TMP_VAR && - (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) || - opline->result.var == opline->op1.var)) { - src = VAR_SOURCE(opline->op1); - if (src) { - if (src->opcode == ZEND_BOOL || - src->opcode == ZEND_QM_ASSIGN) { - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - continue; - } - } - } - break; - } - break; - - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - if (opline->op1_type == IS_CONST && - opline->op2_type == IS_CONST) { - goto optimize_constant_binary_op; - } - - if (opline->op2_type == IS_CONST && - opline->op1_type == IS_TMP_VAR) { - - src = VAR_SOURCE(opline->op1); - if (src && - (src->opcode == ZEND_CONCAT || - src->opcode == ZEND_FAST_CONCAT) && - src->op2_type == IS_CONST) { - /* compress consecutive CONCATs */ - int l, old_len; - - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(src)); - } - - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - old_len = Z_STRLEN(ZEND_OP2_LITERAL(src)); - l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline)); - if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) { - zend_string *tmp = zend_string_alloc(l, 0); - memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len); - Z_STR(ZEND_OP2_LITERAL(src)) = tmp; - } else { - Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0); - } - Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX; - memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); - Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0'; - zval_ptr_dtor_str(&ZEND_OP2_LITERAL(opline)); - ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src)))); - ZVAL_NULL(&ZEND_OP2_LITERAL(src)); - MAKE_NOP(src); - ++(*opt_count); - } - } - - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - src = VAR_SOURCE(opline->op1); - if (src && - src->opcode == ZEND_CAST && - src->extended_value == IS_STRING && - src->op1_type != IS_CONST) { - /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - } - } - if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { - src = VAR_SOURCE(opline->op2); - if (src && - src->opcode == ZEND_CAST && - src->extended_value == IS_STRING && - src->op1_type != IS_CONST) { - /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */ - zend_op *src = VAR_SOURCE(opline->op2); - VAR_SOURCE(opline->op2) = NULL; - COPY_NODE(opline->op2, src->op1); - MAKE_NOP(src); - ++(*opt_count); - } - } - if (opline->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && - Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) { - /* convert CONCAT('', X) => CAST(STRING, X) */ - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_CAST; - opline->extended_value = IS_STRING; - COPY_NODE(opline->op1, opline->op2); - opline->op2_type = IS_UNUSED; - opline->op2.var = 0; - ++(*opt_count); - } else if (opline->op2_type == IS_CONST && - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && - Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) { - /* convert CONCAT(X, '') => CAST(STRING, X) */ - literal_dtor(&ZEND_OP2_LITERAL(opline)); - opline->opcode = ZEND_CAST; - opline->extended_value = IS_STRING; - opline->op2_type = IS_UNUSED; - opline->op2.var = 0; - ++(*opt_count); - } else if (opline->opcode == ZEND_CONCAT && - (opline->op1_type == IS_CONST || - (opline->op1_type == IS_TMP_VAR && - VAR_SOURCE(opline->op1) && - (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT || - VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END || - VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT || - VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) && - (opline->op2_type == IS_CONST || - (opline->op2_type == IS_TMP_VAR && - VAR_SOURCE(opline->op2) && - (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT || - VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END || - VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT || - VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) { - opline->opcode = ZEND_FAST_CONCAT; - ++(*opt_count); - } - break; - - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_SL: - case ZEND_SR: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_BOOL_XOR: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - if (opline->op1_type == IS_CONST && - opline->op2_type == IS_CONST) { - /* evaluate constant expressions */ - zval result; - -optimize_constant_binary_op: - if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP2_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &result); - ++(*opt_count); - } - } - break; - - case ZEND_BW_NOT: - if (opline->op1_type == IS_CONST) { - /* evaluate constant unary ops */ - zval result; - -optimize_const_unary_op: - if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &result); - ++(*opt_count); - } - } - break; - - case ZEND_CAST: - if (opline->op1_type == IS_CONST) { - /* cast of constant operand */ - zval result; - - if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - zend_optimizer_update_op1_const(op_array, opline, &result); - ++(*opt_count); - } - } - break; - - case ZEND_STRLEN: - if (opline->op1_type == IS_CONST) { - zval result; - - if (zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &result); - ++(*opt_count); - } - } - break; - - case ZEND_RETURN: - case ZEND_EXIT: - if (opline->op1_type == IS_TMP_VAR) { - src = VAR_SOURCE(opline->op1); - if (src && src->opcode == ZEND_QM_ASSIGN) { - zend_op *op = src + 1; - bool optimize = 1; - - while (op < opline) { - if ((op->op1_type == opline->op1_type - && op->op1.var == opline->op1.var) - || (op->op2_type == opline->op1_type - && op->op2.var == opline->op1.var)) { - optimize = 0; - break; - } - op++; - } - - if (optimize) { - /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */ - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - MAKE_NOP(src); - ++(*opt_count); - } - } - } - break; - - case ZEND_QM_ASSIGN: - if (opline->op1_type == opline->result_type && - opline->op1.var == opline->result.var) { - /* strip T = QM_ASSIGN(T) */ - MAKE_NOP(opline); - ++(*opt_count); - } else if (opline->op1_type == IS_TMP_VAR && - opline->result_type == IS_TMP_VAR && - !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) { - /* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */ - src = VAR_SOURCE(opline->op1); - if (src && - src->opcode != ZEND_COPY_TMP && - src->opcode != ZEND_ADD_ARRAY_ELEMENT && - src->opcode != ZEND_ADD_ARRAY_UNPACK && - (src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION || - src == opline -1)) { - src->result.var = opline->result.var; - VAR_SOURCE(opline->op1) = NULL; - VAR_SOURCE(opline->result) = src; - MAKE_NOP(opline); - ++(*opt_count); - } - } - break; - } - - /* get variable source */ - if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { - SET_VAR_SOURCE(opline); - } - opline++; - } -} - -/* Rebuild plain (optimized) op_array from CFG */ -static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_basic_block *blocks = cfg->blocks; - zend_basic_block *end = blocks + cfg->blocks_count; - zend_basic_block *b; - zend_op *new_opcodes; - zend_op *opline; - uint32_t len = 0; - int n; - - for (b = blocks; b < end; b++) { - if (b->len == 0) { - continue; - } - if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { - 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); - len += b->len = 1; - continue; - } - - opline = op_array->opcodes + b->start + b->len - 1; - if (opline->opcode == ZEND_JMP) { - zend_basic_block *next = b + 1; - - while (next < end && !(next->flags & ZEND_BB_REACHABLE)) { - next++; - } - if (next < end && next == blocks + b->successors[0]) { - /* JMP to the next block - strip it */ - MAKE_NOP(opline); - b->len--; - } - } else if (b->len == 1 && opline->opcode == ZEND_NOP) { - /* skip empty block */ - b->len--; - } - len += b->len; - } else { - /* this block will not be used, delete all constants there */ - zend_op *op = op_array->opcodes + b->start; - zend_op *end = op + b->len; - for (; op < end; op++) { - if (op->op1_type == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(op)); - } - if (op->op2_type == IS_CONST) { - literal_dtor(&ZEND_OP2_LITERAL(op)); - } - } - } - } - - new_opcodes = emalloc(len * sizeof(zend_op)); - opline = new_opcodes; - - /* Copy code of reachable blocks into a single buffer */ - for (b = blocks; b < end; b++) { - if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { - memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op)); - b->start = opline - new_opcodes; - opline += b->len; - } - } - - /* adjust jump targets */ - efree(op_array->opcodes); - op_array->opcodes = new_opcodes; - op_array->last = len; - - for (b = blocks; b < end; b++) { - if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) { - continue; - } - opline = op_array->opcodes + b->start + b->len - 1; - switch (opline->opcode) { - case ZEND_FAST_CALL: - case ZEND_JMP: - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start); - break; - case ZEND_JMPZNZ: - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start); - /* 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: - case ZEND_JMP_NULL: - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start); - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start); - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start); - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); - zval *zv; - uint32_t s = 0; - ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable)); - - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start); - } ZEND_HASH_FOREACH_END(); - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start); - break; - } - } - } - - /* adjust exception jump targets & remove unused try_catch_array entries */ - if (op_array->last_try_catch) { - int i, j; - uint32_t *map; - ALLOCA_FLAG(use_heap); - - map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap); - for (i = 0, j = 0; i< op_array->last_try_catch; i++) { - if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) { - map[i] = j; - op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start; - if (op_array->try_catch_array[i].catch_op) { - op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start; - } else { - op_array->try_catch_array[j].catch_op = 0; - } - if (op_array->try_catch_array[i].finally_op) { - op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start; - } else { - op_array->try_catch_array[j].finally_op = 0; - } - if (!op_array->try_catch_array[i].finally_end) { - op_array->try_catch_array[j].finally_end = 0; - } else { - op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start; - } - j++; - } - } - if (i != j) { - op_array->last_try_catch = j; - if (j == 0) { - efree(op_array->try_catch_array); - op_array->try_catch_array = NULL; - } - - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - zend_op *opline = new_opcodes; - zend_op *end = opline + len; - while (opline < end) { - if (opline->opcode == ZEND_FAST_RET && - opline->op2.num != (uint32_t)-1 && - opline->op2.num < (uint32_t)j) { - opline->op2.num = map[opline->op2.num]; - } - opline++; - } - } - } - free_alloca(map, use_heap); - } - - /* adjust early binding list */ - if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { - ZEND_ASSERT(op_array == &ctx->script->main_op_array); - ctx->script->first_early_binding_opline = - zend_build_delayed_early_binding_list(op_array); - } - - /* rebuild map (just for printing) */ - memset(cfg->map, -1, sizeof(int) * op_array->last); - for (n = 0; n < cfg->blocks_count; n++) { - if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { - cfg->map[cfg->blocks[n].start] = n; - } - } -} - -static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count) -{ - int b; - zend_basic_block *target_block = cfg->blocks + block->successors[n]; - - if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) { - do { - b = target_block->successors[0]; - target_block = cfg->blocks + b; - } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)); - block->successors[n] = b; - ++(*opt_count); - } - return target_block; -} - -static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, zend_basic_block *block, int n, uint32_t *opt_count) -{ - int b; - zend_basic_block *target_block = cfg->blocks + block->successors[n]; - - if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) { - do { - b = target_block->successors[0]; - target_block = cfg->blocks + b; - } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)); - block->successors[n] = b; - ++(*opt_count); - } - return target_block; -} - -static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block) -{ - zend_basic_block *next_block = block + 1; - zend_basic_block *end = cfg->blocks + cfg->blocks_count; - - while (1) { - if (next_block == end) { - return NULL; - } else if (next_block->flags & ZEND_BB_REACHABLE) { - break; - } - next_block++; - } - while (next_block->len == 0 && !(next_block->flags & ZEND_BB_PROTECTED)) { - next_block = cfg->blocks + next_block->successors[0]; - } - return next_block; -} - - -/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ -static zend_always_inline int in_hitlist(int target, int *jmp_hitlist, int jmp_hitlist_count) -{ - int i; - - for (i = 0; i < jmp_hitlist_count; i++) { - if (jmp_hitlist[i] == target) { - return 1; - } - } - return 0; -} - -#define CHECK_LOOP(target) \ - if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \ - jmp_hitlist[jmp_hitlist_count++] = target; \ - } else { \ - break; \ - } - -static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count) -{ - /* last_op is the last opcode of the current block */ - zend_basic_block *target_block, *follow_block, *next_block; - zend_op *last_op, *target; - int next, jmp_hitlist_count; - - if (block->len == 0) { - return; - } - - last_op = op_array->opcodes + block->start + block->len - 1; - switch (last_op->opcode) { - case ZEND_JMP: - jmp_hitlist_count = 0; - - target_block = get_target_block(cfg, block, 0, opt_count); - while (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - if (target->opcode == ZEND_JMP) { - /* JMP L, L: JMP L1 -> JMP L1 */ - next = target_block->successors[0]; - } else { - break; - } - CHECK_LOOP(next); - block->successors[0] = next; - ++(*opt_count); - target_block = get_target_block(cfg, block, 0, opt_count); - } - - next_block = get_next_block(cfg, block); - if (target_block == next_block) { - /* JMP(next) -> NOP */ - MAKE_NOP(last_op); - ++(*opt_count); - block->len--; - } else if (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - if (target->opcode == ZEND_JMPZNZ) { - /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */ - *last_op = *target; - if (last_op->op1_type == IS_CONST) { - zval zv; - ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op)); - last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv); - } - block->successors_count = 2; - block->successors[0] = target_block->successors[0]; - block->successors[1] = target_block->successors[1]; - ++(*opt_count); - goto optimize_jmpznz; - } else if ((target->opcode == ZEND_RETURN || - target->opcode == ZEND_RETURN_BY_REF || - target->opcode == ZEND_GENERATOR_RETURN || - target->opcode == ZEND_EXIT) && - !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) { - /* JMP L, L: RETURN to immediate RETURN */ - *last_op = *target; - if (last_op->op1_type == IS_CONST) { - zval zv; - ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op)); - last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv); - } - block->successors_count = 0; - ++(*opt_count); - } - } - break; - - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_JMP_NULL: - jmp_hitlist_count = 0; - - target_block = get_target_block(cfg, block, 0, opt_count); - while (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - - if (target->opcode == ZEND_JMP) { - /* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */ - next = target_block->successors[0]; - CHECK_LOOP(next); - block->successors[0] = next; - ++(*opt_count); - } else { - break; - } - target_block = get_target_block(cfg, block, 0, opt_count); - } - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - jmp_hitlist_count = 0; - - target_block = get_target_block(cfg, block, 0, opt_count); - while (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - - if (target->opcode == ZEND_JMP) { - /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == last_op->opcode && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == INV_COND(last_op->opcode) && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */ - next = target_block->successors[1]; - } else if (target->opcode == ZEND_JMPZNZ && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */ - next = target_block->successors[last_op->opcode == ZEND_JMPNZ]; - } else { - break; - } - CHECK_LOOP(next); - block->successors[0] = next; - ++(*opt_count); - target_block = get_target_block(cfg, block, 0, opt_count); - } - - follow_block = get_follow_block(cfg, block, 1, opt_count); - if (target_block == follow_block) { - /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */ - if (last_op->op1_type == IS_CV) { - last_op->opcode = ZEND_CHECK_VAR; - last_op->op2.num = 0; - } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) { - last_op->opcode = ZEND_FREE; - last_op->op2.num = 0; - } else { - MAKE_NOP(last_op); - block->len--; - } - block->successors_count = 1; - ++(*opt_count); - } else if (follow_block->len == 1) { - target = op_array->opcodes + follow_block->start; - if (target->opcode == ZEND_JMP) { - if (block->successors[0] == follow_block->successors[0]) { - /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */ - if (last_op->op1_type == IS_CV) { - last_op->opcode = ZEND_CHECK_VAR; - last_op->op2.num = 0; - } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) { - last_op->opcode = ZEND_FREE; - last_op->op2.num = 0; - } else { - MAKE_NOP(last_op); - block->len--; - } - block->successors[0] = follow_block - cfg->blocks; - block->successors_count = 1; - ++(*opt_count); - break; - } else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) { - next_block = get_next_block(cfg, follow_block); - - if (target_block == next_block) { - /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/ - - last_op->opcode = INV_COND(last_op->opcode); - - block->successors[0] = follow_block->successors[0]; - block->successors[1] = next_block - cfg->blocks; - - follow_block->flags &= ~ZEND_BB_REACHABLE; - MAKE_NOP(target); - follow_block->len = 0; - - next_block->flags |= ZEND_BB_FOLLOW; - - break; - } - } - - /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ - if (last_op->opcode == ZEND_JMPZ) { - block->successors[1] = follow_block->successors[0]; - } else { - block->successors[1] = block->successors[0]; - block->successors[0] = follow_block->successors[0]; - } - last_op->opcode = ZEND_JMPZNZ; - ++(*opt_count); - } - } - break; - - case ZEND_JMPNZ_EX: - case ZEND_JMPZ_EX: - jmp_hitlist_count = 0; - - target_block = get_target_block(cfg, block, 0, opt_count); - while (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - - if (target->opcode == ZEND_JMP) { - /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == last_op->opcode-3 && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == last_op->opcode && - target->result.var == last_op->result.var && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == ZEND_JMPZNZ && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */ - next = target_block->successors[last_op->opcode == ZEND_JMPNZ_EX]; - } else if (target->opcode == INV_EX_COND(last_op->opcode) && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */ - next = target_block->successors[1]; - } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) && - target->result.var == last_op->result.var && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */ - next = target_block->successors[1]; - } else if (target->opcode == ZEND_BOOL && - (SAME_VAR(target->op1, last_op->result) || - SAME_VAR(target->op1, last_op->op1))) { - /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to - Z = JMPZ_EX(X,L1+1) */ - - /* NOTE: This optimization pattern is not safe, but works, */ - /* because result of JMPZ_EX instruction */ - /* is not used on the following path and */ - /* should be used once on the branch path. */ - /* */ - /* The pattern works well only if jums processed in */ - /* direct order, otherwise it breaks JMPZ_EX */ - /* sequences too early. */ - last_op->result.var = target->result.var; - next = target_block->successors[0]; - } else { - break; - } - CHECK_LOOP(next); - block->successors[0] = next; - ++(*opt_count); - target_block = get_target_block(cfg, block, 0, opt_count); - } - - follow_block = get_follow_block(cfg, block, 1, opt_count); - if (target_block == follow_block) { - /* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */ - last_op->opcode = ZEND_BOOL; - last_op->op2.num = 0; - block->successors_count = 1; - ++(*opt_count); - break; - } - break; - - case ZEND_JMPZNZ: { -optimize_jmpznz: - jmp_hitlist_count = 0; - target_block = get_target_block(cfg, block, 0, opt_count); - while (target_block->len == 1) { - target = op_array->opcodes + target_block->start; - - if (target->opcode == ZEND_JMP) { - /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */ - next = target_block->successors[0]; - } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ - next = target_block->successors[0]; - } else if (target->opcode == ZEND_JMPNZ && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ - next = target_block->successors[1]; - } else { - break; - } - CHECK_LOOP(next); - block->successors[0] = next; - ++(*opt_count); - target_block = get_target_block(cfg, block, 0, opt_count); - } - - jmp_hitlist_count = 0; - follow_block = get_target_block(cfg, block, 1, opt_count); - while (follow_block->len == 1) { - target = op_array->opcodes + follow_block->start; - - if (target->opcode == ZEND_JMP) { - /* JMPZNZ(X, L1, L2), L2: JMP(L3) -> JMPZNZ(X, L1, L3) */ - next = follow_block->successors[0]; - } else if (target->opcode == ZEND_JMPNZ && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZNZ(X, L1, L2), L2: X = JMPNZ(X, L3) -> JMPZNZ(X, L1, L3) */ - next = follow_block->successors[0]; - } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && - SAME_VAR(target->op1, last_op->op1)) { - /* JMPZNZ(X, L1, L2), L2: JMPZ(X, L3) -> JMPZNZ(X, L1, L2+1) */ - next = follow_block->successors[1]; - } else { - break; - } - CHECK_LOOP(next); - block->successors[1] = next; - ++(*opt_count); - follow_block = get_target_block(cfg, block, 1, opt_count); - } - - next_block = get_next_block(cfg, block); - if (target_block == follow_block && - !(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) { - /* JMPZNZ(?,L,L) -> JMP(L) */ - last_op->opcode = ZEND_JMP; - SET_UNUSED(last_op->op1); - SET_UNUSED(last_op->op2); - last_op->extended_value = 0; - block->successors_count = 1; - ++(*opt_count); - } else if (target_block == next_block) { - /* jumping to next on Z - can follow to it and jump only on NZ */ - /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */ - int tmp = block->successors[0]; - last_op->opcode = ZEND_JMPNZ; - block->successors[0] = block->successors[1]; - block->successors[1] = tmp; - ++(*opt_count); - } else if (follow_block == next_block) { - /* jumping to next on NZ - can follow to it and jump only on Z */ - /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */ - last_op->opcode = ZEND_JMPZ; - ++(*opt_count); - } - break; - } - } -} - -/* Global data dependencies */ - -/* Find a set of variables which are used outside of the block where they are - * defined. We won't apply some optimization patterns for such variables. */ -static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx) -{ - int n; - zend_basic_block *block, *next_block; - uint32_t var_num; - uint32_t bitset_len; - zend_bitset usage; - zend_bitset defined_here; - void *checkpoint; - zend_op *opline, *end; - - - if (op_array->T == 0) { - /* shortcut - if no Ts, nothing to do */ - return; - } - - checkpoint = zend_arena_checkpoint(ctx->arena); - bitset_len = zend_bitset_len(op_array->last_var + op_array->T); - defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); - - zend_bitset_clear(defined_here, bitset_len); - for (n = 1; n < cfg->blocks_count; n++) { - block = cfg->blocks + n; - - if (!(block->flags & ZEND_BB_REACHABLE)) { - continue; - } - - opline = op_array->opcodes + block->start; - end = opline + block->len; - if (!(block->flags & ZEND_BB_FOLLOW) || - (block->flags & ZEND_BB_TARGET)) { - /* Skip continuation of "extended" BB */ - zend_bitset_clear(defined_here, bitset_len); - } - - while (opline<end) { - if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - var_num = VAR_NUM(opline->op1.var); - if (!zend_bitset_in(defined_here, var_num)) { - zend_bitset_incl(used_ext, var_num); - } - } - if (opline->op2_type == IS_VAR) { - var_num = VAR_NUM(opline->op2.var); - if (opline->opcode == ZEND_FE_FETCH_R || - opline->opcode == ZEND_FE_FETCH_RW) { - /* these opcode use the op2 as result */ - zend_bitset_incl(defined_here, var_num); - } else if (!zend_bitset_in(defined_here, var_num)) { - zend_bitset_incl(used_ext, var_num); - } - } else if (opline->op2_type == IS_TMP_VAR) { - var_num = VAR_NUM(opline->op2.var); - if (!zend_bitset_in(defined_here, var_num)) { - zend_bitset_incl(used_ext, var_num); - } - } - - if (opline->result_type == IS_VAR) { - var_num = VAR_NUM(opline->result.var); - zend_bitset_incl(defined_here, var_num); - } else if (opline->result_type == IS_TMP_VAR) { - var_num = VAR_NUM(opline->result.var); - switch (opline->opcode) { - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_ADD_ARRAY_UNPACK: - case ZEND_ROPE_ADD: - /* these opcodes use the result as argument */ - if (!zend_bitset_in(defined_here, var_num)) { - zend_bitset_incl(used_ext, var_num); - } - break; - default : - zend_bitset_incl(defined_here, var_num); - } - } - opline++; - } - } - - if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) { - int printed = 0; - uint32_t i; - - for (i = op_array->last_var; i< op_array->T; i++) { - if (zend_bitset_in(used_ext, i)) { - if (!printed) { - fprintf(stderr, "NON-LOCAL-VARS: %d", i); - printed = 1; - } else { - fprintf(stderr, ", %d", i); - } - } - } - if (printed) { - fprintf(stderr, "\n"); - } - } - - usage = defined_here; - next_block = NULL; - for (n = cfg->blocks_count; n > 0;) { - block = cfg->blocks + (--n); - - if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) { - continue; - } - - end = op_array->opcodes + block->start; - opline = end + block->len - 1; - if (!next_block || - !(next_block->flags & ZEND_BB_FOLLOW) || - (next_block->flags & ZEND_BB_TARGET)) { - /* Skip continuation of "extended" BB */ - zend_bitset_copy(usage, used_ext, bitset_len); - } else if (block->successors_count > 1) { - zend_bitset_union(usage, used_ext, bitset_len); - } - next_block = block; - - while (opline >= end) { - /* usage checks */ - if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { - if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) { - switch (opline->opcode) { - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_ASSIGN: - case ZEND_ASSIGN_REF: - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - opline->result_type = IS_UNUSED; - break; - case ZEND_POST_INC: - case ZEND_POST_DEC: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_POST_INC_STATIC_PROP: - case ZEND_POST_DEC_STATIC_PROP: - opline->opcode -= 2; - opline->result_type = IS_UNUSED; - break; - case ZEND_QM_ASSIGN: - case ZEND_BOOL: - case ZEND_BOOL_NOT: - if (opline->op1_type == IS_CV) { - opline->opcode = ZEND_CHECK_VAR; - SET_UNUSED(opline->result); - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - opline->opcode = ZEND_FREE; - SET_UNUSED(opline->result); - } else { - if (opline->op1_type == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - } - MAKE_NOP(opline); - } - break; - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - opline->opcode -= 3; - SET_UNUSED(opline->result); - break; - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_ADD_ARRAY_UNPACK: - case ZEND_ROPE_ADD: - zend_bitset_incl(usage, VAR_NUM(opline->result.var)); - break; - } - } else { - switch (opline->opcode) { - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_ADD_ARRAY_UNPACK: - case ZEND_ROPE_ADD: - break; - default: - zend_bitset_excl(usage, VAR_NUM(opline->result.var)); - break; - } - } - } - - if (opline->op2_type == IS_VAR) { - switch (opline->opcode) { - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - zend_bitset_excl(usage, VAR_NUM(opline->op2.var)); - break; - default: - zend_bitset_incl(usage, VAR_NUM(opline->op2.var)); - break; - } - } else if (opline->op2_type == IS_TMP_VAR) { - zend_bitset_incl(usage, VAR_NUM(opline->op2.var)); - } - - if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - zend_bitset_incl(usage, VAR_NUM(opline->op1.var)); - } - - opline--; - } - } - - zend_arena_release(&ctx->arena, checkpoint); -} - -static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg, uint32_t *opt_count) -{ - int i; - zend_basic_block *b, *bb; - zend_basic_block *prev = NULL; - - for (i = 0; i < cfg->blocks_count; i++) { - b = cfg->blocks + i; - if (b->flags & ZEND_BB_REACHABLE) { - if ((b->flags & ZEND_BB_FOLLOW) && - !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) && - prev && prev->successors_count == 1 && prev->successors[0] == i) - { - zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1; - if (prev->len != 0 && last_op->opcode == ZEND_JMP) { - MAKE_NOP(last_op); - } - - for (bb = prev + 1; bb != b; bb++) { - zend_op *op = op_array->opcodes + bb->start; - zend_op *end = op + bb->len; - while (op < end) { - if (op->op1_type == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(op)); - } - if (op->op2_type == IS_CONST) { - literal_dtor(&ZEND_OP2_LITERAL(op)); - } - MAKE_NOP(op); - op++; - } - /* make block empty */ - bb->len = 0; - } - - /* re-link */ - prev->flags |= (b->flags & ZEND_BB_EXIT); - prev->len = b->start + b->len - prev->start; - prev->successors_count = b->successors_count; - if (b->successors != b->successors_storage) { - prev->successors = b->successors; - b->successors = b->successors_storage; - } else { - memcpy(prev->successors, b->successors, b->successors_count * sizeof(int)); - } - - /* unlink & make block empty and unreachable */ - b->flags = 0; - b->len = 0; - b->successors_count = 0; - ++(*opt_count); - } else { - prev = b; - } - } - } -} - -#define PASSES 3 - -void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_cfg cfg; - zend_basic_block *blocks, *end, *b; - int pass; - uint32_t bitset_len; - zend_bitset usage; - void *checkpoint; - zend_op **Tsource; - uint32_t opt_count; - int *jmp_hitlist; - - /* Build CFG */ - checkpoint = zend_arena_checkpoint(ctx->arena); - if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) { - zend_arena_release(&ctx->arena, checkpoint); - return; - } - - if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) { - zend_arena_release(&ctx->arena, checkpoint); - return; - } - - if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) { - zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg); - } - - bitset_len = zend_bitset_len(op_array->last_var + op_array->T); - Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *)); - usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); - jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int)); - - blocks = cfg.blocks; - end = blocks + cfg.blocks_count; - for (pass = 0; pass < PASSES; pass++) { - opt_count = 0; - - /* Compute data dependencies */ - zend_bitset_clear(usage, bitset_len); - zend_t_usage(&cfg, op_array, usage, ctx); - - /* optimize each basic block separately */ - for (b = blocks; b < end; b++) { - if (!(b->flags & ZEND_BB_REACHABLE)) { - continue; - } - /* we track data dependencies only inside a single basic block */ - if (!(b->flags & ZEND_BB_FOLLOW) || - (b->flags & ZEND_BB_TARGET)) { - /* Skip continuation of "extended" BB */ - memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *)); - } - zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count); - } - - /* Eliminate NOPs */ - for (b = blocks; b < end; b++) { - if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { - strip_nops(op_array, b); - } - } - - opt_count = 0; - - /* Jump optimization for each block */ - for (b = blocks; b < end; b++) { - if (b->flags & ZEND_BB_REACHABLE) { - zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count); - } - } - - /* Eliminate unreachable basic blocks */ - zend_cfg_remark_reachable_blocks(op_array, &cfg); - - /* Merge Blocks */ - zend_merge_blocks(op_array, &cfg, &opt_count); - - if (opt_count == 0) { - break; - } - } - - assemble_code_blocks(&cfg, op_array, ctx); - - if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) { - zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg); - } - - /* Destroy CFG */ - zend_arena_release(&ctx->arena, checkpoint); -} diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c deleted file mode 100644 index 0e1529d2bd..0000000000 --- a/ext/opcache/Optimizer/compact_literals.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - | Xinchen Hui <laruence@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 11 - * - compact literals table - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" -#include "zend_extensions.h" - -#define DEBUG_COMPACT_LITERALS 0 - -#define LITERAL_VALUE 0x0100 -#define LITERAL_FUNC 0x0200 -#define LITERAL_CLASS 0x0300 -#define LITERAL_CONST 0x0400 -#define LITERAL_CLASS_CONST 0x0500 -#define LITERAL_STATIC_METHOD 0x0600 -#define LITERAL_STATIC_PROPERTY 0x0700 -#define LITERAL_METHOD 0x0800 -#define LITERAL_PROPERTY 0x0900 -#define LITERAL_GLOBAL 0x0A00 - -#define LITERAL_KIND_MASK 0x0f00 -#define LITERAL_NUM_RELATED_MASK 0x000f - -#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK) - -typedef struct _literal_info { - uint32_t flags; /* bitmask (see defines above) */ -} literal_info; - -#define LITERAL_INFO(n, kind, related) do { \ - info[n].flags = ((kind) | (related)); \ - } while (0) - -static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num) -{ - zend_arg_info *arg_info; - if (arg_num > 0) { - if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { - return 0; - } - if (EXPECTED(arg_num <= op_array->num_args)) { - arg_info = &op_array->arg_info[arg_num-1]; - } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) { - arg_info = &op_array->arg_info[op_array->num_args]; - } else { - return 0; - } - } else { - arg_info = op_array->arg_info - 1; - } - - if (ZEND_TYPE_HAS_CLASS(arg_info->type)) { - if (ZEND_TYPE_HAS_LIST(arg_info->type)) { - return ZEND_TYPE_LIST(arg_info->type)->num_types; - } - return 1; - } - - return 0; -} - -static uint32_t add_static_slot(HashTable *hash, - zend_op_array *op_array, - uint32_t op1, - uint32_t op2, - uint32_t kind, - int *cache_size) -{ - uint32_t ret; - zval *class_name = &op_array->literals[op1]; - zval *prop_name = &op_array->literals[op2]; - zval *pos, tmp; - - zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name)); - ZSTR_H(key) = zend_string_hash_func(key); - ZSTR_H(key) += kind; - - pos = zend_hash_find(hash, key); - if (pos) { - ret = Z_LVAL_P(pos); - } else { - ret = *cache_size; - *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *); - ZVAL_LONG(&tmp, ret); - zend_hash_add(hash, key, &tmp); - } - zend_string_release_ex(key, 0); - return ret; -} - -void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_op *opline, *end; - int i, j, n, *map, cache_size; - zval zv, *pos; - literal_info *info; - int l_null = -1; - int l_false = -1; - int l_true = -1; - int l_empty_arr = -1; - HashTable hash, double_hash; - zend_string *key = NULL; - void *checkpoint = zend_arena_checkpoint(ctx->arena); - int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot; - - if (op_array->last_literal) { - info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info)); - - /* Mark literals of specific types */ - opline = op_array->opcodes; - end = opline + op_array->last; - while (opline < end) { - switch (opline->opcode) { - case ZEND_INIT_FCALL: - LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1); - break; - case ZEND_INIT_FCALL_BY_NAME: - LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2); - break; - case ZEND_INIT_NS_FCALL_BY_NAME: - LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3); - break; - case ZEND_INIT_METHOD_CALL: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); - } - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2); - } - break; - case ZEND_INIT_STATIC_METHOD_CALL: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2); - } - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2); - } - break; - case ZEND_CATCH: - LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2); - break; - case ZEND_DEFINED: - LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1); - break; - case ZEND_FETCH_CONSTANT: - if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) { - LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3); - } else { - LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2); - } - break; - case ZEND_FETCH_CLASS_CONSTANT: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2); - } - LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1); - break; - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_FETCH_STATIC_PROP_R: - case ZEND_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_IS: - case ZEND_FETCH_STATIC_PROP_UNSET: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_UNSET_STATIC_PROP: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_PRE_INC_STATIC_PROP: - case ZEND_PRE_DEC_STATIC_PROP: - case ZEND_POST_INC_STATIC_PROP: - case ZEND_POST_DEC_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_OP: - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2); - } - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1); - } - break; - case ZEND_FETCH_CLASS: - case ZEND_INSTANCEOF: - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2); - } - break; - case ZEND_NEW: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2); - } - break; - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_UNSET_OBJ: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ASSIGN_OBJ_OP: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); - } - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1); - } - break; - case ZEND_BIND_GLOBAL: - LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1); - break; - case ZEND_RECV_INIT: - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - break; - case ZEND_DECLARE_FUNCTION: - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); - break; - case ZEND_DECLARE_CLASS: - case ZEND_DECLARE_CLASS_DELAYED: - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - } - break; - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ASSIGN_DIM: - case ZEND_UNSET_DIM: - case ZEND_FETCH_DIM_R: - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_LIST_R: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM_OP: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); - } - if (opline->op2_type == IS_CONST) { - if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2); - } else { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - } - } - break; - default: - if (opline->op1_type == IS_CONST) { - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); - } - if (opline->op2_type == IS_CONST) { - LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); - } - break; - } - opline++; - } - -#if DEBUG_COMPACT_LITERALS - { - int i, use_copy; - fprintf(stderr, "File %s func %s\n", op_array->filename->val, - op_array->function_name ? op_array->function_name->val : "main"); - fprintf(stderr, "Literals table size %d\n", op_array->last_literal); - - for (i = 0; i < op_array->last_literal; i++) { - zval zv; - ZVAL_COPY_VALUE(&zv, op_array->literals + i); - use_copy = zend_make_printable_zval(op_array->literals + i, &zv); - fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); - if (use_copy) { - zval_ptr_dtor_nogc(&zv); - } - } - fflush(stderr); - } -#endif - - /* Merge equal constants */ - j = 0; - zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0); - /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */ - zend_hash_init(&double_hash, 0, NULL, NULL, 0); - map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int)); - memset(map, 0, op_array->last_literal * sizeof(int)); - for (i = 0; i < op_array->last_literal; i++) { - if (!info[i].flags) { - /* unset literal */ - zval_ptr_dtor_nogc(&op_array->literals[i]); - continue; - } - switch (Z_TYPE(op_array->literals[i])) { - case IS_NULL: - if (l_null < 0) { - l_null = j; - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } - map[i] = l_null; - break; - case IS_FALSE: - if (l_false < 0) { - l_false = j; - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } - map[i] = l_false; - break; - case IS_TRUE: - if (l_true < 0) { - l_true = j; - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } - map[i] = l_true; - break; - case IS_LONG: - if (LITERAL_NUM_RELATED(info[i].flags) == 1) { - if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) { - map[i] = Z_LVAL_P(pos); - } else { - map[i] = j; - ZVAL_LONG(&zv, j); - zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv); - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } - } else { - ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2); - key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0); - ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 + - LITERAL_NUM_RELATED(info[i].flags) - 1; - if ((pos = zend_hash_find(&hash, key)) != NULL - && LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) { - map[i] = Z_LVAL_P(pos); - zval_ptr_dtor_nogc(&op_array->literals[i+1]); - } else { - map[i] = j; - ZVAL_LONG(&zv, j); - zend_hash_add_new(&hash, key, &zv); - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - op_array->literals[j+1] = op_array->literals[i+1]; - info[j+1] = info[i+1]; - } - j += 2; - } - zend_string_release_ex(key, 0); - i++; - } - break; - case IS_DOUBLE: - if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) { - map[i] = Z_LVAL_P(pos); - } else { - map[i] = j; - ZVAL_LONG(&zv, j); - zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv); - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } - break; - case IS_STRING: { - if (LITERAL_NUM_RELATED(info[i].flags) == 1) { - key = zend_string_copy(Z_STR(op_array->literals[i])); - } else if ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE) { - key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0); - ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) + - LITERAL_NUM_RELATED(info[i].flags) - 1; - } else { - /* Don't merge LITERAL_VALUE that has related literals */ - key = NULL; - } - if (key && (pos = zend_hash_find(&hash, key)) != NULL && - Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING && - LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) && - (LITERAL_NUM_RELATED(info[i].flags) != 2 || - ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE && - (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) { - zend_string_release_ex(key, 0); - map[i] = Z_LVAL_P(pos); - zval_ptr_dtor_nogc(&op_array->literals[i]); - n = LITERAL_NUM_RELATED(info[i].flags); - while (n > 1) { - i++; - zval_ptr_dtor_nogc(&op_array->literals[i]); - n--; - } - } else { - map[i] = j; - ZVAL_LONG(&zv, j); - if (key) { - zend_hash_add_new(&hash, key, &zv); - zend_string_release_ex(key, 0); - } - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - n = LITERAL_NUM_RELATED(info[i].flags); - while (n > 1) { - i++; - if (i != j) op_array->literals[j] = op_array->literals[i]; - j++; - n--; - } - } - break; - } - case IS_ARRAY: - if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) { - if (l_empty_arr < 0) { - l_empty_arr = j; - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - } else { - zval_ptr_dtor_nogc(&op_array->literals[i]); - } - map[i] = l_empty_arr; - break; - } - /* break missing intentionally */ - default: - /* don't merge other types */ - map[i] = j; - if (i != j) { - op_array->literals[j] = op_array->literals[i]; - info[j] = info[i]; - } - j++; - break; - } - } - - /* Only clean "hash", as it will be reused in the loop below. */ - zend_hash_clean(&hash); - zend_hash_destroy(&double_hash); - op_array->last_literal = j; - - const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int)); - memset(const_slot, -1, j * 6 * sizeof(int)); - class_slot = const_slot + j; - func_slot = class_slot + j; - bind_var_slot = func_slot + j; - property_slot = bind_var_slot + j; - method_slot = property_slot + j; - - /* Update opcodes to use new literals table */ - cache_size = zend_op_array_extension_handles * sizeof(void*); - opline = op_array->opcodes; - end = opline + op_array->last; - while (opline < end) { - if (opline->op1_type == IS_CONST) { - opline->op1.constant = map[opline->op1.constant]; - } - if (opline->op2_type == IS_CONST) { - opline->op2.constant = map[opline->op2.constant]; - } - switch (opline->opcode) { - case ZEND_RECV_INIT: - case ZEND_RECV: - case ZEND_RECV_VARIADIC: - { - size_t num_classes = type_num_classes(op_array, opline->op1.num); - if (num_classes) { - opline->extended_value = cache_size; - cache_size += num_classes * sizeof(void *); - } - break; - } - case ZEND_VERIFY_RETURN_TYPE: - { - size_t num_classes = type_num_classes(op_array, 0); - if (num_classes) { - opline->op2.num = cache_size; - cache_size += num_classes * sizeof(void *); - } - break; - } - case ZEND_ASSIGN_STATIC_PROP_OP: - if (opline->op1_type == IS_CONST) { - // op1 static property - if (opline->op2_type == IS_CONST) { - (opline+1)->extended_value = add_static_slot(&hash, op_array, - opline->op2.constant, - opline->op1.constant, - LITERAL_STATIC_PROPERTY, - &cache_size); - } else { - (opline+1)->extended_value = cache_size; - cache_size += 3 * sizeof(void *); - } - } else if (opline->op2_type == IS_CONST) { - // op2 class - if (class_slot[opline->op2.constant] >= 0) { - (opline+1)->extended_value = class_slot[opline->op2.constant]; - } else { - (opline+1)->extended_value = cache_size; - class_slot[opline->op2.constant] = cache_size; - cache_size += sizeof(void *); - } - } - break; - case ZEND_ASSIGN_OBJ_OP: - if (opline->op2_type == IS_CONST) { - // op2 property - if (opline->op1_type == IS_UNUSED && - property_slot[opline->op2.constant] >= 0) { - (opline+1)->extended_value = property_slot[opline->op2.constant]; - } else { - (opline+1)->extended_value = cache_size; - cache_size += 3 * sizeof(void *); - if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = (opline+1)->extended_value; - } - } - } - break; - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_UNSET_OBJ: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - if (opline->op2_type == IS_CONST) { - // op2 property - if (opline->op1_type == IS_UNUSED && - property_slot[opline->op2.constant] >= 0) { - opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - cache_size += 3 * sizeof(void *); - if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS; - } - } - } - break; - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - if (opline->op2_type == IS_CONST) { - // op2 property - if (opline->op1_type == IS_UNUSED && - property_slot[opline->op2.constant] >= 0) { - opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY); - } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); - cache_size += 3 * sizeof(void *); - if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY; - } - } - } - break; - case ZEND_INIT_FCALL: - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - // op2 func - if (func_slot[opline->op2.constant] >= 0) { - opline->result.num = func_slot[opline->op2.constant]; - } else { - opline->result.num = cache_size; - cache_size += sizeof(void *); - func_slot[opline->op2.constant] = opline->result.num; - } - break; - case ZEND_INIT_METHOD_CALL: - if (opline->op2_type == IS_CONST) { - // op2 method - if (opline->op1_type == IS_UNUSED && - method_slot[opline->op2.constant] >= 0) { - opline->result.num = method_slot[opline->op2.constant]; - } else { - opline->result.num = cache_size; - cache_size += 2 * sizeof(void *); - if (opline->op1_type == IS_UNUSED) { - method_slot[opline->op2.constant] = opline->result.num; - } - } - } - break; - case ZEND_INIT_STATIC_METHOD_CALL: - if (opline->op2_type == IS_CONST) { - // op2 static method - if (opline->op1_type == IS_CONST) { - opline->result.num = add_static_slot(&hash, op_array, - opline->op1.constant, - opline->op2.constant, - LITERAL_STATIC_METHOD, - &cache_size); - } else { - opline->result.num = cache_size; - cache_size += 2 * sizeof(void *); - } - } else if (opline->op1_type == IS_CONST) { - // op1 class - if (class_slot[opline->op1.constant] >= 0) { - opline->result.num = class_slot[opline->op1.constant]; - } else { - opline->result.num = cache_size; - cache_size += sizeof(void *); - class_slot[opline->op1.constant] = opline->result.num; - } - } - break; - case ZEND_DEFINED: - // op1 const - if (const_slot[opline->op1.constant] >= 0) { - opline->extended_value = const_slot[opline->op1.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - const_slot[opline->op1.constant] = opline->extended_value; - } - break; - case ZEND_FETCH_CONSTANT: - // op2 const - if (const_slot[opline->op2.constant] >= 0) { - opline->extended_value = const_slot[opline->op2.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - const_slot[opline->op2.constant] = opline->extended_value; - } - break; - case ZEND_FETCH_CLASS_CONSTANT: - if (opline->op1_type == IS_CONST) { - // op1/op2 class_const - opline->extended_value = add_static_slot(&hash, op_array, - opline->op1.constant, - opline->op2.constant, - LITERAL_CLASS_CONST, - &cache_size); - } else { - opline->extended_value = cache_size; - cache_size += 2 * sizeof(void *); - } - break; - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_FETCH_STATIC_PROP_R: - case ZEND_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_IS: - case ZEND_FETCH_STATIC_PROP_UNSET: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_UNSET_STATIC_PROP: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_PRE_INC_STATIC_PROP: - case ZEND_PRE_DEC_STATIC_PROP: - case ZEND_POST_INC_STATIC_PROP: - case ZEND_POST_DEC_STATIC_PROP: - if (opline->op1_type == IS_CONST) { - // op1 static property - if (opline->op2_type == IS_CONST) { - opline->extended_value = add_static_slot(&hash, op_array, - opline->op2.constant, - opline->op1.constant, - LITERAL_STATIC_PROPERTY, - &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - cache_size += 3 * sizeof(void *); - } - } else if (opline->op2_type == IS_CONST) { - // op2 class - if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - class_slot[opline->op2.constant] = cache_size; - cache_size += sizeof(void *); - } - } - break; - case ZEND_FETCH_CLASS: - case ZEND_INSTANCEOF: - if (opline->op2_type == IS_CONST) { - // op2 class - if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - class_slot[opline->op2.constant] = opline->extended_value; - } - } - break; - case ZEND_NEW: - if (opline->op1_type == IS_CONST) { - // op1 class - if (class_slot[opline->op1.constant] >= 0) { - opline->op2.num = class_slot[opline->op1.constant]; - } else { - opline->op2.num = cache_size; - cache_size += sizeof(void *); - class_slot[opline->op1.constant] = opline->op2.num; - } - } - break; - case ZEND_CATCH: - if (opline->op1_type == IS_CONST) { - // op1 class - if (class_slot[opline->op1.constant] >= 0) { - opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH); - } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH); - cache_size += sizeof(void *); - class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH; - } - } - break; - case ZEND_BIND_GLOBAL: - // op2 bind var - if (bind_var_slot[opline->op2.constant] >= 0) { - opline->extended_value = bind_var_slot[opline->op2.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - bind_var_slot[opline->op2.constant] = opline->extended_value; - } - break; - case ZEND_DECLARE_LAMBDA_FUNCTION: - case ZEND_DECLARE_ANON_CLASS: - case ZEND_DECLARE_CLASS_DELAYED: - opline->extended_value = cache_size; - cache_size += sizeof(void *); - break; - case ZEND_SEND_VAL: - case ZEND_SEND_VAL_EX: - case ZEND_SEND_VAR: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_REF: - case ZEND_SEND_FUNC_ARG: - case ZEND_CHECK_FUNC_ARG: - if (opline->op2_type == IS_CONST) { - opline->result.num = cache_size; - cache_size += 2 * sizeof(void *); - } - break; - } - opline++; - } - op_array->cache_size = cache_size; - zend_hash_destroy(&hash); - zend_arena_release(&ctx->arena, checkpoint); - - if (1) { - opline = op_array->opcodes; - while (1) { - if (opline->opcode == ZEND_RECV_INIT) { - zval *val = &op_array->literals[opline->op2.constant]; - - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - /* Ensure zval is aligned to 8 bytes */ - op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8); - Z_CACHE_SLOT_P(val) = op_array->cache_size; - op_array->cache_size += sizeof(zval); - } - } else if (opline->opcode != ZEND_RECV) { - break; - } - opline++; - } - } - -#if DEBUG_COMPACT_LITERALS - { - int i, use_copy; - fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal); - - for (i = 0; i < op_array->last_literal; i++) { - zval zv; - ZVAL_COPY_VALUE(&zv, op_array->literals + i); - use_copy = zend_make_printable_zval(op_array->literals + i, &zv); - fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); - if (use_copy) { - zval_ptr_dtor_nogc(&zv); - } - } - fflush(stderr); - } -#endif - } -} diff --git a/ext/opcache/Optimizer/compact_vars.c b/ext/opcache/Optimizer/compact_vars.c deleted file mode 100644 index bf7a005787..0000000000 --- a/ext/opcache/Optimizer/compact_vars.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Removing unused variables | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "ZendAccelerator.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_bitset.h" - -/* 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 CV name table */ - if (num_cvs != op_array->last_var) { - if (num_cvs) { - 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_ex(op_array->vars[i], 0); - } - } - efree(op_array->vars); - op_array->vars = names; - } else { - for (i = 0; i < op_array->last_var; i++) { - zend_string_release_ex(op_array->vars[i], 0); - } - efree(op_array->vars); - op_array->vars = NULL; - } - op_array->last_var = num_cvs; - } - - op_array->T = num_tmps; - - free_alloca(vars_map, use_heap2); -} diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c deleted file mode 100644 index 339b08167d..0000000000 --- a/ext/opcache/Optimizer/dce.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, DCE - Dead Code Elimination | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "ZendAccelerator.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "Optimizer/zend_inference.h" -#include "Optimizer/zend_ssa.h" -#include "Optimizer/zend_func_info.h" -#include "Optimizer/zend_call_graph.h" -#include "zend_bitset.h" - -/* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes - * that all instructions and phis are dead. Instructions with immediate side-effects are then marked - * as live. We then recursively (using a worklist) propagate liveness to the instructions that def - * the used operands. - * - * Notes: - * * This pass does not perform unreachable code elimination. This happens as part of the SCCP - * pass. - * * The DCE is performed without taking control-dependence into account, i.e. all conditional - * branches are assumed to be live. It's possible to take control-dependence into account using - * the DCE algorithm described by Cytron et al., however it requires the construction of a - * postdominator tree and of postdominance frontiers, which does not seem worthwhile at this - * point. - * * We separate intrinsic side-effects from potential side-effects in the form of notices thrown - * by the instruction (in case we want to make this configurable). See may_have_side_effects() and - * zend_may_throw(). - * * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same - * order. There is an optimization option to allow reordering of dtor effects. - * * The algorithm is able to eliminate dead modifications of non-escaping arrays - * and objects as well as dead arrays and objects allocations. - */ - -typedef struct { - zend_ssa *ssa; - zend_op_array *op_array; - zend_bitset instr_dead; - zend_bitset phi_dead; - zend_bitset instr_worklist; - zend_bitset phi_worklist; - zend_bitset phi_worklist_no_val; - uint32_t instr_worklist_len; - uint32_t phi_worklist_len; - unsigned reorder_dtor_effects : 1; -} context; - -static inline bool is_bad_mod(const zend_ssa *ssa, int use, int def) { - if (def < 0) { - /* This modification is not tracked by SSA, assume the worst */ - return 1; - } - if (ssa->var_info[use].type & MAY_BE_REF) { - /* Modification of reference may have side-effect */ - return 1; - } - return 0; -} - -static inline bool may_have_side_effects( - zend_op_array *op_array, zend_ssa *ssa, - const zend_op *opline, const zend_ssa_op *ssa_op, - bool reorder_dtor_effects) { - switch (opline->opcode) { - case ZEND_NOP: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_QM_ASSIGN: - case ZEND_FREE: - case ZEND_FE_FREE: - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_POW: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_BOOL_XOR: - case ZEND_BOOL: - case ZEND_BOOL_NOT: - case ZEND_BW_NOT: - case ZEND_SL: - case ZEND_SR: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_CAST: - case ZEND_ROPE_INIT: - case ZEND_ROPE_ADD: - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_SPACESHIP: - case ZEND_STRLEN: - case ZEND_COUNT: - case ZEND_GET_TYPE: - case ZEND_ISSET_ISEMPTY_THIS: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_FETCH_DIM_IS: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_FETCH_IS: - case ZEND_IN_ARRAY: - case ZEND_FUNC_NUM_ARGS: - case ZEND_FUNC_GET_ARGS: - case ZEND_ARRAY_KEY_EXISTS: - /* No side effects */ - return 0; - case ZEND_ROPE_END: - /* TODO: Rope dce optimization, see #76446 */ - return 1; - case ZEND_JMP: - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - case ZEND_JMP_NULL: - /* For our purposes a jumps and branches are side effects. */ - return 1; - case ZEND_BEGIN_SILENCE: - case ZEND_END_SILENCE: - case ZEND_ECHO: - case ZEND_INCLUDE_OR_EVAL: - case ZEND_THROW: - case ZEND_MATCH_ERROR: - case ZEND_EXT_STMT: - case ZEND_EXT_FCALL_BEGIN: - case ZEND_EXT_FCALL_END: - case ZEND_TICKS: - case ZEND_YIELD: - case ZEND_YIELD_FROM: - /* Intrinsic side effects */ - return 1; - case ZEND_DO_FCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - /* For now assume all calls have side effects */ - return 1; - case ZEND_RECV: - case ZEND_RECV_INIT: - /* Even though RECV_INIT can be side-effect free, these cannot be simply dropped - * due to the prologue skipping code. */ - return 1; - case ZEND_ASSIGN_REF: - return 1; - case ZEND_ASSIGN: - { - if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) { - return 1; - } - if (!reorder_dtor_effects) { - if (opline->op2_type != IS_CONST - && (OP2_INFO() & MAY_HAVE_DTOR) - && ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) { - /* DCE might shorten lifetime */ - return 1; - } - } - return 0; - } - case ZEND_UNSET_VAR: - return 1; - case ZEND_UNSET_CV: - { - uint32_t t1 = OP1_INFO(); - if (t1 & MAY_BE_REF) { - /* We don't consider uses as the LHS of an assignment as real uses during DCE, so - * an unset may be considered dead even if there is a later assignment to the - * variable. Removing the unset in this case would not be correct if the variable - * is a reference, because unset breaks references. */ - return 1; - } - return 0; - } - case ZEND_PRE_INC: - case ZEND_POST_INC: - case ZEND_PRE_DEC: - case ZEND_POST_DEC: - return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def); - case ZEND_ASSIGN_OP: - return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def) - || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE; - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def) - || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) { - return 1; - } - if (!reorder_dtor_effects) { - opline++; - ssa_op++; - if (opline->op1_type != IS_CONST - && (OP1_INFO() & MAY_HAVE_DTOR)) { - /* DCE might shorten lifetime */ - return 1; - } - } - return 0; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def) - || ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) { - return 1; - } - return 0; - case ZEND_BIND_STATIC: - if (op_array->static_variables - && (opline->extended_value & ZEND_BIND_REF) != 0) { - zval *value = - (zval*)((char*)op_array->static_variables->arData + - (opline->extended_value & ~ZEND_BIND_REF)); - if (Z_TYPE_P(value) == IS_CONSTANT_AST) { - /* AST may contain undefined constants */ - return 1; - } - } - return 0; - case ZEND_CHECK_VAR: - return (OP1_INFO() & MAY_BE_UNDEF) != 0; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - /* Model as not having side-effects -- let the side-effect be introduced by - * FE_FETCH if the array is not known to be non-empty. */ - return (OP1_INFO() & MAY_BE_ANY) != MAY_BE_ARRAY; - default: - /* For everything we didn't handle, assume a side-effect */ - return 1; - } -} - -static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) { - zend_ssa_var *var = &ctx->ssa->vars[var_num]; - if (var->definition >= 0) { - if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) { - zend_bitset_incl(ctx->instr_worklist, var->definition); - } - } else if (var->definition_phi) { - if (!check || zend_bitset_in(ctx->phi_dead, var_num)) { - zend_bitset_incl(ctx->phi_worklist, var_num); - } - } -} - -static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) { - zend_ssa_var *var = &ctx->ssa->vars[var_num]; - if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) { - zend_bitset_incl(ctx->phi_worklist_no_val, var_num); - } -} - -static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) { - if (ssa_op->result_use >= 0) { - add_to_worklists(ctx, ssa_op->result_use, check); - } - if (ssa_op->op1_use >= 0) { - if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use) - || (opline->opcode == ZEND_ASSIGN - && (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) != 0)) { - add_to_worklists(ctx, ssa_op->op1_use, check); - } else { - add_to_phi_worklist_no_val(ctx, ssa_op->op1_use); - } - } - if (ssa_op->op2_use >= 0) { - if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use) - || (opline->opcode == ZEND_FE_FETCH_R - && (ssa->var_info[ssa_op->op2_use].type & MAY_BE_REF) != 0)) { - add_to_worklists(ctx, ssa_op->op2_use, check); - } else { - add_to_phi_worklist_no_val(ctx, ssa_op->op2_use); - } - } -} - -static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) { - zend_ssa *ssa = ctx->ssa; - int source; - FOREACH_PHI_SOURCE(phi, source) { - add_to_worklists(ctx, source, check); - } FOREACH_PHI_SOURCE_END(); -} - -static inline bool is_var_dead(context *ctx, int var_num) { - zend_ssa_var *var = &ctx->ssa->vars[var_num]; - if (var->definition_phi) { - return zend_bitset_in(ctx->phi_dead, var_num); - } else if (var->definition >= 0) { - return zend_bitset_in(ctx->instr_dead, var->definition); - } else { - /* Variable has no definition, so either the definition has already been removed (var is - * dead) or this is one of the implicit variables at the start of the function (for our - * purposes live) */ - return var_num >= ctx->op_array->last_var; - } -} - -// Sometimes we can mark the var as EXT_UNUSED -static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) { - if (use_chain >= 0) { - return 0; - } - zend_ssa_var *var = &ctx->ssa->vars[free_var]; - int def = var->definition; - - if (def >= 0) { - zend_ssa_op *def_op = &ctx->ssa->ops[def]; - - if (def_op->result_def == free_var - && var->phi_use_chain == NULL - && var->use_chain == (opline - ctx->op_array->opcodes)) { - zend_op *def_opline = &ctx->op_array->opcodes[def]; - - switch (def_opline->opcode) { - case ZEND_ASSIGN: - case ZEND_ASSIGN_REF: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_PRE_INC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_DO_FCALL: - case ZEND_INCLUDE_OR_EVAL: - case ZEND_YIELD: - case ZEND_YIELD_FROM: - case ZEND_ASSERT_CHECK: - def_opline->result_type = IS_UNUSED; - def_opline->result.var = 0; - def_op->result_def = -1; - var->definition = -1; - return 1; - default: - break; - } - } - } - return 0; -} - -static inline bool is_free_of_live_var(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) { - switch (opline->opcode) { - case ZEND_FREE: - /* It is always safe to remove FREEs of non-refcounted values, even if they are live. */ - if (!(ctx->ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - return 0; - } - /* break missing intentionally */ - case ZEND_FE_FREE: - return !is_var_dead(ctx, ssa_op->op1_use); - default: - return 0; - } -} - -/* Returns whether the instruction has been DCEd */ -static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) { - zend_ssa *ssa = ctx->ssa; - int free_var = -1; - zend_uchar free_var_type; - - if (opline->opcode == ZEND_NOP) { - return 0; - } - - /* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */ - if (is_free_of_live_var(ctx, opline, ssa_op)) { - return 0; - } - - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) { - if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) { - if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF) - && opline->opcode != ZEND_CASE - && opline->opcode != ZEND_CASE_STRICT) { - free_var = ssa_op->op1_use; - free_var_type = opline->op1_type; - } - } - } - if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) { - if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) { - if (ssa->var_info[ssa_op->op2_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { - if (free_var >= 0) { - // TODO: We can't free two vars. Keep instruction alive. - zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes); - return 0; - } - free_var = ssa_op->op2_use; - free_var_type = opline->op2_type; - } - } - } - - zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op); - zend_ssa_remove_instr(ctx->ssa, opline, ssa_op); - - if (free_var >= 0) { - opline->opcode = ZEND_FREE; - opline->op1.var = EX_NUM_TO_VAR(ssa->vars[free_var].var); - opline->op1_type = free_var_type; - - ssa_op->op1_use = free_var; - ssa_op->op1_use_chain = ssa->vars[free_var].use_chain; - ssa->vars[free_var].use_chain = ssa_op - ssa->ops; - return 0; - } - return 1; -} - -static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) { - int common_source = -1; - int source; - FOREACH_PHI_SOURCE(phi, source) { - if (common_source == -1) { - common_source = source; - } else if (common_source != source && source != phi->ssa_var) { - return -1; - } - } FOREACH_PHI_SOURCE_END(); - ZEND_ASSERT(common_source != -1); - return common_source; -} - -static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) { - zend_ssa *ssa = ctx->ssa; - if (phi->pi < 0) { - /* Phi assignment with identical source operands */ - int common_source = get_common_phi_source(ssa, phi); - if (common_source >= 0) { - zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1); - zend_ssa_remove_phi(ssa, phi); - } - } else { - /* Pi assignment that is only used in Phi/Pi assignments */ - // TODO What if we want to rerun type inference after DCE? Maybe separate this? - /*ZEND_ASSERT(phi->sources[0] != -1); - if (ssa->vars[phi->ssa_var].use_chain < 0) { - zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1); - zend_ssa_remove_phi(ssa, phi); - }*/ - } -} - -static inline bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) { - if (ssa_op->op1_def >= 0 - && ssa->vars[ssa_op->op1_def].var < op_array->num_args) { - return 1; - } - if (ssa_op->op2_def >= 0 - && ssa->vars[ssa_op->op2_def].var < op_array->num_args) { - return 1; - } - if (ssa_op->result_def >= 0 - && ssa->vars[ssa_op->result_def].var < op_array->num_args) { - return 1; - } - return 0; -} - -int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects) { - int i; - zend_ssa_phi *phi; - int removed_ops = 0; - - /* DCE of CV operations that changes arguments may affect vararg functions. */ - bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0; - - context ctx; - ctx.ssa = ssa; - ctx.op_array = op_array; - ctx.reorder_dtor_effects = reorder_dtor_effects; - - /* We have no dedicated phi vector, so we use the whole ssa var vector instead */ - ctx.instr_worklist_len = zend_bitset_len(op_array->last); - ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len); - memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len); - ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count); - ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len); - memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len); - ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len); - memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len); - - /* Optimistically assume all instructions and phis to be dead */ - ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len); - memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len); - ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len); - memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len); - - /* Mark reacable instruction without side effects as dead */ - int b = ssa->cfg.blocks_count; - while (b > 0) { - int op_data = -1; - - b--; - zend_basic_block *block = &ssa->cfg.blocks[b]; - if (!(block->flags & ZEND_BB_REACHABLE)) { - continue; - } - i = block->start + block->len; - while (i > block->start) { - i--; - - if (op_array->opcodes[i].opcode == ZEND_OP_DATA) { - op_data = i; - continue; - } - - if (zend_bitset_in(ctx.instr_worklist, i)) { - zend_bitset_excl(ctx.instr_worklist, i); - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0); - if (op_data >= 0) { - add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0); - } - } else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects) - || zend_may_throw(&op_array->opcodes[i], &ssa->ops[i], op_array, ssa) - || (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) { - if (op_array->opcodes[i].opcode == ZEND_NEW - && op_array->opcodes[i+1].opcode == ZEND_DO_FCALL - && ssa->ops[i].result_def >= 0 - && ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { - zend_bitset_incl(ctx.instr_dead, i); - zend_bitset_incl(ctx.instr_dead, i+1); - } else { - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0); - if (op_data >= 0) { - add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0); - } - } - } else { - zend_bitset_incl(ctx.instr_dead, i); - if (op_data >= 0) { - zend_bitset_incl(ctx.instr_dead, op_data); - } - } - op_data = -1; - } - } - - /* Propagate liveness backwards to all definitions of used vars */ - while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len) - || !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) { - while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) { - zend_bitset_excl(ctx.instr_dead, i); - add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1); - if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) { - zend_bitset_excl(ctx.instr_dead, i+1); - add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1); - } - } - while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) { - zend_bitset_excl(ctx.phi_dead, i); - zend_bitset_excl(ctx.phi_worklist_no_val, i); - add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1); - } - } - - /* Eliminate dead instructions */ - ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) { - removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]); - } ZEND_BITSET_FOREACH_END(); - - /* Improper uses don't count as "uses" for the purpose of instruction elimination, - * but we have to retain phis defining them. - * Propagate this information backwards, marking any phi with an improperly used - * target as non-dead. */ - while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) { - zend_ssa_phi *phi = ssa->vars[i].definition_phi; - int source; - zend_bitset_excl(ctx.phi_dead, i); - FOREACH_PHI_SOURCE(phi, source) { - add_to_phi_worklist_no_val(&ctx, source); - } FOREACH_PHI_SOURCE_END(); - } - - /* Now collect the actually dead phis */ - FOREACH_PHI(phi) { - if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) { - zend_ssa_remove_uses_of_var(ssa, phi->ssa_var); - zend_ssa_remove_phi(ssa, phi); - } else { - /* Remove trivial phis (phis with identical source operands) */ - try_remove_trivial_phi(&ctx, phi); - } - } FOREACH_PHI_END(); - - return removed_ops; -} diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c deleted file mode 100644 index fe06de276b..0000000000 --- a/ext/opcache/Optimizer/dfa_pass.c +++ /dev/null @@ -1,1637 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" -#include "zend_bitset.h" -#include "zend_cfg.h" -#include "zend_ssa.h" -#include "zend_func_info.h" -#include "zend_call_graph.h" -#include "zend_inference.h" -#include "zend_dump.h" - -#ifndef ZEND_DEBUG_DFA -# define ZEND_DEBUG_DFA ZEND_DEBUG -#endif - -#if ZEND_DEBUG_DFA -# include "ssa_integrity.c" -#endif - -int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa) -{ - uint32_t build_flags; - - if (op_array->last_try_catch) { - /* TODO: we can't analyze functions with try/catch/finally ??? */ - return FAILURE; - } - - /* Build SSA */ - memset(ssa, 0, sizeof(zend_ssa)); - - if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg) != SUCCESS) { - return FAILURE; - } - - if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) { - /* TODO: we can't analyze functions with indirect variable access ??? */ - return FAILURE; - } - - if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) { - return FAILURE; - } - - if (ctx->debug_level & ZEND_DUMP_DFA_CFG) { - zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg); - } - - /* Compute Dominators Tree */ - if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) { - return FAILURE; - } - - /* Identify reducible and irreducible loops */ - if (zend_cfg_identify_loops(op_array, &ssa->cfg) != SUCCESS) { - return FAILURE; - } - - if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) { - zend_dump_dominators(op_array, &ssa->cfg); - } - - build_flags = 0; - if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) { - build_flags |= ZEND_SSA_DEBUG_LIVENESS; - } - if (ctx->debug_level & ZEND_DUMP_DFA_PHI) { - build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT; - } - if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa) != SUCCESS) { - return FAILURE; - } - - if (ctx->debug_level & ZEND_DUMP_DFA_SSA) { - zend_dump_op_array(op_array, ZEND_DUMP_SSA, "dfa ssa", ssa); - } - - - if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){ - return FAILURE; - } - - if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) { - return FAILURE; - } - - if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){ - return FAILURE; - } - - if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) != SUCCESS) { - return FAILURE; - } - - if (zend_ssa_escape_analysis(ctx->script, op_array, ssa) != SUCCESS) { - return FAILURE; - } - - if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) { - zend_dump_ssa_variables(op_array, ssa, 0); - } - - 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 *blocks_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 < blocks_end; b++) { - if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) { - if (b->len) { - uint32_t new_start, old_end; - 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; - } - - new_start = target; - old_end = b->start + b->len; - while (i < old_end) { - shiftlist[i] = i - target; - if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP)) { - 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++; - } - b->start = new_start; - if (target != old_end) { - zend_op *opline; - zend_op *new_opline; - - b->len = target - b->start; - opline = op_array->opcodes + old_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 < blocks_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]; - if (call_info->caller_call_opline) { - 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 bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { - if (ce1 == ce2) { - return 1; - } - if (!(ce1->ce_flags & ZEND_ACC_LINKED)) { - /* This case could be generalized, similarly to unlinked_instanceof */ - return 0; - } - return instanceof_function(ce1, ce2); -} - -static inline bool can_elide_return_type_check( - zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) { - zend_arg_info *info = &op_array->arg_info[-1]; - zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use]; - zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def]; - - /* TODO: It would be better to rewrite this without using def_info, - * which may not be an exact representation of the type. */ - if (use_info->type & MAY_BE_REF) { - return 0; - } - - /* A type is possible that is not in the allowed types */ - if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) { - return 0; - } - - /* These types are not represented exactly */ - if (ZEND_TYPE_FULL_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE|MAY_BE_STATIC)) { - return 0; - } - - if (ZEND_TYPE_HAS_CLASS(info->type)) { - if (!use_info->ce || !def_info->ce || !safe_instanceof(use_info->ce, def_info->ce)) { - return 0; - } - } - - return 1; -} - -static bool opline_supports_assign_contraction( - zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) { - if (opline->opcode == ZEND_NEW) { - /* see Zend/tests/generators/aborted_yield_during_new.phpt */ - return 0; - } - - if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL - || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) { - /* Function calls may dtor the return value after it has already been written -- allow - * direct assignment only for types where a double-dtor does not matter. */ - uint32_t type = ssa->var_info[src_var].type; - uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE; - return !((type & MAY_BE_ANY) & ~simple); - } - - if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) { - /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++ - * eliding the temporary variable would thus yield an incorrect result. */ - return opline->op1_type != IS_CV || opline->op1.var != cv_var; - } - - if (opline->opcode == ZEND_INIT_ARRAY) { - /* INIT_ARRAY initializes the result array before reading key/value. */ - return (opline->op1_type != IS_CV || opline->op1.var != cv_var) - && (opline->op2_type != IS_CV || opline->op2.var != cv_var); - } - - if (opline->opcode == ZEND_CAST - && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) { - /* CAST to array/object may initialize the result to an empty array/object before - * reading the expression. */ - return opline->op1_type != IS_CV || opline->op1.var != cv_var; - } - - return 1; -} - -int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) -{ - zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - int removed_ops = 0; - - if (func_info->callee_info) { - zend_call_info *call_info = func_info->callee_info; - - do { - if (call_info->caller_call_opline - && call_info->caller_call_opline->opcode == ZEND_DO_ICALL - && call_info->callee_func - && zend_string_equals_literal(call_info->callee_func->common.function_name, "in_array") - && (call_info->caller_init_opline->extended_value == 2 - || (call_info->caller_init_opline->extended_value == 3 - && (call_info->caller_call_opline - 1)->opcode == ZEND_SEND_VAL - && (call_info->caller_call_opline - 1)->op1_type == IS_CONST))) { - - zend_op *send_array; - zend_op *send_needly; - bool strict = 0; - - if (call_info->caller_init_opline->extended_value == 2) { - send_array = call_info->caller_call_opline - 1; - send_needly = call_info->caller_call_opline - 2; - } else { - if (zend_is_true(CT_CONSTANT_EX(op_array, (call_info->caller_call_opline - 1)->op1.constant))) { - strict = 1; - } - send_array = call_info->caller_call_opline - 2; - send_needly = call_info->caller_call_opline - 3; - } - - if (send_array->opcode == ZEND_SEND_VAL - && send_array->op1_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)) == IS_ARRAY - && (send_needly->opcode == ZEND_SEND_VAL - || send_needly->opcode == ZEND_SEND_VAR) - ) { - int ok = 1; - - HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, send_array->op1.constant)); - HashTable *dst; - zval *val, tmp; - zend_ulong idx; - - ZVAL_TRUE(&tmp); - dst = zend_new_array(zend_hash_num_elements(src)); - if (strict) { - ZEND_HASH_FOREACH_VAL(src, val) { - if (Z_TYPE_P(val) == IS_STRING) { - zend_hash_add(dst, Z_STR_P(val), &tmp); - } else if (Z_TYPE_P(val) == IS_LONG) { - zend_hash_index_add(dst, Z_LVAL_P(val), &tmp); - } else { - zend_array_destroy(dst); - ok = 0; - break; - } - } ZEND_HASH_FOREACH_END(); - } else { - ZEND_HASH_FOREACH_VAL(src, val) { - if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) { - zend_array_destroy(dst); - ok = 0; - break; - } - zend_hash_add(dst, Z_STR_P(val), &tmp); - } ZEND_HASH_FOREACH_END(); - } - - if (ok) { - uint32_t op_num = send_needly - op_array->opcodes; - zend_ssa_op *ssa_op = ssa->ops + op_num; - - if (ssa_op->op1_use >= 0) { - /* Reconstruct SSA */ - int var_num = ssa_op->op1_use; - zend_ssa_var *var = ssa->vars + var_num; - - ZEND_ASSERT(ssa_op->op1_def < 0); - zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); - ssa_op->op1_use = -1; - ssa_op->op1_use_chain = -1; - op_num = call_info->caller_call_opline - op_array->opcodes; - ssa_op = ssa->ops + op_num; - ssa_op->op1_use = var_num; - ssa_op->op1_use_chain = var->use_chain; - var->use_chain = op_num; - } - - ZVAL_ARR(&tmp, dst); - - /* Update opcode */ - call_info->caller_call_opline->opcode = ZEND_IN_ARRAY; - call_info->caller_call_opline->extended_value = strict; - call_info->caller_call_opline->op1_type = send_needly->op1_type; - call_info->caller_call_opline->op1.num = send_needly->op1.num; - call_info->caller_call_opline->op2_type = IS_CONST; - call_info->caller_call_opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - if (call_info->caller_init_opline->extended_value == 3) { - MAKE_NOP(call_info->caller_call_opline - 1); - } - MAKE_NOP(call_info->caller_init_opline); - MAKE_NOP(send_needly); - MAKE_NOP(send_array); - removed_ops++; - - op_num = call_info->caller_call_opline - op_array->opcodes; - ssa_op = ssa->ops + op_num; - if (ssa_op->result_def >= 0) { - int var = ssa_op->result_def; - int use = ssa->vars[var].use_chain; - - /* If the result is used only in a JMPZ/JMPNZ, replace result type with - * IS_TMP_VAR, which will enable use of smart branches. Don't do this - * in other cases, as not all opcodes support both VAR and TMP. */ - if (ssa->vars[var].phi_use_chain == NULL - && ssa->ops[use].op1_use == var - && ssa->ops[use].op1_use_chain == -1 - && (op_array->opcodes[use].opcode == ZEND_JMPZ - || op_array->opcodes[use].opcode == ZEND_JMPNZ)) { - call_info->caller_call_opline->result_type = IS_TMP_VAR; - op_array->opcodes[use].op1_type = IS_TMP_VAR; - } - } - } - } - } - call_info = call_info->next_callee; - } while (call_info); - } - - return removed_ops; -} - -static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, zend_basic_block *block) -{ - if (block->successors_count == 2) { - if (block->successors[1] != block->successors[0]) { - zend_ssa_remove_predecessor(ssa, block_num, block->successors[1]); - } - block->successors_count = 1; - } -} - -static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, zend_basic_block *block) -{ - if (block->successors_count == 2) { - if (block->successors[1] != block->successors[0]) { - zend_ssa_remove_predecessor(ssa, block_num, block->successors[0]); - block->successors[0] = block->successors[1]; - } - block->successors_count = 1; - } -} - -static zend_always_inline void take_successor_ex(zend_ssa *ssa, int block_num, zend_basic_block *block, int target_block) -{ - int i; - - for (i = 0; i < block->successors_count; i++) { - if (block->successors[i] != target_block) { - zend_ssa_remove_predecessor(ssa, block_num, block->successors[i]); - } - } - block->successors[0] = target_block; - block->successors_count = 1; -} - -static void compress_block(zend_op_array *op_array, zend_basic_block *block) -{ - while (block->len > 0) { - zend_op *opline = &op_array->opcodes[block->start + block->len - 1]; - - if (opline->opcode == ZEND_NOP) { - block->len--; - } else { - break; - } - } -} - -static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int new_pred) { - zend_basic_block *block = &ssa->cfg.blocks[block_id]; - int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset]; - zend_ssa_phi *phi; - - int i; - int old_pred_idx = -1; - int new_pred_idx = -1; - for (i = 0; i < block->predecessors_count; i++) { - if (predecessors[i] == old_pred) { - old_pred_idx = i; - } - if (predecessors[i] == new_pred) { - new_pred_idx = i; - } - } - - ZEND_ASSERT(old_pred_idx != -1); - if (new_pred_idx == -1) { - /* If the new predecessor doesn't exist yet, simply rewire the old one */ - predecessors[old_pred_idx] = new_pred; - } else { - /* Otherwise, rewiring the old predecessor would make the new predecessor appear - * twice, which violates our CFG invariants. Remove the old predecessor instead. */ - memmove( - predecessors + old_pred_idx, - predecessors + old_pred_idx + 1, - sizeof(int) * (block->predecessors_count - old_pred_idx - 1) - ); - - /* Also remove the corresponding phi node entries */ - for (phi = ssa->blocks[block_id].phis; phi; phi = phi->next) { - memmove( - phi->sources + old_pred_idx, - phi->sources + old_pred_idx + 1, - sizeof(int) * (block->predecessors_count - old_pred_idx - 1) - ); - } - - block->predecessors_count--; - } -} - -static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa, int from, int to, int new_to) -{ - zend_basic_block *src = &ssa->cfg.blocks[from]; - zend_basic_block *old = &ssa->cfg.blocks[to]; - zend_basic_block *dst = &ssa->cfg.blocks[new_to]; - int i; - zend_op *opline; - - for (i = 0; i < src->successors_count; i++) { - if (src->successors[i] == to) { - src->successors[i] = new_to; - } - } - - if (src->len > 0) { - opline = op_array->opcodes + src->start + src->len - 1; - switch (opline->opcode) { - case ZEND_JMP: - case ZEND_FAST_CALL: - ZEND_ASSERT(ZEND_OP1_JMP_ADDR(opline) == op_array->opcodes + old->start); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + dst->start); - break; - case ZEND_JMPZNZ: - if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) { - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start); - } - /* 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: - case ZEND_JMP_NULL: - if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) { - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start); - } - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) { - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start); - } - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) { - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start); - } - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); - zval *zv; - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) == old->start) { - Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start); - } - } ZEND_HASH_FOREACH_END(); - if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) { - opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start); - } - break; - } - } - } - - replace_predecessor(ssa, new_to, to, from); -} - -static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, int block_num) -{ - if (block->predecessors_count == 1 && ssa->blocks[block_num].phis == NULL) { - int *predecessors, i; - - ZEND_ASSERT(block->successors_count == 1); - predecessors = &ssa->cfg.predecessors[block->predecessor_offset]; - for (i = 0; i < block->predecessors_count; i++) { - zend_ssa_replace_control_link(op_array, ssa, predecessors[i], block_num, block->successors[0]); - } - zend_ssa_remove_block(op_array, ssa, block_num); - } -} - -static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) -{ - int removed_ops = 0; - int block_num = 0; - - for (block_num = 1; block_num < ssa->cfg.blocks_count; block_num++) { - zend_basic_block *block = &ssa->cfg.blocks[block_num]; - - if (!(block->flags & ZEND_BB_REACHABLE)) { - continue; - } - compress_block(op_array, block); - if (block->len == 0) { - zend_ssa_unlink_block(op_array, ssa, block, block_num); - } - } - - block_num = 0; - while (block_num < ssa->cfg.blocks_count - && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)) { - block_num++; - } - while (block_num < ssa->cfg.blocks_count) { - int next_block_num = block_num + 1; - zend_basic_block *block = &ssa->cfg.blocks[block_num]; - uint32_t op_num; - zend_op *opline; - zend_ssa_op *ssa_op; - - while (next_block_num < ssa->cfg.blocks_count - && !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) { - next_block_num++; - } - - if (block->len) { - op_num = block->start + block->len - 1; - opline = op_array->opcodes + op_num; - ssa_op = ssa->ops + op_num; - - switch (opline->opcode) { - case ZEND_JMP: -optimize_jmp: - if (block->successors[0] == next_block_num) { - MAKE_NOP(opline); - removed_ops++; - goto optimize_nop; - } - break; - case ZEND_JMPZ: -optimize_jmpz: - if (opline->op1_type == IS_CONST) { - if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - MAKE_NOP(opline); - removed_ops++; - take_successor_1(ssa, block_num, block); - goto optimize_nop; - } else { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - take_successor_0(ssa, block_num, block); - goto optimize_jmp; - } - } else { - if (block->successors[0] == next_block_num) { - take_successor_0(ssa, block_num, block); - if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { - opline->opcode = ZEND_CHECK_VAR; - opline->op2.num = 0; - } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - zend_ssa_remove_instr(ssa, opline, ssa_op); - removed_ops++; - goto optimize_nop; - } else { - opline->opcode = ZEND_FREE; - opline->op2.num = 0; - } - } - } - break; - case ZEND_JMPNZ: -optimize_jmpnz: - if (opline->op1_type == IS_CONST) { - if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - take_successor_0(ssa, block_num, block); - goto optimize_jmp; - } else { - MAKE_NOP(opline); - removed_ops++; - take_successor_1(ssa, block_num, block); - goto optimize_nop; - } - } else if (block->successors_count == 2) { - if (block->successors[0] == next_block_num) { - take_successor_0(ssa, block_num, block); - if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { - opline->opcode = ZEND_CHECK_VAR; - opline->op2.num = 0; - } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - zend_ssa_remove_instr(ssa, opline, ssa_op); - removed_ops++; - goto optimize_nop; - } else { - opline->opcode = ZEND_FREE; - opline->op2.num = 0; - } - } - } - break; - case ZEND_JMPZNZ: - if (opline->op1_type == IS_CONST) { - if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - take_successor_1(ssa, block_num, block); - } else { - zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - take_successor_0(ssa, block_num, block); - } - opline->op1_type = IS_UNUSED; - opline->extended_value = 0; - opline->opcode = ZEND_JMP; - goto optimize_jmp; - } else if (block->successors_count == 2) { - if (block->successors[0] == block->successors[1]) { - take_successor_0(ssa, block_num, block); - if (block->successors[0] == next_block_num) { - if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { - opline->opcode = ZEND_CHECK_VAR; - opline->op2.num = 0; - } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - zend_ssa_remove_instr(ssa, opline, ssa_op); - removed_ops++; - goto optimize_nop; - } else { - opline->opcode = ZEND_FREE; - opline->op2.num = 0; - } - } else if ((opline->op1_type == IS_CV && !(OP1_INFO() & MAY_BE_UNDEF)) || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - ZEND_ASSERT(ssa_op->op1_use >= 0); - zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use); - ssa_op->op1_use = -1; - ssa_op->op1_use_chain = -1; - opline->opcode = ZEND_JMP; - opline->op1_type = IS_UNUSED; - opline->op1.num = opline->op2.num; - goto optimize_jmp; - } - } - } - break; - case ZEND_JMPZ_EX: - if (ssa->vars[ssa_op->result_def].use_chain < 0 - && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - opline->opcode = ZEND_JMPZ; - opline->result_type = IS_UNUSED; - zend_ssa_remove_result_def(ssa, ssa_op); - goto optimize_jmpz; - } else if (opline->op1_type == IS_CONST) { - if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - opline->opcode = ZEND_QM_ASSIGN; - take_successor_1(ssa, block_num, block); - } - } - break; - case ZEND_JMPNZ_EX: - if (ssa->vars[ssa_op->result_def].use_chain < 0 - && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - opline->opcode = ZEND_JMPNZ; - opline->result_type = IS_UNUSED; - zend_ssa_remove_result_def(ssa, ssa_op); - goto optimize_jmpnz; - } else if (opline->op1_type == IS_CONST) { - if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - opline->opcode = ZEND_QM_ASSIGN; - take_successor_1(ssa, block_num, block); - } - } - break; - case ZEND_JMP_SET: - if (ssa->vars[ssa_op->result_def].use_chain < 0 - && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - opline->opcode = ZEND_JMPNZ; - opline->result_type = IS_UNUSED; - zend_ssa_remove_result_def(ssa, ssa_op); - goto optimize_jmpnz; - } else if (opline->op1_type == IS_CONST) { - if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) { - MAKE_NOP(opline); - removed_ops++; - take_successor_1(ssa, block_num, block); - zend_ssa_remove_result_def(ssa, ssa_op); - goto optimize_nop; - } - } - break; - case ZEND_COALESCE: - { - zend_ssa_var *var = &ssa->vars[ssa_op->result_def]; - if (opline->op1_type == IS_CONST - && var->use_chain < 0 && var->phi_use_chain == NULL) { - if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) { - zend_ssa_remove_result_def(ssa, ssa_op); - MAKE_NOP(opline); - removed_ops++; - take_successor_1(ssa, block_num, block); - goto optimize_nop; - } else { - opline->opcode = ZEND_JMP; - opline->result_type = IS_UNUSED; - zend_ssa_remove_result_def(ssa, ssa_op); - COPY_NODE(opline->op1, opline->op2); - take_successor_0(ssa, block_num, block); - goto optimize_jmp; - } - } - break; - } - case ZEND_JMP_NULL: - { - zend_ssa_var *var = &ssa->vars[ssa_op->result_def]; - if (opline->op1_type == IS_CONST - && var->use_chain < 0 && var->phi_use_chain == NULL) { - if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) { - opline->opcode = ZEND_JMP; - opline->result_type = IS_UNUSED; - zend_ssa_remove_result_def(ssa, ssa_op); - COPY_NODE(opline->op1, opline->op2); - take_successor_0(ssa, block_num, block); - goto optimize_jmp; - } else { - zend_ssa_remove_result_def(ssa, ssa_op); - MAKE_NOP(opline); - removed_ops++; - take_successor_1(ssa, block_num, block); - goto optimize_nop; - } - } - break; - } - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - if (opline->op1_type == IS_CONST) { - zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - zend_uchar type = Z_TYPE_P(zv); - bool correct_type = - (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG) - || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING) - || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING)); - - if (!correct_type) { - removed_ops++; - MAKE_NOP(opline); - opline->extended_value = 0; - take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]); - goto optimize_nop; - } else { - HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); - zval *jmp_zv = type == IS_LONG - ? zend_hash_index_find(jmptable, Z_LVAL_P(zv)) - : zend_hash_find(jmptable, Z_STR_P(zv)); - - uint32_t target; - if (jmp_zv) { - target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv)); - } else { - target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value); - } - opline->opcode = ZEND_JMP; - opline->extended_value = 0; - SET_UNUSED(opline->op1); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target); - SET_UNUSED(opline->op2); - take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]); - goto optimize_jmp; - } - } - break; - case ZEND_NOP: -optimize_nop: - compress_block(op_array, block); - if (block->len == 0) { - if (block_num > 0) { - zend_ssa_unlink_block(op_array, ssa, block, block_num); - /* backtrack to previous basic block */ - do { - block_num--; - } while (block_num >= 0 - && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)); - if (block_num >= 0) { - continue; - } - } - } - break; - default: - break; - } - } - - block_num = next_block_num; - } - - return removed_ops; -} - -static int zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ssa, int def, int cv_var) -{ - int result_var = ssa->ops[def].result_def; - int cv = EX_NUM_TO_VAR(ssa->vars[cv_var].var); - - if (result_var >= 0 - && !(ssa->var_info[cv_var].type & MAY_BE_REF) - && ssa->vars[cv_var].alias == NO_ALIAS - && ssa->vars[result_var].phi_use_chain == NULL - && ssa->vars[result_var].sym_use_chain == NULL) { - int use = ssa->vars[result_var].use_chain; - - if (use >= 0 - && zend_ssa_next_use(ssa->ops, result_var, use) < 0 - && op_array->opcodes[use].opcode != ZEND_FREE - && op_array->opcodes[use].opcode != ZEND_SEND_VAL - && op_array->opcodes[use].opcode != ZEND_SEND_VAL_EX - && op_array->opcodes[use].opcode != ZEND_VERIFY_RETURN_TYPE) { - if (use > def) { - int i = use; - const zend_op *opline = &op_array->opcodes[use]; - - while (i > def) { - if ((opline->op1_type == IS_CV && opline->op1.var == cv) - || (opline->op2_type == IS_CV && opline->op2.var == cv) - || (opline->result_type == IS_CV && opline->result.var == cv)) { - return 0; - } - opline--; - i--; - } - - /* Update opcodes and reconstruct SSA */ - ssa->vars[result_var].definition = -1; - ssa->vars[result_var].use_chain = -1; - ssa->ops[def].result_def = -1; - - op_array->opcodes[def].result_type = IS_UNUSED; - op_array->opcodes[def].result.var = 0; - - if (ssa->ops[use].op1_use == result_var) { - ssa->ops[use].op1_use = cv_var; - ssa->ops[use].op1_use_chain = ssa->vars[cv_var].use_chain; - ssa->vars[cv_var].use_chain = use; - - op_array->opcodes[use].op1_type = IS_CV; - op_array->opcodes[use].op1.var = cv; - } else if (ssa->ops[use].op2_use == result_var) { - ssa->ops[use].op2_use = cv_var; - ssa->ops[use].op2_use_chain = ssa->vars[cv_var].use_chain; - ssa->vars[cv_var].use_chain = use; - - op_array->opcodes[use].op2_type = IS_CV; - op_array->opcodes[use].op2.var = cv; - } else if (ssa->ops[use].result_use == result_var) { - ssa->ops[use].result_use = cv_var; - ssa->ops[use].res_use_chain = ssa->vars[cv_var].use_chain; - ssa->vars[cv_var].use_chain = use; - - op_array->opcodes[use].result_type = IS_CV; - op_array->opcodes[use].result.var = cv; - } - - return 1; - } - } - } - - return 0; -} - -void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map) -{ - if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) { - zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa); - } - - if (ssa->var_info) { - int op_1; - int v; - int remove_nops = 0; - zend_op *opline; - zend_ssa_op *ssa_op; - zval tmp; - -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "before dfa"); -#endif - - if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) { - if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) { - remove_nops = 1; - } - - if (zend_dfa_optimize_jmps(op_array, ssa)) { - remove_nops = 1; - } - -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after sccp"); -#endif - if (ZEND_FUNC_INFO(op_array)) { - if (zend_dfa_optimize_calls(op_array, ssa)) { - remove_nops = 1; - } - } - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_8) { - zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa); - } -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after calls"); -#endif - } - - if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) { - if (dce_optimize_op_array(op_array, ssa, 0)) { - remove_nops = 1; - } - if (zend_dfa_optimize_jmps(op_array, ssa)) { - remove_nops = 1; - } - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) { - zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa); - } -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after dce"); -#endif - } - - for (v = op_array->last_var; v < ssa->vars_count; v++) { - - op_1 = ssa->vars[v].definition; - - if (op_1 < 0) { - continue; - } - - opline = op_array->opcodes + op_1; - ssa_op = &ssa->ops[op_1]; - - /* Convert LONG constants to DOUBLE */ - if (ssa->var_info[v].use_as_double) { - if (opline->opcode == ZEND_ASSIGN - && opline->op2_type == IS_CONST - && ssa->ops[op_1].op1_def == v - && !RETURN_VALUE_USED(opline) - ) { - -// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?) - - zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); - ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - - } else if (opline->opcode == ZEND_QM_ASSIGN - && opline->op1_type == IS_CONST - ) { - -// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?) - - zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); - } - - } else { - if (opline->opcode == ZEND_ADD - || opline->opcode == ZEND_SUB - || opline->opcode == ZEND_MUL - || opline->opcode == ZEND_IS_EQUAL - || opline->opcode == ZEND_IS_NOT_EQUAL - || opline->opcode == ZEND_IS_SMALLER - || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL - ) { - - if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST) { - zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - - if ((OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE - && Z_TYPE_INFO_P(zv) == IS_LONG) { - -// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double] - - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); - zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - } - if (opline->opcode == ZEND_ADD) { - zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - - if (((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG - && Z_TYPE_INFO_P(zv) == IS_LONG - && Z_LVAL_P(zv) == 0) - || ((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE - && Z_TYPE_INFO_P(zv) == IS_DOUBLE - && Z_DVAL_P(zv) == 0.0)) { - -// op_1: #v.? = ADD 0, #?.? [double,long] => #v.? = QM_ASSIGN #?.? - - opline->opcode = ZEND_QM_ASSIGN; - opline->op1_type = opline->op2_type; - opline->op1.var = opline->op2.var; - opline->op2_type = IS_UNUSED; - opline->op2.num = 0; - ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use; - ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain; - ssa->ops[op_1].op2_use = -1; - ssa->ops[op_1].op2_use_chain = -1; - } - } - } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST) { - zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); - - if ((OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE - && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) { - -// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?) - - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - zv = CT_CONSTANT_EX(op_array, opline->op2.constant); - } - if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB) { - if (((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG - && Z_TYPE_INFO_P(zv) == IS_LONG - && Z_LVAL_P(zv) == 0) - || ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE - && Z_TYPE_INFO_P(zv) == IS_DOUBLE - && Z_DVAL_P(zv) == 0.0)) { - -// op_1: #v.? = ADD #?.? [double,long], 0 => #v.? = QM_ASSIGN #?.? - - opline->opcode = ZEND_QM_ASSIGN; - opline->op2_type = IS_UNUSED; - opline->op2.num = 0; - } - } - } - } else if (opline->opcode == ZEND_CONCAT) { - if (!(OP1_INFO() & MAY_BE_OBJECT) - && !(OP2_INFO() & MAY_BE_OBJECT)) { - opline->opcode = ZEND_FAST_CONCAT; - } - } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE - && opline->op1_type != IS_CONST - && ssa->ops[op_1].op1_def == v - && ssa->ops[op_1].op1_use >= 0 - && ssa->ops[op_1].op1_use_chain == -1 - && ssa->vars[v].use_chain >= 0 - && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) { - -// op_1: VERIFY_RETURN_TYPE #orig_var.? [T] -> #v.? [T] => NOP - - int orig_var = ssa->ops[op_1].op1_use; - if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { - - int ret = ssa->vars[v].use_chain; - - ssa->ops[ret].op1_use = orig_var; - ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain; - ssa->vars[orig_var].use_chain = ret; - - ssa->vars[v].definition = -1; - ssa->vars[v].use_chain = -1; - - ssa->ops[op_1].op1_def = -1; - ssa->ops[op_1].op1_use = -1; - - MAKE_NOP(opline); - remove_nops = 1; - } - } - } - - if (opline->opcode == ZEND_QM_ASSIGN - && ssa->ops[op_1].result_def == v - && opline->op1_type & (IS_TMP_VAR|IS_VAR) - && !(ssa->var_info[v].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - ) { - - int src_var = ssa->ops[op_1].op1_use; - - if (src_var >= 0 - && !(ssa->var_info[src_var].type & MAY_BE_REF) - && ssa->vars[src_var].definition >= 0 - && ssa->ops[ssa->vars[src_var].definition].result_def == src_var - && ssa->ops[ssa->vars[src_var].definition].result_use < 0 - && ssa->vars[src_var].use_chain == op_1 - && ssa->ops[op_1].op1_use_chain < 0 - && !ssa->vars[src_var].phi_use_chain - && !ssa->vars[src_var].sym_use_chain - && opline_supports_assign_contraction( - ssa, &op_array->opcodes[ssa->vars[src_var].definition], - src_var, opline->result.var) - ) { - - int orig_var = ssa->ops[op_1].result_use; - int op_2 = ssa->vars[src_var].definition; - -// op_2: #src_var.T = OP ... => #v.CV = OP ... -// op_1: QM_ASSIGN #src_var.T #orig_var.CV [undef,scalar] -> #v.CV, NOP - - if (orig_var < 0 || zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { - /* Reconstruct SSA */ - ssa->vars[v].definition = op_2; - ssa->ops[op_2].result_def = v; - - ssa->vars[src_var].definition = -1; - ssa->vars[src_var].use_chain = -1; - - ssa->ops[op_1].op1_use = -1; - ssa->ops[op_1].op1_def = -1; - ssa->ops[op_1].op1_use_chain = -1; - ssa->ops[op_1].result_use = -1; - ssa->ops[op_1].result_def = -1; - ssa->ops[op_1].res_use_chain = -1; - - /* Update opcodes */ - op_array->opcodes[op_2].result_type = opline->result_type; - op_array->opcodes[op_2].result.var = opline->result.var; - - MAKE_NOP(opline); - remove_nops = 1; - - if (op_array->opcodes[op_2].opcode == ZEND_SUB - && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1 - && ssa->ops[op_2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_DEC; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - - } else if (op_array->opcodes[op_2].opcode == ZEND_ADD - && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1 - && ssa->ops[op_2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_INC; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - - } else if (op_array->opcodes[op_2].opcode == ZEND_ADD - && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op1_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1 - && ssa->ops[op_2].op2_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_INC; - op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type; - op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use; - ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain; - ssa->ops[op_2].op2_use = -1; - ssa->ops[op_2].op2_use_chain = -1; - } - } - } - } - - if (ssa->vars[v].var >= op_array->last_var) { - /* skip TMP and VAR */ - continue; - } - - if (ssa->ops[op_1].op1_def == v - && RETURN_VALUE_USED(opline)) { - if (opline->opcode == ZEND_ASSIGN - || opline->opcode == ZEND_ASSIGN_OP - || opline->opcode == ZEND_PRE_INC - || opline->opcode == ZEND_PRE_DEC) { - zend_dfa_try_to_replace_result(op_array, ssa, op_1, v); - } else if (opline->opcode == ZEND_POST_INC) { - int result_var = ssa->ops[op_1].result_def; - - if (result_var >= 0 - && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) { - int use = ssa->vars[result_var].use_chain; - - if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER - && ssa->ops[use].op1_use == result_var - && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) { - opline->opcode = ZEND_PRE_INC; - op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL; - } - } - } else if (opline->opcode == ZEND_POST_DEC) { - int result_var = ssa->ops[op_1].result_def; - - if (result_var >= 0 - && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) { - int use = ssa->vars[result_var].use_chain; - - if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER - && ssa->ops[use].op2_use == result_var - && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) { - opline->opcode = ZEND_PRE_DEC; - op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL; - } - } - } - } - - if (opline->opcode == ZEND_ASSIGN - && ssa->ops[op_1].op1_def == v - && !RETURN_VALUE_USED(opline) - ) { - int orig_var = ssa->ops[op_1].op1_use; - - if (orig_var >= 0 - && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - ) { - int src_var = ssa->ops[op_1].op2_use; - - if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) - && src_var >= 0 - && !(ssa->var_info[src_var].type & MAY_BE_REF) - && ssa->vars[src_var].definition >= 0 - && ssa->ops[ssa->vars[src_var].definition].result_def == src_var - && ssa->ops[ssa->vars[src_var].definition].result_use < 0 - && ssa->vars[src_var].use_chain == op_1 - && ssa->ops[op_1].op2_use_chain < 0 - && !ssa->vars[src_var].phi_use_chain - && !ssa->vars[src_var].sym_use_chain - && opline_supports_assign_contraction( - ssa, &op_array->opcodes[ssa->vars[src_var].definition], - src_var, opline->op1.var) - ) { - - int op_2 = ssa->vars[src_var].definition; - -// op_2: #src_var.T = OP ... => #v.CV = OP ... -// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T NOP - - if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { - /* Reconstruct SSA */ - ssa->vars[v].definition = op_2; - ssa->ops[op_2].result_def = v; - - ssa->vars[src_var].definition = -1; - ssa->vars[src_var].use_chain = -1; - - ssa->ops[op_1].op1_use = -1; - ssa->ops[op_1].op2_use = -1; - ssa->ops[op_1].op1_def = -1; - ssa->ops[op_1].op1_use_chain = -1; - - /* Update opcodes */ - op_array->opcodes[op_2].result_type = opline->op1_type; - op_array->opcodes[op_2].result.var = opline->op1.var; - - MAKE_NOP(opline); - remove_nops = 1; - - if (op_array->opcodes[op_2].opcode == ZEND_SUB - && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1 - && ssa->ops[op_2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_DEC; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - - } else if (op_array->opcodes[op_2].opcode == ZEND_ADD - && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1 - && ssa->ops[op_2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_INC; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - - } else if (op_array->opcodes[op_2].opcode == ZEND_ADD - && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type - && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var - && op_array->opcodes[op_2].op1_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1 - && ssa->ops[op_2].op2_use >= 0 - && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - - op_array->opcodes[op_2].opcode = ZEND_PRE_INC; - op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type; - op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var; - SET_UNUSED(op_array->opcodes[op_2].op2); - SET_UNUSED(op_array->opcodes[op_2].result); - - ssa->ops[op_2].result_def = -1; - ssa->ops[op_2].op1_def = v; - ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use; - ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain; - ssa->ops[op_2].op2_use = -1; - ssa->ops[op_2].op2_use_chain = -1; - } - } - } else if (opline->op2_type == IS_CONST - || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) - && ssa->ops[op_1].op2_use >= 0 - && ssa->ops[op_1].op2_def < 0) - ) { - -// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR - - if (ssa->ops[op_1].op1_use != ssa->ops[op_1].op2_use) { - zend_ssa_unlink_use_chain(ssa, op_1, orig_var); - } else { - ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain; - } - - /* Reconstruct SSA */ - ssa->ops[op_1].result_def = v; - ssa->ops[op_1].op1_def = -1; - ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use; - ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain; - ssa->ops[op_1].op2_use = -1; - ssa->ops[op_1].op2_use_chain = -1; - - /* Update opcode */ - opline->result_type = opline->op1_type; - opline->result.var = opline->op1.var; - opline->op1_type = opline->op2_type; - opline->op1.var = opline->op2.var; - opline->op2_type = IS_UNUSED; - opline->op2.var = 0; - opline->opcode = ZEND_QM_ASSIGN; - } - } - - } else if (opline->opcode == ZEND_ASSIGN_OP - && opline->extended_value == ZEND_ADD - && ssa->ops[op_1].op1_def == v - && opline->op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 - && ssa->ops[op_1].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - -// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV - - opline->opcode = ZEND_PRE_INC; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - - } else if (opline->opcode == ZEND_ASSIGN_OP - && opline->extended_value == ZEND_SUB - && ssa->ops[op_1].op1_def == v - && opline->op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 - && ssa->ops[op_1].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - -// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV - - opline->opcode = ZEND_PRE_DEC; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - - } else if (ssa->ops[op_1].op1_def == v - && !RETURN_VALUE_USED(opline) - && ssa->ops[op_1].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - && opline->opcode == ZEND_ASSIGN_OP - && opline->extended_value != ZEND_CONCAT) { - -// op_1: ASSIGN_OP #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ? - - /* Reconstruct SSA */ - ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def; - ssa->ops[op_1].op1_def = -1; - - /* Update opcode */ - opline->opcode = opline->extended_value; - opline->extended_value = 0; - opline->result_type = opline->op1_type; - opline->result.var = opline->op1.var; - - } - } - -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after dfa"); -#endif - - if (remove_nops) { - zend_ssa_remove_nops(op_array, ssa, ctx); -#if ZEND_DEBUG_DFA - ssa_verify_integrity(op_array, ssa, "after nop"); -#endif - } - } - - if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) { - zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa); - } -} - -void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - void *checkpoint = zend_arena_checkpoint(ctx->arena); - zend_ssa ssa; - - if (zend_dfa_analyze_op_array(op_array, ctx, &ssa) != SUCCESS) { - zend_arena_release(&ctx->arena, checkpoint); - return; - } - - zend_dfa_optimize_op_array(op_array, ctx, &ssa, NULL); - - /* Destroy SSA */ - zend_arena_release(&ctx->arena, checkpoint); -} diff --git a/ext/opcache/Optimizer/escape_analysis.c b/ext/opcache/Optimizer/escape_analysis.c deleted file mode 100644 index c0d5081c1f..0000000000 --- a/ext/opcache/Optimizer/escape_analysis.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache, Escape Analysis | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_bitset.h" -#include "zend_cfg.h" -#include "zend_ssa.h" -#include "zend_inference.h" -#include "zend_dump.h" - -/* - * T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic - * compilation and deoptimization. In Proceedings of the International - * Conference on Virtual Execution Environments, pages 111-120, Chicago, - * June 2005 - */ - -static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */ -{ - int i; - - for (i = 0; i < count; i++) { - parent[i] = i; - size[i] = 1; - } -} -/* }}} */ - -static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */ -{ - int p = parent[i]; - - while (i != p) { - p = parent[p]; - parent[i] = p; - i = p; - p = parent[i]; - } - return i; -} -/* }}} */ - -static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */ -{ - int r1 = union_find_root(parent, i); - int r2 = union_find_root(parent, j); - - if (r1 != r2) { - if (size[r1] < size[r2]) { - parent[r1] = r2; - size[r2] += size[r1]; - } else { - parent[r2] = r1; - size[r1] += size[r2]; - } - } -} -/* }}} */ - -static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - zend_ssa_var *ssa_vars = ssa->vars; - int ssa_vars_count = ssa->vars_count; - zend_ssa_phi *p; - int i, j; - int *size; - ALLOCA_FLAG(use_heap) - - size = do_alloca(sizeof(int) * ssa_vars_count, use_heap); - if (!size) { - return FAILURE; - } - union_find_init(parent, size, ssa_vars_count); - - for (i = 0; i < ssa_vars_count; i++) { - if (ssa_vars[i].definition_phi) { - p = ssa_vars[i].definition_phi; - if (p->pi >= 0) { - union_find_unite(parent, size, i, p->sources[0]); - } else { - for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) { - union_find_unite(parent, size, i, p->sources[j]); - } - } - } else if (ssa_vars[i].definition >= 0) { - int def = ssa_vars[i].definition; - zend_ssa_op *op = ssa->ops + def; - zend_op *opline = op_array->opcodes + def; - - if (op->op1_def >= 0) { - if (op->op1_use >= 0) { - if (opline->opcode != ZEND_ASSIGN) { - union_find_unite(parent, size, op->op1_def, op->op1_use); - } - } - if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) { - union_find_unite(parent, size, op->op1_def, op->op2_use); - } - } - if (op->op2_def >= 0) { - if (op->op2_use >= 0) { - union_find_unite(parent, size, op->op2_def, op->op2_use); - } - } - if (op->result_def >= 0) { - if (op->result_use >= 0) { - if (opline->opcode != ZEND_QM_ASSIGN) { - union_find_unite(parent, size, op->result_def, op->result_use); - } - } - if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) { - union_find_unite(parent, size, op->result_def, op->op1_use); - } - if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) { - union_find_unite(parent, size, op->result_def, op->op2_use); - } - if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) { - union_find_unite(parent, size, op->result_def, op->op1_def); - } - } - } - } - - for (i = 0; i < ssa_vars_count; i++) { - parent[i] = union_find_root(parent, i); - } - - free_alloca(size, use_heap); - - return SUCCESS; -} -/* }}} */ - -static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */ -{ - zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; - if (ce) { - return ce; - } - - ce = zend_hash_find_ptr(CG(class_table), lcname); - if (ce && ce->type == ZEND_INTERNAL_CLASS) { - return ce; - } - - return NULL; -} -/* }}} */ - -static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */ -{ - zend_ssa_op *ssa_op = ssa->ops + def; - zend_op *opline = op_array->opcodes + def; - - if (ssa_op->result_def == var) { - switch (opline->opcode) { - case ZEND_INIT_ARRAY: - return 1; - case ZEND_NEW: - /* objects with destructors should escape */ - if (opline->op1_type == IS_CONST) { - zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1)); - uint32_t forbidden_flags = - /* These flags will always cause an exception */ - ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS - | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT; - if (ce && !ce->parent && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && - !(ce->ce_flags & forbidden_flags) && - (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - return 1; - } - } - break; - case ZEND_QM_ASSIGN: - if (opline->op1_type == IS_CONST - && Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) { - return 1; - } - if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) { - return 1; - } - break; - case ZEND_ASSIGN: - if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) { - return 1; - } - break; - } - } else if (ssa_op->op1_def == var) { - switch (opline->opcode) { - case ZEND_ASSIGN: - if (opline->op2_type == IS_CONST - && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) { - return 1; - } - if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) { - return 1; - } - break; - case ZEND_ASSIGN_DIM: - if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) { - /* implicit object/array allocation */ - return 1; - } - break; - } - } - - return 0; -} -/* }}} */ - -static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */ -{ - zend_ssa_op *op = ssa->ops + def; - zend_op *opline = op_array->opcodes + def; - - if (op->result_def == var) { - switch (opline->opcode) { - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_QM_ASSIGN: - case ZEND_ASSIGN: - return 1; - case ZEND_NEW: - /* objects with destructors should escape */ - if (opline->op1_type == IS_CONST) { - zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1)); - if (ce && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && !ce->parent) { - return 1; - } - } - break; - } - } else if (op->op1_def == var) { - switch (opline->opcode) { - case ZEND_ASSIGN: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - return 1; - } - } - - return 0; -} -/* }}} */ - -static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */ -{ - zend_ssa_op *ssa_op = ssa->ops + use; - zend_op *opline = op_array->opcodes + use; - - if (ssa_op->op1_use == var) { - switch (opline->opcode) { - case ZEND_ASSIGN: - /* no_val */ - break; - case ZEND_QM_ASSIGN: - if (opline->op1_type == IS_CV) { - if (OP1_INFO() & MAY_BE_OBJECT) { - /* object aliasing */ - return 1; - } - } - break; - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_FETCH_DIM_R: - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_OBJ_IS: - break; - case ZEND_ASSIGN_OP: - return 1; - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - break; - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { - return 1; - } - if (OP1_INFO() & MAY_BE_OBJECT) { - /* object aliasing */ - return 1; - } - /* reference dependencies processed separately */ - break; - case ZEND_OP_DATA: - if ((opline-1)->opcode != ZEND_ASSIGN_DIM - && (opline-1)->opcode != ZEND_ASSIGN_OBJ) { - return 1; - } - if (OP1_INFO() & MAY_BE_OBJECT) { - /* object aliasing */ - return 1; - } - opline--; - ssa_op--; - if (opline->op1_type != IS_CV - || (OP1_INFO() & MAY_BE_REF) - || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) { - /* assignment into escaping structure */ - return 1; - } - /* reference dependencies processed separately */ - break; - default: - return 1; - } - } - - if (ssa_op->op2_use == var) { - switch (opline->opcode) { - case ZEND_ASSIGN: - if (opline->op1_type != IS_CV - || (OP1_INFO() & MAY_BE_REF) - || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) { - /* assignment into escaping variable */ - return 1; - } - if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) { - if (OP2_INFO() & MAY_BE_OBJECT) { - /* object aliasing */ - return 1; - } - } - break; - default: - return 1; - } - } - - if (ssa_op->result_use == var) { - switch (opline->opcode) { - case ZEND_ASSIGN: - case ZEND_QM_ASSIGN: - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - break; - default: - return 1; - } - } - - return 0; -} -/* }}} */ - -int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - zend_ssa_var *ssa_vars = ssa->vars; - int ssa_vars_count = ssa->vars_count; - int i, root, use; - int *ees; - bool has_allocations; - int num_non_escaped; - ALLOCA_FLAG(use_heap) - - if (!ssa_vars) { - return SUCCESS; - } - - has_allocations = 0; - for (i = op_array->last_var; i < ssa_vars_count; i++) { - if (ssa_vars[i].definition >= 0 - && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT)) - && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) { - has_allocations = 1; - break; - } - } - if (!has_allocations) { - return SUCCESS; - } - - - /* 1. Build EES (Equi-Escape Sets) */ - ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap); - if (!ees) { - return FAILURE; - } - - if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) { - return FAILURE; - } - - /* 2. Identify Allocations */ - num_non_escaped = 0; - for (i = op_array->last_var; i < ssa_vars_count; i++) { - root = ees[i]; - if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) { - /* already escape. skip */ - } else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) { - if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) { - num_non_escaped--; - } - ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE; - } else if (ssa_vars[i].definition >= 0 - && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) { - if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) { - if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) { - num_non_escaped--; - } - ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE; - } else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN - && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) { - ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE; - num_non_escaped++; - } - } - } - - /* 3. Mark escaped EES */ - if (num_non_escaped) { - for (i = 0; i < ssa_vars_count; i++) { - if (ssa_vars[i].use_chain >= 0) { - root = ees[i]; - if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) { - FOREACH_USE(ssa_vars + i, use) { - if (is_escape_use(op_array, ssa, use, i)) { - ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE; - num_non_escaped--; - if (num_non_escaped == 0) { - i = ssa_vars_count; - } - break; - } - } FOREACH_USE_END(); - } - } - } - } - - /* 4. Process referential dependencies */ - if (num_non_escaped) { - bool changed; - - do { - changed = 0; - for (i = 0; i < ssa_vars_count; i++) { - if (ssa_vars[i].use_chain >= 0) { - root = ees[i]; - if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) { - FOREACH_USE(ssa_vars + i, use) { - zend_ssa_op *op = ssa->ops + use; - zend_op *opline = op_array->opcodes + use; - int enclosing_root; - - if (opline->opcode == ZEND_OP_DATA && - ((opline-1)->opcode == ZEND_ASSIGN_DIM || - (opline-1)->opcode == ZEND_ASSIGN_OBJ || - (opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) && - op->op1_use == i && - (op-1)->op1_use >= 0) { - enclosing_root = ees[(op-1)->op1_use]; - } else if ((opline->opcode == ZEND_INIT_ARRAY || - opline->opcode == ZEND_ADD_ARRAY_ELEMENT) && - op->op1_use == i && - op->result_def >= 0) { - enclosing_root = ees[op->result_def]; - } else { - continue; - } - - if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN || - ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) { - if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) { - ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE; - } else { - ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state; - } - if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) { - num_non_escaped--; - if (num_non_escaped == 0) { - changed = 0; - } else { - changed = 1; - } - break; - } else { - changed = 1; - } - } - } FOREACH_USE_END(); - } - } - } - } while (changed); - } - - /* 5. Propagate values of escape sets to variables */ - for (i = 0; i < ssa_vars_count; i++) { - root = ees[i]; - if (i != root) { - ssa_vars[i].escape_state = ssa_vars[root].escape_state; - } - } - - free_alloca(ees, use_heap); - - return SUCCESS; -} -/* }}} */ diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c deleted file mode 100644 index 32d2f10bf4..0000000000 --- a/ext/opcache/Optimizer/nop_removal.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 10: - * - remove NOPs - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" - -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 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); -} diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c deleted file mode 100644 index 319b17438d..0000000000 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - | Xinchen Hui <laruence@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 4 - * - optimize INIT_FCALL_BY_NAME to DO_FCALL - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" - -#define ZEND_OP1_IS_CONST_STRING(opline) \ - (opline->op1_type == IS_CONST && \ - Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING) -#define ZEND_OP2_IS_CONST_STRING(opline) \ - (opline->op2_type == IS_CONST && \ - Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING) - -typedef struct _optimizer_call_info { - zend_function *func; - zend_op *opline; - bool is_prototype; - bool try_inline; - uint32_t func_arg_num; -} optimizer_call_info; - -static void zend_delete_call_instructions(zend_op *opline) -{ - int call = 0; - - while (1) { - switch (opline->opcode) { - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_FCALL: - if (call == 0) { - MAKE_NOP(opline); - return; - } - /* break missing intentionally */ - case ZEND_NEW: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_USER_CALL: - call--; - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - call++; - break; - case ZEND_SEND_VAL: - case ZEND_SEND_VAR: - if (call == 0) { - if (opline->op1_type == IS_CONST) { - MAKE_NOP(opline); - } else if (opline->op1_type == IS_CV) { - opline->opcode = ZEND_CHECK_VAR; - opline->extended_value = 0; - opline->result.var = 0; - } else { - opline->opcode = ZEND_FREE; - opline->extended_value = 0; - opline->result.var = 0; - } - } - break; - } - opline--; - } -} - -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)) - /* TODO: function copied from trait may be inconsistent ??? */ - && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE)) - && 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_STATIC_METHOD_CALL - && !(func->op_array.fn_flags & ZEND_ACC_STATIC)) { - /* Don't inline static call to instance method. */ - 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 (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) { - return; - } - } - - if (fcall->extended_value < func->op_array.num_args) { - /* don't inline functions with named constants in default arguments */ - i = fcall->extended_value; - - do { - if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) { - return; - } - i++; - } while (i < func->op_array.num_args); - } - - if (RETURN_VALUE_USED(opline)) { - zval zv; - - ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, 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); - } - } -} - -void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_op *opline = op_array->opcodes; - zend_op *end = opline + op_array->last; - int call = 0; - void *checkpoint; - optimizer_call_info *call_stack; - - if (op_array->last < 2) { - return; - } - - checkpoint = zend_arena_checkpoint(ctx->arena); - call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info)); - while (opline < end) { - switch (opline->opcode) { - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_FCALL: - case ZEND_NEW: - /* The argument passing optimizations are valid for prototypes as well, - * as inheritance cannot change between ref <-> non-ref arguments. */ - call_stack[call].func = zend_optimizer_get_called_func( - ctx->script, op_array, opline, &call_stack[call].is_prototype); - call_stack[call].try_inline = - !call_stack[call].is_prototype && opline->opcode != ZEND_NEW; - /* break missing intentionally */ - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_USER_CALL: - call_stack[call].opline = opline; - call_stack[call].func_arg_num = (uint32_t)-1; - call++; - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - call--; - if (call_stack[call].func && call_stack[call].opline) { - zend_op *fcall = call_stack[call].opline; - - if (fcall->opcode == ZEND_INIT_FCALL) { - /* nothing to do */ - } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) { - fcall->opcode = ZEND_INIT_FCALL; - fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); - literal_dtor(&ZEND_OP2_LITERAL(fcall)); - fcall->op2.constant = fcall->op2.constant + 1; - opline->opcode = zend_get_call_op(fcall, call_stack[call].func); - } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { - fcall->opcode = ZEND_INIT_FCALL; - fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); - literal_dtor(&op_array->literals[fcall->op2.constant]); - literal_dtor(&op_array->literals[fcall->op2.constant + 2]); - fcall->op2.constant = fcall->op2.constant + 1; - opline->opcode = zend_get_call_op(fcall, call_stack[call].func); - } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL - || fcall->opcode == ZEND_INIT_METHOD_CALL - || fcall->opcode == ZEND_NEW) { - /* We don't have specialized opcodes for this, do nothing */ - } else { - ZEND_UNREACHABLE(); - } - - if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level) - && call_stack[call].try_inline) { - zend_try_inline_call(op_array, fcall, opline, call_stack[call].func); - } - } - call_stack[call].func = NULL; - call_stack[call].opline = NULL; - call_stack[call].try_inline = 0; - call_stack[call].func_arg_num = (uint32_t)-1; - break; - case ZEND_FETCH_FUNC_ARG: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_FETCH_DIM_FUNC_ARG: - if (call_stack[call - 1].func - && call_stack[call - 1].func_arg_num != (uint32_t)-1) { - if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) { - if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) { - opline->opcode -= 9; - } else { - opline->opcode = ZEND_FETCH_STATIC_PROP_W; - } - } else { - if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG - && opline->op2_type == IS_UNUSED) { - /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not. - * Performing the replacement would create an invalid opcode. */ - call_stack[call - 1].try_inline = 0; - break; - } - - if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) { - opline->opcode -= 12; - } else { - opline->opcode = ZEND_FETCH_STATIC_PROP_R; - } - } - } - break; - case ZEND_SEND_VAL_EX: - if (call_stack[call - 1].func) { - if (opline->op2_type == IS_CONST) { - call_stack[call - 1].try_inline = 0; - break; - } - - if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { - /* We won't convert it into_DO_FCALL to emit error at run-time */ - call_stack[call - 1].opline = NULL; - } else { - opline->opcode = ZEND_SEND_VAL; - } - } - break; - case ZEND_CHECK_FUNC_ARG: - if (call_stack[call - 1].func) { - if (opline->op2_type == IS_CONST) { - call_stack[call - 1].try_inline = 0; - call_stack[call - 1].func_arg_num = (uint32_t)-1; - break; - } - - call_stack[call - 1].func_arg_num = opline->op2.num; - MAKE_NOP(opline); - } - break; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - if (call_stack[call - 1].func) { - if (opline->op2_type == IS_CONST) { - call_stack[call - 1].try_inline = 0; - break; - } - - call_stack[call - 1].func_arg_num = (uint32_t)-1; - if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { - opline->opcode = ZEND_SEND_REF; - } else { - opline->opcode = ZEND_SEND_VAR; - } - } - break; - case ZEND_SEND_VAR_NO_REF_EX: - if (call_stack[call - 1].func) { - if (opline->op2_type == IS_CONST) { - call_stack[call - 1].try_inline = 0; - break; - } - - if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { - opline->opcode = ZEND_SEND_VAR_NO_REF; - } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { - opline->opcode = ZEND_SEND_VAL; - } else { - opline->opcode = ZEND_SEND_VAR; - } - } - break; - case ZEND_SEND_VAL: - case ZEND_SEND_VAR: - case ZEND_SEND_REF: - if (opline->op2_type == IS_CONST) { - call_stack[call - 1].try_inline = 0; - break; - } - break; - case ZEND_SEND_UNPACK: - case ZEND_SEND_USER: - case ZEND_SEND_ARRAY: - call_stack[call - 1].try_inline = 0; - break; - default: - break; - } - opline++; - } - - zend_arena_release(&ctx->arena, checkpoint); -} diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c deleted file mode 100644 index 6f7400159d..0000000000 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" -#include "zend_bitset.h" - -#define GET_AVAILABLE_T() \ - for (i = 0; i < T; i++) { \ - if (!zend_bitset_in(taken_T, i)) { \ - break; \ - } \ - } \ - zend_bitset_incl(taken_T, i); \ - if (i > max) { \ - max = i; \ - } - -void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - int T = op_array->T; - int offset = op_array->last_var; - uint32_t bitset_len; - zend_bitset taken_T; /* T index in use */ - zend_op **start_of_T; /* opline where T is first used */ - zend_bitset valid_T; /* Is the map_T valid */ - int *map_T; /* Map's the T to its new index */ - zend_op *opline, *end; - int currT; - int i; - int max = -1; - void *checkpoint = zend_arena_checkpoint(ctx->arena); - - bitset_len = zend_bitset_len(T); - taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); - start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *)); - valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE); - map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int)); - - end = op_array->opcodes; - opline = &op_array->opcodes[op_array->last - 1]; - - /* Find T definition points */ - while (opline >= end) { - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - start_of_T[VAR_NUM(opline->result.var) - offset] = opline; - } - opline--; - } - - zend_bitset_clear(valid_T, bitset_len); - zend_bitset_clear(taken_T, bitset_len); - - end = op_array->opcodes; - opline = &op_array->opcodes[op_array->last - 1]; - - while (opline >= end) { - if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) { - currT = VAR_NUM(opline->op1.var) - offset; - if (opline->opcode == ZEND_ROPE_END) { - int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); - int var; - - var = max; - while (var >= 0 && !zend_bitset_in(taken_T, var)) { - var--; - } - max = MAX(max, var + num); - var = var + 1; - map_T[currT] = var; - zend_bitset_incl(valid_T, currT); - zend_bitset_incl(taken_T, var); - opline->op1.var = NUM_VAR(var + offset); - while (num > 1) { - num--; - zend_bitset_incl(taken_T, var + num); - } - } else { - if (!zend_bitset_in(valid_T, currT)) { - int use_new_var = 0; - - /* Code in "finally" blocks may modify temporary variables. - * We allocate new temporaries for values that need to - * relive FAST_CALLs. - */ - if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) && - (opline->opcode == ZEND_RETURN || - opline->opcode == ZEND_GENERATOR_RETURN || - opline->opcode == ZEND_RETURN_BY_REF || - opline->opcode == ZEND_FREE || - opline->opcode == ZEND_FE_FREE)) { - zend_op *curr = opline; - - while (--curr >= end) { - if (curr->opcode == ZEND_FAST_CALL) { - use_new_var = 1; - break; - } else if (curr->opcode != ZEND_FREE && - curr->opcode != ZEND_FE_FREE && - curr->opcode != ZEND_VERIFY_RETURN_TYPE && - curr->opcode != ZEND_DISCARD_EXCEPTION) { - break; - } - } - } - if (use_new_var) { - i = ++max; - zend_bitset_incl(taken_T, i); - } else { - GET_AVAILABLE_T(); - } - map_T[currT] = i; - zend_bitset_incl(valid_T, currT); - } - opline->op1.var = NUM_VAR(map_T[currT] + offset); - } - } - - if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) { - currT = VAR_NUM(opline->op2.var) - offset; - if (!zend_bitset_in(valid_T, currT)) { - GET_AVAILABLE_T(); - map_T[currT] = i; - zend_bitset_incl(valid_T, currT); - } - opline->op2.var = NUM_VAR(map_T[currT] + offset); - } - - if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { - currT = VAR_NUM(opline->result.var) - offset; - if (zend_bitset_in(valid_T, currT)) { - if (start_of_T[currT] == opline) { - /* ZEND_FAST_CALL can not share temporary var with others - * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION - * which could be ahead of it */ - if (opline->opcode != ZEND_FAST_CALL) { - zend_bitset_excl(taken_T, map_T[currT]); - } - } - opline->result.var = NUM_VAR(map_T[currT] + offset); - if (opline->opcode == ZEND_ROPE_INIT) { - if (start_of_T[currT] == opline) { - uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); - while (num > 1) { - num--; - zend_bitset_excl(taken_T, map_T[currT]+num); - } - } - } - } else { - /* Code which gets here is using a wrongly built opcode such as RECV() */ - GET_AVAILABLE_T(); - map_T[currT] = i; - zend_bitset_incl(valid_T, currT); - opline->result.var = NUM_VAR(i + offset); - } - } - - opline--; - } - - zend_arena_release(&ctx->arena, checkpoint); - op_array->T = max + 1; -} diff --git a/ext/opcache/Optimizer/pass1.c b/ext/opcache/Optimizer/pass1.c deleted file mode 100644 index 86774afef4..0000000000 --- a/ext/opcache/Optimizer/pass1.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 1 (Simple local optimizations) - * - persistent constant substitution (true, false, null, etc) - * - constant casting (ADD expects numbers, CONCAT strings, etc) - * - constant expression evaluation - * - optimize constant conditional JMPs - * - pre-evaluate constant function calls - * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" - -void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_op *opline = op_array->opcodes; - zend_op *end = opline + op_array->last; - bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)? - (op_array == &ctx->script->main_op_array) : 0; - - while (opline < end) { - switch (opline->opcode) { - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP1_LITERAL(opline)); - } - } - if (opline->op2_type == IS_CONST) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - if (opline->op1_type == IS_CONST) { - goto constant_binary_op; - } - } - break; - - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_POW: - case ZEND_MOD: - case ZEND_SL: - case ZEND_SR: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_BOOL_XOR: - case ZEND_SPACESHIP: - case ZEND_CASE: - case ZEND_CASE_STRICT: - if (opline->op1_type == IS_CONST && - opline->op2_type == IS_CONST) { - /* binary operation with constant operands */ - zval result; - -constant_binary_op: - if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - literal_dtor(&ZEND_OP2_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &result); - } - } - } - break; - - case ZEND_ASSIGN_OP: - if (opline->op2_type == IS_CONST) { - if (opline->extended_value == ZEND_ADD - || opline->extended_value == ZEND_SUB - || opline->extended_value == ZEND_MUL - || opline->extended_value == ZEND_DIV - || opline->extended_value == ZEND_POW) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - /* don't optimise if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); - } - } - } else if (opline->extended_value == ZEND_MOD - || opline->extended_value == ZEND_SL - || opline->extended_value == ZEND_SR) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { - /* don't optimise if it should produce a runtime numeric string error */ - if (!(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING - && !is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0))) { - convert_to_long(&ZEND_OP2_LITERAL(opline)); - } - } - } else if (opline->extended_value == ZEND_CONCAT) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - } - } - break; - - case ZEND_CAST: - if (opline->op1_type == IS_CONST) { - /* cast of constant operand */ - zval result; - - if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - zend_optimizer_update_op1_const(op_array, opline, &result); - } - break; - } - } - break; - - case ZEND_BW_NOT: - case ZEND_BOOL_NOT: - if (opline->op1_type == IS_CONST) { - /* unary operation on constant operand */ - zval result; - - if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &result); - } - } - } - break; - - case ZEND_FETCH_CONSTANT: - if (opline->op2_type == IS_CONST && - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && - Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 && - memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) { - /* substitute __COMPILER_HALT_OFFSET__ constant */ - zend_execute_data *orig_execute_data = EG(current_execute_data); - zend_execute_data fake_execute_data; - zval *offset; - - memset(&fake_execute_data, 0, sizeof(zend_execute_data)); - fake_execute_data.func = (zend_function*)op_array; - EG(current_execute_data) = &fake_execute_data; - if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) { - - literal_dtor(&ZEND_OP2_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, offset); - } - } - EG(current_execute_data) = orig_execute_data; - break; - } - - if (opline->op2_type == IS_CONST && - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - /* substitute persistent constants */ - zval c; - - if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) { - if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) { - break; - } - } - if (Z_TYPE(c) == IS_CONSTANT_AST) { - break; - } - literal_dtor(&ZEND_OP2_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &c); - } - } - break; - - case ZEND_FETCH_CLASS_CONSTANT: - if (opline->op2_type == IS_CONST && - Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - - zend_class_entry *ce = NULL; - - if (opline->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { - /* for A::B */ - if (op_array->scope && - !strncasecmp(Z_STRVAL(ZEND_OP1_LITERAL(opline)), - ZSTR_VAL(op_array->scope->name), Z_STRLEN(ZEND_OP1_LITERAL(opline)) + 1)) { - ce = op_array->scope; - } else { - if ((ce = zend_hash_find_ptr(EG(class_table), - Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL || - (ce->type == ZEND_INTERNAL_CLASS && - ce->info.internal.module->type != MODULE_PERSISTENT) || - (ce->type == ZEND_USER_CLASS && - ce->info.user.filename != op_array->filename)) { - break; - } - } - } else if (op_array->scope && - opline->op1_type == IS_UNUSED && - (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { - /* for self::B */ - ce = op_array->scope; - } else if (op_array->scope && - opline->op1_type == IS_VAR && - (opline - 1)->opcode == ZEND_FETCH_CLASS && - ((opline - 1)->op2_type == IS_UNUSED && - ((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) && - (opline - 1)->result.var == opline->op1.var) { - /* for self::B */ - ce = op_array->scope; - } - - if (ce) { - zend_class_constant *cc; - zval *c, t; - - if ((cc = zend_hash_find_ptr(&ce->constants_table, - Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL && - (Z_ACCESS_FLAGS(cc->value) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { - c = &cc->value; - if (Z_TYPE_P(c) == IS_CONSTANT_AST) { - zend_ast *ast = Z_ASTVAL_P(c); - if (ast->kind != ZEND_AST_CONSTANT - || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1) - || Z_TYPE(t) == IS_CONSTANT_AST) { - break; - } - } else { - ZVAL_COPY_OR_DUP(&t, c); - } - - if (opline->op1_type == IS_CONST) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - } else if (opline->op1_type == IS_VAR) { - MAKE_NOP((opline - 1)); - } - literal_dtor(&ZEND_OP2_LITERAL(opline)); - - if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &t); - } - } - } - } - break; - - case ZEND_DO_ICALL: { - zend_op *send1_opline = opline - 1; - zend_op *send2_opline = NULL; - zend_op *init_opline = NULL; - - while (send1_opline->opcode == ZEND_NOP) { - send1_opline--; - } - if (send1_opline->opcode != ZEND_SEND_VAL || - send1_opline->op1_type != IS_CONST) { - /* don't colllect constants after unknown function call */ - collect_constants = 0; - break; - } - if (send1_opline->op2.num == 2) { - send2_opline = send1_opline; - send1_opline--; - while (send1_opline->opcode == ZEND_NOP) { - send1_opline--; - } - if (send1_opline->opcode != ZEND_SEND_VAL || - send1_opline->op1_type != IS_CONST) { - /* don't colllect constants after unknown function call */ - collect_constants = 0; - break; - } - } - init_opline = send1_opline - 1; - while (init_opline->opcode == ZEND_NOP) { - init_opline--; - } - if (init_opline->opcode != ZEND_INIT_FCALL || - init_opline->op2_type != IS_CONST || - Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) { - /* don't colllect constants after unknown function call */ - collect_constants = 0; - break; - } - - /* define("name", scalar); */ - if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 && - zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) { - - if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && - send2_opline && - Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) { - - if (collect_constants) { - zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline)); - } - - if (RESULT_UNUSED(opline) && - !zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) { - - opline->opcode = ZEND_DECLARE_CONST; - opline->op1_type = IS_CONST; - opline->op2_type = IS_CONST; - opline->result_type = IS_UNUSED; - opline->op1.constant = send1_opline->op1.constant; - opline->op2.constant = send2_opline->op1.constant; - opline->result.num = 0; - - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - MAKE_NOP(send1_opline); - MAKE_NOP(send2_opline); - } - break; - } - } - - /* pre-evaluate constant functions: - constant(x) - function_exists(x) - is_callable(x) - extension_loaded(x) - */ - if (!send2_opline && - Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) { - if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 && - !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), - "function_exists", sizeof("function_exists")-1)) || - (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 && - !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), - "is_callable", sizeof("is_callable")))) { - zend_internal_function *func; - zend_string *lc_name = zend_string_tolower( - Z_STR(ZEND_OP1_LITERAL(send1_opline))); - - if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL - && func->type == ZEND_INTERNAL_FUNCTION - && func->module->type == MODULE_PERSISTENT -#ifdef ZEND_WIN32 - && func->module->handle == NULL -#endif - ) { - zval t; - ZVAL_TRUE(&t); - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &t); - } - } - zend_string_release_ex(lc_name, 0); - break; - } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 && - !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), - "extension_loaded", sizeof("extension_loaded")-1)) { - zval t; - zend_string *lc_name = zend_string_tolower( - Z_STR(ZEND_OP1_LITERAL(send1_opline))); - zend_module_entry *m = zend_hash_find_ptr(&module_registry, - lc_name); - - zend_string_release_ex(lc_name, 0); - if (!m) { - if (PG(enable_dl)) { - break; - } else { - ZVAL_FALSE(&t); - } - } else { - if (m->type == MODULE_PERSISTENT -#ifdef ZEND_WIN32 - && m->handle == NULL -#endif - ) { - ZVAL_TRUE(&t); - } else { - break; - } - } - - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &t); - } - break; - } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 && - !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), - "constant", sizeof("constant")-1)) { - zval t; - - if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) { - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &t); - } - } - break; - /* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */ - } else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 && - !memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), - "dirname", sizeof("dirname") - 1) && - IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) { - zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0); - ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname)); - if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) { - zval t; - - ZVAL_STR(&t, dirname); - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - opline->extended_value = 0; - SET_UNUSED(opline->op2); - zend_optimizer_update_op1_const(op_array, opline, &t); - } - } else { - zend_string_release_ex(dirname, 0); - } - break; - } - } - /* don't colllect constants after any other function call */ - collect_constants = 0; - break; - } - case ZEND_STRLEN: - if (opline->op1_type == IS_CONST) { - zval t; - - if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) { - literal_dtor(&ZEND_OP1_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &t); - } - } - } - break; - case ZEND_DEFINED: - { - zval c; - if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) { - break; - } - ZVAL_TRUE(&c); - literal_dtor(&ZEND_OP1_LITERAL(opline)); - if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_QM_ASSIGN; - zend_optimizer_update_op1_const(op_array, opline, &c); - } - } - break; - case ZEND_DECLARE_CONST: - if (collect_constants && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && - Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) { - zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)); - } - break; -#if 0 - /* see ext/opcache/tests/bug78961.phpt */ -// case ZEND_FETCH_R: - case ZEND_FETCH_W: -// case ZEND_FETCH_RW: - case ZEND_FETCH_IS: -// case ZEND_FETCH_FUNC_ARG: - case ZEND_FETCH_UNSET: - /* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */ - if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 && - opline->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && - zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") && - ((opline + 1)->opcode == opline->opcode + 1 || - ((opline + 1)->opcode == ZEND_UNSET_DIM && - opline->opcode == ZEND_FETCH_UNSET) || - ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ && - opline->opcode == ZEND_FETCH_IS)) && - (opline + 1)->op1_type == opline->result_type && - (opline + 1)->op1.var == opline->result.var && - ((opline + 1)->op2_type != IS_CONST || - Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) { - - if ((opline + 1)->opcode == ZEND_UNSET_DIM) { - (opline + 1)->opcode = ZEND_UNSET_VAR; - (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; - } else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) { - (opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR; - (opline + 1)->extended_value |= ZEND_FETCH_GLOBAL; - } else { - (opline + 1)->opcode = opline->opcode; - (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; - } - (opline + 1)->op1_type = (opline + 1)->op2_type; - (opline + 1)->op1 = (opline + 1)->op2; - if ((opline + 1)->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) { - - convert_to_string(&ZEND_OP1_LITERAL(opline + 1)); - zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1))); - } - SET_UNUSED((opline + 1)->op2); - MAKE_NOP(opline); - } - break; -#endif - - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) - in case we know it wouldn't jump */ - if (opline->op1_type == IS_CONST) { - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - if (opline->opcode == ZEND_JMPZ_EX) { - opline->opcode = ZEND_QM_ASSIGN; - zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); - ZVAL_TRUE(&ZEND_OP1_LITERAL(opline)); - opline->op2.num = 0; - break; - } - } else { - if (opline->opcode == ZEND_JMPNZ_EX) { - opline->opcode = ZEND_QM_ASSIGN; - zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline)); - ZVAL_FALSE(&ZEND_OP1_LITERAL(opline)); - opline->op2.num = 0; - break; - } - } - } - collect_constants = 0; - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - if (opline->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); - - if (opline->opcode == ZEND_JMPZ) { - should_jmp = !should_jmp; - } - literal_dtor(&ZEND_OP1_LITERAL(opline)); - opline->op1_type = IS_UNUSED; - if (should_jmp) { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - opline->op2.num = 0; - } else { - MAKE_NOP(opline); - break; - } - } - collect_constants = 0; - break; - - case ZEND_JMPZNZ: - if (opline->op1_type == IS_CONST) { - zend_op *target_opline; - - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */ - } else { - target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */ - } - literal_dtor(&ZEND_OP1_LITERAL(opline)); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline); - opline->op1_type = IS_UNUSED; - opline->opcode = ZEND_JMP; - } - collect_constants = 0; - break; - - case ZEND_RETURN: - case ZEND_RETURN_BY_REF: - case ZEND_GENERATOR_RETURN: - case ZEND_EXIT: - case ZEND_THROW: - case ZEND_MATCH_ERROR: - case ZEND_CATCH: - case ZEND_FAST_CALL: - case ZEND_FAST_RET: - case ZEND_JMP: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_ASSERT_CHECK: - case ZEND_JMP_NULL: - collect_constants = 0; - break; - } - opline++; - } -} diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c deleted file mode 100644 index f98c41848c..0000000000 --- a/ext/opcache/Optimizer/pass3.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 3: (Jump optimization) - * - optimize series of JMPs - */ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" - -/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ -static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count) -{ - int i; - - for (i = 0; i < jmp_hitlist_count; i++) { - if (jmp_hitlist[i] == target) { - return 1; - } - } - return 0; -} - -#define CHECK_LOOP(target) \ - if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \ - jmp_hitlist[jmp_hitlist_count++] = target; \ - } else { \ - break; \ - } - -void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) -{ - zend_op *opline; - zend_op *end; - zend_op *target; - zend_op **jmp_hitlist; - int jmp_hitlist_count; - ALLOCA_FLAG(use_heap); - - jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); - opline = op_array->opcodes; - end = opline + op_array->last; - - while (opline < end) { - - switch (opline->opcode) { - case ZEND_JMP: - jmp_hitlist_count = 0; - - target = ZEND_OP1_JMP_ADDR(opline); - while (1) { - if (target->opcode == ZEND_JMP) { - /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); - } - - if (target == opline + 1) { - /* convert L: JMP L+1 to NOP */ - MAKE_NOP(opline); - } else if (target->opcode == ZEND_JMPZNZ) { - /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */ - *opline = *target; - if (opline->op1_type == IS_CONST) { - zval zv; - ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline)); - opline->op1.constant = zend_optimizer_add_literal(op_array, &zv); - } - goto optimize_jmpznz; - } else if ((target->opcode == ZEND_RETURN || - target->opcode == ZEND_RETURN_BY_REF || - target->opcode == ZEND_GENERATOR_RETURN || - target->opcode == ZEND_EXIT) && - !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) { - /* JMP L, L: RETURN to immediate RETURN */ - *opline = *target; - if (opline->op1_type == IS_CONST) { - zval zv; - ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline)); - opline->op1.constant = zend_optimizer_add_literal(op_array, &zv); - } - } else if (opline > op_array->opcodes && - ((opline-1)->opcode == ZEND_JMPZ || - (opline-1)->opcode == ZEND_JMPNZ)) { - if (ZEND_OP2_JMP_ADDR(opline-1) == target) { - /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */ - if ((opline-1)->op1_type == IS_CV) { - (opline-1)->opcode = ZEND_CHECK_VAR; - (opline-1)->op2.num = 0; - } else if ((opline-1)->op1_type & (IS_TMP_VAR|IS_VAR)) { - (opline-1)->opcode = ZEND_FREE; - (opline-1)->op2.num = 0; - } else { - MAKE_NOP(opline-1); - } - } else { - /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ - if ((opline-1)->opcode == ZEND_JMPZ) { - (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), target); - } else { - (opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), ZEND_OP2_JMP_ADDR(opline-1)); - ZEND_SET_OP_JMP_ADDR((opline-1), (opline-1)->op2, target); - } - (opline-1)->opcode = ZEND_JMPZNZ; - } - } - break; - - case ZEND_JMP_SET: - case ZEND_COALESCE: - jmp_hitlist_count = 0; - - target = ZEND_OP2_JMP_ADDR(opline); - while (1) { - if (target->opcode == ZEND_JMP) { - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); - } - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - jmp_hitlist_count = 0; - - target = ZEND_OP2_JMP_ADDR(opline); - while (1) { - if (target->opcode == ZEND_JMP) { - /* plain JMP */ - /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(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) */ - target = ZEND_OP2_JMP_ADDR(target); - CHECK_LOOP(target); - } 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) */ - target = target + 1; - } else if (target->opcode == ZEND_JMPZNZ && - SAME_VAR(opline->op1, target->op1)) { - target = (opline->opcode == ZEND_JMPZ) ? - ZEND_OP2_JMP_ADDR(target) : - ZEND_OFFSET_TO_OPLINE(target, target->extended_value); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); - } - - /* convert L: JMPZ L+1 to NOP */ - if (target == opline + 1) { - if (opline->op1_type == IS_CV) { - opline->opcode = ZEND_CHECK_VAR; - opline->op2.num = 0; - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - opline->opcode = ZEND_FREE; - opline->op2.num = 0; - } else { - MAKE_NOP(opline); - } - } - break; - - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - jmp_hitlist_count = 0; - - target = ZEND_OP2_JMP_ADDR(opline); - while (1) { - if (target->opcode == ZEND_JMP) { - /* plain JMP */ - /* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */ - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == opline->opcode-3 && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to - JMPZ_EX(X,L2) */ - target = ZEND_OP2_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == opline->opcode && - target->result.var == opline->result.var && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to - JMPZ_EX(X,L2) */ - target = ZEND_OP2_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_JMPZNZ && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* Check for JMPZNZ with same cond variable */ - target = (opline->opcode == ZEND_JMPZ_EX) ? - ZEND_OP2_JMP_ADDR(target) : - ZEND_OFFSET_TO_OPLINE(target, target->extended_value); - CHECK_LOOP(target); - } else if (target->opcode == INV_EX_COND(opline->opcode) && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to - JMPZ_EX(X,L1+1) */ - target = target + 1; - } else if (target->opcode == INV_EX_COND_EX(opline->opcode) && - target->result.var == opline->result.var && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to - JMPZ_EX(X,L1+1) */ - target = target + 1; - } else if (target->opcode == ZEND_BOOL && - (SAME_VAR(target->op1, opline->result) || - SAME_VAR(target->op1, opline->op1))) { - /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to - Z = JMPZ_EX(X,L1+1) */ - - /* NOTE: This optimization pattern is not safe, but works, */ - /* because result of JMPZ_EX instruction */ - /* is not used on the following path and */ - /* should be used once on the branch path. */ - /* */ - /* The pattern works well only if jums processed in */ - /* direct order, otherwise it breaks JMPZ_EX */ - /* sequences too early. */ - opline->result.var = target->result.var; - target = target + 1; - CHECK_LOOP(target); - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); - } - - /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ - if (target == opline + 1) { - opline->opcode = ZEND_BOOL; - opline->op2.num = 0; - } - break; - - case ZEND_JMPZNZ: -optimize_jmpznz: - jmp_hitlist_count = 0; - target = ZEND_OP2_JMP_ADDR(opline); - while (1) { - if (target->opcode == ZEND_JMP) { - /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(target); - } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && - SAME_VAR(target->op1, opline->op1)) { - /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ - target = ZEND_OP2_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_JMPNZ && - SAME_VAR(target->op1, opline->op1)) { - /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ - target = target + 1; - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); - } - - jmp_hitlist_count = 0; - target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - while (1) { - if (target->opcode == ZEND_JMP) { - /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ - target = ZEND_OP1_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_JMPNZ && - SAME_VAR(target->op1, opline->op1)) { - /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ - target = ZEND_OP2_JMP_ADDR(target); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_JMPZ && - SAME_VAR(target->op1, opline->op1)) { - /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ - target = target + 1; - } else if (target->opcode == ZEND_JMPZNZ && - SAME_VAR(target->op1, opline->op1)) { - /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ - target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); - CHECK_LOOP(target); - } else if (target->opcode == ZEND_NOP) { - target = target + 1; - } else { - break; - } - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target); - } - - if (ZEND_OP2_JMP_ADDR(opline) == target && - !(opline->op1_type & (IS_VAR|IS_TMP_VAR))) { - /* JMPZNZ(?,L,L) -> JMP(L) */ - opline->opcode = ZEND_JMP; - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - opline->extended_value = 0; - } - /* Don't convert JMPZNZ back to JMPZ/JMPNZ, because the - following JMP is not removed yet. */ - break; - } - opline++; - } - free_alloca(jmp_hitlist, use_heap); -} diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c deleted file mode 100644 index bc9dc38680..0000000000 --- a/ext/opcache/Optimizer/sccp.c +++ /dev/null @@ -1,2529 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, SCCP - Sparse Conditional Constant Propagation | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_type_info.h" -#include "ZendAccelerator.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "Optimizer/zend_call_graph.h" -#include "Optimizer/zend_inference.h" -#include "Optimizer/scdf.h" -#include "Optimizer/zend_dump.h" -#include "ext/standard/php_string.h" -#include "zend_exceptions.h" - -/* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The - * used value lattice is defined as follows: - * - * BOT < {constant values} < TOP - * - * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable. - * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant. - * - * All variables are optimistically initialized to TOP, apart from the implicit variables defined - * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to - * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway. - * This is better because such operations might never be reached due to the conditional nature of - * the algorithm. - * - * The meet operation for phi functions is defined as follows: - * BOT + any = BOT - * TOP + any = any - * C_i + C_i = C_i (i.e. two equal constants) - * C_i + C_j = BOT (i.e. two different constants) - * - * When evaluating instructions TOP and BOT are handled as follows: - * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which - * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT. - * b) Otherwise, if the instruction can never be evaluated (either in general, or with the - * specific modifiers) the result is BOT. - * c) Otherwise, if any operand is TOP, the result is TOP. - * d) Otherwise (at this point all operands are known and constant), if we can compute the result - * for these specific constants (without throwing notices or similar) then that is the result. - * e) Otherwise the result is BOT. - * - * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things - * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating - * TYPE_CHECKS based on the type information. - * - * Feasible successors for conditional branches are determined as follows: - * a) If we don't support the branch type or branch on BOT, all successors are feasible. - * b) Otherwise, if we branch on TOP none of the successors are feasible. - * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant - * (usually only one successor will be feasible). - * - * The original SCCP algorithm is extended with ability to propagate constant array - * elements and object properties. The extension is based on a variation of Array - * SSA form and its application to Spare Constant Propagation, described at - * "Array SSA Form" by Vivek Sarkar, Kathleen Knobe and Stephen Fink in chapter - * 16 of the SSA book. - */ - -#define SCP_DEBUG 0 - -typedef struct _sccp_ctx { - scdf_ctx scdf; - zend_call_info **call_map; - zval *values; - zval top; - zval bot; -} sccp_ctx; - -#define TOP ((zend_uchar)-1) -#define BOT ((zend_uchar)-2) -#define PARTIAL_ARRAY ((zend_uchar)-3) -#define PARTIAL_OBJECT ((zend_uchar)-4) -#define IS_TOP(zv) (Z_TYPE_P(zv) == TOP) -#define IS_BOT(zv) (Z_TYPE_P(zv) == BOT) -#define IS_PARTIAL_ARRAY(zv) (Z_TYPE_P(zv) == PARTIAL_ARRAY) -#define IS_PARTIAL_OBJECT(zv) (Z_TYPE_P(zv) == PARTIAL_OBJECT) - -#define MAKE_PARTIAL_ARRAY(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) -#define MAKE_PARTIAL_OBJECT(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) - -#define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP) -#define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT) - -static void scp_dump_value(zval *zv) { - if (IS_TOP(zv)) { - fprintf(stderr, " top"); - } else if (IS_BOT(zv)) { - fprintf(stderr, " bot"); - } else if (Z_TYPE_P(zv) == IS_ARRAY || IS_PARTIAL_ARRAY(zv)) { - fprintf(stderr, " %s[", IS_PARTIAL_ARRAY(zv) ? "partial " : ""); - zend_dump_ht(Z_ARRVAL_P(zv)); - fprintf(stderr, "]"); - } else if (IS_PARTIAL_OBJECT(zv)) { - fprintf(stderr, " {"); - zend_dump_ht(Z_ARRVAL_P(zv)); - fprintf(stderr, "}"); - } else { - zend_dump_const(zv); - } -} - -static void empty_partial_array(zval *zv) -{ - MAKE_PARTIAL_ARRAY(zv); - Z_ARR_P(zv) = zend_new_array(8); -} - -static void dup_partial_array(zval *dst, zval *src) -{ - MAKE_PARTIAL_ARRAY(dst); - Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src)); -} - -static void empty_partial_object(zval *zv) -{ - MAKE_PARTIAL_OBJECT(zv); - Z_ARR_P(zv) = zend_new_array(8); -} - -static void dup_partial_object(zval *dst, zval *src) -{ - MAKE_PARTIAL_OBJECT(dst); - Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src)); -} - -static inline bool value_known(zval *zv) { - return !IS_TOP(zv) && !IS_BOT(zv); -} - -/* Sets new value for variable and ensures that it is lower or equal - * the previous one in the constant propagation lattice. */ -static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) { - zval *value = &ctx->values[var]; - if (IS_BOT(value) || IS_TOP(new)) { - return; - } - -#if SCP_DEBUG - fprintf(stderr, "Lowering #%d.", var); - zend_dump_var(scdf->op_array, IS_CV, scdf->ssa->vars[var].var); - fprintf(stderr, " from"); - scp_dump_value(value); - fprintf(stderr, " to"); - scp_dump_value(new); - fprintf(stderr, "\n"); -#endif - - if (IS_TOP(value) || IS_BOT(new)) { - zval_ptr_dtor_nogc(value); - ZVAL_COPY(value, new); - scdf_add_to_worklist(scdf, var); - return; - } - - /* Always replace PARTIAL_(ARRAY|OBJECT), as new maybe changed by join_partial_(arrays|object) */ - if (IS_PARTIAL_ARRAY(new) || IS_PARTIAL_OBJECT(new)) { - if (Z_TYPE_P(value) != Z_TYPE_P(new) - || zend_hash_num_elements(Z_ARR_P(new)) != zend_hash_num_elements(Z_ARR_P(value))) { - zval_ptr_dtor_nogc(value); - ZVAL_COPY(value, new); - scdf_add_to_worklist(scdf, var); - } - return; - } - -#if ZEND_DEBUG - ZEND_ASSERT(zend_is_identical(value, new)); -#endif -} - -static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) { - if (opline->op1_type == IS_CONST) { - return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant); - } else if (ssa_op->op1_use != -1) { - return &ctx->values[ssa_op->op1_use]; - } else { - return NULL; - } -} - -static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) { - if (opline->op2_type == IS_CONST) { - return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant); - } else if (ssa_op->op2_use != -1) { - return &ctx->values[ssa_op->op2_use]; - } else { - return NULL; - } -} - -static bool can_replace_op1( - const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) { - switch (opline->opcode) { - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC: - case ZEND_POST_DEC: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_ASSIGN: - case ZEND_ASSIGN_REF: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_FETCH_LIST_W: - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - case ZEND_SEND_REF: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_UNPACK: - case ZEND_SEND_ARRAY: - case ZEND_SEND_USER: - case ZEND_FE_RESET_RW: - return 0; - /* Do not accept CONST */ - case ZEND_ROPE_ADD: - case ZEND_ROPE_END: - case ZEND_BIND_STATIC: - case ZEND_BIND_GLOBAL: - case ZEND_MAKE_REF: - case ZEND_UNSET_CV: - case ZEND_ISSET_ISEMPTY_CV: - return 0; - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF); - case ZEND_YIELD: - return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE); - case ZEND_VERIFY_RETURN_TYPE: - // TODO: This would require a non-local change ??? - return 0; - case ZEND_OP_DATA: - return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF && - (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF; - default: - if (ssa_op->op1_def != -1) { - ZEND_UNREACHABLE(); - return 0; - } - } - - return 1; -} - -static bool can_replace_op2( - const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) { - switch (opline->opcode) { - /* Do not accept CONST */ - case ZEND_DECLARE_CLASS_DELAYED: - case ZEND_BIND_LEXICAL: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - return 0; - } - return 1; -} - -static bool try_replace_op1( - sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) { - if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) { - zval zv; - ZVAL_COPY(&zv, value); - if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) { - return 1; - } else { - // TODO: check the following special cases ??? - switch (opline->opcode) { - case ZEND_CASE: - opline->opcode = ZEND_IS_EQUAL; - goto replace_op1_simple; - case ZEND_CASE_STRICT: - opline->opcode = ZEND_IS_IDENTICAL; - goto replace_op1_simple; - case ZEND_FETCH_LIST_R: - case ZEND_SWITCH_STRING: - case ZEND_SWITCH_LONG: - case ZEND_MATCH: -replace_op1_simple: - if (Z_TYPE(zv) == IS_STRING) { - zend_string_hash_val(Z_STR(zv)); - } - opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv); - opline->op1_type = IS_CONST; - return 1; - case ZEND_INSTANCEOF: - zval_ptr_dtor_nogc(&zv); - ZVAL_FALSE(&zv); - opline->opcode = ZEND_QM_ASSIGN; - opline->op1_type = IS_CONST; - opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv); - opline->op2_type = IS_UNUSED; - if (ssa_op->op2_use >= 0) { - ZEND_ASSERT(ssa_op->op2_def == -1); - zend_ssa_unlink_use_chain(ctx->scdf.ssa, ssa_op - ctx->scdf.ssa->ops, ssa_op->op2_use); - ssa_op->op2_use = -1; - ssa_op->op2_use_chain = -1; - } - return 1; - default: - break; - } - zval_ptr_dtor_nogc(&zv); - } - } - return 0; -} - -static bool try_replace_op2( - sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) { - if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) { - zval zv; - ZVAL_COPY(&zv, value); - if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) { - return 1; - } else { - switch (opline->opcode) { - case ZEND_FETCH_CLASS: - if (Z_TYPE(zv) == IS_STRING) { - ZEND_ASSERT((opline + 1)->opcode == ZEND_INSTANCEOF); - ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use); - if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) { - zend_ssa_op *next_op = ssa_op + 1; - zend_ssa_unlink_use_chain(ctx->scdf.ssa, next_op - ctx->scdf.ssa->ops, next_op->op2_use); - next_op->op2_use = -1; - next_op->op2_use_chain = -1; - zend_ssa_remove_result_def(ctx->scdf.ssa, ssa_op); - MAKE_NOP(opline); - return 1; - } - } - default: - break; - } - zval_ptr_dtor_nogc(&zv); - } - } - return 0; -} - -static inline int ct_eval_binary_op(zval *result, zend_uchar binop, zval *op1, zval *op2) { - /* TODO: We could implement support for evaluation of + on partial arrays. */ - if (IS_PARTIAL_ARRAY(op1) || IS_PARTIAL_ARRAY(op2)) { - return FAILURE; - } - - return zend_optimizer_eval_binary_op(result, binop, op1, op2); -} - -static inline int ct_eval_bool_cast(zval *result, zval *op) { - if (IS_PARTIAL_ARRAY(op)) { - if (zend_hash_num_elements(Z_ARRVAL_P(op)) == 0) { - /* An empty partial array may be non-empty at runtime, we don't know whether the - * result will be true or false. */ - return FAILURE; - } - - ZVAL_TRUE(result); - return SUCCESS; - } - - ZVAL_BOOL(result, zend_is_true(op)); - return SUCCESS; -} - -static inline int zval_to_string_offset(zend_long *result, zval *op) { - switch (Z_TYPE_P(op)) { - case IS_LONG: - *result = Z_LVAL_P(op); - return SUCCESS; - case IS_STRING: - if (IS_LONG == is_numeric_string( - Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) { - return SUCCESS; - } - return FAILURE; - default: - return FAILURE; - } -} - -static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) { - switch (Z_TYPE_P(op2)) { - case IS_NULL: - *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC()); - return SUCCESS; - case IS_FALSE: - *result = zend_hash_index_find(Z_ARR_P(op1), 0); - return SUCCESS; - case IS_TRUE: - *result = zend_hash_index_find(Z_ARR_P(op1), 1); - return SUCCESS; - case IS_LONG: - *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2)); - return SUCCESS; - case IS_DOUBLE: - *result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2))); - return SUCCESS; - case IS_STRING: - *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2)); - return SUCCESS; - default: - return FAILURE; - } -} - -static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) { - if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) { - zval *value; - if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) { - ZVAL_COPY(result, value); - return SUCCESS; - } - } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) { - zend_long index; - if (zval_to_string_offset(&index, op2) == FAILURE) { - return FAILURE; - } - if (index >= 0 && index < Z_STRLEN_P(op1)) { - ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0)); - return SUCCESS; - } - } - return FAILURE; -} - -/* op1 may be NULL here to indicate an unset value */ -static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) { - zval zv; - if (!(extended_value & ZEND_ISEMPTY)) { - ZVAL_BOOL(result, op1 && Z_TYPE_P(op1) != IS_NULL); - return SUCCESS; - } else if (!op1) { - ZVAL_TRUE(result); - return SUCCESS; - } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) { - ZVAL_BOOL(result, Z_TYPE(zv) == IS_FALSE); - return SUCCESS; - } else { - return FAILURE; - } -} - -static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) { - if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) { - zval *value; - if (fetch_array_elem(&value, op1, op2) == FAILURE) { - return FAILURE; - } - if (IS_PARTIAL_ARRAY(op1) && (!value || IS_BOT(value))) { - return FAILURE; - } - return ct_eval_isset_isempty(result, extended_value, value); - } else if (Z_TYPE_P(op1) == IS_STRING) { - // TODO - return FAILURE; - } else { - ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY)); - return SUCCESS; - } -} - -static inline int ct_eval_del_array_elem(zval *result, zval *key) { - ZEND_ASSERT(IS_PARTIAL_ARRAY(result)); - - switch (Z_TYPE_P(key)) { - case IS_NULL: - zend_hash_del(Z_ARR_P(result), ZSTR_EMPTY_ALLOC()); - break; - case IS_FALSE: - zend_hash_index_del(Z_ARR_P(result), 0); - break; - case IS_TRUE: - zend_hash_index_del(Z_ARR_P(result), 1); - break; - case IS_LONG: - zend_hash_index_del(Z_ARR_P(result), Z_LVAL_P(key)); - break; - case IS_DOUBLE: - zend_hash_index_del(Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key))); - break; - case IS_STRING: - zend_symtable_del(Z_ARR_P(result), Z_STR_P(key)); - break; - default: - return FAILURE; - } - - return SUCCESS; -} - -static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) { - if (!key) { - SEPARATE_ARRAY(result); - if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) { - Z_TRY_ADDREF_P(value); - return SUCCESS; - } - return FAILURE; - } - - switch (Z_TYPE_P(key)) { - case IS_NULL: - SEPARATE_ARRAY(result); - value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value); - break; - case IS_FALSE: - SEPARATE_ARRAY(result); - value = zend_hash_index_update(Z_ARR_P(result), 0, value); - break; - case IS_TRUE: - SEPARATE_ARRAY(result); - value = zend_hash_index_update(Z_ARR_P(result), 1, value); - break; - case IS_LONG: - SEPARATE_ARRAY(result); - value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value); - break; - case IS_DOUBLE: - SEPARATE_ARRAY(result); - value = zend_hash_index_update( - Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value); - break; - case IS_STRING: - SEPARATE_ARRAY(result); - value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value); - break; - default: - return FAILURE; - } - - Z_TRY_ADDREF_P(value); - return SUCCESS; -} - -static inline int ct_eval_add_array_unpack(zval *result, zval *array) { - zend_string *key; - zval *value; - if (Z_TYPE_P(array) != IS_ARRAY) { - return FAILURE; - } - - SEPARATE_ARRAY(result); - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array), key, value) { - if (key) { - return FAILURE; - } - value = zend_hash_next_index_insert(Z_ARR_P(result), value); - if (!value) { - return FAILURE; - } - Z_TRY_ADDREF_P(value); - } ZEND_HASH_FOREACH_END(); - return SUCCESS; -} - -static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) { - switch (Z_TYPE_P(result)) { - case IS_NULL: - case IS_FALSE: - array_init(result); - /* break missing intentionally */ - case IS_ARRAY: - case PARTIAL_ARRAY: - return ct_eval_add_array_elem(result, value, key); - case IS_STRING: - // TODO Before enabling this case, make sure ARRAY_DIM result op is correct -#if 0 - zend_long index; - zend_string *new_str, *value_str; - if (!key || Z_TYPE_P(value) == IS_ARRAY - || zval_to_string_offset(&index, key) == FAILURE || index < 0) { - return FAILURE; - } - - if (index >= Z_STRLEN_P(result)) { - new_str = zend_string_alloc(index + 1, 0); - memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result)); - memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result)); - ZSTR_VAL(new_str)[index + 1] = 0; - } else { - new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0); - } - - value_str = zval_get_string(value); - ZVAL_STR(result, new_str); - Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0]; - zend_string_release_ex(value_str, 0); -#endif - return FAILURE; - default: - return FAILURE; - } -} - -static inline int fetch_obj_prop(zval **result, zval *op1, zval *op2) { - switch (Z_TYPE_P(op2)) { - case IS_STRING: - *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2)); - return SUCCESS; - default: - return FAILURE; - } -} - -static inline int ct_eval_fetch_obj(zval *result, zval *op1, zval *op2) { - if (IS_PARTIAL_OBJECT(op1)) { - zval *value; - if (fetch_obj_prop(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) { - ZVAL_COPY(result, value); - return SUCCESS; - } - } - return FAILURE; -} - -static inline int ct_eval_isset_obj(zval *result, uint32_t extended_value, zval *op1, zval *op2) { - if (IS_PARTIAL_OBJECT(op1)) { - zval *value; - if (fetch_obj_prop(&value, op1, op2) == FAILURE) { - return FAILURE; - } - if (!value || IS_BOT(value)) { - return FAILURE; - } - return ct_eval_isset_isempty(result, extended_value, value); - } else { - ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY)); - return SUCCESS; - } -} - -static inline int ct_eval_del_obj_prop(zval *result, zval *key) { - ZEND_ASSERT(IS_PARTIAL_OBJECT(result)); - - switch (Z_TYPE_P(key)) { - case IS_STRING: - zend_symtable_del(Z_ARR_P(result), Z_STR_P(key)); - break; - default: - return FAILURE; - } - - return SUCCESS; -} - -static inline int ct_eval_add_obj_prop(zval *result, zval *value, zval *key) { - switch (Z_TYPE_P(key)) { - case IS_STRING: - value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value); - break; - default: - return FAILURE; - } - - Z_TRY_ADDREF_P(value); - return SUCCESS; -} - -static inline int ct_eval_assign_obj(zval *result, zval *value, zval *key) { - switch (Z_TYPE_P(result)) { - case IS_NULL: - case IS_FALSE: - empty_partial_object(result); - /* break missing intentionally */ - case PARTIAL_OBJECT: - return ct_eval_add_obj_prop(result, value, key); - default: - return FAILURE; - } -} - -static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) { - ZVAL_COPY(result, op1); - if (opcode == ZEND_PRE_INC - || opcode == ZEND_POST_INC - || opcode == ZEND_PRE_INC_OBJ - || opcode == ZEND_POST_INC_OBJ) { - increment_function(result); - } else { - decrement_function(result); - } - return SUCCESS; -} - -static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) { - uint32_t type = Z_TYPE_P(op1); - if (type == PARTIAL_ARRAY) { - type = IS_ARRAY; - } else if (type == PARTIAL_OBJECT) { - type = IS_OBJECT; - } - ZVAL_BOOL(result, (type_mask >> type) & 1); -} - -static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) { - HashTable *ht; - bool res; - - if (Z_TYPE_P(op2) != IS_ARRAY) { - return FAILURE; - } - ht = Z_ARRVAL_P(op2); - if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { - res = zend_hash_exists(ht, Z_STR_P(op1)); - } else if (extended_value) { - if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { - res = zend_hash_index_exists(ht, Z_LVAL_P(op1)); - } else { - res = 0; - } - } else if (Z_TYPE_P(op1) <= IS_FALSE) { - res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()); - } else { - zend_string *key; - zval key_tmp; - - res = 0; - ZEND_HASH_FOREACH_STR_KEY(ht, key) { - ZVAL_STR(&key_tmp, key); - if (zend_compare(op1, &key_tmp) == 0) { - res = 1; - break; - } - } ZEND_HASH_FOREACH_END(); - } - ZVAL_BOOL(result, res); - return SUCCESS; -} - -static inline int ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) { - zval *value; - - if (Z_TYPE_P(op2) != IS_ARRAY && !IS_PARTIAL_ARRAY(op2)) { - return FAILURE; - } - if (Z_TYPE_P(op1) != IS_STRING && Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_NULL) { - return FAILURE; - } - if (fetch_array_elem(&value, op2, op1) == FAILURE) { - return FAILURE; - } - if (IS_PARTIAL_ARRAY(op2) && (!value || IS_BOT(value))) { - return FAILURE; - } - - ZVAL_BOOL(result, value != NULL); - return SUCCESS; -} - -static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **args) { - /* Functions that can be evaluated independently of what the arguments are. - * It's okay if these functions throw on invalid arguments, but they should not warn. */ - if (false - || zend_string_equals_literal(name, "array_diff") - || zend_string_equals_literal(name, "array_diff_assoc") - || zend_string_equals_literal(name, "array_diff_key") - || zend_string_equals_literal(name, "array_is_list") - || zend_string_equals_literal(name, "array_key_exists") - || zend_string_equals_literal(name, "array_keys") - || zend_string_equals_literal(name, "array_merge") - || zend_string_equals_literal(name, "array_merge_recursive") - || zend_string_equals_literal(name, "array_replace") - || zend_string_equals_literal(name, "array_replace_recursive") - || zend_string_equals_literal(name, "array_values") - || zend_string_equals_literal(name, "base64_decode") - || zend_string_equals_literal(name, "base64_encode") -#ifndef ZEND_WIN32 - /* On Windows this function may be code page dependent. */ - || zend_string_equals_literal(name, "dirname") -#endif - || zend_string_equals_literal(name, "imagetypes") - || zend_string_equals_literal(name, "in_array") - || zend_string_equals_literal(name, "ltrim") - || zend_string_equals_literal(name, "php_sapi_name") - || zend_string_equals_literal(name, "php_uname") - || zend_string_equals_literal(name, "phpversion") - || zend_string_equals_literal(name, "pow") - || zend_string_equals_literal(name, "preg_quote") - || zend_string_equals_literal(name, "rawurldecode") - || zend_string_equals_literal(name, "rawurlencode") - || zend_string_equals_literal(name, "rtrim") - || zend_string_equals_literal(name, "serialize") - || zend_string_equals_literal(name, "str_contains") - || zend_string_equals_literal(name, "str_ends_with") - || zend_string_equals_literal(name, "str_split") - || zend_string_equals_literal(name, "str_starts_with") - || zend_string_equals_literal(name, "strpos") - || zend_string_equals_literal(name, "substr") - || zend_string_equals_literal(name, "trim") - || zend_string_equals_literal(name, "urldecode") - || zend_string_equals_literal(name, "urlencode") - || zend_string_equals_literal(name, "version_compare") - ) { - return true; - } - - /* For the following functions we need to check arguments to prevent warnings during - * evaluation. */ - if (num_args == 1) { - if (zend_string_equals_literal(name, "array_flip")) { - zval *entry; - - if (Z_TYPE_P(args[0]) != IS_ARRAY) { - return false; - } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) { - /* Throws warning for non int/string values. */ - if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) { - return false; - } - } ZEND_HASH_FOREACH_END(); - return true; - } - if (zend_string_equals_literal(name, "implode")) { - zval *entry; - - if (Z_TYPE_P(args[0]) != IS_ARRAY) { - return false; - } - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) { - /* May throw warning during conversion to string. */ - if (Z_TYPE_P(entry) > IS_STRING) { - return false; - } - } ZEND_HASH_FOREACH_END(); - return true; - } - return false; - } - - if (num_args == 2) { - if (zend_string_equals_literal(name, "str_repeat")) { - /* Avoid creating overly large strings at compile-time. */ - bool overflow; - return Z_TYPE_P(args[0]) == IS_STRING - && Z_TYPE_P(args[1]) == IS_LONG - && zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024 - && !overflow; - } else if (zend_string_equals_literal(name, "implode")) { - zval *entry; - - if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY) - && (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) { - return false; - } - - /* May throw warning during conversion to string. */ - if (Z_TYPE_P(args[0]) == IS_ARRAY) { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) { - if (Z_TYPE_P(entry) > IS_STRING) { - return false; - } - } ZEND_HASH_FOREACH_END(); - } else { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) { - if (Z_TYPE_P(entry) > IS_STRING) { - return false; - } - } ZEND_HASH_FOREACH_END(); - } - return true; - } - return false; - } - - return false; -} - -/* The functions chosen here are simple to implement and either likely to affect a branch, - * or just happened to be commonly used with constant operands in WP (need to test other - * applications as well, of course). */ -static inline int ct_eval_func_call( - zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) { - uint32_t i; - zend_function *func = zend_hash_find_ptr(CG(function_table), name); - if (!func || func->type != ZEND_INTERNAL_FUNCTION) { - return FAILURE; - } - - if (num_args == 1) { - /* Handle a few functions for which we manually implement evaluation here. */ - if (zend_string_equals_literal(name, "chr")) { - zend_long c; - if (Z_TYPE_P(args[0]) != IS_LONG) { - return FAILURE; - } - - c = Z_LVAL_P(args[0]) & 0xff; - ZVAL_CHAR(result, c); - return SUCCESS; - } else if (zend_string_equals_literal(name, "count")) { - if (Z_TYPE_P(args[0]) != IS_ARRAY) { - return FAILURE; - } - - ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0]))); - return SUCCESS; - } else if (zend_string_equals_literal(name, "ini_get")) { - zend_ini_entry *ini_entry; - - if (Z_TYPE_P(args[0]) != IS_STRING) { - return FAILURE; - } - - ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0])); - if (!ini_entry) { - ZVAL_FALSE(result); - } else if (ini_entry->modifiable != ZEND_INI_SYSTEM) { - return FAILURE; - } else if (ini_entry->value) { - ZVAL_STR_COPY(result, ini_entry->value); - } else { - ZVAL_EMPTY_STRING(result); - } - return SUCCESS; - } - } - - if (!can_ct_eval_func_call(name, num_args, args)) { - return FAILURE; - } - - zend_execute_data *prev_execute_data = EG(current_execute_data); - zend_execute_data *execute_data, dummy_frame; - zend_op dummy_opline; - - /* Add a dummy frame to get the correct strict_types behavior. */ - memset(&dummy_frame, 0, sizeof(zend_execute_data)); - memset(&dummy_opline, 0, sizeof(zend_op)); - dummy_frame.func = (zend_function *) op_array; - dummy_frame.opline = &dummy_opline; - dummy_opline.opcode = ZEND_DO_FCALL; - - execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval)); - memset(execute_data, 0, sizeof(zend_execute_data)); - execute_data->prev_execute_data = &dummy_frame; - EG(current_execute_data) = execute_data; - - EX(func) = func; - EX_NUM_ARGS() = num_args; - for (i = 0; i < num_args; i++) { - ZVAL_COPY(EX_VAR_NUM(i), args[i]); - } - ZVAL_NULL(result); - func->internal_function.handler(execute_data, result); - for (i = 0; i < num_args; i++) { - zval_ptr_dtor_nogc(EX_VAR_NUM(i)); - } - - int retval = SUCCESS; - if (EG(exception)) { - zval_ptr_dtor(result); - zend_clear_exception(); - retval = FAILURE; - } - - efree(execute_data); - EG(current_execute_data) = prev_execute_data; - return retval; -} - -#define SET_RESULT(op, zv) do { \ - if (ssa_op->op##_def >= 0) { \ - set_value(scdf, ctx, ssa_op->op##_def, zv); \ - } \ -} while (0) -#define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot) -#define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top) - -#define SKIP_IF_TOP(op) if (IS_TOP(op)) return; - -static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) { - sccp_ctx *ctx = (sccp_ctx *) scdf; - zval *op1, *op2, zv; /* zv is a temporary to hold result values */ - - op1 = get_op1_value(ctx, opline, ssa_op); - op2 = get_op2_value(ctx, opline, ssa_op); - - switch (opline->opcode) { - case ZEND_ASSIGN: - /* The value of op1 is irrelevant here, because we are overwriting it - * -- unless it can be a reference, in which case we propagate a BOT. */ - if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) { - SET_RESULT_BOT(op1); - } else { - SET_RESULT(op1, op2); - } - - SET_RESULT(result, op2); - return; - case ZEND_TYPE_CHECK: - /* We may be able to evaluate TYPE_CHECK based on type inference info, - * even if we don't know the precise value. */ - if (!value_known(op1)) { - uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type; - uint32_t expected_type_mask = opline->extended_value; - if (!(type & expected_type_mask) && !(type & MAY_BE_UNDEF)) { - ZVAL_FALSE(&zv); - SET_RESULT(result, &zv); - return; - } else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type_mask)) - && !(expected_type_mask & MAY_BE_RESOURCE)) { - ZVAL_TRUE(&zv); - SET_RESULT(result, &zv); - return; - } - } - break; - case ZEND_ASSIGN_DIM: - { - zval *data = get_op1_value(ctx, opline+1, ssa_op+1); - - /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */ - if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) { - op1 = &EG(uninitialized_zval); - } - - if (IS_BOT(op1)) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - return; - } - - SKIP_IF_TOP(op1); - SKIP_IF_TOP(data); - if (op2) { - SKIP_IF_TOP(op2); - } - - if (op2 && IS_BOT(op2)) { - /* Update of unknown index */ - SET_RESULT_BOT(result); - if (ssa_op->op1_def >= 0) { - empty_partial_array(&zv); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&zv); - } else { - SET_RESULT_BOT(op1); - } - return; - } - - if (IS_BOT(data)) { - - SET_RESULT_BOT(result); - if ((IS_PARTIAL_ARRAY(op1) - || Z_TYPE_P(op1) == IS_NULL - || Z_TYPE_P(op1) == IS_FALSE - || Z_TYPE_P(op1) == IS_ARRAY) - && ssa_op->op1_def >= 0) { - - if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) { - empty_partial_array(&zv); - } else { - dup_partial_array(&zv, op1); - } - - if (!op2) { - /* We can't add NEXT element into partial array (skip it) */ - SET_RESULT(op1, &zv); - } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) { - SET_RESULT(op1, &zv); - } else { - SET_RESULT_BOT(op1); - } - - zval_ptr_dtor_nogc(&zv); - } else { - SET_RESULT_BOT(op1); - } - - } else { - - if (IS_PARTIAL_ARRAY(op1)) { - dup_partial_array(&zv, op1); - } else { - ZVAL_COPY(&zv, op1); - } - - if (!op2 && IS_PARTIAL_ARRAY(&zv)) { - /* We can't add NEXT element into partial array (skip it) */ - SET_RESULT(result, data); - SET_RESULT(op1, &zv); - } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) { - /* Mark array containing partial array as partial */ - if (IS_PARTIAL_ARRAY(data)) { - MAKE_PARTIAL_ARRAY(&zv); - } - SET_RESULT(result, data); - SET_RESULT(op1, &zv); - } else { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - } - - zval_ptr_dtor_nogc(&zv); - } - return; - } - - case ZEND_ASSIGN_OBJ: - if (ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { - zval *data = get_op1_value(ctx, opline+1, ssa_op+1); - zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use]; - - /* Don't try to propagate assignments to (potentially) typed properties. We would - * need to deal with errors and type conversions first. */ - if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - return; - } - - if (IS_BOT(op1)) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - return; - } - - SKIP_IF_TOP(op1); - SKIP_IF_TOP(data); - SKIP_IF_TOP(op2); - - if (IS_BOT(op2)) { - /* Update of unknown property */ - SET_RESULT_BOT(result); - empty_partial_object(&zv); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&zv); - return; - } - - if (IS_BOT(data)) { - SET_RESULT_BOT(result); - if (IS_PARTIAL_OBJECT(op1) - || Z_TYPE_P(op1) == IS_NULL - || Z_TYPE_P(op1) == IS_FALSE) { - - if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) { - empty_partial_object(&zv); - } else { - dup_partial_object(&zv, op1); - } - - if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) { - SET_RESULT(op1, &zv); - } else { - SET_RESULT_BOT(op1); - } - zval_ptr_dtor_nogc(&zv); - } else { - SET_RESULT_BOT(op1); - } - - } else { - - if (IS_PARTIAL_OBJECT(op1)) { - dup_partial_object(&zv, op1); - } else { - ZVAL_COPY(&zv, op1); - } - - if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) { - SET_RESULT(result, data); - SET_RESULT(op1, &zv); - } else { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - } - - zval_ptr_dtor_nogc(&zv); - } - } else { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - } - return; - - case ZEND_SEND_VAL: - case ZEND_SEND_VAR: - { - /* If the value of a SEND for an ICALL changes, we need to reconsider the - * ICALL result value. Otherwise we can ignore the opcode. */ - zend_call_info *call; - if (!ctx->call_map) { - return; - } - - call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - if (IS_TOP(op1) || !call || !call->caller_call_opline - || call->caller_call_opline->opcode != ZEND_DO_ICALL) { - return; - } - - opline = call->caller_call_opline; - ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]; - break; - } - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - { - zval *result = NULL; - - if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) { - result = &ctx->values[ssa_op->result_use]; - if (IS_BOT(result)) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - return; - } - SKIP_IF_TOP(result); - } - - if (op1) { - SKIP_IF_TOP(op1); - } - - if (op2) { - SKIP_IF_TOP(op2); - } - - /* We want to avoid keeping around intermediate arrays for each SSA variable in the - * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode - * and use a NULL value everywhere else. */ - if (result && Z_TYPE_P(result) == IS_NULL) { - SET_RESULT_BOT(result); - return; - } - - if (op2 && IS_BOT(op2)) { - /* Update of unknown index */ - SET_RESULT_BOT(op1); - if (ssa_op->result_def >= 0) { - empty_partial_array(&zv); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - } else { - SET_RESULT_BOT(result); - } - return; - } - - if ((op1 && IS_BOT(op1)) - || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { - - SET_RESULT_BOT(op1); - if (ssa_op->result_def >= 0) { - if (!result) { - empty_partial_array(&zv); - } else { - MAKE_PARTIAL_ARRAY(result); - ZVAL_COPY_VALUE(&zv, result); - ZVAL_NULL(result); - } - if (!op2) { - /* We can't add NEXT element into partial array (skip it) */ - SET_RESULT(result, &zv); - } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) { - SET_RESULT(result, &zv); - } else { - SET_RESULT_BOT(result); - } - zval_ptr_dtor_nogc(&zv); - } else { - /* If any operand is BOT, mark the result as BOT right away. - * Exceptions to this rule are handled above. */ - SET_RESULT_BOT(result); - } - - } else { - if (result) { - ZVAL_COPY_VALUE(&zv, result); - ZVAL_NULL(result); - } else { - array_init(&zv); - } - - if (op1) { - if (!op2 && IS_PARTIAL_ARRAY(&zv)) { - /* We can't add NEXT element into partial array (skip it) */ - SET_RESULT(result, &zv); - } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) { - if (IS_PARTIAL_ARRAY(op1)) { - MAKE_PARTIAL_ARRAY(&zv); - } - SET_RESULT(result, &zv); - } else { - SET_RESULT_BOT(result); - } - } else { - SET_RESULT(result, &zv); - } - - zval_ptr_dtor_nogc(&zv); - } - return; - } - case ZEND_ADD_ARRAY_UNPACK: { - zval *result = &ctx->values[ssa_op->result_use]; - if (IS_BOT(result) || IS_BOT(op1)) { - SET_RESULT_BOT(result); - return; - } - SKIP_IF_TOP(result); - SKIP_IF_TOP(op1); - - /* See comment for ADD_ARRAY_ELEMENT. */ - if (Z_TYPE_P(result) == IS_NULL) { - SET_RESULT_BOT(result); - return; - } - ZVAL_COPY_VALUE(&zv, result); - ZVAL_NULL(result); - - if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) { - SET_RESULT(result, &zv); - } else { - SET_RESULT_BOT(result); - } - zval_ptr_dtor_nogc(&zv); - return; - } - case ZEND_NEW: - if (ssa_op->result_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { - empty_partial_object(&zv); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - } else { - SET_RESULT_BOT(result); - } - return; - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_ASSIGN_OBJ_REF: - /* Handled here because we also need to BOT the OP_DATA operand, while the generic - * code below will not do so. */ - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - SET_RESULT_BOT(op2); - opline++; - ssa_op++; - SET_RESULT_BOT(op1); - break; - } - - if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) { - /* If any operand is BOT, mark the result as BOT right away. - * Exceptions to this rule are handled above. */ - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - SET_RESULT_BOT(op2); - return; - } - - switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_POW: - case ZEND_SL: - case ZEND_SR: - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - case ZEND_BOOL_XOR: - case ZEND_CASE: - case ZEND_CASE_STRICT: - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - - if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - if (op1) { - SKIP_IF_TOP(op1); - } - if (op2) { - SKIP_IF_TOP(op2); - } - if (opline->opcode == ZEND_ASSIGN_OP) { - if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) { - SET_RESULT(op1, &zv); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) { - if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY) - && ssa_op->op1_def >= 0 && op2) { - zval tmp; - zval *data = get_op1_value(ctx, opline+1, ssa_op+1); - - SKIP_IF_TOP(data); - - if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) { - if (IS_BOT(data)) { - dup_partial_array(&zv, op1); - ct_eval_del_array_elem(&zv, op2); - SET_RESULT_BOT(result); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - break; - } - - if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - zval_ptr_dtor_nogc(&tmp); - break; - } - - if (IS_PARTIAL_ARRAY(op1)) { - dup_partial_array(&zv, op1); - } else { - ZVAL_COPY(&zv, op1); - } - - if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) { - SET_RESULT(result, &tmp); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - break; - } - - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - } - } - } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { - if (op1 && IS_PARTIAL_OBJECT(op1) - && ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { - zval tmp; - zval *data = get_op1_value(ctx, opline+1, ssa_op+1); - - SKIP_IF_TOP(data); - - if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) { - if (IS_BOT(data)) { - dup_partial_object(&zv, op1); - ct_eval_del_obj_prop(&zv, op2); - SET_RESULT_BOT(result); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - break; - } - - if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) != SUCCESS) { - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - zval_ptr_dtor_nogc(&tmp); - break; - } - - dup_partial_object(&zv, op1); - - if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) { - SET_RESULT(result, &tmp); - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - break; - } - - zval_ptr_dtor_nogc(&tmp); - zval_ptr_dtor_nogc(&zv); - } - } - } - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - if (op1) { - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - if (IS_PARTIAL_OBJECT(op1) - && ssa_op->op1_def >= 0 - && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) { - zval tmp1, tmp2; - - if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS - && ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) { - - dup_partial_object(&zv, op1); - ct_eval_assign_obj(&zv, &tmp2, op2); - if (opline->opcode == ZEND_PRE_INC_OBJ - || opline->opcode == ZEND_PRE_DEC_OBJ) { - SET_RESULT(result, &tmp2); - } else { - SET_RESULT(result, &tmp1); - } - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - } - } - SET_RESULT_BOT(op1); - SET_RESULT_BOT(result); - break; - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - SKIP_IF_TOP(op1); - if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) { - SET_RESULT(op1, &zv); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(op1); - SET_RESULT_BOT(result); - break; - case ZEND_POST_INC: - case ZEND_POST_DEC: - SKIP_IF_TOP(op1); - SET_RESULT(result, op1); - if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) { - SET_RESULT(op1, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(op1); - break; - case ZEND_BW_NOT: - case ZEND_BOOL_NOT: - SKIP_IF_TOP(op1); - if (IS_PARTIAL_ARRAY(op1)) { - SET_RESULT_BOT(result); - break; - } - if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_CAST: - SKIP_IF_TOP(op1); - if (IS_PARTIAL_ARRAY(op1)) { - SET_RESULT_BOT(result); - break; - } - if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_BOOL: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - SKIP_IF_TOP(op1); - if (ct_eval_bool_cast(&zv, op1) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_STRLEN: - SKIP_IF_TOP(op1); - if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_YIELD_FROM: - // tmp = yield from [] -> tmp = null - SKIP_IF_TOP(op1); - if (Z_TYPE_P(op1) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(op1)) == 0) { - ZVAL_NULL(&zv); - SET_RESULT(result, &zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_COUNT: - SKIP_IF_TOP(op1); - if (Z_TYPE_P(op1) == IS_ARRAY) { - ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1))); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_IN_ARRAY: - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_ARRAY_KEY_EXISTS: - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_FETCH_DIM_R: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_LIST_R: - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - - if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - - if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_IS: - if (op1) { - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - - if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - } - SET_RESULT_BOT(result); - break; - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - if (op1) { - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - - if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - } - SET_RESULT_BOT(result); - break; - case ZEND_QM_ASSIGN: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_COPY_TMP: - SET_RESULT(result, op1); - break; - case ZEND_JMP_NULL: - switch (opline->extended_value) { - case ZEND_SHORT_CIRCUITING_CHAIN_EXPR: - ZVAL_NULL(&zv); - break; - case ZEND_SHORT_CIRCUITING_CHAIN_ISSET: - ZVAL_FALSE(&zv); - break; - case ZEND_SHORT_CIRCUITING_CHAIN_EMPTY: - ZVAL_TRUE(&zv); - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - SET_RESULT(result, &zv); - break; -#if 0 - case ZEND_FETCH_CLASS: - if (!op1) { - SET_RESULT_BOT(result); - break; - } - SET_RESULT(result, op1); - break; -#endif - case ZEND_ISSET_ISEMPTY_CV: - SKIP_IF_TOP(op1); - if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_TYPE_CHECK: - SKIP_IF_TOP(op1); - ct_eval_type_check(&zv, opline->extended_value, op1); - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - case ZEND_INSTANCEOF: - SKIP_IF_TOP(op1); - ZVAL_FALSE(&zv); - SET_RESULT(result, &zv); - break; - case ZEND_ROPE_INIT: - SKIP_IF_TOP(op2); - if (IS_PARTIAL_ARRAY(op2)) { - SET_RESULT_BOT(result); - break; - } - if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_ROPE_ADD: - case ZEND_ROPE_END: - // TODO The way this is currently implemented will result in quadratic runtime - // This is not necessary, the way the algorithm works it's okay to reuse the same - // string for all SSA vars with some extra checks - SKIP_IF_TOP(op1); - SKIP_IF_TOP(op2); - if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - SET_RESULT_BOT(result); - break; - case ZEND_DO_ICALL: - { - zend_call_info *call; - zval *name, *args[3] = {NULL}; - int i; - - if (!ctx->call_map) { - SET_RESULT_BOT(result); - break; - } - - call = ctx->call_map[opline - ctx->scdf.op_array->opcodes]; - name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant); - - /* We already know it can't be evaluated, don't bother checking again */ - if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) { - break; - } - - /* We're only interested in functions with up to three arguments right now */ - if (call->num_args > 3 || call->send_unpack) { - SET_RESULT_BOT(result); - break; - } - - for (i = 0; i < call->num_args; i++) { - zend_op *opline = call->arg_info[i].opline; - if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) { - SET_RESULT_BOT(result); - return; - } - - args[i] = get_op1_value(ctx, opline, - &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]); - if (args[i]) { - if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) { - SET_RESULT_BOT(result); - return; - } else if (IS_TOP(args[i])) { - return; - } - } - } - - /* We didn't get a BOT argument, so value stays the same */ - if (!IS_TOP(&ctx->values[ssa_op->result_def])) { - break; - } - - if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) { - SET_RESULT(result, &zv); - zval_ptr_dtor_nogc(&zv); - break; - } - -#if 0 - /* sort out | uniq -c | sort -n */ - fprintf(stderr, "%s\n", Z_STRVAL_P(name)); - /*if (args[1]) { - php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]); - } else { - php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]); - }*/ -#endif - - SET_RESULT_BOT(result); - break; - } - default: - { - /* If we have no explicit implementation return BOT */ - SET_RESULT_BOT(result); - SET_RESULT_BOT(op1); - SET_RESULT_BOT(op2); - break; - } - } -} - -/* Returns whether there is a successor */ -static void sccp_mark_feasible_successors( - scdf_ctx *scdf, - int block_num, zend_basic_block *block, - zend_op *opline, zend_ssa_op *ssa_op) { - sccp_ctx *ctx = (sccp_ctx *) scdf; - zval *op1, zv; - int s; - - /* We can't determine the branch target at compile-time for these */ - switch (opline->opcode) { - case ZEND_ASSERT_CHECK: - case ZEND_CATCH: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); - scdf_mark_edge_feasible(scdf, block_num, block->successors[1]); - return; - } - - op1 = get_op1_value(ctx, opline, ssa_op); - - /* Branch target can be either one */ - if (!op1 || IS_BOT(op1)) { - for (s = 0; s < block->successors_count; s++) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[s]); - } - return; - } - - /* Branch target not yet known */ - if (IS_TOP(op1)) { - return; - } - - switch (opline->opcode) { - case ZEND_JMPZ: - case ZEND_JMPZNZ: - case ZEND_JMPZ_EX: - { - if (ct_eval_bool_cast(&zv, op1) == FAILURE) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); - scdf_mark_edge_feasible(scdf, block_num, block->successors[1]); - return; - } - s = Z_TYPE(zv) == IS_TRUE; - break; - } - case ZEND_JMPNZ: - case ZEND_JMPNZ_EX: - case ZEND_JMP_SET: - { - if (ct_eval_bool_cast(&zv, op1) == FAILURE) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); - scdf_mark_edge_feasible(scdf, block_num, block->successors[1]); - return; - } - s = Z_TYPE(zv) == IS_FALSE; - break; - } - case ZEND_COALESCE: - s = (Z_TYPE_P(op1) == IS_NULL); - break; - case ZEND_JMP_NULL: - s = (Z_TYPE_P(op1) != IS_NULL); - break; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - /* A non-empty partial array is definitely non-empty, but an - * empty partial array may be non-empty at runtime. */ - if (Z_TYPE_P(op1) != IS_ARRAY || - (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); - scdf_mark_edge_feasible(scdf, block_num, block->successors[1]); - return; - } - s = zend_hash_num_elements(Z_ARR_P(op1)) != 0; - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - bool strict_comparison = opline->opcode == ZEND_MATCH; - zend_uchar type = Z_TYPE_P(op1); - bool correct_type = - (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG) - || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING) - || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING)); - - if (correct_type) { - zend_op_array *op_array = scdf->op_array; - zend_ssa *ssa = scdf->ssa; - HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)); - zval *jmp_zv = type == IS_LONG - ? zend_hash_index_find(jmptable, Z_LVAL_P(op1)) - : zend_hash_find(jmptable, Z_STR_P(op1)); - int target; - - if (jmp_zv) { - target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))]; - } else { - target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; - } - scdf_mark_edge_feasible(scdf, block_num, target); - return; - } else if (strict_comparison) { - zend_op_array *op_array = scdf->op_array; - zend_ssa *ssa = scdf->ssa; - int target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; - scdf_mark_edge_feasible(scdf, block_num, target); - return; - } - s = 0; - break; - } - default: - for (s = 0; s < block->successors_count; s++) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[s]); - } - return; - } - scdf_mark_edge_feasible(scdf, block_num, block->successors[s]); -} - -static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2) -{ - zend_ulong index; - zend_string *key; - zval *val1, *val2; - - ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) { - if (key) { - val2 = zend_hash_find(ht2, key); - } else { - val2 = zend_hash_index_find(ht2, index); - } - if (val2 && zend_is_identical(val1, val2)) { - if (key) { - val1 = zend_hash_add_new(ret, key, val1); - } else { - val1 = zend_hash_index_add_new(ret, index, val1); - } - Z_TRY_ADDREF_P(val1); - } - } ZEND_HASH_FOREACH_END(); -} - -static int join_partial_arrays(zval *a, zval *b) -{ - zval ret; - - if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a)) - || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) { - return FAILURE; - } - - empty_partial_array(&ret); - join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b)); - zval_ptr_dtor_nogc(a); - ZVAL_COPY_VALUE(a, &ret); - - return SUCCESS; -} - -static int join_partial_objects(zval *a, zval *b) -{ - zval ret; - - if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) { - return FAILURE; - } - - empty_partial_object(&ret); - join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b)); - zval_ptr_dtor_nogc(a); - ZVAL_COPY_VALUE(a, &ret); - - return SUCCESS; -} - -static void join_phi_values(zval *a, zval *b, bool escape) { - if (IS_BOT(a) || IS_TOP(b)) { - return; - } - if (IS_TOP(a)) { - zval_ptr_dtor_nogc(a); - ZVAL_COPY(a, b); - return; - } - if (IS_BOT(b)) { - zval_ptr_dtor_nogc(a); - MAKE_BOT(a); - return; - } - if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) { - if (join_partial_arrays(a, b) != SUCCESS) { - zval_ptr_dtor_nogc(a); - MAKE_BOT(a); - } - } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) { - if (escape || join_partial_objects(a, b) != SUCCESS) { - zval_ptr_dtor_nogc(a); - MAKE_BOT(a); - } - } else if (!zend_is_identical(a, b)) { - if (join_partial_arrays(a, b) != SUCCESS) { - zval_ptr_dtor_nogc(a); - MAKE_BOT(a); - } - } -} - -static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) { - sccp_ctx *ctx = (sccp_ctx *) scdf; - zend_ssa *ssa = scdf->ssa; - ZEND_ASSERT(phi->ssa_var >= 0); - if (!IS_BOT(&ctx->values[phi->ssa_var])) { - zend_basic_block *block = &ssa->cfg.blocks[phi->block]; - int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset]; - - int i; - zval result; - MAKE_TOP(&result); -#if SCP_DEBUG - fprintf(stderr, "Handling phi("); -#endif - if (phi->pi >= 0) { - ZEND_ASSERT(phi->sources[0] >= 0); - if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) { - join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE); - } - } else { - for (i = 0; i < block->predecessors_count; i++) { - ZEND_ASSERT(phi->sources[i] >= 0); - if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) { -#if SCP_DEBUG - scp_dump_value(&ctx->values[phi->sources[i]]); - fprintf(stderr, ","); -#endif - join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE); - } else { -#if SCP_DEBUG - fprintf(stderr, " --,"); -#endif - } - } - } -#if SCP_DEBUG - fprintf(stderr, ")\n"); -#endif - - set_value(scdf, ctx, phi->ssa_var, &result); - zval_ptr_dtor_nogc(&result); - } -} - -static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) { - zend_ssa *ssa = ctx->scdf.ssa; - zend_ssa_var_info *info = &ssa->var_info[var_num]; - - if (info->type & MAY_BE_UNDEF) { - return NULL; - } - - if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) { - ZVAL_NULL(tmp); - return tmp; - } - if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) { - ZVAL_FALSE(tmp); - return tmp; - } - if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) { - ZVAL_TRUE(tmp); - return tmp; - } - - if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG)) - && info->has_range - && !info->range.overflow && !info->range.underflow - && info->range.min == info->range.max) { - ZVAL_LONG(tmp, info->range.min); - return tmp; - } - - return NULL; -} - -/* Call instruction -> remove opcodes that are part of the call */ -static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) -{ - zend_ssa *ssa = ctx->scdf.ssa; - zend_op_array *op_array = ctx->scdf.op_array; - zend_call_info *call; - int i; - - ZEND_ASSERT(ctx->call_map); - call = ctx->call_map[opline - op_array->opcodes]; - ZEND_ASSERT(call); - ZEND_ASSERT(call->caller_call_opline == opline); - zend_ssa_remove_instr(ssa, opline, ssa_op); - zend_ssa_remove_instr(ssa, call->caller_init_opline, - &ssa->ops[call->caller_init_opline - op_array->opcodes]); - - for (i = 0; i < call->num_args; i++) { - zend_ssa_remove_instr(ssa, call->arg_info[i].opline, - &ssa->ops[call->arg_info[i].opline - op_array->opcodes]); - } - - // TODO: remove call_info completely??? - call->callee_func = NULL; - - return call->num_args + 2; -} - -/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result - * value(s) were determined by SCCP. It removes dead computational instructions and converts - * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons: - * a) During operand replacement we eliminate FREEs. The corresponding computational instructions - * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass. - * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects - * and can't be DCEd. This means that it will not be able collect all instructions rendered dead - * by SCCP, because they may have potentially side-effecting types, but the actual values are - * not. As such doing DCE here will allow us to eliminate more dead code in combination. - * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which - * we need to collect. - * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects. - */ -static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value) -{ - zend_ssa *ssa = ctx->scdf.ssa; - zend_op_array *op_array = ctx->scdf.op_array; - int removed_ops = 0; - - if (var->definition >= 0) { - zend_op *opline = &op_array->opcodes[var->definition]; - zend_ssa_op *ssa_op = &ssa->ops[var->definition]; - - if (opline->opcode == ZEND_ASSIGN) { - /* Leave assigns to DCE (due to dtor effects) */ - return 0; - } - - if (ssa_op->result_def == var_num) { - if (ssa_op->op1_def >= 0 - || ssa_op->op2_def >= 0) { - /* we cannot remove instruction that defines other variables */ - return 0; - } else if (opline->opcode == ZEND_JMPZ_EX - || opline->opcode == ZEND_JMPNZ_EX - || opline->opcode == ZEND_JMP_SET - || opline->opcode == ZEND_COALESCE - || opline->opcode == ZEND_JMP_NULL - || opline->opcode == ZEND_FE_RESET_R - || opline->opcode == ZEND_FE_RESET_RW - || opline->opcode == ZEND_FE_FETCH_R - || opline->opcode == ZEND_FE_FETCH_RW - || opline->opcode == ZEND_NEW) { - /* we cannot simple remove jump instructions */ - return 0; - } else if (var->use_chain >= 0 - || var->phi_use_chain != NULL) { - if (value - && (opline->result_type & (IS_VAR|IS_TMP_VAR)) - && opline->opcode != ZEND_QM_ASSIGN - && opline->opcode != ZEND_ROPE_INIT - && opline->opcode != ZEND_ROPE_ADD - && opline->opcode != ZEND_INIT_ARRAY - && opline->opcode != ZEND_ADD_ARRAY_ELEMENT - && opline->opcode != ZEND_ADD_ARRAY_UNPACK) { - /* Replace with QM_ASSIGN */ - zend_uchar old_type = opline->result_type; - uint32_t old_var = opline->result.var; - - ssa_op->result_def = -1; - if (opline->opcode == ZEND_DO_ICALL) { - removed_ops = remove_call(ctx, opline, ssa_op) - 1; - } else { - zend_ssa_remove_instr(ssa, opline, ssa_op); - } - ssa_op->result_def = var_num; - opline->opcode = ZEND_QM_ASSIGN; - opline->result_type = old_type; - opline->result.var = old_var; - Z_TRY_ADDREF_P(value); - zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, value); - } - return 0; - } else { - zend_ssa_remove_result_def(ssa, ssa_op); - if (opline->opcode == ZEND_DO_ICALL) { - removed_ops = remove_call(ctx, opline, ssa_op); - } else if (opline->opcode == ZEND_TYPE_CHECK - && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && !value_known(&ctx->values[ssa_op->op1_use])) { - /* For TYPE_CHECK we may compute the result value without knowing the - * operand, based on type inference information. Make sure the operand is - * freed and leave further cleanup to DCE. */ - opline->opcode = ZEND_FREE; - opline->result_type = IS_UNUSED; - removed_ops++; - } else { - zend_ssa_remove_instr(ssa, opline, ssa_op); - removed_ops++; - } - } - } else if (ssa_op->op1_def == var_num) { - /* Compound assign or incdec -> convert to direct ASSIGN */ - - if (!value) { - /* In some cases zend_may_throw() may be avoided */ - switch (opline->opcode) { - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) - || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) { - return 0; - } - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) { - return 0; - } - break; - default: - if (zend_may_throw(opline, ssa_op, op_array, ssa)) { - return 0; - } - break; - } - } - - /* Mark result unused, if possible */ - if (ssa_op->result_def >= 0) { - if (ssa->vars[ssa_op->result_def].use_chain < 0 - && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - zend_ssa_remove_result_def(ssa, ssa_op); - opline->result_type = IS_UNUSED; - } else if (opline->opcode != ZEND_PRE_INC && - opline->opcode != ZEND_PRE_DEC) { - /* op1_def and result_def are different */ - return removed_ops; - } - } - - /* Destroy previous op2 */ - if (opline->op2_type == IS_CONST) { - literal_dtor(&ZEND_OP2_LITERAL(opline)); - } else if (ssa_op->op2_use >= 0) { - if (ssa_op->op2_use != ssa_op->op1_use) { - zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use); - } - ssa_op->op2_use = -1; - ssa_op->op2_use_chain = -1; - } - - /* Remove OP_DATA opcode */ - switch (opline->opcode) { - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - removed_ops++; - zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1); - break; - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - removed_ops++; - zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1); - break; - default: - break; - } - - if (value) { - /* Convert to ASSIGN */ - opline->opcode = ZEND_ASSIGN; - opline->op2_type = IS_CONST; - opline->op2.constant = zend_optimizer_add_literal(op_array, value); - Z_TRY_ADDREF_P(value); - } else { - /* Remove dead array or object construction */ - removed_ops++; - if (var->use_chain >= 0 || var->phi_use_chain != NULL) { - zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1); - } - zend_ssa_remove_op1_def(ssa, ssa_op); - zend_ssa_remove_instr(ssa, opline, ssa_op); - } - } - } else if (var->definition_phi - && var->use_chain < 0 - && var->phi_use_chain == NULL) { - zend_ssa_remove_phi(ssa, var->definition_phi); - } - return removed_ops; -} - -/* This will try to replace uses of SSA variables we have determined to be constant. Not all uses - * can be replaced, because some instructions don't accept constant operands or only accept them - * if they have a certain type. */ -static int replace_constant_operands(sccp_ctx *ctx) { - zend_ssa *ssa = ctx->scdf.ssa; - zend_op_array *op_array = ctx->scdf.op_array; - int i; - zval tmp; - int removed_ops = 0; - - /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE - * and INIT_ARRAY. */ - for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) { - zend_ssa_var *var = &ssa->vars[i]; - zval *value; - int use; - - if (IS_PARTIAL_ARRAY(&ctx->values[i]) - || IS_PARTIAL_OBJECT(&ctx->values[i])) { - if (!Z_DELREF(ctx->values[i])) { - zend_array_destroy(Z_ARR(ctx->values[i])); - } - MAKE_BOT(&ctx->values[i]); - if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) { - removed_ops += try_remove_definition(ctx, i, var, NULL); - } - continue; - } else if (value_known(&ctx->values[i])) { - value = &ctx->values[i]; - } else { - value = value_from_type_and_range(ctx, i, &tmp); - if (!value) { - continue; - } - } - - FOREACH_USE(var, use) { - zend_op *opline = &op_array->opcodes[use]; - zend_ssa_op *ssa_op = &ssa->ops[use]; - if (try_replace_op1(ctx, opline, ssa_op, i, value)) { - if (opline->opcode == ZEND_NOP) { - removed_ops++; - } - ZEND_ASSERT(ssa_op->op1_def == -1); - if (ssa_op->op1_use != ssa_op->op2_use) { - zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use); - } else { - ssa_op->op2_use_chain = ssa_op->op1_use_chain; - } - ssa_op->op1_use = -1; - ssa_op->op1_use_chain = -1; - } - if (try_replace_op2(ctx, opline, ssa_op, i, value)) { - ZEND_ASSERT(ssa_op->op2_def == -1); - if (ssa_op->op2_use != ssa_op->op1_use) { - zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use); - } - ssa_op->op2_use = -1; - ssa_op->op2_use_chain = -1; - } - } FOREACH_USE_END(); - - if (value_known(&ctx->values[i])) { - removed_ops += try_remove_definition(ctx, i, var, value); - } - } - - return removed_ops; -} - -static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp, - zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) { - int i; - sccp->call_map = call_map; - sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count); - - MAKE_TOP(&sccp->top); - MAKE_BOT(&sccp->bot); - - i = 0; - for (; i < op_array->last_var; ++i) { - /* These are all undefined variables, which we have to mark BOT. - * Otherwise the undefined variable warning might not be preserved. */ - MAKE_BOT(&sccp->values[i]); - } - for (; i < ssa->vars_count; ++i) { - if (ssa->vars[i].alias) { - MAKE_BOT(&sccp->values[i]); - } else { - MAKE_TOP(&sccp->values[i]); - } - } -} - -static void sccp_context_free(sccp_ctx *sccp) { - int i; - for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) { - zval_ptr_dtor_nogc(&sccp->values[i]); - } -} - -int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map) -{ - sccp_ctx sccp; - int removed_ops = 0; - void *checkpoint = zend_arena_checkpoint(ctx->arena); - - sccp_context_init(ctx, &sccp, ssa, op_array, call_map); - - sccp.scdf.handlers.visit_instr = sccp_visit_instr; - sccp.scdf.handlers.visit_phi = sccp_visit_phi; - sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors; - - scdf_init(ctx, &sccp.scdf, op_array, ssa); - scdf_solve(&sccp.scdf, "SCCP"); - - if (ctx->debug_level & ZEND_DUMP_SCCP) { - int i, first = 1; - - for (i = op_array->last_var; i < ssa->vars_count; i++) { - zval *zv = &sccp.values[i]; - - if (IS_TOP(zv) || IS_BOT(zv)) { - continue; - } - if (first) { - first = 0; - fprintf(stderr, "\nSCCP Values for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\":\n"); - } - fprintf(stderr, " #%d.", i); - zend_dump_var(op_array, IS_CV, ssa->vars[i].var); - fprintf(stderr, " ="); - scp_dump_value(zv); - fprintf(stderr, "\n"); - } - } - - removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf); - removed_ops += replace_constant_operands(&sccp); - - sccp_context_free(&sccp); - zend_arena_release(&ctx->arena, checkpoint); - - return removed_ops; -} diff --git a/ext/opcache/Optimizer/scdf.c b/ext/opcache/Optimizer/scdf.c deleted file mode 100644 index 89852e89e0..0000000000 --- a/ext/opcache/Optimizer/scdf.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Sparse Conditional Data Flow Propagation Framework | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "ZendAccelerator.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "Optimizer/scdf.h" - -/* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is - * based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a - * generalized implementation as described in chapter 8.3 of the SSA book. - * - * Every SSA variable is associated with an element on a finite-height lattice, those value can only - * ever be lowered during the operation of the algorithm. If a value is lowered all instructions and - * phis using that value need to be reconsidered (this is done by adding the variable to a - * worklist). For phi functions the result is computed by applying the meet operation to the - * operands. This continues until a fixed point is reached. - * - * The algorithm is control-flow sensitive: All blocks except the start block are initially assumed - * to be unreachable. When considering a branch instruction, we determine the feasible successors - * based on the current state of the variable lattice. If a new edge becomes feasible we either have - * to mark the successor block executable and consider all instructions in it, or, if the target is - * already executable, we only have to reconsider the phi functions (as we only consider phi - * operands which are associated with a feasible edge). - * - * The generic framework requires the definition of three functions: - * * visit_instr() should recompute the lattice values of all SSA variables defined by an - * instruction. - * * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While - * doing this it should only consider operands for which scfg_is_edge_feasible() returns true. - * * get_feasible_successors() should determine the feasible successors for a branch instruction. - * Note that this callback only needs to handle conditional branches (with two successors). - */ - -#if 0 -#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__) -#else -#define DEBUG_PRINT(...) -#endif - -void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) { - uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to); - - if (zend_bitset_in(scdf->feasible_edges, edge)) { - /* We already handled this edge */ - return; - } - - DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to); - zend_bitset_incl(scdf->feasible_edges, edge); - - if (!zend_bitset_in(scdf->executable_blocks, to)) { - if (!zend_bitset_in(scdf->block_worklist, to)) { - DEBUG_PRINT("Adding block %d to worklist\n", to); - } - zend_bitset_incl(scdf->block_worklist, to); - } else { - /* Block is already executable, only a new edge became feasible. - * Reevaluate phi nodes to account for changed source operands. */ - zend_ssa_block *ssa_block = &scdf->ssa->blocks[to]; - zend_ssa_phi *phi; - for (phi = ssa_block->phis; phi; phi = phi->next) { - zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var); - scdf->handlers.visit_phi(scdf, phi); - } - } -} - -void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) { - scdf->op_array = op_array; - scdf->ssa = ssa; - - scdf->instr_worklist_len = zend_bitset_len(op_array->last); - scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count); - scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count); - - scdf->instr_worklist = zend_arena_calloc(&ctx->arena, - scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count), - sizeof(zend_ulong)); - - scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len; - scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len; - scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len; - scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len; - - zend_bitset_incl(scdf->block_worklist, 0); - zend_bitset_incl(scdf->executable_blocks, 0); -} - -void scdf_solve(scdf_ctx *scdf, const char *name) { - zend_ssa *ssa = scdf->ssa; - DEBUG_PRINT("Start SCDF solve (%s)\n", name); - while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len) - || !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len) - || !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len) - ) { - int i; - while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) { - zend_ssa_phi *phi = ssa->vars[i].definition_phi; - ZEND_ASSERT(phi); - if (zend_bitset_in(scdf->executable_blocks, phi->block)) { - scdf->handlers.visit_phi(scdf, phi); - } - } - - while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) { - int block_num = ssa->cfg.map[i]; - if (zend_bitset_in(scdf->executable_blocks, block_num)) { - zend_basic_block *block = &ssa->cfg.blocks[block_num]; - zend_op *opline = &scdf->op_array->opcodes[i]; - zend_ssa_op *ssa_op = &ssa->ops[i]; - if (opline->opcode == ZEND_OP_DATA) { - opline--; - ssa_op--; - } - scdf->handlers.visit_instr(scdf, opline, ssa_op); - if (i == block->start + block->len - 1) { - if (block->successors_count == 1) { - scdf_mark_edge_feasible(scdf, block_num, block->successors[0]); - } else if (block->successors_count > 1) { - scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op); - } - } - } - } - - while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) { - /* This block is now live. Interpret phis and instructions in it. */ - zend_basic_block *block = &ssa->cfg.blocks[i]; - zend_ssa_block *ssa_block = &ssa->blocks[i]; - - DEBUG_PRINT("Pop block %d from worklist\n", i); - zend_bitset_incl(scdf->executable_blocks, i); - - { - zend_ssa_phi *phi; - for (phi = ssa_block->phis; phi; phi = phi->next) { - zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var); - scdf->handlers.visit_phi(scdf, phi); - } - } - - if (block->len == 0) { - /* Zero length blocks don't have a last instruction that would normally do this */ - scdf_mark_edge_feasible(scdf, i, block->successors[0]); - } else { - zend_op *opline = NULL; - int j, end = block->start + block->len; - for (j = block->start; j < end; j++) { - opline = &scdf->op_array->opcodes[j]; - zend_bitset_excl(scdf->instr_worklist, j); - if (opline->opcode != ZEND_OP_DATA) { - scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]); - } - } - if (block->successors_count == 1) { - scdf_mark_edge_feasible(scdf, i, block->successors[0]); - } else if (block->successors_count > 1) { - ZEND_ASSERT(opline && "Should have opline in non-empty block"); - if (opline->opcode == ZEND_OP_DATA) { - opline--; - j--; - } - scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]); - } - } - } - } -} - -/* If a live range starts in a reachable block and ends in an unreachable block, we should - * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var - * is necessary for the correctness of temporary compaction. */ -static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) { - uint32_t i; - const zend_op_array *op_array = scdf->op_array; - const zend_cfg *cfg = &scdf->ssa->cfg; - const zend_basic_block *block = &cfg->blocks[block_idx]; - if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) { - return 0; - } - for (i = block->start; i < block->start + block->len; i++) { - zend_op *opline = &op_array->opcodes[i]; - if (zend_optimizer_is_loop_var_free(opline)) { - int ssa_var = scdf->ssa->ops[i].op1_use; - if (ssa_var >= 0) { - int op_num = scdf->ssa->vars[ssa_var].definition; - uint32_t def_block; - ZEND_ASSERT(op_num >= 0); - def_block = cfg->map[op_num]; - if (zend_bitset_in(scdf->executable_blocks, def_block)) { - return 1; - } - } - } - } - return 0; -} - -/* Removes unreachable blocks. This will remove both the instructions (and phis) in the - * blocks, as well as remove them from the successor / predecessor lists and mark them - * unreachable. Blocks already marked unreachable are not removed. */ -int scdf_remove_unreachable_blocks(scdf_ctx *scdf) { - zend_ssa *ssa = scdf->ssa; - int i; - int removed_ops = 0; - for (i = 0; i < ssa->cfg.blocks_count; i++) { - if (!zend_bitset_in(scdf->executable_blocks, i) - && (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE) - && !kept_alive_by_loop_var_free(scdf, i)) { - removed_ops += ssa->cfg.blocks[i].len; - zend_ssa_remove_block(scdf->op_array, ssa, i); - } - } - return removed_ops; -} diff --git a/ext/opcache/Optimizer/scdf.h b/ext/opcache/Optimizer/scdf.h deleted file mode 100644 index 1ab1cec3bd..0000000000 --- a/ext/opcache/Optimizer/scdf.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Call Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef _SCDF_H -#define _SCDF_H - -#include "zend_bitset.h" - -typedef struct _scdf_ctx { - zend_op_array *op_array; - zend_ssa *ssa; - zend_bitset instr_worklist; - /* Represent phi-instructions through the defining var */ - zend_bitset phi_var_worklist; - zend_bitset block_worklist; - zend_bitset executable_blocks; - /* 1 bit per edge, see scdf_edge(cfg, from, to) */ - zend_bitset feasible_edges; - uint32_t instr_worklist_len; - uint32_t phi_var_worklist_len; - uint32_t block_worklist_len; - - struct { - void (*visit_instr)( - struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op); - void (*visit_phi)( - struct _scdf_ctx *scdf, zend_ssa_phi *phi); - void (*mark_feasible_successors)( - struct _scdf_ctx *scdf, int block_num, zend_basic_block *block, - zend_op *opline, zend_ssa_op *ssa_op); - } handlers; -} scdf_ctx; - -void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa); -void scdf_solve(scdf_ctx *scdf, const char *name); - -int scdf_remove_unreachable_blocks(scdf_ctx *scdf); - -/* Add uses to worklist */ -static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) { - zend_ssa *ssa = scdf->ssa; - zend_ssa_var *var = &ssa->vars[var_num]; - int use; - zend_ssa_phi *phi; - FOREACH_USE(var, use) { - zend_bitset_incl(scdf->instr_worklist, use); - } FOREACH_USE_END(); - FOREACH_PHI_USE(var, phi) { - zend_bitset_incl(scdf->phi_var_worklist, phi->ssa_var); - } FOREACH_PHI_USE_END(); -} - -/* This should usually not be necessary, however it's used for type narrowing. */ -static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) { - zend_ssa_var *var = &scdf->ssa->vars[var_num]; - if (var->definition >= 0) { - zend_bitset_incl(scdf->instr_worklist, var->definition); - } else if (var->definition_phi) { - zend_bitset_incl(scdf->phi_var_worklist, var_num); - } -} - -static inline uint32_t scdf_edge(zend_cfg *cfg, int from, int to) { - zend_basic_block *to_block = cfg->blocks + to; - int i; - - for (i = 0; i < to_block->predecessors_count; i++) { - uint32_t edge = to_block->predecessor_offset + i; - - if (cfg->predecessors[edge] == from) { - return edge; - } - } - ZEND_UNREACHABLE(); -} - -static inline bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to) { - uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to); - return zend_bitset_in(scdf->feasible_edges, edge); -} - -void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to); - -#endif diff --git a/ext/opcache/Optimizer/ssa_integrity.c b/ext/opcache/Optimizer/ssa_integrity.c deleted file mode 100644 index 47b9d6053e..0000000000 --- a/ext/opcache/Optimizer/ssa_integrity.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, SSA validation | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Nikita Popov <nikic@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "ZendAccelerator.h" -#include "Optimizer/zend_optimizer_internal.h" - -/* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and - * CFG are upheld and prints messages to stderr if this is not the case. */ - -static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) { - int use; - FOREACH_USE(&ssa->vars[var], use) { - if (use == check) { - return 1; - } - } FOREACH_USE_END(); - return 0; -} - -static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) { - zend_ssa_phi *phi; - FOREACH_PHI_USE(&ssa->vars[var], phi) { - if (phi == check) { - return 1; - } - } FOREACH_PHI_USE_END(); - return 0; -} - -static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) { - zend_ssa_op *ssa_op = &ssa->ops[op]; - return (ssa_op->op1_use == check) - || (ssa_op->op2_use == check) - || (ssa_op->result_use == check); -} - -static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) { - zend_ssa_op *ssa_op = &ssa->ops[op]; - return (ssa_op->op1_def == check) - || (ssa_op->op2_def == check) - || (ssa_op->result_def == check); -} - -static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) { - int source; - FOREACH_PHI_SOURCE(phi, source) { - if (source == check) { - return 1; - } - } FOREACH_PHI_SOURCE_END(); - return 0; -} - -static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) { - int i, *predecessors = &cfg->predecessors[block->predecessor_offset]; - for (i = 0; i < block->predecessors_count; i++) { - if (predecessors[i] == check) { - return 1; - } - } - return 0; -} - -static inline bool is_in_successors(zend_basic_block *block, int check) { - int s; - for (s = 0; s < block->successors_count; s++) { - if (block->successors[s] == check) { - return 1; - } - } - return 0; -} - -static inline bool is_var_type(zend_uchar type) { - return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0; -} - -#define FAIL(...) do { \ - if (status == SUCCESS) { \ - fprintf(stderr, "\nIn function %s::%s (%s):\n", \ - op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \ - op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \ - } \ - fprintf(stderr, __VA_ARGS__); \ - status = FAILURE; \ -} while (0) - -#define VARFMT "%d (%s%s)" -#define VAR(i) \ - (i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \ - (ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "") - -#define INSTRFMT "%d (%s)" -#define INSTR(i) \ - (i), (zend_get_opcode_name(op_array->opcodes[i].opcode)) - -int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) { - zend_cfg *cfg = &ssa->cfg; - zend_ssa_phi *phi; - int i, status = SUCCESS; - - /* Vars */ - for (i = 0; i < ssa->vars_count; i++) { - zend_ssa_var *var = &ssa->vars[i]; - int use, c; - uint32_t type = ssa->var_info[i].type; - - if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) { - if (var->use_chain >= 0) { - FAIL("var " VARFMT " without def has op uses\n", VAR(i)); - } - if (var->phi_use_chain) { - FAIL("var " VARFMT " without def has phi uses\n", VAR(i)); - } - } - if (var->definition >= 0 && var->definition_phi) { - FAIL("var " VARFMT " has both def and def_phi\n", VAR(i)); - } - if (var->definition >= 0) { - if (!is_defined_by_op(ssa, var->definition, i)) { - FAIL("var " VARFMT " not defined by op " INSTRFMT "\n", - VAR(i), INSTR(var->definition)); - } - } - if (var->definition_phi) { - if (var->definition_phi->ssa_var != i) { - FAIL("var " VARFMT " not defined by given phi\n", VAR(i)); - } - } - - c = 0; - FOREACH_USE(var, use) { - if (++c > 10000) { - FAIL("cycle in uses of " VARFMT "\n", VAR(i)); - return status; - } - if (!is_used_by_op(ssa, use, i)) { - fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use); - } - } FOREACH_USE_END(); - - c = 0; - FOREACH_PHI_USE(var, phi) { - if (++c > 10000) { - FAIL("cycle in phi uses of " VARFMT "\n", VAR(i)); - return status; - } - if (!is_in_phi_sources(ssa, phi, i)) { - FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var); - } - } FOREACH_PHI_USE_END(); - - if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) { - FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); - } - if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) { - FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); - } - } - - /* Instructions */ - FOREACH_INSTR_NUM(i) { - zend_ssa_op *ssa_op = &ssa->ops[i]; - zend_op *opline = &op_array->opcodes[i]; - if (is_var_type(opline->op1_type)) { - if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) { - FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); - } - } else { - if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) { - FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); - } - } - if (is_var_type(opline->op2_type)) { - if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) { - FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); - } - } else { - if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) { - FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); - } - } - if (is_var_type(opline->result_type)) { - if (ssa_op->result_use < 0 && ssa_op->result_def < 0) { - FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); - } - } else { - if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) { - FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); - } - } - - if (ssa_op->op1_use >= 0) { - if (ssa_op->op1_use >= ssa->vars_count) { - FAIL("op1 use %d out of range\n", ssa_op->op1_use); - } - if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) { - FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n", - VAR(ssa_op->op1_use), INSTR(i)); - } - if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) { - FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i)); - } - } - if (ssa_op->op2_use >= 0) { - if (ssa_op->op2_use >= ssa->vars_count) { - FAIL("op2 use %d out of range\n", ssa_op->op2_use); - } - if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) { - FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n", - VAR(ssa_op->op2_use), INSTR(i)); - } - if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) { - FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i)); - } - } - if (ssa_op->result_use >= 0) { - if (ssa_op->result_use >= ssa->vars_count) { - FAIL("result use %d out of range\n", ssa_op->result_use); - } - if (!is_in_use_chain(ssa, ssa_op->result_use, i)) { - FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n", - VAR(ssa_op->result_use), INSTR(i)); - } - if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) { - FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i)); - } - } - if (ssa_op->op1_def >= 0) { - if (ssa_op->op1_def >= ssa->vars_count) { - FAIL("op1 def %d out of range\n", ssa_op->op1_def); - } - if (ssa->vars[ssa_op->op1_def].definition != i) { - FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n", - VAR(ssa_op->op1_def), INSTR(i)); - } - if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) { - FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i)); - } - } - if (ssa_op->op2_def >= 0) { - if (ssa_op->op2_def >= ssa->vars_count) { - FAIL("op2 def %d out of range\n", ssa_op->op2_def); - } - if (ssa->vars[ssa_op->op2_def].definition != i) { - FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n", - VAR(ssa_op->op2_def), INSTR(i)); - } - if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) { - FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i)); - } - } - if (ssa_op->result_def >= 0) { - if (ssa_op->result_def >= ssa->vars_count) { - FAIL("result def %d out of range\n", ssa_op->result_def); - } - if (ssa->vars[ssa_op->result_def].definition != i) { - FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n", - VAR(ssa_op->result_def), INSTR(i)); - } - if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) { - FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n", - VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i)); - } - } - } FOREACH_INSTR_NUM_END(); - - /* Phis */ - FOREACH_PHI(phi) { - unsigned num_sources = NUM_PHI_SOURCES(phi); - for (i = 0; i < num_sources; i++) { - int source = phi->sources[i]; - if (source < 0) { - FAIL(VARFMT " negative source\n", VAR(phi->ssa_var)); - } - if (!is_in_phi_use_chain(ssa, source, phi)) { - FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source); - } - if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) { - FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var)); - } - if (phi->use_chains[i]) { - int j; - for (j = i + 1; j < num_sources; j++) { - if (phi->sources[j] == source && phi->use_chains[j]) { - FAIL("use chain for source " VARFMT " of phi " VARFMT - " at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j); - } - } - } - } - if (ssa->vars[phi->ssa_var].definition_phi != phi) { - FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var)); - } - } FOREACH_PHI_END(); - - /* Blocks */ - for (i = 0; i < cfg->blocks_count; i++) { - zend_basic_block *block = &cfg->blocks[i]; - int *predecessors = &cfg->predecessors[block->predecessor_offset]; - int s, j; - - if (i != 0 && block->start < (block-1)->start + (block-1)->len) { - FAIL("Block %d start %d smaller previous end %d\n", - i, block->start, (block-1)->start + (block-1)->len); - } - if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) { - FAIL("Block %d end %d greater next start %d\n", - i, block->start + block->len, (block+1)->start); - } - - for (j = block->start; j < block->start + block->len; j++) { - if (cfg->map[j] != i) { - FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i); - } - } - - if (!(block->flags & ZEND_BB_REACHABLE)) { - if (ssa->blocks[i].phis) { - FAIL("Unreachable block %d has phis\n", i); - } - continue; - } - - for (s = 0; s < block->successors_count; s++) { - zend_basic_block *next_block; - if (block->successors[s] < 0) { - FAIL("Successor number %d of %d negative", s, i); - } - next_block = &cfg->blocks[block->successors[s]]; - if (!(next_block->flags & ZEND_BB_REACHABLE)) { - FAIL("Successor %d of %d not reachable\n", block->successors[s], i); - } - if (!is_in_predecessors(cfg, next_block, i)) { - FAIL("Block %d predecessors missing %d\n", block->successors[s], i); - } - } - - for (j = 0; j < block->predecessors_count; j++) { - if (predecessors[j] >= 0) { - int k; - zend_basic_block *prev_block = &cfg->blocks[predecessors[j]]; - if (!(prev_block->flags & ZEND_BB_REACHABLE)) { - FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i); - } - if (!is_in_successors(prev_block, i)) { - FAIL("Block %d successors missing %d\n", predecessors[j], i); - } - for (k = 0; k < block->predecessors_count; k++) { - if (k != j && predecessors[k] == predecessors[j]) { - FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]); - } - } - } - } - } - - return status; -} diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c deleted file mode 100644 index 124596ea39..0000000000 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Call Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_extensions.h" -#include "Optimizer/zend_optimizer.h" -#include "zend_optimizer_internal.h" -#include "zend_inference.h" -#include "zend_call_graph.h" -#include "zend_func_info.h" -#include "zend_inference.h" -#include "zend_call_graph.h" - -static void zend_op_array_calc(zend_op_array *op_array, void *context) -{ - zend_call_graph *call_graph = context; - call_graph->op_arrays_count++; -} - -static void zend_op_array_collect(zend_op_array *op_array, void *context) -{ - zend_call_graph *call_graph = context; - zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count; - - ZEND_SET_FUNC_INFO(op_array, func_info); - call_graph->op_arrays[call_graph->op_arrays_count] = op_array; - func_info->num = call_graph->op_arrays_count; - call_graph->op_arrays_count++; -} - -int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info) -{ - zend_op *opline = op_array->opcodes; - zend_op *end = opline + op_array->last; - zend_function *func; - zend_call_info *call_info; - int call = 0; - zend_call_info **call_stack; - ALLOCA_FLAG(use_heap); - bool is_prototype; - - call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap); - call_info = NULL; - while (opline != end) { - switch (opline->opcode) { - case ZEND_INIT_FCALL: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - call_stack[call] = call_info; - func = zend_optimizer_get_called_func( - script, op_array, opline, &is_prototype); - /* TODO: Support prototypes? */ - if (func && !is_prototype) { - call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1))); - call_info->caller_op_array = op_array; - call_info->caller_init_opline = opline; - call_info->caller_call_opline = NULL; - call_info->callee_func = func; - call_info->num_args = opline->extended_value; - call_info->next_callee = func_info->callee_info; - func_info->callee_info = call_info; - - if (build_flags & ZEND_CALL_TREE) { - call_info->next_caller = NULL; - } else if (func->type == ZEND_INTERNAL_FUNCTION) { - call_info->next_caller = NULL; - } else { - zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array); - if (callee_func_info) { - call_info->next_caller = callee_func_info->caller_info; - callee_func_info->caller_info = call_info; - } else { - call_info->next_caller = NULL; - } - } - } else { - call_info = NULL; - } - call++; - break; - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_NEW: - case ZEND_INIT_USER_CALL: - call_stack[call] = call_info; - call_info = NULL; - call++; - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - func_info->flags |= ZEND_FUNC_HAS_CALLS; - if (call_info) { - call_info->caller_call_opline = opline; - } - call--; - call_info = call_stack[call]; - break; - case ZEND_SEND_VAL: - case ZEND_SEND_VAR: - case ZEND_SEND_VAL_EX: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_REF: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_USER: - if (call_info) { - if (opline->op2_type == IS_CONST) { - call_info->named_args = 1; - break; - } - - uint32_t num = opline->op2.num; - if (num > 0) { - num--; - } - call_info->arg_info[num].opline = opline; - } - break; - case ZEND_SEND_ARRAY: - case ZEND_SEND_UNPACK: - if (call_info) { - call_info->send_unpack = 1; - } - break; - case ZEND_EXIT: - /* In this case the DO_CALL opcode may have been dropped - * and caller_call_opline will be NULL. */ - break; - } - opline++; - } - free_alloca(call_stack, use_heap); - return SUCCESS; -} - -static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited) -{ - zend_func_info *func_info; - zend_call_info *call_info; - int ret = 0; - - if (op_array == root) { - return 1; - } - - func_info = ZEND_FUNC_INFO(op_array); - if (zend_bitset_in(visited, func_info->num)) { - return 0; - } - zend_bitset_incl(visited, func_info->num); - call_info = func_info->caller_info; - while (call_info) { - if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) { - call_info->recursive = 1; - ret = 1; - } - call_info = call_info->next_caller; - } - return ret; -} - -static void zend_analyze_recursion(zend_call_graph *call_graph) -{ - zend_op_array *op_array; - zend_func_info *func_info; - zend_call_info *call_info; - int i; - int set_len = zend_bitset_len(call_graph->op_arrays_count); - zend_bitset visited; - ALLOCA_FLAG(use_heap); - - visited = ZEND_BITSET_ALLOCA(set_len, use_heap); - for (i = 0; i < call_graph->op_arrays_count; i++) { - op_array = call_graph->op_arrays[i]; - func_info = call_graph->func_infos + i; - call_info = func_info->caller_info; - while (call_info) { - if (call_info->caller_op_array == op_array) { - call_info->recursive = 1; - func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY; - } else { - memset(visited, 0, sizeof(zend_ulong) * set_len); - if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) { - call_info->recursive = 1; - func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY; - } - } - call_info = call_info->next_caller; - } - } - - free_alloca(visited, use_heap); -} - -static void zend_sort_op_arrays(zend_call_graph *call_graph) -{ - (void) call_graph; - - // TODO: perform topological sort of cyclic call graph -} - -int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */ -{ - call_graph->op_arrays_count = 0; - zend_foreach_op_array(script, zend_op_array_calc, call_graph); - - call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*)); - call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info)); - call_graph->op_arrays_count = 0; - zend_foreach_op_array(script, zend_op_array_collect, call_graph); - - return SUCCESS; -} -/* }}} */ - -void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */ -{ - int i; - - for (i = 0; i < call_graph->op_arrays_count; i++) { - zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i); - } - zend_analyze_recursion(call_graph); - zend_sort_op_arrays(call_graph); -} -/* }}} */ - -zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */ -{ - zend_call_info **map, *call; - if (!info->callee_info) { - /* Don't build call map if function contains no calls */ - return NULL; - } - - map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last); - for (call = info->callee_info; call; call = call->next_callee) { - int i; - map[call->caller_init_opline - op_array->opcodes] = call; - if (call->caller_call_opline) { - map[call->caller_call_opline - op_array->opcodes] = call; - } - for (i = 0; i < call->num_args; i++) { - if (call->arg_info[i].opline) { - map[call->arg_info[i].opline - op_array->opcodes] = call; - } - } - } - return map; -} -/* }}} */ diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h deleted file mode 100644 index 2a3fb4380f..0000000000 --- a/ext/opcache/Optimizer/zend_call_graph.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Call Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_CALL_GRAPH_H -#define ZEND_CALL_GRAPH_H - -#include "zend_ssa.h" -#include "zend_func_info.h" -#include "zend_optimizer.h" - -typedef struct _zend_send_arg_info { - zend_op *opline; -} zend_send_arg_info; - -struct _zend_call_info { - zend_op_array *caller_op_array; - zend_op *caller_init_opline; - zend_op *caller_call_opline; - zend_function *callee_func; - zend_call_info *next_caller; - zend_call_info *next_callee; - bool recursive; - bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */ - bool named_args; /* Function has named arguments */ - int num_args; - zend_send_arg_info arg_info[1]; -}; - -struct _zend_func_info { - int num; - uint32_t flags; - zend_ssa ssa; /* Static Single Assignmnt Form */ - zend_call_info *caller_info; /* where this function is called from */ - zend_call_info *callee_info; /* which functions are called from this one */ - zend_call_info **call_map; /* Call info associated with init/call/send opnum */ - zend_ssa_var_info return_info; -}; - -typedef struct _zend_call_graph { - int op_arrays_count; - zend_op_array **op_arrays; - zend_func_info *func_infos; -} zend_call_graph; - -BEGIN_EXTERN_C() - -int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); -void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); -zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array); -int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info); - -END_EXTERN_C() - -#endif /* ZEND_CALL_GRAPH_H */ diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c deleted file mode 100644 index bbf8efb41a..0000000000 --- a/ext/opcache/Optimizer/zend_cfg.c +++ /dev/null @@ -1,909 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, CFG - Control Flow Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_cfg.h" -#include "zend_func_info.h" -#include "zend_worklist.h" -#include "zend_optimizer.h" -#include "zend_optimizer_internal.h" - -static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */ -{ - zend_basic_block *blocks = cfg->blocks; - - while (1) { - int i; - - b->flags |= ZEND_BB_REACHABLE; - if (b->successors_count == 0) { - b->flags |= ZEND_BB_EXIT; - return; - } - - for (i = 0; i < b->successors_count; i++) { - zend_basic_block *succ = blocks + b->successors[i]; - - if (b->len != 0) { - zend_uchar opcode = opcodes[b->start + b->len - 1].opcode; - if (b->successors_count == 1) { - if (opcode == ZEND_JMP) { - succ->flags |= ZEND_BB_TARGET; - } else { - succ->flags |= ZEND_BB_FOLLOW; - - if ((cfg->flags & ZEND_CFG_STACKLESS)) { - if (opcode == ZEND_INCLUDE_OR_EVAL || - opcode == ZEND_GENERATOR_CREATE || - opcode == ZEND_YIELD || - opcode == ZEND_YIELD_FROM || - opcode == ZEND_DO_FCALL || - opcode == ZEND_DO_UCALL || - opcode == ZEND_DO_FCALL_BY_NAME) { - succ->flags |= ZEND_BB_ENTRY; - } - } - if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) { - if (opcode == ZEND_RECV || - opcode == ZEND_RECV_INIT) { - succ->flags |= ZEND_BB_RECV_ENTRY; - } - } - } - } else if (b->successors_count == 2) { - if (i == 0 || opcode == ZEND_JMPZNZ) { - succ->flags |= ZEND_BB_TARGET; - } else { - succ->flags |= ZEND_BB_FOLLOW; - } - } else { - ZEND_ASSERT( - opcode == ZEND_SWITCH_LONG - || opcode == ZEND_SWITCH_STRING - || opcode == ZEND_MATCH - ); - if (i == b->successors_count - 1) { - succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET; - } else { - succ->flags |= ZEND_BB_TARGET; - } - } - } else { - succ->flags |= ZEND_BB_FOLLOW; - } - - if (i == b->successors_count - 1) { - /* Tail call optimization */ - if (succ->flags & ZEND_BB_REACHABLE) { - return; - } - - b = succ; - break; - } else { - /* Recursively check reachability */ - if (!(succ->flags & ZEND_BB_REACHABLE)) { - zend_mark_reachable(opcodes, cfg, succ); - } - } - } - } -} -/* }}} */ - -static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */ -{ - zend_basic_block *blocks = cfg->blocks; - - blocks[start].flags = ZEND_BB_START; - zend_mark_reachable(op_array->opcodes, cfg, blocks + start); - - if (op_array->last_try_catch) { - zend_basic_block *b; - int j, changed; - uint32_t *block_map = cfg->map; - - do { - changed = 0; - - /* Add exception paths */ - for (j = 0; j < op_array->last_try_catch; j++) { - - /* check for jumps into the middle of try block */ - b = blocks + block_map[op_array->try_catch_array[j].try_op]; - if (!(b->flags & ZEND_BB_REACHABLE)) { - zend_basic_block *end; - - if (op_array->try_catch_array[j].catch_op) { - end = blocks + block_map[op_array->try_catch_array[j].catch_op]; - while (b != end) { - if (b->flags & ZEND_BB_REACHABLE) { - op_array->try_catch_array[j].try_op = b->start; - break; - } - b++; - } - } - b = blocks + block_map[op_array->try_catch_array[j].try_op]; - if (!(b->flags & ZEND_BB_REACHABLE)) { - if (op_array->try_catch_array[j].finally_op) { - end = blocks + block_map[op_array->try_catch_array[j].finally_op]; - while (b != end) { - if (b->flags & ZEND_BB_REACHABLE) { - op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op; - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]); - break; - } - b++; - } - } - } - } - - b = blocks + block_map[op_array->try_catch_array[j].try_op]; - if (b->flags & ZEND_BB_REACHABLE) { - b->flags |= ZEND_BB_TRY; - if (op_array->try_catch_array[j].catch_op) { - b = blocks + block_map[op_array->try_catch_array[j].catch_op]; - b->flags |= ZEND_BB_CATCH; - if (!(b->flags & ZEND_BB_REACHABLE)) { - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, b); - } - } - if (op_array->try_catch_array[j].finally_op) { - b = blocks + block_map[op_array->try_catch_array[j].finally_op]; - b->flags |= ZEND_BB_FINALLY; - if (!(b->flags & ZEND_BB_REACHABLE)) { - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, b); - } - } - if (op_array->try_catch_array[j].finally_end) { - b = blocks + block_map[op_array->try_catch_array[j].finally_end]; - b->flags |= ZEND_BB_FINALLY_END; - if (!(b->flags & ZEND_BB_REACHABLE)) { - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, b); - } - } - } else { - if (op_array->try_catch_array[j].catch_op) { - ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE)); - } - if (op_array->try_catch_array[j].finally_op) { - ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE)); - } - if (op_array->try_catch_array[j].finally_end) { - ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE)); - } - } - } - } while (changed); - } - - if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) { - zend_basic_block *b; - int j; - uint32_t *block_map = cfg->map; - - /* Mark blocks that are unreachable, but free a loop var created in a reachable block. */ - for (b = blocks; b < blocks + cfg->blocks_count; b++) { - if (b->flags & ZEND_BB_REACHABLE) { - continue; - } - - for (j = b->start; j < b->start + b->len; j++) { - zend_op *opline = &op_array->opcodes[j]; - if (zend_optimizer_is_loop_var_free(opline)) { - zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline); - if (def_opline) { - uint32_t def_block = block_map[def_opline - op_array->opcodes]; - if (blocks[def_block].flags & ZEND_BB_REACHABLE) { - b->flags |= ZEND_BB_UNREACHABLE_FREE; - break; - } - } - } - } - } - } -} -/* }}} */ - -void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */ -{ - zend_basic_block *blocks = cfg->blocks; - int i; - int start = 0; - - for (i = 0; i < cfg->blocks_count; i++) { - if (blocks[i].flags & ZEND_BB_REACHABLE) { - start = i; - i++; - break; - } - } - - /* clear all flags */ - for (i = 0; i < cfg->blocks_count; i++) { - blocks[i].flags = 0; - } - - zend_mark_reachable_blocks(op_array, cfg, start); -} -/* }}} */ - -static void initialize_block(zend_basic_block *block) { - block->flags = 0; - block->successors = block->successors_storage; - block->successors_count = 0; - block->predecessors_count = 0; - block->predecessor_offset = -1; - block->idom = -1; - block->loop_header = -1; - block->level = -1; - block->children = -1; - block->next_child = -1; -} - -#define BB_START(i) do { \ - if (!block_map[i]) { blocks_count++;} \ - block_map[i]++; \ - } while (0) - -int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */ -{ - 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; - bool extra_entry_block = 0; - - cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY); - - cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); - - /* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */ - BB_START(0); - for (i = 0; i < op_array->last; i++) { - zend_op *opline = op_array->opcodes + i; - switch (opline->opcode) { - case ZEND_RECV: - case ZEND_RECV_INIT: - if (build_flags & ZEND_CFG_RECV_ENTRY) { - BB_START(i + 1); - } - break; - case ZEND_RETURN: - case ZEND_RETURN_BY_REF: - case ZEND_GENERATOR_RETURN: - case ZEND_EXIT: - case ZEND_MATCH_ERROR: - if (i + 1 < op_array->last) { - BB_START(i + 1); - } - break; - case ZEND_THROW: - /* Don't treat THROW as terminator if it's used in expression context, - * as we may lose live ranges when eliminating unreachable code. */ - if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) { - BB_START(i + 1); - } - break; - case ZEND_INCLUDE_OR_EVAL: - flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; - case ZEND_GENERATOR_CREATE: - case ZEND_YIELD: - case ZEND_YIELD_FROM: - if (build_flags & ZEND_CFG_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 (build_flags & ZEND_CFG_STACKLESS) { - BB_START(i + 1); - } - break; - case ZEND_DO_ICALL: - flags |= ZEND_FUNC_HAS_CALLS; - break; - case ZEND_INIT_FCALL: - case ZEND_INIT_NS_FCALL_BY_NAME: - zv = CRT_CONSTANT(opline->op2); - if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { - /* The third literal is the lowercased unqualified name */ - zv += 2; - } - if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) { - if (fn->type == ZEND_INTERNAL_FUNCTION) { - flags |= zend_optimizer_classify_function( - Z_STR_P(zv), opline->extended_value); - } - } - break; - case ZEND_FAST_CALL: - BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes); - BB_START(i + 1); - break; - case ZEND_FAST_RET: - if (i + 1 < op_array->last) { - 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: - case ZEND_JMP_NULL: - BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); - BB_START(i + 1); - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); - } - 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: - BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes); - BB_START(i + 1); - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); - zval *zv; - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); - } ZEND_HASH_FOREACH_END(); - BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - BB_START(i + 1); - 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: - case ZEND_UNSET_VAR: - case ZEND_ISSET_ISEMPTY_VAR: - if (opline->extended_value & ZEND_FETCH_LOCAL) { - flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) && - !op_array->function_name) { - flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; - } - break; - case ZEND_FUNC_GET_ARGS: - flags |= ZEND_FUNC_VARARG; - break; - case ZEND_EXT_STMT: - flags |= ZEND_FUNC_HAS_EXTENDED_STMT; - break; - case ZEND_EXT_FCALL_BEGIN: - case ZEND_EXT_FCALL_END: - flags |= ZEND_FUNC_HAS_EXTENDED_FCALL; - break; - case ZEND_FREE: - if (opline->extended_value == ZEND_FREE_SWITCH) { - flags |= ZEND_FUNC_FREE_LOOP_VAR; - } - break; - case ZEND_FE_FREE: - flags |= ZEND_FUNC_FREE_LOOP_VAR; - break; - } - } - - /* If the entry block has predecessors, we may need to split it */ - if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS) - && op_array->last > 0 && block_map[0] > 1) { - extra_entry_block = 1; - } - - 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); - } - } - } - - blocks_count += extra_entry_block; - 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); - - blocks_count = -1; - - if (extra_entry_block) { - initialize_block(&blocks[0]); - blocks[0].start = 0; - blocks[0].len = 0; - blocks_count++; - } - - for (i = 0; i < op_array->last; i++) { - if (block_map[i]) { - if (blocks_count >= 0) { - blocks[blocks_count].len = i - blocks[blocks_count].start; - } - blocks_count++; - initialize_block(&blocks[blocks_count]); - blocks[blocks_count].start = i; - } - block_map[i] = blocks_count; - } - - blocks[blocks_count].len = i - blocks[blocks_count].start; - blocks_count++; - - /* Build CFG, Step 3: Calculate successors */ - for (j = 0; j < blocks_count; j++) { - zend_basic_block *block = &blocks[j]; - zend_op *opline; - if (block->len == 0) { - block->successors_count = 1; - block->successors[0] = j + 1; - continue; - } - - opline = op_array->opcodes + block->start + block->len - 1; - 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: - case ZEND_MATCH_ERROR: - break; - case ZEND_JMP: - block->successors_count = 1; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]; - break; - case ZEND_JMPZNZ: - block->successors_count = 2; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]; - block->successors[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: - case ZEND_JMP_NULL: - block->successors_count = 2; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]; - block->successors[1] = j + 1; - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - block->successors_count = 2; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]; - block->successors[1] = j + 1; - } else { - block->successors_count = 1; - block->successors[0] = j + 1; - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - block->successors_count = 2; - block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; - block->successors[1] = j + 1; - break; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - block->successors_count = 2; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]; - block->successors[1] = j + 1; - break; - case ZEND_FAST_CALL: - block->successors_count = 2; - block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]; - block->successors[1] = j + 1; - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2)); - zval *zv; - uint32_t s = 0; - - block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable); - block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int)); - - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]; - } ZEND_HASH_FOREACH_END(); - - block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]; - if (opline->opcode != ZEND_MATCH) { - block->successors[s++] = j + 1; - } - break; - } - default: - block->successors_count = 1; - block->successors[0] = j + 1; - break; - } - } - - /* Build CFG, Step 4, Mark Reachable Basic Blocks */ - cfg->flags |= flags; - zend_mark_reachable_blocks(op_array, cfg, 0); - - return SUCCESS; -} -/* }}} */ - -int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */ -{ - int j, s, edges; - zend_basic_block *b; - zend_basic_block *blocks = cfg->blocks; - zend_basic_block *end = blocks + cfg->blocks_count; - int *predecessors; - - edges = 0; - for (b = blocks; b < end; b++) { - b->predecessors_count = 0; - } - for (b = blocks; b < end; b++) { - if (!(b->flags & ZEND_BB_REACHABLE)) { - b->successors_count = 0; - b->predecessors_count = 0; - } else { - for (s = 0; s < b->successors_count; s++) { - edges++; - blocks[b->successors[s]].predecessors_count++; - } - } - } - - cfg->edges_count = edges; - cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges); - - edges = 0; - for (b = blocks; b < end; b++) { - if (b->flags & ZEND_BB_REACHABLE) { - b->predecessor_offset = edges; - edges += b->predecessors_count; - b->predecessors_count = 0; - } - } - - for (j = 0; j < cfg->blocks_count; j++) { - if (blocks[j].flags & ZEND_BB_REACHABLE) { - /* SWITCH_STRING/LONG may have few identical successors */ - for (s = 0; s < blocks[j].successors_count; s++) { - int duplicate = 0; - int p; - - for (p = 0; p < s; p++) { - if (blocks[j].successors[p] == blocks[j].successors[s]) { - duplicate = 1; - break; - } - } - if (!duplicate) { - zend_basic_block *b = blocks + blocks[j].successors[s]; - - predecessors[b->predecessor_offset + b->predecessors_count] = j; - b->predecessors_count++; - } - } - } - } - - return SUCCESS; -} -/* }}} */ - -/* Computes a postorder numbering of the CFG */ -static void compute_postnum_recursive( - int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */ -{ - int s; - zend_basic_block *block = &cfg->blocks[block_num]; - if (postnum[block_num] != -1) { - return; - } - - postnum[block_num] = -2; /* Marker for "currently visiting" */ - for (s = 0; s < block->successors_count; s++) { - compute_postnum_recursive(postnum, cur, cfg, block->successors[s]); - } - postnum[block_num] = (*cur)++; -} -/* }}} */ - -/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by - * Cooper, Harvey and Kennedy. */ -int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */ -{ - zend_basic_block *blocks = cfg->blocks; - int blocks_count = cfg->blocks_count; - int j, k, changed; - - ALLOCA_FLAG(use_heap) - int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap); - memset(postnum, -1, sizeof(int) * cfg->blocks_count); - j = 0; - compute_postnum_recursive(postnum, &j, cfg, 0); - - /* FIXME: move declarations */ - blocks[0].idom = 0; - do { - changed = 0; - /* Iterating in RPO here would converge faster */ - for (j = 1; j < blocks_count; j++) { - int idom = -1; - - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - for (k = 0; k < blocks[j].predecessors_count; k++) { - int pred = cfg->predecessors[blocks[j].predecessor_offset + k]; - - if (idom < 0) { - if (blocks[pred].idom >= 0) - idom = pred; - continue; - } - - if (blocks[pred].idom >= 0) { - while (idom != pred) { - while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom; - while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom; - } - } - } - - if (idom >= 0 && blocks[j].idom != idom) { - blocks[j].idom = idom; - changed = 1; - } - } - } while (changed); - blocks[0].idom = -1; - - for (j = 1; j < blocks_count; j++) { - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - if (blocks[j].idom >= 0) { - /* Sort by block number to traverse children in pre-order */ - if (blocks[blocks[j].idom].children < 0 || - j < blocks[blocks[j].idom].children) { - blocks[j].next_child = blocks[blocks[j].idom].children; - blocks[blocks[j].idom].children = j; - } else { - int k = blocks[blocks[j].idom].children; - while (blocks[k].next_child >=0 && j > blocks[k].next_child) { - k = blocks[k].next_child; - } - blocks[j].next_child = blocks[k].next_child; - blocks[k].next_child = j; - } - } - } - - for (j = 0; j < blocks_count; j++) { - int idom = blocks[j].idom, level = 0; - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - while (idom >= 0) { - level++; - if (blocks[idom].level >= 0) { - level += blocks[idom].level; - break; - } else { - idom = blocks[idom].idom; - } - } - blocks[j].level = level; - } - - free_alloca(postnum, use_heap); - return SUCCESS; -} -/* }}} */ - -static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */ -{ - while (blocks[b].level > blocks[a].level) { - b = blocks[b].idom; - } - return a == b; -} -/* }}} */ - -typedef struct { - int id; - int level; -} block_info; -static int compare_block_level(const block_info *a, const block_info *b) { - return b->level - a->level; -} -static void swap_blocks(block_info *a, block_info *b) { - block_info tmp = *a; - *a = *b; - *b = tmp; -} - -int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */ -{ - int i, j, k, n; - int time; - zend_basic_block *blocks = cfg->blocks; - int *entry_times, *exit_times; - zend_worklist work; - int flag = ZEND_FUNC_NO_LOOPS; - block_info *sorted_blocks; - ALLOCA_FLAG(list_use_heap) - ALLOCA_FLAG(tree_use_heap) - ALLOCA_FLAG(sorted_blocks_use_heap) - - ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap); - - /* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor - * queries. These are implemented by checking entry/exit times of the DFS search. */ - entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap); - exit_times = entry_times + cfg->blocks_count; - memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count); - - zend_worklist_push(&work, 0); - time = 0; - while (zend_worklist_len(&work)) { - next: - i = zend_worklist_peek(&work); - if (entry_times[i] == -1) { - entry_times[i] = time++; - } - /* Visit blocks immediately dominated by i. */ - for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) { - if (zend_worklist_push(&work, j)) { - goto next; - } - } - /* Visit join edges. */ - for (j = 0; j < blocks[i].successors_count; j++) { - int succ = blocks[i].successors[j]; - if (blocks[succ].idom == i) { - continue; - } else if (zend_worklist_push(&work, succ)) { - goto next; - } - } - exit_times[i] = time++; - zend_worklist_pop(&work); - } - - /* Sort blocks by decreasing level, which is the order in which we want to process them */ - sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap); - for (i = 0; i < cfg->blocks_count; i++) { - sorted_blocks[i].id = i; - sorted_blocks[i].level = blocks[i].level; - } - zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info), - (compare_func_t) compare_block_level, (swap_func_t) swap_blocks); - - /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ - Graphs". */ - - for (n = 0; n < cfg->blocks_count; n++) { - i = sorted_blocks[n].id; - - 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 DJ spanning tree. */ - if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) { - blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP; - flag |= ZEND_FUNC_IRREDUCIBLE; - flag &= ~ZEND_FUNC_NO_LOOPS; - } - } - } - while (zend_worklist_len(&work)) { - j = zend_worklist_pop(&work); - while (blocks[j].loop_header >= 0) { - j = blocks[j].loop_header; - } - if (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(sorted_blocks, sorted_blocks_use_heap); - free_alloca(entry_times, tree_use_heap); - ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap); - - cfg->flags |= flag; - - return SUCCESS; -} -/* }}} */ diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h deleted file mode 100644 index eb607c83c2..0000000000 --- a/ext/opcache/Optimizer/zend_cfg.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, CFG - Control Flow Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_CFG_H -#define ZEND_CFG_H - -/* zend_basic_block.flags */ -#define ZEND_BB_START (1<<0) /* first block */ -#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */ -#define ZEND_BB_TARGET (1<<2) /* jump target */ -#define ZEND_BB_EXIT (1<<3) /* without successors */ -#define ZEND_BB_ENTRY (1<<4) /* stackless entry */ -#define ZEND_BB_TRY (1<<5) /* start of try block */ -#define ZEND_BB_CATCH (1<<6) /* start of catch block */ -#define ZEND_BB_FINALLY (1<<7) /* start of finally block */ -#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */ -#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */ -#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */ - -#define ZEND_BB_LOOP_HEADER (1<<16) -#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17) - -#define ZEND_BB_REACHABLE (1U<<31) - -#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE) - -typedef struct _zend_basic_block { - int *successors; /* successor block indices */ - uint32_t flags; - uint32_t start; /* first opcode number */ - uint32_t len; /* number of opcodes */ - int successors_count; /* number of successors */ - int predecessors_count; /* number of predecessors */ - int predecessor_offset; /* offset of 1-st predecessor */ - int idom; /* immediate dominator block */ - int loop_header; /* closest loop header, or -1 */ - int level; /* steps away from the entry in the dom. tree */ - int children; /* list of dominated blocks */ - int next_child; /* next dominated block */ - int successors_storage[2]; /* up to 2 successor blocks */ -} zend_basic_block; - -/* -+------------+---+---+---+---+---+ -| |OP1|OP2|EXT| 0 | 1 | -+------------+---+---+---+---+---+ -|JMP |ADR| | |OP1| - | -|JMPZ | |ADR| |OP2|FOL| -|JMPNZ | |ADR| |OP2|FOL| -|JMPZNZ | |ADR|ADR|OP2|EXT| -|JMPZ_EX | |ADR| |OP2|FOL| -|JMPNZ_EX | |ADR| |OP2|FOL| -|JMP_SET | |ADR| |OP2|FOL| -|COALESCE | |ADR| |OP2|FOL| -|ASSERT_CHK | |ADR| |OP2|FOL| -|NEW | |ADR| |OP2|FOL| -|DCL_ANON* |ADR| | |OP1|FOL| -|FE_RESET_* | |ADR| |OP2|FOL| -|FE_FETCH_* | | |ADR|EXT|FOL| -|CATCH | | |ADR|EXT|FOL| -|FAST_CALL |ADR| | |OP1|FOL| -|FAST_RET | | | | - | - | -|RETURN* | | | | - | - | -|EXIT | | | | - | - | -|THROW | | | | - | - | -|* | | | |FOL| - | -+------------+---+---+---+---+---+ -*/ - -typedef struct _zend_cfg { - int blocks_count; /* number of basic blocks */ - int edges_count; /* number of edges */ - zend_basic_block *blocks; /* array of basic blocks */ - int *predecessors; - uint32_t *map; - uint32_t flags; -} zend_cfg; - -/* Build Flags */ -#define ZEND_CFG_STACKLESS (1<<30) -#define ZEND_SSA_DEBUG_LIVENESS (1<<29) -#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28) -#define ZEND_SSA_RC_INFERENCE (1<<27) -#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25) -#define ZEND_CFG_RECV_ENTRY (1<<24) -#define ZEND_CALL_TREE (1<<23) -#define ZEND_SSA_USE_CV_RESULTS (1<<22) - -#define CRT_CONSTANT_EX(op_array, opline, node) \ - (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \ - RT_CONSTANT(opline, (node)) \ - : \ - CT_CONSTANT_EX(op_array, (node).constant) \ - ) - -#define CRT_CONSTANT(node) \ - CRT_CONSTANT_EX(op_array, opline, node) - -#define RETURN_VALUE_USED(opline) \ - ((opline)->result_type != IS_UNUSED) - -BEGIN_EXTERN_C() - -int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg); -void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg); -int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg); -int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg); -int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg); - -END_EXTERN_C() - -#endif /* ZEND_CFG_H */ diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c deleted file mode 100644 index 25a910ef71..0000000000 --- a/ext/opcache/Optimizer/zend_dfg.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, DFG - Data Flow Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_dfg.h" - -static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */ -{ - uint32_t var_num; - const zend_op *next; - - if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(opline->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - } - if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0 - && opline->opcode != ZEND_FE_FETCH_R - && opline->opcode != ZEND_FE_FETCH_RW) - || (opline->op2_type == IS_CV)) { - var_num = EX_VAR_TO_NUM(opline->op2.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - } - if ((build_flags & ZEND_SSA_USE_CV_RESULTS) - && opline->result_type == IS_CV - && opline->opcode != ZEND_RECV) { - var_num = EX_VAR_TO_NUM(opline->result.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - } - - switch (opline->opcode) { - case ZEND_ASSIGN: - if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) { - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var)); - } - if (opline->op1_type == IS_CV) { -add_op1_def: - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var)); - } - break; - case ZEND_ASSIGN_REF: - if (opline->op2_type == IS_CV) { - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var)); - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) { - zend_bitset_incl(def, var_num); - } - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_OBJ_REF: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - if (next->op1_type == IS_CV) { - zend_bitset_incl(def, var_num); - } - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_STATIC_PROP: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) { - zend_bitset_incl(def, var_num); - } - } - break; - case ZEND_ASSIGN_STATIC_PROP_REF: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - if (next->op1_type == IS_CV) { - zend_bitset_incl(def, var_num); - } - } - break; - case ZEND_ASSIGN_STATIC_PROP_OP: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - } - break; - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - next = opline + 1; - if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - var_num = EX_VAR_TO_NUM(next->op1.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_OP: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - case ZEND_BIND_GLOBAL: - case ZEND_BIND_STATIC: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_REF: - case ZEND_SEND_UNPACK: - case ZEND_FE_RESET_RW: - case ZEND_MAKE_REF: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_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_LIST_W: - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_SEND_VAR: - case ZEND_CAST: - case ZEND_QM_ASSIGN: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_FE_RESET_R: - if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ADD_ARRAY_UNPACK: - var_num = EX_VAR_TO_NUM(opline->result.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - break; - case ZEND_ADD_ARRAY_ELEMENT: - var_num = EX_VAR_TO_NUM(opline->result.var); - if (!zend_bitset_in(def, var_num)) { - zend_bitset_incl(use, var_num); - } - /* break missing intentionally */ - case ZEND_INIT_ARRAY: - if (((build_flags & ZEND_SSA_RC_INFERENCE) - || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) - && opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_YIELD: - if (opline->op1_type == IS_CV - && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) - || (build_flags & ZEND_SSA_RC_INFERENCE))) { - goto add_op1_def; - } - break; - case ZEND_UNSET_CV: - goto add_op1_def; - case ZEND_VERIFY_RETURN_TYPE: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { - goto add_op1_def; - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: -#if 0 - /* This special case was handled above the switch */ - if (opline->op2_type != IS_CV) { - op2_use = -1; /* not used */ - } -#endif - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var)); - break; - case ZEND_BIND_LEXICAL: - if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) { - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var)); - } - break; - default: - break; - } - - if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var)); - } -} -/* }}} */ - -void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */ -{ - _zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def); -} -/* }}} */ - -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; - 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; - zend_bitset b_use, b_def; - - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - - opline = op_array->opcodes + blocks[j].start; - end = opline + blocks[j].len; - b_use = DFG_BITSET(use, set_size, j); - b_def = DFG_BITSET(def, set_size, j); - for (; opline < end; opline++) { - if (opline->opcode != ZEND_OP_DATA) { - _zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def); - } - } - } - - /* 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; -} -/* }}} */ diff --git a/ext/opcache/Optimizer/zend_dfg.h b/ext/opcache/Optimizer/zend_dfg.h deleted file mode 100644 index a675187794..0000000000 --- a/ext/opcache/Optimizer/zend_dfg.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, DFG - Data Flow Graph | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_DFG_H -#define ZEND_DFG_H - -#include "zend_bitset.h" -#include "zend_cfg.h" - -typedef struct _zend_dfg { - int vars; - uint32_t size; - zend_bitset tmp; - zend_bitset def; - zend_bitset use; - zend_bitset in; - zend_bitset out; -} zend_dfg; - -#define DFG_BITSET(set, set_size, block_num) \ - ((set) + ((block_num) * (set_size))) - -#define DFG_SET(set, set_size, block_num, var_num) \ - zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num)) - -#define DFG_ISSET(set, set_size, block_num, var_num) \ - zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num)) - -BEGIN_EXTERN_C() - -int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags); -void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def); - -END_EXTERN_C() - -#endif /* ZEND_DFG_H */ diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c deleted file mode 100644 index cb835574d8..0000000000 --- a/ext/opcache/Optimizer/zend_dump.c +++ /dev/null @@ -1,1239 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Bytecode Visualisation | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_cfg.h" -#include "zend_ssa.h" -#include "zend_inference.h" -#include "zend_func_info.h" -#include "zend_call_graph.h" -#include "zend_dump.h" - -void zend_dump_ht(HashTable *ht) -{ - zend_ulong index; - zend_string *key; - zval *val; - int first = 1; - - ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, val) { - if (first) { - first = 0; - } else { - fprintf(stderr, ", "); - } - if (key) { - fprintf(stderr, "\"%s\"", ZSTR_VAL(key)); - } else { - fprintf(stderr, ZEND_LONG_FMT, index); - } - fprintf(stderr, " =>"); - zend_dump_const(val); - } ZEND_HASH_FOREACH_END(); -} - -void zend_dump_const(const zval *zv) -{ - switch (Z_TYPE_P(zv)) { - case IS_NULL: - fprintf(stderr, " null"); - break; - case IS_FALSE: - fprintf(stderr, " bool(false)"); - break; - case IS_TRUE: - fprintf(stderr, " bool(true)"); - break; - case IS_LONG: - fprintf(stderr, " int(" ZEND_LONG_FMT ")", Z_LVAL_P(zv)); - break; - case IS_DOUBLE: - fprintf(stderr, " float(%g)", Z_DVAL_P(zv)); - break; - case IS_STRING: - fprintf(stderr, " string(\"%s\")", Z_STRVAL_P(zv)); - break; - case IS_ARRAY: - fprintf(stderr, " array(...)"); - break; - default: - fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv)); - break; - } -} - -static void zend_dump_class_fetch_type(uint32_t fetch_type) -{ - switch (fetch_type & ZEND_FETCH_CLASS_MASK) { - case ZEND_FETCH_CLASS_SELF: - fprintf(stderr, " (self)"); - break; - case ZEND_FETCH_CLASS_PARENT: - fprintf(stderr, " (parent)"); - break; - case ZEND_FETCH_CLASS_STATIC: - fprintf(stderr, " (static)"); - break; - case ZEND_FETCH_CLASS_AUTO: - fprintf(stderr, " (auto)"); - break; - case ZEND_FETCH_CLASS_INTERFACE: - fprintf(stderr, " (interface)"); - break; - case ZEND_FETCH_CLASS_TRAIT: - fprintf(stderr, " (trait)"); - break; - } - if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) { - fprintf(stderr, " (no-autolod)"); - } - if (fetch_type & ZEND_FETCH_CLASS_SILENT) { - fprintf(stderr, " (silent)"); - } - if (fetch_type & ZEND_FETCH_CLASS_EXCEPTION) { - fprintf(stderr, " (exception)"); - } -} - -static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t flags) { - if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) { - fprintf(stderr, " %u", op.num); - } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) { - if (op.num != (uint32_t)-1) { - fprintf(stderr, " try-catch(%u)", op.num); - } - } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) { - fprintf(stderr, " THIS"); - } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) { - fprintf(stderr, " NEXT"); - } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) { - zend_dump_class_fetch_type(op.num); - } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) { - fprintf(stderr, " CONSTRUCTOR"); - } else if (ZEND_VM_OP_CONST_FETCH == (flags & ZEND_VM_OP_MASK)) { - if (op.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) { - fprintf(stderr, " (unqualified-in-namespace)"); - } - } -} - -void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num) -{ - if (var_type == IS_CV && var_num < op_array->last_var) { - fprintf(stderr, "CV%d($%s)", var_num, op_array->vars[var_num]->val); - } else if (var_type == IS_VAR) { - fprintf(stderr, "V%d", var_num); - } else if ((var_type & (IS_VAR|IS_TMP_VAR)) == IS_TMP_VAR) { - fprintf(stderr, "T%d", var_num); - } else { - fprintf(stderr, "X%d", var_num); - } -} - -static void zend_dump_range(const zend_ssa_range *r) -{ - if (r->underflow && r->overflow) { - return; - } - fprintf(stderr, " RANGE["); - if (r->underflow) { - fprintf(stderr, "--.."); - } else if (r->min == ZEND_LONG_MIN) { - fprintf(stderr, "MIN.."); - } else { - fprintf(stderr, ZEND_LONG_FMT "..", r->min); - } - if (r->overflow) { - fprintf(stderr, "++]"); - } else if (r->max == ZEND_LONG_MAX) { - fprintf(stderr, "MAX]"); - } else { - fprintf(stderr, ZEND_LONG_FMT "]", r->max); - } -} - -static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_instanceof, uint32_t dump_flags) -{ - int first = 1; - - fprintf(stderr, " ["); - if (info & MAY_BE_GUARD) { - fprintf(stderr, "!"); - } - if (info & MAY_BE_UNDEF) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "undef"); - } - if (info & MAY_BE_REF) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "ref"); - } - if (dump_flags & ZEND_DUMP_RC_INFERENCE) { - if (info & MAY_BE_RC1) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "rc1"); - } - if (info & MAY_BE_RCN) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "rcn"); - } - } - if (info & MAY_BE_CLASS) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "class"); - if (ce) { - if (is_instanceof) { - fprintf(stderr, " (instanceof %s)", ce->name->val); - } else { - fprintf(stderr, " (%s)", ce->name->val); - } - } - } else if ((info & MAY_BE_ANY) == MAY_BE_ANY) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "any"); - } else { - if (info & MAY_BE_NULL) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "null"); - } - if ((info & MAY_BE_FALSE) && (info & MAY_BE_TRUE)) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "bool"); - } else if (info & MAY_BE_FALSE) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "false"); - } else if (info & MAY_BE_TRUE) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "true"); - } - if (info & MAY_BE_LONG) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "long"); - } - if (info & MAY_BE_DOUBLE) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "double"); - } - if (info & MAY_BE_STRING) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "string"); - } - if (info & MAY_BE_ARRAY) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "array"); - if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 && - ((info & MAY_BE_ARRAY_KEY_LONG) == 0 || - (info & MAY_BE_ARRAY_KEY_STRING) == 0)) { - int afirst = 1; - fprintf(stderr, " ["); - if (info & MAY_BE_ARRAY_KEY_LONG) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "long"); - } - if (info & MAY_BE_ARRAY_KEY_STRING) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "string"); - } - fprintf(stderr, "]"); - } - if (info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)) { - int afirst = 1; - fprintf(stderr, " of ["); - if ((info & MAY_BE_ARRAY_OF_ANY) == MAY_BE_ARRAY_OF_ANY) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "any"); - } else { - if (info & MAY_BE_ARRAY_OF_NULL) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "null"); - } - if (info & MAY_BE_ARRAY_OF_FALSE) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "false"); - } - if (info & MAY_BE_ARRAY_OF_TRUE) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "true"); - } - if (info & MAY_BE_ARRAY_OF_LONG) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "long"); - } - if (info & MAY_BE_ARRAY_OF_DOUBLE) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "double"); - } - if (info & MAY_BE_ARRAY_OF_STRING) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "string"); - } - if (info & MAY_BE_ARRAY_OF_ARRAY) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "array"); - } - if (info & MAY_BE_ARRAY_OF_OBJECT) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "object"); - } - if (info & MAY_BE_ARRAY_OF_RESOURCE) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "resource"); - } - } - if (info & MAY_BE_ARRAY_OF_REF) { - if (afirst) afirst = 0; else fprintf(stderr, ", "); - fprintf(stderr, "ref"); - } - fprintf(stderr, "]"); - } - } - if (info & MAY_BE_OBJECT) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "object"); - if (ce) { - if (is_instanceof) { - fprintf(stderr, " (instanceof %s)", ce->name->val); - } else { - fprintf(stderr, " (%s)", ce->name->val); - } - } - } - if (info & MAY_BE_RESOURCE) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "resource"); - } - } - fprintf(stderr, "]"); -} - -static void zend_dump_ssa_var_info(const zend_ssa *ssa, int ssa_var_num, uint32_t dump_flags) -{ - zend_dump_type_info( - ssa->var_info[ssa_var_num].type, - ssa->var_info[ssa_var_num].ce, - ssa->var_info[ssa_var_num].ce ? - ssa->var_info[ssa_var_num].is_instanceof : 0, - dump_flags); -} - -void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags) -{ - if (ssa_var_num >= 0) { - fprintf(stderr, "#%d.", ssa_var_num); - } else { - fprintf(stderr, "#?."); - } - zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : var_type), var_num); - - if (ssa_var_num >= 0 && ssa->vars) { - if (ssa->vars[ssa_var_num].no_val) { - fprintf(stderr, " NOVAL"); - } - if (ssa->vars[ssa_var_num].escape_state == ESCAPE_STATE_NO_ESCAPE) { - fprintf(stderr, " NOESC"); - } - if (ssa->var_info) { - zend_dump_ssa_var_info(ssa, ssa_var_num, dump_flags); - if (ssa->var_info[ssa_var_num].has_range) { - zend_dump_range(&ssa->var_info[ssa_var_num].range); - } - } - } -} - -static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags) -{ - fprintf(stderr, " TYPE"); - zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags); -} - -static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags) -{ - if (r->range.underflow && r->range.overflow) { - return; - } - fprintf(stderr, " RANGE"); - if (r->negative) { - fprintf(stderr, "~"); - } - fprintf(stderr, "["); - if (r->range.underflow) { - fprintf(stderr, "-- .. "); - } else { - if (r->min_ssa_var >= 0) { - zend_dump_ssa_var(op_array, ssa, r->min_ssa_var, (r->min_var < op_array->last_var ? IS_CV : 0), r->min_var, dump_flags); - if (r->range.min > 0) { - fprintf(stderr, " + " ZEND_LONG_FMT, r->range.min); - } else if (r->range.min < 0) { - fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.min); - } - fprintf(stderr, " .. "); - } else { - fprintf(stderr, ZEND_LONG_FMT " .. ", r->range.min); - } - } - if (r->range.overflow) { - fprintf(stderr, "++]"); - } else { - if (r->max_ssa_var >= 0) { - zend_dump_ssa_var(op_array, ssa, r->max_ssa_var, (r->max_var < op_array->last_var ? IS_CV : 0), r->max_var, dump_flags); - if (r->range.max > 0) { - fprintf(stderr, " + " ZEND_LONG_FMT, r->range.max); - } else if (r->range.max < 0) { - fprintf(stderr, " - " ZEND_LONG_FMT, -r->range.max); - } - fprintf(stderr, "]"); - } else { - fprintf(stderr, ZEND_LONG_FMT "]", r->range.max); - } - } -} - -void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op) -{ - const char *name = zend_get_opcode_name(opline->opcode); - uint32_t flags = zend_get_opcode_flags(opline->opcode); - uint32_t n = 0; - - if (!ssa_op || ssa_op->result_use < 0) { - if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op && ssa_op->result_def >= 0) { - int ssa_var_num = ssa_op->result_def; - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags); - } else { - zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); - } - fprintf(stderr, " = "); - } - } - - if (name) { - fprintf(stderr, "%s", (name + 5)); - } else { - fprintf(stderr, "OP_%d", (int)opline->opcode); - } - - if (ZEND_VM_EXT_NUM == (flags & ZEND_VM_EXT_MASK)) { - fprintf(stderr, " %u", opline->extended_value); - } else if (ZEND_VM_EXT_OP == (flags & ZEND_VM_EXT_MASK)) { - fprintf(stderr, " (%s)", zend_get_opcode_name(opline->extended_value) + 5); - } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) { - switch (opline->extended_value) { - case IS_NULL: - fprintf(stderr, " (null)"); - break; - case IS_FALSE: - fprintf(stderr, " (false)"); - break; - case IS_TRUE: - fprintf(stderr, " (true)"); - break; - case IS_LONG: - fprintf(stderr, " (long)"); - break; - case IS_DOUBLE: - fprintf(stderr, " (double)"); - break; - case IS_STRING: - fprintf(stderr, " (string)"); - break; - case IS_ARRAY: - fprintf(stderr, " (array)"); - break; - case IS_OBJECT: - fprintf(stderr, " (object)"); - break; - case IS_RESOURCE: - fprintf(stderr, " (resource)"); - break; - case _IS_BOOL: - fprintf(stderr, " (bool)"); - break; - case IS_CALLABLE: - fprintf(stderr, " (callable)"); - break; - case IS_VOID: - fprintf(stderr, " (void)"); - break; - default: - fprintf(stderr, " (\?\?\?)"); - break; - } - } else if (ZEND_VM_EXT_TYPE_MASK == (flags & ZEND_VM_EXT_MASK)) { - switch (opline->extended_value) { - case (1<<IS_NULL): - fprintf(stderr, " (null)"); - break; - case (1<<IS_FALSE): - fprintf(stderr, " (false)"); - break; - case (1<<IS_TRUE): - fprintf(stderr, " (true)"); - break; - case (1<<IS_LONG): - fprintf(stderr, " (long)"); - break; - case (1<<IS_DOUBLE): - fprintf(stderr, " (double)"); - break; - case (1<<IS_STRING): - fprintf(stderr, " (string)"); - break; - case (1<<IS_ARRAY): - fprintf(stderr, " (array)"); - break; - case (1<<IS_OBJECT): - fprintf(stderr, " (object)"); - break; - case (1<<IS_RESOURCE): - fprintf(stderr, " (resource)"); - break; - case ((1<<IS_FALSE)|(1<<IS_TRUE)): - fprintf(stderr, " (bool)"); - break; - default: - fprintf(stderr, " TYPE"); - zend_dump_type_info(opline->extended_value, NULL, 0, dump_flags); - break; - } - } else if (ZEND_VM_EXT_EVAL == (flags & ZEND_VM_EXT_MASK)) { - switch (opline->extended_value) { - case ZEND_EVAL: - fprintf(stderr, " (eval)"); - break; - case ZEND_INCLUDE: - fprintf(stderr, " (include)"); - break; - case ZEND_INCLUDE_ONCE: - fprintf(stderr, " (include_once)"); - break; - case ZEND_REQUIRE: - fprintf(stderr, " (require)"); - break; - case ZEND_REQUIRE_ONCE: - fprintf(stderr, " (require_once)"); - break; - default: - fprintf(stderr, " (\?\?\?)"); - break; - } - } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) { - if (opline->extended_value == ZEND_RETURNS_VALUE) { - fprintf(stderr, " (value)"); - } else if (opline->extended_value & ZEND_RETURNS_FUNCTION) { - fprintf(stderr, " (function)"); - } - } else { - if (ZEND_VM_EXT_VAR_FETCH & flags) { - if (opline->extended_value & ZEND_FETCH_GLOBAL) { - fprintf(stderr, " (global)"); - } else if (opline->extended_value & ZEND_FETCH_LOCAL) { - fprintf(stderr, " (local)"); - } else if (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK) { - fprintf(stderr, " (global+lock)"); - } - } - if (ZEND_VM_EXT_ISSET & flags) { - if (!(opline->extended_value & ZEND_ISEMPTY)) { - fprintf(stderr, " (isset)"); - } else { - fprintf(stderr, " (empty)"); - } - } - if (ZEND_VM_EXT_ARRAY_INIT & flags) { - fprintf(stderr, " %u", opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT); - if (!(opline->extended_value & ZEND_ARRAY_NOT_PACKED)) { - fprintf(stderr, " (packed)"); - } - } - if (ZEND_VM_EXT_REF & flags) { - if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { - fprintf(stderr, " (ref)"); - } - } - if ((ZEND_VM_EXT_DIM_WRITE|ZEND_VM_EXT_FETCH_REF) & flags) { - uint32_t obj_flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; - if (obj_flags == ZEND_FETCH_REF) { - fprintf(stderr, " (ref)"); - } else if (obj_flags == ZEND_FETCH_DIM_WRITE) { - fprintf(stderr, " (dim write)"); - } - } - } - - if (opline->op1_type == IS_CONST) { - zend_dump_const(CRT_CONSTANT(opline->op1)); - } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op) { - int ssa_var_num = ssa_op->op1_use; - if (ssa_var_num >= 0) { - fprintf(stderr, " "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags); - } else if (ssa_op->op1_def < 0) { - fprintf(stderr, " "); - zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var)); - } - } else { - fprintf(stderr, " "); - zend_dump_var(op_array, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var)); - } - if (ssa_op) { - int ssa_var_num = ssa_op->op1_def; - if (ssa_var_num >= 0) { - fprintf(stderr, " -> "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op1_type, EX_VAR_TO_NUM(opline->op1.var), dump_flags); - } - } - } else { - uint32_t op1_flags = ZEND_VM_OP1_FLAGS(flags); - if (ZEND_VM_OP_JMP_ADDR == (op1_flags & ZEND_VM_OP_MASK)) { - if (b) { - fprintf(stderr, " BB%d", b->successors[n++]); - } else { - fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes)); - } - } else { - zend_dump_unused_op(opline, opline->op1, op1_flags); - } - } - - if (opline->op2_type == IS_CONST) { - zval *op = CRT_CONSTANT(opline->op2); - if ( - opline->opcode == ZEND_SWITCH_LONG - || opline->opcode == ZEND_SWITCH_STRING - || opline->opcode == ZEND_MATCH - ) { - HashTable *jumptable = Z_ARRVAL_P(op); - zend_string *key; - zend_ulong num_key; - zval *zv; - ZEND_HASH_FOREACH_KEY_VAL(jumptable, num_key, key, zv) { - if (key) { - fprintf(stderr, " \"%s\":", ZSTR_VAL(key)); - } else { - fprintf(stderr, " " ZEND_LONG_FMT ":", num_key); - } - if (b) { - fprintf(stderr, " BB%d,", b->successors[n++]); - } else { - fprintf(stderr, " %04u,", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); - } - } ZEND_HASH_FOREACH_END(); - fprintf(stderr, " default:"); - } else { - zend_dump_const(op); - } - } else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op) { - int ssa_var_num = ssa_op->op2_use; - if (ssa_var_num >= 0) { - fprintf(stderr, " "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags); - } else if (ssa_op->op2_def < 0) { - fprintf(stderr, " "); - zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var)); - } - } else { - fprintf(stderr, " "); - zend_dump_var(op_array, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var)); - } - if (ssa_op) { - int ssa_var_num = ssa_op->op2_def; - if (ssa_var_num >= 0) { - fprintf(stderr, " -> "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->op2_type, EX_VAR_TO_NUM(opline->op2.var), dump_flags); - } - } - } else { - uint32_t op2_flags = ZEND_VM_OP2_FLAGS(flags); - if (ZEND_VM_OP_JMP_ADDR == (op2_flags & ZEND_VM_OP_MASK)) { - if (opline->opcode != ZEND_CATCH || !(opline->extended_value & ZEND_LAST_CATCH)) { - if (b) { - fprintf(stderr, " BB%d", b->successors[n++]); - } else { - fprintf(stderr, " %04u", (uint32_t)(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes)); - } - } - } else { - zend_dump_unused_op(opline, opline->op2, op2_flags); - } - } - - if (ZEND_VM_EXT_JMP_ADDR == (flags & ZEND_VM_EXT_MASK)) { - if (b) { - fprintf(stderr, " BB%d", b->successors[n++]); - } else { - fprintf(stderr, " %04u", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)); - } - } - if (opline->result_type == IS_CONST) { - zend_dump_const(CRT_CONSTANT(opline->result)); -#if 0 - } else if (opline->result_type & IS_SMART_BRANCH_JMPZ) { - fprintf(stderr, " jmpz"); - } else if (opline->result_type & IS_SMART_BRANCH_JMPNZ) { - fprintf(stderr, " jmpnz"); -#endif - } else if (ssa_op && ssa_op->result_use >= 0) { - if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { - if (ssa_op) { - int ssa_var_num = ssa_op->result_use; - if (ssa_var_num >= 0) { - fprintf(stderr, " "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags); - } - } else { - fprintf(stderr, " "); - zend_dump_var(op_array, opline->result_type, EX_VAR_TO_NUM(opline->result.var)); - } - if (ssa_op) { - int ssa_var_num = ssa_op->result_def; - if (ssa_var_num >= 0) { - fprintf(stderr, " -> "); - zend_dump_ssa_var(op_array, ssa, ssa_var_num, opline->result_type, EX_VAR_TO_NUM(opline->result.var), dump_flags); - } - } - } - } -} - -static void zend_dump_op_line(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const void *data) -{ - int len = 0; - const zend_ssa *ssa = NULL; - zend_ssa_op *ssa_op = NULL; - - len = fprintf(stderr, "%04u", (uint32_t)(opline - op_array->opcodes)); - fprintf(stderr, "%*c", 5-len, ' '); - - if (dump_flags & ZEND_DUMP_SSA) { - ssa = (const zend_ssa*)data; - if (ssa && ssa->ops) { - ssa_op = &ssa->ops[opline - op_array->opcodes]; - } - } - - zend_dump_op(op_array, b, opline, dump_flags, ssa, ssa_op); - fprintf(stderr, "\n"); -} - -static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags) -{ - zend_basic_block *b = cfg->blocks + n; - - if (n > 0) { - fprintf(stderr, "\n"); - } - fprintf(stderr, "BB%d:\n ;", n); - if (b->flags & ZEND_BB_START) { - fprintf(stderr, " start"); - } - if (b->flags & ZEND_BB_RECV_ENTRY) { - fprintf(stderr, " recv"); - } - if (b->flags & ZEND_BB_FOLLOW) { - fprintf(stderr, " follow"); - } - if (b->flags & ZEND_BB_TARGET) { - fprintf(stderr, " target"); - } - if (b->flags & ZEND_BB_EXIT) { - fprintf(stderr, " exit"); - } - if (b->flags & (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) { - fprintf(stderr, " entry"); - } - if (b->flags & ZEND_BB_TRY) { - fprintf(stderr, " try"); - } - if (b->flags & ZEND_BB_CATCH) { - fprintf(stderr, " catch"); - } - if (b->flags & ZEND_BB_FINALLY) { - fprintf(stderr, " finally"); - } - if (b->flags & ZEND_BB_FINALLY_END) { - fprintf(stderr, " finally_end"); - } - if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) && !(b->flags & ZEND_BB_REACHABLE)) { - fprintf(stderr, " unreachable"); - } - if (b->flags & ZEND_BB_UNREACHABLE_FREE) { - fprintf(stderr, " unreachable_free"); - } - if (b->flags & ZEND_BB_LOOP_HEADER) { - fprintf(stderr, " loop_header"); - } - if (b->flags & ZEND_BB_IRREDUCIBLE_LOOP) { - fprintf(stderr, " irreducible"); - } - if (b->len != 0) { - fprintf(stderr, " lines=[%d-%d]", b->start, b->start + b->len - 1); - } else { - fprintf(stderr, " empty"); - } - fprintf(stderr, "\n"); - - if (b->predecessors_count) { - int *p = cfg->predecessors + b->predecessor_offset; - int *end = p + b->predecessors_count; - - fprintf(stderr, " ; from=(BB%d", *p); - for (p++; p < end; p++) { - fprintf(stderr, ", BB%d", *p); - } - fprintf(stderr, ")\n"); - } - - if (b->successors_count > 0) { - int s; - fprintf(stderr, " ; to=(BB%d", b->successors[0]); - for (s = 1; s < b->successors_count; s++) { - fprintf(stderr, ", BB%d", b->successors[s]); - } - fprintf(stderr, ")\n"); - } - - if (b->idom >= 0) { - fprintf(stderr, " ; idom=BB%d\n", b->idom); - } - if (b->level >= 0) { - fprintf(stderr, " ; level=%d\n", b->level); - } - if (b->loop_header >= 0) { - fprintf(stderr, " ; loop_header=%d\n", b->loop_header); - } - if (b->children >= 0) { - int j = b->children; - fprintf(stderr, " ; children=(BB%d", j); - j = cfg->blocks[j].next_child; - while (j >= 0) { - fprintf(stderr, ", BB%d", j); - j = cfg->blocks[j].next_child; - } - fprintf(stderr, ")\n"); - } -} - -static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_array, const zend_ssa *ssa, int n, uint32_t dump_flags) -{ - zend_dump_block_info(cfg, n, dump_flags); - if (ssa && ssa->blocks && ssa->blocks[n].phis) { - zend_ssa_phi *p = ssa->blocks[n].phis; - - do { - int j; - - fprintf(stderr, " "); - zend_dump_ssa_var(op_array, ssa, p->ssa_var, 0, p->var, dump_flags); - if (p->pi < 0) { - fprintf(stderr, " = Phi("); - for (j = 0; j < cfg->blocks[n].predecessors_count; j++) { - if (j > 0) { - fprintf(stderr, ", "); - } - zend_dump_ssa_var(op_array, ssa, p->sources[j], 0, p->var, dump_flags); - } - fprintf(stderr, ")\n"); - } else { - fprintf(stderr, " = Pi<BB%d>(", p->pi); - zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags); - fprintf(stderr, " &"); - if (p->has_range_constraint) { - zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags); - } else { - zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags); - } - fprintf(stderr, ")\n"); - } - p = p->next; - } while (p); - } -} - -void zend_dump_op_array_name(const zend_op_array *op_array) -{ - if (op_array->function_name) { - if (op_array->scope && op_array->scope->name) { - fprintf(stderr, "%s::%s", op_array->scope->name->val, op_array->function_name->val); - } else { - fprintf(stderr, "%s", op_array->function_name->val); - } - } else { - fprintf(stderr, "%s", "$_main"); - } -} - -void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data) -{ - int i; - const zend_cfg *cfg = NULL; - const zend_ssa *ssa = NULL; - zend_func_info *func_info = NULL; - uint32_t func_flags = 0; - - if (dump_flags & (ZEND_DUMP_CFG|ZEND_DUMP_SSA)) { - cfg = (const zend_cfg*)data; - if (!cfg->blocks) { - cfg = data = NULL; - } - } - if (dump_flags & ZEND_DUMP_SSA) { - ssa = (const zend_ssa*)data; - } - - func_info = ZEND_FUNC_INFO(op_array); - if (func_info) { - func_flags = func_info->flags; - } - - fprintf(stderr, "\n"); - zend_dump_op_array_name(op_array); - fprintf(stderr, ":\n ; (lines=%d, args=%d", - op_array->last, - op_array->num_args); - fprintf(stderr, ", vars=%d, tmps=%d", op_array->last_var, op_array->T); - if (ssa) { - fprintf(stderr, ", ssa_vars=%d", ssa->vars_count); - } - if (func_flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) { - fprintf(stderr, ", dynamic"); - } - if (func_flags & ZEND_FUNC_RECURSIVE) { - fprintf(stderr, ", recursive"); - if (func_flags & ZEND_FUNC_RECURSIVE_DIRECTLY) { - fprintf(stderr, " directly"); - } - if (func_flags & ZEND_FUNC_RECURSIVE_INDIRECTLY) { - fprintf(stderr, " indirectly"); - } - } - if (func_flags & ZEND_FUNC_IRREDUCIBLE) { - fprintf(stderr, ", irreducable"); - } - if (func_flags & ZEND_FUNC_NO_LOOPS) { - fprintf(stderr, ", no_loops"); - } - if (func_flags & ZEND_FUNC_HAS_EXTENDED_STMT) { - fprintf(stderr, ", extended_stmt"); - } - if (func_flags & ZEND_FUNC_HAS_EXTENDED_FCALL) { - fprintf(stderr, ", extended_fcall"); - } -//TODO: this is useful only for JIT??? -#if 0 - if (info->flags & ZEND_JIT_FUNC_NO_IN_MEM_CVS) { - fprintf(stderr, ", no_in_mem_cvs"); - } - if (info->flags & ZEND_JIT_FUNC_NO_USED_ARGS) { - fprintf(stderr, ", no_used_args"); - } - if (info->flags & ZEND_JIT_FUNC_NO_SYMTAB) { - fprintf(stderr, ", no_symtab"); - } - if (info->flags & ZEND_JIT_FUNC_NO_FRAME) { - fprintf(stderr, ", no_frame"); - } - if (info->flags & ZEND_JIT_FUNC_INLINE) { - fprintf(stderr, ", inline"); - } -#endif - fprintf(stderr, ")\n"); - if (msg) { - fprintf(stderr, " ; (%s)\n", msg); - } - fprintf(stderr, " ; %s:%u-%u\n", op_array->filename->val, op_array->line_start, op_array->line_end); - - if (func_info) { - fprintf(stderr, " ; return "); - zend_dump_type_info(func_info->return_info.type, func_info->return_info.ce, func_info->return_info.is_instanceof, dump_flags); - zend_dump_range(&func_info->return_info.range); - fprintf(stderr, "\n"); - } - - if (ssa && ssa->var_info) { - for (i = 0; i < op_array->last_var; i++) { - fprintf(stderr, " ; "); - zend_dump_ssa_var(op_array, ssa, i, IS_CV, i, dump_flags); - fprintf(stderr, "\n"); - } - } - - if (cfg) { - int n; - zend_basic_block *b; - - for (n = 0; n < cfg->blocks_count; n++) { - b = cfg->blocks + n; - if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) || (b->flags & ZEND_BB_REACHABLE)) { - const zend_op *opline; - const zend_op *end; - - zend_dump_block_header(cfg, op_array, ssa, n, dump_flags); - opline = op_array->opcodes + b->start; - end = opline + b->len; - while (opline < end) { - zend_dump_op_line(op_array, b, opline, dump_flags, data); - opline++; - } - } - } - if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { - fprintf(stderr, "LIVE RANGES:\n"); - for (i = 0; i < op_array->last_live_range; i++) { - fprintf(stderr, - " %u: %04u - %04u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - op_array->live_range[i].start, - op_array->live_range[i].end); - switch (op_array->live_range[i].var & ZEND_LIVE_MASK) { - case ZEND_LIVE_TMPVAR: - fprintf(stderr, "(tmp/var)\n"); - break; - case ZEND_LIVE_LOOP: - fprintf(stderr, "(loop)\n"); - break; - case ZEND_LIVE_SILENCE: - fprintf(stderr, "(silence)\n"); - break; - case ZEND_LIVE_ROPE: - fprintf(stderr, "(rope)\n"); - break; - case ZEND_LIVE_NEW: - fprintf(stderr, "(new)\n"); - break; - } - } - } - if (op_array->last_try_catch) { - fprintf(stderr, "EXCEPTION TABLE:\n"); - for (i = 0; i < op_array->last_try_catch; i++) { - fprintf(stderr, " BB%u", - cfg->map[op_array->try_catch_array[i].try_op]); - if (op_array->try_catch_array[i].catch_op) { - fprintf(stderr, ", BB%u", - cfg->map[op_array->try_catch_array[i].catch_op]); - } else { - fprintf(stderr, ", -"); - } - if (op_array->try_catch_array[i].finally_op) { - fprintf(stderr, ", BB%u", - cfg->map[op_array->try_catch_array[i].finally_op]); - } else { - fprintf(stderr, ", -"); - } - if (op_array->try_catch_array[i].finally_end) { - fprintf(stderr, ", BB%u\n", - cfg->map[op_array->try_catch_array[i].finally_end]); - } else { - fprintf(stderr, ", -\n"); - } - } - } - } else { - const zend_op *opline = op_array->opcodes; - const zend_op *end = opline + op_array->last; - - while (opline < end) { - zend_dump_op_line(op_array, NULL, opline, dump_flags, data); - opline++; - } - if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { - fprintf(stderr, "LIVE RANGES:\n"); - for (i = 0; i < op_array->last_live_range; i++) { - fprintf(stderr, - " %u: %04u - %04u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - op_array->live_range[i].start, - op_array->live_range[i].end); - switch (op_array->live_range[i].var & ZEND_LIVE_MASK) { - case ZEND_LIVE_TMPVAR: - fprintf(stderr, "(tmp/var)\n"); - break; - case ZEND_LIVE_LOOP: - fprintf(stderr, "(loop)\n"); - break; - case ZEND_LIVE_SILENCE: - fprintf(stderr, "(silence)\n"); - break; - case ZEND_LIVE_ROPE: - fprintf(stderr, "(rope)\n"); - break; - case ZEND_LIVE_NEW: - fprintf(stderr, "(new)\n"); - break; - } - } - } - if (op_array->last_try_catch) { - fprintf(stderr, "EXCEPTION TABLE:\n"); - for (i = 0; i < op_array->last_try_catch; i++) { - fprintf(stderr, - " %04u", - op_array->try_catch_array[i].try_op); - - if (op_array->try_catch_array[i].catch_op) { - fprintf(stderr, - ", %04u", - op_array->try_catch_array[i].catch_op); - } else { - fprintf(stderr, ", -"); - } - if (op_array->try_catch_array[i].finally_op) { - fprintf(stderr, - ", %04u", - op_array->try_catch_array[i].finally_op); - } else { - fprintf(stderr, ", -"); - } - if (op_array->try_catch_array[i].finally_end) { - fprintf(stderr, - ", %04u", - op_array->try_catch_array[i].finally_end); - } else { - fprintf(stderr, ", -\n"); - } - } - } - } -} - -void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg) -{ - int j; - - fprintf(stderr, "\nDOMINATORS-TREE for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\"\n"); - for (j = 0; j < cfg->blocks_count; j++) { - zend_basic_block *b = cfg->blocks + j; - if (b->flags & ZEND_BB_REACHABLE) { - zend_dump_block_info(cfg, j, 0); - } - } -} - -void zend_dump_variables(const zend_op_array *op_array) -{ - int j; - - fprintf(stderr, "\nCV Variables for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\"\n"); - for (j = 0; j < op_array->last_var; j++) { - fprintf(stderr, " "); - zend_dump_var(op_array, IS_CV, j); - fprintf(stderr, "\n"); - } -} - -void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags) -{ - int j; - - if (ssa->vars) { - fprintf(stderr, "\nSSA Variable for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\"\n"); - - for (j = 0; j < ssa->vars_count; j++) { - fprintf(stderr, " "); - zend_dump_ssa_var(op_array, ssa, j, IS_CV, ssa->vars[j].var, dump_flags); - if (ssa->vars[j].scc >= 0) { - if (ssa->vars[j].scc_entry) { - fprintf(stderr, " *"); - } else { - fprintf(stderr, " "); - } - fprintf(stderr, "SCC=%d", ssa->vars[j].scc); - } - fprintf(stderr, "\n"); - } - } -} - -static void zend_dump_var_set(const zend_op_array *op_array, const char *name, zend_bitset set) -{ - int first = 1; - uint32_t i; - - fprintf(stderr, " ; %s = {", name); - for (i = 0; i < op_array->last_var + op_array->T; i++) { - if (zend_bitset_in(set, i)) { - if (first) { - first = 0; - } else { - fprintf(stderr, ", "); - } - zend_dump_var(op_array, IS_CV, i); - } - } - fprintf(stderr, "}\n"); -} - -void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg) -{ - int j; - fprintf(stderr, "\nVariable Liveness for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\"\n"); - - for (j = 0; j < cfg->blocks_count; j++) { - fprintf(stderr, " BB%d:\n", j); - zend_dump_var_set(op_array, "def", DFG_BITSET(dfg->def, dfg->size, j)); - zend_dump_var_set(op_array, "use", DFG_BITSET(dfg->use, dfg->size, j)); - zend_dump_var_set(op_array, "in ", DFG_BITSET(dfg->in, dfg->size, j)); - zend_dump_var_set(op_array, "out", DFG_BITSET(dfg->out, dfg->size, j)); - } -} - -void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa) -{ - int j; - zend_ssa_block *ssa_blocks = ssa->blocks; - int blocks_count = ssa->cfg.blocks_count; - - fprintf(stderr, "\nSSA Phi() Placement for \""); - zend_dump_op_array_name(op_array); - fprintf(stderr, "\"\n"); - for (j = 0; j < blocks_count; j++) { - if (ssa_blocks && ssa_blocks[j].phis) { - zend_ssa_phi *p = ssa_blocks[j].phis; - int first = 1; - - fprintf(stderr, " BB%d:\n", j); - if (p->pi >= 0) { - fprintf(stderr, " ; pi={"); - } else { - fprintf(stderr, " ; phi={"); - } - do { - if (first) { - first = 0; - } else { - fprintf(stderr, ", "); - } - zend_dump_var(op_array, IS_CV, p->var); - p = p->next; - } while (p); - fprintf(stderr, "}\n"); - } - } -} diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h deleted file mode 100644 index 3d8ff7ad7a..0000000000 --- a/ext/opcache/Optimizer/zend_dump.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Bytecode Visualisation | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_DUMP_H -#define ZEND_DUMP_H - -#include "zend_ssa.h" -#include "zend_dfg.h" - -#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0) -#define ZEND_DUMP_RC_INFERENCE (1<<1) -#define ZEND_DUMP_CFG (1<<2) -#define ZEND_DUMP_SSA (1<<3) -#define ZEND_DUMP_LIVE_RANGES (1<<4) - -BEGIN_EXTERN_C() - -void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data); -void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op); -void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg); -void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg); -void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa); -void zend_dump_variables(const zend_op_array *op_array); -void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags); -void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags); -void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num); -void zend_dump_op_array_name(const zend_op_array *op_array); -void zend_dump_const(const zval *zv); -void zend_dump_ht(HashTable *ht); - -END_EXTERN_C() - -#endif /* ZEND_DUMP_H */ diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c deleted file mode 100644 index 228051ee86..0000000000 --- a/ext/opcache/Optimizer/zend_func_info.c +++ /dev/null @@ -1,973 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Func Info | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - | Xinchen Hui <laruence@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_extensions.h" -#include "zend_ssa.h" -#include "zend_optimizer_internal.h" -#include "zend_inference.h" -#include "zend_call_graph.h" -#include "zend_func_info.h" -#include "zend_inference.h" -#ifdef _WIN32 -#include "win32/ioutil.h" -#endif - -typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa); - -typedef struct _func_info_t { - const char *name; - int name_len; - uint32_t info; - info_func_t info_func; -} func_info_t; - -#define F0(name, info) \ - {name, sizeof(name)-1, (info), NULL} -#define F1(name, info) \ - {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL} -#define FN(name, info) \ - {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL} -#define FR(name, info) \ - {name, sizeof(name)-1, (MAY_BE_REF | (info)), NULL} -#define FX(name, info) \ - {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL} -#define FC(name, callback) \ - {name, sizeof(name)-1, 0, callback} - -static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (!call_info->send_unpack - && (call_info->num_args == 2 || call_info->num_args == 3) - && ssa - && !(ssa->cfg.flags & ZEND_SSA_TSSA)) { - zend_op_array *op_array = call_info->caller_op_array; - uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline, - &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]); - uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, - &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]); - uint32_t t3 = 0; - uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED; - - if (call_info->num_args == 3) { - t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, - &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]); - } - if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) { - tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; - } - if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING)) - || (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING)) - || (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) { - tmp |= MAY_BE_ARRAY_OF_DOUBLE; - } - if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) { - if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) { - tmp |= MAY_BE_ARRAY_OF_LONG; - } - } - return tmp; - } else { - /* May throw */ - return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; - } -} - -#define UNKNOWN_INFO (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF) - -static const func_info_t func_infos[] = { - /* zend */ - F1("zend_version", MAY_BE_STRING), - FN("func_get_arg", UNKNOWN_INFO), - FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT), - F0("restore_error_handler", MAY_BE_TRUE), - F0("restore_exception_handler", MAY_BE_TRUE), - F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - F1("get_resource_type", MAY_BE_STRING), - F1("get_defined_constants", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY), - F1("debug_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - - /* ext/standard */ - FN("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("bin2hex", MAY_BE_STRING), - F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING), -#if HAVE_NANOSLEEP - F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), -#endif -#if HAVE_STRPTIME - F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), -#endif - F1("wordwrap", MAY_BE_STRING), - F1("htmlspecialchars", MAY_BE_STRING), - F1("htmlentities", MAY_BE_STRING), - F1("get_html_translation_table", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("sha1", MAY_BE_STRING), - F1("sha1_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("md5", MAY_BE_STRING), - F1("md5_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("iptcparse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("iptcembed", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("getimagesize", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("getimagesizefromstring", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("image_type_to_mime_type", MAY_BE_STRING), - F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING), - F0("phpinfo", MAY_BE_TRUE), - F1("phpversion", MAY_BE_FALSE | MAY_BE_STRING), - F0("phpcredits", MAY_BE_TRUE), - F1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("php_uname", MAY_BE_STRING), - F1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING), - F1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("strtok", MAY_BE_FALSE | MAY_BE_STRING), - F1("strrev", MAY_BE_STRING), - F1("hebrev", MAY_BE_STRING), - F1("basename", MAY_BE_STRING), - F1("dirname", MAY_BE_STRING), - F1("pathinfo", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("stripslashes", MAY_BE_STRING), - F1("stripcslashes", MAY_BE_STRING), - F1("strstr", MAY_BE_FALSE | MAY_BE_STRING), - F1("stristr", MAY_BE_FALSE | MAY_BE_STRING), - F1("strrchr", MAY_BE_FALSE | MAY_BE_STRING), - F1("str_shuffle", MAY_BE_STRING), - F1("str_word_count", MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("str_split", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING), - FN("substr_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), - F1("quotemeta", MAY_BE_STRING), - F1("ucwords", MAY_BE_STRING), - F1("addcslashes", MAY_BE_STRING), - FN("str_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT), - FN("str_ireplace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT), - F1("str_repeat", MAY_BE_STRING), - F1("count_chars", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("chunk_split", MAY_BE_STRING), - F1("strip_tags", MAY_BE_STRING), - F1("explode", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("localeconv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), -#if HAVE_NL_LANGINFO - F1("nl_langinfo", MAY_BE_FALSE | MAY_BE_STRING), -#endif - F1("soundex", MAY_BE_STRING), - F1("chr", MAY_BE_STRING), - F1("str_getcsv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("strchr", MAY_BE_FALSE | MAY_BE_STRING), - F1("sprintf", MAY_BE_STRING), - F1("vsprintf", MAY_BE_STRING), - F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG), - F1("urlencode", MAY_BE_STRING), - F1("urldecode", MAY_BE_STRING), - F1("rawurlencode", MAY_BE_STRING), - F1("rawurldecode", MAY_BE_STRING), - F1("http_build_query", MAY_BE_STRING), -#if defined(HAVE_SYMLINK) || defined(PHP_WIN32) - F1("readlink", MAY_BE_FALSE | MAY_BE_STRING), -#endif - F1("exec", MAY_BE_FALSE | MAY_BE_STRING), - F1("system", MAY_BE_FALSE | MAY_BE_STRING), - F1("escapeshellcmd", MAY_BE_STRING), - F1("escapeshellarg", MAY_BE_STRING), - F0("passthru", MAY_BE_NULL | MAY_BE_FALSE), - F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), -#ifdef PHP_CAN_SUPPORT_PROC_OPEN - F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("proc_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), -#endif - F1("random_bytes", MAY_BE_STRING), -#if HAVE_GETSERVBYPORT - F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING), -#endif -#if HAVE_GETPROTOBYNUMBER - F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING), -#endif - F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING), - F1("base64_encode", MAY_BE_STRING), - F1("password_hash", MAY_BE_STRING), - F1("password_get_info", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("convert_uuencode", MAY_BE_STRING), - F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING), - F1("pow", MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT), - F1("decbin", MAY_BE_STRING), - F1("decoct", MAY_BE_STRING), - F1("dechex", MAY_BE_STRING), - F1("base_convert", MAY_BE_STRING), - F1("number_format", MAY_BE_STRING), -#ifdef HAVE_INET_NTOP - F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING), -#endif -#ifdef HAVE_INET_PTON - F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING), -#endif - F1("long2ip", MAY_BE_FALSE | MAY_BE_STRING), - F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), -#ifdef HAVE_PUTENV -#endif - F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), -#ifdef HAVE_GETLOADAVG - F1("sys_getloadavg", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), -#endif -#ifdef HAVE_GETTIMEOFDAY - F1("microtime", MAY_BE_DOUBLE | MAY_BE_STRING), - F1("gettimeofday", MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), -#endif -#ifdef HAVE_GETRUSAGE - F1("getrusage", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), -#endif -#ifdef HAVE_GETTIMEOFDAY - F1("uniqid", MAY_BE_STRING), -#endif - F1("quoted_printable_decode", MAY_BE_STRING), - F1("quoted_printable_encode", MAY_BE_STRING), - F1("get_current_user", MAY_BE_STRING), - F1("get_cfg_var", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - FN("call_user_func", UNKNOWN_INFO), - FN("call_user_func_array", UNKNOWN_INFO), - FN("call_user_method", UNKNOWN_INFO), - FN("call_user_method_array", UNKNOWN_INFO), - FN("forward_static_call", UNKNOWN_INFO), - FN("forward_static_call_array", UNKNOWN_INFO), - F1("serialize", MAY_BE_STRING), - FN("unserialize", UNKNOWN_INFO), - F1("var_export", MAY_BE_NULL | MAY_BE_STRING), - F1("print_r", MAY_BE_TRUE | MAY_BE_STRING), - F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE), - F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("show_source", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("php_strip_whitespace", MAY_BE_STRING), - F1("ini_get_all", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("ini_alter", MAY_BE_FALSE | MAY_BE_STRING), - F1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING), - F1("set_include_path", MAY_BE_FALSE | MAY_BE_STRING), - F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), -#if ZEND_DEBUG - F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), -#endif - F1("gethostbyaddr", MAY_BE_FALSE | MAY_BE_STRING), - F1("gethostbyname", MAY_BE_STRING), - F1("gethostbynamel", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), -#ifdef HAVE_GETHOSTNAME - F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING), -#endif -#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC -# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS - F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), -# endif -#endif - F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING), - F1("fgets", MAY_BE_FALSE | MAY_BE_STRING), - F1("fread", MAY_BE_FALSE | MAY_BE_STRING), - F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), - F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING), - F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING), - F1("stream_context_create", MAY_BE_RESOURCE), - F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING), -#if HAVE_SOCKETPAIR - F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE), -#endif - F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING), - F1("fgetcsv", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("stream_get_meta_data", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING), - F1("stream_get_wrappers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("stream_get_transports", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING), - F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("realpath", MAY_BE_FALSE | MAY_BE_STRING), - F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pack", MAY_BE_STRING), - F1("unpack", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("crypt", MAY_BE_STRING), - FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING), - F1("readdir", MAY_BE_FALSE | MAY_BE_STRING), - F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("scandir", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), -#ifdef HAVE_GLOB - F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), -#endif - F1("filetype", MAY_BE_FALSE | MAY_BE_STRING), - F1("stat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("lstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), -#ifdef HAVE_SYSLOG_H - F0("syslog", MAY_BE_TRUE), - F0("closelog", MAY_BE_TRUE), -#endif - F1("metaphone", MAY_BE_STRING), - F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING), - F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING), - F1("ob_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("ob_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F0("array_walk", MAY_BE_TRUE), - F0("array_walk_recursive", MAY_BE_TRUE), - F0("arsort", MAY_BE_TRUE), - F0("asort", MAY_BE_TRUE), - F0("krsort", MAY_BE_TRUE), - F0("ksort", MAY_BE_TRUE), - F0("shuffle", MAY_BE_TRUE), - F0("sort", MAY_BE_TRUE), - F0("usort", MAY_BE_TRUE), - F0("uasort", MAY_BE_TRUE), - F0("uksort", MAY_BE_TRUE), - FN("end", UNKNOWN_INFO), - FN("prev", UNKNOWN_INFO), - FN("next", UNKNOWN_INFO), - FN("reset", UNKNOWN_INFO), - FN("current", UNKNOWN_INFO), - FN("min", UNKNOWN_INFO), - FN("max", UNKNOWN_INFO), - F1("compact", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_fill", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("array_fill_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FC("range", zend_range_info), - FN("array_pop", UNKNOWN_INFO), - FN("array_shift", UNKNOWN_INFO), - F1("array_splice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_slice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_replace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_replace_recursive", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - FN("array_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_count_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), - F1("array_column", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_reverse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_reduce", UNKNOWN_INFO), - F1("array_flip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("array_change_key_case", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_rand", MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("array_intersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_chunk", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_combine", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("pos", UNKNOWN_INFO), - F1("assert_options", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT), - F1("str_rot13", MAY_BE_STRING), - F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT), - F1("stream_bucket_new", MAY_BE_OBJECT), - F1("sys_get_temp_dir", MAY_BE_STRING), - - /* ext/date */ - F1("date", MAY_BE_STRING), - F1("gmdate", MAY_BE_STRING), - F1("strftime", MAY_BE_FALSE | MAY_BE_STRING), - F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING), - F1("localtime", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), - F1("getdate", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_parse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("date_parse_from_format", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("date_format", MAY_BE_STRING), - F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_diff", MAY_BE_OBJECT), - F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("timezone_name_get", MAY_BE_STRING), - F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING), - F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING), - F1("timezone_identifiers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("timezone_version_get", MAY_BE_STRING), - F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("date_interval_format", MAY_BE_STRING), - F1("date_default_timezone_get", MAY_BE_STRING), - F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING), - F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING), - F1("date_sun_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG), - - /* ext/preg */ - FN("preg_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), - FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), - F1("preg_filter", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), - F1("preg_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - - /* ext/mysqli */ - F1("mysqli_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), - F0("mysqli_close", MAY_BE_TRUE), - F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING), - F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("mysqli_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("mysqli_get_links_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), - F1("mysqli_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT), - F1("mysqli_get_charset", MAY_BE_NULL | MAY_BE_OBJECT), - F1("mysqli_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("mysqli_fetch_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("mysqli_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("mysqli_fetch_object", MAY_BE_NULL | MAY_BE_OBJECT), - F1("mysqli_affected_rows", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_character_set_name", MAY_BE_STRING), - F0("mysqli_debug", MAY_BE_TRUE), - F1("mysqli_error", MAY_BE_STRING), - F1("mysqli_reap_async_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT), - F1("mysqli_stmt_get_result", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_stmt_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("mysqli_stmt_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_fetch_field", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_fetch_fields", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_OBJECT), - F1("mysqli_fetch_field_direct", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_fetch_lengths", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("mysqli_fetch_row", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("mysqli_get_client_info", MAY_BE_NULL | MAY_BE_STRING), - F1("mysqli_get_host_info", MAY_BE_STRING), - F1("mysqli_get_server_info", MAY_BE_STRING), - F1("mysqli_info", MAY_BE_NULL | MAY_BE_STRING), - F1("mysqli_init", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_insert_id", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_num_rows", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_prepare", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_real_escape_string", MAY_BE_STRING), - F1("mysqli_stmt_affected_rows", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_stmt_insert_id", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_stmt_num_rows", MAY_BE_LONG | MAY_BE_STRING), - F1("mysqli_sqlstate", MAY_BE_STRING), - F0("mysqli_ssl_set", MAY_BE_TRUE), - F1("mysqli_stat", MAY_BE_FALSE | MAY_BE_STRING), - F1("mysqli_stmt_error", MAY_BE_STRING), - F1("mysqli_stmt_init", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_stmt_result_metadata", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_stmt_sqlstate", MAY_BE_STRING), - F1("mysqli_store_result", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("mysqli_use_result", MAY_BE_FALSE | MAY_BE_OBJECT), - - /* ext/curl */ - F1("curl_init", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("curl_copy_handle", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("curl_version", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("curl_error", MAY_BE_STRING), - F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING), - F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING), - F1("curl_escape", MAY_BE_FALSE | MAY_BE_STRING), - F1("curl_unescape", MAY_BE_FALSE | MAY_BE_STRING), - F1("curl_multi_init", MAY_BE_OBJECT), - F1("curl_multi_info_read", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT), - F1("curl_share_init", MAY_BE_OBJECT), - F1("curl_file_create", MAY_BE_OBJECT), - - /* ext/mbstring */ - F1("mb_convert_case", MAY_BE_STRING), - F1("mb_strtoupper", MAY_BE_STRING), - F1("mb_strtolower", MAY_BE_STRING), - F1("mb_language", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("mb_http_input", MAY_BE_FALSE | MAY_BE_STRING| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("mb_http_output", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("mb_detect_order", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("mb_substitute_character", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING), - F1("mb_output_handler", MAY_BE_STRING), - F1("mb_preferred_mime_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_strstr", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_strrchr", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_stristr", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_strrichr", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_substr", MAY_BE_STRING), - F1("mb_strcut", MAY_BE_STRING), - F1("mb_strimwidth", MAY_BE_STRING), - F1("mb_convert_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("mb_detect_encoding", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("mb_encoding_aliases", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("mb_convert_kana", MAY_BE_STRING), - F1("mb_encode_mimeheader", MAY_BE_STRING), - F1("mb_decode_mimeheader", MAY_BE_STRING), - F1("mb_convert_variables", MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_encode_numericentity", MAY_BE_STRING), - F1("mb_decode_numericentity", MAY_BE_STRING), - F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - - F1("mb_regex_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("mb_regex_set_options", MAY_BE_STRING), - F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_ereg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("mb_ereg_search_pos", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("mb_ereg_search_regs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING), - F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING), - - /* ext/iconv */ - F1("iconv", MAY_BE_FALSE | MAY_BE_STRING), - F1("iconv_get_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING), - F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING), - F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING), - F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - - /* ext/json */ - F1("json_encode", MAY_BE_FALSE | MAY_BE_STRING), - F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("json_last_error_msg", MAY_BE_STRING), - - /* ext/xml */ - F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING), - F1("xml_parser_get_option", MAY_BE_LONG | MAY_BE_STRING), - F1("utf8_encode", MAY_BE_STRING), - F1("utf8_decode", MAY_BE_STRING), - - /* ext/zlib */ - F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzread", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzdeflate", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzinflate", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzencode", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzdecode", MAY_BE_FALSE | MAY_BE_STRING), - F1("zlib_encode", MAY_BE_FALSE | MAY_BE_STRING), - F1("zlib_decode", MAY_BE_FALSE | MAY_BE_STRING), - F1("zlib_get_coding_type", MAY_BE_FALSE | MAY_BE_STRING), - F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING), - - /* ext/hash */ - F1("hash", MAY_BE_FALSE | MAY_BE_STRING), - F1("hash_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("hash_hmac", MAY_BE_FALSE | MAY_BE_STRING), - F1("hash_hmac_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("hash_hmac_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("hash_hkdf", MAY_BE_STRING), - F1("hash_init", MAY_BE_OBJECT), - F1("hash_final", MAY_BE_STRING), - F1("hash_copy", MAY_BE_OBJECT), - F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("hash_pbkdf2", MAY_BE_STRING), - F1("mhash_keygen_s2k", MAY_BE_FALSE | MAY_BE_STRING), - F1("mhash_get_hash_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("mhash", MAY_BE_FALSE | MAY_BE_FALSE | MAY_BE_STRING), - - /* ext/sodium */ - F1("sodium_crypto_shorthash", MAY_BE_STRING), - F1("sodium_crypto_secretbox", MAY_BE_STRING), - F1("sodium_crypto_secretbox_open", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_generichash", MAY_BE_STRING), - F1("sodium_crypto_generichash_init", MAY_BE_STRING), - F0("sodium_crypto_generichash_update", MAY_BE_TRUE), - F1("sodium_crypto_generichash_final", MAY_BE_STRING), - F1("sodium_crypto_box_keypair", MAY_BE_STRING), - F1("sodium_crypto_box_seed_keypair", MAY_BE_STRING), - F1("sodium_crypto_box_secretkey", MAY_BE_STRING), - F1("sodium_crypto_box_publickey", MAY_BE_STRING), - F1("sodium_crypto_box", MAY_BE_STRING), - F1("sodium_crypto_box_open", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_box_seal", MAY_BE_STRING), - F1("sodium_crypto_box_seal_open", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_sign_keypair", MAY_BE_STRING), - F1("sodium_crypto_sign_seed_keypair", MAY_BE_STRING), - F1("sodium_crypto_sign_secretkey", MAY_BE_STRING), - F1("sodium_crypto_sign_publickey", MAY_BE_STRING), - F1("sodium_crypto_sign", MAY_BE_STRING), - F1("sodium_crypto_sign_open", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_sign_detached", MAY_BE_STRING), - F1("sodium_crypto_stream", MAY_BE_STRING), - F1("sodium_crypto_stream_xor", MAY_BE_STRING), - F1("sodium_crypto_pwhash", MAY_BE_STRING), - F1("sodium_crypto_pwhash_str", MAY_BE_STRING), - F1("sodium_crypto_aead_aes256gcm_encrypt", MAY_BE_STRING), - F1("sodium_crypto_aead_aes256gcm_decrypt", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_bin2hex", MAY_BE_STRING), - F1("sodium_hex2bin", MAY_BE_STRING), - F1("sodium_crypto_scalarmult", MAY_BE_STRING), - F1("sodium_crypto_kx_seed_keypair", MAY_BE_STRING), - F1("sodium_crypto_kx_keypair", MAY_BE_STRING), - F1("sodium_crypto_kx_secretkey", MAY_BE_STRING), - F1("sodium_crypto_kx_publickey", MAY_BE_STRING), - F1("sodium_crypto_kx_client_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("sodium_crypto_kx_server_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("sodium_crypto_auth", MAY_BE_STRING), - F1("sodium_crypto_aead_aes256gcm_keygen", MAY_BE_STRING), - F1("sodium_crypto_auth_keygen", MAY_BE_STRING), - F1("sodium_crypto_generichash_keygen", MAY_BE_STRING), - F1("sodium_crypto_kdf_keygen", MAY_BE_STRING), - F1("sodium_crypto_secretbox_keygen", MAY_BE_STRING), - F1("sodium_crypto_shorthash_keygen", MAY_BE_STRING), - F1("sodium_crypto_stream_keygen", MAY_BE_STRING), - F1("sodium_crypto_kdf_derive_from_key", MAY_BE_STRING), - F1("sodium_pad", MAY_BE_STRING), - F1("sodium_unpad", MAY_BE_STRING), - - F1("sodium_crypto_box_keypair_from_secretkey_and_publickey", MAY_BE_STRING), - F1("sodium_crypto_box_publickey_from_secretkey", MAY_BE_STRING), - F1("sodium_crypto_sign_keypair_from_secretkey_and_publickey", MAY_BE_STRING), - F1("sodium_crypto_sign_publickey_from_secretkey", MAY_BE_STRING), - F1("sodium_crypto_pwhash_scryptsalsa208sha256", MAY_BE_STRING), - F1("sodium_crypto_pwhash_scryptsalsa208sha256_str", MAY_BE_STRING), - F1("sodium_crypto_sign_ed25519_sk_to_curve25519", MAY_BE_STRING), - F1("sodium_crypto_sign_ed25519_pk_to_curve25519", MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_encrypt", MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_decrypt", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_ietf_encrypt", MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", MAY_BE_STRING), - F1("sodium_crypto_aead_xchacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_keygen", MAY_BE_STRING), - F1("sodium_crypto_aead_chacha20poly1305_ietf_keygen", MAY_BE_STRING), - F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING), - - /* ext/session */ - F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("session_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("session_module_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("session_save_path", MAY_BE_FALSE | MAY_BE_STRING), - F1("session_create_id", MAY_BE_FALSE | MAY_BE_STRING), - F1("session_cache_limiter", MAY_BE_FALSE | MAY_BE_STRING), - F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING), - - /* ext/pgsql */ - F1("pg_connect", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("pg_pconnect", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_dbname", MAY_BE_STRING), - F1("pg_last_error", MAY_BE_STRING), - F1("pg_options", MAY_BE_STRING), - F1("pg_port", MAY_BE_STRING), - F1("pg_tty", MAY_BE_STRING), - F1("pg_host", MAY_BE_STRING), - F1("pg_version", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_NULL), - F1("pg_parameter_status", MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_query", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_query_params", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_prepare", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_execute", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("pg_last_notice", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY ), - F1("pg_field_table", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F1("pg_field_name", MAY_BE_STRING), - F1("pg_field_type", MAY_BE_STRING), - F1("pg_field_type_oid", MAY_BE_LONG | MAY_BE_STRING), - F1("pg_fetch_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_fetch_row", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("pg_fetch_assoc", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("pg_fetch_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("pg_fetch_object", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("pg_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("pg_fetch_all_columns", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F1("pg_last_oid", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F1("pg_lo_create", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F1("pg_lo_open", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_lo_read", MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_lo_import", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F1("pg_copy_to", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("pg_escape_string", MAY_BE_STRING), - F1("pg_escape_bytea", MAY_BE_STRING), - F1("pg_unescape_bytea", MAY_BE_STRING), - F1("pg_escape_literal", MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_escape_identifier", MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_result_error", MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_result_error_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("pg_get_result", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING), - F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("pg_socket", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_STRING), - F1("pg_update", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("pg_delete", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("pg_select", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - - /* ext/bcmath */ - F1("bcadd", MAY_BE_STRING), - F1("bcsub", MAY_BE_STRING), - F1("bcmul", MAY_BE_STRING), - F1("bcdiv", MAY_BE_STRING), - F1("bcmod", MAY_BE_STRING), - F1("bcpowmod", MAY_BE_STRING), - F1("bcpow", MAY_BE_STRING), - F1("bcsqrt", MAY_BE_STRING), - - /* ext/exif */ - F1("exif_tagname", MAY_BE_FALSE | MAY_BE_STRING), - F1("exif_read_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("exif_thumbnail", MAY_BE_FALSE | MAY_BE_STRING), - - /* ext/filter */ - FN("filter_input", UNKNOWN_INFO), - FN("filter_var", UNKNOWN_INFO), - F1("filter_input_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("filter_var_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - - /* ext/gettext */ - F1("textdomain", MAY_BE_STRING), - F1("gettext", MAY_BE_STRING), - F1("_", MAY_BE_STRING), - F1("dgettext", MAY_BE_STRING), - F1("dcgettext", MAY_BE_STRING), - F1("bindtextdomain", MAY_BE_FALSE | MAY_BE_STRING), -#if HAVE_NGETTEXT - F1("ngettext", MAY_BE_STRING), -#endif -#if HAVE_DNGETTEXT - F1("dcngettext", MAY_BE_STRING), -#endif -#if HAVE_BIND_TEXTDOMAIN_CODESET - F1("bind_textdomain_codeset", MAY_BE_FALSE | MAY_BE_STRING), -#endif - - /* ext/fileinfo */ - F1("finfo_open", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("finfo_file", MAY_BE_FALSE | MAY_BE_STRING), - F1("finfo_buffer", MAY_BE_FALSE | MAY_BE_STRING), - F1("mime_content_type", MAY_BE_FALSE | MAY_BE_STRING), - - /* ext/gd */ - F1("gd_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE), - F1("imagecreatetruecolor", MAY_BE_FALSE | MAY_BE_OBJECT), -#ifdef PHP_WIN32 - F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif - F1("imagerotate", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreate", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreatefromstring", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreatefromgif", MAY_BE_FALSE | MAY_BE_OBJECT), -#ifdef HAVE_GD_JPG - F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif -#ifdef HAVE_GD_PNG - F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif -#ifdef HAVE_GD_WEBP - F1("imagecreatefromwebp", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif - F1("imagecreatefromxbm", MAY_BE_FALSE | MAY_BE_OBJECT), -#if defined(HAVE_GD_XPM) - F1("imagecreatefromxpm", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif - F1("imagecreatefromwbmp", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreatefromgd", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreatefromgd2", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecreatefromgd2part", MAY_BE_FALSE | MAY_BE_OBJECT), -#if defined(HAVE_GD_BMP) - F1("imagecreatefrombmp", MAY_BE_FALSE | MAY_BE_OBJECT), -#endif - F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE), - F1("imagecolorsforindex", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), - F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("imageftbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("imagefttext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("imagettfbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("imagettftext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("imagecrop", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagecropauto", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imagescale", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imageaffine", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("imageaffinematrixget", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), - F1("imageaffinematrixconcat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), - F1("imageresolution", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - - /* ext/spl */ - F1("class_implements", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("class_parents", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("class_uses", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("iterator_to_array", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("spl_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("spl_object_hash", MAY_BE_STRING), - -}; - -static HashTable func_info; -int zend_func_info_rid = -1; - -static uint32_t get_internal_func_info( - const zend_call_info *call_info, const zend_ssa *ssa, zend_string *lcname) { - if (call_info->callee_func->common.scope) { - /* This is a method, not a function. */ - return 0; - } - - zval *zv = zend_hash_find_ex(&func_info, lcname, 1); - if (!zv) { - return 0; - } - - func_info_t *info = Z_PTR_P(zv); - if (info->info_func) { - return info->info_func(call_info, ssa); - } else { - return info->info; - } -} - -uint32_t zend_get_func_info( - const zend_call_info *call_info, const zend_ssa *ssa, - zend_class_entry **ce, bool *ce_is_instanceof) -{ - uint32_t ret = 0; - const zend_function *callee_func = call_info->callee_func; - *ce = NULL; - *ce_is_instanceof = 0; - - if (callee_func->type == ZEND_INTERNAL_FUNCTION) { - zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline, call_info->caller_init_opline->op2)); - - uint32_t internal_ret = get_internal_func_info(call_info, ssa, lcname); -#if !ZEND_DEBUG - if (internal_ret) { - return internal_ret; - } -#endif - - if (callee_func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - ret = zend_fetch_arg_info_type(NULL, callee_func->common.arg_info - 1, ce); - *ce_is_instanceof = 1; - } else { -#if 0 - fprintf(stderr, "Unknown internal function '%s'\n", func->common.function_name); -#endif - ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF - | MAY_BE_RC1 | MAY_BE_RCN; - } - if (callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { - ret |= MAY_BE_REF; - } - -#if ZEND_DEBUG - if (internal_ret) { - /* Check whether the func_info information is a subset of the information we can - * compute from the specified return type, otherwise it contains redundant types. */ - if (internal_ret & ~ret) { - fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(lcname)); - } - /* Check whether the func info is completely redundant with arginfo. - * Ignore UNKNOWN_INFO for now. */ - if (internal_ret == ret && (internal_ret & MAY_BE_ANY) != MAY_BE_ANY) { - fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(lcname)); - } - /* If the return type is not mixed, check that the types match exactly if we exclude - * RC and array information. */ - uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY; - if (ret_any != MAY_BE_ANY) { - uint32_t diff = internal_ret_any ^ ret_any; - /* Func info may contain "true" types as well as isolated "null" and "false". */ - if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE)) - && (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) { - fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(lcname)); - } - } - return internal_ret; - } -#endif - } else { - // FIXME: the order of functions matters!!! - zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func); - if (info) { - ret = info->return_info.type; - *ce = info->return_info.ce; - *ce_is_instanceof = info->return_info.is_instanceof; - } - if (!ret) { - ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF - | MAY_BE_RC1 | MAY_BE_RCN; - /* For generators RETURN_REFERENCE refers to the yielded values. */ - if ((callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) - && !(callee_func->common.fn_flags & ZEND_ACC_GENERATOR)) { - ret |= MAY_BE_REF; - } - } - } - return ret; -} - -int zend_func_info_startup(void) -{ - size_t i; - - if (zend_func_info_rid == -1) { - zend_func_info_rid = zend_get_resource_handle("Zend Optimizer"); - if (zend_func_info_rid < 0) { - return FAILURE; - } - - zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1); - for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) { - zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1); - - if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) { - fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name); - } - zend_string_release_ex(key, 1); - } - } - - return SUCCESS; -} - -int zend_func_info_shutdown(void) -{ - if (zend_func_info_rid != -1) { - zend_hash_destroy(&func_info); - zend_func_info_rid = -1; - } - return SUCCESS; -} diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h deleted file mode 100644 index 423d557f7a..0000000000 --- a/ext/opcache/Optimizer/zend_func_info.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, Func Info | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_FUNC_INFO_H -#define ZEND_FUNC_INFO_H - -#include "zend_ssa.h" - -/* func/cfg flags */ -#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */ -#define ZEND_FUNC_HAS_CALLS (1<<1) -#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */ -#define ZEND_FUNC_NO_LOOPS (1<<3) -#define ZEND_FUNC_IRREDUCIBLE (1<<4) -#define ZEND_FUNC_FREE_LOOP_VAR (1<<5) -#define ZEND_FUNC_RECURSIVE (1<<7) -#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8) -#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9) -#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10) -#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11) -#define ZEND_SSA_TSSA (1<<12) /* used by tracing JIT */ - -#define ZEND_FUNC_JIT_ON_FIRST_EXEC (1<<13) /* used by JIT */ -#define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */ -#define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */ -#define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */ - - -typedef struct _zend_func_info zend_func_info; -typedef struct _zend_call_info zend_call_info; - -#define ZEND_FUNC_INFO(op_array) \ - ((zend_func_info*)((op_array)->reserved[zend_func_info_rid])) - -#define ZEND_SET_FUNC_INFO(op_array, info) do { \ - zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \ - *pinfo = info; \ - } while (0) - -BEGIN_EXTERN_C() - -extern int zend_func_info_rid; - -uint32_t zend_get_func_info( - const zend_call_info *call_info, const zend_ssa *ssa, - zend_class_entry **ce, bool *ce_is_instanceof); - -int zend_func_info_startup(void); -int zend_func_info_shutdown(void); - -END_EXTERN_C() - -#endif /* ZEND_FUNC_INFO_H */ diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c deleted file mode 100644 index 92be7ee802..0000000000 --- a/ext/opcache/Optimizer/zend_inference.c +++ /dev/null @@ -1,4681 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, e-SSA based Type & Range Inference | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_generators.h" -#include "zend_inference.h" -#include "zend_func_info.h" -#include "zend_call_graph.h" -#include "zend_worklist.h" - -/* The used range inference algorithm is described in: - * V. Campos, R. Rodrigues, I. de Assis Costa and F. Pereira. - * "Speed and Precision in Range Analysis", SBLP'12. - * - * There are a couple degrees of freedom, we use: - * * Propagation on SCCs. - * * e-SSA for live range splitting. - * * Only intra-procedural inference. - * * Widening with warmup passes, but without jump sets. - */ - -/* Whether to handle symbolic range constraints */ -#define SYM_RANGE - -/* Whether to handle negative range constraints */ -/* Negative range inference is buggy, so disabled for now */ -#undef NEG_RANGE - -/* Number of warmup passes to use prior to widening */ -#define RANGE_WARMUP_PASSES 16 - -/* Logging for range inference in general */ -#if 0 -#define LOG_SSA_RANGE(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG_SSA_RANGE(...) -#endif - -/* Logging for negative range constraints */ -#if 0 -#define LOG_NEG_RANGE(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG_NEG_RANGE(...) -#endif - -/* Pop elements in unspecified order from worklist until it is empty */ -#define WHILE_WORKLIST(worklist, len, i) do { \ - bool _done = 0; \ - while (!_done) { \ - _done = 1; \ - ZEND_BITSET_FOREACH(worklist, len, i) { \ - zend_bitset_excl(worklist, i); \ - _done = 0; - -#define WHILE_WORKLIST_END() \ - } ZEND_BITSET_FOREACH_END(); \ - } \ -} while (0) - -#define CHECK_SCC_VAR(var2) \ - do { \ - if (!ssa->vars[var2].no_val) { \ - if (dfs[var2] < 0) { \ - zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \ - } \ - if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ - root[var] = root[var2]; \ - } \ - } \ - } while (0) - -#define CHECK_SCC_ENTRY(var2) \ - do { \ - if (ssa->vars[var2].scc != ssa->vars[var].scc) { \ - ssa->vars[var2].scc_entry = 1; \ - } \ - } while (0) - -#define ADD_SCC_VAR(_var) \ - do { \ - if (ssa->vars[_var].scc == scc) { \ - zend_bitset_incl(worklist, _var); \ - } \ - } while (0) - -#define ADD_SCC_VAR_1(_var) \ - do { \ - if (ssa->vars[_var].scc == scc && \ - !zend_bitset_in(visited, _var)) { \ - zend_bitset_incl(worklist, _var); \ - } \ - } while (0) - -#define FOR_EACH_DEFINED_VAR(line, MACRO) \ - do { \ - if (ssa->ops[line].op1_def >= 0) { \ - MACRO(ssa->ops[line].op1_def); \ - } \ - if (ssa->ops[line].op2_def >= 0) { \ - MACRO(ssa->ops[line].op2_def); \ - } \ - if (ssa->ops[line].result_def >= 0) { \ - MACRO(ssa->ops[line].result_def); \ - } \ - if (op_array->opcodes[line].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line-1].op1_def >= 0) { \ - MACRO(ssa->ops[line-1].op1_def); \ - } \ - if (ssa->ops[line-1].op2_def >= 0) { \ - MACRO(ssa->ops[line-1].op2_def); \ - } \ - if (ssa->ops[line-1].result_def >= 0) { \ - MACRO(ssa->ops[line-1].result_def); \ - } \ - } else if ((uint32_t)line+1 < op_array->last && \ - op_array->opcodes[line+1].opcode == ZEND_OP_DATA) { \ - if (ssa->ops[line+1].op1_def >= 0) { \ - MACRO(ssa->ops[line+1].op1_def); \ - } \ - if (ssa->ops[line+1].op2_def >= 0) { \ - MACRO(ssa->ops[line+1].op2_def); \ - } \ - if (ssa->ops[line+1].result_def >= 0) { \ - MACRO(ssa->ops[line+1].result_def); \ - } \ - } \ - } while (0) - - -#define FOR_EACH_VAR_USAGE(_var, MACRO) \ - do { \ - zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ - int use = ssa->vars[_var].use_chain; \ - while (use >= 0) { \ - FOR_EACH_DEFINED_VAR(use, MACRO); \ - use = zend_ssa_next_use(ssa->ops, _var, use); \ - } \ - p = ssa->vars[_var].phi_use_chain; \ - while (p) { \ - MACRO(p->ssa_var); \ - p = zend_ssa_next_use_phi(ssa, _var, p); \ - } \ - } while (0) - -static inline bool add_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a > ZEND_LONG_MAX - b) - || (b < 0 && a < ZEND_LONG_MIN - b); -} -#if 0 -static inline bool sub_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a < ZEND_LONG_MIN + b) - || (b < 0 && a > ZEND_LONG_MAX + b); -} -#endif - -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */ -{ -#ifdef SYM_RANGE - zend_ssa_phi *p; -#endif - - dfs[var] = *index; - (*index)++; - root[var] = var; - - FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); - -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - p = ssa->vars[var].sym_use_chain; - while (p) { - CHECK_SCC_VAR(p->ssa_var); - p = p->sym_use_chain; - } -#endif - - if (root[var] == var) { - ssa->vars[var].scc = ssa->sccs; - while (stack->len > 0) { - int var2 = zend_worklist_stack_peek(stack); - if (dfs[var2] <= dfs[var]) { - break; - } - zend_worklist_stack_pop(stack); - ssa->vars[var2].scc = ssa->sccs; - } - ssa->sccs++; - } else { - zend_worklist_stack_push(stack, var); - } -} -/* }}} */ - -int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int index = 0, *dfs, *root; - zend_worklist_stack stack; - int j; - ALLOCA_FLAG(dfs_use_heap) - ALLOCA_FLAG(root_use_heap) - ALLOCA_FLAG(stack_use_heap) - - dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap); - memset(dfs, -1, sizeof(int) * ssa->vars_count); - root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap); - ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - - /* Find SCCs using Tarjan's algorithm. */ - for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && dfs[j] < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack); - } - } - - /* Revert SCC order. This results in a topological order. */ - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1); - } - } - - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - int var = j; - if (root[j] == j) { - ssa->vars[j].scc_entry = 1; - } - FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); - } - } - - ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); - free_alloca(root, root_use_heap); - free_alloca(dfs, dfs_use_heap); - - return SUCCESS; -} -/* }}} */ - -int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - zend_ssa_var *ssa_vars = ssa->vars; - zend_ssa_op *ssa_ops = ssa->ops; - int ssa_vars_count = ssa->vars_count; - zend_bitset worklist; - int i, j, use; - zend_ssa_phi *p; - ALLOCA_FLAG(use_heap); - - if (!op_array->function_name || !ssa->vars || !ssa->ops) { - return SUCCESS; - } - - worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap); - memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count)); - - for (i = 0; i < ssa_vars_count; i++) { - ssa_vars[i].no_val = 1; /* mark as unused */ - use = ssa->vars[i].use_chain; - while (use >= 0) { - if (!zend_ssa_is_no_val_use(&op_array->opcodes[use], &ssa->ops[use], i)) { - ssa_vars[i].no_val = 0; /* used directly */ - zend_bitset_incl(worklist, i); - break; - } - use = zend_ssa_next_use(ssa_ops, i, use); - } - } - - WHILE_WORKLIST(worklist, zend_bitset_len(ssa_vars_count), i) { - if (ssa_vars[i].definition_phi) { - /* mark all possible sources as used */ - p = ssa_vars[i].definition_phi; - if (p->pi >= 0) { - if (ssa_vars[p->sources[0]].no_val) { - ssa_vars[p->sources[0]].no_val = 0; /* used indirectly */ - zend_bitset_incl(worklist, p->sources[0]); - } - } else { - for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) { - ZEND_ASSERT(p->sources[j] >= 0); - if (ssa->vars[p->sources[j]].no_val) { - ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */ - zend_bitset_incl(worklist, p->sources[j]); - } - } - } - } - } WHILE_WORKLIST_END(); - - free_alloca(worklist, use_heap); - - return SUCCESS; -} -/* }}} */ - -/* From "Hacker's Delight" */ -zend_ulong minOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - zend_ulong m, temp; - - m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1); - while (m != 0) { - if (~a & c & m) { - temp = (a | m) & -m; - if (temp <= b) { - a = temp; - break; - } - } else if (a & ~c & m) { - temp = (c | m) & -m; - if (temp <= d) { - c = temp; - break; - } - } - m = m >> 1; - } - return a | c; -} - -zend_ulong maxOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - zend_ulong m, temp; - - m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1); - while (m != 0) { - if (b & d & m) { - temp = (b - m) | (m - 1); - if (temp >= a) { - b = temp; - break; - } - temp = (d - m) | (m - 1); - if (temp >= c) { - d = temp; - break; - } - } - m = m >> 1; - } - return b | d; -} - -zend_ulong minAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - zend_ulong m, temp; - - m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1); - while (m != 0) { - if (~a & ~c & m) { - temp = (a | m) & -m; - if (temp <= b) { - a = temp; - break; - } - temp = (c | m) & -m; - if (temp <= d) { - c = temp; - break; - } - } - m = m >> 1; - } - return a & c; -} - -zend_ulong maxAND(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - zend_ulong m, temp; - - m = Z_UL(1) << (sizeof(zend_ulong) * 8 - 1); - while (m != 0) { - if (b & ~d & m) { - temp = (b | ~m) | (m - 1); - if (temp >= a) { - b = temp; - break; - } - } else if (~b & d & m) { - temp = (d | ~m) | (m - 1); - if (temp >= c) { - d = temp; - break; - } - } - m = m >> 1; - } - return b & d; -} - -zend_ulong minXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - return minAND(a, b, ~d, ~c) | minAND(~b, ~a, c, d); -} - -zend_ulong maxXOR(zend_ulong a, zend_ulong b, zend_ulong c, zend_ulong d) -{ - return maxOR(0, maxAND(a, b, ~d, ~c), 0, maxAND(~b, ~a, c, d)); -} - -/* Based on "Hacker's Delight" */ - -/* -0: + + + + 0 0 0 0 => 0 0 + min/max -2: + + - + 0 0 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d) -3: + + - - 0 0 1 1 => 1 1 - min/max -8: - + + + 1 0 0 0 => 1 0 ? min(a,-1,b,d)/max(0,b,c,d) -a: - + - + 1 0 1 0 => 1 0 ? MIN(a,c)/max(0,b,0,d) -b: - + - - 1 0 1 1 => 1 1 - c/-1 -c: - - + + 1 1 0 0 => 1 1 - min/max -e: - - - + 1 1 1 0 => 1 1 - a/-1 -f - - - - 1 1 1 1 => 1 1 - min/max -*/ -static void zend_ssa_range_or(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp) -{ - int x = ((a < 0) ? 8 : 0) | - ((b < 0) ? 4 : 0) | - ((c < 0) ? 2 : 0) | - ((d < 0) ? 2 : 0); - switch (x) { - case 0x0: - case 0x3: - case 0xc: - case 0xf: - tmp->min = minOR(a, b, c, d); - tmp->max = maxOR(a, b, c, d); - break; - case 0x2: - tmp->min = minOR(a, b, c, -1); - tmp->max = maxOR(a, b, 0, d); - break; - case 0x8: - tmp->min = minOR(a, -1, c, d); - tmp->max = maxOR(0, b, c, d); - break; - case 0xa: - tmp->min = MIN(a, c); - tmp->max = maxOR(0, b, 0, d); - break; - case 0xb: - tmp->min = c; - tmp->max = -1; - break; - case 0xe: - tmp->min = a; - tmp->max = -1; - break; - } -} - -/* -0: + + + + 0 0 0 0 => 0 0 + min/max -2: + + - + 0 0 1 0 => 0 0 + 0/b -3: + + - - 0 0 1 1 => 0 0 + min/max -8: - + + + 1 0 0 0 => 0 0 + 0/d -a: - + - + 1 0 1 0 => 1 0 ? min(a,-1,c,-1)/NAX(b,d) -b: - + - - 1 0 1 1 => 1 0 ? min(a,-1,c,d)/max(0,b,c,d) -c: - - + + 1 1 0 0 => 1 1 - min/max -e: - - - + 1 1 1 0 => 1 0 ? min(a,b,c,-1)/max(a,b,0,d) -f - - - - 1 1 1 1 => 1 1 - min/max -*/ -static void zend_ssa_range_and(zend_long a, zend_long b, zend_long c, zend_long d, zend_ssa_range *tmp) -{ - int x = ((a < 0) ? 8 : 0) | - ((b < 0) ? 4 : 0) | - ((c < 0) ? 2 : 0) | - ((d < 0) ? 2 : 0); - switch (x) { - case 0x0: - case 0x3: - case 0xc: - case 0xf: - tmp->min = minAND(a, b, c, d); - tmp->max = maxAND(a, b, c, d); - break; - case 0x2: - tmp->min = 0; - tmp->max = b; - break; - case 0x8: - tmp->min = 0; - tmp->max = d; - break; - case 0xa: - tmp->min = minAND(a, -1, c, -1); - tmp->max = MAX(b, d); - break; - case 0xb: - tmp->min = minAND(a, -1, c, d); - tmp->max = maxAND(0, b, c, d); - break; - case 0xe: - tmp->min = minAND(a, b, c, -1); - tmp->max = maxAND(a, b, 0, d); - break; - } -} - -static inline bool zend_abs_range( - zend_long min, zend_long max, zend_long *abs_min, zend_long *abs_max) { - if (min == ZEND_LONG_MIN) { - /* Cannot take absolute value of LONG_MIN */ - return 0; - } - - if (min >= 0) { - *abs_min = min; - *abs_max = max; - } else if (max <= 0) { - *abs_min = -max; - *abs_max = -min; - } else { - /* Range crossing zero */ - *abs_min = 0; - *abs_max = MAX(max, -min); - } - - return 1; -} - -static inline zend_long safe_shift_left(zend_long n, zend_long s) { - return (zend_long) ((zend_ulong) n << (zend_ulong) s); -} - -static inline bool shift_left_overflows(zend_long n, zend_long s) { - /* This considers shifts that shift in the sign bit to be overflowing as well */ - if (n >= 0) { - return s >= SIZEOF_ZEND_LONG * 8 - 1 || safe_shift_left(n, s) < n; - } else { - return s >= SIZEOF_ZEND_LONG * 8 || safe_shift_left(n, s) > n; - } -} - -/* If b does not divide a exactly, return the two adjacent values between which the real result - * lies. */ -static void float_div(zend_long a, zend_long b, zend_long *r1, zend_long *r2) { - *r1 = *r2 = a / b; - if (a % b != 0) { - if (*r2 < 0) { - (*r2)--; - } else { - (*r2)++; - } - } -} - -static int zend_inference_calc_binary_op_range( - const zend_op_array *op_array, zend_ssa *ssa, - zend_op *opline, zend_ssa_op *ssa_op, zend_uchar opcode, zend_ssa_range *tmp) { - zend_long op1_min, op2_min, op1_max, op2_max, t1, t2, t3, t4; - - switch (opcode) { - case ZEND_ADD: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - zend_add_will_overflow(op1_min, op2_min)) { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - } else { - tmp->min = op1_min + op2_min; - } - if (OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW() || - zend_add_will_overflow(op1_max, op2_max)) { - tmp->overflow = 1; - tmp->max = ZEND_LONG_MAX; - } else { - tmp->max = op1_max + op2_max; - } - return 1; - } - break; - case ZEND_SUB: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_OVERFLOW() || - zend_sub_will_overflow(op1_min, op2_max)) { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - } else { - tmp->min = op1_min - op2_max; - } - if (OP1_RANGE_OVERFLOW() || - OP2_RANGE_UNDERFLOW() || - zend_sub_will_overflow(op1_max, op2_min)) { - tmp->overflow = 1; - tmp->max = ZEND_LONG_MAX; - } else { - tmp->max = op1_max - op2_min; - } - return 1; - } - break; - case ZEND_MUL: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - double dummy; - zend_long t1_overflow, t2_overflow, t3_overflow, t4_overflow; - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - /* Suppress uninit variable warnings, these will only be used if the overflow - * flags are all false. */ - t1 = t2 = t3 = t4 = 0; - ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_min, t1, dummy, t1_overflow); - ZEND_SIGNED_MULTIPLY_LONG(op1_min, op2_max, t2, dummy, t2_overflow); - ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_min, t3, dummy, t3_overflow); - ZEND_SIGNED_MULTIPLY_LONG(op1_max, op2_max, t4, dummy, t4_overflow); - (void) dummy; - - // FIXME: more careful overflow checks? - if (OP1_RANGE_UNDERFLOW() || OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || OP2_RANGE_OVERFLOW() || - t1_overflow || t2_overflow || t3_overflow || t4_overflow - ) { - tmp->underflow = 1; - tmp->overflow = 1; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); - tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); - } - return 1; - } - break; - case ZEND_DIV: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - if (op2_min <= 0 && op2_max >= 0) { - /* If op2 crosses zero, then floating point values close to zero might be - * possible, which will result in arbitrarily large results. As such, we can't - * do anything useful in that case. */ - break; - } - if (op1_min == ZEND_LONG_MIN && op2_max == -1) { - /* Avoid ill-defined division, which may trigger SIGFPE. */ - break; - } - - zend_long t1_, t2_, t3_, t4_; - float_div(op1_min, op2_min, &t1, &t1_); - float_div(op1_min, op2_max, &t2, &t2_); - float_div(op1_max, op2_min, &t3, &t3_); - float_div(op1_max, op2_max, &t4, &t4_); - - /* The only case in which division can "overflow" either a division by an absolute - * value smaller than one, or LONG_MIN / -1 in particular. Both cases have already - * been excluded above. */ - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->underflow = 1; - tmp->overflow = 1; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - tmp->min = MIN(MIN(MIN(t1, t2), MIN(t3, t4)), MIN(MIN(t1_, t2_), MIN(t3_, t4_))); - tmp->max = MAX(MAX(MAX(t1, t2), MAX(t3, t4)), MAX(MAX(t1_, t2_), MAX(t3_, t4_))); - } - return 1; - } - break; - case ZEND_MOD: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - zend_long op2_abs_min, op2_abs_max; - - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - if (!zend_abs_range(op2_min, op2_max, &op2_abs_min, &op2_abs_max)) { - break; - } - - if (op2_abs_max == 0) { - /* Always modulus by zero, nothing we can do */ - break; - } - if (op2_abs_min == 0) { - /* Ignore the modulus by zero case, which will throw */ - op2_abs_min++; - } - - if (op1_min >= 0) { - tmp->min = op1_max < op2_abs_min ? op1_min : 0; - tmp->max = MIN(op1_max, op2_abs_max - 1); - } else if (op1_max <= 0) { - tmp->min = MAX(op1_min, -op2_abs_max + 1); - tmp->max = op1_min > -op2_abs_min ? op1_max : 0; - } else { - tmp->min = MAX(op1_min, -op2_abs_max + 1); - tmp->max = MIN(op1_max, op2_abs_max - 1); - } - } - return 1; - } - break; - case ZEND_SL: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - /* Shifts by negative numbers will throw, ignore them */ - if (op2_min < 0) { - op2_min = 0; - } - if (op2_max < 0) { - op2_max = 0; - } - - if (shift_left_overflows(op1_min, op2_max) - || shift_left_overflows(op1_max, op2_max)) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - t1 = safe_shift_left(op1_min, op2_min); - t2 = safe_shift_left(op1_min, op2_max); - t3 = safe_shift_left(op1_max, op2_min); - t4 = safe_shift_left(op1_max, op2_max); - tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); - tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); - } - } - return 1; - } - break; - case ZEND_SR: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - /* Shifts by negative numbers will throw, ignore them */ - if (op2_min < 0) { - op2_min = 0; - } - if (op2_max < 0) { - op2_max = 0; - } - - /* Shifts by more than the integer size will be 0 or -1 */ - if (op2_min >= SIZEOF_ZEND_LONG * 8) { - op2_min = SIZEOF_ZEND_LONG * 8 - 1; - } - if (op2_max >= SIZEOF_ZEND_LONG * 8) { - op2_max = SIZEOF_ZEND_LONG * 8 - 1; - } - - t1 = op1_min >> op2_min; - t2 = op1_min >> op2_max; - t3 = op1_max >> op2_min; - t4 = op1_max >> op2_max; - tmp->min = MIN(MIN(t1, t2), MIN(t3, t4)); - tmp->max = MAX(MAX(t1, t2), MAX(t3, t4)); - } - return 1; - } - break; - case ZEND_BW_OR: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - zend_ssa_range_or(op1_min, op1_max, op2_min, op2_max, tmp); - } - return 1; - } - break; - case ZEND_BW_AND: - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP2_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW() || - OP2_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - zend_ssa_range_and(op1_min, op1_max, op2_min, op2_max, tmp); - } - return 1; - } - break; - case ZEND_BW_XOR: - // TODO - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - return 0; -} - -int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp) -{ - uint32_t line; - zend_op *opline; - zend_ssa_op *ssa_op; - - if (ssa->vars[var].definition_phi) { - zend_ssa_phi *p = ssa->vars[var].definition_phi; - int i; - - tmp->underflow = 0; - tmp->min = ZEND_LONG_MAX; - tmp->max = ZEND_LONG_MIN; - tmp->overflow = 0; - if (p->pi >= 0 && p->has_range_constraint) { - zend_ssa_range_constraint *constraint = &p->constraint.range; - if (constraint->negative) { - if (ssa->var_info[p->sources[0]].has_range) { - *tmp = ssa->var_info[p->sources[0]].range; - } else if (narrowing) { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 1; - } - -#ifdef NEG_RANGE - if (constraint->min_ssa_var < 0 && - constraint->max_ssa_var < 0 && - ssa->var_info[p->ssa_var].has_range) { - LOG_NEG_RANGE("%s() #%d [%ld..%ld] -> [%ld..%ld]?\n", - ZSTR_VAL(op_array->function_name), - p->ssa_var, - ssa->var_info[p->ssa_var].range.min, - ssa->var_info[p->ssa_var].range.max, - tmp->min, - tmp->max); - if (constraint->negative == NEG_USE_LT && - tmp->max >= constraint->range.min) { - tmp->overflow = 0; - tmp->max = constraint->range.min - 1; - LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max); - } else if (constraint->negative == NEG_USE_GT && - tmp->min <= constraint->range.max) { - tmp->underflow = 0; - tmp->min = constraint->range.max + 1; - LOG_NEG_RANGE(" => [%ld..%ld]\n", tmp->min, tmp->max); - } - } -#endif - } else if (ssa->var_info[p->sources[0]].has_range) { - /* intersection */ - *tmp = ssa->var_info[p->sources[0]].range; - if (constraint->min_ssa_var < 0) { - tmp->underflow = constraint->range.underflow && tmp->underflow; - tmp->min = MAX(constraint->range.min, tmp->min); -#ifdef SYM_RANGE - } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) { - tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow; - if (!add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) { - tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min); - } -#endif - } - if (constraint->max_ssa_var < 0) { - tmp->max = MIN(constraint->range.max, tmp->max); - tmp->overflow = constraint->range.overflow && tmp->overflow; -#ifdef SYM_RANGE - } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) { - if (!add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) { - tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max); - } - tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow; -#endif - } - } else if (narrowing) { - if (constraint->min_ssa_var < 0) { - tmp->underflow = constraint->range.underflow; - tmp->min = constraint->range.min; -#ifdef SYM_RANGE - } else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) { - if (add_will_overflow(ssa->var_info[constraint->min_ssa_var].range.min, constraint->range.min)) { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - } else { - tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow; - tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min; - } -#endif - } else { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - } - if (constraint->max_ssa_var < 0) { - tmp->max = constraint->range.max; - tmp->overflow = constraint->range.overflow; -#ifdef SYM_RANGE - } else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) { - if (add_will_overflow(ssa->var_info[constraint->max_ssa_var].range.max, constraint->range.max)) { - tmp->overflow = 1; - tmp->max = ZEND_LONG_MAX; - } else { - tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max; - tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow; - } -#endif - } else { - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 1; - } - } - } else { - for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) { - ZEND_ASSERT(p->sources[i] >= 0); - if (ssa->var_info[p->sources[i]].has_range) { - /* union */ - tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow; - tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min); - tmp->max = MAX(tmp->max, ssa->var_info[p->sources[i]].range.max); - tmp->overflow |= ssa->var_info[p->sources[i]].range.overflow; - } else if (narrowing) { - tmp->underflow = 1; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 1; - } - } - } - return (tmp->min <= tmp->max); - } else if (ssa->vars[var].definition < 0) { - if (var < op_array->last_var && - op_array->function_name) { - - tmp->min = 0; - tmp->max = 0; - tmp->underflow = 0; - tmp->overflow = 0; - return 1; - } - return 0; - } - line = ssa->vars[var].definition; - opline = op_array->opcodes + line; - ssa_op = &ssa->ops[line]; - - return zend_inference_propagate_range(op_array, ssa, opline, ssa_op, var, tmp); -} - -int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp) -{ - zend_long op1_min, op2_min, op1_max, op2_max; - - tmp->underflow = 0; - tmp->overflow = 0; - switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_SL: - case ZEND_SR: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - if (ssa_op->result_def == var) { - return zend_inference_calc_binary_op_range( - op_array, ssa, opline, ssa_op, opline->opcode, tmp); - } - break; - - case ZEND_BW_NOT: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - if (OP1_RANGE_UNDERFLOW() || - OP1_RANGE_OVERFLOW()) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - tmp->min = ~op1_max; - tmp->max = ~op1_min; - } - return 1; - } - } - break; - case ZEND_CAST: - if (ssa_op->op1_def == var) { - if (ssa_op->op1_def >= 0) { - if (OP1_HAS_RANGE()) { - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - } else if (ssa_op->result_def == var) { - if (opline->extended_value == IS_LONG) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - return 1; - } else { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - return 1; - } - } - } - break; - case ZEND_BOOL: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - tmp->min = (op1_min > 0 || op1_max < 0); - tmp->max = (op1_min != 0 || op1_max != 0); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_BOOL_NOT: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - tmp->min = (op1_min == 0 && op1_max == 0); - tmp->max = (op1_min <= 0 && op1_max >= 0); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_BOOL_XOR: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - op1_min = (op1_min > 0 || op1_max < 0); - op1_max = (op1_min != 0 || op1_max != 0); - op2_min = (op2_min > 0 || op2_max < 0); - op2_max = (op2_min != 0 || op2_max != 0); - tmp->min = 0; - tmp->max = 1; - if (op1_min == op1_max && op2_min == op2_max) { - if (op1_min == op2_min) { - tmp->max = 0; - } else { - tmp->min = 1; - } - } - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_IDENTICAL: - case ZEND_IS_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = (op1_min == op1_max && - op2_min == op2_max && - op1_min == op2_max); - tmp->max = (op1_min <= op2_max && op1_max >= op2_min); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_NOT_IDENTICAL: - case ZEND_IS_NOT_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = (op1_min > op2_max || op1_max < op2_min); - tmp->max = (op1_min != op1_max || - op2_min != op2_max || - op1_min != op2_max); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_SMALLER: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = op1_max < op2_min; - tmp->max = op1_min < op2_max; - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_SMALLER_OR_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = op1_max <= op2_min; - tmp->max = op1_min <= op2_max; - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_QM_ASSIGN: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_COPY_TMP: - if (ssa_op->op1_def == var) { - if (ssa_op->op1_def >= 0) { - if (OP1_HAS_RANGE()) { - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - } - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - break; - case ZEND_ASSERT_CHECK: - if (ssa_op->result_def == var) { - tmp->min = 0; - tmp->max = 1; - return 1; - } - break; - case ZEND_SEND_VAR: - if (ssa_op->op1_def == var) { - if (ssa_op->op1_def >= 0) { - if (OP1_HAS_RANGE()) { - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - } - break; - case ZEND_PRE_INC: - if (ssa_op->op1_def == var || ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - if (tmp->max < ZEND_LONG_MAX) { - tmp->max++; - } else { - tmp->overflow = 1; - } - if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) { - tmp->min++; - } - return 1; - } - } - break; - case ZEND_PRE_DEC: - if (ssa_op->op1_def == var || ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - if (tmp->min > ZEND_LONG_MIN) { - tmp->min--; - } else { - tmp->underflow = 1; - } - if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) { - tmp->max--; - } - return 1; - } - } - break; - case ZEND_POST_INC: - if (ssa_op->op1_def == var || ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - if (ssa_op->result_def == var) { - return 1; - } - if (tmp->max < ZEND_LONG_MAX) { - tmp->max++; - } else { - tmp->overflow = 1; - } - if (tmp->min < ZEND_LONG_MAX && !tmp->underflow) { - tmp->min++; - } - return 1; - } - } - break; - case ZEND_POST_DEC: - if (ssa_op->op1_def == var || ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - if (ssa_op->result_def == var) { - return 1; - } - if (tmp->min > ZEND_LONG_MIN) { - tmp->min--; - } else { - tmp->underflow = 1; - } - if (tmp->max > ZEND_LONG_MIN && !tmp->overflow) { - tmp->max--; - } - return 1; - } - } - break; - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - if (ssa_op->op1_def == var) { - /* If op1 is scalar, UNSET_DIM and UNSET_OBJ have no effect, so we can keep - * the previous ranges. */ - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - break; - case ZEND_ASSIGN: - if (ssa_op->op1_def == var || ssa_op->op2_def == var || ssa_op->result_def == var) { - if (OP2_HAS_RANGE()) { - tmp->min = OP2_MIN_RANGE(); - tmp->max = OP2_MAX_RANGE(); - tmp->underflow = OP2_RANGE_UNDERFLOW(); - tmp->overflow = OP2_RANGE_OVERFLOW(); - return 1; - } - } - break; - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - if ((ssa_op+1)->op1_def == var) { - opline++; - ssa_op++; - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - } - return 1; - } - break; - case ZEND_ASSIGN_OP: - if (opline->extended_value != ZEND_CONCAT - && opline->extended_value != ZEND_POW) { - if (ssa_op->op1_def == var || ssa_op->result_def == var) { - return zend_inference_calc_binary_op_range( - op_array, ssa, opline, ssa_op, - opline->extended_value, tmp); - } - } - break; - case ZEND_OP_DATA: - if (ssa_op->op1_def == var) { - if ((opline-1)->opcode == ZEND_ASSIGN_DIM || - (opline-1)->opcode == ZEND_ASSIGN_OBJ || - (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP || - (opline-1)->opcode == ZEND_ASSIGN_DIM_OP || - (opline-1)->opcode == ZEND_ASSIGN_OBJ_OP || - (opline-1)->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { - if (OP1_HAS_RANGE()) { - tmp->min = OP1_MIN_RANGE(); - tmp->max = OP1_MAX_RANGE(); - tmp->underflow = OP1_RANGE_UNDERFLOW(); - tmp->overflow = OP1_RANGE_OVERFLOW(); - return 1; - } - } - break; - } - break; - case ZEND_RECV: - case ZEND_RECV_INIT: - if (ssa_op->result_def == var) { - if (op_array->arg_info && - opline->op1.num <= op_array->num_args) { - zend_type type = op_array->arg_info[opline->op1.num-1].type; - uint32_t mask = ZEND_TYPE_PURE_MASK_WITHOUT_NULL(type); - if (mask == MAY_BE_LONG) { - tmp->underflow = 0; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 0; - return 1; - } else if (mask == MAY_BE_BOOL) { - tmp->underflow = 0; - tmp->min = 0; - tmp->max = 1; - tmp->overflow = 0; - return 1; - } - } - } - break; - case ZEND_STRLEN: - if (ssa_op->result_def == var) { -#if SIZEOF_ZEND_LONG == 4 - /* The length of a string is a non-negative integer. However, on 32-bit - * platforms overflows into negative lengths may occur, so it's better - * to not assume any particular range. */ - tmp->min = ZEND_LONG_MIN; -#else - tmp->min = 0; -#endif - tmp->max = ZEND_LONG_MAX; - return 1; - } - break; - case ZEND_FUNC_NUM_ARGS: - tmp->min = 0; - tmp->max = ZEND_LONG_MAX; - return 1; - case ZEND_COUNT: - /* count() on Countable objects may return negative numbers */ - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - return 1; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - if (ssa_op->result_def == var) { - zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - zend_call_info *call_info; - if (!func_info || !func_info->call_map) { - break; - } - - call_info = func_info->call_map[opline - op_array->opcodes]; - if (!call_info) { - break; - } - if (call_info->callee_func->type == ZEND_USER_FUNCTION) { - func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array); - if (func_info && func_info->return_info.has_range) { - *tmp = func_info->return_info.range; - return 1; - } - } -//TODO: we can't use type inference for internal functions at this point ??? -#if 0 - uint32_t type; - - type = zend_get_func_info(call_info, ssa); - if (!(type & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)))) { - tmp->underflow = 0; - tmp->min = 0; - tmp->max = 0; - tmp->overflow = 0; - if (type & MAY_BE_LONG) { - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - } else if (type & MAY_BE_TRUE) { - if (!(type & (MAY_BE_NULL|MAY_BE_FALSE))) { - tmp->min = 1; - } - tmp->max = 1; - } - return 1; - } -#endif - } - break; - // FIXME: support for more opcodes - default: - break; - } - return 0; -} - -void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow) -{ - if (underflow) { - min = ZEND_LONG_MIN; - } - if (overflow) { - max = ZEND_LONG_MAX; - } - ssa->var_info[var].has_range = 1; - ssa->var_info[var].range.underflow = underflow; - ssa->var_info[var].range.min = min; - ssa->var_info[var].range.max = max; - ssa->var_info[var].range.overflow = overflow; - LOG_SSA_RANGE(" change range (init SCC %2d) %2d [%s%ld..%ld%s]\n", ssa->vars[var].scc, var, (underflow?"-- ":""), min, max, (overflow?" ++":"")); -} - -int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r) -{ - if (!var_info->has_range) { - var_info->has_range = 1; - } else { - if (r->underflow || - var_info->range.underflow || - r->min < var_info->range.min) { - r->underflow = 1; - r->min = ZEND_LONG_MIN; - } - if (r->overflow || - var_info->range.overflow || - r->max > var_info->range.max) { - r->overflow = 1; - r->max = ZEND_LONG_MAX; - } - if (var_info->range.min == r->min && - var_info->range.max == r->max && - var_info->range.underflow == r->underflow && - var_info->range.overflow == r->overflow) { - return 0; - } - } - var_info->range = *r; - return 1; -} - -static int zend_ssa_range_widening(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc) -{ - zend_ssa_range tmp; - - if (zend_inference_calc_range(op_array, ssa, var, 1, 0, &tmp)) { - if (zend_inference_widening_meet(&ssa->var_info[var], &tmp)) { - LOG_SSA_RANGE(" change range (widening SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":"")); - return 1; - } - } - return 0; -} - -int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r) -{ - if (!var_info->has_range) { - var_info->has_range = 1; - } else { - if (!r->underflow && - !var_info->range.underflow && - var_info->range.min < r->min) { - r->min = var_info->range.min; - } - if (!r->overflow && - !var_info->range.overflow && - var_info->range.max > r->max) { - r->max = var_info->range.max; - } - if (r->underflow) { - r->min = ZEND_LONG_MIN; - } - if (r->overflow) { - r->max = ZEND_LONG_MAX; - } - if (var_info->range.min == r->min && - var_info->range.max == r->max && - var_info->range.underflow == r->underflow && - var_info->range.overflow == r->overflow) { - return 0; - } - } - var_info->range = *r; - return 1; -} - -static int zend_ssa_range_narrowing(const zend_op_array *op_array, zend_ssa *ssa, int var, int scc) -{ - zend_ssa_range tmp; - - if (zend_inference_calc_range(op_array, ssa, var, 0, 1, &tmp)) { - if (zend_inference_narrowing_meet(&ssa->var_info[var], &tmp)) { - LOG_SSA_RANGE(" change range (narrowing SCC %2d) %2d [%s%ld..%ld%s]\n", scc, var, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":"")); - return 1; - } - } - return 0; -} - -#ifdef NEG_RANGE -# define CHECK_INNER_CYCLE(var2) \ - do { \ - if (ssa->vars[var2].scc == ssa->vars[var].scc && \ - !ssa->vars[var2].scc_entry && \ - !zend_bitset_in(visited, var2) && \ - zend_check_inner_cycles(op_array, ssa, worklist, visited, var2)) { \ - return 1; \ - } \ - } while (0) - -static int zend_check_inner_cycles(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, zend_bitset visited, int var) -{ - if (zend_bitset_in(worklist, var)) { - return 1; - } - zend_bitset_incl(worklist, var); - FOR_EACH_VAR_USAGE(var, CHECK_INNER_CYCLE); - zend_bitset_incl(visited, var); - return 0; -} -#endif - -static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ssa, int *scc_var, int *next_scc_var, int scc) -{ - int worklist_len = zend_bitset_len(ssa->vars_count); - int j, n; - zend_ssa_range tmp; - ALLOCA_FLAG(use_heap) - zend_bitset worklist = do_alloca(sizeof(zend_ulong) * worklist_len * 2, use_heap); - zend_bitset visited = worklist + worklist_len; -#ifdef NEG_RANGE - int has_inner_cycles = 0; - - memset(worklist, 0, sizeof(zend_ulong) * worklist_len); - memset(visited, 0, sizeof(zend_ulong) * worklist_len); - j = scc_var[scc]; - while (j >= 0) { - if (!zend_bitset_in(visited, j) && - zend_check_inner_cycles(op_array, ssa, worklist, visited, j)) { - has_inner_cycles = 1; - break; - } - j = next_scc_var[j]; - } -#endif - - memset(worklist, 0, sizeof(zend_ulong) * worklist_len); - - for (n = 0; n < RANGE_WARMUP_PASSES; n++) { - j= scc_var[scc]; - while (j >= 0) { - if (ssa->vars[j].scc_entry) { - zend_bitset_incl(worklist, j); - } - j = next_scc_var[j]; - } - - memset(visited, 0, sizeof(zend_ulong) * worklist_len); - - WHILE_WORKLIST(worklist, worklist_len, j) { - if (zend_inference_calc_range(op_array, ssa, j, 0, 0, &tmp)) { -#ifdef NEG_RANGE - if (!has_inner_cycles && - ssa->var_info[j].has_range && - ssa->vars[j].definition_phi && - ssa->vars[j].definition_phi->pi >= 0 && - ssa->vars[j].definition_phi->has_range_constraint && - ssa->vars[j].definition_phi->constraint.range.negative && - ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 && - ssa->vars[j].definition_phi->constraint.range.max_ssa_var < 0) { - zend_ssa_range_constraint *constraint = - &ssa->vars[j].definition_phi->constraint.range; - if (tmp.min == ssa->var_info[j].range.min && - tmp.max == ssa->var_info[j].range.max) { - if (constraint->negative == NEG_INIT) { - LOG_NEG_RANGE("#%d INVARIANT\n", j); - constraint->negative = NEG_INVARIANT; - } - } else if (tmp.min == ssa->var_info[j].range.min && - tmp.max == ssa->var_info[j].range.max + 1 && - tmp.max < constraint->range.min) { - if (constraint->negative == NEG_INIT || - constraint->negative == NEG_INVARIANT) { - LOG_NEG_RANGE("#%d LT\n", j); - constraint->negative = NEG_USE_LT; -//???NEG - } else if (constraint->negative == NEG_USE_GT) { - LOG_NEG_RANGE("#%d UNKNOWN\n", j); - constraint->negative = NEG_UNKNOWN; - } - } else if (tmp.max == ssa->var_info[j].range.max && - tmp.min == ssa->var_info[j].range.min - 1 && - tmp.min > constraint->range.max) { - if (constraint->negative == NEG_INIT || - constraint->negative == NEG_INVARIANT) { - LOG_NEG_RANGE("#%d GT\n", j); - constraint->negative = NEG_USE_GT; -//???NEG - } else if (constraint->negative == NEG_USE_LT) { - LOG_NEG_RANGE("#%d UNKNOWN\n", j); - constraint->negative = NEG_UNKNOWN; - } - } else { - LOG_NEG_RANGE("#%d UNKNOWN\n", j); - constraint->negative = NEG_UNKNOWN; - } - } -#endif - if (zend_inference_narrowing_meet(&ssa->var_info[j], &tmp)) { - LOG_SSA_RANGE(" change range (warmup %2d SCC %2d) %2d [%s%ld..%ld%s]\n", n, scc, j, (tmp.underflow?"-- ":""), tmp.min, tmp.max, (tmp.overflow?" ++":"")); - zend_bitset_incl(visited, j); - FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR_1); - } - } - } WHILE_WORKLIST_END(); - } - free_alloca(worklist, use_heap); -} - -static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - int worklist_len = zend_bitset_len(ssa->vars_count); - zend_bitset worklist; - int *next_scc_var; - int *scc_var; - zend_ssa_phi *p; - zend_ssa_range tmp; - int scc, j; - ALLOCA_FLAG(use_heap); - - worklist = do_alloca( - ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len) + - ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count) + - sizeof(int) * ssa->sccs, use_heap); - next_scc_var = (int*)((char*)worklist + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ulong) * worklist_len)); - scc_var = (int*)((char*)next_scc_var + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->vars_count)); - - LOG_SSA_RANGE("Range Inference\n"); - - /* Create linked lists of SSA variables for each SCC */ - memset(scc_var, -1, sizeof(int) * ssa->sccs); - for (j = 0; j < ssa->vars_count; j++) { - if (ssa->vars[j].scc >= 0) { - next_scc_var[j] = scc_var[ssa->vars[j].scc]; - scc_var[ssa->vars[j].scc] = j; - } - } - - for (scc = 0; scc < ssa->sccs; scc++) { - j = scc_var[scc]; - if (next_scc_var[j] < 0) { - /* SCC with a single element */ - if (zend_inference_calc_range(op_array, ssa, j, 0, 1, &tmp)) { - zend_inference_init_range(op_array, ssa, j, tmp.underflow, tmp.min, tmp.max, tmp.overflow); - } else { - zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1); - } - } else { - /* Find SCC entry points */ - memset(worklist, 0, sizeof(zend_ulong) * worklist_len); - do { - if (ssa->vars[j].scc_entry) { - zend_bitset_incl(worklist, j); - } - j = next_scc_var[j]; - } while (j >= 0); - -#if RANGE_WARMUP_PASSES > 0 - zend_infer_ranges_warmup(op_array, ssa, scc_var, next_scc_var, scc); - j = scc_var[scc]; - do { - zend_bitset_incl(worklist, j); - j = next_scc_var[j]; - } while (j >= 0); -#endif - - /* widening */ - WHILE_WORKLIST(worklist, worklist_len, j) { - if (zend_ssa_range_widening(op_array, ssa, j, scc)) { - FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR); - } - } WHILE_WORKLIST_END(); - - /* initialize missing ranges */ - for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) { - if (!ssa->var_info[j].has_range) { - zend_inference_init_range(op_array, ssa, j, 1, ZEND_LONG_MIN, ZEND_LONG_MAX, 1); - FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR); - } - } - - /* widening (second round) */ - WHILE_WORKLIST(worklist, worklist_len, j) { - if (zend_ssa_range_widening(op_array, ssa, j, scc)) { - FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR); - } - } WHILE_WORKLIST_END(); - - /* Add all SCC entry variables into worklist for narrowing */ - for (j = scc_var[scc]; j >= 0; j = next_scc_var[j]) { - if (ssa->vars[j].definition_phi - && ssa->vars[j].definition_phi->pi < 0) { - /* narrowing Phi functions first */ - zend_ssa_range_narrowing(op_array, ssa, j, scc); - } - zend_bitset_incl(worklist, j); - } - - /* narrowing */ - WHILE_WORKLIST(worklist, worklist_len, j) { - if (zend_ssa_range_narrowing(op_array, ssa, j, scc)) { - FOR_EACH_VAR_USAGE(j, ADD_SCC_VAR); -#ifdef SYM_RANGE - /* Process symbolic control-flow constraints */ - p = ssa->vars[j].sym_use_chain; - while (p) { - ADD_SCC_VAR(p->ssa_var); - p = p->sym_use_chain; - } -#endif - } - } WHILE_WORKLIST_END(); - } - } - - free_alloca(worklist, use_heap); - - return SUCCESS; -} -/* }}} */ - -static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) { - if (alias == HTTP_RESPONSE_HEADER_ALIAS) { - return MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_RC1 | MAY_BE_RCN; - } else { - return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } -} - -#define UPDATE_SSA_TYPE(_type, _var) \ - do { \ - uint32_t __type = (_type) & ~MAY_BE_GUARD; \ - int __var = (_var); \ - if (__type & MAY_BE_REF) { \ - __type |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; \ - } \ - if (__var >= 0) { \ - zend_ssa_var *__ssa_var = &ssa_vars[__var]; \ - if (__ssa_var->var < op_array->last_var) { \ - if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \ - __type |= MAY_BE_RC1 | MAY_BE_RCN; \ - } \ - if ((__type & MAY_BE_RC1) && (__type & MAY_BE_STRING)) {\ - /* TODO: support for array keys and ($str . "")*/ \ - __type |= MAY_BE_RCN; \ - } \ - if (__ssa_var->alias) { \ - __type |= get_ssa_alias_types(__ssa_var->alias); \ - } \ - } \ - if (ssa_var_info[__var].type != __type) { \ - if (ssa_var_info[__var].type & ~__type) { \ - emit_type_narrowing_warning(op_array, ssa, __var); \ - return FAILURE; \ - } \ - ssa_var_info[__var].type = __type; \ - if (update_worklist) { \ - add_usages(op_array, ssa, worklist, __var); \ - } \ - } \ - /*zend_bitset_excl(worklist, var);*/ \ - } \ - } while (0) - -#define UPDATE_SSA_OBJ_TYPE(_ce, _is_instanceof, var) \ - do { \ - if (var >= 0) { \ - if (ssa_var_info[var].ce != (_ce) || \ - ssa_var_info[var].is_instanceof != (_is_instanceof)) { \ - ssa_var_info[var].ce = (_ce); \ - ssa_var_info[var].is_instanceof = (_is_instanceof); \ - if (update_worklist) { \ - add_usages(op_array, ssa, worklist, var); \ - } \ - } \ - /*zend_bitset_excl(worklist, var);*/ \ - } \ - } while (0) - -#define COPY_SSA_OBJ_TYPE(from_var, to_var) do { \ - if ((from_var) >= 0 && (ssa_var_info[(from_var)].type & MAY_BE_OBJECT) \ - && ssa_var_info[(from_var)].ce) { \ - UPDATE_SSA_OBJ_TYPE(ssa_var_info[(from_var)].ce, \ - ssa_var_info[(from_var)].is_instanceof, (to_var)); \ - } else { \ - UPDATE_SSA_OBJ_TYPE(NULL, 0, (to_var)); \ - } \ -} while (0) - -static void add_usages(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset worklist, int var) -{ - if (ssa->vars[var].phi_use_chain) { - zend_ssa_phi *p = ssa->vars[var].phi_use_chain; - do { - zend_bitset_incl(worklist, p->ssa_var); - p = zend_ssa_next_use_phi(ssa, var, p); - } while (p); - } - if (ssa->vars[var].use_chain >= 0) { - int use = ssa->vars[var].use_chain; - zend_ssa_op *op; - - do { - op = ssa->ops + use; - if (op->result_def >= 0) { - zend_bitset_incl(worklist, op->result_def); - } - if (op->op1_def >= 0) { - zend_bitset_incl(worklist, op->op1_def); - } - if (op->op2_def >= 0) { - zend_bitset_incl(worklist, op->op2_def); - } - if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { - op--; - if (op->result_def >= 0) { - zend_bitset_incl(worklist, op->result_def); - } - if (op->op1_def >= 0) { - zend_bitset_incl(worklist, op->op1_def); - } - if (op->op2_def >= 0) { - zend_bitset_incl(worklist, op->op2_def); - } - } - use = zend_ssa_next_use(ssa->ops, var, use); - } while (use >= 0); - } -} - -static void emit_type_narrowing_warning(const zend_op_array *op_array, zend_ssa *ssa, int var) -{ - int def_op_num = ssa->vars[var].definition; - const zend_op *def_opline = def_op_num >= 0 ? &op_array->opcodes[def_op_num] : NULL; - const char *def_op_name = def_opline ? zend_get_opcode_name(def_opline->opcode) : "PHI"; - zend_error(E_WARNING, "Narrowing occurred during type inference of %s. Please file a bug report on bugs.php.net", def_op_name); -} - -uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert) -{ - uint32_t tmp = 0; - - if (t1 & MAY_BE_OBJECT) { - if (!write) { - /* can't be REF because of ZVAL_COPY_DEREF() usage */ - tmp |= MAY_BE_ANY | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } else { - tmp |= MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - if (write) { - tmp |= MAY_BE_INDIRECT; - } - } - if (t1 & MAY_BE_ARRAY) { - if (insert) { - tmp |= MAY_BE_NULL; - } else { - tmp |= MAY_BE_NULL | ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT); - if (tmp & MAY_BE_ARRAY) { - tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - if (!write) { - /* can't be REF because of ZVAL_COPY_DEREF() usage */ - tmp |= MAY_BE_RCN; - if ((op_type & (IS_VAR|IS_TMP_VAR)) && (t1 & MAY_BE_RC1)) { - tmp |= MAY_BE_RC1; - } - } else if (t1 & MAY_BE_ARRAY_OF_REF) { - tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN; - } else { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - } - } - if (write) { - tmp |= MAY_BE_INDIRECT; - } - } - if (t1 & MAY_BE_STRING) { - tmp |= MAY_BE_STRING | MAY_BE_RC1; - if (write) { - tmp |= MAY_BE_NULL; - } - } - if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { - tmp |= MAY_BE_NULL; - if (write) { - tmp |= MAY_BE_INDIRECT; - } - } - if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) { - if (!write) { - tmp |= MAY_BE_NULL; - } - } - return tmp; -} - -static uint32_t assign_dim_result_type( - uint32_t arr_type, uint32_t dim_type, uint32_t value_type, zend_uchar dim_op_type) { - uint32_t tmp = arr_type & ~(MAY_BE_RC1|MAY_BE_RCN); - - if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { - tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE); - tmp |= MAY_BE_ARRAY|MAY_BE_RC1; - } - if (tmp & (MAY_BE_ARRAY|MAY_BE_STRING)) { - tmp |= MAY_BE_RC1; - } - if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - if (tmp & MAY_BE_ARRAY) { - /* Only add key type if we have a value type. We want to maintain the invariant that a - * key type exists iff a value type exists even in dead code that may use empty types. */ - if (value_type & (MAY_BE_ANY|MAY_BE_UNDEF)) { - if (value_type & MAY_BE_UNDEF) { - tmp |= MAY_BE_ARRAY_OF_NULL; - } - if (dim_op_type == IS_UNUSED) { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } else { - if (dim_type & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - if (dim_type & MAY_BE_STRING) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - if (dim_op_type != IS_CONST) { - // FIXME: numeric string - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - } - if (dim_type & (MAY_BE_UNDEF|MAY_BE_NULL)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - } - } - } - /* Only add value type if we have a key type. It might be that the key type is illegal - * for arrays. */ - if (tmp & MAY_BE_ARRAY_KEY_ANY) { - tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT; - } - } - return tmp; -} - -/* For binary ops that have compound assignment operators */ -static uint32_t binary_op_result_type( - zend_ssa *ssa, zend_uchar opcode, uint32_t t1, uint32_t t2, int result_var, - zend_long optimization_level) { - uint32_t tmp = 0; - uint32_t t1_type = (t1 & MAY_BE_ANY) | (t1 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); - uint32_t t2_type = (t2 & MAY_BE_ANY) | (t2 & MAY_BE_UNDEF ? MAY_BE_NULL : 0); - - if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) { - /* Handle potentially overloaded operators. - * This could be made more precise by checking the class type, if known. */ - if ((t1_type & MAY_BE_OBJECT) || (t2_type & MAY_BE_OBJECT)) { - /* This is somewhat GMP specific. */ - tmp |= MAY_BE_OBJECT | MAY_BE_FALSE | MAY_BE_RC1; - } - } - - switch (opcode) { - case ZEND_ADD: - if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) { - if (result_var < 0 || - !ssa->var_info[result_var].has_range || - ssa->var_info[result_var].range.underflow || - ssa->var_info[result_var].range.overflow) { - /* may overflow */ - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG; - } - } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { - tmp |= MAY_BE_DOUBLE; - } else if (t1_type == MAY_BE_ARRAY && t2_type == MAY_BE_ARRAY) { - tmp |= MAY_BE_ARRAY | MAY_BE_RC1; - tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); - tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); - } else { - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - if ((t1_type & MAY_BE_ARRAY) && (t2_type & MAY_BE_ARRAY)) { - tmp |= MAY_BE_ARRAY | MAY_BE_RC1; - tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); - tmp |= t2 & (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); - } - } - break; - case ZEND_SUB: - case ZEND_MUL: - if (t1_type == MAY_BE_LONG && t2_type == MAY_BE_LONG) { - if (result_var < 0 || - !ssa->var_info[result_var].has_range || - ssa->var_info[result_var].range.underflow || - ssa->var_info[result_var].range.overflow) { - /* may overflow */ - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG; - } - } else if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { - tmp |= MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } - break; - case ZEND_DIV: - case ZEND_POW: - if (t1_type == MAY_BE_DOUBLE || t2_type == MAY_BE_DOUBLE) { - tmp |= MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } - /* Division by zero results in Inf/-Inf/Nan (double), so it doesn't need any special - * handling */ - break; - case ZEND_MOD: - tmp |= MAY_BE_LONG; - /* Division by zero results in an exception, so it doesn't need any special handling */ - break; - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - if ((t1_type & MAY_BE_STRING) && (t2_type & MAY_BE_STRING)) { - tmp |= MAY_BE_STRING | MAY_BE_RC1; - } - if ((t1_type & ~MAY_BE_STRING) || (t2_type & ~MAY_BE_STRING)) { - tmp |= MAY_BE_LONG; - } - break; - case ZEND_SL: - case ZEND_SR: - tmp |= MAY_BE_LONG; - break; - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - /* TODO: +MAY_BE_OBJECT ??? */ - tmp = MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN; - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - return tmp; -} - -static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) { - zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; - if (ce) { - return ce; - } - - ce = zend_hash_find_ptr(CG(class_table), lcname); - if (ce && ce->type == ZEND_INTERNAL_CLASS) { - return ce; - } - - return NULL; -} - -static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) { - uint32_t result_mask = type_mask & MAY_BE_ANY; - if (type_mask & MAY_BE_VOID) { - result_mask |= MAY_BE_NULL; - } - if (type_mask & MAY_BE_CALLABLE) { - result_mask |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } - if (type_mask & MAY_BE_ITERABLE) { - result_mask |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } - if (type_mask & MAY_BE_STATIC) { - result_mask |= MAY_BE_OBJECT; - } - if (type_mask & MAY_BE_ARRAY) { - result_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } - return result_mask; -} - -uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) -{ - uint32_t tmp; - - *pce = NULL; - if (!ZEND_TYPE_IS_SET(arg_info->type)) { - return MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_RC1|MAY_BE_RCN; - } - - tmp = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(arg_info->type)); - if (ZEND_TYPE_HAS_CLASS(arg_info->type)) { - tmp |= MAY_BE_OBJECT; - /* As we only have space to store one CE, we use a plain object type for class unions. */ - if (ZEND_TYPE_HAS_NAME(arg_info->type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(arg_info->type)); - *pce = get_class_entry(script, lcname); - zend_string_release_ex(lcname, 0); - } - } - if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - return tmp; -} - -static zend_property_info *lookup_prop_info(zend_class_entry *ce, zend_string *name, zend_class_entry *scope) { - zend_property_info *prop_info; - - /* If the class is linked, reuse the precise runtime logic. */ - if ((ce->ce_flags & ZEND_ACC_LINKED) - && (!scope || (scope->ce_flags & ZEND_ACC_LINKED))) { - zend_class_entry *prev_scope = EG(fake_scope); - EG(fake_scope) = scope; - prop_info = zend_get_property_info(ce, name, 1); - EG(fake_scope) = prev_scope; - if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) { - return prop_info; - } - return NULL; - } - - /* Otherwise, handle only some safe cases */ - prop_info = zend_hash_find_ptr(&ce->properties_info, name); - if (prop_info && - ((prop_info->ce == scope) || - (!scope && (prop_info->flags & ZEND_ACC_PUBLIC))) - ) { - return prop_info; - } - return NULL; -} - -static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) -{ - zend_property_info *prop_info = NULL; - if (opline->op2_type == IS_CONST) { - zend_class_entry *ce = NULL; - - if (opline->op1_type == IS_UNUSED) { - ce = op_array->scope; - } else if (ssa_op->op1_use >= 0) { - ce = ssa->var_info[ssa_op->op1_use].ce; - } - if (ce) { - prop_info = lookup_prop_info(ce, - Z_STR_P(CRT_CONSTANT(opline->op2)), - op_array->scope); - if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) { - prop_info = NULL; - } - } - } - return prop_info; -} - -static zend_property_info *zend_fetch_static_prop_info(const zend_script *script, const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline) -{ - zend_property_info *prop_info = NULL; - if (opline->op1_type == IS_CONST) { - zend_class_entry *ce = NULL; - if (opline->op2_type == IS_UNUSED) { - int fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK; - switch (fetch_type) { - case ZEND_FETCH_CLASS_SELF: - case ZEND_FETCH_CLASS_STATIC: - /* We enforce that static property types cannot change during inheritance, so - * handling static the same way as self here is legal. */ - ce = op_array->scope; - break; - case ZEND_FETCH_CLASS_PARENT: - if (op_array->scope && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - ce = op_array->scope->parent; - } - break; - } - } else if (opline->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT(opline->op2); - ce = get_class_entry(script, Z_STR_P(zv + 1)); - } - - if (ce) { - zval *zv = CRT_CONSTANT(opline->op1); - prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope); - if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) { - prop_info = NULL; - } - } - } - return prop_info; -} - -static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_info *prop_info, zend_class_entry **pce) -{ - if (pce) { - *pce = NULL; - } - if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { - uint32_t type = zend_convert_type_declaration_mask(ZEND_TYPE_PURE_MASK(prop_info->type)); - - if (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - type |= MAY_BE_RC1 | MAY_BE_RCN; - } - if (ZEND_TYPE_HAS_CLASS(prop_info->type)) { - type |= MAY_BE_OBJECT; - if (pce) { - if (ZEND_TYPE_HAS_CE(prop_info->type)) { - *pce = ZEND_TYPE_CE(prop_info->type); - } else if (ZEND_TYPE_HAS_NAME(prop_info->type)) { - zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(prop_info->type)); - *pce = get_class_entry(script, lcname); - zend_string_release(lcname); - } - } - } - return type; - } - return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN; -} - -static zend_always_inline int _zend_update_type_info( - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_script *script, - zend_bitset worklist, - zend_op *opline, - zend_ssa_op *ssa_op, - const zend_op **ssa_opcodes, - zend_long optimization_level, - bool update_worklist) -{ - uint32_t t1, t2; - uint32_t tmp, orig; - zend_ssa_var *ssa_vars = ssa->vars; - zend_ssa_var_info *ssa_var_info = ssa->var_info; - zend_class_entry *ce; - int j; - - if (opline->opcode == ZEND_OP_DATA) { - opline--; - ssa_op--; - } - - t1 = OP1_INFO(); - t2 = OP2_INFO(); - - /* If one of the operands cannot have any type, this means the operand derives from - * unreachable code. Propagate the empty result early, so that that the following - * code may assume that operands have at least one type. */ - if (!(t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS)) - || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))) { - tmp = 0; - if (ssa_op->result_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - if (ssa_op->op1_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - if (ssa_op->op2_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->op2_def); - } - return 1; - } - - switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_POW: - case ZEND_MOD: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - case ZEND_SL: - case ZEND_SR: - case ZEND_CONCAT: - tmp = binary_op_result_type(ssa, opline->opcode, t1, t2, ssa_op->result_def, optimization_level); - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_BW_NOT: - tmp = 0; - if (t1 & MAY_BE_STRING) { - tmp |= MAY_BE_STRING | MAY_BE_RC1; - } - if (t1 & (MAY_BE_ANY-MAY_BE_STRING)) { - tmp |= MAY_BE_LONG; - } - if (!(ZEND_OPTIMIZER_IGNORE_OVERLOADING & optimization_level)) { - if (t1 & MAY_BE_OBJECT) { - /* Potentially overloaded operator. */ - tmp |= MAY_BE_OBJECT | MAY_BE_RC1; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_BEGIN_SILENCE: - UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); - break; - case ZEND_BOOL_NOT: - case ZEND_BOOL_XOR: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_INSTANCEOF: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_BOOL: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_ASSERT_CHECK: - case ZEND_IN_ARRAY: - case ZEND_ARRAY_KEY_EXISTS: - UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def); - break; - case ZEND_CAST: - if (ssa_op->op1_def >= 0) { - tmp = t1; - if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) && - (opline->extended_value == IS_ARRAY || - opline->extended_value == IS_OBJECT)) { - tmp |= MAY_BE_RCN; - } else if ((t1 & MAY_BE_STRING) && - opline->extended_value == IS_STRING) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - tmp = 1 << opline->extended_value; - if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - if ((tmp & MAY_BE_ANY) == (t1 & MAY_BE_ANY)) { - tmp |= (t1 & MAY_BE_RC1) | MAY_BE_RCN; - } else if ((opline->extended_value == IS_ARRAY || - opline->extended_value == IS_OBJECT) && - (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT))) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else if (opline->extended_value == IS_STRING && - (t1 & (MAY_BE_STRING|MAY_BE_OBJECT))) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else { - tmp |= MAY_BE_RC1; - } - } - if (opline->extended_value == IS_ARRAY) { - if (t1 & MAY_BE_ARRAY) { - tmp |= t1 & (MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF); - } - if (t1 & MAY_BE_OBJECT) { - tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } else { - tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY) ? MAY_BE_ARRAY_PACKED : 0); - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_QM_ASSIGN: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_COPY_TMP: - if (ssa_op->op1_def >= 0) { - tmp = t1; - if (t1 & (MAY_BE_RC1|MAY_BE_REF)) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - tmp = t1 & ~(MAY_BE_UNDEF|MAY_BE_REF); - if (t1 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= (t1 & (MAY_BE_RC1|MAY_BE_RCN)); - if (opline->op1_type == IS_CV) { - tmp |= MAY_BE_RCN; - } - } - if (opline->opcode != ZEND_QM_ASSIGN) { - /* COALESCE and JMP_SET result can't be null */ - tmp &= ~MAY_BE_NULL; - if (opline->opcode == ZEND_JMP_SET) { - /* JMP_SET result can't be false either */ - tmp &= ~MAY_BE_FALSE; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def); - break; - case ZEND_JMP_NULL: - if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) { - tmp = MAY_BE_NULL; - } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { - tmp = MAY_BE_FALSE; - } else { - ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); - tmp = MAY_BE_TRUE; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_STATIC_PROP_OP: - { - zend_property_info *prop_info = NULL; - orig = 0; - tmp = 0; - if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { - prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op); - orig = t1; - t1 = zend_fetch_prop_type(script, prop_info, &ce); - t2 = OP1_DATA_INFO(); - } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) { - if (t1 & MAY_BE_ARRAY_OF_REF) { - tmp |= MAY_BE_REF; - } - orig = t1; - t1 = zend_array_element_type(t1, opline->op1_type, 1, 0); - t2 = OP1_DATA_INFO(); - } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { - prop_info = zend_fetch_static_prop_info(script, op_array, ssa, opline); - t1 = zend_fetch_prop_type(script, prop_info, &ce); - t2 = OP1_DATA_INFO(); - } else { - if (t1 & MAY_BE_REF) { - tmp |= MAY_BE_REF; - } - } - - tmp |= binary_op_result_type( - ssa, opline->extended_value, t1, t2, - opline->opcode == ZEND_ASSIGN_OP ? ssa_op->op1_def : -1, optimization_level); - if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) { - tmp |= MAY_BE_RC1; - } - if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - - if (opline->opcode == ZEND_ASSIGN_DIM_OP) { - if (opline->op1_type == IS_CV) { - orig = assign_dim_result_type(orig, OP2_INFO(), tmp, opline->op2_type); - UPDATE_SSA_TYPE(orig, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { - if (opline->op1_type == IS_CV) { - orig = (orig & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN; - UPDATE_SSA_TYPE(orig, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP) { - /* Nothing to do */ - } else { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - ce = NULL; - if (opline->opcode == ZEND_ASSIGN_DIM_OP) { - if (opline->op2_type == IS_UNUSED) { - /* When appending to an array and the LONG_MAX key is already used - * null will be returned. */ - tmp |= MAY_BE_NULL; - } - if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) { - /* Arrays and objects cannot be used as keys. */ - tmp |= MAY_BE_NULL; - } - if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) { - /* null and false are implicitly converted to array, anything else - * results in a null return value. */ - tmp |= MAY_BE_NULL; - } - } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) { - /* The return value must also satisfy the property type */ - if (prop_info) { - tmp &= zend_fetch_prop_type(script, prop_info, NULL); - } - } else if (opline->opcode == ZEND_ASSIGN_STATIC_PROP_OP) { - /* The return value must also satisfy the property type */ - if (prop_info) { - tmp &= zend_fetch_prop_type(script, prop_info, NULL); - } - } - tmp &= ~MAY_BE_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } - } - break; - } - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - tmp = 0; - if (t1 & MAY_BE_REF) { - tmp |= MAY_BE_REF; - } - if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RC1; - if (ssa_op->result_def >= 0) { - tmp |= MAY_BE_RCN; - } - } - if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { - if (!ssa_var_info[ssa_op->op1_use].has_range || - (opline->opcode == ZEND_PRE_DEC && - (ssa_var_info[ssa_op->op1_use].range.underflow || - ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) || - (opline->opcode == ZEND_PRE_INC && - (ssa_var_info[ssa_op->op1_use].range.overflow || - ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) { - /* may overflow */ - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG; - } - } else { - if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - if (opline->opcode == ZEND_PRE_INC) { - tmp |= MAY_BE_LONG; - } else { - tmp |= MAY_BE_NULL; - } - } - if (t1 & MAY_BE_LONG) { - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } - if (t1 & MAY_BE_DOUBLE) { - tmp |= MAY_BE_DOUBLE; - } - if (t1 & MAY_BE_STRING) { - tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE; - } - tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY); - } - if (ssa_op->op1_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_POST_INC: - case ZEND_POST_DEC: - if (ssa_op->result_def >= 0) { - tmp = 0; - if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RC1|MAY_BE_RCN; - } - tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN); - if (t1 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - tmp = 0; - if (t1 & MAY_BE_REF) { - tmp |= MAY_BE_REF; - } - if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RC1; - } - if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { - if (!ssa_var_info[ssa_op->op1_use].has_range || - (opline->opcode == ZEND_POST_DEC && - (ssa_var_info[ssa_op->op1_use].range.underflow || - ssa_var_info[ssa_op->op1_use].range.min == ZEND_LONG_MIN)) || - (opline->opcode == ZEND_POST_INC && - (ssa_var_info[ssa_op->op1_use].range.overflow || - ssa_var_info[ssa_op->op1_use].range.max == ZEND_LONG_MAX))) { - /* may overflow */ - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } else { - tmp |= MAY_BE_LONG; - } - } else { - if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - if (opline->opcode == ZEND_POST_INC) { - tmp |= MAY_BE_LONG; - } else { - tmp |= MAY_BE_NULL; - } - } - if (t1 & MAY_BE_LONG) { - tmp |= MAY_BE_LONG | MAY_BE_DOUBLE; - } - if (t1 & MAY_BE_DOUBLE) { - tmp |= MAY_BE_DOUBLE; - } - if (t1 & MAY_BE_STRING) { - tmp |= MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE; - } - tmp |= t1 & (MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_KEY_ANY); - } - if (ssa_op->op1_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_ASSIGN_DIM: - if (opline->op1_type == IS_CV) { - tmp = assign_dim_result_type(t1, t2, OP1_DATA_INFO(), opline->op2_type); - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - tmp = 0; - if (t1 & MAY_BE_STRING) { - tmp |= MAY_BE_STRING; - } - if (t1 & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_STRING)) { - tmp |= (OP1_DATA_INFO() & (MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)); - - if (opline->op2_type == IS_UNUSED) { - /* When appending to an array and the LONG_MAX key is already used - * null will be returned. */ - tmp |= MAY_BE_NULL; - } - if (t2 & (MAY_BE_ARRAY | MAY_BE_OBJECT)) { - /* Arrays and objects cannot be used as keys. */ - tmp |= MAY_BE_NULL; - } - if (t1 & (MAY_BE_ANY - (MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY))) { - /* undef, null and false are implicitly converted to array, anything else - * results in a null return value. */ - tmp |= MAY_BE_NULL; - } - } - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - if (t1 & MAY_BE_OBJECT) { - tmp |= MAY_BE_REF; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - if ((ssa_op+1)->op1_def >= 0) { - opline++; - ssa_op++; - tmp = OP1_INFO(); - if (tmp & (MAY_BE_ANY | MAY_BE_REF)) { - if (tmp & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_ASSIGN_OBJ: - if (opline->op1_type == IS_CV) { - tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - // TODO: If there is no __set we might do better - tmp = zend_fetch_prop_type(script, - zend_fetch_prop_info(op_array, ssa, opline, ssa_op), &ce); - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } - } - if ((ssa_op+1)->op1_def >= 0) { - opline++; - ssa_op++; - tmp = OP1_INFO(); - if (tmp & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_ASSIGN_STATIC_PROP: - if (ssa_op->result_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN; - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - if ((ssa_op+1)->op1_def >= 0) { - opline++; - ssa_op++; - tmp = OP1_INFO(); - if (tmp & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - if (opline->op1_type == IS_CV) { - tmp = (t1 & (MAY_BE_REF|MAY_BE_OBJECT))|MAY_BE_RC1|MAY_BE_RCN; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - // TODO: ??? - tmp = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_ASSIGN: - if (ssa_op->op2_def >= 0) { - tmp = t2; - if (tmp & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op2_def); - } - tmp = t2 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN); - if (t2 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - if (t1 & MAY_BE_REF) { - tmp |= MAY_BE_REF; - } - if (t2 & MAY_BE_REF) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { - tmp |= t2 & (MAY_BE_RC1|MAY_BE_RCN); - } else if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RCN; - } - if (RETURN_VALUE_USED(opline) && (tmp & MAY_BE_RC1)) { - tmp |= MAY_BE_RCN; - } - if (ssa_op->op1_def >= 0) { - if (ssa_var_info[ssa_op->op1_def].use_as_double) { - tmp &= ~MAY_BE_LONG; - tmp |= MAY_BE_DOUBLE; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - UPDATE_SSA_TYPE(tmp & ~MAY_BE_REF, ssa_op->result_def); - COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def); - } - break; - case ZEND_ASSIGN_REF: -// TODO: ??? - if (opline->op2_type == IS_CV) { - tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - if (t2 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op2_def); - } - if (opline->op2_type == IS_VAR && opline->extended_value == ZEND_RETURNS_FUNCTION) { - tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF; - } else { - tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - } - if (t2 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - if (ssa_op->result_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_ASSIGN_OBJ_REF: - if (opline->op1_type == IS_CV) { - tmp = t1; - if (tmp & MAY_BE_OBJECT) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - - t2 = OP1_DATA_INFO(); - if ((opline+1)->op1_type == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION)) { - tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF; - } else { - tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - } - if (t2 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - if (ssa_op->result_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - if ((opline+1)->op1_type == IS_CV) { - opline++; - ssa_op++; - tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - if (t2 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_ASSIGN_STATIC_PROP_REF: - if ((opline+1)->op1_type == IS_CV) { - opline++; - ssa_op++; - UPDATE_SSA_TYPE(MAY_BE_REF, ssa_op->op1_def); - } - break; - case ZEND_BIND_GLOBAL: - tmp = MAY_BE_REF | MAY_BE_ANY - | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - break; - case ZEND_BIND_STATIC: - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF - | ((opline->extended_value & ZEND_BIND_REF) ? MAY_BE_REF : (MAY_BE_RC1 | MAY_BE_RCN)); - if (opline->extended_value & ZEND_BIND_IMPLICIT) { - tmp |= MAY_BE_UNDEF; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - break; - case ZEND_SEND_VAR: - if (ssa_op->op1_def >= 0) { - tmp = t1; - if (t1 & (MAY_BE_RC1|MAY_BE_REF)) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - break; - case ZEND_BIND_LEXICAL: - if (ssa_op->op2_def >= 0) { - if (opline->extended_value & ZEND_BIND_REF) { - tmp = t2 | MAY_BE_REF; - } else { - tmp = t2 & ~(MAY_BE_RC1|MAY_BE_RCN); - if (t2 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RCN; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->op2_def); - COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->op2_def); - } - break; - case ZEND_YIELD: - if (ssa_op->op1_def >= 0) { - if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) { - tmp = t1 | MAY_BE_REF; - } else { - tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN); - if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { - tmp |= MAY_BE_RCN; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF - | MAY_BE_RC1 | MAY_BE_RCN; - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - if (ssa_op->op1_def >= 0) { - tmp = (t1 & MAY_BE_UNDEF)|MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_SEND_REF: - if (ssa_op->op1_def >= 0) { - tmp = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_SEND_UNPACK: - if (ssa_op->op1_def >= 0) { - tmp = t1; - if (t1 & MAY_BE_ARRAY) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - if (t1 & MAY_BE_ARRAY_OF_ANY) { - /* SEND_UNPACK may acquire references into the array */ - tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - } - if (t1 & MAY_BE_OBJECT) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_FAST_CONCAT: - case ZEND_ROPE_INIT: - case ZEND_ROPE_ADD: - case ZEND_ROPE_END: - UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def); - break; - case ZEND_RECV: - case ZEND_RECV_INIT: - case ZEND_RECV_VARIADIC: - { - /* Typehinting */ - zend_arg_info *arg_info = &op_array->arg_info[opline->op1.num-1]; - - ce = NULL; - tmp = zend_fetch_arg_info_type(script, arg_info, &ce); - if (ZEND_ARG_SEND_MODE(arg_info)) { - tmp |= MAY_BE_REF; - } - - if (opline->opcode == ZEND_RECV_VARIADIC) { - uint32_t elem_type = tmp & MAY_BE_REF - ? MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF - : (tmp & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT; - tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|elem_type; - ce = NULL; - } - - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - break; - } - case ZEND_DECLARE_ANON_CLASS: - UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def); - if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT(opline->op1)))) != NULL) { - UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def); - } - break; - case ZEND_FETCH_CLASS: - UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_op->result_def); - if (opline->op2_type == IS_UNUSED) { - switch (opline->op1.num & ZEND_FETCH_CLASS_MASK) { - case ZEND_FETCH_CLASS_SELF: - if (op_array->scope) { - UPDATE_SSA_OBJ_TYPE(op_array->scope, 0, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - break; - case ZEND_FETCH_CLASS_PARENT: - if (op_array->scope && op_array->scope->parent && (op_array->scope->ce_flags & ZEND_ACC_LINKED)) { - UPDATE_SSA_OBJ_TYPE(op_array->scope->parent, 0, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - break; - case ZEND_FETCH_CLASS_STATIC: - default: - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - break; - } - } else if (opline->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT(opline->op2); - if (Z_TYPE_P(zv) == IS_STRING) { - ce = get_class_entry(script, Z_STR_P(zv+1)); - UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - } else { - COPY_SSA_OBJ_TYPE(ssa_op->op2_use, ssa_op->result_def); - } - break; - case ZEND_NEW: - tmp = MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT; - if (opline->op1_type == IS_CONST && - (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1))) != NULL) { - UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_op->result_def); - } else if ((t1 & MAY_BE_CLASS) && ssa_op->op1_use >= 0 && ssa_var_info[ssa_op->op1_use].ce) { - UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_op->op1_use].ce, ssa_var_info[ssa_op->op1_use].is_instanceof, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_CLONE: - UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def); - break; - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - if (ssa_op->op1_def >= 0) { - if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { - tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - if (t1 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - } else if ((t1 & (MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN)) == MAY_BE_REF) { - tmp = (MAY_BE_REF | t1) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); - if (t1 & MAY_BE_UNDEF) { - tmp |= MAY_BE_NULL; - } - } else if (t1 & MAY_BE_REF) { - tmp = (MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | t1); - } else { - tmp = t1; - if (t1 & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - tmp = MAY_BE_RC1|MAY_BE_ARRAY; - if (ssa_op->result_use >= 0) { - tmp |= ssa_var_info[ssa_op->result_use].type; - } - if (opline->op1_type != IS_UNUSED) { - tmp |= (t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT; - if (t1 & MAY_BE_UNDEF) { - tmp |= MAY_BE_ARRAY_OF_NULL; - } - if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { - tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } - if (opline->op2_type == IS_UNUSED) { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } else { - if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - if (t2 & (MAY_BE_STRING)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - if (opline->op2_type != IS_CONST) { - // FIXME: numeric string - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - } - if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - } - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_ADD_ARRAY_UNPACK: - tmp = ssa_var_info[ssa_op->result_use].type; - ZEND_ASSERT(tmp & MAY_BE_ARRAY); - /* Ignore string keys as they will throw. */ - if (t1 & MAY_BE_ARRAY_KEY_LONG) { - tmp |= MAY_BE_ARRAY_KEY_LONG | (t1 & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)); - } - if (t1 & MAY_BE_OBJECT) { - tmp |= MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_UNSET_CV: - tmp = MAY_BE_UNDEF; - if (!op_array->function_name) { - /* In global scope, we know nothing */ - tmp |= MAY_BE_REF; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - break; - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - if (ssa_op->op1_def >= 0) { - UPDATE_SSA_TYPE(t1, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - break; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - if (ssa_op->op1_def >= 0) { - tmp = t1; - if (opline->opcode == ZEND_FE_RESET_RW) { - tmp |= MAY_BE_REF; - } else if (t1 & MAY_BE_RC1) { - tmp |= MAY_BE_RCN; - } - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - if (opline->opcode == ZEND_FE_RESET_RW) { -//??? - tmp = MAY_BE_REF | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT)); - } else { - tmp = MAY_BE_RC1 | MAY_BE_RCN | (t1 & (MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF)); - } - /* The result is set to UNDEF for invalid foreach inputs. */ - if ((t1 & (MAY_BE_ANY | MAY_BE_UNDEF)) & ~(MAY_BE_ARRAY | MAY_BE_OBJECT)) { - tmp |= MAY_BE_UNDEF; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def); - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - tmp = t2 & MAY_BE_REF; - if (t1 & MAY_BE_OBJECT) { - if (opline->opcode == ZEND_FE_FETCH_RW) { - tmp |= MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } else { - tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - } - if (t1 & MAY_BE_ARRAY) { - if (opline->opcode == ZEND_FE_FETCH_RW) { - tmp |= MAY_BE_REF | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } else { - tmp |= ((t1 & MAY_BE_ARRAY_OF_ANY) >> MAY_BE_ARRAY_SHIFT); - if (tmp & MAY_BE_ARRAY) { - tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - if (t1 & MAY_BE_ARRAY_OF_REF) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->op2_def); - if (ssa_op->result_def >= 0) { - tmp = (ssa_op->result_use >= 0) ? RES_USE_INFO() : 0; - if (t1 & MAY_BE_OBJECT) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } - if (t1 & MAY_BE_ARRAY) { - if (t1 & MAY_BE_ARRAY_KEY_LONG) { - tmp |= MAY_BE_LONG; - } - if (t1 & MAY_BE_ARRAY_KEY_STRING) { - tmp |= MAY_BE_STRING | MAY_BE_RCN; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - case ZEND_FETCH_DIM_R: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_LIST_R: - case ZEND_FETCH_LIST_W: - if (ssa_op->op1_def >= 0) { - uint32_t key_type = 0; - tmp = t1 & ~(MAY_BE_RC1|MAY_BE_RCN); - if (opline->opcode == ZEND_FETCH_DIM_W || - opline->opcode == ZEND_FETCH_DIM_RW || - opline->opcode == ZEND_FETCH_DIM_FUNC_ARG || - opline->opcode == ZEND_FETCH_LIST_W) { - if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { - if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) { - tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE); - } - tmp |= MAY_BE_ARRAY | MAY_BE_RC1; - } - if (t1 & (MAY_BE_STRING|MAY_BE_ARRAY)) { - tmp |= MAY_BE_RC1; - if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG) { - tmp |= t1 & MAY_BE_RCN; - } - } - if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN); - } - if (opline->op2_type == IS_UNUSED) { - key_type |= MAY_BE_ARRAY_KEY_LONG; - } else { - if (t2 & (MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_RESOURCE|MAY_BE_DOUBLE)) { - key_type |= MAY_BE_ARRAY_KEY_LONG; - } - if (t2 & MAY_BE_STRING) { - key_type |= MAY_BE_ARRAY_KEY_STRING; - if (opline->op2_type != IS_CONST) { - // FIXME: numeric string - key_type |= MAY_BE_ARRAY_KEY_LONG; - } - } - if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - key_type |= MAY_BE_ARRAY_KEY_STRING; - } - } - } else if (opline->opcode == ZEND_FETCH_DIM_UNSET) { - if (t1 & MAY_BE_ARRAY) { - tmp |= MAY_BE_RC1; - } - if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN); - } - } - if (opline->opcode == ZEND_FETCH_DIM_RW - || opline->opcode == ZEND_FETCH_DIM_W - || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG - || opline->opcode == ZEND_FETCH_LIST_W) { - j = ssa_vars[ssa_op->result_def].use_chain; - while (j >= 0) { - zend_uchar opcode; - - if (!ssa_opcodes) { - ZEND_ASSERT(j == (opline - op_array->opcodes) + 1 && "Use must be in next opline"); - opcode = op_array->opcodes[j].opcode; - } else { - ZEND_ASSERT(ssa_opcodes[j] == opline + 1 && "Use must be in next opline"); - opcode = ssa_opcodes[j]->opcode; - } - switch (opcode) { - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_DIM_OP: - tmp |= key_type | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY; - break; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_REF: - case ZEND_ASSIGN_REF: - case ZEND_YIELD: - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_RETURN_BY_REF: - case ZEND_VERIFY_RETURN_TYPE: - case ZEND_MAKE_REF: - case ZEND_FE_RESET_RW: - tmp |= key_type | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - break; - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - if (tmp & MAY_BE_ARRAY_OF_LONG) { - /* may overflow */ - tmp |= key_type | MAY_BE_ARRAY_OF_DOUBLE; - } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) { - tmp |= key_type | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE; - } - break; - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - /* These will result in an error exception, unless the element - * is already an object. */ - break; - case ZEND_SEND_VAR: - /* This can occur if a DIM_FETCH_FUNC_ARG with UNUSED op2 is left - * behind, because it can't be converted to DIM_FETCH_R. */ - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - j = zend_ssa_next_use(ssa->ops, ssa_op->result_def, j); - ZEND_ASSERT(j < 0 && "There should only be one use"); - } - } - if ((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } else { - /* invalid key type */ - tmp = (tmp & (MAY_BE_RC1|MAY_BE_RCN)) | (t1 & ~(MAY_BE_RC1|MAY_BE_RCN)); - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); - } - /* FETCH_LIST on a string behaves like FETCH_R on null */ - tmp = zend_array_element_type( - opline->opcode != ZEND_FETCH_LIST_R ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL), - opline->op1_type, - opline->result_type == IS_VAR, - opline->op2_type == IS_UNUSED); - if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_FETCH_THIS: - UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_op->result_def); - UPDATE_SSA_TYPE(MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def); - break; - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - if (ssa_op->result_def >= 0) { - zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op); - - tmp = zend_fetch_prop_type(script, prop_info, &ce); - if (opline->result_type != IS_TMP_VAR) { - tmp |= MAY_BE_REF | MAY_BE_INDIRECT; - } else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) { - zend_class_entry *ce = NULL; - - if (opline->op1_type == IS_UNUSED) { - ce = op_array->scope; - } else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) { - ce = ssa->var_info[ssa_op->op1_use].ce; - } - if (prop_info) { - /* FETCH_OBJ_R/IS for plain property increments reference counter, - so it can't be 1 */ - if (ce && !ce->create_object) { - tmp &= ~MAY_BE_RC1; - } - } else { - if (ce && !ce->create_object && !ce->__get) { - tmp &= ~MAY_BE_RC1; - } - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } - } - break; - case ZEND_FETCH_STATIC_PROP_R: - case ZEND_FETCH_STATIC_PROP_IS: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_UNSET: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - tmp = zend_fetch_prop_type(script, - zend_fetch_static_prop_info(script, op_array, ssa, opline), &ce); - if (opline->result_type != IS_TMP_VAR) { - tmp |= MAY_BE_REF | MAY_BE_INDIRECT; - } else { - tmp &= ~MAY_BE_RC1; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - if (ssa_op->result_def >= 0) { - zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - zend_call_info *call_info; - - if (!func_info || !func_info->call_map) { - goto unknown_opcode; - } - call_info = func_info->call_map[opline - op_array->opcodes]; - if (!call_info) { - goto unknown_opcode; - } - - zend_class_entry *ce; - bool ce_is_instanceof; - tmp = zend_get_func_info(call_info, ssa, &ce, &ce_is_instanceof); - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, ce_is_instanceof, ssa_op->result_def); - } - } - break; - case ZEND_FETCH_CONSTANT: - case ZEND_FETCH_CLASS_CONSTANT: - UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); - break; - case ZEND_STRLEN: - tmp = MAY_BE_LONG; - if (t1 & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) { - tmp |= MAY_BE_NULL; - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - break; - case ZEND_COUNT: - case ZEND_FUNC_NUM_ARGS: - UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); - break; - case ZEND_FUNC_GET_ARGS: - UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); - break; - case ZEND_GET_CLASS: - case ZEND_GET_CALLED_CLASS: - UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_STRING|MAY_BE_RCN, ssa_op->result_def); - break; - case ZEND_GET_TYPE: - UPDATE_SSA_TYPE(MAY_BE_STRING|MAY_BE_RC1|MAY_BE_RCN, ssa_op->result_def); - break; - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - UPDATE_SSA_TYPE(MAY_BE_FALSE|MAY_BE_TRUE, ssa_op->result_def); - break; - case ZEND_VERIFY_RETURN_TYPE: - if (t1 & MAY_BE_REF) { - tmp = t1; - ce = NULL; - } else { - zend_arg_info *ret_info = op_array->arg_info - 1; - tmp = zend_fetch_arg_info_type(script, ret_info, &ce); - } - if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->op1_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->op1_def); - } - } else { - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ce) { - UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_op->result_def); - } else { - UPDATE_SSA_OBJ_TYPE(NULL, 0, ssa_op->result_def); - } - } - break; - case ZEND_MAKE_REF: - tmp = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - if (ssa_op->op1_def >= 0) { - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - break; - case ZEND_CATCH: - /* Forbidden opcodes */ - ZEND_UNREACHABLE(); - break; - default: -unknown_opcode: - if (ssa_op->op1_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); - } - if (ssa_op->result_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - if (opline->result_type == IS_TMP_VAR) { - if (opline->opcode == ZEND_FETCH_R || opline->opcode == ZEND_FETCH_IS) { - tmp |= MAY_BE_RCN; - } else { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } - } else { - tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN; - switch (opline->opcode) { - case ZEND_FETCH_W: - case ZEND_FETCH_RW: - case ZEND_FETCH_FUNC_ARG: - case ZEND_FETCH_UNSET: - 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_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_FETCH_STATIC_PROP_UNSET: - tmp |= MAY_BE_INDIRECT; - break; - } - } - UPDATE_SSA_TYPE(tmp, ssa_op->result_def); - } - break; - } - - return SUCCESS; -} - -int zend_update_type_info( - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_script *script, - zend_op *opline, - zend_ssa_op *ssa_op, - const zend_op **ssa_opcodes, - zend_long optimization_level) -{ - return _zend_update_type_info(op_array, ssa, script, NULL, opline, ssa_op, ssa_opcodes, optimization_level, 0); -} - -static uint32_t get_class_entry_rank(zend_class_entry *ce) { - uint32_t rank = 0; - if (ce->ce_flags & ZEND_ACC_LINKED) { - while (ce->parent) { - rank++; - ce = ce->parent; - } - } - return rank; -} - -/* Compute least common ancestor on class inheritance tree only */ -static zend_class_entry *join_class_entries( - zend_class_entry *ce1, zend_class_entry *ce2, int *is_instanceof) { - uint32_t rank1, rank2; - if (ce1 == ce2) { - return ce1; - } - if (!ce1 || !ce2) { - return NULL; - } - - rank1 = get_class_entry_rank(ce1); - rank2 = get_class_entry_rank(ce2); - - while (rank1 != rank2) { - if (rank1 > rank2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - rank1--; - } else { - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; - rank2--; - } - } - - while (ce1 != ce2) { - ce1 = !(ce1->ce_flags & ZEND_ACC_LINKED) ? NULL : ce1->parent; - ce2 = !(ce2->ce_flags & ZEND_ACC_LINKED) ? NULL : ce2->parent; - } - - if (ce1) { - *is_instanceof = 1; - } - return ce1; -} - -int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level) -{ - zend_basic_block *blocks = ssa->cfg.blocks; - zend_ssa_var *ssa_vars = ssa->vars; - zend_ssa_var_info *ssa_var_info = ssa->var_info; - int ssa_vars_count = ssa->vars_count; - int i, j; - uint32_t tmp, worklist_len = zend_bitset_len(ssa_vars_count); - bool update_worklist = 1; - - while (!zend_bitset_empty(worklist, worklist_len)) { - j = zend_bitset_first(worklist, worklist_len); - zend_bitset_excl(worklist, j); - if (ssa_vars[j].definition_phi) { - zend_ssa_phi *p = ssa_vars[j].definition_phi; - if (p->pi >= 0) { - zend_class_entry *ce = ssa_var_info[p->sources[0]].ce; - int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof; - tmp = get_ssa_var_info(ssa, p->sources[0]); - - if (!p->has_range_constraint) { - zend_ssa_type_constraint *constraint = &p->constraint.type; - tmp &= constraint->type_mask; - if (!(tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { - tmp &= ~(MAY_BE_RC1|MAY_BE_RCN); - } - if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) { - if (!ce) { - ce = constraint->ce; - is_instanceof = 1; - } else if (is_instanceof && instanceof_function(constraint->ce, ce)) { - ce = constraint->ce; - } else { - /* Ignore the constraint (either ce instanceof constraint->ce or - * they are unrelated, as far as we can statically determine) */ - } - } - } - - UPDATE_SSA_TYPE(tmp, j); - UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j); - } else { - int first = 1; - int is_instanceof = 0; - zend_class_entry *ce = NULL; - - tmp = 0; - for (i = 0; i < blocks[p->block].predecessors_count; i++) { - tmp |= get_ssa_var_info(ssa, p->sources[i]); - } - UPDATE_SSA_TYPE(tmp, j); - for (i = 0; i < blocks[p->block].predecessors_count; i++) { - zend_ssa_var_info *info; - - ZEND_ASSERT(p->sources[i] >= 0); - info = &ssa_var_info[p->sources[i]]; - if (info->type & MAY_BE_OBJECT) { - if (first) { - ce = info->ce; - is_instanceof = info->is_instanceof; - first = 0; - } else { - is_instanceof |= info->is_instanceof; - ce = join_class_entries(ce, info->ce, &is_instanceof); - } - } - } - UPDATE_SSA_OBJ_TYPE(ce, ce ? is_instanceof : 0, j); - } - } else if (ssa_vars[j].definition >= 0) { - i = ssa_vars[j].definition; - if (_zend_update_type_info(op_array, ssa, script, worklist, op_array->opcodes + i, ssa->ops + i, NULL, optimization_level, 1) == FAILURE) { - return FAILURE; - } - } - } - return SUCCESS; -} - -static bool is_narrowable_instr(zend_op *opline) { - return opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB - || opline->opcode == ZEND_MUL || opline->opcode == ZEND_DIV; -} - -static bool is_effective_op1_double_cast(zend_op *opline, zval *op2) { - return (opline->opcode == ZEND_ADD && Z_LVAL_P(op2) == 0) - || (opline->opcode == ZEND_SUB && Z_LVAL_P(op2) == 0) - || (opline->opcode == ZEND_MUL && Z_LVAL_P(op2) == 1) - || (opline->opcode == ZEND_DIV && Z_LVAL_P(op2) == 1); -} -static bool is_effective_op2_double_cast(zend_op *opline, zval *op1) { - /* In PHP it holds that (double)(0-$int) is bitwise identical to 0.0-(double)$int, - * so allowing SUB here is fine. */ - return (opline->opcode == ZEND_ADD && Z_LVAL_P(op1) == 0) - || (opline->opcode == ZEND_SUB && Z_LVAL_P(op1) == 0) - || (opline->opcode == ZEND_MUL && Z_LVAL_P(op1) == 1); -} - -/* This function recursively checks whether it's possible to convert an integer variable - * initialization to a double initialization. The basic idea is that if the value is used - * only in add/sub/mul/div ("narrowable" instructions) with a double result value, then it - * will be cast to double at that point anyway, so we may as well do it earlier already. - * - * The tricky case are chains of operations, where it's not necessarily a given that converting - * an integer to double before the chain of operations is the same as converting it after the - * chain. What this function does is detect two cases where it is safe: - * * If the operations only involve constants, then we can simply verify that performing the - * calculation on integers and doubles yields the same value. - * * Even if one operand is not known, we may be able to determine that the operations with the - * integer replaced by a double only acts as an effective double cast on the unknown operand. - * E.g. 0+$i and 0.0+$i only differ by that cast. If then the consuming instruction of this - * result will perform a double cast anyway, the conversion is safe. - * - * The checks happens recursively, while keeping track of which variables are already visisted to - * avoid infinite loops. An iterative, worklist driven approach would be possible, but the state - * management more cumbersome to implement, so we don't bother for now. - */ -static bool can_convert_to_double( - const zend_op_array *op_array, zend_ssa *ssa, int var_num, - zval *value, zend_bitset visited) { - zend_ssa_var *var = &ssa->vars[var_num]; - zend_ssa_phi *phi; - int use; - uint32_t type; - - if (zend_bitset_in(visited, var_num)) { - return 1; - } - zend_bitset_incl(visited, var_num); - - for (use = var->use_chain; use >= 0; use = zend_ssa_next_use(ssa->ops, var_num, use)) { - zend_op *opline = &op_array->opcodes[use]; - zend_ssa_op *ssa_op = &ssa->ops[use]; - - if (zend_ssa_is_no_val_use(opline, ssa_op, var_num)) { - continue; - } - - if (!is_narrowable_instr(opline)) { - return 0; - } - - /* Instruction always returns double, the conversion is certainly fine */ - type = ssa->var_info[ssa_op->result_def].type; - if ((type & MAY_BE_ANY) == MAY_BE_DOUBLE) { - continue; - } - - /* UNDEF signals that the previous result is an effective double cast, this is only allowed - * if this instruction would have done the cast anyway (previous check). */ - if (Z_ISUNDEF_P(value)) { - return 0; - } - - /* Check that narrowing can actually be useful */ - if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) { - return 0; - } - - { - /* For calculation on original values */ - zval orig_op1, orig_op2, orig_result; - /* For calculation with var_num cast to double */ - zval dval_op1, dval_op2, dval_result; - - ZVAL_UNDEF(&orig_op1); - ZVAL_UNDEF(&dval_op1); - if (ssa_op->op1_use == var_num) { - ZVAL_COPY_VALUE(&orig_op1, value); - ZVAL_DOUBLE(&dval_op1, (double) Z_LVAL_P(value)); - } else if (opline->op1_type == IS_CONST) { - zval *zv = CRT_CONSTANT(opline->op1); - if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) { - ZVAL_COPY_VALUE(&orig_op1, zv); - ZVAL_COPY_VALUE(&dval_op1, zv); - } - } - - ZVAL_UNDEF(&orig_op2); - ZVAL_UNDEF(&dval_op2); - if (ssa_op->op2_use == var_num) { - ZVAL_COPY_VALUE(&orig_op2, value); - ZVAL_DOUBLE(&dval_op2, (double) Z_LVAL_P(value)); - } else if (opline->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT(opline->op2); - if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_DOUBLE) { - ZVAL_COPY_VALUE(&orig_op2, zv); - ZVAL_COPY_VALUE(&dval_op2, zv); - } - } - - ZEND_ASSERT(!Z_ISUNDEF(orig_op1) || !Z_ISUNDEF(orig_op2)); - if (Z_ISUNDEF(orig_op1)) { - if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op2) == 0) { - ZVAL_LONG(&orig_result, 0); - } else if (is_effective_op1_double_cast(opline, &orig_op2)) { - ZVAL_UNDEF(&orig_result); - } else { - return 0; - } - } else if (Z_ISUNDEF(orig_op2)) { - if (opline->opcode == ZEND_MUL && Z_LVAL(orig_op1) == 0) { - ZVAL_LONG(&orig_result, 0); - } else if (is_effective_op2_double_cast(opline, &orig_op1)) { - ZVAL_UNDEF(&orig_result); - } else { - return 0; - } - } else { - zend_uchar opcode = opline->opcode; - - if (opcode == ZEND_ASSIGN_OP) { - opcode = opline->extended_value; - } - - /* Avoid division by zero */ - if (opcode == ZEND_DIV && zval_get_double(&orig_op2) == 0.0) { - return 0; - } - - get_binary_op(opcode)(&orig_result, &orig_op1, &orig_op2); - get_binary_op(opcode)(&dval_result, &dval_op1, &dval_op2); - ZEND_ASSERT(Z_TYPE(dval_result) == IS_DOUBLE); - if (zval_get_double(&orig_result) != Z_DVAL(dval_result)) { - return 0; - } - } - - if (!can_convert_to_double(op_array, ssa, ssa_op->result_def, &orig_result, visited)) { - return 0; - } - } - } - - for (phi = var->phi_use_chain; phi; phi = zend_ssa_next_use_phi(ssa, var_num, phi)) { - /* Check that narrowing can actually be useful */ - type = ssa->var_info[phi->ssa_var].type; - if ((type & MAY_BE_ANY) & ~(MAY_BE_LONG|MAY_BE_DOUBLE)) { - return 0; - } - - if (!can_convert_to_double(op_array, ssa, phi->ssa_var, value, visited)) { - return 0; - } - } - - return 1; -} - -static int zend_type_narrowing(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) -{ - uint32_t bitset_len = zend_bitset_len(ssa->vars_count); - zend_bitset visited, worklist; - int i, v; - zend_op *opline; - bool narrowed = 0; - ALLOCA_FLAG(use_heap) - - visited = ZEND_BITSET_ALLOCA(2 * bitset_len, use_heap); - worklist = visited + bitset_len; - - zend_bitset_clear(worklist, bitset_len); - - for (v = op_array->last_var; v < ssa->vars_count; v++) { - if ((ssa->var_info[v].type & (MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF)) != MAY_BE_LONG) continue; - if (ssa->vars[v].definition < 0) continue; - if (ssa->vars[v].no_val) continue; - opline = op_array->opcodes + ssa->vars[v].definition; - /* Go through assignments of literal integers and check if they can be converted to - * doubles instead, in the hope that we'll narrow long|double to double. */ - if (opline->opcode == ZEND_ASSIGN && opline->result_type == IS_UNUSED && - opline->op1_type == IS_CV && opline->op2_type == IS_CONST) { - zval *value = CRT_CONSTANT(opline->op2); - - zend_bitset_clear(visited, bitset_len); - if (can_convert_to_double(op_array, ssa, v, value, visited)) { - narrowed = 1; - ssa->var_info[v].use_as_double = 1; - /* The "visited" vars are exactly those which may change their type due to - * narrowing. Reset their types and add them to the type inference worklist */ - ZEND_BITSET_FOREACH(visited, bitset_len, i) { - ssa->var_info[i].type &= ~MAY_BE_ANY; - } ZEND_BITSET_FOREACH_END(); - zend_bitset_union(worklist, visited, bitset_len); - } - } - } - - if (!narrowed) { - free_alloca(visited, use_heap); - return SUCCESS; - } - - if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) { - free_alloca(visited, use_heap); - return FAILURE; - } - - free_alloca(visited, use_heap); - return SUCCESS; -} - -static int is_recursive_tail_call(const zend_op_array *op_array, - zend_op *opline) -{ - zend_func_info *info = ZEND_FUNC_INFO(op_array); - - if (info->ssa.ops && info->ssa.vars && info->call_map && - info->ssa.ops[opline - op_array->opcodes].op1_use >= 0 && - info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition >= 0) { - - zend_op *op = op_array->opcodes + info->ssa.vars[info->ssa.ops[opline - op_array->opcodes].op1_use].definition; - - if (op->opcode == ZEND_DO_UCALL) { - zend_call_info *call_info = info->call_map[op - op_array->opcodes]; - if (call_info && op_array == &call_info->callee_func->op_array) { - return 1; - } - } - } - return 0; -} - -void zend_init_func_return_info(const zend_op_array *op_array, - const zend_script *script, - zend_ssa_var_info *ret) -{ - if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_arg_info *ret_info = op_array->arg_info - 1; - zend_ssa_range tmp_range = {0, 0, 0, 0}; - - ret->type = zend_fetch_arg_info_type(script, ret_info, &ret->ce); - if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) { - ret->type |= MAY_BE_REF; - } - ret->is_instanceof = (ret->ce) ? 1 : 0; - ret->range = tmp_range; - ret->has_range = 0; - } -} - -void zend_func_return_info(const zend_op_array *op_array, - const zend_script *script, - int recursive, - int widening, - zend_ssa_var_info *ret) -{ - zend_func_info *info = ZEND_FUNC_INFO(op_array); - zend_ssa *ssa = &info->ssa; - int blocks_count = info->ssa.cfg.blocks_count; - zend_basic_block *blocks = info->ssa.cfg.blocks; - int j; - uint32_t t1; - uint32_t tmp = 0; - zend_class_entry *tmp_ce = NULL; - int tmp_is_instanceof = -1; - zend_class_entry *arg_ce; - int arg_is_instanceof; - zend_ssa_range tmp_range = {0, 0, 0, 0}; - int tmp_has_range = -1; - - if (op_array->fn_flags & ZEND_ACC_GENERATOR) { - ret->type = MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN; - ret->ce = zend_ce_generator; - ret->is_instanceof = 0; - ret->range = tmp_range; - ret->has_range = 0; - return; - } - - for (j = 0; j < blocks_count; j++) { - if ((blocks[j].flags & ZEND_BB_REACHABLE) && blocks[j].len != 0) { - zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1; - - if (opline->opcode == ZEND_RETURN || opline->opcode == ZEND_RETURN_BY_REF) { - zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL; - if (!recursive && ssa_op && info->ssa.var_info && - ssa_op->op1_use >= 0 && - info->ssa.var_info[ssa_op->op1_use].recursive) { - continue; - } - if (is_recursive_tail_call(op_array, opline)) { - continue; - } - t1 = OP1_INFO(); - if (t1 & MAY_BE_UNDEF) { - t1 |= MAY_BE_NULL; - } - if (opline->opcode == ZEND_RETURN) { - if (t1 & MAY_BE_RC1) { - t1 |= MAY_BE_RCN; - } - t1 &= ~(MAY_BE_UNDEF | MAY_BE_REF); - } else { - t1 |= MAY_BE_REF; - t1 &= ~(MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN); - } - tmp |= t1; - - if (ssa_op && info->ssa.var_info && - ssa_op->op1_use >= 0 && - info->ssa.var_info[ssa_op->op1_use].ce) { - arg_ce = info->ssa.var_info[ssa_op->op1_use].ce; - arg_is_instanceof = info->ssa.var_info[ssa_op->op1_use].is_instanceof; - } else { - arg_ce = NULL; - arg_is_instanceof = 0; - } - - if (tmp_is_instanceof < 0) { - tmp_ce = arg_ce; - tmp_is_instanceof = arg_is_instanceof; - } else if (arg_ce && arg_ce == tmp_ce) { - if (tmp_is_instanceof != arg_is_instanceof) { - tmp_is_instanceof = 1; - } - } else { - tmp_ce = NULL; - tmp_is_instanceof = 0; - } - - if (opline->op1_type == IS_CONST) { - zval *zv = CRT_CONSTANT(opline->op1); - - if (Z_TYPE_P(zv) == IS_NULL) { - if (tmp_has_range < 0) { - tmp_has_range = 1; - tmp_range.underflow = 0; - tmp_range.min = 0; - tmp_range.max = 0; - tmp_range.overflow = 0; - } else if (tmp_has_range) { - if (!tmp_range.underflow) { - tmp_range.min = MIN(tmp_range.min, 0); - } - if (!tmp_range.overflow) { - tmp_range.max = MAX(tmp_range.max, 0); - } - } - } else if (Z_TYPE_P(zv) == IS_FALSE) { - if (tmp_has_range < 0) { - tmp_has_range = 1; - tmp_range.underflow = 0; - tmp_range.min = 0; - tmp_range.max = 0; - tmp_range.overflow = 0; - } else if (tmp_has_range) { - if (!tmp_range.underflow) { - tmp_range.min = MIN(tmp_range.min, 0); - } - if (!tmp_range.overflow) { - tmp_range.max = MAX(tmp_range.max, 0); - } - } - } else if (Z_TYPE_P(zv) == IS_TRUE) { - if (tmp_has_range < 0) { - tmp_has_range = 1; - tmp_range.underflow = 0; - tmp_range.min = 1; - tmp_range.max = 1; - tmp_range.overflow = 0; - } else if (tmp_has_range) { - if (!tmp_range.underflow) { - tmp_range.min = MIN(tmp_range.min, 1); - } - if (!tmp_range.overflow) { - tmp_range.max = MAX(tmp_range.max, 1); - } - } - } else if (Z_TYPE_P(zv) == IS_LONG) { - if (tmp_has_range < 0) { - tmp_has_range = 1; - tmp_range.underflow = 0; - tmp_range.min = Z_LVAL_P(zv); - tmp_range.max = Z_LVAL_P(zv); - tmp_range.overflow = 0; - } else if (tmp_has_range) { - if (!tmp_range.underflow) { - tmp_range.min = MIN(tmp_range.min, Z_LVAL_P(zv)); - } - if (!tmp_range.overflow) { - tmp_range.max = MAX(tmp_range.max, Z_LVAL_P(zv)); - } - } - } else { - tmp_has_range = 0; - } - } else if (ssa_op && info->ssa.var_info && ssa_op->op1_use >= 0) { - if (info->ssa.var_info[ssa_op->op1_use].has_range) { - if (tmp_has_range < 0) { - tmp_has_range = 1; - tmp_range = info->ssa.var_info[ssa_op->op1_use].range; - } else if (tmp_has_range) { - /* union */ - if (info->ssa.var_info[ssa_op->op1_use].range.underflow) { - tmp_range.underflow = 1; - tmp_range.min = ZEND_LONG_MIN; - } else { - tmp_range.min = MIN(tmp_range.min, info->ssa.var_info[ssa_op->op1_use].range.min); - } - if (info->ssa.var_info[ssa_op->op1_use].range.overflow) { - tmp_range.overflow = 1; - tmp_range.max = ZEND_LONG_MAX; - } else { - tmp_range.max = MAX(tmp_range.max, info->ssa.var_info[ssa_op->op1_use].range.max); - } - } - } else if (!widening) { - tmp_has_range = 1; - tmp_range.underflow = 1; - tmp_range.min = ZEND_LONG_MIN; - tmp_range.max = ZEND_LONG_MAX; - tmp_range.overflow = 1; - } - } else { - tmp_has_range = 0; - } - } - } - } - - if (!(op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { - if (tmp_is_instanceof < 0) { - tmp_is_instanceof = 0; - tmp_ce = NULL; - } - if (tmp_has_range < 0) { - tmp_has_range = 0; - } - ret->type = tmp; - ret->ce = tmp_ce; - ret->is_instanceof = tmp_is_instanceof; - } - ret->range = tmp_range; - ret->has_range = tmp_has_range; -} - -static int zend_infer_types(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) -{ - zend_ssa_var_info *ssa_var_info = ssa->var_info; - int ssa_vars_count = ssa->vars_count; - int j; - zend_bitset worklist; - ALLOCA_FLAG(use_heap); - - worklist = do_alloca(sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count), use_heap); - memset(worklist, 0, sizeof(zend_ulong) * zend_bitset_len(ssa_vars_count)); - - /* Type Inference */ - for (j = op_array->last_var; j < ssa_vars_count; j++) { - zend_bitset_incl(worklist, j); - ssa_var_info[j].type = 0; - } - - if (zend_infer_types_ex(op_array, script, ssa, worklist, optimization_level) != SUCCESS) { - free_alloca(worklist, use_heap); - return FAILURE; - } - - /* Narrowing integer initialization to doubles */ - zend_type_narrowing(op_array, script, ssa, optimization_level); - - if (ZEND_FUNC_INFO(op_array)) { - zend_func_return_info(op_array, script, 1, 0, &ZEND_FUNC_INFO(op_array)->return_info); - } - - free_alloca(worklist, use_heap); - return SUCCESS; -} - -int zend_ssa_inference(zend_arena **arena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level) /* {{{ */ -{ - zend_ssa_var_info *ssa_var_info; - int i; - - if (!ssa->var_info) { - ssa->var_info = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var_info)); - } - ssa_var_info = ssa->var_info; - - if (!op_array->function_name) { - for (i = 0; i < op_array->last_var; i++) { - ssa_var_info[i].type = MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - ssa_var_info[i].has_range = 0; - } - } else { - for (i = 0; i < op_array->last_var; i++) { - ssa_var_info[i].type = MAY_BE_UNDEF; - ssa_var_info[i].has_range = 0; - if (ssa->vars[i].alias) { - ssa_var_info[i].type |= get_ssa_alias_types(ssa->vars[i].alias); - } - } - } - for (i = op_array->last_var; i < ssa->vars_count; i++) { - ssa_var_info[i].type = 0; - ssa_var_info[i].has_range = 0; - } - - if (zend_infer_ranges(op_array, ssa) != SUCCESS) { - return FAILURE; - } - - if (zend_infer_types(op_array, script, ssa, optimization_level) != SUCCESS) { - return FAILURE; - } - - return SUCCESS; -} -/* }}} */ - -void zend_inference_check_recursive_dependencies(zend_op_array *op_array) -{ - zend_func_info *info = ZEND_FUNC_INFO(op_array); - zend_call_info *call_info; - zend_bitset worklist; - int worklist_len, i; - ALLOCA_FLAG(use_heap); - - if (!info->ssa.var_info || !(info->flags & ZEND_FUNC_RECURSIVE)) { - return; - } - worklist_len = zend_bitset_len(info->ssa.vars_count); - worklist = do_alloca(sizeof(zend_ulong) * worklist_len, use_heap); - memset(worklist, 0, sizeof(zend_ulong) * worklist_len); - call_info = info->callee_info; - while (call_info) { - if (call_info->recursive && call_info->caller_call_opline && - info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def >= 0) { - zend_bitset_incl(worklist, info->ssa.ops[call_info->caller_call_opline - op_array->opcodes].result_def); - } - call_info = call_info->next_callee; - } - WHILE_WORKLIST(worklist, worklist_len, i) { - if (!info->ssa.var_info[i].recursive) { - info->ssa.var_info[i].recursive = 1; - add_usages(op_array, &info->ssa, worklist, i); - } - } WHILE_WORKLIST_END(); - free_alloca(worklist, use_heap); -} - -int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2) -{ - if (opline->op1_type == IS_CV) { - if (t1 & MAY_BE_UNDEF) { - switch (opline->opcode) { - case ZEND_UNSET_VAR: - case ZEND_ISSET_ISEMPTY_VAR: - return 1; - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ASSIGN: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_REF: - case ZEND_BIND_GLOBAL: - case ZEND_BIND_STATIC: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_OBJ_IS: - case ZEND_SEND_REF: - case ZEND_UNSET_CV: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_MAKE_REF: - case ZEND_FETCH_DIM_W: - break; - default: - /* undefined variable warning */ - return 1; - } - } - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - if ((t1 & MAY_BE_RC1) - && (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - switch (opline->opcode) { - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_FETCH_LIST_R: - case ZEND_QM_ASSIGN: - case ZEND_SEND_VAL: - case ZEND_SEND_VAL_EX: - case ZEND_SEND_VAR: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_REF: - case ZEND_SEPARATE: - case ZEND_END_SILENCE: - case ZEND_MAKE_REF: - break; - default: - /* destructor may be called */ - return 1; - } - } - } - - if (opline->op2_type == IS_CV) { - if (t2 & MAY_BE_UNDEF) { - switch (opline->opcode) { - case ZEND_ASSIGN_REF: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - break; - default: - /* undefined variable warning */ - return 1; - } - } - } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) { - if ((t2 & MAY_BE_RC1) - && (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - switch (opline->opcode) { - case ZEND_ASSIGN: - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - break; - default: - /* destructor may be called */ - return 1; - } - } - } - - switch (opline->opcode) { - case ZEND_NOP: - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_QM_ASSIGN: - case ZEND_JMP: - case ZEND_CHECK_VAR: - case ZEND_MAKE_REF: - case ZEND_BEGIN_SILENCE: - case ZEND_END_SILENCE: - case ZEND_FREE: - case ZEND_FE_FREE: - case ZEND_SEPARATE: - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - case ZEND_ISSET_ISEMPTY_THIS: - case ZEND_COALESCE: - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_FUNC_NUM_ARGS: - case ZEND_FUNC_GET_ARGS: - case ZEND_COPY_TMP: - case ZEND_CASE_STRICT: - case ZEND_JMP_NULL: - return 0; - case ZEND_SEND_VAR: - case ZEND_SEND_VAL: - case ZEND_SEND_REF: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_CHECK_FUNC_ARG: - /* May throw for named params. */ - return opline->op2_type == IS_CONST; - case ZEND_INIT_FCALL: - /* can't throw, because call is resolved at compile time */ - return 0; - case ZEND_BIND_GLOBAL: - if ((opline+1)->opcode == ZEND_BIND_GLOBAL) { - return zend_may_throw(opline + 1, ssa_op + 1, op_array, ssa); - } - return 0; - case ZEND_ADD: - if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY - && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_DIV: - case ZEND_MOD: - if (!OP2_HAS_RANGE() || - (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) { - /* Division by zero */ - return 1; - } - /* break missing intentionally */ - case ZEND_SUB: - case ZEND_MUL: - case ZEND_POW: - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_SL: - case ZEND_SR: - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - !OP2_HAS_RANGE() || - OP2_MIN_RANGE() < 0; - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - if ((t1 & MAY_BE_ANY) == MAY_BE_STRING - && (t2 & MAY_BE_ANY) == MAY_BE_STRING) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_BW_NOT: - return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_PRE_INC: - case ZEND_POST_INC: - case ZEND_PRE_DEC: - case ZEND_POST_DEC: - return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_BOOL_NOT: - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_BOOL: - case ZEND_JMP_SET: - return (t1 & MAY_BE_OBJECT); - case ZEND_BOOL_XOR: - return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT); - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_CASE: - case ZEND_SPACESHIP: - if ((t1 & MAY_BE_ANY) == MAY_BE_NULL - || (t2 & MAY_BE_ANY) == MAY_BE_NULL) { - return 0; - } - return (t1 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)) || (t2 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)); - case ZEND_ASSIGN_OP: - if (opline->extended_value == ZEND_ADD) { - if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY - && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - } else if (opline->extended_value == ZEND_DIV || - opline->extended_value == ZEND_MOD) { - if (!OP2_HAS_RANGE() || - (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) { - /* Division by zero */ - return 1; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - } else if (opline->extended_value == ZEND_SUB || - opline->extended_value == ZEND_MUL || - opline->extended_value == ZEND_POW) { - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - } else if (opline->extended_value == ZEND_SL || - opline->extended_value == ZEND_SR) { - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - !OP2_HAS_RANGE() || - OP2_MIN_RANGE() < 0; - } else if (opline->extended_value == ZEND_CONCAT) { - return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) || - (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - } else if (opline->extended_value == ZEND_BW_OR || - opline->extended_value == ZEND_BW_AND || - opline->extended_value == ZEND_BW_XOR) { - if ((t1 & MAY_BE_ANY) == MAY_BE_STRING - && (t2 & MAY_BE_ANY) == MAY_BE_STRING) { - return 0; - } - return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - } - return 1; - case ZEND_ASSIGN: - if (t1 & MAY_BE_REF) { - return 1; - } - case ZEND_BIND_STATIC: - case ZEND_UNSET_VAR: - return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)); - case ZEND_ASSIGN_DIM: - if ((opline+1)->op1_type == IS_CV) { - if (_ssa_op1_info(op_array, ssa, opline+1, ssa_op+1) & MAY_BE_UNDEF) { - return 1; - } - } - return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED || - (t2 & (MAY_BE_UNDEF|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_ASSIGN_OBJ: - if (t1 & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_OBJECT))) { - return 1; - } - if (ssa_op->op1_use) { - zend_ssa_var_info *var_info = ssa->var_info + ssa_op->op1_use; - zend_class_entry *ce = var_info->ce; - - if (var_info->is_instanceof || - !ce || ce->create_object || ce->__get || ce->__set || ce->parent) { - return 1; - } - - if (op_array->scope != ce && ce->default_properties_count) { - zend_property_info *prop_info; - - if (opline->op2_type == IS_CONST) { - prop_info = zend_hash_find_ptr(&ce->properties_info, - Z_STR_P(CRT_CONSTANT(opline->op2))); - if (prop_info && !(prop_info->flags & ZEND_ACC_PUBLIC)) { - return 1; - } - } else { - if (t2 & (MAY_BE_ANY-MAY_BE_STRING)) { - return 1; - } - ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) { - if (!(prop_info->flags & ZEND_ACC_PUBLIC)) { - return 1; - } - } ZEND_HASH_FOREACH_END(); - } - } - return 0; - } - return 1; - case ZEND_ROPE_INIT: - case ZEND_ROPE_ADD: - case ZEND_ROPE_END: - return t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT); - case ZEND_INIT_ARRAY: - case ZEND_ADD_ARRAY_ELEMENT: - return (opline->op2_type != IS_UNUSED) && (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_STRLEN: - return (t1 & MAY_BE_ANY) != MAY_BE_STRING; - case ZEND_COUNT: - return (t1 & MAY_BE_ANY) != MAY_BE_ARRAY; - case ZEND_RECV_INIT: - if (Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_CONSTANT_AST) { - return 1; - } - if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { - uint32_t arg_num = opline->op1.num; - zend_arg_info *cur_arg_info; - - if (EXPECTED(arg_num <= op_array->num_args)) { - cur_arg_info = &op_array->arg_info[arg_num-1]; - } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) { - cur_arg_info = &op_array->arg_info[op_array->num_args]; - } else { - return 0; - } - return ZEND_TYPE_IS_SET(cur_arg_info->type); - } else { - return 0; - } - case ZEND_FETCH_IS: - return (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - case ZEND_FETCH_DIM_IS: - return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); - case ZEND_CAST: - switch (opline->extended_value) { - case IS_LONG: - case IS_DOUBLE: - return (t1 & MAY_BE_OBJECT); - case IS_STRING: - return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); - case IS_ARRAY: - return (t1 & MAY_BE_OBJECT); - case IS_OBJECT: - return 0; - EMPTY_SWITCH_DEFAULT_CASE() - } - case ZEND_ARRAY_KEY_EXISTS: - if ((t2 & MAY_BE_ANY) != MAY_BE_ARRAY) { - return 1; - } - if ((t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { - return 1; - } - return 0; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) { - return 1; - } - return 0; - case ZEND_FE_FETCH_R: - if ((t1 & (MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) { - return 1; - } - if (opline->op2_type == IS_CV - && (t2 & MAY_BE_RC1) - && (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { - return 1; - } - return 0; - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_LIST_W: - if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { - return 1; - } - if (t2 & (MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT)) { - return 1; - } - if (opline->op2_type == IS_UNUSED) { - return 1; - } - return 0; - default: - return 1; - } -} - -int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa) -{ - return zend_may_throw_ex(opline, ssa_op, op_array, ssa, OP1_INFO(), OP2_INFO()); -} diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h deleted file mode 100644 index 11a9d74d75..0000000000 --- a/ext/opcache/Optimizer/zend_inference.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, e-SSA based Type & Range Inference | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_INFERENCE_H -#define ZEND_INFERENCE_H - -#include "zend_optimizer.h" -#include "zend_ssa.h" -#include "zend_bitset.h" - -/* Bitmask for type inference (zend_ssa_var_info.type) */ -#include "zend_type_info.h" - -#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */ -#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */ -#define MAY_BE_GUARD (1<<28) /* needs type guard */ -//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */ - -//TODO: remome MAY_BE_RC1, MAY_BE_RCN??? -#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */ -#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */ - -#define MAY_HAVE_DTOR \ - (MAY_BE_OBJECT|MAY_BE_RESOURCE \ - |MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE) - -#define DEFINE_SSA_OP_HAS_RANGE(opN) \ - static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT(opline->opN); \ - return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \ - } else { \ - return (opline->opN##_type != IS_UNUSED && \ - ssa->var_info && \ - ssa_op->opN##_use >= 0 && \ - ssa->var_info[ssa_op->opN##_use].has_range); \ - } \ - return 0; \ - } \ - -#define DEFINE_SSA_OP_MIN_RANGE(opN) \ - static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT(opline->opN); \ - if (Z_TYPE_P(zv) == IS_LONG) { \ - return Z_LVAL_P(zv); \ - } else if (Z_TYPE_P(zv) == IS_TRUE) { \ - return 1; \ - } else if (Z_TYPE_P(zv) == IS_FALSE) { \ - return 0; \ - } else if (Z_TYPE_P(zv) == IS_NULL) { \ - return 0; \ - } \ - } else if (opline->opN##_type != IS_UNUSED && \ - ssa->var_info && \ - ssa_op->opN##_use >= 0 && \ - ssa->var_info[ssa_op->opN##_use].has_range) { \ - return ssa->var_info[ssa_op->opN##_use].range.min; \ - } \ - return ZEND_LONG_MIN; \ - } \ - -#define DEFINE_SSA_OP_MAX_RANGE(opN) \ - static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT(opline->opN); \ - if (Z_TYPE_P(zv) == IS_LONG) { \ - return Z_LVAL_P(zv); \ - } else if (Z_TYPE_P(zv) == IS_TRUE) { \ - return 1; \ - } else if (Z_TYPE_P(zv) == IS_FALSE) { \ - return 0; \ - } else if (Z_TYPE_P(zv) == IS_NULL) { \ - return 0; \ - } \ - } else if (opline->opN##_type != IS_UNUSED && \ - ssa->var_info && \ - ssa_op->opN##_use >= 0 && \ - ssa->var_info[ssa_op->opN##_use].has_range) { \ - return ssa->var_info[ssa_op->opN##_use].range.max; \ - } \ - return ZEND_LONG_MAX; \ - } \ - -#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \ - static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT(opline->opN); \ - if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \ - return 0; \ - } \ - } else if (opline->opN##_type != IS_UNUSED && \ - ssa->var_info && \ - ssa_op->opN##_use >= 0 && \ - ssa->var_info[ssa_op->opN##_use].has_range) { \ - return ssa->var_info[ssa_op->opN##_use].range.underflow; \ - } \ - return 1; \ - } \ - -#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \ - static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT(opline->opN); \ - if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \ - return 0; \ - } \ - } else if (opline->opN##_type != IS_UNUSED && \ - ssa->var_info && \ - ssa_op->opN##_use >= 0 && \ - ssa->var_info[ssa_op->opN##_use].has_range) { \ - return ssa->var_info[ssa_op->opN##_use].range.overflow; \ - } \ - return 1; \ - } \ - -DEFINE_SSA_OP_HAS_RANGE(op1) -DEFINE_SSA_OP_MIN_RANGE(op1) -DEFINE_SSA_OP_MAX_RANGE(op1) -DEFINE_SSA_OP_RANGE_UNDERFLOW(op1) -DEFINE_SSA_OP_RANGE_OVERFLOW(op1) -DEFINE_SSA_OP_HAS_RANGE(op2) -DEFINE_SSA_OP_MIN_RANGE(op2) -DEFINE_SSA_OP_MAX_RANGE(op2) -DEFINE_SSA_OP_RANGE_UNDERFLOW(op2) -DEFINE_SSA_OP_RANGE_OVERFLOW(op2) - -#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline, ssa_op)) -#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline, ssa_op)) -#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline, ssa_op)) -#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op)) -#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op)) -#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline, ssa_op)) -#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline, ssa_op)) -#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline, ssa_op)) -#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op)) -#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op)) - -static zend_always_inline uint32_t _const_op_type(const zval *zv) { - if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { - return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY; - } else if (Z_TYPE_P(zv) == IS_ARRAY) { - HashTable *ht = Z_ARRVAL_P(zv); - uint32_t tmp = MAY_BE_ARRAY; - zend_string *str; - zval *val; - - if (Z_REFCOUNTED_P(zv)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else { - tmp |= MAY_BE_RCN; - } - - ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) { - if (str) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - } else { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); - } ZEND_HASH_FOREACH_END(); - if (HT_IS_PACKED(ht)) { - tmp &= ~MAY_BE_ARRAY_HASH; - } - return tmp; - } else { - uint32_t tmp = (1 << Z_TYPE_P(zv)); - - if (Z_REFCOUNTED_P(zv)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else if (Z_TYPE_P(zv) == IS_STRING) { - tmp |= MAY_BE_RCN; - } - return tmp; - } -} - -static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num) -{ - if (ssa->var_info && ssa_var_num >= 0) { - return ssa->var_info[ssa_var_num].type; - } else { - return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - } -} - -#define DEFINE_SSA_OP_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - if (opline->opN##_type == IS_CONST) { \ - return _const_op_type(CRT_CONSTANT(opline->opN)); \ - } else { \ - return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \ - } \ - } \ - -#define DEFINE_SSA_OP_DEF_INFO(opN) \ - static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \ - { \ - return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \ - } \ - - -DEFINE_SSA_OP_INFO(op1) -DEFINE_SSA_OP_INFO(op2) -DEFINE_SSA_OP_INFO(result) -DEFINE_SSA_OP_DEF_INFO(op1) -DEFINE_SSA_OP_DEF_INFO(op2) -DEFINE_SSA_OP_DEF_INFO(result) - -#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op)) -#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op)) -#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op)) -#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op)) -#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op)) -#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1))) -#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op)) - -static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a > ZEND_LONG_MAX - b) - || (b < 0 && a < ZEND_LONG_MIN - b); -} -static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) { - return (b > 0 && a < ZEND_LONG_MIN + b) - || (b < 0 && a > ZEND_LONG_MAX + b); -} - -BEGIN_EXTERN_C() - -int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa); -int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); -int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level); - -uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert); - -int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp); -int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp); -void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow); -int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r); -int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r); -void zend_inference_check_recursive_dependencies(zend_op_array *op_array); - -int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level); - -uint32_t zend_fetch_arg_info_type( - const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce); -void zend_init_func_return_info(const zend_op_array *op_array, - const zend_script *script, - zend_ssa_var_info *ret); -void zend_func_return_info(const zend_op_array *op_array, - const zend_script *script, - int recursive, - int widening, - zend_ssa_var_info *ret); - -int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2); -int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa); - -int zend_update_type_info(const zend_op_array *op_array, - zend_ssa *ssa, - const zend_script *script, - zend_op *opline, - zend_ssa_op *ssa_op, - const zend_op **ssa_opcodes, - zend_long optimization_level); - -END_EXTERN_C() - -#endif /* ZEND_INFERENCE_H */ diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c deleted file mode 100644 index 00c579491a..0000000000 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ /dev/null @@ -1,1568 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "Optimizer/zend_optimizer.h" -#include "Optimizer/zend_optimizer_internal.h" -#include "zend_API.h" -#include "zend_constants.h" -#include "zend_execute.h" -#include "zend_vm.h" -#include "zend_cfg.h" -#include "zend_func_info.h" -#include "zend_call_graph.h" -#include "zend_inference.h" -#include "zend_dump.h" - -static void zend_optimizer_zval_dtor_wrapper(zval *zvalue) -{ - zval_ptr_dtor_nogc(zvalue); -} - -void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value) -{ - zval val; - - if (!ctx->constants) { - ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable)); - zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0); - } - ZVAL_COPY(&val, value); - zend_hash_add(ctx->constants, Z_STR_P(name), &val); -} - -int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */ -{ - binary_op_type binary_op = get_binary_op(opcode); - int er, ret; - - if (zend_binary_op_produces_error(opcode, op1, op2)) { - return FAILURE; - } - - er = EG(error_reporting); - EG(error_reporting) = 0; - ret = binary_op(result, op1, op2); - EG(error_reporting) = er; - - return ret; -} -/* }}} */ - -int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */ -{ - unary_op_type unary_op = get_unary_op(opcode); - - if (unary_op) { - if (opcode == ZEND_BW_NOT - && Z_TYPE_P(op1) != IS_LONG - && Z_TYPE_P(op1) != IS_DOUBLE - && Z_TYPE_P(op1) != IS_STRING) { - /* produces "Unsupported operand types" exception */ - return FAILURE; - } - return unary_op(result, op1); - } else { /* ZEND_BOOL */ - ZVAL_BOOL(result, zend_is_true(op1)); - return SUCCESS; - } -} -/* }}} */ - -int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */ -{ - switch (type) { - case IS_NULL: - ZVAL_NULL(result); - return SUCCESS; - case _IS_BOOL: - ZVAL_BOOL(result, zval_is_true(op1)); - return SUCCESS; - case IS_LONG: - ZVAL_LONG(result, zval_get_long(op1)); - return SUCCESS; - case IS_DOUBLE: - ZVAL_DOUBLE(result, zval_get_double(op1)); - return SUCCESS; - case IS_STRING: - /* Conversion from double to string takes into account run-time - 'precision' setting and cannot be evaluated at compile-time */ - if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) { - ZVAL_STR(result, zval_get_string(op1)); - return SUCCESS; - } - break; - case IS_ARRAY: - ZVAL_COPY(result, op1); - convert_to_array(result); - return SUCCESS; - } - return FAILURE; -} -/* }}} */ - -int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */ -{ - if (Z_TYPE_P(op1) != IS_STRING) { - return FAILURE; - } - ZVAL_LONG(result, Z_STRLEN_P(op1)); - return SUCCESS; -} -/* }}} */ - -int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value) -{ - zval *val; - - if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) { - ZVAL_COPY(value, val); - return 1; - } - return 0; -} - -int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv) -{ - int i = op_array->last_literal; - op_array->last_literal++; - op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); - ZVAL_COPY_VALUE(&op_array->literals[i], zv); - Z_EXTRA(op_array->literals[i]) = 0; - return i; -} - -static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) { - zval zv; - ZVAL_STR(&zv, str); - zend_string_hash_val(str); - return zend_optimizer_add_literal(op_array, &zv); -} - -static inline void drop_leading_backslash(zval *val) { - if (Z_STRVAL_P(val)[0] == '\\') { - zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0); - zval_ptr_dtor_nogc(val); - ZVAL_STR(val, str); - } -} - -static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) { - uint32_t ret = op_array->cache_size; - op_array->cache_size += num * sizeof(void *); - return ret; -} - -#define REQUIRES_STRING(val) do { \ - if (Z_TYPE_P(val) != IS_STRING) { \ - return 0; \ - } \ -} while (0) - -#define TO_STRING_NOWARN(val) do { \ - if (Z_TYPE_P(val) >= IS_ARRAY) { \ - return 0; \ - } \ - convert_to_string(val); \ -} while (0) - -int zend_optimizer_update_op1_const(zend_op_array *op_array, - zend_op *opline, - zval *val) -{ - switch (opline->opcode) { - case ZEND_OP_DATA: - switch ((opline-1)->opcode) { - case ZEND_ASSIGN_OBJ_REF: - case ZEND_ASSIGN_STATIC_PROP_REF: - return 0; - } - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - break; - case ZEND_FREE: - case ZEND_CHECK_VAR: - MAKE_NOP(opline); - zval_ptr_dtor_nogc(val); - return 1; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM: - case ZEND_RETURN_BY_REF: - case ZEND_INSTANCEOF: - case ZEND_MAKE_REF: - return 0; - case ZEND_CATCH: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - case ZEND_DEFINED: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 1); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - case ZEND_NEW: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - opline->op2.num = alloc_cache_slots(op_array, 1); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - case ZEND_INIT_STATIC_METHOD_CALL: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type != IS_CONST) { - opline->result.num = alloc_cache_slots(op_array, 1); - } - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - case ZEND_FETCH_CLASS_CONSTANT: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1); - } - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - case ZEND_ASSIGN_OP: - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - break; - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_FETCH_STATIC_PROP_R: - case ZEND_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_IS: - case ZEND_FETCH_STATIC_PROP_UNSET: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_UNSET_STATIC_PROP: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_PRE_INC_STATIC_PROP: - case ZEND_PRE_DEC_STATIC_PROP: - case ZEND_POST_INC_STATIC_PROP: - case ZEND_POST_DEC_STATIC_PROP: - TO_STRING_NOWARN(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) { - op_array->cache_size += sizeof(void *); - } else { - opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); - } - break; - case ZEND_SEND_VAR: - opline->opcode = ZEND_SEND_VAL; - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - break; - case ZEND_SEPARATE: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - return 0; - case ZEND_VERIFY_RETURN_TYPE: - /* This would require a non-local change. - * zend_optimizer_replace_by_const() supports this. */ - return 0; - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_FETCH_LIST_R: - case ZEND_COPY_TMP: - case ZEND_FETCH_CLASS_NAME: - return 0; - case ZEND_ECHO: - { - zval zv; - if (Z_TYPE_P(val) != IS_STRING && zend_optimizer_eval_cast(&zv, IS_STRING, val) == SUCCESS) { - zval_ptr_dtor_nogc(val); - val = &zv; - } - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) { - MAKE_NOP(opline); - } - /* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */ - /* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */ - break; - } - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - case ZEND_FETCH_R: - case ZEND_FETCH_W: - case ZEND_FETCH_RW: - case ZEND_FETCH_IS: - case ZEND_FETCH_UNSET: - case ZEND_FETCH_FUNC_ARG: - TO_STRING_NOWARN(val); - if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) { - opline->opcode = ZEND_FAST_CONCAT; - } - /* break missing intentionally */ - default: - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - break; - } - - opline->op1_type = IS_CONST; - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { - zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); - } - return 1; -} - -int zend_optimizer_update_op2_const(zend_op_array *op_array, - zend_op *opline, - zval *val) -{ - zval tmp; - - switch (opline->opcode) { - case ZEND_ASSIGN_REF: - case ZEND_FAST_CALL: - return 0; - case ZEND_FETCH_CLASS: - if ((opline + 1)->opcode == ZEND_INSTANCEOF && - (opline + 1)->op2.var == opline->result.var) { - return 0; - } - /* break missing intentionally */ - case ZEND_INSTANCEOF: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->extended_value = alloc_cache_slots(op_array, 1); - break; - case ZEND_INIT_FCALL_BY_NAME: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->result.num = alloc_cache_slots(op_array, 1); - break; - case ZEND_ASSIGN_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_REF: - case ZEND_FETCH_STATIC_PROP_R: - case ZEND_FETCH_STATIC_PROP_W: - case ZEND_FETCH_STATIC_PROP_RW: - case ZEND_FETCH_STATIC_PROP_IS: - case ZEND_FETCH_STATIC_PROP_UNSET: - case ZEND_FETCH_STATIC_PROP_FUNC_ARG: - case ZEND_UNSET_STATIC_PROP: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_PRE_INC_STATIC_PROP: - case ZEND_PRE_DEC_STATIC_PROP: - case ZEND_POST_INC_STATIC_PROP: - case ZEND_POST_DEC_STATIC_PROP: - case ZEND_ASSIGN_STATIC_PROP_OP: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - if (opline->op1_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS)); - } - break; - case ZEND_INIT_FCALL: - REQUIRES_STRING(val); - if (Z_REFCOUNT_P(val) == 1) { - zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); - } else { - ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val))); - zval_ptr_dtor_nogc(val); - val = &tmp; - } - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->result.num = alloc_cache_slots(op_array, 1); - break; - case ZEND_INIT_DYNAMIC_CALL: - if (Z_TYPE_P(val) == IS_STRING) { - if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) { - return 0; - } - - if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) { - /* Dynamic call to various special functions must stay dynamic, - * otherwise would drop a warning */ - return 0; - } - - opline->opcode = ZEND_INIT_FCALL_BY_NAME; - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->result.num = alloc_cache_slots(op_array, 1); - } else { - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - } - break; - case ZEND_INIT_METHOD_CALL: - REQUIRES_STRING(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - opline->result.num = alloc_cache_slots(op_array, 2); - break; - case ZEND_INIT_STATIC_METHOD_CALL: - REQUIRES_STRING(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - if (opline->op1_type != IS_CONST) { - opline->result.num = alloc_cache_slots(op_array, 2); - } - break; - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_REF: - case ZEND_FETCH_OBJ_R: - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_UNSET_OBJ: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_ASSIGN_OBJ_OP: - TO_STRING_NOWARN(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 3); - break; - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - TO_STRING_NOWARN(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY); - break; - case ZEND_ASSIGN_DIM_OP: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ASSIGN_DIM: - case ZEND_UNSET_DIM: - case ZEND_FETCH_DIM_R: - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_IS: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_LIST_R: - case ZEND_FETCH_LIST_W: - if (Z_TYPE_P(val) == IS_STRING) { - zend_ulong index; - - if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { - ZVAL_LONG(&tmp, index); - opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - zend_string_hash_val(Z_STR_P(val)); - zend_optimizer_add_literal(op_array, val); - Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE; - break; - } - } - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - break; - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_INIT_ARRAY: - if (Z_TYPE_P(val) == IS_STRING) { - zend_ulong index; - if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { - zval_ptr_dtor_nogc(val); - ZVAL_LONG(val, index); - } - } - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - break; - case ZEND_ROPE_INIT: - case ZEND_ROPE_ADD: - case ZEND_ROPE_END: - case ZEND_CONCAT: - case ZEND_FAST_CONCAT: - TO_STRING_NOWARN(val); - if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) { - opline->opcode = ZEND_FAST_CONCAT; - } - /* break missing intentionally */ - default: - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - break; - } - - opline->op2_type = IS_CONST; - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); - } - return 1; -} - -int zend_optimizer_replace_by_const(zend_op_array *op_array, - zend_op *opline, - zend_uchar type, - uint32_t var, - zval *val) -{ - zend_op *end = op_array->opcodes + op_array->last; - - while (opline < end) { - if (opline->op1_type == type && - opline->op1.var == var) { - switch (opline->opcode) { - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM: - case ZEND_SEPARATE: - case ZEND_RETURN_BY_REF: - return 0; - case ZEND_SEND_VAR: - opline->extended_value = 0; - opline->opcode = ZEND_SEND_VAL; - break; - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - opline->extended_value = 0; - opline->opcode = ZEND_SEND_VAL_EX; - break; - case ZEND_SEND_VAR_NO_REF: - return 0; - case ZEND_SEND_VAR_NO_REF_EX: - opline->opcode = ZEND_SEND_VAL; - break; - case ZEND_SEND_USER: - opline->opcode = ZEND_SEND_VAL_EX; - break; - /* In most cases IS_TMP_VAR operand may be used only once. - * The operands are usually destroyed by the opcode handler. - * ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand - * unchanged, and allows its reuse. these instructions - * usually terminated by ZEND_FREE that finally kills the value. - */ - case ZEND_FETCH_LIST_R: { - zend_op *m = opline; - - do { - if (m->opcode == ZEND_FETCH_LIST_R && - m->op1_type == type && - m->op1.var == var) { - zval v; - ZVAL_COPY(&v, val); - if (Z_TYPE(v) == IS_STRING) { - zend_string_hash_val(Z_STR(v)); - } - m->op1.constant = zend_optimizer_add_literal(op_array, &v); - m->op1_type = IS_CONST; - } - m++; - } while (m->opcode != ZEND_FREE || m->op1_type != type || m->op1.var != var); - - ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var); - MAKE_NOP(m); - zval_ptr_dtor_nogc(val); - return 1; - } - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - case ZEND_CASE: - case ZEND_CASE_STRICT: { - zend_op *end = op_array->opcodes + op_array->last; - while (opline < end) { - if (opline->op1_type == type && opline->op1.var == var) { - if ( - opline->opcode == ZEND_CASE - || opline->opcode == ZEND_CASE_STRICT - || opline->opcode == ZEND_SWITCH_LONG - || opline->opcode == ZEND_SWITCH_STRING - || opline->opcode == ZEND_MATCH - ) { - zval v; - - if (opline->opcode == ZEND_CASE) { - opline->opcode = ZEND_IS_EQUAL; - } else if (opline->opcode == ZEND_CASE_STRICT) { - opline->opcode = ZEND_IS_IDENTICAL; - } - ZVAL_COPY(&v, val); - if (Z_TYPE(v) == IS_STRING) { - zend_string_hash_val(Z_STR(v)); - } - opline->op1.constant = zend_optimizer_add_literal(op_array, &v); - opline->op1_type = IS_CONST; - } else if (opline->opcode == ZEND_FREE) { - if (opline->extended_value == ZEND_FREE_SWITCH) { - /* We found the end of the switch. */ - MAKE_NOP(opline); - break; - } - - ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN); - MAKE_NOP(opline); - } else { - ZEND_UNREACHABLE(); - } - } - opline++; - } - zval_ptr_dtor_nogc(val); - return 1; - } - case ZEND_VERIFY_RETURN_TYPE: { - zend_arg_info *ret_info = op_array->arg_info - 1; - if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val)) - || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { - return 0; - } - MAKE_NOP(opline); - - /* zend_handle_loops_and_finally may inserts other oplines */ - do { - ++opline; - } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF); - ZEND_ASSERT(opline->op1.var == var); - - break; - } - default: - break; - } - return zend_optimizer_update_op1_const(op_array, opline, val); - } - - if (opline->op2_type == type && - opline->op2.var == var) { - return zend_optimizer_update_op2_const(op_array, opline, val); - } - opline++; - } - - return 1; -} - -/* Update jump offsets after a jump was migrated to another opline */ -void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *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: - case ZEND_JMP_NULL: - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); - break; - 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; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline)); - } - break; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); - zval *zv; - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))); - } ZEND_HASH_FOREACH_END(); - 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; - } - } -} - -/* Shift jump offsets based on shiftlist */ -void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) { - 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: - case ZEND_JMP_NULL: - 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_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - 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_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - 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; - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - { - HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline)); - zval *zv; - ZEND_HASH_FOREACH_VAL(jumptable, zv) { - Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]); - } ZEND_HASH_FOREACH_END(); - 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; - } - } -} - -static zend_class_entry *get_class_entry_from_op1( - zend_script *script, zend_op_array *op_array, zend_op *opline) { - if (opline->op1_type == IS_CONST) { - zval *op1 = CRT_CONSTANT(opline->op1); - if (Z_TYPE_P(op1) == IS_STRING) { - zend_string *class_name = Z_STR_P(op1 + 1); - zend_class_entry *ce; - if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) { - return ce; - } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) { - if (ce->type == ZEND_INTERNAL_CLASS) { - return ce; - } else if (ce->type == ZEND_USER_CLASS && - ce->info.user.filename && - ce->info.user.filename == op_array->filename) { - return ce; - } - } - } - } else if (opline->op1_type == IS_UNUSED && op_array->scope - && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT) - && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { - return op_array->scope; - } - return NULL; -} - -zend_function *zend_optimizer_get_called_func( - zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype) -{ - *is_prototype = 0; - switch (opline->opcode) { - case ZEND_INIT_FCALL: - { - zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2)); - zend_function *func; - if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) { - return func; - } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - return func; - } else if (func->type == ZEND_USER_FUNCTION && - func->op_array.filename && - func->op_array.filename == op_array->filename) { - return func; - } - } - break; - } - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) { - zval *function_name = CRT_CONSTANT(opline->op2) + 1; - zend_function *func; - if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) { - return func; - } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - return func; - } else if (func->type == ZEND_USER_FUNCTION && - func->op_array.filename && - func->op_array.filename == op_array->filename) { - return func; - } - } - } - break; - case ZEND_INIT_STATIC_METHOD_CALL: - if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) { - zend_class_entry *ce = get_class_entry_from_op1( - script, op_array, opline); - if (ce) { - zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1); - zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name); - if (fbc) { - bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0; - bool same_scope = fbc->common.scope == op_array->scope; - if (is_public || same_scope) { - return fbc; - } - } - } - } - break; - case ZEND_INIT_METHOD_CALL: - if (opline->op1_type == IS_UNUSED - && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING - && op_array->scope - && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) - && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { - zend_string *method_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1); - zend_function *fbc = zend_hash_find_ptr( - &op_array->scope->function_table, method_name); - if (fbc) { - bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0; - bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0; - bool same_scope = fbc->common.scope == op_array->scope; - if (is_private) { - /* Only use private method if in the same scope. We can't even use it - * as a prototype, as it may be overridden with changed signature. */ - return same_scope ? fbc : NULL; - } - /* If the method is non-final, it may be overridden, - * but only with a compatible method signature. */ - *is_prototype = !is_final; - return fbc; - } - } - break; - case ZEND_NEW: - { - zend_class_entry *ce = get_class_entry_from_op1( - script, op_array, opline); - if (ce && ce->type == ZEND_USER_CLASS) { - return ce->constructor; - } - break; - } - } - return NULL; -} - -uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) { - if (zend_string_equals_literal(name, "extract")) { - return ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if (zend_string_equals_literal(name, "compact")) { - return ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if (zend_string_equals_literal(name, "get_defined_vars")) { - return ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if (zend_string_equals_literal(name, "db2_execute")) { - return ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if (zend_string_equals_literal(name, "func_num_args")) { - return ZEND_FUNC_VARARG; - } else if (zend_string_equals_literal(name, "func_get_arg")) { - return ZEND_FUNC_VARARG; - } else if (zend_string_equals_literal(name, "func_get_args")) { - return ZEND_FUNC_VARARG; - } else { - return 0; - } -} - -zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) { - uint32_t var = free_opline->op1.var; - ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline)); - - while (--free_opline >= op_array->opcodes) { - if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) { - return free_opline; - } - } - return NULL; -} - -static void zend_optimize(zend_op_array *op_array, - zend_optimizer_ctx *ctx) -{ - if (op_array->type == ZEND_EVAL_CODE) { - return; - } - - if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) { - zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); - } - - /* pass 1 (Simple local optimizations) - * - persistent constant substitution (true, false, null, etc) - * - constant casting (ADD expects numbers, CONCAT strings, etc) - * - constant expression evaluation - * - optimize constant conditional JMPs - * - pre-evaluate constant function calls - * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM - */ - if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) { - zend_optimizer_pass1(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) { - zend_dump_op_array(op_array, 0, "after pass 1", NULL); - } - } - - /* pass 3: (Jump optimization) - * - optimize series of JMPs - */ - if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) { - zend_optimizer_pass3(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) { - zend_dump_op_array(op_array, 0, "after pass 3", NULL); - } - } - - /* pass 4: - * - INIT_FCALL_BY_NAME -> DO_FCALL - */ - if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) { - zend_optimize_func_calls(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) { - zend_dump_op_array(op_array, 0, "after pass 4", NULL); - } - } - - /* pass 5: - * - CFG optimization - */ - if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) { - zend_optimize_cfg(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) { - zend_dump_op_array(op_array, 0, "after pass 5", NULL); - } - } - - /* pass 6: - * - DFA optimization - */ - if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) && - !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) { - zend_optimize_dfa(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) { - zend_dump_op_array(op_array, 0, "after pass 6", NULL); - } - } - - /* pass 9: - * - Optimize temp variables usage - */ - if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) { - zend_optimize_temporary_variables(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) { - zend_dump_op_array(op_array, 0, "after pass 9", NULL); - } - } - - /* pass 10: - * - remove NOPs - */ - if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) { - zend_optimizer_nop_removal(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) { - zend_dump_op_array(op_array, 0, "after pass 10", NULL); - } - } - - /* pass 11: - * - Compact literals table - */ - if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) && - (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) || - !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) { - zend_optimizer_compact_literals(op_array, ctx); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) { - zend_dump_op_array(op_array, 0, "after pass 11", NULL); - } - } - - if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) && - (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) || - !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) { - zend_optimizer_compact_vars(op_array); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) { - zend_dump_op_array(op_array, 0, "after pass 13", NULL); - } - } - - if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) { - return; - } - - if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) { - zend_dump_op_array(op_array, 0, "after optimizer", NULL); - } -} - -static void zend_revert_pass_two(zend_op_array *op_array) -{ - zend_op *opline, *end; - - ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0); - - opline = op_array->opcodes; - end = opline + op_array->last; - while (opline < end) { - if (opline->op1_type == IS_CONST) { - ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1); - } - if (opline->op2_type == IS_CONST) { - ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2); - } - /* reset smart branch flags IS_SMART_BRANCH_JMP[N]Z */ - opline->result_type &= (IS_TMP_VAR|IS_VAR|IS_CV|IS_CONST); - opline++; - } -#if !ZEND_USE_ABS_CONST_ADDR - if (op_array->literals) { - zval *literals = emalloc(sizeof(zval) * op_array->last_literal); - memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal); - op_array->literals = literals; - } -#endif - - op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO; -} - -static void zend_redo_pass_two(zend_op_array *op_array) -{ - zend_op *opline, *end; -#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR - zend_op *old_opcodes = op_array->opcodes; -#endif - - ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0); - -#if !ZEND_USE_ABS_CONST_ADDR - if (op_array->last_literal) { - op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) + - sizeof(zval) * op_array->last_literal); - memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16), - op_array->literals, sizeof(zval) * op_array->last_literal); - efree(op_array->literals); - op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16)); - } else { - if (op_array->literals) { - efree(op_array->literals); - } - op_array->literals = NULL; - } -#endif - - opline = op_array->opcodes; - end = opline + op_array->last; - while (opline < end) { - if (opline->op1_type == IS_CONST) { - ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1); - } - if (opline->op2_type == IS_CONST) { - ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2); - } - /* fix jumps to point to new array */ - switch (opline->opcode) { -#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR - case ZEND_JMP: - case ZEND_FAST_CALL: - opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes]; - break; - case ZEND_JMPZNZ: - /* relative extended_value don't have to be changed */ - /* break omitted intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_ASSERT_CHECK: - case ZEND_JMP_NULL: - opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - /* relative extended_value don't have to be changed */ - break; -#endif - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_INSTANCEOF: - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - case ZEND_IN_ARRAY: - case ZEND_ARRAY_KEY_EXISTS: - if (opline->result_type & IS_TMP_VAR) { - /* reinitialize result_type of smart branch instructions */ - if (opline + 1 < end) { - if ((opline+1)->opcode == ZEND_JMPZ - && (opline+1)->op1_type == IS_TMP_VAR - && (opline+1)->op1.var == opline->result.var) { - opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR; - } else if ((opline+1)->opcode == ZEND_JMPNZ - && (opline+1)->op1_type == IS_TMP_VAR - && (opline+1)->op1.var == opline->result.var) { - opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR; - } - } - } - break; - } - ZEND_VM_SET_OPCODE_HANDLER(opline); - opline++; - } - - op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; -} - -static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) -{ - zend_op *opline, *end; -#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR - zend_op *old_opcodes = op_array->opcodes; -#endif - - ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0); - -#if !ZEND_USE_ABS_CONST_ADDR - if (op_array->last_literal) { - op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) + - sizeof(zval) * op_array->last_literal); - memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16), - op_array->literals, sizeof(zval) * op_array->last_literal); - efree(op_array->literals); - op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16)); - } else { - if (op_array->literals) { - efree(op_array->literals); - } - op_array->literals = NULL; - } -#endif - - opline = op_array->opcodes; - end = opline + op_array->last; - while (opline < end) { - zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; - uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)); - uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)); - uint32_t res_info = - (opline->opcode == ZEND_PRE_INC || - opline->opcode == ZEND_PRE_DEC || - opline->opcode == ZEND_POST_INC || - opline->opcode == ZEND_POST_DEC) ? - ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) : - (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))); - - if (opline->op1_type == IS_CONST) { - ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1); - } - if (opline->op2_type == IS_CONST) { - ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2); - } - - /* fix jumps to point to new array */ - switch (opline->opcode) { -#if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR - case ZEND_JMP: - case ZEND_FAST_CALL: - opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes]; - break; - case ZEND_JMPZNZ: - /* relative extended_value don't have to be changed */ - /* break omitted intentionally */ - case ZEND_JMPZ: - case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - case ZEND_ASSERT_CHECK: - case ZEND_JMP_NULL: - opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; - break; - case ZEND_CATCH: - if (!(opline->extended_value & ZEND_LAST_CATCH)) { - opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes]; - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - case ZEND_MATCH: - /* relative extended_value don't have to be changed */ - break; -#endif - case ZEND_IS_IDENTICAL: - case ZEND_IS_NOT_IDENTICAL: - case ZEND_IS_EQUAL: - case ZEND_IS_NOT_EQUAL: - case ZEND_IS_SMALLER: - case ZEND_IS_SMALLER_OR_EQUAL: - case ZEND_CASE: - case ZEND_CASE_STRICT: - case ZEND_ISSET_ISEMPTY_CV: - case ZEND_ISSET_ISEMPTY_VAR: - case ZEND_ISSET_ISEMPTY_DIM_OBJ: - case ZEND_ISSET_ISEMPTY_PROP_OBJ: - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - case ZEND_INSTANCEOF: - case ZEND_TYPE_CHECK: - case ZEND_DEFINED: - case ZEND_IN_ARRAY: - case ZEND_ARRAY_KEY_EXISTS: - if (opline->result_type & IS_TMP_VAR) { - /* reinitialize result_type of smart branch instructions */ - if (opline + 1 < end) { - if ((opline+1)->opcode == ZEND_JMPZ - && (opline+1)->op1_type == IS_TMP_VAR - && (opline+1)->op1.var == opline->result.var) { - opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR; - } else if ((opline+1)->opcode == ZEND_JMPNZ - && (opline+1)->op1_type == IS_TMP_VAR - && (opline+1)->op1.var == opline->result.var) { - opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR; - } - } - } - break; - } - zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info); - opline++; - } - - op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; -} - -static void zend_optimize_op_array(zend_op_array *op_array, - zend_optimizer_ctx *ctx) -{ - /* Revert pass_two() */ - zend_revert_pass_two(op_array); - - /* Do actual optimizations */ - zend_optimize(op_array, ctx); - - /* Redo pass_two() */ - zend_redo_pass_two(op_array); - - if (op_array->live_range) { - zend_recalc_live_ranges(op_array, NULL); - } -} - -static void zend_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(opline, opline->op2))); - if (func) { - opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func); - } - } - opline++; - } -} - -static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) -{ - zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - - if (func_info) { - zend_call_info *call_info =func_info->callee_info; - - while (call_info) { - zend_op *opline = call_info->caller_init_opline; - - if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) { - opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func); - } - call_info = call_info->next_callee; - } - } -} - -static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { - zend_func_info *func_info = ZEND_FUNC_INFO(op_array); - zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes]; - int ssa_var = ssa_op->result_def; - if (ssa_var < 0) { - /* Be conservative. */ - return 1; - } - - /* If the variable is used by a PHI, this may be the assignment of the final branch of a - * ternary/etc structure. While this is where the live range starts, the value from the other - * branch may also be used. As such, use the type of the PHI node for the following check. */ - if (func_info->ssa.vars[ssa_var].phi_use_chain) { - ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var; - } - - uint32_t type = func_info->ssa.var_info[ssa_var].type; - return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; -} - -void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context) -{ - zend_class_entry *ce; - zend_string *key; - zend_op_array *op_array; - - func(&script->main_op_array, context); - - ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - func(op_array, context); - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { - if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { - continue; - } - ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { - if (op_array->scope == ce - && op_array->type == ZEND_USER_FUNCTION - && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - func(op_array, context); - } - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FOREACH_END(); -} - -static void step_optimize_op_array(zend_op_array *op_array, void *context) { - zend_optimize_op_array(op_array, (zend_optimizer_ctx *) context); -} - -static void step_adjust_fcall_stack_size(zend_op_array *op_array, void *context) { - zend_adjust_fcall_stack_size(op_array, (zend_optimizer_ctx *) context); -} - -static void step_dump_after_optimizer(zend_op_array *op_array, void *context) { - zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); -} - -int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) -{ - zend_class_entry *ce; - zend_string *key; - zend_op_array *op_array; - zend_string *name; - zend_optimizer_ctx ctx; - zend_call_graph call_graph; - - ctx.arena = zend_arena_create(64 * 1024); - ctx.script = script; - ctx.constants = NULL; - ctx.optimization_level = optimization_level; - ctx.debug_level = debug_level; - - if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) && - (ZEND_OPTIMIZER_PASS_7 & optimization_level) && - zend_build_call_graph(&ctx.arena, script, &call_graph) == SUCCESS) { - /* Optimize using call-graph */ - int i; - zend_func_info *func_info; - - for (i = 0; i < call_graph.op_arrays_count; i++) { - zend_revert_pass_two(call_graph.op_arrays[i]); - zend_optimize(call_graph.op_arrays[i], &ctx); - } - - zend_analyze_call_graph(&ctx.arena, script, &call_graph); - - for (i = 0; i < call_graph.op_arrays_count; i++) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); - if (func_info) { - func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]); - if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info); - } - } - } - - for (i = 0; i < call_graph.op_arrays_count; i++) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); - if (func_info) { - if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) { - func_info->flags = func_info->ssa.cfg.flags; - } else { - ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); - } - } - } - - //TODO: perform inner-script inference??? - for (i = 0; i < call_graph.op_arrays_count; i++) { - func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); - if (func_info) { - zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map); - } - } - - if (debug_level & ZEND_DUMP_AFTER_PASS_7) { - for (i = 0; i < call_graph.op_arrays_count; i++) { - zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL); - } - } - - if (ZEND_OPTIMIZER_PASS_11 & optimization_level) { - for (i = 0; i < call_graph.op_arrays_count; i++) { - zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx); - if (debug_level & ZEND_DUMP_AFTER_PASS_11) { - zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL); - } - } - } - - if (ZEND_OPTIMIZER_PASS_13 & optimization_level) { - for (i = 0; i < call_graph.op_arrays_count; i++) { - zend_optimizer_compact_vars(call_graph.op_arrays[i]); - if (debug_level & ZEND_DUMP_AFTER_PASS_13) { - zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL); - } - } - } - - if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { - for (i = 0; i < call_graph.op_arrays_count; i++) { - zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]); - } - } - - for (i = 0; i < call_graph.op_arrays_count; i++) { - op_array = call_graph.op_arrays[i]; - func_info = ZEND_FUNC_INFO(op_array); - if (func_info && func_info->ssa.var_info) { - zend_redo_pass_two_ex(op_array, &func_info->ssa); - if (op_array->live_range) { - zend_recalc_live_ranges(op_array, needs_live_range); - } - } else { - zend_redo_pass_two(op_array); - if (op_array->live_range) { - zend_recalc_live_ranges(op_array, NULL); - } - } - } - - for (i = 0; i < call_graph.op_arrays_count; i++) { - ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); - } - } else { - zend_foreach_op_array(script, step_optimize_op_array, &ctx); - - if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { - zend_foreach_op_array(script, step_adjust_fcall_stack_size, &ctx); - } - } - - ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { - if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) { - continue; - } - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) { - zend_op_array *orig_op_array = - zend_hash_find_ptr(&op_array->scope->function_table, name); - - ZEND_ASSERT(orig_op_array != NULL); - if (orig_op_array != op_array) { - uint32_t fn_flags = op_array->fn_flags; - zend_function *prototype = op_array->prototype; - HashTable *ht = op_array->static_variables; - - *op_array = *orig_op_array; - op_array->fn_flags = fn_flags; - op_array->prototype = prototype; - op_array->static_variables = ht; - } - } - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FOREACH_END(); - - if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && - (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { - zend_foreach_op_array(script, step_dump_after_optimizer, NULL); - } - - if (ctx.constants) { - zend_hash_destroy(ctx.constants); - } - zend_arena_destroy(ctx.arena); - - return 1; -} - -int zend_optimizer_startup(void) -{ - return zend_func_info_startup(); -} - -int zend_optimizer_shutdown(void) -{ - return zend_func_info_shutdown(); -} diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h deleted file mode 100644 index 04528d33e2..0000000000 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_OPTIMIZER_H -#define ZEND_OPTIMIZER_H - -#include "zend.h" -#include "zend_compile.h" - -#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */ -#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */ -#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */ -#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */ -#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */ -#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */ -#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */ -#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */ -#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */ -#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */ -#define ZEND_OPTIMIZER_PASS_11 (1<<10) /* Merge equal constants */ -#define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */ -#define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */ -#define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */ -#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */ -#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */ - -#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */ - -#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF - -#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF" - - -#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1 -#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2 -#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3 -#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4 -#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5 -#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6 -#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7 -#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8 -#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9 -#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10 -#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11 -#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12 -#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13 -#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14 - -#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16) -#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17) - -#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18) -#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19) -#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20) - -#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21) -#define ZEND_DUMP_AFTER_DFA_PASS (1<<22) -#define ZEND_DUMP_DFA_CFG (1<<23) -#define ZEND_DUMP_DFA_DOMINATORS (1<<24) -#define ZEND_DUMP_DFA_LIVENESS (1<<25) -#define ZEND_DUMP_DFA_PHI (1<<26) -#define ZEND_DUMP_DFA_SSA (1<<27) -#define ZEND_DUMP_DFA_SSA_VARS (1<<28) -#define ZEND_DUMP_SCCP (1<<29) - -typedef struct _zend_script { - zend_string *filename; - zend_op_array main_op_array; - HashTable function_table; - HashTable class_table; - uint32_t first_early_binding_opline; /* the linked list of delayed declarations */ -} zend_script; - -int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level); -int zend_optimizer_startup(void); -int zend_optimizer_shutdown(void); - -#endif diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h deleted file mode 100644 index 911eb79e64..0000000000 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_OPTIMIZER_INTERNAL_H -#define ZEND_OPTIMIZER_INTERNAL_H - -#include "zend_ssa.h" -#include "zend_func_info.h" - -#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant] -#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1) -#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant] -#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2) - -#define VAR_NUM(v) EX_VAR_TO_NUM(v) -#define NUM_VAR(v) EX_NUM_TO_VAR(v) - -#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ) -#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ) -#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX) -#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX) - -#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED) -#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var) - -typedef struct _zend_optimizer_ctx { - zend_arena *arena; - zend_script *script; - HashTable *constants; - zend_long optimization_level; - zend_long debug_level; -} zend_optimizer_ctx; - -#define LITERAL_LONG(op, val) do { \ - zval _c; \ - ZVAL_LONG(&_c, val); \ - op.constant = zend_optimizer_add_literal(op_array, &_c); \ - } while (0) - -#define LITERAL_BOOL(op, val) do { \ - zval _c; \ - ZVAL_BOOL(&_c, val); \ - op.constant = zend_optimizer_add_literal(op_array, &_c); \ - } while (0) - -#define literal_dtor(zv) do { \ - zval_ptr_dtor_nogc(zv); \ - ZVAL_NULL(zv); \ - } while (0) - -#define COPY_NODE(target, src) do { \ - target ## _type = src ## _type; \ - target = src; \ - } while (0) - -static inline bool zend_optimizer_is_loop_var_free(const zend_op *opline) { - return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN) - || (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH); -} - -int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv); -int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy); -void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value); -int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value); -int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2); -int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1); -int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1); -int zend_optimizer_eval_strlen(zval *result, zval *op1); -int zend_optimizer_update_op1_const(zend_op_array *op_array, - zend_op *opline, - zval *val); -int zend_optimizer_update_op2_const(zend_op_array *op_array, - zend_op *opline, - zval *val); -int zend_optimizer_replace_by_const(zend_op_array *op_array, - zend_op *opline, - zend_uchar type, - uint32_t var, - zval *val); -zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline); - -void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx); -int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa); -void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map); -void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx); -void zend_optimizer_compact_vars(zend_op_array *op_array); -zend_function *zend_optimizer_get_called_func( - zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype); -uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args); -void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline); -void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist); -int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_arrya, zend_ssa *ssa, zend_call_info **call_map); -int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects); -int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa); - -typedef void (*zend_op_array_func_t)(zend_op_array *, void *context); -void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context); - -#endif diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c deleted file mode 100644 index ad3ff7022b..0000000000 --- a/ext/opcache/Optimizer/zend_ssa.c +++ /dev/null @@ -1,1628 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, SSA - Static Single Assignment Form | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - | Nikita Popov <nikic@php.net> | - +----------------------------------------------------------------------+ -*/ - -#include "php.h" -#include "zend_compile.h" -#include "zend_dfg.h" -#include "zend_ssa.h" -#include "zend_dump.h" -#include "zend_inference.h" -#include "Optimizer/zend_optimizer_internal.h" - -static bool dominates(const zend_basic_block *blocks, int a, int b) { - while (blocks[b].level > blocks[a].level) { - b = blocks[b].idom; - } - return a == b; -} - -static bool will_rejoin( - const zend_cfg *cfg, const zend_dfg *dfg, const zend_basic_block *block, - int other_successor, int exclude, int var) { - int i; - for (i = 0; i < block->predecessors_count; i++) { - int predecessor = cfg->predecessors[block->predecessor_offset + i]; - if (predecessor == exclude) { - continue; - } - - /* The variable is changed in this predecessor, - * so we will not rejoin with the original value. */ - // TODO: This should not be limited to the direct predecessor block. - if (DFG_ISSET(dfg->def, dfg->size, predecessor, var)) { - continue; - } - - /* The other successor dominates this predecessor, - * so we will get the original value from it. */ - if (dominates(cfg->blocks, other_successor, predecessor)) { - return 1; - } - } - return 0; -} - -static bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */ -{ - zend_basic_block *from_block, *to_block; - int other_successor; - - if (!DFG_ISSET(dfg->in, dfg->size, to, var)) { - /* Variable is not live, certainly won't benefit from pi */ - return 0; - } - - /* Make sure that both successors of the from block aren't the same. Pi nodes are associated - * with predecessor blocks, so we can't distinguish which edge the pi belongs to. */ - from_block = &ssa->cfg.blocks[from]; - ZEND_ASSERT(from_block->successors_count == 2); - if (from_block->successors[0] == from_block->successors[1]) { - return 0; - } - - to_block = &ssa->cfg.blocks[to]; - if (to_block->predecessors_count == 1) { - /* Always place pi if one predecessor (an if branch) */ - return 1; - } - - /* Check whether we will rejoin with the original value coming from the other successor, - * in which case the pi node will not have an effect. */ - other_successor = from_block->successors[0] == to - ? from_block->successors[1] : from_block->successors[0]; - return !will_rejoin(&ssa->cfg, dfg, to_block, other_successor, from, var); -} -/* }}} */ - -static zend_ssa_phi *add_pi( - zend_arena **arena, const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, - int from, int to, int var) /* {{{ */ -{ - zend_ssa_phi *phi; - if (!needs_pi(op_array, dfg, ssa, from, to, var)) { - return NULL; - } - - phi = zend_arena_calloc(arena, 1, - ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) + - ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count) + - sizeof(void*) * ssa->cfg.blocks[to].predecessors_count); - phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi))); - memset(phi->sources, 0xff, sizeof(int) * ssa->cfg.blocks[to].predecessors_count); - phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[to].predecessors_count)); - - phi->pi = from; - phi->var = var; - phi->ssa_var = -1; - phi->next = ssa->blocks[to].phis; - ssa->blocks[to].phis = phi; - - /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that - * this is not entirely accurate, because the pi is actually placed along the edge from->to. - * If there is a back-edge to "to" this may result in non-minimal SSA form. */ - DFG_SET(dfg->def, dfg->size, to, var); - - /* If there are multiple predecessors in the target block, we need to place a phi there. - * However this can (generally) not be expressed in terms of dominance frontiers, so place it - * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */ - if (ssa->cfg.blocks[to].predecessors_count > 1) { - DFG_SET(dfg->use, dfg->size, to, var); - } - - return phi; -} -/* }}} */ - -static void pi_range( - zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max, - char underflow, char overflow, char negative) /* {{{ */ -{ - zend_ssa_range_constraint *constraint = &phi->constraint.range; - constraint->min_var = min_var; - constraint->max_var = max_var; - constraint->min_ssa_var = -1; - constraint->max_ssa_var = -1; - constraint->range.min = min; - constraint->range.max = max; - constraint->range.underflow = underflow; - constraint->range.overflow = overflow; - constraint->negative = negative ? NEG_INIT : NEG_NONE; - phi->has_range_constraint = 1; -} -/* }}} */ - -static inline void pi_range_equals(zend_ssa_phi *phi, int var, zend_long val) { - pi_range(phi, var, var, val, val, 0, 0, 0); -} -static inline void pi_range_not_equals(zend_ssa_phi *phi, int var, zend_long val) { - pi_range(phi, var, var, val, val, 0, 0, 1); -} -static inline void pi_range_min(zend_ssa_phi *phi, int var, zend_long val) { - pi_range(phi, var, -1, val, ZEND_LONG_MAX, 0, 1, 0); -} -static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) { - pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0); -} - -static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) { - phi->has_range_constraint = 0; - phi->constraint.type.ce = NULL; - phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; - phi->constraint.type.type_mask |= type_mask; - if (type_mask & MAY_BE_NULL) { - phi->constraint.type.type_mask |= MAY_BE_UNDEF; - } -} -static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) { - uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - pi_type_mask(phi, ~type_mask & relevant); -} -static inline uint32_t mask_for_type_check(uint32_t type) { - if (type & MAY_BE_ARRAY) { - return type | (MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF); - } else { - return type; - } -} - -/* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand. - * This negated adjustment is what is written into the "adjustment" parameter. */ -static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */ -{ - zend_op *op = opline; - zval *zv; - - while (op != op_array->opcodes) { - op--; - if (op->result_type != IS_TMP_VAR || op->result.var != var_num) { - continue; - } - - if (op->opcode == ZEND_POST_DEC) { - if (op->op1_type == IS_CV) { - *adjustment = -1; - return EX_VAR_TO_NUM(op->op1.var); - } - } else if (op->opcode == ZEND_POST_INC) { - if (op->op1_type == IS_CV) { - *adjustment = 1; - return EX_VAR_TO_NUM(op->op1.var); - } - } else if (op->opcode == ZEND_ADD) { - if (op->op1_type == IS_CV && op->op2_type == IS_CONST) { - zv = CRT_CONSTANT_EX(op_array, op, op->op2); - if (Z_TYPE_P(zv) == IS_LONG - && Z_LVAL_P(zv) != ZEND_LONG_MIN) { - *adjustment = -Z_LVAL_P(zv); - return EX_VAR_TO_NUM(op->op1.var); - } - } else if (op->op2_type == IS_CV && op->op1_type == IS_CONST) { - zv = CRT_CONSTANT_EX(op_array, op, op->op1); - if (Z_TYPE_P(zv) == IS_LONG - && Z_LVAL_P(zv) != ZEND_LONG_MIN) { - *adjustment = -Z_LVAL_P(zv); - return EX_VAR_TO_NUM(op->op2.var); - } - } - } else if (op->opcode == ZEND_SUB) { - if (op->op1_type == IS_CV && op->op2_type == IS_CONST) { - zv = CRT_CONSTANT_EX(op_array, op, op->op2); - if (Z_TYPE_P(zv) == IS_LONG) { - *adjustment = Z_LVAL_P(zv); - return EX_VAR_TO_NUM(op->op1.var); - } - } - } - break; - } - return -1; -} -/* }}} */ - -/* e-SSA construction: Pi placement (Pi is actually a Phi with single - * source and constraint). - * Order of Phis is important, Pis must be placed before Phis - */ -static void place_essa_pis( - zend_arena **arena, const zend_script *script, const zend_op_array *op_array, - uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ { - zend_basic_block *blocks = ssa->cfg.blocks; - int j, blocks_count = ssa->cfg.blocks_count; - for (j = 0; j < blocks_count; j++) { - zend_ssa_phi *pi; - zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1; - int bt; /* successor block number if a condition is true */ - int bf; /* successor block number if a condition is false */ - - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) { - continue; - } - /* the last instruction of basic block is conditional branch, - * based on comparison of CV(s) - */ - switch (opline->opcode) { - case ZEND_JMPZ: - case ZEND_JMPZNZ: - bf = blocks[j].successors[0]; - bt = blocks[j].successors[1]; - break; - case ZEND_JMPNZ: - bt = blocks[j].successors[0]; - bf = blocks[j].successors[1]; - break; - case ZEND_COALESCE: - if (opline->op1_type == IS_CV) { - int var = EX_VAR_TO_NUM(opline->op1.var); - if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[0], var))) { - pi_not_type_mask(pi, MAY_BE_NULL); - } - } - continue; - case ZEND_JMP_NULL: - if (opline->op1_type == IS_CV) { - int var = EX_VAR_TO_NUM(opline->op1.var); - if ((pi = add_pi(arena, op_array, dfg, ssa, j, blocks[j].successors[1], var))) { - pi_not_type_mask(pi, MAY_BE_NULL); - } - } - continue; - default: - continue; - } - if (opline->op1_type == IS_TMP_VAR && - ((opline-1)->opcode == ZEND_IS_EQUAL || - (opline-1)->opcode == ZEND_IS_NOT_EQUAL || - (opline-1)->opcode == ZEND_IS_SMALLER || - (opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) && - opline->op1.var == (opline-1)->result.var) { - int var1 = -1; - int var2 = -1; - zend_long val1 = 0; - zend_long val2 = 0; -// long val = 0; - - if ((opline-1)->op1_type == IS_CV) { - var1 = EX_VAR_TO_NUM((opline-1)->op1.var); - } else if ((opline-1)->op1_type == IS_TMP_VAR) { - var1 = find_adjusted_tmp_var( - op_array, build_flags, opline, (opline-1)->op1.var, &val2); - } - - if ((opline-1)->op2_type == IS_CV) { - var2 = EX_VAR_TO_NUM((opline-1)->op2.var); - } else if ((opline-1)->op2_type == IS_TMP_VAR) { - var2 = find_adjusted_tmp_var( - op_array, build_flags, opline, (opline-1)->op2.var, &val1); - } - - if (var1 >= 0 && var2 >= 0) { - if (!zend_sub_will_overflow(val1, val2) && !zend_sub_will_overflow(val2, val1)) { - zend_long tmp = val1; - val1 -= val2; - val2 -= tmp; - } else { - var1 = -1; - var2 = -1; - } - } else if (var1 >= 0 && var2 < 0) { - zend_long add_val2 = 0; - if ((opline-1)->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2); - - if (Z_TYPE_P(zv) == IS_LONG) { - add_val2 = Z_LVAL_P(zv); - } else if (Z_TYPE_P(zv) == IS_FALSE) { - add_val2 = 0; - } else if (Z_TYPE_P(zv) == IS_TRUE) { - add_val2 = 1; - } else { - var1 = -1; - } - } else { - var1 = -1; - } - if (!zend_add_will_overflow(val2, add_val2)) { - val2 += add_val2; - } else { - var1 = -1; - } - } else if (var1 < 0 && var2 >= 0) { - zend_long add_val1 = 0; - if ((opline-1)->op1_type == IS_CONST) { - zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1); - if (Z_TYPE_P(zv) == IS_LONG) { - add_val1 = Z_LVAL_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1)); - } else if (Z_TYPE_P(zv) == IS_FALSE) { - add_val1 = 0; - } else if (Z_TYPE_P(zv) == IS_TRUE) { - add_val1 = 1; - } else { - var2 = -1; - } - } else { - var2 = -1; - } - if (!zend_add_will_overflow(val1, add_val1)) { - val1 += add_val1; - } else { - var2 = -1; - } - } - - if (var1 >= 0) { - if ((opline-1)->opcode == ZEND_IS_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) { - pi_range_equals(pi, var2, val2); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) { - pi_range_not_equals(pi, var2, val2); - } - } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) { - pi_range_equals(pi, var2, val2); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) { - pi_range_not_equals(pi, var2, val2); - } - } else if ((opline-1)->opcode == ZEND_IS_SMALLER) { - if (val2 > ZEND_LONG_MIN) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) { - pi_range_max(pi, var2, val2-1); - } - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) { - pi_range_min(pi, var2, val2); - } - } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var1))) { - pi_range_max(pi, var2, val2); - } - if (val2 < ZEND_LONG_MAX) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var1))) { - pi_range_min(pi, var2, val2+1); - } - } - } - } - if (var2 >= 0) { - if((opline-1)->opcode == ZEND_IS_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) { - pi_range_equals(pi, var1, val1); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) { - pi_range_not_equals(pi, var1, val1); - } - } else if ((opline-1)->opcode == ZEND_IS_NOT_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) { - pi_range_equals(pi, var1, val1); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) { - pi_range_not_equals(pi, var1, val1); - } - } else if ((opline-1)->opcode == ZEND_IS_SMALLER) { - if (val1 < ZEND_LONG_MAX) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) { - pi_range_min(pi, var1, val1+1); - } - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) { - pi_range_max(pi, var1, val1); - } - } else if ((opline-1)->opcode == ZEND_IS_SMALLER_OR_EQUAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var2))) { - pi_range_min(pi, var1, val1); - } - if (val1 > ZEND_LONG_MIN) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var2))) { - pi_range_max(pi, var1, val1-1); - } - } - } - } - } else if (opline->op1_type == IS_TMP_VAR && - ((opline-1)->opcode == ZEND_POST_INC || - (opline-1)->opcode == ZEND_POST_DEC) && - opline->op1.var == (opline-1)->result.var && - (opline-1)->op1_type == IS_CV) { - int var = EX_VAR_TO_NUM((opline-1)->op1.var); - - if ((opline-1)->opcode == ZEND_POST_DEC) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_range_equals(pi, -1, -1); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_range_not_equals(pi, -1, -1); - } - } else if ((opline-1)->opcode == ZEND_POST_INC) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_range_equals(pi, -1, 1); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_range_not_equals(pi, -1, 1); - } - } - } else if (opline->op1_type == IS_TMP_VAR && - ((opline-1)->opcode == ZEND_PRE_INC || - (opline-1)->opcode == ZEND_PRE_DEC) && - opline->op1.var == (opline-1)->result.var && - (opline-1)->op1_type == IS_CV) { - int var = EX_VAR_TO_NUM((opline-1)->op1.var); - - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_range_equals(pi, -1, 0); - } - /* speculative */ - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_range_not_equals(pi, -1, 0); - } - } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK && - opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) { - int var = EX_VAR_TO_NUM((opline-1)->op1.var); - uint32_t type = (opline-1)->extended_value; - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_type_mask(pi, mask_for_type_check(type)); - } - if (type != MAY_BE_RESOURCE) { - /* is_resource() may return false for closed resources */ - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_not_type_mask(pi, mask_for_type_check(type)); - } - } - } else if (opline->op1_type == IS_TMP_VAR && - ((opline-1)->opcode == ZEND_IS_IDENTICAL - || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) && - opline->op1.var == (opline-1)->result.var) { - int var; - zval *val; - uint32_t type_mask; - if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) { - var = EX_VAR_TO_NUM((opline-1)->op1.var); - val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2); - } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) { - var = EX_VAR_TO_NUM((opline-1)->op2.var); - val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1); - } else { - continue; - } - - /* We're interested in === null/true/false comparisons here, because they eliminate - * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */ - if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) { - continue; - } - - type_mask = _const_op_type(val); - if ((opline-1)->opcode == ZEND_IS_IDENTICAL) { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_type_mask(pi, type_mask); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_not_type_mask(pi, type_mask); - } - } else { - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bf, var))) { - pi_type_mask(pi, type_mask); - } - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_not_type_mask(pi, type_mask); - } - } - } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF && - opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV && - (opline-1)->op2_type == IS_CONST) { - int var = EX_VAR_TO_NUM((opline-1)->op1.var); - zend_string *lcname = Z_STR_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op2) + 1); - zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; - if (!ce) { - ce = zend_hash_find_ptr(CG(class_table), lcname); - if (!ce || ce->type != ZEND_INTERNAL_CLASS) { - continue; - } - } - - if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { - pi_type_mask(pi, MAY_BE_OBJECT); - pi->constraint.type.ce = ce; - } - } - } -} -/* }}} */ - -static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */ -{ - const zend_op *next; - - 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->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) - } - if ((build_flags & ZEND_SSA_USE_CV_RESULTS) - && opline->result_type == IS_CV - && opline->opcode != ZEND_RECV) { - ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)]; - //USE_SSA_VAR(op_array->last_var + opline->result.var) - } - - switch (opline->opcode) { - case ZEND_ASSIGN: - 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) - } - if (opline->op1_type == IS_CV) { -add_op1_def: - 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_REF: - 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) - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_OBJ: - next = opline + 1; - if (next->op1_type & (IS_CV|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 (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) - } - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_OBJ_REF: - next = opline + 1; - if (next->op1_type & (IS_CV|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->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) - } - } - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_STATIC_PROP: - next = opline + 1; - if (next->op1_type & (IS_CV|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 ((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_ASSIGN_STATIC_PROP_REF: - next = opline + 1; - if (next->op1_type & (IS_CV|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->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_ASSIGN_STATIC_PROP_OP: - next = opline + 1; - if (next->op1_type & (IS_CV|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); - } - break; - case ZEND_ASSIGN_DIM_OP: - case ZEND_ASSIGN_OBJ_OP: - next = opline + 1; - if (next->op1_type & (IS_CV|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 (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ASSIGN_OP: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - case ZEND_BIND_GLOBAL: - case ZEND_BIND_STATIC: - case ZEND_SEND_VAR_NO_REF: - case ZEND_SEND_VAR_NO_REF_EX: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - case ZEND_SEND_REF: - case ZEND_SEND_UNPACK: - case ZEND_FE_RESET_RW: - case ZEND_MAKE_REF: - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_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_LIST_W: - if (opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_SEND_VAR: - case ZEND_CAST: - case ZEND_QM_ASSIGN: - case ZEND_JMP_SET: - case ZEND_COALESCE: - case ZEND_FE_RESET_R: - if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_ADD_ARRAY_UNPACK: - ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)]; - break; - case ZEND_ADD_ARRAY_ELEMENT: - ssa_ops[k].result_use = var[EX_VAR_TO_NUM(opline->result.var)]; - /* break missing intentionally */ - case ZEND_INIT_ARRAY: - if (((build_flags & ZEND_SSA_RC_INFERENCE) - || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) - && opline->op1_type == IS_CV) { - goto add_op1_def; - } - break; - case ZEND_YIELD: - if (opline->op1_type == IS_CV - && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) - || (build_flags & ZEND_SSA_RC_INFERENCE))) { - goto add_op1_def; - } - break; - case ZEND_UNSET_CV: - goto add_op1_def; - case ZEND_VERIFY_RETURN_TYPE: - if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { - goto add_op1_def; - } - break; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - if (opline->op2_type != IS_CV) { - ssa_ops[k].op2_use = -1; /* not used */ - } - 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_LEXICAL: - if ((opline->extended_value & ZEND_BIND_REF) || (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++; - //NEW_SSA_VAR(opline->op2.var) - } - break; - default: - break; - } - - if (opline->result_type & (IS_CV|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) - } - - return ssa_vars_count; -} -/* }}} */ - -int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var) /* {{{ */ -{ - return _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var); -} -/* }}} */ - -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; - zend_op *opline, *end; - int *tmp = NULL; - ALLOCA_FLAG(use_heap = 0); - - // 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); - } - - opline = op_array->opcodes + blocks[n].start; - end = opline + blocks[n].len; - for (; opline < end; opline++) { - uint32_t k = opline - op_array->opcodes; - if (opline->opcode != ZEND_OP_DATA) { - ssa_vars_count = _zend_ssa_rename_op(op_array, opline, k, build_flags, ssa_vars_count, ssa_ops, var); - } - } - - zend_ssa_op *fe_fetch_ssa_op = blocks[n].len != 0 - && ((end-1)->opcode == ZEND_FE_FETCH_R || (end-1)->opcode == ZEND_FE_FETCH_RW) - && (end-1)->op2_type == IS_CV - ? &ssa_ops[blocks[n].start + blocks[n].len - 1] : NULL; - for (i = 0; i < blocks[n].successors_count; i++) { - int succ = blocks[n].successors[i]; - zend_ssa_phi *p; - for (p = ssa_blocks[succ].phis; p; p = p->next) { - if (p->pi == n) { - /* e-SSA Pi */ - if (p->has_range_constraint) { - if (p->constraint.range.min_var >= 0) { - p->constraint.range.min_ssa_var = var[p->constraint.range.min_var]; - } - if (p->constraint.range.max_var >= 0) { - p->constraint.range.max_ssa_var = var[p->constraint.range.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]; - if (fe_fetch_ssa_op && i == 0 && p->sources[j] == fe_fetch_ssa_op->op2_def) { - /* On the exit edge of an FE_FETCH, use the pre-modification value instead. */ - p->sources[j] = fe_fetch_ssa_op->op2_use; - } - } - } - 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; -} -/* }}} */ - -int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa) /* {{{ */ -{ - 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 def, in, phi; - int *var = NULL; - int i, j, k, changed; - zend_dfg dfg; - ALLOCA_FLAG(dfg_use_heap) - ALLOCA_FLAG(var_use_heap) - - if ((blocks_count * (op_array->last_var + op_array->T)) > 4 * 1024 * 1024) { - /* Don't build SSA for very big functions */ - return FAILURE; - } - - ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block)); - 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 * 4 + 1), dfg_use_heap); - memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1)); - dfg.def = dfg.tmp + set_size; - 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); - } - - def = dfg.def; - in = dfg.in; - - /* Reuse the "use" set, as we no longer need it */ - phi = dfg.use; - zend_bitset_clear(phi, set_size * blocks_count); - - /* Place e-SSA pis. This will add additional "def" points, so it must - * happen before def propagation. */ - place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg); - - /* SSA construction, Step 1: Propagate "def" sets in merge points */ - do { - changed = 0; - for (j = 0; j < blocks_count; j++) { - zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size; - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - if (blocks[j].predecessors_count > 1) { - 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_union(phi_j, 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( - phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size); - i = blocks[i].idom; - } - } - } - if (!zend_bitset_subset(phi_j, def_j, set_size)) { - zend_bitset_union(def_j, phi_j, 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; - } - - for (j = 0; j < blocks_count; j++) { - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - if (!zend_bitset_empty(phi + j * set_size, set_size)) { - ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) { - zend_ssa_phi *phi = zend_arena_calloc(arena, 1, - ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi)) + - ZEND_MM_ALIGNED_SIZE(sizeof(int) * blocks[j].predecessors_count) + - sizeof(void*) * blocks[j].predecessors_count); - - phi->sources = (int*)(((char*)phi) + ZEND_MM_ALIGNED_SIZE(sizeof(zend_ssa_phi))); - memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); - phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + ZEND_MM_ALIGNED_SIZE(sizeof(int) * ssa->cfg.blocks[j].predecessors_count)); - - phi->pi = -1; - phi->var = i; - phi->ssa_var = -1; - - /* Place phis after pis */ - { - zend_ssa_phi **pp = &ssa_blocks[j].phis; - while (*pp) { - if ((*pp)->pi < 0) { - break; - } - pp = &(*pp)->next; - } - phi->next = *pp; - *pp = phi; - } - } ZEND_BITSET_FOREACH_END(); - } - } - - 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 + op_array->last_var, 0xff, 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) { - 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; -} -/* }}} */ - -int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ -{ - zend_ssa_var *ssa_vars; - int i; - - if (!ssa->vars) { - ssa->vars = zend_arena_calloc(arena, ssa->vars_count, sizeof(zend_ssa_var)); - } - ssa_vars = ssa->vars; - - for (i = 0; i < op_array->last_var; i++) { - ssa_vars[i].var = i; - ssa_vars[i].scc = -1; - ssa_vars[i].definition = -1; - ssa_vars[i].use_chain = -1; - } - for (i = op_array->last_var; i < ssa->vars_count; i++) { - ssa_vars[i].var = -1; - ssa_vars[i].scc = -1; - ssa_vars[i].definition = -1; - ssa_vars[i].use_chain = -1; - } - - for (i = op_array->last - 1; i >= 0; i--) { - zend_ssa_op *op = ssa->ops + i; - - if (op->op1_use >= 0) { - op->op1_use_chain = ssa_vars[op->op1_use].use_chain; - ssa_vars[op->op1_use].use_chain = i; - } - if (op->op2_use >= 0 && op->op2_use != op->op1_use) { - op->op2_use_chain = ssa_vars[op->op2_use].use_chain; - ssa_vars[op->op2_use].use_chain = i; - } - if (op->result_use >= 0 && op->result_use != op->op1_use && op->result_use != op->op2_use) { - op->res_use_chain = ssa_vars[op->result_use].use_chain; - ssa_vars[op->result_use].use_chain = i; - } - if (op->op1_def >= 0) { - ssa_vars[op->op1_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op1.var); - ssa_vars[op->op1_def].definition = i; - } - if (op->op2_def >= 0) { - ssa_vars[op->op2_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].op2.var); - ssa_vars[op->op2_def].definition = i; - } - if (op->result_def >= 0) { - ssa_vars[op->result_def].var = EX_VAR_TO_NUM(op_array->opcodes[i].result.var); - ssa_vars[op->result_def].definition = i; - } - } - - for (i = 0; i < ssa->cfg.blocks_count; i++) { - zend_ssa_phi *phi = ssa->blocks[i].phis; - while (phi) { - phi->block = i; - ssa_vars[phi->ssa_var].var = phi->var; - ssa_vars[phi->ssa_var].definition_phi = phi; - if (phi->pi >= 0) { - zend_ssa_phi *p; - - ZEND_ASSERT(phi->sources[0] >= 0); - p = ssa_vars[phi->sources[0]].phi_use_chain; - while (p && p != phi) { - p = zend_ssa_next_use_phi(ssa, phi->sources[0], p); - } - if (!p) { - phi->use_chains[0] = ssa_vars[phi->sources[0]].phi_use_chain; - ssa_vars[phi->sources[0]].phi_use_chain = phi; - } - if (phi->has_range_constraint) { - /* min and max variables can't be used together */ - zend_ssa_range_constraint *constraint = &phi->constraint.range; - if (constraint->min_ssa_var >= 0) { - phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain; - ssa_vars[constraint->min_ssa_var].sym_use_chain = phi; - } else if (constraint->max_ssa_var >= 0) { - phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain; - ssa_vars[constraint->max_ssa_var].sym_use_chain = phi; - } - } - } else { - int j; - - for (j = 0; j < ssa->cfg.blocks[i].predecessors_count; j++) { - zend_ssa_phi *p; - - ZEND_ASSERT(phi->sources[j] >= 0); - p = ssa_vars[phi->sources[j]].phi_use_chain; - while (p && p != phi) { - p = zend_ssa_next_use_phi(ssa, phi->sources[j], p); - } - if (!p) { - phi->use_chains[j] = ssa_vars[phi->sources[j]].phi_use_chain; - ssa_vars[phi->sources[j]].phi_use_chain = phi; - } - } - } - phi = phi->next; - } - } - - /* Mark indirectly accessed variables */ - for (i = 0; i < op_array->last_var; i++) { - if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) { - ssa_vars[i].alias = SYMTABLE_ALIAS; - } else if (zend_string_equals_literal(op_array->vars[i], "http_response_header")) { - ssa_vars[i].alias = HTTP_RESPONSE_HEADER_ALIAS; - } - } - for (i = op_array->last_var; i < ssa->vars_count; i++) { - if (ssa_vars[i].var < op_array->last_var) { - ssa_vars[i].alias = ssa_vars[ssa_vars[i].var].alias; - } - } - - return SUCCESS; -} -/* }}} */ - -int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */ -{ - if (ssa->vars[var].use_chain == op) { - ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op); - return 1; - } else { - int use = ssa->vars[var].use_chain; - - while (use >= 0) { - if (ssa->ops[use].result_use == var) { - if (ssa->ops[use].res_use_chain == op) { - ssa->ops[use].res_use_chain = zend_ssa_next_use(ssa->ops, var, op); - return 1; - } else { - use = ssa->ops[use].res_use_chain; - } - } else if (ssa->ops[use].op1_use == var) { - if (ssa->ops[use].op1_use_chain == op) { - ssa->ops[use].op1_use_chain = zend_ssa_next_use(ssa->ops, var, op); - return 1; - } else { - use = ssa->ops[use].op1_use_chain; - } - } else if (ssa->ops[use].op2_use == var) { - if (ssa->ops[use].op2_use_chain == op) { - ssa->ops[use].op2_use_chain = zend_ssa_next_use(ssa->ops, var, op); - return 1; - } else { - use = ssa->ops[use].op2_use_chain; - } - } else { - break; - } - } - /* something wrong */ - ZEND_UNREACHABLE(); - return 0; - } -} -/* }}} */ - -void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op) /* {{{ */ -{ - if (ssa_op->result_use >= 0) { - zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->result_use); - ssa_op->result_use = -1; - ssa_op->res_use_chain = -1; - } - if (ssa_op->op1_use >= 0) { - if (ssa_op->op1_use != ssa_op->op2_use) { - zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op1_use); - } else { - ssa_op->op2_use_chain = ssa_op->op1_use_chain; - } - ssa_op->op1_use = -1; - ssa_op->op1_use_chain = -1; - } - if (ssa_op->op2_use >= 0) { - zend_ssa_unlink_use_chain(ssa, ssa_op - ssa->ops, ssa_op->op2_use); - ssa_op->op2_use = -1; - ssa_op->op2_use_chain = -1; - } - - /* We let the caller make sure that all defs are gone */ - ZEND_ASSERT(ssa_op->result_def == -1); - ZEND_ASSERT(ssa_op->op1_def == -1); - ZEND_ASSERT(ssa_op->op2_def == -1); - - MAKE_NOP(opline); -} -/* }}} */ - -static inline zend_ssa_phi **zend_ssa_next_use_phi_ptr(zend_ssa *ssa, int var, zend_ssa_phi *p) /* {{{ */ -{ - if (p->pi >= 0) { - return &p->use_chains[0]; - } else { - int j; - for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) { - if (p->sources[j] == var) { - return &p->use_chains[j]; - } - } - } - ZEND_UNREACHABLE(); - return NULL; -} -/* }}} */ - -/* May be called even if source is not used in the phi (useful when removing uses in a phi - * with multiple identical operands) */ -static inline void zend_ssa_remove_use_of_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int source, zend_ssa_phi *next_use_phi) /* {{{ */ -{ - zend_ssa_phi **cur = &ssa->vars[source].phi_use_chain; - while (*cur && *cur != phi) { - cur = zend_ssa_next_use_phi_ptr(ssa, source, *cur); - } - if (*cur) { - *cur = next_use_phi; - } -} -/* }}} */ - -static void zend_ssa_remove_uses_of_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */ -{ - int source; - FOREACH_PHI_SOURCE(phi, source) { - zend_ssa_remove_use_of_phi_source(ssa, phi, source, zend_ssa_next_use_phi(ssa, source, phi)); - } FOREACH_PHI_SOURCE_END(); -} -/* }}} */ - -static void zend_ssa_remove_phi_from_block(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */ -{ - zend_ssa_block *block = &ssa->blocks[phi->block]; - zend_ssa_phi **cur = &block->phis; - while (*cur != phi) { - ZEND_ASSERT(*cur != NULL); - cur = &(*cur)->next; - } - *cur = (*cur)->next; -} -/* }}} */ - -static inline void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */ -{ - if (ssa_op->op1_def >= 0) { - zend_ssa_remove_uses_of_var(ssa, ssa_op->op1_def); - zend_ssa_remove_op1_def(ssa, ssa_op); - } - if (ssa_op->op2_def >= 0) { - zend_ssa_remove_uses_of_var(ssa, ssa_op->op2_def); - zend_ssa_remove_op2_def(ssa, ssa_op); - } - if (ssa_op->result_def >= 0) { - zend_ssa_remove_uses_of_var(ssa, ssa_op->result_def); - zend_ssa_remove_result_def(ssa, ssa_op); - } -} -/* }}} */ - -static inline void zend_ssa_remove_phi_source(zend_ssa *ssa, zend_ssa_phi *phi, int pred_offset, int predecessors_count) /* {{{ */ -{ - int j, var_num = phi->sources[pred_offset]; - zend_ssa_phi *next_phi = phi->use_chains[pred_offset]; - - predecessors_count--; - if (pred_offset < predecessors_count) { - memmove(phi->sources + pred_offset, phi->sources + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(uint32_t)); - memmove(phi->use_chains + pred_offset, phi->use_chains + pred_offset + 1, (predecessors_count - pred_offset) * sizeof(zend_ssa_phi*)); - } - - /* Check if they same var is used in a different phi operand as well, in this case we don't - * need to adjust the use chain (but may have to move the next pointer). */ - for (j = 0; j < predecessors_count; j++) { - if (phi->sources[j] == var_num) { - if (j < pred_offset) { - ZEND_ASSERT(next_phi == NULL); - } else if (j >= pred_offset) { - phi->use_chains[j] = next_phi; - } - return; - } - } - - /* Variable only used in one operand, remove the phi from the use chain. */ - zend_ssa_remove_use_of_phi_source(ssa, phi, var_num, next_phi); -} -/* }}} */ - -void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi) /* {{{ */ -{ - ZEND_ASSERT(phi->ssa_var >= 0); - ZEND_ASSERT(ssa->vars[phi->ssa_var].use_chain < 0 - && ssa->vars[phi->ssa_var].phi_use_chain == NULL); - zend_ssa_remove_uses_of_phi_sources(ssa, phi); - zend_ssa_remove_phi_from_block(ssa, phi); - ssa->vars[phi->ssa_var].definition_phi = NULL; - phi->ssa_var = -1; -} -/* }}} */ - -void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */ -{ - zend_ssa_var *var = &ssa->vars[var_num]; - zend_ssa_phi *phi; - int use; - FOREACH_PHI_USE(var, phi) { - int i, end = NUM_PHI_SOURCES(phi); - for (i = 0; i < end; i++) { - if (phi->sources[i] == var_num) { - phi->use_chains[i] = NULL; - } - } - } FOREACH_PHI_USE_END(); - var->phi_use_chain = NULL; - FOREACH_USE(var, use) { - zend_ssa_op *ssa_op = &ssa->ops[use]; - if (ssa_op->op1_use == var_num) { - ssa_op->op1_use = -1; - ssa_op->op1_use_chain = -1; - } - if (ssa_op->op2_use == var_num) { - ssa_op->op2_use = -1; - ssa_op->op2_use_chain = -1; - } - if (ssa_op->result_use == var_num) { - ssa_op->result_use = -1; - ssa_op->res_use_chain = -1; - } - } FOREACH_USE_END(); - var->use_chain = -1; -} -/* }}} */ - -void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */ -{ - zend_basic_block *next_block = &ssa->cfg.blocks[to]; - zend_ssa_block *next_ssa_block = &ssa->blocks[to]; - zend_ssa_phi *phi; - int j; - - /* Find at which predecessor offset this block is referenced */ - int pred_offset = -1; - int *predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset]; - - for (j = 0; j < next_block->predecessors_count; j++) { - if (predecessors[j] == from) { - pred_offset = j; - break; - } - } - - /* If there are duplicate successors, the predecessors may have been removed in - * a previous iteration already. */ - if (pred_offset == -1) { - return; - } - - /* For phis in successor blocks, remove the operands associated with this block */ - for (phi = next_ssa_block->phis; phi; phi = phi->next) { - if (phi->pi >= 0) { - if (phi->pi == from) { - zend_ssa_rename_var_uses(ssa, phi->ssa_var, phi->sources[0], /* update_types */ 0); - zend_ssa_remove_phi(ssa, phi); - } - } else { - ZEND_ASSERT(phi->sources[pred_offset] >= 0); - zend_ssa_remove_phi_source(ssa, phi, pred_offset, next_block->predecessors_count); - } - } - - /* Remove this predecessor */ - next_block->predecessors_count--; - if (pred_offset < next_block->predecessors_count) { - predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset + pred_offset]; - memmove(predecessors, predecessors + 1, (next_block->predecessors_count - pred_offset) * sizeof(uint32_t)); - } -} -/* }}} */ - -void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ */ -{ - zend_basic_block *block = &ssa->cfg.blocks[i]; - zend_ssa_block *ssa_block = &ssa->blocks[i]; - int *predecessors; - zend_ssa_phi *phi; - int j, s; - - block->flags &= ~ZEND_BB_REACHABLE; - - /* Removes phis in this block */ - for (phi = ssa_block->phis; phi; phi = phi->next) { - zend_ssa_remove_uses_of_var(ssa, phi->ssa_var); - zend_ssa_remove_phi(ssa, phi); - } - - /* Remove instructions in this block */ - for (j = block->start; j < block->start + block->len; j++) { - if (op_array->opcodes[j].opcode == ZEND_NOP) { - continue; - } - - zend_ssa_remove_defs_of_instr(ssa, &ssa->ops[j]); - zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]); - } - - for (s = 0; s < block->successors_count; s++) { - zend_ssa_remove_predecessor(ssa, i, block->successors[s]); - } - - /* Remove successors of predecessors */ - predecessors = &ssa->cfg.predecessors[block->predecessor_offset]; - for (j = 0; j < block->predecessors_count; j++) { - if (predecessors[j] >= 0) { - zend_basic_block *prev_block = &ssa->cfg.blocks[predecessors[j]]; - - for (s = 0; s < prev_block->successors_count; s++) { - if (prev_block->successors[s] == i) { - memmove(prev_block->successors + s, - prev_block->successors + s + 1, - sizeof(int) * (prev_block->successors_count - s - 1)); - prev_block->successors_count--; - s--; - } - } - } - } - - block->successors_count = 0; - block->predecessors_count = 0; - - /* Remove from dominators tree */ - if (block->idom >= 0) { - j = ssa->cfg.blocks[block->idom].children; - if (j == i) { - ssa->cfg.blocks[block->idom].children = block->next_child; - } else if (j >= 0) { - while (ssa->cfg.blocks[j].next_child >= 0) { - if (ssa->cfg.blocks[j].next_child == i) { - ssa->cfg.blocks[j].next_child = block->next_child; - break; - } - j = ssa->cfg.blocks[j].next_child; - } - } - } - block->idom = -1; - block->level = -1; - block->children = -1; - block->next_child = -1; -} -/* }}} */ - -static void propagate_phi_type_widening(zend_ssa *ssa, int var) /* {{{ */ -{ - zend_ssa_phi *phi; - FOREACH_PHI_USE(&ssa->vars[var], phi) { - if (ssa->var_info[var].type & ~ssa->var_info[phi->ssa_var].type) { - ssa->var_info[phi->ssa_var].type |= ssa->var_info[var].type; - propagate_phi_type_widening(ssa, phi->ssa_var); - } - } FOREACH_PHI_USE_END(); -} -/* }}} */ - -void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types) /* {{{ */ -{ - zend_ssa_var *old_var = &ssa->vars[old]; - zend_ssa_var *new_var = &ssa->vars[new]; - int use; - zend_ssa_phi *phi; - - ZEND_ASSERT(old >= 0 && new >= 0); - ZEND_ASSERT(old != new); - - /* Only a no_val is both variables are */ - new_var->no_val &= old_var->no_val; - - /* Update ssa_op use chains */ - FOREACH_USE(old_var, use) { - zend_ssa_op *ssa_op = &ssa->ops[use]; - - /* If the op already uses the new var, don't add the op to the use - * list again. Instead move the use_chain to the correct operand. */ - bool add_to_use_chain = 1; - if (ssa_op->result_use == new) { - add_to_use_chain = 0; - } else if (ssa_op->op1_use == new) { - if (ssa_op->result_use == old) { - ssa_op->res_use_chain = ssa_op->op1_use_chain; - ssa_op->op1_use_chain = -1; - } - add_to_use_chain = 0; - } else if (ssa_op->op2_use == new) { - if (ssa_op->result_use == old) { - ssa_op->res_use_chain = ssa_op->op2_use_chain; - ssa_op->op2_use_chain = -1; - } else if (ssa_op->op1_use == old) { - ssa_op->op1_use_chain = ssa_op->op2_use_chain; - ssa_op->op2_use_chain = -1; - } - add_to_use_chain = 0; - } - - /* Perform the actual renaming */ - if (ssa_op->result_use == old) { - ssa_op->result_use = new; - } - if (ssa_op->op1_use == old) { - ssa_op->op1_use = new; - } - if (ssa_op->op2_use == old) { - ssa_op->op2_use = new; - } - - /* Add op to use chain of new var (if it isn't already). We use the - * first use chain of (result, op1, op2) that has the new variable. */ - if (add_to_use_chain) { - if (ssa_op->result_use == new) { - ssa_op->res_use_chain = new_var->use_chain; - new_var->use_chain = use; - } else if (ssa_op->op1_use == new) { - ssa_op->op1_use_chain = new_var->use_chain; - new_var->use_chain = use; - } else { - ZEND_ASSERT(ssa_op->op2_use == new); - ssa_op->op2_use_chain = new_var->use_chain; - new_var->use_chain = use; - } - } - } FOREACH_USE_END(); - old_var->use_chain = -1; - - /* Update phi use chains */ - FOREACH_PHI_USE(old_var, phi) { - int j; - bool after_first_new_source = 0; - - /* If the phi already uses the new var, find its use chain, as we may - * need to move it to a different source operand. */ - zend_ssa_phi **existing_use_chain_ptr = NULL; - for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) { - if (phi->sources[j] == new) { - existing_use_chain_ptr = &phi->use_chains[j]; - break; - } - } - - for (j = 0; j < ssa->cfg.blocks[phi->block].predecessors_count; j++) { - if (phi->sources[j] == new) { - after_first_new_source = 1; - } else if (phi->sources[j] == old) { - phi->sources[j] = new; - - /* Either move existing use chain to this source, or add the phi - * to the phi use chain of the new variables. Do this only once. */ - if (!after_first_new_source) { - if (existing_use_chain_ptr) { - phi->use_chains[j] = *existing_use_chain_ptr; - *existing_use_chain_ptr = NULL; - } else { - phi->use_chains[j] = new_var->phi_use_chain; - new_var->phi_use_chain = phi; - } - after_first_new_source = 1; - } else { - phi->use_chains[j] = NULL; - } - } - } - - /* Make sure phi result types are not incorrectly narrow after renaming. - * This should not normally happen, but can occur if we DCE an assignment - * or unset and there is an improper phi-indirected use lateron. */ - // TODO Alternatively we could rerun type-inference after DCE - if (update_types && (ssa->var_info[new].type & ~ssa->var_info[phi->ssa_var].type)) { - ssa->var_info[phi->ssa_var].type |= ssa->var_info[new].type; - propagate_phi_type_widening(ssa, phi->ssa_var); - } - } FOREACH_PHI_USE_END(); - old_var->phi_use_chain = NULL; -} -/* }}} */ diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h deleted file mode 100644 index 68f39ad0ec..0000000000 --- a/ext/opcache/Optimizer/zend_ssa.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine, SSA - Static Single Assignment Form | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -#ifndef ZEND_SSA_H -#define ZEND_SSA_H - -#include "zend_optimizer.h" -#include "zend_cfg.h" - -typedef struct _zend_ssa_range { - zend_long min; - zend_long max; - bool underflow; - bool overflow; -} zend_ssa_range; - -typedef enum _zend_ssa_negative_lat { - NEG_NONE = 0, - NEG_INIT = 1, - NEG_INVARIANT = 2, - NEG_USE_LT = 3, - NEG_USE_GT = 4, - NEG_UNKNOWN = 5 -} zend_ssa_negative_lat; - -/* Special kind of SSA Phi function used in eSSA */ -typedef struct _zend_ssa_range_constraint { - zend_ssa_range range; /* simple range constraint */ - int min_var; - int max_var; - int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */ - int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */ - zend_ssa_negative_lat negative; -} zend_ssa_range_constraint; - -typedef struct _zend_ssa_type_constraint { - uint32_t type_mask; /* Type mask to intersect with */ - zend_class_entry *ce; /* Class entry for instanceof constraints */ -} zend_ssa_type_constraint; - -typedef union _zend_ssa_pi_constraint { - zend_ssa_range_constraint range; - zend_ssa_type_constraint type; -} zend_ssa_pi_constraint; - -/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */ -typedef struct _zend_ssa_phi zend_ssa_phi; -struct _zend_ssa_phi { - zend_ssa_phi *next; /* next Phi in the same BB */ - int pi; /* if >= 0 this is actually a e-SSA Pi */ - zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */ - int var; /* Original CV, VAR or TMP variable index */ - int ssa_var; /* SSA variable index */ - int block; /* current BB index */ - int visited : 1; /* flag to avoid recursive processing */ - int has_range_constraint : 1; - zend_ssa_phi **use_chains; - zend_ssa_phi *sym_use_chain; - int *sources; /* Array of SSA IDs that produce this var. - As many as this block has - predecessors. */ -}; - -typedef struct _zend_ssa_block { - zend_ssa_phi *phis; -} zend_ssa_block; - -typedef struct _zend_ssa_op { - int op1_use; - int op2_use; - int result_use; - int op1_def; - int op2_def; - int result_def; - int op1_use_chain; - int op2_use_chain; - int res_use_chain; -} zend_ssa_op; - -typedef enum _zend_ssa_alias_kind { - NO_ALIAS, - SYMTABLE_ALIAS, - HTTP_RESPONSE_HEADER_ALIAS -} zend_ssa_alias_kind; - -typedef enum _zend_ssa_escape_state { - ESCAPE_STATE_UNKNOWN, - ESCAPE_STATE_NO_ESCAPE, - ESCAPE_STATE_FUNCTION_ESCAPE, - ESCAPE_STATE_GLOBAL_ESCAPE -} zend_ssa_escape_state; - -typedef struct _zend_ssa_var { - int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */ - int scc; /* strongly connected component */ - int definition; /* opcode that defines this value */ - zend_ssa_phi *definition_phi; /* phi that defines this value */ - int use_chain; /* uses of this value, linked through opN_use_chain */ - zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */ - zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */ - unsigned int no_val : 1; /* value doesn't matter (used as op1 in ZEND_ASSIGN) */ - unsigned int scc_entry : 1; - unsigned int alias : 2; /* value may be changed indirectly */ - unsigned int escape_state : 2; -} zend_ssa_var; - -typedef struct _zend_ssa_var_info { - uint32_t type; /* inferred type (see zend_inference.h) */ - zend_ssa_range range; - zend_class_entry *ce; - unsigned int has_range : 1; - unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */ - unsigned int recursive : 1; - unsigned int use_as_double : 1; - unsigned int delayed_fetch_this : 1; - unsigned int avoid_refcounting : 1; - unsigned int guarded_reference : 1; - unsigned int indirect_reference : 1; /* IS_INDIRECT returned by FETCH_DIM_W/FETCH_OBJ_W */ -} zend_ssa_var_info; - -typedef struct _zend_ssa { - zend_cfg cfg; /* control flow graph */ - int vars_count; /* number of SSA variables */ - int sccs; /* number of SCCs */ - zend_ssa_block *blocks; /* array of SSA blocks */ - zend_ssa_op *ops; /* array of SSA instructions */ - zend_ssa_var *vars; /* use/def chain of SSA variables */ - zend_ssa_var_info *var_info; -} zend_ssa; - -BEGIN_EXTERN_C() - -int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa); -int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var); -int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa); -int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var); - -void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to); -void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op); -void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi); -void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num); -void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b); -void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types); - -static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var) -{ - ZEND_ASSERT(var->definition >= 0); - ZEND_ASSERT(var->use_chain < 0); - ZEND_ASSERT(!var->phi_use_chain); - var->definition = -1; -} - -static zend_always_inline void zend_ssa_remove_result_def(zend_ssa *ssa, zend_ssa_op *ssa_op) -{ - zend_ssa_var *var = &ssa->vars[ssa_op->result_def]; - _zend_ssa_remove_def(var); - ssa_op->result_def = -1; -} - -static zend_always_inline void zend_ssa_remove_op1_def(zend_ssa *ssa, zend_ssa_op *ssa_op) -{ - zend_ssa_var *var = &ssa->vars[ssa_op->op1_def]; - _zend_ssa_remove_def(var); - ssa_op->op1_def = -1; -} - -static zend_always_inline void zend_ssa_remove_op2_def(zend_ssa *ssa, zend_ssa_op *ssa_op) -{ - zend_ssa_var *var = &ssa->vars[ssa_op->op2_def]; - _zend_ssa_remove_def(var); - ssa_op->op2_def = -1; -} - -END_EXTERN_C() - -static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use) -{ - ssa_op += use; - if (ssa_op->op1_use == var) { - return ssa_op->op1_use_chain; - } else if (ssa_op->op2_use == var) { - return ssa_op->op2_use_chain; - } else { - return ssa_op->res_use_chain; - } -} - -static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p) -{ - if (p->pi >= 0) { - return p->use_chains[0]; - } else { - int j; - for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) { - if (p->sources[j] == var) { - return p->use_chains[j]; - } - } - } - return NULL; -} - -static zend_always_inline bool zend_ssa_is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var) -{ - if (opline->opcode == ZEND_ASSIGN - || opline->opcode == ZEND_UNSET_CV - || opline->opcode == ZEND_BIND_GLOBAL - || opline->opcode == ZEND_BIND_STATIC) { - return ssa_op->op1_use == var && ssa_op->op2_use != var; - } - if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) { - return ssa_op->op2_use == var && ssa_op->op1_use != var; - } - if (ssa_op->result_use == var - && opline->opcode != ZEND_ADD_ARRAY_ELEMENT - && opline->opcode != ZEND_ADD_ARRAY_UNPACK) { - return ssa_op->op1_use != var && ssa_op->op2_use != var; - } - return 0; -} - -static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) { - /* Rename def to use if possible. Mark variable as not defined otherwise. */ - if (ssa_op->op1_def >= 0) { - if (ssa_op->op1_use >= 0) { - zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1); - } - ssa->vars[ssa_op->op1_def].definition = -1; - ssa_op->op1_def = -1; - } - if (ssa_op->op2_def >= 0) { - if (ssa_op->op2_use >= 0) { - zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1); - } - ssa->vars[ssa_op->op2_def].definition = -1; - ssa_op->op2_def = -1; - } - if (ssa_op->result_def >= 0) { - if (ssa_op->result_use >= 0) { - zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1); - } - ssa->vars[ssa_op->result_def].definition = -1; - ssa_op->result_def = -1; - } -} - -#define NUM_PHI_SOURCES(phi) \ - ((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count)) - -/* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue" - * and changing the use chain of the current element */ -#define FOREACH_USE(var, use) do { \ - int _var_num = (var) - ssa->vars, next; \ - for (use = (var)->use_chain; use >= 0; use = next) { \ - next = zend_ssa_next_use(ssa->ops, _var_num, use); -#define FOREACH_USE_END() \ - } \ -} while (0) - -#define FOREACH_PHI_USE(var, phi) do { \ - int _var_num = (var) - ssa->vars; \ - zend_ssa_phi *next_phi; \ - for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \ - next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi); -#define FOREACH_PHI_USE_END() \ - } \ -} while (0) - -#define FOREACH_PHI_SOURCE(phi, source) do { \ - zend_ssa_phi *_phi = (phi); \ - int _i, _end = NUM_PHI_SOURCES(phi); \ - for (_i = 0; _i < _end; _i++) { \ - ZEND_ASSERT(_phi->sources[_i] >= 0); \ - source = _phi->sources[_i]; -#define FOREACH_PHI_SOURCE_END() \ - } \ -} while (0) - -#define FOREACH_PHI(phi) do { \ - int _i; \ - for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \ - phi = ssa->blocks[_i].phis; \ - for (; phi; phi = phi->next) { -#define FOREACH_PHI_END() \ - } \ - } \ -} while (0) - -#define FOREACH_BLOCK(block) do { \ - int _i; \ - for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \ - (block) = &ssa->cfg.blocks[_i]; \ - if (!((block)->flags & ZEND_BB_REACHABLE)) { \ - continue; \ - } -#define FOREACH_BLOCK_END() \ - } \ -} while (0) - -/* Does not support "break" */ -#define FOREACH_INSTR_NUM(i) do { \ - zend_basic_block *_block; \ - FOREACH_BLOCK(_block) { \ - uint32_t _end = _block->start + _block->len; \ - for ((i) = _block->start; (i) < _end; (i)++) { -#define FOREACH_INSTR_NUM_END() \ - } \ - } FOREACH_BLOCK_END(); \ -} while (0) - -#endif /* ZEND_SSA_H */ diff --git a/ext/opcache/Optimizer/zend_worklist.h b/ext/opcache/Optimizer/zend_worklist.h deleted file mode 100644 index 2f3e3dd979..0000000000 --- a/ext/opcache/Optimizer/zend_worklist.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend Engine | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andy Wingo <wingo@igalia.com> | - +----------------------------------------------------------------------+ -*/ - -#ifndef _ZEND_WORKLIST_H_ -#define _ZEND_WORKLIST_H_ - -#include "zend_arena.h" -#include "zend_bitset.h" - -typedef struct _zend_worklist_stack { - int *buf; - int len; - int capacity; -} zend_worklist_stack; - -#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \ - (s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \ - (s)->len = 0; \ - (s)->capacity = _len; \ - } while (0) - -#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \ - free_alloca((s)->buf, use_heap) - -static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len) -{ - ZEND_ASSERT(len >= 0); - - stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len); - stack->len = 0; - stack->capacity = len; - - return SUCCESS; -} - -static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i) -{ - ZEND_ASSERT(stack->len < stack->capacity); - stack->buf[stack->len++] = i; -} - -static inline int zend_worklist_stack_peek(zend_worklist_stack *stack) -{ - ZEND_ASSERT(stack->len); - return stack->buf[stack->len - 1]; -} - -static inline int zend_worklist_stack_pop(zend_worklist_stack *stack) -{ - ZEND_ASSERT(stack->len); - return stack->buf[--stack->len]; -} - -typedef struct _zend_worklist { - zend_bitset visited; - zend_worklist_stack stack; -} zend_worklist; - -#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \ - (w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \ - (w)->stack.len = 0; \ - (w)->stack.capacity = _len; \ - (w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \ - memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \ - } while (0) - -#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \ - free_alloca((w)->stack.buf, use_heap) - -static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len) -{ - ZEND_ASSERT(len >= 0); - worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len)); - return zend_worklist_stack_prepare(arena, &worklist->stack, len); -} - -static inline int zend_worklist_len(zend_worklist *worklist) -{ - return worklist->stack.len; -} - -static inline int zend_worklist_push(zend_worklist *worklist, int i) -{ - ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity); - - if (zend_bitset_in(worklist->visited, i)) { - return 0; - } - - zend_bitset_incl(worklist->visited, i); - zend_worklist_stack_push(&worklist->stack, i); - return 1; -} - -static inline int zend_worklist_peek(zend_worklist *worklist) -{ - return zend_worklist_stack_peek(&worklist->stack); -} - -static inline int zend_worklist_pop(zend_worklist *worklist) -{ - /* Does not clear visited flag */ - return zend_worklist_stack_pop(&worklist->stack); -} - -#endif /* _ZEND_WORKLIST_H_ */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 0ebe5a41ae..5101660a4a 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2957,9 +2957,6 @@ static zend_result accel_post_startup(void) } } - /* Initialize zend_func_info_rid */ - zend_optimizer_startup(); - /********************************************/ /* End of non-SHM dependent initializations */ /********************************************/ @@ -3119,8 +3116,6 @@ file_cache_fallback: zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename)); } - zend_optimizer_startup(); - if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) { accel_use_shm_interned_strings(); } @@ -3144,8 +3139,6 @@ void accel_shutdown(void) zend_jit_shutdown(); #endif - zend_optimizer_shutdown(); - zend_accel_blacklist_shutdown(&accel_blacklist); if (!ZCG(enabled) || !accel_startup_ok) { diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 93d72fb73d..33c99c59c1 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -312,31 +312,9 @@ int main() { shared_alloc_shm.c \ shared_alloc_mmap.c \ shared_alloc_posix.c \ - Optimizer/zend_optimizer.c \ - Optimizer/pass1.c \ - Optimizer/pass3.c \ - Optimizer/optimize_func_calls.c \ - Optimizer/block_pass.c \ - Optimizer/optimize_temp_vars_5.c \ - Optimizer/nop_removal.c \ - Optimizer/compact_literals.c \ - Optimizer/zend_cfg.c \ - Optimizer/zend_dfg.c \ - Optimizer/dfa_pass.c \ - Optimizer/zend_ssa.c \ - Optimizer/zend_inference.c \ - Optimizer/zend_func_info.c \ - Optimizer/zend_call_graph.c \ - Optimizer/sccp.c \ - Optimizer/scdf.c \ - Optimizer/dce.c \ - Optimizer/escape_analysis.c \ - Optimizer/compact_vars.c \ - Optimizer/zend_dump.c \ $ZEND_JIT_SRC, shared,,-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1,,yes) - PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1) PHP_ADD_EXTENSION_DEP(opcache, pcre) if test "$have_shm_ipc" != "yes" && test "$have_shm_mmap_posix" != "yes" && test "$have_shm_mmap_anon" != "yes"; then diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 index fb921c73da..a7f292ee76 100644 --- a/ext/opcache/config.w32 +++ b/ext/opcache/config.w32 @@ -38,8 +38,6 @@ if (PHP_OPCACHE != "no") { } } - ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c", "opcache", "ext\\opcache\\Optimizer"); - ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname); } |
