diff options
Diffstat (limited to 'Zend')
159 files changed, 25942 insertions, 1769 deletions
diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c new file mode 100644 index 0000000000..e833ff3dbf --- /dev/null +++ b/Zend/Optimizer/block_pass.c @@ -0,0 +1,1937 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c new file mode 100644 index 0000000000..0e1529d2bd --- /dev/null +++ b/Zend/Optimizer/compact_literals.c @@ -0,0 +1,843 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/compact_vars.c b/Zend/Optimizer/compact_vars.c new file mode 100644 index 0000000000..e70e189827 --- /dev/null +++ b/Zend/Optimizer/compact_vars.c @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c new file mode 100644 index 0000000000..940f1b6ee2 --- /dev/null +++ b/Zend/Optimizer/dce.c @@ -0,0 +1,628 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c new file mode 100644 index 0000000000..fe06de276b --- /dev/null +++ b/Zend/Optimizer/dfa_pass.c @@ -0,0 +1,1637 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c new file mode 100644 index 0000000000..c0d5081c1f --- /dev/null +++ b/Zend/Optimizer/escape_analysis.c @@ -0,0 +1,539 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/nop_removal.c b/Zend/Optimizer/nop_removal.c new file mode 100644 index 0000000000..32d2f10bf4 --- /dev/null +++ b/Zend/Optimizer/nop_removal.c @@ -0,0 +1,106 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c new file mode 100644 index 0000000000..319b17438d --- /dev/null +++ b/Zend/Optimizer/optimize_func_calls.c @@ -0,0 +1,337 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/optimize_temp_vars_5.c b/Zend/Optimizer/optimize_temp_vars_5.c new file mode 100644 index 0000000000..6f7400159d --- /dev/null +++ b/Zend/Optimizer/optimize_temp_vars_5.c @@ -0,0 +1,187 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c new file mode 100644 index 0000000000..86774afef4 --- /dev/null +++ b/Zend/Optimizer/pass1.c @@ -0,0 +1,685 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/pass3.c b/Zend/Optimizer/pass3.c new file mode 100644 index 0000000000..f98c41848c --- /dev/null +++ b/Zend/Optimizer/pass3.c @@ -0,0 +1,356 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c new file mode 100644 index 0000000000..e097f654c8 --- /dev/null +++ b/Zend/Optimizer/sccp.c @@ -0,0 +1,2528 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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/Zend/Optimizer/scdf.c b/Zend/Optimizer/scdf.c new file mode 100644 index 0000000000..e414081987 --- /dev/null +++ b/Zend/Optimizer/scdf.c @@ -0,0 +1,229 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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/Zend/Optimizer/scdf.h b/Zend/Optimizer/scdf.h new file mode 100644 index 0000000000..1ab1cec3bd --- /dev/null +++ b/Zend/Optimizer/scdf.h @@ -0,0 +1,99 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c new file mode 100644 index 0000000000..4bd7705816 --- /dev/null +++ b/Zend/Optimizer/ssa_integrity.c @@ -0,0 +1,378 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c new file mode 100644 index 0000000000..b0f3247cd7 --- /dev/null +++ b/Zend/Optimizer/zend_call_graph.c @@ -0,0 +1,271 @@ +/* + +----------------------------------------------------------------------+ + | 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++; +} + +ZEND_API 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 +} + +ZEND_API 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; +} +/* }}} */ + +ZEND_API 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_API 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/Zend/Optimizer/zend_call_graph.h b/Zend/Optimizer/zend_call_graph.h new file mode 100644 index 0000000000..a456dcfbb8 --- /dev/null +++ b/Zend/Optimizer/zend_call_graph.h @@ -0,0 +1,69 @@ +/* + +----------------------------------------------------------------------+ + | 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() + +ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); +ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); +ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array); +ZEND_API 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/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c new file mode 100644 index 0000000000..973a93ef6c --- /dev/null +++ b/Zend/Optimizer/zend_cfg.c @@ -0,0 +1,909 @@ +/* + +----------------------------------------------------------------------+ + | 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) + +ZEND_API 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; +} +/* }}} */ + +ZEND_API 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. */ +ZEND_API 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; +} + +ZEND_API 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/Zend/Optimizer/zend_cfg.h b/Zend/Optimizer/zend_cfg.h new file mode 100644 index 0000000000..6fff720ed3 --- /dev/null +++ b/Zend/Optimizer/zend_cfg.h @@ -0,0 +1,127 @@ +/* + +----------------------------------------------------------------------+ + | 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() + +ZEND_API 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); +ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg); +ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg); +ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg); + +END_EXTERN_C() + +#endif /* ZEND_CFG_H */ diff --git a/Zend/Optimizer/zend_dfg.c b/Zend/Optimizer/zend_dfg.c new file mode 100644 index 0000000000..b059f1f0ea --- /dev/null +++ b/Zend/Optimizer/zend_dfg.c @@ -0,0 +1,333 @@ +/* + +----------------------------------------------------------------------+ + | 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)); + } +} +/* }}} */ + +ZEND_API 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/Zend/Optimizer/zend_dfg.h b/Zend/Optimizer/zend_dfg.h new file mode 100644 index 0000000000..6ec92be307 --- /dev/null +++ b/Zend/Optimizer/zend_dfg.h @@ -0,0 +1,51 @@ +/* + +----------------------------------------------------------------------+ + | 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); +ZEND_API 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/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c new file mode 100644 index 0000000000..e2fdbbcbf7 --- /dev/null +++ b/Zend/Optimizer/zend_dump.c @@ -0,0 +1,1239 @@ +/* + +----------------------------------------------------------------------+ + | 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)"); + } + } +} + +ZEND_API 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); +} + +ZEND_API 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); + } + } +} + +ZEND_API 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"); + } +} + +ZEND_API 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/Zend/Optimizer/zend_dump.h b/Zend/Optimizer/zend_dump.h new file mode 100644 index 0000000000..b0e0d7966b --- /dev/null +++ b/Zend/Optimizer/zend_dump.h @@ -0,0 +1,48 @@ +/* + +----------------------------------------------------------------------+ + | 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() + +ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data); +ZEND_API 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); +ZEND_API 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); +ZEND_API 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/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c new file mode 100644 index 0000000000..073a054c0f --- /dev/null +++ b/Zend/Optimizer/zend_func_info.c @@ -0,0 +1,973 @@ +/* + +----------------------------------------------------------------------+ + | 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; +ZEND_API 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; + } +} + +ZEND_API 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/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h new file mode 100644 index 0000000000..53ad99c22e --- /dev/null +++ b/Zend/Optimizer/zend_func_info.h @@ -0,0 +1,68 @@ +/* + +----------------------------------------------------------------------+ + | 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 ZEND_API int zend_func_info_rid; + +ZEND_API 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/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c new file mode 100644 index 0000000000..ba51283beb --- /dev/null +++ b/Zend/Optimizer/zend_inference.c @@ -0,0 +1,4680 @@ +/* + +----------------------------------------------------------------------+ + | 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); + } +} +/* }}} */ + +ZEND_API 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; +} +/* }}} */ + +ZEND_API 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); +} + +ZEND_API 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); +} + +ZEND_API 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; +} + +ZEND_API 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; +} + +ZEND_API 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; +} + +ZEND_API 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; +} + +ZEND_API 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); +} + +ZEND_API 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; + } +} + +ZEND_API 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/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h new file mode 100644 index 0000000000..8390946012 --- /dev/null +++ b/Zend/Optimizer/zend_inference.h @@ -0,0 +1,288 @@ +/* + +----------------------------------------------------------------------+ + | 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() + +ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa); +ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa); +ZEND_API int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level); + +ZEND_API 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); +ZEND_API 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); + +ZEND_API uint32_t zend_fetch_arg_info_type( + const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce); +ZEND_API 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); + +ZEND_API 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); +ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa); + +ZEND_API 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/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c new file mode 100644 index 0000000000..ae548fc520 --- /dev/null +++ b/Zend/Optimizer/zend_optimizer.c @@ -0,0 +1,1568 @@ +/* + +----------------------------------------------------------------------+ + | 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); +} + +ZEND_API 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/Zend/Optimizer/zend_optimizer.h b/Zend/Optimizer/zend_optimizer.h new file mode 100644 index 0000000000..4b43255437 --- /dev/null +++ b/Zend/Optimizer/zend_optimizer.h @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | 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; + +BEGIN_EXTERN_C() +ZEND_API 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); +END_EXTERN_C() + +#endif diff --git a/Zend/Optimizer/zend_optimizer_internal.h b/Zend/Optimizer/zend_optimizer_internal.h new file mode 100644 index 0000000000..911eb79e64 --- /dev/null +++ b/Zend/Optimizer/zend_optimizer_internal.h @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c new file mode 100644 index 0000000000..6fe97cbc1e --- /dev/null +++ b/Zend/Optimizer/zend_ssa.c @@ -0,0 +1,1628 @@ +/* + +----------------------------------------------------------------------+ + | 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; +} +/* }}} */ + +ZEND_API 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; +} +/* }}} */ + +ZEND_API 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; +} +/* }}} */ + +ZEND_API 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/Zend/Optimizer/zend_ssa.h b/Zend/Optimizer/zend_ssa.h new file mode 100644 index 0000000000..ddc9f95f43 --- /dev/null +++ b/Zend/Optimizer/zend_ssa.h @@ -0,0 +1,326 @@ +/* + +----------------------------------------------------------------------+ + | 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() + +ZEND_API 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_API int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa); +ZEND_API 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_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/Zend/Optimizer/zend_worklist.h b/Zend/Optimizer/zend_worklist.h new file mode 100644 index 0000000000..2f3e3dd979 --- /dev/null +++ b/Zend/Optimizer/zend_worklist.h @@ -0,0 +1,121 @@ +/* + +----------------------------------------------------------------------+ + | 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/Zend/Zend.m4 b/Zend/Zend.m4 index 781e51d3e4..081a237012 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -209,6 +209,10 @@ fi test -n "$GCC" && CFLAGS="-Wall -Wextra -Wno-strict-aliasing -Wno-implicit-fallthrough -Wno-unused-parameter -Wno-sign-compare $CFLAGS" dnl Check if compiler supports -Wno-clobbered (only GCC) AX_CHECK_COMPILE_FLAG([-Wno-clobbered], CFLAGS="-Wno-clobbered $CFLAGS", , [-Werror]) +AX_CHECK_COMPILE_FLAG([-Wduplicated-cond], CFLAGS="-Wduplicated-cond $CFLAGS", , [-Werror]) +AX_CHECK_COMPILE_FLAG([-Wlogical-op], CFLAGS="-Wlogical-op $CFLAGS", , [-Werror]) +AX_CHECK_COMPILE_FLAG([-Wformat-truncation], CFLAGS="-Wformat-truncation $CFLAGS", , [-Werror]) +AX_CHECK_COMPILE_FLAG([-fno-common], CFLAGS="-fno-common $CFLAGS", , [-Werror]) test -n "$DEBUG_CFLAGS" && CFLAGS="$CFLAGS $DEBUG_CFLAGS" diff --git a/Zend/tests/array_self_add_globals.phpt b/Zend/tests/array_self_add_globals.phpt index ebad7c3fdf..78dd3bf9a3 100644 --- a/Zend/tests/array_self_add_globals.phpt +++ b/Zend/tests/array_self_add_globals.phpt @@ -2,7 +2,6 @@ Add $GLOBALS to itself --FILE-- <?php -$GLOBALS += $GLOBALS; $x = $GLOBALS + $GLOBALS; ?> ===DONE=== diff --git a/Zend/tests/attributes/016_custom_attribute_validation.phpt b/Zend/tests/attributes/016_custom_attribute_validation.phpt index 0f3167f986..11ffeaa774 100644 --- a/Zend/tests/attributes/016_custom_attribute_validation.phpt +++ b/Zend/tests/attributes/016_custom_attribute_validation.phpt @@ -2,8 +2,8 @@ Attribute validation callback of internal attributes. --SKIPIF-- <?php -if (!extension_loaded('zend-test')) { - echo "skip requires zend-test extension\n"; +if (!extension_loaded('zend_test')) { + echo "skip requires zend_test extension\n"; } --FILE-- <?php diff --git a/Zend/tests/bug71539_6.phpt b/Zend/tests/bug71539_6.phpt deleted file mode 100644 index d690538595..0000000000 --- a/Zend/tests/bug71539_6.phpt +++ /dev/null @@ -1,15 +0,0 @@ ---TEST-- -Bug #71539.5 (Memory error on $arr[$a] =& $arr[$b] if RHS rehashes) ---FILE-- -<?php -$name = 'a'; -for ($i = 0; $i < 100000; $i++) { - if ($name != 'i') { - $$name =& $GLOBALS; - } - $name++; -} -?> -OK ---EXPECT-- -OK diff --git a/Zend/tests/bug71695.phpt b/Zend/tests/bug71695.phpt deleted file mode 100644 index 31ae73a99d..0000000000 --- a/Zend/tests/bug71695.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -Bug #71695 (Global variables are reserved before execution) ---FILE-- -<?php -function provideGlobals() { - var_dump(array_key_exists("foo", $GLOBALS)); - var_dump(isset($GLOBALS["foo"])); - $GLOBALS += array("foo" => "foo"); -} - -provideGlobals(); -echo $foo; -?> ---EXPECT-- -bool(false) -bool(false) -foo diff --git a/Zend/tests/bug78239.phpt b/Zend/tests/bug78239.phpt index 1ecad67460..50dd46c971 100644 --- a/Zend/tests/bug78239.phpt +++ b/Zend/tests/bug78239.phpt @@ -1,7 +1,7 @@ --TEST-- Bug #78239: Deprecation notice during string conversion converted to exception hangs --SKIPIF-- -<?php if (!extension_loaded("zend-test")) die("skip requires zend-test extension"); ?> +<?php if (!extension_loaded("zend_test")) die("skip requires zend_test extension"); ?> --FILE-- <?php function handleError($level, $message, $file = '', $line = 0, $context = []) diff --git a/Zend/tests/bug78335_2.phpt b/Zend/tests/bug78335_2.phpt index 5fb5fcb788..5f2c9ed9df 100644 --- a/Zend/tests/bug78335_2.phpt +++ b/Zend/tests/bug78335_2.phpt @@ -1,7 +1,7 @@ --TEST-- Bug #78335: Static properties containing cycles report as leak (internal class variant) --SKIPIF-- -<?php if (!extension_loaded("zend-test")) die("skip requires zend-test"); ?> +<?php if (!extension_loaded("zend_test")) die("skip requires zend_test"); ?> --FILE-- <?php diff --git a/Zend/tests/call_to_deprecated_function_args.phpt b/Zend/tests/call_to_deprecated_function_args.phpt index c7781e30e0..7bb9039b46 100644 --- a/Zend/tests/call_to_deprecated_function_args.phpt +++ b/Zend/tests/call_to_deprecated_function_args.phpt @@ -2,7 +2,7 @@ Check that arguments are freed when calling a deprecated function --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); --FILE-- <?php diff --git a/Zend/tests/const_deprecation.phpt b/Zend/tests/const_deprecation.phpt index b7cf99d52e..08de4229b9 100644 --- a/Zend/tests/const_deprecation.phpt +++ b/Zend/tests/const_deprecation.phpt @@ -2,7 +2,7 @@ Internal constant deprecation --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip requires zend-test'); +if (!extension_loaded('zend_test')) die('skip requires zend_test'); ?> --FILE-- <?php diff --git a/Zend/tests/gc_010.phpt b/Zend/tests/gc_010.phpt deleted file mode 100644 index 756c8ebc2a..0000000000 --- a/Zend/tests/gc_010.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -GC 010: Cycle with reference to $GLOBALS ---INI-- -zend.enable_gc=1 ---FILE-- -<?php -$a = array(); -$a[] =& $a; -var_dump($a); -$a[] =& $GLOBALS; -unset($a); -var_dump(gc_collect_cycles()); -echo "ok\n" -?> ---EXPECT-- -array(1) { - [0]=> - *RECURSION* -} -int(1) -ok diff --git a/Zend/tests/globals_001.phpt b/Zend/tests/globals_001.phpt index 069e88730c..7c54046212 100644 --- a/Zend/tests/globals_001.phpt +++ b/Zend/tests/globals_001.phpt @@ -29,6 +29,6 @@ string(%d) "%s" Warning: Undefined array key "PHP_SELF" in %s on line %d NULL -Warning: Undefined variable $_SERVER in %s on line %d +Warning: Undefined global variable $_SERVER in %s on line %d NULL Done diff --git a/Zend/tests/globals_002.phpt b/Zend/tests/globals_002.phpt index d8f9ad4aa2..faa9b94ade 100644 --- a/Zend/tests/globals_002.phpt +++ b/Zend/tests/globals_002.phpt @@ -32,6 +32,6 @@ string(%d) "%s" Warning: Undefined array key "PHP_SELF" in %s on line %d NULL -Warning: Undefined variable $_SERVER in %s on line %d +Warning: Undefined global variable $_SERVER in %s on line %d NULL Done diff --git a/Zend/tests/globals_003.phpt b/Zend/tests/globals_003.phpt index 6ac9d9f779..36e74bde64 100644 --- a/Zend/tests/globals_003.phpt +++ b/Zend/tests/globals_003.phpt @@ -38,6 +38,6 @@ string(%d) "%s" Warning: Undefined array key "PHP_SELF" in %s on line %d NULL -Warning: Undefined variable $_SERVER in %s on line %d +Warning: Undefined global variable $_SERVER in %s on line %d NULL Done diff --git a/Zend/tests/globals_004.phpt b/Zend/tests/globals_004.phpt index 27520a220c..4c81e5e57a 100644 --- a/Zend/tests/globals_004.phpt +++ b/Zend/tests/globals_004.phpt @@ -23,6 +23,6 @@ string(%d) "%s" Warning: Undefined array key "PHP_SELF" in %s on line %d NULL -Warning: Undefined variable $_SERVER in %s on line %d +Warning: Undefined global variable $_SERVER in %s on line %d NULL Done diff --git a/Zend/tests/inherit_internal_static.phpt b/Zend/tests/inherit_internal_static.phpt index 4716717f15..6e29301b74 100644 --- a/Zend/tests/inherit_internal_static.phpt +++ b/Zend/tests/inherit_internal_static.phpt @@ -1,7 +1,7 @@ --TEST-- Inherit internal static property into userland class --SKIPIF-- -<?php if (!extension_loaded('zend-test')) die('skip requires zend-test'); ?> +<?php if (!extension_loaded('zend_test')) die('skip requires zend_test'); ?> --FILE-- <?php diff --git a/Zend/tests/iterable_or_null.phpt b/Zend/tests/iterable_or_null.phpt index 9f798af06d..19cb854437 100644 --- a/Zend/tests/iterable_or_null.phpt +++ b/Zend/tests/iterable_or_null.phpt @@ -2,7 +2,7 @@ Test Z_PARAM_ITERABLE() and Z_PARAM_ITERABLE_OR_NULL --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/list_keyed_leading_comma.phpt b/Zend/tests/list_keyed_leading_comma.phpt new file mode 100644 index 0000000000..dcadcbf393 --- /dev/null +++ b/Zend/tests/list_keyed_leading_comma.phpt @@ -0,0 +1,10 @@ +--TEST-- +Leading comma in keyed list assignment +--FILE-- +<?php + +[, "a" => $b] = [1, "a" => 2]; + +?> +--EXPECTF-- +Fatal error: Cannot use empty array entries in keyed array assignment in %s on line %d diff --git a/Zend/tests/lsb_023.phpt b/Zend/tests/lsb_023.phpt new file mode 100644 index 0000000000..a8051aa85f --- /dev/null +++ b/Zend/tests/lsb_023.phpt @@ -0,0 +1,26 @@ +--TEST-- +Late Static Binding static:: calls protected / public method of child class even then +the method is private in parent class +--FILE-- +<?php +class A { + public static function out() { + echo static::value(), PHP_EOL; + } + + private static function value() { return 'A'; } +} +class B extends A { + protected static function value() { return 'B'; } +} +class C extends A { + public static function value() { return 'C'; } +} +A::out(); +B::out(); +C::out(); +echo PHP_EOL; +--EXPECT-- +A +B +C diff --git a/Zend/tests/lsb_024.phpt b/Zend/tests/lsb_024.phpt new file mode 100644 index 0000000000..2c71c678d3 --- /dev/null +++ b/Zend/tests/lsb_024.phpt @@ -0,0 +1,25 @@ +--TEST-- +Late Static Binding static:: accesses protected / public static variables of child +class when the variable is private in parent class +--FILE-- +<?php +class A { + private static $value = 'A'; + + public static function out() { + echo static::$value, PHP_EOL; + } +} +class B extends A { + protected static $value = 'B'; +} +class C extends A { + public static $value = 'C'; +} +A::out(); +B::out(); +C::out(); +--EXPECT-- +A +B +C diff --git a/Zend/tests/multibyte/multibyte_encoding_001.phpt b/Zend/tests/multibyte/multibyte_encoding_001.phpt index 3b26dcb989..f95b4311a9 100644 --- a/Zend/tests/multibyte/multibyte_encoding_001.phpt +++ b/Zend/tests/multibyte/multibyte_encoding_001.phpt @@ -13,7 +13,7 @@ internal_encoding=SJIS <?php declare(encoding='Shift_JIS'); $s = "•\"; // 0x95+0x5c in script, not somewhere else " -printf("%x:%x\n", ord($s[0]), ord($s[1])); +printf("%x:%x", ord($s[0]), ord($s[1])); ?> --EXPECT-- 95:5c diff --git a/Zend/tests/multibyte/multibyte_encoding_003.phpt b/Zend/tests/multibyte/multibyte_encoding_003.phpt Binary files differindex a0983329f4..f0fb60f6cd 100644 --- a/Zend/tests/multibyte/multibyte_encoding_003.phpt +++ b/Zend/tests/multibyte/multibyte_encoding_003.phpt diff --git a/Zend/tests/named_params/undef_var.phpt b/Zend/tests/named_params/undef_var.phpt index b1d5682434..5ef41e26dd 100644 --- a/Zend/tests/named_params/undef_var.phpt +++ b/Zend/tests/named_params/undef_var.phpt @@ -1,5 +1,5 @@ --TEST-- -Passing undefined variabled to named arg +Passing undefined variable to named arg --FILE-- <?php diff --git a/Zend/tests/oct_whitespace.phpt b/Zend/tests/oct_whitespace.phpt new file mode 100644 index 0000000000..cb0bf4ad7a --- /dev/null +++ b/Zend/tests/oct_whitespace.phpt @@ -0,0 +1,8 @@ +--TEST-- +Octal literal followed by whitespace and another number +--FILE-- +<?php +var_dump(0o0 2); +?> +--EXPECTF-- +Parse error: syntax error, unexpected integer "2", expecting ")" in %s on line %d diff --git a/Zend/tests/overloaded_func_001.phpt b/Zend/tests/overloaded_func_001.phpt index 7fc435f920..d78ef1971c 100644 --- a/Zend/tests/overloaded_func_001.phpt +++ b/Zend/tests/overloaded_func_001.phpt @@ -2,7 +2,7 @@ Overloaded function 001 --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/overloaded_func_002.phpt b/Zend/tests/overloaded_func_002.phpt index a02f1e8e37..0a3107182a 100644 --- a/Zend/tests/overloaded_func_002.phpt +++ b/Zend/tests/overloaded_func_002.phpt @@ -2,7 +2,7 @@ Overloaded function 002 --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/resource_key.phpt b/Zend/tests/resource_key.phpt new file mode 100644 index 0000000000..fddd41bcad --- /dev/null +++ b/Zend/tests/resource_key.phpt @@ -0,0 +1,36 @@ +--TEST-- +Behavior of resources as array keys +--FILE-- +<?php + +$r = fopen(__FILE__, 'r'); +$a = []; +echo "Assign:"; +$a[$r] = 1; +echo "Add assign:"; +$a[$r] += 1; +echo "Inc:"; +$a[$r]++; +echo "Get:"; +var_dump($a[$r]); +echo "Isset:"; +var_dump(isset($a[$r])); +echo "Unset:"; +unset($a[$r]); + +?> +--EXPECTF-- +Assign: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +Add assign: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +Inc: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +Get: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +int(3) +Isset: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d +bool(true) +Unset: +Warning: Resource ID#%d used as offset, casting to integer (%d) in %s on line %d diff --git a/Zend/tests/restrict_globals/globals_in_globals.phpt b/Zend/tests/restrict_globals/globals_in_globals.phpt new file mode 100644 index 0000000000..fbb811708c --- /dev/null +++ b/Zend/tests/restrict_globals/globals_in_globals.phpt @@ -0,0 +1,11 @@ +--TEST-- +$GLOBALS no longer contains 'GLOBALS' +--FILE-- +<?php + +$g = $GLOBALS; +var_dump(isset($g['GLOBALS'])); + +?> +--EXPECT-- +bool(false) diff --git a/Zend/tests/restrict_globals/invalid_append.phpt b/Zend/tests/restrict_globals/invalid_append.phpt new file mode 100644 index 0000000000..8e8b99fb9b --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_append.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot append to $GLOBALS +--FILE-- +<?php + +$GLOBALS[] = 1; + +?> +--EXPECTF-- +Fatal error: Cannot append to $GLOBALS in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_append_isset.phpt b/Zend/tests/restrict_globals/invalid_append_isset.phpt new file mode 100644 index 0000000000..6cb57351e7 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_append_isset.phpt @@ -0,0 +1,8 @@ +--TEST-- +Cannot append to $GLOBALS in isset() +--FILE-- +<?php +isset($GLOBALS[]); +?> +--EXPECTF-- +Fatal error: Cannot use [] for reading in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_append_unset.phpt b/Zend/tests/restrict_globals/invalid_append_unset.phpt new file mode 100644 index 0000000000..b7c06179c7 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_append_unset.phpt @@ -0,0 +1,8 @@ +--TEST-- +Cannot append to $GLOBALS in unset() +--FILE-- +<?php +unset($GLOBALS[]); +?> +--EXPECTF-- +Fatal error: Cannot use [] for unsetting in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign.phpt b/Zend/tests/restrict_globals/invalid_assign.phpt new file mode 100644 index 0000000000..c42cc9e1f9 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot assign to $GLOBALS +--FILE-- +<?php + +$GLOBALS = []; + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign_list.phpt b/Zend/tests/restrict_globals/invalid_assign_list.phpt new file mode 100644 index 0000000000..8fc3e524a9 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign_list.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot list-assign to $GLOBALS +--FILE-- +<?php + +list($GLOBALS) = [1]; + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt b/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt new file mode 100644 index 0000000000..594a308ea1 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign_list_ref.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot list-assign to $GLOBALS (by-ref) +--FILE-- +<?php + +list(&$GLOBALS) = [1]; + +?> +--EXPECTF-- +Fatal error: Cannot assign reference to non referencable value in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign_op.phpt b/Zend/tests/restrict_globals/invalid_assign_op.phpt new file mode 100644 index 0000000000..a9b36853ea --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign_op.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot compound assign to $GLOBALS +--FILE-- +<?php + +$GLOBALS += []; + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt b/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt new file mode 100644 index 0000000000..b14c167646 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign_ref_lhs.phpt @@ -0,0 +1,11 @@ +--TEST-- +Cannot by-ref assign to $GLOBALS (LHS) +--FILE-- +<?php + +$var = []; +$GLOBALS =& $var; + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt b/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt new file mode 100644 index 0000000000..3cdab0f9c9 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_assign_ref_rhs.phpt @@ -0,0 +1,11 @@ +--TEST-- +Cannot by-ref assign to $GLOBALS (RHS) +--FILE-- +<?php + +$var = []; +$var =& $GLOBALS; + +?> +--EXPECTF-- +Fatal error: Cannot acquire reference to $GLOBALS in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_foreach.phpt b/Zend/tests/restrict_globals/invalid_foreach.phpt new file mode 100644 index 0000000000..0e7c74881a --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_foreach.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use $GLOBALS as foreach result variable +--FILE-- +<?php + +foreach ([1] as $GLOBALS) {} + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_foreach_ref.phpt b/Zend/tests/restrict_globals/invalid_foreach_ref.phpt new file mode 100644 index 0000000000..2c355c09ae --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_foreach_ref.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot use $GLOBALS as foreach result variable (by-ref) +--FILE-- +<?php + +foreach ([1] as &$GLOBALS) {} + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt b/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt new file mode 100644 index 0000000000..a0145a0624 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_pass_by_ref.phpt @@ -0,0 +1,23 @@ +--TEST-- +$GLOBALS cannot be passed by reference (runtime error) +--FILE-- +<?php + +function by_ref(&$ref) {} +try { + by_ref($GLOBALS); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + by_ref2($GLOBALS); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +function by_ref2(&$ref) {} + +?> +--EXPECT-- +by_ref(): Argument #1 ($ref) cannot be passed by reference +by_ref2(): Argument #1 ($ref) cannot be passed by reference diff --git a/Zend/tests/restrict_globals/invalid_unset.phpt b/Zend/tests/restrict_globals/invalid_unset.phpt new file mode 100644 index 0000000000..781388b9b2 --- /dev/null +++ b/Zend/tests/restrict_globals/invalid_unset.phpt @@ -0,0 +1,10 @@ +--TEST-- +Cannot unset $GLOBALS +--FILE-- +<?php + +unset($GLOBALS); + +?> +--EXPECTF-- +Fatal error: $GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax in %s on line %d diff --git a/Zend/tests/restrict_globals/key_canonicalization.phpt b/Zend/tests/restrict_globals/key_canonicalization.phpt new file mode 100644 index 0000000000..66d9a29a92 --- /dev/null +++ b/Zend/tests/restrict_globals/key_canonicalization.phpt @@ -0,0 +1,11 @@ +--TEST-- +$GLOBALS should have canonicalized keys +--FILE-- +<?php + +${1} = 42; +var_dump($GLOBALS[1]); + +?> +--EXPECT-- +int(42) diff --git a/Zend/tests/restrict_globals/valid.phpt b/Zend/tests/restrict_globals/valid.phpt new file mode 100644 index 0000000000..b99bbe402f --- /dev/null +++ b/Zend/tests/restrict_globals/valid.phpt @@ -0,0 +1,52 @@ +--TEST-- +Supported operations on $GLOBALS +--FILE-- +<?php + +function test() { + var_dump($GLOBALS['x']); + $GLOBALS['x'] = 1; + var_dump($GLOBALS['x']); + $GLOBALS['x']++; + var_dump($GLOBALS['x']); + $GLOBALS['x'] += 2; + var_dump($GLOBALS['x']); + unset($GLOBALS['y']); + var_dump(isset($GLOBALS['x'])); + var_dump(isset($GLOBALS['y'])); + $GLOBALS['z'][] = 1; +} + +$y = 1; +test(); +var_dump($x, $y, $z); + +$ref = 1; +$GLOBALS['z'] =& $ref; +$ref++; +var_dump($z); + +$x = 1; +$ref2 =& $GLOBALS['x']; +$ref2++; +var_dump($x); + +?> +--EXPECTF-- +Warning: Undefined global variable $x in %s on line %d +NULL +int(1) +int(2) +int(4) +bool(true) +bool(false) + +Warning: Undefined variable $y in %s on line %d +int(4) +NULL +array(1) { + [0]=> + int(1) +} +int(2) +int(2) diff --git a/Zend/tests/return_types/internal_functions001.phpt b/Zend/tests/return_types/internal_functions001.phpt index 153e32ca6e..183f394c9b 100644 --- a/Zend/tests/return_types/internal_functions001.phpt +++ b/Zend/tests/return_types/internal_functions001.phpt @@ -2,7 +2,7 @@ Return type for internal functions --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); // Internal function return types are only checked in debug builds if (!PHP_DEBUG) die('skip requires debug build'); ?> diff --git a/Zend/tests/return_types/internal_functions002.phpt b/Zend/tests/return_types/internal_functions002.phpt index 531b6c452d..f6287b72f0 100644 --- a/Zend/tests/return_types/internal_functions002.phpt +++ b/Zend/tests/return_types/internal_functions002.phpt @@ -2,7 +2,7 @@ Return type for internal functions 2 --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); --FILE-- <?php zend_test_nullable_array_return(); diff --git a/Zend/tests/return_types/internal_functions003.phpt b/Zend/tests/return_types/internal_functions003.phpt index a7e71204fa..754b14574d 100644 --- a/Zend/tests/return_types/internal_functions003.phpt +++ b/Zend/tests/return_types/internal_functions003.phpt @@ -2,7 +2,7 @@ Return type for internal functions 3: Void return type --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); --FILE-- <?php var_dump(zend_test_void_return()); diff --git a/Zend/tests/str_or_obj_of_class_zpp.phpt b/Zend/tests/str_or_obj_of_class_zpp.phpt index b8f5d8492f..6f6a155895 100644 --- a/Zend/tests/str_or_obj_of_class_zpp.phpt +++ b/Zend/tests/str_or_obj_of_class_zpp.phpt @@ -2,7 +2,7 @@ Test Z_PARAM_OBJ_OF_CLASS_OR_STR() and Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/str_or_obj_zpp.phpt b/Zend/tests/str_or_obj_zpp.phpt index 301abd8ddc..6328c61b2d 100644 --- a/Zend/tests/str_or_obj_zpp.phpt +++ b/Zend/tests/str_or_obj_zpp.phpt @@ -2,7 +2,7 @@ Test Z_PARAM_OBJ_OR_STR() and Z_PARAM_OBJ_OR_STR_OR_NULL --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/traits/bug69579.phpt b/Zend/tests/traits/bug69579.phpt index 94387ce3cd..c12f54a374 100644 --- a/Zend/tests/traits/bug69579.phpt +++ b/Zend/tests/traits/bug69579.phpt @@ -2,7 +2,7 @@ Bug #69579 (Internal trait double-free) --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip zend-test extension not loaded'); +if (!extension_loaded('zend_test')) die('skip zend_test extension not loaded'); ?> --FILE-- <?php diff --git a/Zend/tests/type_declarations/typed_properties_095.phpt b/Zend/tests/type_declarations/typed_properties_095.phpt index 8470d4f437..7e92cec682 100644 --- a/Zend/tests/type_declarations/typed_properties_095.phpt +++ b/Zend/tests/type_declarations/typed_properties_095.phpt @@ -1,7 +1,7 @@ --TEST-- Typed properties in internal classes --SKIPIF-- -<?php if (!extension_loaded('zend-test')) die('skip requires zend-test'); ?> +<?php if (!extension_loaded('zend_test')) die('skip requires zend_test'); ?> --FILE-- <?php diff --git a/Zend/tests/type_declarations/union_types/inheritance_internal.phpt b/Zend/tests/type_declarations/union_types/inheritance_internal.phpt index bb53411cad..84d0f88211 100644 --- a/Zend/tests/type_declarations/union_types/inheritance_internal.phpt +++ b/Zend/tests/type_declarations/union_types/inheritance_internal.phpt @@ -2,7 +2,7 @@ Inheritance of union type from internal class --SKIPIF-- <?php -if (!extension_loaded('zend-test')) die('skip requires zend-test extension'); +if (!extension_loaded('zend_test')) die('skip requires zend_test extension'); ?> --FILE-- <?php diff --git a/Zend/tests/undef_index_to_exception.phpt b/Zend/tests/undef_index_to_exception.phpt index c01aaba658..bbe13c0e71 100644 --- a/Zend/tests/undef_index_to_exception.phpt +++ b/Zend/tests/undef_index_to_exception.phpt @@ -42,5 +42,5 @@ array(0) { Undefined array key "key" array(0) { } -Undefined array key "test" +Undefined global variable $test Undefined variable $test diff --git a/Zend/tests/unset_cv09.phpt b/Zend/tests/unset_cv09.phpt deleted file mode 100644 index 63a7eab0f2..0000000000 --- a/Zend/tests/unset_cv09.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -unset() CV 9 (unset() of global variable in array_pop($GLOBALS)) ---FILE-- -<?php -$x = "ok\n"; -echo array_pop($GLOBALS); -echo $x; -echo "ok\n"; -?> ---EXPECTF-- -ok - -Warning: Undefined variable $x in %s on line %d -ok diff --git a/Zend/tests/unset_cv10.phpt b/Zend/tests/unset_cv10.phpt index c62b8ae57a..e8908be0f7 100644 --- a/Zend/tests/unset_cv10.phpt +++ b/Zend/tests/unset_cv10.phpt @@ -2,6 +2,7 @@ unset() CV 10 (unset() of global variable in ArrayObject::offsetUnset($GLOBALS)) --FILE-- <?php +/* This is working on a copy of $GLOBALS, so nothing interesting happens here. */ $a = new ArrayObject($GLOBALS); $x = "ok\n"; echo $x; @@ -12,5 +13,6 @@ echo "ok\n"; --EXPECTF-- ok -Warning: Undefined variable $x in %s on line %d +Warning: Undefined array key "x" in %s on line %d +ok ok diff --git a/Zend/zend.c b/Zend/zend.c index cbd5aef42b..4da7991db6 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -34,6 +34,7 @@ #include "zend_cpuinfo.h" #include "zend_attributes.h" #include "zend_observer.h" +#include "Optimizer/zend_optimizer.h" static size_t global_map_ptr_last = 0; @@ -60,7 +61,7 @@ ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_API zend_utility_values zend_uv; -ZEND_API zend_bool zend_dtrace_enabled; +ZEND_API bool zend_dtrace_enabled; /* version information */ static char *zend_version_info; @@ -92,7 +93,7 @@ static void (*zend_message_dispatcher_p)(zend_long message, const void *data); static zval *(*zend_get_configuration_directive_p)(zend_string *name); #if ZEND_RC_DEBUG -ZEND_API zend_bool zend_rc_debug = 0; +ZEND_API bool zend_rc_debug = 0; #endif static ZEND_INI_MH(OnUpdateErrorReporting) /* {{{ */ @@ -108,7 +109,7 @@ static ZEND_INI_MH(OnUpdateErrorReporting) /* {{{ */ static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */ { - zend_bool val; + bool val; val = zend_ini_parse_bool(new_value); gc_enable(val); @@ -290,7 +291,7 @@ ZEND_API zend_string *zend_strpprintf_unchecked(size_t max_len, const char *form static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent); -static void print_hash(smart_str *buf, HashTable *ht, int indent, zend_bool is_object) /* {{{ */ +static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object) /* {{{ */ { zval *tmp; zend_string *string_key; @@ -524,7 +525,7 @@ static FILE *zend_fopen_wrapper(const char *filename, zend_string **opened_path) /* }}} */ #ifdef ZTS -static zend_bool short_tags_default = 1; +static bool short_tags_default = 1; static uint32_t compiler_options_default = ZEND_COMPILE_DEFAULT; #else # define short_tags_default 1 @@ -784,15 +785,10 @@ static void module_destructor_zval(zval *zv) /* {{{ */ } /* }}} */ -static zend_bool php_auto_globals_create_globals(zend_string *name) /* {{{ */ +static bool php_auto_globals_create_globals(zend_string *name) /* {{{ */ { - zval globals; - - /* IS_ARRAY, but with ref-counter 1 and not IS_TYPE_REFCOUNTED */ - ZVAL_ARR(&globals, &EG(symbol_table)); - Z_TYPE_FLAGS_P(&globals) = 0; - ZVAL_NEW_REF(&globals, &globals); - zend_hash_update(&EG(symbol_table), name, &globals); + /* While we keep registering $GLOBALS as an auto-global, we do not create an + * actual variable for it. Access to it handled specially by the compiler. */ return 0; } /* }}} */ @@ -958,6 +954,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ php_win32_cp_setup(); #endif + zend_optimizer_startup(); + #ifdef ZTS tsrm_set_new_thread_end_handler(zend_new_thread_end_handler); tsrm_set_shutdown_handler(zend_interned_strings_dtor); @@ -1117,6 +1115,8 @@ void zend_shutdown(void) /* {{{ */ } #endif zend_destroy_rsrc_list_dtors(); + + zend_optimizer_shutdown(); } /* }}} */ @@ -1292,7 +1292,7 @@ static ZEND_COLD void zend_error_impl( zval params[4]; zval retval; zval orig_user_error_handler; - zend_bool in_compilation; + bool in_compilation; zend_class_entry *saved_class_entry; zend_stack loop_var_stack; zend_stack delayed_oplines_stack; @@ -1596,7 +1596,7 @@ ZEND_API ZEND_COLD void zend_value_error(const char *format, ...) /* {{{ */ va_end(va); } /* }}} */ -ZEND_API ZEND_COLD void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) /* {{{ */ +ZEND_API ZEND_COLD void zend_output_debug_string(bool trigger_break, const char *format, ...) /* {{{ */ { #if ZEND_DEBUG va_list args; diff --git a/Zend/zend.h b/Zend/zend.h index a851ded9b9..6a2a834d93 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.0.3-dev" +#define ZEND_VERSION "4.1.0-dev" #define ZEND_ENGINE_3 @@ -203,7 +203,7 @@ typedef struct _zend_utility_functions { } zend_utility_functions; typedef struct _zend_utility_values { - zend_bool html_errors; + bool html_errors; } zend_utility_values; typedef size_t (*zend_write_func_t)(const char *str, size_t str_length); @@ -255,7 +255,7 @@ ZEND_API void zend_print_flat_zval_r(zval *expr); #define zend_print_variable(var) \ zend_print_zval((var), 0) -ZEND_API ZEND_COLD void zend_output_debug_string(zend_bool trigger_break, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); +ZEND_API ZEND_COLD void zend_output_debug_string(bool trigger_break, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); ZEND_API void zend_activate(void); ZEND_API void zend_deactivate(void); @@ -316,7 +316,7 @@ extern ZEND_API zend_class_entry *zend_standard_class_def; extern ZEND_API zend_utility_values zend_uv; /* If DTrace is available and enabled */ -extern ZEND_API zend_bool zend_dtrace_enabled; +extern ZEND_API bool zend_dtrace_enabled; END_EXTERN_C() #define ZEND_UV(name) (zend_uv.name) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 11760d50f0..5f558c4930 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -426,7 +426,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **p } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest) /* {{{ */ +ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, bool *dest) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) { *dest = zend_is_true(arg); @@ -437,7 +437,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest) } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest) /* {{{ */ +ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, bool *dest) /* {{{ */ { if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { return 0; @@ -644,10 +644,10 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec case 'l': { zend_long *p = va_arg(*va, zend_long *); - zend_bool *is_null = NULL; + bool *is_null = NULL; if (check_null) { - is_null = va_arg(*va, zend_bool *); + is_null = va_arg(*va, bool *); } if (!zend_parse_arg_long(arg, p, is_null, check_null)) { @@ -659,10 +659,10 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec case 'd': { double *p = va_arg(*va, double *); - zend_bool *is_null = NULL; + bool *is_null = NULL; if (check_null) { - is_null = va_arg(*va, zend_bool *); + is_null = va_arg(*va, bool *); } if (!zend_parse_arg_double(arg, p, is_null, check_null)) { @@ -731,11 +731,11 @@ static const char *zend_parse_arg_impl(zval *arg, va_list *va, const char **spec case 'b': { - zend_bool *p = va_arg(*va, zend_bool *); - zend_bool *is_null = NULL; + bool *p = va_arg(*va, bool *); + bool *is_null = NULL; if (check_null) { - is_null = va_arg(*va, zend_bool *); + is_null = va_arg(*va, bool *); } if (!zend_parse_arg_bool(arg, p, is_null, check_null)) { @@ -956,8 +956,8 @@ static zend_result zend_parse_va_args(uint32_t num_args, const char *type_spec, uint32_t max_num_args = 0; uint32_t post_varargs = 0; zval *arg; - zend_bool have_varargs = 0; - zend_bool have_optional_args = 0; + bool have_varargs = 0; + bool have_optional_args = 0; zval **varargs = NULL; int *n_varargs = NULL; @@ -1130,7 +1130,7 @@ ZEND_API zend_result zend_parse_method_parameters(uint32_t num_args, zval *this_ * Z_OBJ(EG(This)) to NULL when calling an internal function with common.scope == NULL. * In that case EG(This) would still be the $this from the calling code and we'd take the * wrong branch here. */ - zend_bool is_method = EG(current_execute_data)->func->common.scope != NULL; + bool is_method = EG(current_execute_data)->func->common.scope != NULL; if (!is_method || !this_ptr || Z_TYPE_P(this_ptr) != IS_OBJECT) { va_start(va, type_spec); @@ -1531,6 +1531,33 @@ ZEND_API void add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, c } /* }}} */ +ZEND_API void add_assoc_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr) /* {{{ */ +{ + zval tmp; + + ZVAL_ARR(&tmp, arr); + zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp); +} +/* }}} */ + +ZEND_API void add_assoc_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj) /* {{{ */ +{ + zval tmp; + + ZVAL_OBJ(&tmp, obj); + zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp); +} +/* }}} */ + +ZEND_API void add_assoc_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_REF(&tmp, ref); + zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, &tmp); +} +/* }}} */ + ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval *value) /* {{{ */ { zend_symtable_str_update(Z_ARRVAL_P(arg), key, key_len, value); @@ -1609,6 +1636,33 @@ ZEND_API void add_index_stringl(zval *arg, zend_ulong index, const char *str, si } /* }}} */ +ZEND_API void add_index_array(zval *arg, zend_ulong index, zend_array *arr) /* {{{ */ +{ + zval tmp; + + ZVAL_ARR(&tmp, arr); + zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp); +} +/* }}} */ + +ZEND_API void add_index_object(zval *arg, zend_ulong index, zend_object *obj) /* {{{ */ +{ + zval tmp; + + ZVAL_OBJ(&tmp, obj); + zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp); +} +/* }}} */ + +ZEND_API void add_index_reference(zval *arg, zend_ulong index, zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_REF(&tmp, ref); + zend_hash_index_update(Z_ARRVAL_P(arg), index, &tmp); +} +/* }}} */ + ZEND_API zend_result add_next_index_long(zval *arg, zend_long n) /* {{{ */ { zval tmp; @@ -1627,7 +1681,7 @@ ZEND_API zend_result add_next_index_null(zval *arg) /* {{{ */ } /* }}} */ -ZEND_API zend_result add_next_index_bool(zval *arg, zend_bool b) /* {{{ */ +ZEND_API zend_result add_next_index_bool(zval *arg, bool b) /* {{{ */ { zval tmp; @@ -1681,6 +1735,33 @@ ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t l } /* }}} */ +ZEND_API zend_result add_next_index_array(zval *arg, zend_array *arr) /* {{{ */ +{ + zval tmp; + + ZVAL_ARR(&tmp, arr); + return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE; +} +/* }}} */ + +ZEND_API zend_result add_next_index_object(zval *arg, zend_object *obj) /* {{{ */ +{ + zval tmp; + + ZVAL_OBJ(&tmp, obj); + return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE; +} +/* }}} */ + +ZEND_API zend_result add_next_index_reference(zval *arg, zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_REF(&tmp, ref); + return zend_hash_next_index_insert(Z_ARRVAL_P(arg), &tmp) ? SUCCESS : FAILURE; +} +/* }}} */ + ZEND_API zend_result array_set_zval_key(HashTable *ht, zval *key, zval *value) /* {{{ */ { zval *result; @@ -1798,6 +1879,36 @@ ZEND_API void add_property_stringl_ex(zval *arg, const char *key, size_t key_len } /* }}} */ +ZEND_API void add_property_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr) /* {{{ */ +{ + zval tmp; + + ZVAL_ARR(&tmp, arr); + add_property_zval_ex(arg, key, key_len, &tmp); + zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */ +} +/* }}} */ + +ZEND_API void add_property_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj) /* {{{ */ +{ + zval tmp; + + ZVAL_OBJ(&tmp, obj); + add_property_zval_ex(arg, key, key_len, &tmp); + zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */ +} +/* }}} */ + +ZEND_API void add_property_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_REF(&tmp, ref); + add_property_zval_ex(arg, key, key_len, &tmp); + zval_ptr_dtor(&tmp); /* write_property will add 1 to refcount */ +} +/* }}} */ + ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, zval *value) /* {{{ */ { zend_string *str; @@ -2838,7 +2949,7 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ /* }}} */ // TODO num_symbol_tables as unsigned int? -ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, zend_bool is_ref, int num_symbol_tables, ...) /* {{{ */ +ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...) /* {{{ */ { HashTable *symbol_table; va_list symbol_table_list; @@ -3337,11 +3448,11 @@ ZEND_API zend_string *zend_get_callable_name(zval *callable) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_is_callable_at_frame( +ZEND_API bool zend_is_callable_at_frame( zval *callable, zend_object *object, zend_execute_data *frame, uint32_t check_flags, zend_fcall_info_cache *fcc, char **error) /* {{{ */ { - zend_bool ret; + bool ret; zend_fcall_info_cache fcc_local; bool strict_class = 0; @@ -3458,7 +3569,7 @@ check_func: } /* }}} */ -ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */ +ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error) /* {{{ */ { /* Determine callability at the first parent user frame. */ zend_execute_data *frame = EG(current_execute_data); @@ -3466,20 +3577,20 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint frame = frame->prev_execute_data; } - zend_bool ret = zend_is_callable_at_frame(callable, object, frame, check_flags, fcc, error); + bool ret = zend_is_callable_at_frame(callable, object, frame, check_flags, fcc, error); if (callable_name) { *callable_name = zend_get_callable_name_ex(callable, object); } return ret; } -ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */ +ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name) /* {{{ */ { return zend_is_callable_ex(callable, NULL, check_flags, callable_name, NULL, NULL); } /* }}} */ -ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name) /* {{{ */ +ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name) /* {{{ */ { zend_fcall_info_cache fcc; @@ -3679,7 +3790,7 @@ static inline zend_string *zval_make_interned_string(zval *zv) /* {{{ */ return Z_STR_P(zv); } -static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) { +static zend_always_inline bool is_persistent_class(zend_class_entry *ce) { return (ce->type & ZEND_INTERNAL_CLASS) && ce->info.internal.module->type == MODULE_PERSISTENT; } @@ -3787,7 +3898,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z } /* }}} */ -ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val, zend_bool strict) /* {{{ */ +ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val, bool strict) /* {{{ */ { if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, val, strict))) { zval_ptr_dtor(val); @@ -3815,7 +3926,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_null(zend_reference *ref) /* {{{ } /* }}} */ -ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val) /* {{{ */ +ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, bool val) /* {{{ */ { zval tmp; @@ -3905,7 +4016,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval(zend_reference *ref, zval *z } /* }}} */ -ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict) /* {{{ */ +ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, bool strict) /* {{{ */ { zval tmp; @@ -4056,7 +4167,7 @@ ZEND_API void zend_declare_class_constant_long(zend_class_entry *ce, const char } /* }}} */ -ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value) /* {{{ */ +ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, bool value) /* {{{ */ { zval constant; @@ -4297,7 +4408,7 @@ ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope } /* }}} */ -ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zend_bool silent, zval *rv) /* {{{ */ +ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv) /* {{{ */ { zval *value; zend_class_entry *old_scope = EG(fake_scope); @@ -4311,7 +4422,7 @@ ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *objec } /* }}} */ -ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_bool silent, zval *rv) /* {{{ */ +ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv) /* {{{ */ { zval *value; zend_string *str; @@ -4323,7 +4434,7 @@ ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, } /* }}} */ -ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, zend_bool silent) /* {{{ */ +ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, bool silent) /* {{{ */ { zval *property; zend_class_entry *old_scope = EG(fake_scope); @@ -4336,7 +4447,7 @@ ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string } /* }}} */ -ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent) /* {{{ */ +ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, bool silent) /* {{{ */ { zend_string *key = zend_string_init(name, name_length, 0); zval *property = zend_read_static_property_ex(scope, key, silent); @@ -4382,7 +4493,7 @@ ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce) } /* }}} */ -ZEND_API zend_bool zend_is_iterable(zval *iterable) /* {{{ */ +ZEND_API bool zend_is_iterable(zval *iterable) /* {{{ */ { switch (Z_TYPE_P(iterable)) { case IS_ARRAY: @@ -4395,7 +4506,7 @@ ZEND_API zend_bool zend_is_iterable(zval *iterable) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_is_countable(zval *countable) /* {{{ */ +ZEND_API bool zend_is_countable(zval *countable) /* {{{ */ { switch (Z_TYPE_P(countable)) { case IS_ARRAY: @@ -4419,7 +4530,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def zend_string *code = zend_string_concat3( "<?php ", sizeof("<?php ") - 1, default_value, strlen(default_value), ";", 1); - ast = zend_compile_string_to_ast(code, &ast_arena, ""); + ast = zend_compile_string_to_ast(code, &ast_arena, ZSTR_EMPTY_ALLOC()); zend_string_release(code); if (!ast) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 290db040b2..89eb6312d6 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -352,12 +352,12 @@ ZEND_API ZEND_COLD void zend_wrong_param_count(void); ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc); ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *object); ZEND_API zend_string *zend_get_callable_name(zval *callable); -ZEND_API zend_bool zend_is_callable_at_frame( +ZEND_API bool zend_is_callable_at_frame( zval *callable, zend_object *object, zend_execute_data *frame, uint32_t check_flags, zend_fcall_info_cache *fcc, char **error); -ZEND_API zend_bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error); -ZEND_API zend_bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name); -ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_name); +ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error); +ZEND_API bool zend_is_callable(zval *callable, uint32_t check_flags, zend_string **callable_name); +ZEND_API bool zend_make_callable(zval *callable, zend_string **callable_name); ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); @@ -376,7 +376,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c ZEND_API void zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API void zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API void zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); -ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_bool value); +ZEND_API void zend_declare_class_constant_bool(zend_class_entry *ce, const char *name, size_t name_length, bool value); ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const char *name, size_t name_length, double value); ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length); ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value); @@ -403,11 +403,11 @@ ZEND_API zend_result zend_update_static_property_double(zend_class_entry *scope, ZEND_API zend_result zend_update_static_property_string(zend_class_entry *scope, const char *name, size_t name_length, const char *value); ZEND_API zend_result zend_update_static_property_stringl(zend_class_entry *scope, const char *name, size_t name_length, const char *value, size_t value_length); -ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zend_bool silent, zval *rv); -ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_bool silent, zval *rv); +ZEND_API zval *zend_read_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, bool silent, zval *rv); +ZEND_API zval *zend_read_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, bool silent, zval *rv); -ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, zend_bool silent); -ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, zend_bool silent); +ZEND_API zval *zend_read_static_property_ex(zend_class_entry *scope, zend_string *name, bool silent); +ZEND_API zval *zend_read_static_property(zend_class_entry *scope, const char *name, size_t name_length, bool silent); ZEND_API const char *zend_get_type_by_const(int type); @@ -445,6 +445,9 @@ ZEND_API void add_assoc_double_ex(zval *arg, const char *key, size_t key_len, do ZEND_API void add_assoc_str_ex(zval *arg, const char *key, size_t key_len, zend_string *str); ZEND_API void add_assoc_string_ex(zval *arg, const char *key, size_t key_len, const char *str); ZEND_API void add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, const char *str, size_t length); +ZEND_API void add_assoc_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr); +ZEND_API void add_assoc_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj); +ZEND_API void add_assoc_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref); ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval *value); #define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n) @@ -455,6 +458,9 @@ ZEND_API void add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval #define add_assoc_str(__arg, __key, __str) add_assoc_str_ex(__arg, __key, strlen(__key), __str) #define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str) #define add_assoc_stringl(__arg, __key, __str, __length) add_assoc_stringl_ex(__arg, __key, strlen(__key), __str, __length) +#define add_assoc_array(__arg, __key, __arr) add_assoc_array_ex(__arg, __key, strlen(__key), __arr) +#define add_assoc_object(__arg, __key, __obj) add_assoc_object_ex(__arg, __key, strlen(__key), __obj) +#define add_assoc_reference(__arg, __key, __ref) add_assoc_object_ex(__arg, __key, strlen(__key), __ref) #define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value) ZEND_API void add_index_long(zval *arg, zend_ulong index, zend_long n); @@ -465,6 +471,9 @@ ZEND_API void add_index_double(zval *arg, zend_ulong index, double d); ZEND_API void add_index_str(zval *arg, zend_ulong index, zend_string *str); ZEND_API void add_index_string(zval *arg, zend_ulong index, const char *str); ZEND_API void add_index_stringl(zval *arg, zend_ulong index, const char *str, size_t length); +ZEND_API void add_index_array(zval *arg, zend_ulong index, zend_array *arr); +ZEND_API void add_index_object(zval *arg, zend_ulong index, zend_object *obj); +ZEND_API void add_index_reference(zval *arg, zend_ulong index, zend_reference *ref); static zend_always_inline zend_result add_index_zval(zval *arg, zend_ulong index, zval *value) { @@ -473,12 +482,15 @@ static zend_always_inline zend_result add_index_zval(zval *arg, zend_ulong index ZEND_API zend_result add_next_index_long(zval *arg, zend_long n); ZEND_API zend_result add_next_index_null(zval *arg); -ZEND_API zend_result add_next_index_bool(zval *arg, zend_bool b); +ZEND_API zend_result add_next_index_bool(zval *arg, bool b); ZEND_API zend_result add_next_index_resource(zval *arg, zend_resource *r); ZEND_API zend_result add_next_index_double(zval *arg, double d); ZEND_API zend_result add_next_index_str(zval *arg, zend_string *str); ZEND_API zend_result add_next_index_string(zval *arg, const char *str); ZEND_API zend_result add_next_index_stringl(zval *arg, const char *str, size_t length); +ZEND_API zend_result add_next_index_array(zval *arg, zend_array *arr); +ZEND_API zend_result add_next_index_object(zval *arg, zend_object *obj); +ZEND_API zend_result add_next_index_reference(zval *arg, zend_reference *ref); static zend_always_inline zend_result add_next_index_zval(zval *arg, zval *value) { @@ -495,6 +507,9 @@ ZEND_API void add_property_double_ex(zval *arg, const char *key, size_t key_len, ZEND_API void add_property_str_ex(zval *arg, const char *key, size_t key_len, zend_string *str); ZEND_API void add_property_string_ex(zval *arg, const char *key, size_t key_len, const char *str); ZEND_API void add_property_stringl_ex(zval *arg, const char *key, size_t key_len, const char *str, size_t length); +ZEND_API void add_property_array_ex(zval *arg, const char *key, size_t key_len, zend_array *arr); +ZEND_API void add_property_object_ex(zval *arg, const char *key, size_t key_len, zend_object *obj); +ZEND_API void add_property_reference_ex(zval *arg, const char *key, size_t key_len, zend_reference *ref); ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, zval *value); #define add_property_long(__arg, __key, __n) add_property_long_ex(__arg, __key, strlen(__key), __n) @@ -505,6 +520,9 @@ ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, z #define add_property_str(__arg, __key, __str) add_property_str_ex(__arg, __key, strlen(__key), __str) #define add_property_string(__arg, __key, __str) add_property_string_ex(__arg, __key, strlen(__key), __str) #define add_property_stringl(__arg, __key, __str, __length) add_property_stringl_ex(__arg, __key, strlen(__key), __str, __length) +#define add_property_array(__arg, __key, __arr) add_property_array_ex(__arg, __key, strlen(__key), __arr) +#define add_property_object(__arg, __key, __obj) add_property_object_ex(__arg, __key, strlen(__key), __obj) +#define add_property_reference(__arg, __key, __ref) add_property_reference_ex(__arg, __key, strlen(__key), __ref) #define add_property_zval(__arg, __key, __value) add_property_zval_ex(__arg, __key, strlen(__key), __value) @@ -605,7 +623,7 @@ static zend_always_inline void zend_call_known_instance_method_with_1_params( ZEND_API void zend_call_known_instance_method_with_2_params( zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2); -ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, zend_bool is_ref, int num_symbol_tables, ...); +ZEND_API zend_result zend_set_hash_symbol(zval *symbol, const char *name, size_t name_length, bool is_ref, int num_symbol_tables, ...); ZEND_API zend_result zend_delete_global_variable(zend_string *name); @@ -630,9 +648,9 @@ static zend_always_inline zend_result zend_forbid_dynamic_call(const char *func_ ZEND_API ZEND_COLD const char *zend_get_object_type(const zend_class_entry *ce); -ZEND_API zend_bool zend_is_iterable(zval *iterable); +ZEND_API bool zend_is_iterable(zval *iterable); -ZEND_API zend_bool zend_is_countable(zval *countable); +ZEND_API bool zend_is_countable(zval *countable); ZEND_API zend_result zend_get_default_from_internal_arg_info( zval *default_value_zval, zend_internal_arg_info *arg_info); @@ -779,11 +797,11 @@ END_EXTERN_C() /* May modify arg in-place. Will free arg in failure case (and take ownership in success case). * Prefer using the ZEND_TRY_ASSIGN_* macros over these APIs. */ -ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *zv, zend_bool strict); +ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *zv, bool strict); ZEND_API zend_result zend_try_assign_typed_ref(zend_reference *ref, zval *zv); ZEND_API zend_result zend_try_assign_typed_ref_null(zend_reference *ref); -ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val); +ZEND_API zend_result zend_try_assign_typed_ref_bool(zend_reference *ref, bool val); ZEND_API zend_result zend_try_assign_typed_ref_long(zend_reference *ref, zend_long lval); ZEND_API zend_result zend_try_assign_typed_ref_double(zend_reference *ref, double dval); ZEND_API zend_result zend_try_assign_typed_ref_empty_string(zend_reference *ref); @@ -793,7 +811,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_stringl(zend_reference *ref, cons ZEND_API zend_result zend_try_assign_typed_ref_arr(zend_reference *ref, zend_array *arr); ZEND_API zend_result zend_try_assign_typed_ref_res(zend_reference *ref, zend_resource *res); ZEND_API zend_result zend_try_assign_typed_ref_zval(zend_reference *ref, zval *zv); -ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict); +ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, bool strict); #define _ZEND_TRY_ASSIGN_NULL(zv, is_ref) do { \ zval *_zv = zv; \ @@ -1282,8 +1300,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * zval *_real_arg, *_arg = NULL; \ zend_expected_type _expected_type = Z_EXPECTED_LONG; \ char *_error = NULL; \ - ZEND_ATTRIBUTE_UNUSED zend_bool _dummy; \ - zend_bool _optional = 0; \ + ZEND_ATTRIBUTE_UNUSED bool _dummy; \ + bool _optional = 0; \ int _error_code = ZPP_ERROR_OK; \ ((void)_i); \ ((void)_real_arg); \ @@ -1314,6 +1332,7 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * } while (0) #define ZEND_PARSE_PARAMETERS_END_EX(failure) \ + ZEND_ASSERT(_i == _max_num_args || _max_num_args == (uint32_t) -1); \ } while (0); \ if (UNEXPECTED(_error_code != ZPP_ERROR_OK)) { \ if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ @@ -1396,17 +1415,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_ITERABLE_EX(dest, 1) /* old "b" */ -#define Z_PARAM_BOOL_EX2(dest, is_null, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_BOOL_EX(dest, is_null, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_bool(_arg, &dest, &is_null, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_BOOL_OR_NULL : Z_EXPECTED_BOOL; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_BOOL_EX(dest, is_null, check_null, separate) \ - Z_PARAM_BOOL_EX2(dest, is_null, check_null, separate, separate) - #define Z_PARAM_BOOL(dest) \ Z_PARAM_BOOL_EX(dest, _dummy, 0, 0) @@ -1414,16 +1430,13 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_BOOL_EX(dest, is_null, 1, 0) /* old "C" */ -#define Z_PARAM_CLASS_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_CLASS_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_class(_arg, &dest, _i, check_null))) { \ _error_code = ZPP_ERROR_FAILURE; \ break; \ } -#define Z_PARAM_CLASS_EX(dest, check_null, separate) \ - Z_PARAM_CLASS_EX2(dest, check_null, separate, separate) - #define Z_PARAM_CLASS(dest) \ Z_PARAM_CLASS_EX(dest, 0, 0) @@ -1479,17 +1492,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_OBJ_OF_CLASS_OR_STR_EX(destination_object, base_ce, destination_string, 1); /* old "d" */ -#define Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_DOUBLE_EX(dest, is_null, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_double(_arg, &dest, &is_null, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_DOUBLE_OR_NULL : Z_EXPECTED_DOUBLE; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_DOUBLE_EX(dest, is_null, check_null, separate) \ - Z_PARAM_DOUBLE_EX2(dest, is_null, check_null, separate, separate) - #define Z_PARAM_DOUBLE(dest) \ Z_PARAM_DOUBLE_EX(dest, _dummy, 0, 0) @@ -1497,8 +1507,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_DOUBLE_EX(dest, is_null, 1, 0) /* old "f" */ -#define Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_func(_arg, &dest_fci, &dest_fcc, check_null, &_error))) { \ if (!_error) { \ _expected_type = check_null ? Z_EXPECTED_FUNC_OR_NULL : Z_EXPECTED_FUNC; \ @@ -1509,9 +1519,6 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * break; \ } \ -#define Z_PARAM_FUNC_EX(dest_fci, dest_fcc, check_null, separate) \ - Z_PARAM_FUNC_EX2(dest_fci, dest_fcc, check_null, separate, separate) - #define Z_PARAM_FUNC(dest_fci, dest_fcc) \ Z_PARAM_FUNC_EX(dest_fci, dest_fcc, 0, 0) @@ -1566,17 +1573,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_ARRAY_OR_OBJECT_HT_EX(dest, 0, 0) /* old "l" */ -#define Z_PARAM_LONG_EX2(dest, is_null, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_LONG_EX(dest, is_null, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_long(_arg, &dest, &is_null, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_LONG_OR_NULL : Z_EXPECTED_LONG; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_LONG_EX(dest, is_null, check_null, separate) \ - Z_PARAM_LONG_EX2(dest, is_null, check_null, separate, separate) - #define Z_PARAM_LONG(dest) \ Z_PARAM_LONG_EX(dest, _dummy, 0, 0) @@ -1599,35 +1603,29 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_NUMBER_EX(dest, 0) /* old "o" */ -#define Z_PARAM_OBJECT_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_OBJECT_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, NULL, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_OBJECT_EX(dest, check_null, separate) \ - Z_PARAM_OBJECT_EX2(dest, check_null, separate, separate) - #define Z_PARAM_OBJECT(dest) \ Z_PARAM_OBJECT_EX(dest, 0, 0) #define Z_PARAM_OBJECT_OR_NULL(dest) \ Z_PARAM_OBJECT_EX(dest, 1, 0) -/* The same as Z_PARAM_OBJECT_EX2 except that dest is a zend_object rather than a zval */ -#define Z_PARAM_OBJ_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +/* The same as Z_PARAM_OBJECT_EX except that dest is a zend_object rather than a zval */ +#define Z_PARAM_OBJ_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, NULL, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_OBJ_EX(dest, check_null, separate) \ - Z_PARAM_OBJ_EX2(dest, check_null, separate, separate) - #define Z_PARAM_OBJ(dest) \ Z_PARAM_OBJ_EX(dest, 0, 0) @@ -1635,8 +1633,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_OBJ_EX(dest, 1, 0) /* old "O" */ -#define Z_PARAM_OBJECT_OF_CLASS_EX2(dest, _ce, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_object(_arg, &dest, _ce, check_null))) { \ if (_ce) { \ _error = ZSTR_VAL((_ce)->name); \ @@ -1649,18 +1647,15 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * } \ } -#define Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, check_null, separate) \ - Z_PARAM_OBJECT_OF_CLASS_EX2(dest, _ce, check_null, separate, separate) - #define Z_PARAM_OBJECT_OF_CLASS(dest, _ce) \ Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, 0, 0) #define Z_PARAM_OBJECT_OF_CLASS_OR_NULL(dest, _ce) \ Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, 1, 0) -/* The same as Z_PARAM_OBJECT_OF_CLASS_EX2 except that dest is a zend_object rather than a zval */ -#define Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +/* The same as Z_PARAM_OBJECT_OF_CLASS_EX except that dest is a zend_object rather than a zval */ +#define Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, _ce, check_null))) { \ if (_ce) { \ _error = ZSTR_VAL((_ce)->name); \ @@ -1673,9 +1668,6 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * } \ } -#define Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, check_null, separate) \ - Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, separate, separate) - #define Z_PARAM_OBJ_OF_CLASS(dest, _ce) \ Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, 0, 0) @@ -1697,17 +1689,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1) /* old "p" */ -#define Z_PARAM_PATH_EX2(dest, dest_len, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_PATH_EX(dest, dest_len, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_path(_arg, &dest, &dest_len, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_PATH_EX(dest, dest_len, check_null, separate) \ - Z_PARAM_PATH_EX2(dest, dest_len, check_null, separate, separate) - #define Z_PARAM_PATH(dest, dest_len) \ Z_PARAM_PATH_EX(dest, dest_len, 0, 0) @@ -1715,17 +1704,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_PATH_EX(dest, dest_len, 1, 0) /* old "P" */ -#define Z_PARAM_PATH_STR_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_PATH_STR_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_path_str(_arg, &dest, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_PATH_OR_NULL : Z_EXPECTED_PATH; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_PATH_STR_EX(dest, check_null, separate) \ - Z_PARAM_PATH_STR_EX2(dest, check_null, separate, separate) - #define Z_PARAM_PATH_STR(dest) \ Z_PARAM_PATH_STR_EX(dest, 0, 0) @@ -1733,17 +1719,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_PATH_STR_EX(dest, 1, 0) /* old "r" */ -#define Z_PARAM_RESOURCE_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_RESOURCE_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_resource(_arg, &dest, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_RESOURCE_OR_NULL : Z_EXPECTED_RESOURCE; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_RESOURCE_EX(dest, check_null, separate) \ - Z_PARAM_RESOURCE_EX2(dest, check_null, separate, separate) - #define Z_PARAM_RESOURCE(dest) \ Z_PARAM_RESOURCE_EX(dest, 0, 0) @@ -1751,17 +1734,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_RESOURCE_EX(dest, 1, 0) /* old "s" */ -#define Z_PARAM_STRING_EX2(dest, dest_len, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_STRING_EX(dest, dest_len, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_string(_arg, &dest, &dest_len, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_STRING_EX(dest, dest_len, check_null, separate) \ - Z_PARAM_STRING_EX2(dest, dest_len, check_null, separate, separate) - #define Z_PARAM_STRING(dest, dest_len) \ Z_PARAM_STRING_EX(dest, dest_len, 0, 0) @@ -1769,17 +1749,14 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * Z_PARAM_STRING_EX(dest, dest_len, 1, 0) /* old "S" */ -#define Z_PARAM_STR_EX2(dest, check_null, deref, separate) \ - Z_PARAM_PROLOGUE(deref, separate); \ +#define Z_PARAM_STR_EX(dest, check_null, deref) \ + Z_PARAM_PROLOGUE(deref, 0); \ if (UNEXPECTED(!zend_parse_arg_str(_arg, &dest, check_null))) { \ _expected_type = check_null ? Z_EXPECTED_STRING_OR_NULL : Z_EXPECTED_STRING; \ _error_code = ZPP_ERROR_WRONG_ARG; \ break; \ } -#define Z_PARAM_STR_EX(dest, check_null, separate) \ - Z_PARAM_STR_EX2(dest, check_null, separate, separate) - #define Z_PARAM_STR(dest) \ Z_PARAM_STR_EX(dest, 0, 0) @@ -1870,8 +1847,8 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char * /* Inlined implementations shared by new and old parameter parsing APIs */ ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, zend_bool *dest); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, zend_bool *dest); +ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(zval *arg, bool *dest); +ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(zval *arg, bool *dest); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(zval *arg, zend_long *dest); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(zval *arg, zend_long *dest); ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(zval *arg, double *dest); @@ -1881,7 +1858,7 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **des ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long); -static zend_always_inline bool zend_parse_arg_bool(zval *arg, zend_bool *dest, zend_bool *is_null, bool check_null) +static zend_always_inline bool zend_parse_arg_bool(zval *arg, bool *dest, bool *is_null, bool check_null) { if (check_null) { *is_null = 0; @@ -1899,7 +1876,7 @@ static zend_always_inline bool zend_parse_arg_bool(zval *arg, zend_bool *dest, z return 1; } -static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, zend_bool *is_null, bool check_null) +static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, bool *is_null, bool check_null) { if (check_null) { *is_null = 0; @@ -1915,7 +1892,7 @@ static zend_always_inline bool zend_parse_arg_long(zval *arg, zend_long *dest, z return 1; } -static zend_always_inline bool zend_parse_arg_double(zval *arg, double *dest, zend_bool *is_null, bool check_null) +static zend_always_inline bool zend_parse_arg_double(zval *arg, double *dest, bool *is_null, bool check_null) { if (check_null) { *is_null = 0; @@ -2050,7 +2027,7 @@ static zend_always_inline bool zend_parse_arg_array_ht(zval *arg, HashTable **de } static zend_always_inline bool zend_parse_arg_array_ht_or_long( - zval *arg, HashTable **dest_ht, zend_long *dest_long, zend_bool *is_null, bool allow_null + zval *arg, HashTable **dest_ht, zend_long *dest_long, bool *is_null, bool allow_null ) { if (allow_null) { *is_null = 0; @@ -2099,7 +2076,7 @@ static zend_always_inline bool zend_parse_arg_obj(zval *arg, zend_object **dest, } static zend_always_inline bool zend_parse_arg_obj_or_long( - zval *arg, zend_object **dest_obj, zend_class_entry *ce, zend_long *dest_long, zend_bool *is_null, bool allow_null + zval *arg, zend_object **dest_obj, zend_class_entry *ce, zend_long *dest_long, bool *is_null, bool allow_null ) { if (allow_null) { *is_null = 0; @@ -2182,7 +2159,7 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str( } static zend_always_inline bool zend_parse_arg_str_or_long(zval *arg, zend_string **dest_str, zend_long *dest_long, - zend_bool *is_null, bool allow_null) + bool *is_null, bool allow_null) { if (allow_null) { *is_null = 0; diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 098dda104a..97f77827d8 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -117,7 +117,7 @@ static size_t _real_page_size = ZEND_MM_PAGE_SIZE; /* Mac allows to track anonymous page via vmmap per TAG id. * user land applications are allowed to take from 240 to 255. */ -# define ZEND_MM_FD (250<<24) +# define ZEND_MM_FD (250u << 24u) #endif #ifndef ZEND_MM_STAT @@ -1510,7 +1510,7 @@ static zend_never_inline void *zend_mm_realloc_huge(zend_mm_heap *heap, void *pt return zend_mm_realloc_slow(heap, ptr, size, MIN(old_size, copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } -static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, zend_bool use_copy_size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, bool use_copy_size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t page_offset; size_t old_size; @@ -2789,7 +2789,7 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) #if ZEND_MM_CUSTOM tmp = getenv("USE_ZEND_ALLOC"); if (tmp && !zend_atoi(tmp, 0)) { - zend_bool tracked = (tmp = getenv("USE_TRACKED_ALLOC")) && zend_atoi(tmp, 0); + bool tracked = (tmp = getenv("USE_TRACKED_ALLOC")) && zend_atoi(tmp, 0); zend_mm_heap *mm_heap = alloc_globals->mm_heap = malloc(sizeof(zend_mm_heap)); memset(mm_heap, 0, sizeof(zend_mm_heap)); mm_heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_STD; diff --git a/Zend/zend_arena.h b/Zend/zend_arena.h index ff2d0a5fe6..a44082e52f 100644 --- a/Zend/zend_arena.h +++ b/Zend/zend_arena.h @@ -110,7 +110,7 @@ static zend_always_inline void zend_arena_release(zend_arena **arena_ptr, void * arena->ptr = (char*)checkpoint; } -static zend_always_inline zend_bool zend_arena_contains(zend_arena *arena, void *ptr) +static zend_always_inline bool zend_arena_contains(zend_arena *arena, void *ptr) { while (arena) { if ((char*)ptr > (char*)arena && (char*)ptr <= arena->ptr) { @@ -213,7 +213,7 @@ static zend_always_inline void zend_arena_release(zend_arena **arena_ptr, void * } } -static zend_always_inline zend_bool zend_arena_contains(zend_arena *arena, void *ptr) +static zend_always_inline bool zend_arena_contains(zend_arena *arena, void *ptr) { /* TODO: Dummy */ return 1; diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index cb61bec5d7..b2af322d91 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -423,7 +423,7 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki } #endif -static inline zend_bool is_power_of_two(uint32_t n) { +static inline bool is_power_of_two(uint32_t n) { return ((n != 0) && (n == (n & (~n + 1)))); } @@ -1375,7 +1375,7 @@ static ZEND_COLD void zend_ast_export_attribute_group(smart_str *str, zend_ast * } } -static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) { +static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, bool newlines) { zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; @@ -1497,7 +1497,7 @@ tail_call: case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; if (decl->child[4]) { - zend_bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); + bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); zend_ast_export_attributes(str, decl->child[4], indent, newlines); } diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index eb02e9bea0..4d7335853e 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -301,11 +301,11 @@ ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast); typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr); ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn); -static zend_always_inline zend_bool zend_ast_is_special(zend_ast *ast) { +static zend_always_inline bool zend_ast_is_special(zend_ast *ast) { return (ast->kind >> ZEND_AST_SPECIAL_SHIFT) & 1; } -static zend_always_inline zend_bool zend_ast_is_list(zend_ast *ast) { +static zend_always_inline bool zend_ast_is_list(zend_ast *ast) { return (ast->kind >> ZEND_AST_IS_LIST_SHIFT) & 1; } static zend_always_inline zend_ast_list *zend_ast_get_list(zend_ast *ast) { diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index ae07802b5b..9921e6adb0 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -163,7 +163,7 @@ ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags) return smart_str_extract(&str); } -ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr) +ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr) { zend_attribute *other; diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index d19b7e470d..fa21896447 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -72,7 +72,7 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope); ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets); -ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr); +ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr); ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags); ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname); diff --git a/Zend/zend_bitset.h b/Zend/zend_bitset.h index b7c369c749..2bc0ca105d 100644 --- a/Zend/zend_bitset.h +++ b/Zend/zend_bitset.h @@ -122,7 +122,7 @@ static inline uint32_t zend_bitset_len(uint32_t n) return (n + ((sizeof(zend_long) * 8) - 1)) / (sizeof(zend_long) * 8); } -static inline zend_bool zend_bitset_in(zend_bitset set, uint32_t n) +static inline bool zend_bitset_in(zend_bitset set, uint32_t n) { return ZEND_BIT_TEST(set, n); } @@ -158,7 +158,7 @@ static inline void zend_bitset_fill(zend_bitset set, uint32_t len) memset(set, 0xff, len * ZEND_BITSET_ELM_SIZE); } -static inline zend_bool zend_bitset_equal(zend_bitset set1, zend_bitset set2, uint32_t len) +static inline bool zend_bitset_equal(zend_bitset set1, zend_bitset set2, uint32_t len) { return memcmp(set1, set2, len * ZEND_BITSET_ELM_SIZE) == 0; } @@ -213,7 +213,7 @@ static inline void zend_bitset_union_with_difference(zend_bitset set1, zend_bits } } -static inline zend_bool zend_bitset_subset(zend_bitset set1, zend_bitset set2, uint32_t len) +static inline bool zend_bitset_subset(zend_bitset set1, zend_bitset set2, uint32_t len) { uint32_t i; diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 5ec365c920..fa65d1ca07 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -370,7 +370,7 @@ ZEND_FUNCTION(strncasecmp) ZEND_FUNCTION(error_reporting) { zend_long err; - zend_bool err_is_null = 1; + bool err_is_null = 1; int old_error_reporting; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -426,7 +426,7 @@ static bool validate_constant_array_argument(HashTable *ht, int argument_number) zval *val; GC_PROTECT_RECURSION(ht); - ZEND_HASH_FOREACH_VAL_IND(ht, val) { + ZEND_HASH_FOREACH_VAL(ht, val) { ZVAL_DEREF(val); if (Z_REFCOUNTED_P(val)) { if (Z_TYPE_P(val) == IS_ARRAY) { @@ -459,7 +459,7 @@ static void copy_constant_array(zval *dst, zval *src) /* {{{ */ zval *new_val, *val; array_init_size(dst, zend_hash_num_elements(Z_ARRVAL_P(src))); - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(src), idx, key, val) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(src), idx, key, val) { /* constant arrays can't contain references */ ZVAL_DEREF(val); if (key) { @@ -483,7 +483,7 @@ ZEND_FUNCTION(define) { zend_string *name; zval *val, val_free; - zend_bool non_cs = 0; + bool non_cs = 0; zend_constant c; ZEND_PARSE_PARAMETERS_START(2, 3) @@ -631,14 +631,14 @@ ZEND_FUNCTION(get_parent_class) } /* }}} */ -static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool only_subclass) /* {{{ */ +static void is_a_impl(INTERNAL_FUNCTION_PARAMETERS, bool only_subclass) /* {{{ */ { zval *obj; zend_string *class_name; zend_class_entry *instance_ce; zend_class_entry *ce; - zend_bool allow_string = only_subclass; - zend_bool retval; + bool allow_string = only_subclass; + bool retval; ZEND_PARSE_PARAMETERS_START(2, 3) Z_PARAM_ZVAL(obj) @@ -769,7 +769,6 @@ ZEND_FUNCTION(get_class_vars) /* {{{ Returns an array of object properties */ ZEND_FUNCTION(get_object_vars) { - zval *obj; zval *value; HashTable *properties; zend_string *key; @@ -777,10 +776,9 @@ ZEND_FUNCTION(get_object_vars) zend_ulong num_key; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(obj) + Z_PARAM_OBJ(zobj) ZEND_PARSE_PARAMETERS_END(); - zobj = Z_OBJ_P(obj); properties = zobj->handlers->get_properties(zobj); if (properties == NULL) { RETURN_EMPTY_ARRAY(); @@ -796,7 +794,7 @@ ZEND_FUNCTION(get_object_vars) array_init_size(return_value, zend_hash_num_elements(properties)); ZEND_HASH_FOREACH_KEY_VAL(properties, num_key, key, value) { - zend_bool is_dynamic = 1; + bool is_dynamic = 1; if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); if (UNEXPECTED(Z_ISUNDEF_P(value))) { @@ -839,22 +837,22 @@ ZEND_FUNCTION(get_object_vars) /* {{{ Returns an array of mangled object properties. Does not respect property visibility. */ ZEND_FUNCTION(get_mangled_object_vars) { - zval *obj; + zend_object *obj; HashTable *properties; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT(obj) + Z_PARAM_OBJ(obj) ZEND_PARSE_PARAMETERS_END(); - properties = Z_OBJ_HT_P(obj)->get_properties(Z_OBJ_P(obj)); + properties = obj->handlers->get_properties(obj); if (!properties) { ZVAL_EMPTY_ARRAY(return_value); return; } properties = zend_proptable_to_symtable(properties, - (Z_OBJCE_P(obj)->default_properties_count || - Z_OBJ_P(obj)->handlers != &std_object_handlers || + (obj->ce->default_properties_count || + obj->handlers != &std_object_handlers || GC_IS_RECURSIVE(properties))); RETURN_ARR(properties); } @@ -1009,7 +1007,7 @@ static inline void class_exists_impl(INTERNAL_FUNCTION_PARAMETERS, int flags, in zend_string *name; zend_string *lcname; zend_class_entry *ce; - zend_bool autoload = 1; + bool autoload = 1; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(name) @@ -1065,7 +1063,7 @@ ZEND_FUNCTION(trait_exists) ZEND_FUNCTION(function_exists) { zend_string *name; - zend_bool exists; + bool exists; zend_string *lcname; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1094,7 +1092,7 @@ ZEND_FUNCTION(class_alias) char *alias_name; zend_class_entry *ce; size_t alias_name_len; - zend_bool autoload = 1; + bool autoload = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ss|b", &class_name, &alias_name, &alias_name_len, &autoload) == FAILURE) { RETURN_THROWS(); @@ -1328,7 +1326,7 @@ ZEND_FUNCTION(get_defined_functions) zval internal, user; zend_string *key; zend_function *func; - zend_bool exclude_disabled = 1; + bool exclude_disabled = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &exclude_disabled) == FAILURE) { RETURN_THROWS(); @@ -1476,7 +1474,7 @@ static void add_zendext_info(zend_extension *ext, void *arg) /* {{{ */ /* {{{ Return an array containing names of loaded extensions */ ZEND_FUNCTION(get_loaded_extensions) { - zend_bool zendext = 0; + bool zendext = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &zendext) == FAILURE) { RETURN_THROWS(); @@ -1499,7 +1497,7 @@ ZEND_FUNCTION(get_loaded_extensions) /* {{{ Return an array containing the names and values of all defined constants */ ZEND_FUNCTION(get_defined_constants) { - zend_bool categorize = 0; + bool categorize = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &categorize) == FAILURE) { RETURN_THROWS(); @@ -1668,7 +1666,7 @@ void debug_print_backtrace_args(zval *arg_array) /* {{{ */ } /* }}} */ -static inline zend_bool skip_internal_handler(zend_execute_data *skip) /* {{{ */ +static inline bool skip_internal_handler(zend_execute_data *skip) /* {{{ */ { return !(skip->func && ZEND_USER_CODE(skip->func->common.type)) && skip->prev_execute_data @@ -1780,7 +1778,7 @@ ZEND_FUNCTION(debug_print_backtrace) } } else { /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ - zend_bool build_filename_arg = 1; + bool build_filename_arg = 1; uint32_t include_kind = 0; if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { include_kind = ptr->opline->extended_value; @@ -2011,7 +2009,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } } else { /* i know this is kinda ugly, but i'm trying to avoid extra cycles in the main execution loop */ - zend_bool build_filename_arg = 1; + bool build_filename_arg = 1; zend_string *pseudo_function_name; uint32_t include_kind = 0; if (ptr->func && ZEND_USER_CODE(ptr->func->common.type) && ptr->opline->opcode == ZEND_INCLUDE_OR_EVAL) { diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index e7777c58cf..8aed8622dc 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -69,11 +69,11 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ } /* }}} */ -static zend_bool zend_valid_closure_binding( +static bool zend_valid_closure_binding( zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */ { zend_function *func = &closure->func; - zend_bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); @@ -128,6 +128,7 @@ ZEND_METHOD(Closure, call) zend_fcall_info_cache fci_cache; zend_function my_function; zend_object *newobj; + zend_class_entry *newclass; fci.param_count = 0; fci.params = NULL; @@ -140,26 +141,27 @@ ZEND_METHOD(Closure, call) closure = (zend_closure *) Z_OBJ_P(ZEND_THIS); newobj = Z_OBJ_P(newthis); + newclass = newobj->ce; - if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) { + if (!zend_valid_closure_binding(closure, newthis, newclass)) { return; } if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; - zend_create_closure(&new_closure, &closure->func, Z_OBJCE_P(newthis), closure->called_scope, newthis); + zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); closure = (zend_closure *) Z_OBJ(new_closure); fci_cache.function_handler = &closure->func; } else { memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function)); my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE; /* use scope of passed object */ - my_function.common.scope = Z_OBJCE_P(newthis); + my_function.common.scope = newclass; fci_cache.function_handler = &my_function; /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ if (ZEND_USER_CODE(my_function.type) - && (closure->func.common.scope != Z_OBJCE_P(newthis) + && (closure->func.common.scope != newclass || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { void *ptr; @@ -172,7 +174,7 @@ ZEND_METHOD(Closure, call) } } - fci_cache.called_scope = newobj->ce; + fci_cache.called_scope = newclass; fci_cache.object = fci.object = newobj; fci.size = sizeof(fci); @@ -518,7 +520,7 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */ } /* }}} */ -int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */ +int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ { zend_closure *closure = (zend_closure *)obj; *fptr_ptr = &closure->func; @@ -541,7 +543,7 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) zval val; struct _zend_arg_info *arg_info = closure->func.common.arg_info; HashTable *debug_info; - zend_bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO); + bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO); *is_temp = 1; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 57c33e3c96..c671628479 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -87,7 +87,7 @@ ZEND_API zend_executor_globals executor_globals; #endif static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2); -static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast); +static bool zend_try_ct_eval_array(zval *result, zend_ast *ast); static void init_op(zend_op *op) { @@ -145,7 +145,7 @@ static zend_string *zend_build_runtime_definition_key(zend_string *name, uint32_ } /* }}} */ -static zend_bool zend_get_unqualified_name(const zend_string *name, const char **result, size_t *result_len) /* {{{ */ +static bool zend_get_unqualified_name(const zend_string *name, const char **result, size_t *result_len) /* {{{ */ { const char *ns_separator = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name)); if (ns_separator != NULL) { @@ -180,7 +180,7 @@ static const struct reserved_class_name reserved_class_names[] = { {NULL, 0} }; -static zend_bool zend_is_reserved_class_name(const zend_string *name) /* {{{ */ +static bool zend_is_reserved_class_name(const zend_string *name) /* {{{ */ { const struct reserved_class_name *reserved = reserved_class_names; @@ -259,7 +259,7 @@ static zend_always_inline zend_uchar zend_lookup_builtin_type_by_name(const zend } /* }}} */ -static zend_always_inline zend_bool zend_is_confusable_type(const zend_string *name, const char **correct_name) /* {{{ */ +static zend_always_inline bool zend_is_confusable_type(const zend_string *name, const char **correct_name) /* {{{ */ { const confusable_type_info *info = confusable_types; @@ -278,7 +278,7 @@ static zend_always_inline zend_bool zend_is_confusable_type(const zend_string *n } /* }}} */ -static zend_bool zend_is_not_imported(zend_string *name) { +static bool zend_is_not_imported(zend_string *name) { /* Assuming "name" is unqualified here. */ return !FC(imports) || zend_hash_find_ptr_lc(FC(imports), name) == NULL; } @@ -393,7 +393,7 @@ static void zend_register_seen_symbol(zend_string *name, uint32_t kind) { } } -static zend_bool zend_have_seen_symbol(zend_string *name, uint32_t kind) { +static bool zend_have_seen_symbol(zend_string *name, uint32_t kind) { zval *zv = zend_hash_find(&FC(seen_symbols), name); return zv && (Z_LVAL_P(zv) & kind) != 0; } @@ -472,7 +472,7 @@ ZEND_API int zend_get_compiled_lineno(void) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_is_compiling(void) /* {{{ */ +ZEND_API bool zend_is_compiling(void) /* {{{ */ { return CG(in_compilation); } @@ -609,7 +609,7 @@ static int zend_add_class_name_literal(zend_string *name) /* {{{ */ } /* }}} */ -static int zend_add_const_name_literal(zend_string *name, zend_bool unqualified) /* {{{ */ +static int zend_add_const_name_literal(zend_string *name, bool unqualified) /* {{{ */ { zend_string *tmp_name; @@ -658,7 +658,7 @@ void zend_stop_lexing(void) } static inline void zend_begin_loop( - zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */ + zend_uchar free_opcode, const znode *loop_var, bool is_switch) /* {{{ */ { zend_brk_cont_element *brk_cont_element; int parent = CG(context).current_brk_cont; @@ -860,8 +860,8 @@ zend_string *zend_prefix_with_ns(zend_string *name) { } zend_string *zend_resolve_non_class_name( - zend_string *name, uint32_t type, zend_bool *is_fully_qualified, - zend_bool case_sensitive, HashTable *current_import_sub + zend_string *name, uint32_t type, bool *is_fully_qualified, + bool case_sensitive, HashTable *current_import_sub ) { char *compound; *is_fully_qualified = 0; @@ -917,14 +917,14 @@ zend_string *zend_resolve_non_class_name( } /* }}} */ -zend_string *zend_resolve_function_name(zend_string *name, uint32_t type, zend_bool *is_fully_qualified) /* {{{ */ +zend_string *zend_resolve_function_name(zend_string *name, uint32_t type, bool *is_fully_qualified) /* {{{ */ { return zend_resolve_non_class_name( name, type, is_fully_qualified, 0, FC(imports_function)); } /* }}} */ -zend_string *zend_resolve_const_name(zend_string *name, uint32_t type, zend_bool *is_fully_qualified) /* {{{ */ { +zend_string *zend_resolve_const_name(zend_string *name, uint32_t type, bool *is_fully_qualified) /* {{{ */ { return zend_resolve_non_class_name( name, type, is_fully_qualified, 1, FC(imports_const)); } @@ -1001,7 +1001,7 @@ static void str_dtor(zval *zv) /* {{{ */ { } /* }}} */ -static zend_bool zend_is_call(zend_ast *ast); +static bool zend_is_call(zend_ast *ast); static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */ { @@ -1052,7 +1052,7 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ } /* }}} */ -static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, zend_bool compile_time) /* {{{ */ +static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, bool compile_time) /* {{{ */ { zval *zv = zend_hash_find_ex(compile_time ? CG(function_table) : EG(function_table), lcname, 1); int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR; @@ -1246,7 +1246,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop } if (type_mask & MAY_BE_NULL) { - zend_bool is_union = !str || memchr(ZSTR_VAL(str), '|', ZSTR_LEN(str)) != NULL; + bool is_union = !str || memchr(ZSTR_VAL(str), '|', ZSTR_LEN(str)) != NULL; if (!is_union) { zend_string *nullable_str = zend_string_concat2("?", 1, ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release(str); @@ -1262,7 +1262,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type) { return zend_type_to_string_resolved(type, NULL); } -static zend_bool is_generator_compatible_class_type(zend_string *name) { +static bool is_generator_compatible_class_type(zend_string *name) { return zend_string_equals_literal_ci(name, "Traversable") || zend_string_equals_literal_ci(name, "Iterator") || zend_string_equals_literal_ci(name, "Generator"); @@ -1277,7 +1277,7 @@ static void zend_mark_function_as_generator() /* {{{ */ if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { zend_type return_type = CG(active_op_array)->arg_info[-1].type; - zend_bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & (MAY_BE_ITERABLE | MAY_BE_OBJECT)) != 0; + bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & (MAY_BE_ITERABLE | MAY_BE_OBJECT)) != 0; if (!valid_type) { zend_type *single_type; ZEND_TYPE_FOREACH(return_type, single_type) { @@ -1326,7 +1326,7 @@ ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_ ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline) /* {{{ */ { if (first_early_binding_opline != (uint32_t)-1) { - zend_bool orig_in_compilation = CG(in_compilation); + bool orig_in_compilation = CG(in_compilation); uint32_t opline_num = first_early_binding_opline; void **run_time_cache; @@ -1433,7 +1433,7 @@ ZEND_API zend_result zend_unmangle_property_name_ex(const zend_string *name, con } /* }}} */ -static zend_bool can_ct_eval_const(zend_constant *c) { +static bool can_ct_eval_const(zend_constant *c) { if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) { return 0; } @@ -1450,7 +1450,7 @@ static zend_bool can_ct_eval_const(zend_constant *c) { return 0; } -static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool is_fully_qualified) /* {{{ */ +static bool zend_try_ct_eval_const(zval *zv, zend_string *name, bool is_fully_qualified) /* {{{ */ { zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name); if (c && can_ct_eval_const(c)) { @@ -1477,7 +1477,7 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i } /* }}} */ -static inline zend_bool zend_is_scope_known() /* {{{ */ +static inline bool zend_is_scope_known() /* {{{ */ { if (CG(active_op_array)->fn_flags & ZEND_ACC_CLOSURE) { /* Closures can be rebound to a different scope */ @@ -1495,7 +1495,7 @@ static inline zend_bool zend_is_scope_known() /* {{{ */ } /* }}} */ -static inline zend_bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */ +static inline bool class_name_refers_to_active_ce(zend_string *class_name, uint32_t fetch_type) /* {{{ */ { if (!CG(active_class_entry)) { return 0; @@ -1560,7 +1560,7 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ } /* }}} */ -static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */ +static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast) /* {{{ */ { uint32_t fetch_type; zval *class_name; @@ -1603,7 +1603,7 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a /* }}} */ /* We don't use zend_verify_const_access because we need to deal with unlinked classes. */ -static zend_bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope) +static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope) { if (Z_ACCESS_FLAGS(c->value) & ZEND_ACC_PUBLIC) { return 1; @@ -1632,7 +1632,7 @@ static zend_bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_ } } -static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ +static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); zend_class_constant *cc; @@ -1733,7 +1733,7 @@ void zend_do_extended_fcall_end(void) /* {{{ */ } /* }}} */ -zend_bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ { +bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ { zend_auto_global *auto_global; if ((auto_global = zend_hash_str_find_ptr(CG(auto_globals), name, len)) != NULL) { @@ -1746,7 +1746,7 @@ zend_bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ { } /* }}} */ -zend_bool zend_is_auto_global(zend_string *name) /* {{{ */ +bool zend_is_auto_global(zend_string *name) /* {{{ */ { zend_auto_global *auto_global; @@ -1760,7 +1760,7 @@ zend_bool zend_is_auto_global(zend_string *name) /* {{{ */ } /* }}} */ -zend_result zend_register_auto_global(zend_string *name, zend_bool jit, zend_auto_global_callback auto_global_callback) /* {{{ */ +zend_result zend_register_auto_global(zend_string *name, bool jit, zend_auto_global_callback auto_global_callback) /* {{{ */ { zend_auto_global auto_global; zend_result retval; @@ -1808,9 +1808,9 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem) /* {{{ */ } /* }}} */ -ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers) /* {{{ */ +ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers) /* {{{ */ { - zend_bool persistent_hashes = ce->type == ZEND_INTERNAL_CLASS; + bool persistent_hashes = ce->type == ZEND_INTERNAL_CLASS; ce->refcount = 1; ce->ce_flags = ZEND_ACC_CONSTANTS_UPDATED; @@ -2243,7 +2243,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */ } /* }}} */ -static zend_bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind) +static bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind) { switch (ast_kind) { case ZEND_AST_DIM: @@ -2259,7 +2259,7 @@ static zend_bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind) } } -static zend_bool zend_ast_is_short_circuited(const zend_ast *ast) +static bool zend_ast_is_short_circuited(const zend_ast *ast) { switch (ast->kind) { case ZEND_AST_DIM: @@ -2295,7 +2295,7 @@ static uint32_t zend_short_circuiting_checkpoint() static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zend_ast *ast) { - zend_bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind) + bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind) || ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY; if (!is_short_circuited) { ZEND_ASSERT(zend_stack_count(&CG(short_circuiting_opnums)) == checkpoint @@ -2382,7 +2382,7 @@ static size_t zend_type_get_num_classes(zend_type type) { } static void zend_emit_return_type_check( - znode *expr, zend_arg_info *return_info, zend_bool implicit) /* {{{ */ + znode *expr, zend_arg_info *return_info, bool implicit) /* {{{ */ { zend_type type = return_info->type; if (ZEND_TYPE_IS_SET(type)) { @@ -2439,7 +2439,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */ { znode zn; zend_op *ret; - zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; if ((CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) { @@ -2458,7 +2458,7 @@ void zend_emit_final_return(bool return_one) /* {{{ */ } /* }}} */ -static inline zend_bool zend_is_variable(zend_ast *ast) /* {{{ */ +static inline bool zend_is_variable(zend_ast *ast) /* {{{ */ { return ast->kind == ZEND_AST_VAR || ast->kind == ZEND_AST_DIM @@ -2468,7 +2468,7 @@ static inline zend_bool zend_is_variable(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline zend_bool zend_is_call(zend_ast *ast) /* {{{ */ +static inline bool zend_is_call(zend_ast *ast) /* {{{ */ { return ast->kind == ZEND_AST_CALL || ast->kind == ZEND_AST_METHOD_CALL @@ -2477,13 +2477,13 @@ static inline zend_bool zend_is_call(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline zend_bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */ +static inline bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */ { return zend_is_variable(ast) || zend_is_call(ast); } /* }}} */ -static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ +static inline bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ { return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_GROUP @@ -2491,7 +2491,7 @@ static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ +static inline bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ { while ( ast->kind == ZEND_AST_DIM @@ -2504,7 +2504,7 @@ static inline zend_bool zend_can_write_to_variable(zend_ast *ast) /* {{{ */ } /* }}} */ -static inline zend_bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ +static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ { if (name_ast->kind != ZEND_AST_ZVAL) { return 0; @@ -2674,7 +2674,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint } /* }}} */ -static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ +static bool is_this_fetch(zend_ast *ast) /* {{{ */ { if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { zval *name = zend_ast_get_zval(ast->child[0]); @@ -2685,7 +2685,22 @@ static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */ } /* }}} */ -static zend_bool this_guaranteed_exists() /* {{{ */ +static bool is_globals_fetch(const zend_ast *ast) +{ + if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { + zval *name = zend_ast_get_zval(ast->child[0]); + return Z_TYPE_P(name) == IS_STRING && zend_string_equals_literal(Z_STR_P(name), "GLOBALS"); + } + + return 0; +} + +static bool is_global_var_fetch(zend_ast *ast) +{ + return ast->kind == ZEND_AST_DIM && is_globals_fetch(ast->child[0]); +} + +static bool this_guaranteed_exists() /* {{{ */ { zend_op_array *op_array = CG(active_op_array); /* Instance methods always have a $this. @@ -2705,6 +2720,13 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t } CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS; return opline; + } else if (is_globals_fetch(ast)) { + zend_op *opline = zend_emit_op(result, ZEND_FETCH_GLOBALS, NULL, NULL); + if (type == BP_VAR_R || type == BP_VAR_IS) { + opline->result_type = IS_TMP_VAR; + result->op_type = IS_TMP_VAR; + } + return opline; } else if (zend_try_compile_cv(result, ast) == FAILURE) { return zend_compile_simple_var_no_cv(result, ast, type, delayed); } @@ -2726,7 +2748,7 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t } /* }}} */ -zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref); +zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref); void zend_compile_assign(znode *result, zend_ast *ast); static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */ @@ -2750,10 +2772,26 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t znode var_node, dim_node; - zend_short_circuiting_mark_inner(var_ast); - opline = zend_delayed_compile_var(&var_node, var_ast, type, 0); - if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) { - opline->extended_value |= ZEND_FETCH_DIM_WRITE; + if (is_globals_fetch(var_ast)) { + if (dim_ast == NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot append to $GLOBALS"); + } + + zend_compile_expr(&dim_node, dim_ast); + if (dim_node.op_type == IS_CONST) { + convert_to_string(&dim_node.u.constant); + } + + opline = zend_delayed_emit_op(result, ZEND_FETCH_R, &dim_node, NULL); + opline->extended_value = ZEND_FETCH_GLOBAL; + zend_adjust_for_fetch_type(opline, result, type); + return opline; + } else { + zend_short_circuiting_mark_inner(var_ast); + opline = zend_delayed_compile_var(&var_node, var_ast, type, 0); + if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) { + opline->extended_value |= ZEND_FETCH_DIM_WRITE; + } } zend_separate_if_call_and_write(&var_node, var_ast, type); @@ -2795,7 +2833,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t znode obj_node, prop_node; zend_op *opline; - zend_bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_PROP; + bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_PROP; if (is_this_fetch(obj_ast)) { if (this_guaranteed_exists()) { @@ -2904,9 +2942,9 @@ static void zend_verify_list_assign_target(zend_ast *var_ast, zend_ast_attr arra static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node); /* Propagate refs used on leaf elements to the surrounding list() structures. */ -static zend_bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */ +static bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */ zend_ast_list *list = zend_ast_get_list(ast); - zend_bool has_refs = 0; + bool has_refs = 0; uint32_t i; for (i = 0; i < list->children; ++i) { @@ -2925,14 +2963,23 @@ static zend_bool zend_propagate_list_refs(zend_ast *ast) { /* {{{ */ } /* }}} */ +static bool list_is_keyed(zend_ast_list *list) +{ + for (uint32_t i = 0; i < list->children; i++) { + if (list->child[i]) { + return list->child[i]->child[1] != NULL; + } + } + return false; +} + static void zend_compile_list_assign( znode *result, zend_ast *ast, znode *expr_node, zend_ast_attr array_style) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; - zend_bool has_elems = 0; - zend_bool is_keyed = - list->children > 0 && list->child[0] != NULL && list->child[0]->child[1] != NULL; + bool has_elems = 0; + bool is_keyed = list_is_keyed(list); if (list->children && expr_node->op_type == IS_CONST && Z_TYPE(expr_node->u.constant) == IS_STRING) { zval_make_interned_string(&expr_node->u.constant); @@ -3031,11 +3078,15 @@ static void zend_ensure_writable_variable(const zend_ast *ast) /* {{{ */ if (zend_ast_is_short_circuited(ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Can't use nullsafe operator in write context"); } + if (is_globals_fetch(ast)) { + zend_error_noreturn(E_COMPILE_ERROR, + "$GLOBALS can only be modified using the $GLOBALS[$name] = $value syntax"); + } } /* }}} */ /* Detects $a... = $a pattern */ -zend_bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ */ +bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ */ { if (expr_ast->kind != ZEND_AST_VAR || expr_ast->child[0]->kind != ZEND_AST_ZVAL) { return 0; @@ -3052,7 +3103,7 @@ zend_bool zend_is_assign_to_self(zend_ast *var_ast, zend_ast *expr_ast) /* {{{ * { zend_string *name1 = zval_get_string(zend_ast_get_zval(var_ast->child[0])); zend_string *name2 = zval_get_string(zend_ast_get_zval(expr_ast->child[0])); - zend_bool result = zend_string_equals(name1, name2); + bool result = zend_string_equals(name1, name2); zend_string_release_ex(name1, 0); zend_string_release_ex(name2, 0); return result; @@ -3074,7 +3125,9 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ zend_ensure_writable_variable(var_ast); - switch (var_ast->kind) { + /* Treat $GLOBALS['x'] assignment like assignment to variable. */ + zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind; + switch (kind) { case ZEND_AST_VAR: offset = zend_delayed_compile_begin(); zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0); @@ -3182,6 +3235,9 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ if (zend_ast_is_short_circuited(source_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); } + if (is_globals_fetch(source_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot acquire reference to $GLOBALS"); + } offset = zend_delayed_compile_begin(); zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1); @@ -3251,7 +3307,9 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */ zend_ensure_writable_variable(var_ast); - switch (var_ast->kind) { + /* Treat $GLOBALS['x'] assignment like assignment to variable. */ + zend_ast_kind kind = is_global_var_fetch(var_ast) ? ZEND_AST_VAR : var_ast->kind; + switch (kind) { case ZEND_AST_VAR: offset = zend_delayed_compile_begin(); zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0); @@ -3337,14 +3395,14 @@ uint32_t zend_compile_args( { zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; - zend_bool uses_arg_unpack = 0; + bool uses_arg_unpack = 0; uint32_t arg_count = 0; /* number of arguments not including unpacks */ /* Whether named arguments are used syntactically, to enforce language level limitations. * May not actually use named argument passing. */ - zend_bool uses_named_args = 0; + bool uses_named_args = 0; /* Whether there may be any undef arguments due to the use of named arguments. */ - zend_bool may_have_undef = 0; + bool may_have_undef = 0; /* Whether there may be any extra named arguments collected into a variadic. */ *may_have_extra_named_args = 0; @@ -3421,7 +3479,9 @@ uint32_t zend_compile_args( arg_count++; } - if (zend_is_call(arg)) { + /* Treat passing of $GLOBALS the same as passing a call. + * This will error at runtime if the argument is by-ref. */ + if (zend_is_call(arg) || is_globals_fetch(arg)) { zend_compile_var(&arg_node, arg, BP_VAR_R, 0); if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) { /* Function call was converted into builtin instruction */ @@ -3586,10 +3646,10 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function * } /* }}} */ -zend_bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /* {{{ */ +bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /* {{{ */ { zend_string *orig_name = zend_ast_get_str(name_ast); - zend_bool is_fully_qualified; + bool is_fully_qualified; name_node->op_type = IS_CONST; ZVAL_STR(&name_node->u.constant, zend_resolve_function_name( @@ -3646,7 +3706,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a } /* }}} */ -static inline zend_bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */ +static inline bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */ { uint32_t i; for (i = 0; i < args->children; ++i) { @@ -3802,7 +3862,7 @@ zend_result zend_compile_func_ord(znode *result, zend_ast_list *args) /* {{{ */ /* We can only calculate the stack size for functions that have been fully compiled, otherwise * additional CV or TMP slots may still be added. This prevents the use of INIT_FCALL for * directly or indirectly recursive function calls. */ -static zend_bool fbc_is_finalized(zend_function *fbc) { +static bool fbc_is_finalized(zend_function *fbc) { return !ZEND_USER_CODE(fbc->type) || (fbc->common.fn_flags & ZEND_ACC_DONE_PASS_TWO); } @@ -3874,7 +3934,7 @@ zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_stri && args->child[1]->child[1]->kind == ZEND_AST_ARG_LIST) { zend_string *orig_name = zend_ast_get_str(args->child[1]->child[0]); zend_ast_list *list = zend_ast_get_list(args->child[1]->child[1]); - zend_bool is_fully_qualified; + bool is_fully_qualified; zend_string *name = zend_resolve_function_name(orig_name, args->child[1]->child[0]->attr, &is_fully_qualified); if (zend_string_equals_literal_ci(name, "array_slice") @@ -3987,7 +4047,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args) /* {{{ */ { - zend_bool strict = 0; + bool strict = 0; znode array, needly; zend_op *opline; @@ -3997,7 +4057,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args } else if (args->child[2]->kind == ZEND_AST_CONST) { zval value; zend_ast *name_ast = args->child[2]->child[0]; - zend_bool is_fully_qualified; + bool is_fully_qualified; zend_string *resolved_name = zend_resolve_const_name( zend_ast_get_str(name_ast), name_ast->attr, &is_fully_qualified); @@ -4022,7 +4082,7 @@ static zend_result zend_compile_func_in_array(znode *result, zend_ast_list *args } if (zend_hash_num_elements(Z_ARRVAL(array.u.constant)) > 0) { - zend_bool ok = 1; + bool ok = 1; zval *val, tmp; HashTable *src = Z_ARRVAL(array.u.constant); HashTable *dst = zend_new_array(zend_hash_num_elements(src)); @@ -4179,7 +4239,7 @@ zend_result zend_compile_func_array_slice(znode *result, zend_ast_list *args) /* && args->child[1]->kind == ZEND_AST_ZVAL) { zend_string *orig_name = zend_ast_get_str(args->child[0]->child[0]); - zend_bool is_fully_qualified; + bool is_fully_qualified; zend_string *name = zend_resolve_function_name(orig_name, args->child[0]->child[0]->attr, &is_fully_qualified); zend_ast_list *list = zend_ast_get_list(args->child[0]->child[1]); zval *zv = zend_ast_get_zval(args->child[1]); @@ -4301,7 +4361,7 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ } { - zend_bool runtime_resolution = zend_compile_function_name(&name_node, name_ast); + bool runtime_resolution = zend_compile_function_name(&name_node, name_ast); if (runtime_resolution) { if (zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert")) { zend_compile_assert(result, zend_ast_get_list(args_ast), Z_STR(name_node.u.constant), NULL); @@ -4367,7 +4427,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{ znode obj_node, method_node; zend_op *opline; zend_function *fbc = NULL; - zend_bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL; + bool nullsafe = ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL; if (is_this_fetch(obj_ast)) { if (this_guaranteed_exists()) { @@ -4420,7 +4480,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } /* }}} */ -static zend_bool zend_is_constructor(zend_string *name) /* {{{ */ +static bool zend_is_constructor(zend_string *name) /* {{{ */ { return zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME); } @@ -4512,7 +4572,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } /* }}} */ -void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel); +void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel); void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ { @@ -4568,6 +4628,7 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */ convert_to_string(&name_node.u.constant); } + // TODO(GLOBALS) Forbid "global $GLOBALS"? if (is_this_fetch(var_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable"); } else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { @@ -4639,6 +4700,21 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ zend_ensure_writable_variable(var_ast); + if (is_global_var_fetch(var_ast)) { + if (!var_ast->child[1]) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for unsetting"); + } + + zend_compile_expr(&var_node, var_ast->child[1]); + if (var_node.op_type == IS_CONST) { + convert_to_string(&var_node.u.constant); + } + + opline = zend_emit_op(NULL, ZEND_UNSET_VAR, &var_node, NULL); + opline->extended_value = ZEND_FETCH_GLOBAL; + return; + } + switch (var_ast->kind) { case ZEND_AST_VAR: if (is_this_fetch(var_ast)) { @@ -4758,8 +4834,8 @@ static bool zend_has_finally(void) /* {{{ */ void zend_compile_return(zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; - zend_bool is_generator = (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0; - zend_bool by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + bool is_generator = (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) != 0; + bool by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; znode expr_node; zend_op *opline; @@ -5126,8 +5202,8 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_ast *value_ast = ast->child[1]; zend_ast *key_ast = ast->child[2]; zend_ast *stmt_ast = ast->child[3]; - zend_bool by_ref = value_ast->kind == ZEND_AST_REF; - zend_bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); + bool by_ref = value_ast->kind == ZEND_AST_REF; + bool is_variable = zend_is_variable(expr_ast) && zend_can_write_to_variable(expr_ast); znode expr_node, reset_node, value_node, key_node; zend_op *opline; @@ -5296,7 +5372,7 @@ static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) { return common_type; } -static zend_bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) { +static bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) { if (CG(compiler_options) & ZEND_COMPILE_NO_JUMPTABLES) { return 0; } @@ -5317,7 +5393,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_ast_list *cases = zend_ast_get_list(ast->child[1]); uint32_t i; - zend_bool has_default_case = 0; + bool has_default_case = 0; znode expr_node, case_node; zend_op *opline; @@ -5462,7 +5538,7 @@ static uint32_t count_match_conds(zend_ast_list *arms) return num_conds; } -static zend_bool can_match_use_jumptable(zend_ast_list *arms) { +static bool can_match_use_jumptable(zend_ast_list *arms) { for (uint32_t i = 0; i < arms->children; i++) { zend_ast *arm_ast = arms->child[i]; if (!arm_ast->child[0]) { @@ -5493,7 +5569,7 @@ void zend_compile_match(znode *result, zend_ast *ast) { zend_ast *expr_ast = ast->child[0]; zend_ast_list *arms = zend_ast_get_list(ast->child[1]); - zend_bool has_default_arm = 0; + bool has_default_arm = 0; uint32_t opnum_match = (uint32_t)-1; znode expr_node; @@ -5505,7 +5581,7 @@ void zend_compile_match(znode *result, zend_ast *ast) uint32_t num_conds = count_match_conds(arms); zend_uchar can_use_jumptable = can_match_use_jumptable(arms); - zend_bool uses_jumptable = can_use_jumptable && num_conds >= 2; + bool uses_jumptable = can_use_jumptable && num_conds >= 2; HashTable *jumptable = NULL; uint32_t *jmpnz_opnums = NULL; @@ -5571,7 +5647,7 @@ void zend_compile_match(znode *result, zend_ast *ast) opnum_default_jmp = zend_emit_jump(0); } - zend_bool is_first_case = 1; + bool is_first_case = 1; uint32_t cond_count = 0; uint32_t *jmp_end_opnums = safe_emalloc(sizeof(uint32_t), arms->children, 0); @@ -5730,7 +5806,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ zend_ast *var_ast = catch_ast->child[1]; zend_ast *stmt_ast = catch_ast->child[2]; zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL; - zend_bool is_last_catch = (i + 1 == catches->children); + bool is_last_catch = (i + 1 == catches->children); uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0); uint32_t opnum_catch = (uint32_t)-1; @@ -5739,7 +5815,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ for (j = 0; j < classes->children; j++) { zend_ast *class_ast = classes->child[j]; - zend_bool is_last_class = (j + 1 == classes->children); + bool is_last_class = (j + 1 == classes->children); if (!zend_is_const_default_class_ref(class_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); @@ -5846,7 +5922,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ /* }}} */ /* Encoding declarations must already be handled during parsing */ -zend_bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */ +bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */ { zend_ast_list *declares = zend_ast_get_list(ast); uint32_t i; @@ -5898,7 +5974,7 @@ zend_bool zend_handle_encoding_declaration(zend_ast *ast) /* {{{ */ /* }}} */ /* Check whether this is the first statement, not counting declares. */ -static zend_result zend_is_first_statement(zend_ast *ast, zend_bool allow_nop) /* {{{ */ +static zend_result zend_is_first_statement(zend_ast *ast, bool allow_nop) /* {{{ */ { uint32_t i = 0; zend_ast_list *file_ast = zend_ast_get_list(CG(ast)); @@ -6074,7 +6150,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } } -static zend_bool zend_type_contains_traversable(zend_type type) { +static bool zend_type_contains_traversable(zend_type type) { zend_type *single_type; ZEND_TYPE_FOREACH(type, single_type) { if (ZEND_TYPE_HAS_NAME(*single_type) @@ -6088,9 +6164,9 @@ static zend_bool zend_type_contains_traversable(zend_type type) { // TODO: Ideally we'd canonicalize "iterable" into "array|Traversable" and essentially // treat it as a built-in type alias. static zend_type zend_compile_typename( - zend_ast *ast, zend_bool force_allow_null, zend_bool use_arena) /* {{{ */ + zend_ast *ast, bool force_allow_null) /* {{{ */ { - zend_bool allow_null = force_allow_null; + bool allow_null = force_allow_null; zend_ast_attr orig_ast_attr = ast->attr; zend_type type = ZEND_TYPE_INIT_NONE(0); if (ast->attr & ZEND_TYPE_NULLABLE) { @@ -6100,6 +6176,12 @@ static zend_type zend_compile_typename( if (ast->kind == ZEND_AST_TYPE_UNION) { zend_ast_list *list = zend_ast_get_list(ast); + zend_type_list *type_list; + ALLOCA_FLAG(use_heap) + + type_list = do_alloca(ZEND_TYPE_LIST_SIZE(list->children), use_heap); + type_list->num_types = 0; + for (uint32_t i = 0; i < list->children; i++) { zend_ast *type_ast = list->child[i]; zend_type single_type = zend_compile_single_typename(type_ast); @@ -6125,37 +6207,20 @@ static zend_type zend_compile_typename( ZEND_TYPE_SET_PTR(type, ZEND_TYPE_NAME(single_type)); ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_NAME_BIT; } else { - zend_type_list *list; - if (ZEND_TYPE_HAS_LIST(type)) { - /* Add name to existing name list. */ - zend_type_list *old_list = ZEND_TYPE_LIST(type); - if (use_arena) { - // TODO: Add a zend_arena_realloc API? - list = zend_arena_alloc( - &CG(arena), ZEND_TYPE_LIST_SIZE(old_list->num_types + 1)); - memcpy(list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types)); - } else { - list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1)); - } - } else { + if (type_list->num_types == 0) { /* Switch from single name to name list. */ - size_t size = ZEND_TYPE_LIST_SIZE(2); - list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size); - list->num_types = 1; - list->types[0] = type; - ZEND_TYPE_FULL_MASK(list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; + type_list->num_types = 1; + type_list->types[0] = type; + ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK; + ZEND_TYPE_SET_LIST(type, type_list); } - list->types[list->num_types++] = single_type; - ZEND_TYPE_SET_LIST(type, list); - if (use_arena) { - ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT; - } + type_list->types[type_list->num_types++] = single_type; /* Check for trivially redundant class types */ - for (size_t i = 0; i < list->num_types - 1; i++) { + for (size_t i = 0; i < type_list->num_types - 1; i++) { if (zend_string_equals_ci( - ZEND_TYPE_NAME(list->types[i]), ZEND_TYPE_NAME(single_type))) { + ZEND_TYPE_NAME(type_list->types[i]), ZEND_TYPE_NAME(single_type))) { zend_string *single_type_str = zend_type_to_string(single_type); zend_error_noreturn(E_COMPILE_ERROR, "Duplicate type %s is redundant", ZSTR_VAL(single_type_str)); @@ -6164,6 +6229,16 @@ static zend_type zend_compile_typename( } } } + + if (type_list->num_types) { + zend_type_list *list = zend_arena_alloc( + &CG(arena), ZEND_TYPE_LIST_SIZE(type_list->num_types)); + memcpy(list, type_list, ZEND_TYPE_LIST_SIZE(type_list->num_types)); + ZEND_TYPE_SET_LIST(type, list); + ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT; + } + + free_alloca(type_list, use_heap); } else { type = zend_compile_single_typename(ast); } @@ -6216,7 +6291,7 @@ static zend_type zend_compile_typename( /* }}} */ /* May convert value from int to float. */ -static zend_bool zend_is_valid_default_value(zend_type type, zval *value) +static bool zend_is_valid_default_value(zend_type type, zval *value) { ZEND_ASSERT(ZEND_TYPE_IS_SET(type)); if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(value))) { @@ -6265,7 +6340,7 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3 if (args) { ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - zend_bool uses_named_args = 0; + bool uses_named_args = 0; for (j = 0; j < args->children; j++) { zend_ast **arg_ast_ptr = &args->child[j]; zend_ast *arg_ast = *arg_ast_ptr; @@ -6340,7 +6415,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall arg_infos->name = NULL; if (return_type_ast) { arg_infos->type = zend_compile_typename( - return_type_ast, /* force_allow_null */ 0, /* use_arena */ 0); + return_type_ast, /* force_allow_null */ 0); ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS( (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0); } else { @@ -6363,8 +6438,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_ast *attributes_ast = param_ast->child[3]; zend_ast *doc_comment_ast = param_ast->child[4]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); - zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; - zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; + bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; + bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; uint32_t visibility = param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE); @@ -6413,7 +6488,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall if (!optional_param) { /* Ignore parameters of the form "Type $param = null". * This is the PHP 5 style way of writing "?Type $param", so allow it for now. */ - zend_bool is_implicit_nullable = + bool is_implicit_nullable = type_ast && Z_TYPE(default_node.u.constant) == IS_NULL; if (!is_implicit_nullable) { optional_param = name; @@ -6439,10 +6514,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall if (type_ast) { uint32_t default_type = *default_ast_ptr ? Z_TYPE(default_node.u.constant) : IS_UNDEF; - zend_bool force_nullable = default_type == IS_NULL && !visibility; + bool force_nullable = default_type == IS_NULL && !visibility; op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; - arg_info->type = zend_compile_typename(type_ast, force_nullable, /* use_arena */ 0); + arg_info->type = zend_compile_typename(type_ast, force_nullable); if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID) { zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type"); @@ -6479,7 +6554,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall if (visibility) { zend_op_array *op_array = CG(active_op_array); zend_class_entry *scope = op_array->scope; - zend_bool is_ctor = + bool is_ctor = scope && zend_is_constructor(op_array->function_name); if (!is_ctor) { zend_error_noreturn(E_COMPILE_ERROR, @@ -6508,7 +6583,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall /* Recompile the type, as it has different memory management requirements. */ zend_type type = ZEND_TYPE_INIT_NONE(0); if (type_ast) { - type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1); + type = zend_compile_typename(type_ast, /* force_allow_null */ 0); } /* Don't give the property an explicit default value. For typed properties this means @@ -6543,7 +6618,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall for (i = 0; i < list->children; i++) { zend_ast *param_ast = list->child[i]; - zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; + bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; uint32_t visibility = param_ast->attr & (ZEND_ACC_PUBLIC|ZEND_ACC_PROTECTED|ZEND_ACC_PRIVATE); if (!visibility) { @@ -6613,7 +6688,7 @@ static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array typedef struct { HashTable uses; - zend_bool varvars_used; + bool varvars_used; } closure_info; static void find_implicit_binds_recursively(closure_info *info, zend_ast *ast) { @@ -6769,10 +6844,10 @@ static void add_stringable_interface(zend_class_entry *ce) { zend_string_init("stringable", sizeof("stringable") - 1, 0); } -zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */ +zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, bool has_body) /* {{{ */ { zend_class_entry *ce = CG(active_class_entry); - zend_bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0; + bool in_interface = (ce->ce_flags & ZEND_ACC_INTERFACE) != 0; uint32_t fn_flags = op_array->fn_flags; zend_string *lcname; @@ -6826,7 +6901,7 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, } /* }}} */ -static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, zend_bool toplevel) /* {{{ */ +static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ { zend_string *unqualified_name, *name, *lcname, *key; zend_op *opline; @@ -6888,14 +6963,14 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as } /* }}} */ -void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */ +void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; zend_ast *params_ast = decl->child[0]; zend_ast *uses_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; zend_ast *return_type_ast = decl->child[3]; - zend_bool is_method = decl->kind == ZEND_AST_METHOD; + bool is_method = decl->kind == ZEND_AST_METHOD; zend_string *method_lcname; zend_class_entry *orig_class_entry = CG(active_class_entry); @@ -6929,7 +7004,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* } if (is_method) { - zend_bool has_body = stmt_ast != NULL; + bool has_body = stmt_ast != NULL; method_lcname = zend_begin_method_decl(op_array, decl->name, has_body); } else { zend_begin_func_decl(result, op_array, decl, toplevel); @@ -7038,7 +7113,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z zend_type type = ZEND_TYPE_INIT_NONE(0); if (type_ast) { - type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1); + type = zend_compile_typename(type_ast, /* force_allow_null */ 0); if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE)) { zend_string *str = zend_type_to_string(type); @@ -7310,7 +7385,7 @@ static zend_string *zend_generate_anon_class_name(zend_ast_decl *decl) return zend_new_interned_string(result); } -void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */ +void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; zend_ast *extends_ast = decl->child[0]; @@ -7549,7 +7624,7 @@ void zend_compile_use(zend_ast *ast) /* {{{ */ zend_string *current_ns = FC(current_namespace); uint32_t type = ast->attr; HashTable *current_import = zend_get_import_ht(type); - zend_bool case_sensitive = type == ZEND_SYMBOL_CONST; + bool case_sensitive = type == ZEND_SYMBOL_CONST; for (i = 0; i < list->children; ++i) { zend_ast *use_ast = list->child[i]; @@ -7683,7 +7758,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */ zend_ast *name_ast = ast->child[0]; zend_ast *stmt_ast = ast->child[1]; zend_string *name; - zend_bool with_bracket = stmt_ast != NULL; + bool with_bracket = stmt_ast != NULL; /* handle mixed syntax declaration or nested namespaces */ if (!FC(has_bracketed_namespaces)) { @@ -7704,7 +7779,7 @@ void zend_compile_namespace(zend_ast *ast) /* {{{ */ } } - zend_bool is_first_namespace = (!with_bracket && !FC(current_namespace)) + bool is_first_namespace = (!with_bracket && !FC(current_namespace)) || (with_bracket && !FC(has_bracketed_namespaces)); if (is_first_namespace && FAILURE == zend_is_first_statement(ast, /* allow_nop */ 1)) { zend_error_noreturn(E_COMPILE_ERROR, "Namespace declaration statement has to be " @@ -7763,7 +7838,7 @@ void zend_compile_halt_compiler(zend_ast *ast) /* {{{ */ } /* }}} */ -static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ +static bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ { zend_op_array *op_array = CG(active_op_array); zend_class_entry *ce = CG(active_class_entry); @@ -7854,7 +7929,7 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ +ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2) /* {{{ */ { if ((opcode == ZEND_CONCAT || opcode == ZEND_FAST_CONCAT)) { /* Array to string warning. */ @@ -7909,7 +7984,7 @@ ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zva } /* }}} */ -static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */ +static inline bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode, zval *op1, zval *op2) /* {{{ */ { if (zend_binary_op_produces_error(opcode, op1, op2)) { return 0; @@ -7921,7 +7996,7 @@ static inline zend_bool zend_try_ct_eval_binary_op(zval *result, uint32_t opcode } /* }}} */ -zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op) +bool zend_unary_op_produces_error(uint32_t opcode, zval *op) { if (opcode == ZEND_BW_NOT) { return Z_TYPE_P(op) <= IS_TRUE || Z_TYPE_P(op) == IS_ARRAY; @@ -7930,7 +8005,7 @@ zend_bool zend_unary_op_produces_error(uint32_t opcode, zval *op) return 0; } -static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */ +static inline bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */ { if (zend_unary_op_produces_error(opcode, op)) { return 0; @@ -7942,7 +8017,7 @@ static inline zend_bool zend_try_ct_eval_unary_op(zval *result, uint32_t opcode, } /* }}} */ -static inline zend_bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ +static inline bool zend_try_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ { zval right; ZVAL_LONG(&right, (kind == ZEND_AST_UNARY_PLUS) ? 1 : -1); @@ -7958,12 +8033,12 @@ static inline void zend_ct_eval_greater(zval *result, zend_ast_kind kind, zval * } /* }}} */ -static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */ +static bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_ast *last_elem_ast = NULL; uint32_t i; - zend_bool is_constant = 1; + bool is_constant = 1; if (ast->attr == ZEND_ARRAY_SYNTAX_LIST) { zend_error(E_COMPILE_ERROR, "Cannot use list() as standalone expression"); @@ -8489,7 +8564,7 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */ znode var_node_is, var_node_w, default_node, assign_node, *node; zend_op *opline; uint32_t coalesce_opnum; - zend_bool need_frees = 0; + bool need_frees = 0; /* Remember expressions compiled during the initial BP_VAR_IS lookup, * to avoid double-evaluation when we compile again with BP_VAR_W. */ @@ -8619,7 +8694,7 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ znode value_node, key_node; znode *value_node_ptr = NULL, *key_node_ptr = NULL; zend_op *opline; - zend_bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; zend_mark_function_as_generator(); @@ -8732,6 +8807,28 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */ } } + if (is_globals_fetch(var_ast)) { + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, ast->kind == ZEND_AST_ISSET); + return; + } + + if (is_global_var_fetch(var_ast)) { + if (!var_ast->child[1]) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading"); + } + + zend_compile_expr(&var_node, var_ast->child[1]); + if (var_node.op_type == IS_CONST) { + convert_to_string(&var_node.u.constant); + } + + opline = zend_emit_op_tmp(result, ZEND_ISSET_ISEMPTY_VAR, &var_node, NULL); + opline->extended_value = + ZEND_FETCH_GLOBAL | (ast->kind == ZEND_AST_EMPTY ? ZEND_ISEMPTY : 0); + return; + } + zend_short_circuiting_mark_inner(var_ast); switch (var_ast->kind) { case ZEND_AST_VAR: @@ -8810,7 +8907,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ zend_ast_list *list = zend_ast_get_list(ast); zend_op *opline; uint32_t i, opnum_init = -1; - zend_bool packed = 1; + bool packed = 1; if (zend_try_ct_eval_array(&result->u.constant, ast)) { result->op_type = IS_CONST; @@ -8823,7 +8920,7 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; zend_ast *value_ast, *key_ast; - zend_bool by_ref; + bool by_ref; znode value_node, key_node, *key_node_ptr = NULL; if (elem_ast == NULL) { @@ -8890,7 +8987,7 @@ void zend_compile_const(znode *result, zend_ast *ast) /* {{{ */ zend_op *opline; - zend_bool is_fully_qualified; + bool is_fully_qualified; zend_string *orig_name = zend_ast_get_str(name_ast); zend_string *resolved_name = zend_resolve_const_name(orig_name, name_ast->attr, &is_fully_qualified); @@ -9167,7 +9264,7 @@ void zend_compile_magic_const(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ +bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ { return kind == ZEND_AST_ZVAL || kind == ZEND_AST_BINARY_OP || kind == ZEND_AST_GREATER || kind == ZEND_AST_GREATER_EQUAL @@ -9254,7 +9351,7 @@ void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */ zend_ast *ast = *ast_ptr; zend_ast *name_ast = ast->child[0]; zend_string *orig_name = zend_ast_get_str(name_ast); - zend_bool is_fully_qualified; + bool is_fully_qualified; zval result; zend_string *resolved_name; @@ -9675,7 +9772,7 @@ zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_r return opcode; } -zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, zend_bool by_ref) /* {{{ */ +zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref) /* {{{ */ { switch (ast->kind) { case ZEND_AST_VAR: @@ -9736,7 +9833,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_AND: case ZEND_AST_OR: { - zend_bool child0_is_true, child1_is_true; + bool child0_is_true, child1_is_true; zend_eval_const_expr(&ast->child[0]); zend_eval_const_expr(&ast->child[1]); if (ast->child[0]->kind != ZEND_AST_ZVAL) { @@ -9910,7 +10007,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_CONST: { zend_ast *name_ast = ast->child[0]; - zend_bool is_fully_qualified; + bool is_fully_qualified; zend_string *resolved_name = zend_resolve_const_name( zend_ast_get_str(name_ast), name_ast->attr, &is_fully_qualified); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a4cb6fca74..ffc2f7baa4 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -107,8 +107,8 @@ typedef struct _zend_file_context { zend_declarables declarables; zend_string *current_namespace; - zend_bool in_namespace; - zend_bool has_bracketed_namespaces; + bool in_namespace; + bool has_bracketed_namespaces; HashTable *imports; HashTable *imports_function; @@ -117,17 +117,12 @@ typedef struct _zend_file_context { HashTable seen_symbols; } zend_file_context; -typedef struct { - uint32_t offset; - uint32_t len; -} zend_lexer_ident_ref; - typedef union _zend_parser_stack_elem { zend_ast *ast; zend_string *str; zend_ulong num; unsigned char *ptr; - zend_lexer_ident_ref ident; + unsigned char *ident; } zend_parser_stack_elem; void zend_compile_top_stmt(zend_ast *ast); @@ -158,7 +153,7 @@ typedef struct _zend_brk_cont_element { int cont; int brk; int parent; - zend_bool is_switch; + bool is_switch; } zend_brk_cont_element; typedef struct _zend_label { @@ -238,7 +233,7 @@ typedef struct _zend_oparray_context { /* op_array or class is preloaded | | | */ #define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */ /* | | | */ -/* Class Flags (unused: 22...) | | | */ +/* Class Flags (unused: 23...) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -287,6 +282,9 @@ typedef struct _zend_oparray_context { /* Whether this class was used in its unlinked state. | | | */ #define ZEND_ACC_HAS_UNLINKED_USES (1 << 21) /* X | | | */ /* | | | */ +/* stored in opcache (may be partially) | | | */ +#define ZEND_ACC_CACHED (1 << 22) /* X | | | */ +/* | | | */ /* Function Flags (unused: 27-30) | | | */ /* ============== | | | */ /* | | | */ @@ -768,7 +766,7 @@ zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right); zend_ast *zend_negate_num_string(zend_ast *ast); uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag); uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag); -zend_bool zend_handle_encoding_declaration(zend_ast *ast); +bool zend_handle_encoding_declaration(zend_ast *ast); /* parser-driven code generators */ void zend_do_free(znode *op1); @@ -798,7 +796,7 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type); ZEND_API zend_op_array *compile_string(zend_string *source_string, const char *filename); ZEND_API zend_op_array *compile_filename(int type, zval *filename); ZEND_API zend_ast *zend_compile_string_to_ast( - zend_string *code, struct _zend_arena **ast_arena, const char *filename); + zend_string *code, struct _zend_arena **ast_arena, zend_string *filename); ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...); ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); @@ -806,7 +804,7 @@ ZEND_API void destroy_op_array(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_classes(void); -ZEND_API void zend_type_release(zend_type type, zend_bool persistent); +ZEND_API void zend_type_release(zend_type type, bool persistent); ZEND_API zend_string *zend_create_member_string(zend_string *class_name, zend_string *member_name); @@ -840,30 +838,30 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen #define ZEND_FUNCTION_DTOR zend_function_dtor #define ZEND_CLASS_DTOR destroy_zend_class -typedef zend_bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline); +typedef bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline); ZEND_API void zend_recalc_live_ranges( zend_op_array *op_array, zend_needs_live_range_cb needs_live_range); ZEND_API void pass_two(zend_op_array *op_array); -ZEND_API zend_bool zend_is_compiling(void); +ZEND_API bool zend_is_compiling(void); ZEND_API char *zend_make_compiled_string_description(const char *name); -ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers); +ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_handlers); uint32_t zend_get_class_fetch_type(zend_string *name); ZEND_API zend_uchar zend_get_call_op(const zend_op *init_op, zend_function *fbc); ZEND_API bool zend_is_smart_branch(const zend_op *opline); -typedef zend_bool (*zend_auto_global_callback)(zend_string *name); +typedef bool (*zend_auto_global_callback)(zend_string *name); typedef struct _zend_auto_global { zend_string *name; zend_auto_global_callback auto_global_callback; - zend_bool jit; - zend_bool armed; + bool jit; + bool armed; } zend_auto_global; -ZEND_API zend_result zend_register_auto_global(zend_string *name, zend_bool jit, zend_auto_global_callback auto_global_callback); +ZEND_API zend_result zend_register_auto_global(zend_string *name, bool jit, zend_auto_global_callback auto_global_callback); ZEND_API void zend_activate_auto_globals(void); -ZEND_API zend_bool zend_is_auto_global(zend_string *name); -ZEND_API zend_bool zend_is_auto_global_str(const char *name, size_t len); +ZEND_API bool zend_is_auto_global(zend_string *name); +ZEND_API bool zend_is_auto_global_str(const char *name, size_t len); ZEND_API size_t zend_dirname(char *path, size_t len); ZEND_API void zend_set_function_arg_flags(zend_function *func); @@ -1134,6 +1132,6 @@ END_EXTERN_C() /* The default value for CG(compiler_options) during eval() */ #define ZEND_COMPILE_DEFAULT_FOR_EVAL 0 -ZEND_API zend_bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2); +ZEND_API bool zend_binary_op_produces_error(uint32_t opcode, zval *op1, zval *op2); #endif /* ZEND_COMPILE_H */ diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index f289cc5d91..03ab10b523 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -158,7 +158,7 @@ ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int zend_register_constant(&c); } -ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, zend_bool bval, int flags, int module_number) +ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number) { zend_constant c; @@ -482,7 +482,7 @@ ZEND_API zend_result zend_register_constant(zend_constant *c) zend_string *lowercase_name = NULL; zend_string *name; zend_result ret = SUCCESS; - zend_bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0; + bool persistent = (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) != 0; #if 0 printf("Registering constant for module %d\n", c->module_number); diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 0a3c5a7377..0723514447 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -76,7 +76,7 @@ ZEND_API bool zend_verify_const_access(zend_class_constant *c, zend_class_entry ZEND_API zval *zend_get_constant(zend_string *name); ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len); ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags); -ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, zend_bool bval, int flags, int module_number); +ZEND_API void zend_register_bool_constant(const char *name, size_t name_len, bool bval, int flags, int module_number); ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number); ZEND_API void zend_register_long_constant(const char *name, size_t name_len, zend_long lval, int flags, int module_number); ZEND_API void zend_register_double_constant(const char *name, size_t name_len, double dval, int flags, int module_number); diff --git a/Zend/zend_cpuinfo.c b/Zend/zend_cpuinfo.c index 529ab529a3..bd4dcf298f 100644 --- a/Zend/zend_cpuinfo.c +++ b/Zend/zend_cpuinfo.c @@ -90,7 +90,7 @@ static unsigned get_xcr0_eax() { # endif } -static zend_bool is_avx_supported() { +static bool is_avx_supported() { if (!(cpuinfo.ecx & ZEND_CPU_FEATURE_AVX)) { /* No support for AVX */ return 0; @@ -106,7 +106,7 @@ static zend_bool is_avx_supported() { return 1; } #else -static zend_bool is_avx_supported() { +static bool is_avx_supported() { return 0; } #endif diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index b52b1ab113..0026860ef8 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -53,14 +53,21 @@ static zend_object_handlers default_exception_handlers; /* {{{ zend_implement_throwable */ static int zend_implement_throwable(zend_class_entry *interface, zend_class_entry *class_type) { - if (instanceof_function(class_type, zend_ce_exception) || instanceof_function(class_type, zend_ce_error)) { + /* zend_ce_exception and zend_ce_error may not be initialized yet when this is caleld (e.g when + * implementing Throwable for Exception itself). Perform a manual inheritance check. */ + zend_class_entry *root = class_type; + while (root->parent) { + root = root->parent; + } + if (zend_string_equals_literal(root->name, "Exception") + || zend_string_equals_literal(root->name, "Error")) { return SUCCESS; } - zend_error_noreturn(E_ERROR, "Class %s cannot implement interface %s, extend %s or %s instead", + + zend_error_noreturn(E_ERROR, + "Class %s cannot implement interface %s, extend Exception or Error instead", ZSTR_VAL(class_type->name), - ZSTR_VAL(interface->name), - ZSTR_VAL(zend_ce_exception->name), - ZSTR_VAL(zend_ce_error->name)); + ZSTR_VAL(interface->name)); return FAILURE; } /* }}} */ @@ -144,7 +151,7 @@ void zend_exception_restore(void) /* {{{ */ } /* }}} */ -static zend_always_inline zend_bool is_handle_exception_set() { +static zend_always_inline bool is_handle_exception_set() { zend_execute_data *execute_data = EG(current_execute_data); return !execute_data->func || !ZEND_USER_CODE(execute_data->func->common.type) @@ -339,7 +346,7 @@ ZEND_METHOD(ErrorException, __construct) { zend_string *message = NULL, *filename = NULL; zend_long code = 0, severity = E_ERROR, lineno; - zend_bool lineno_is_null = 1; + bool lineno_is_null = 1; zval tmp, *object, *previous = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SllS!l!O!", &message, &code, &severity, &filename, &lineno, &lineno_is_null, &previous, zend_ce_throwable) == FAILURE) { @@ -478,7 +485,7 @@ ZEND_METHOD(ErrorException, getSeverity) static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ { /* the trivial way would be to do - * convert_to_string_ex(arg); + * convert_to_string(arg); * append it and kill the now tmp arg. * but that could cause some E_NOTICE and also damn long lines. */ @@ -740,87 +747,50 @@ ZEND_METHOD(Exception, __toString) } /* }}} */ -static void declare_exception_properties(zend_class_entry *ce) -{ - zval val; - - zend_declare_property_string(ce, "message", sizeof("message")-1, "", ZEND_ACC_PROTECTED); - zend_declare_property_string(ce, "string", sizeof("string")-1, "", ZEND_ACC_PRIVATE); - zend_declare_property_long(ce, "code", sizeof("code")-1, 0, ZEND_ACC_PROTECTED); - zend_declare_property_null(ce, "file", sizeof("file")-1, ZEND_ACC_PROTECTED); - zend_declare_property_null(ce, "line", sizeof("line")-1, ZEND_ACC_PROTECTED); - - ZVAL_EMPTY_ARRAY(&val); - zend_declare_typed_property( - ce, ZSTR_KNOWN(ZEND_STR_TRACE), &val, ZEND_ACC_PRIVATE, NULL, - (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - - ZVAL_NULL(&val); - zend_declare_typed_property( - ce, ZSTR_KNOWN(ZEND_STR_PREVIOUS), &val, ZEND_ACC_PRIVATE, NULL, - (zend_type) ZEND_TYPE_INIT_CE(zend_ce_throwable, /* allow_null */ 1, 0)); -} - void zend_register_default_exception(void) /* {{{ */ { - zend_class_entry ce; - - REGISTER_MAGIC_INTERFACE(throwable, Throwable); - zend_class_implements(zend_ce_throwable, 1, zend_ce_stringable); + zend_ce_throwable = register_class_Throwable(zend_ce_stringable); + zend_ce_throwable->interface_gets_implemented = zend_implement_throwable; memcpy(&default_exception_handlers, &std_object_handlers, sizeof(zend_object_handlers)); default_exception_handlers.clone_obj = NULL; - INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods); - zend_ce_exception = zend_register_internal_class_ex(&ce, NULL); + zend_ce_exception = register_class_Exception(zend_ce_throwable); zend_ce_exception->create_object = zend_default_exception_new; - zend_class_implements(zend_ce_exception, 1, zend_ce_throwable); - declare_exception_properties(zend_ce_exception); - INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods); - zend_ce_error_exception = zend_register_internal_class_ex(&ce, zend_ce_exception); + zend_ce_error_exception = register_class_ErrorException(zend_ce_exception); zend_ce_error_exception->create_object = zend_error_exception_new; + /* Declared manually because it uses constant E_ERROR. */ zend_declare_property_long(zend_ce_error_exception, "severity", sizeof("severity")-1, E_ERROR, ZEND_ACC_PROTECTED); - INIT_CLASS_ENTRY(ce, "Error", class_Error_methods); - zend_ce_error = zend_register_internal_class_ex(&ce, NULL); + zend_ce_error = register_class_Error(zend_ce_throwable); zend_ce_error->create_object = zend_default_exception_new; - zend_class_implements(zend_ce_error, 1, zend_ce_throwable); - declare_exception_properties(zend_ce_error); - INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods); - zend_ce_compile_error = zend_register_internal_class_ex(&ce, zend_ce_error); + zend_ce_compile_error = register_class_CompileError(zend_ce_error); zend_ce_compile_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods); - zend_ce_parse_error = zend_register_internal_class_ex(&ce, zend_ce_compile_error); + zend_ce_parse_error = register_class_ParseError(zend_ce_compile_error); zend_ce_parse_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods); - zend_ce_type_error = zend_register_internal_class_ex(&ce, zend_ce_error); + zend_ce_type_error = register_class_TypeError(zend_ce_error); zend_ce_type_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods); - zend_ce_argument_count_error = zend_register_internal_class_ex(&ce, zend_ce_type_error); + zend_ce_argument_count_error = register_class_ArgumentCountError(zend_ce_type_error); zend_ce_argument_count_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods); - zend_ce_value_error = zend_register_internal_class_ex(&ce, zend_ce_error); + zend_ce_value_error = register_class_ValueError(zend_ce_error); zend_ce_value_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods); - zend_ce_arithmetic_error = zend_register_internal_class_ex(&ce, zend_ce_error); + zend_ce_arithmetic_error = register_class_ArithmeticError(zend_ce_error); zend_ce_arithmetic_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods); - zend_ce_division_by_zero_error = zend_register_internal_class_ex(&ce, zend_ce_arithmetic_error); + zend_ce_division_by_zero_error = register_class_DivisionByZeroError(zend_ce_arithmetic_error); zend_ce_division_by_zero_error->create_object = zend_default_exception_new; - INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); - - INIT_CLASS_ENTRY(ce, "UnhandledMatchError", NULL); - zend_ce_unhandled_match_error = zend_register_internal_class_ex(&ce, zend_ce_error); + zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error); zend_ce_unhandled_match_error->create_object = zend_default_exception_new; + + INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL); } /* }}} */ @@ -1017,7 +987,7 @@ ZEND_API ZEND_COLD void zend_throw_unwind_exit(void) EG(current_execute_data)->opline = EG(exception_op); } -ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex) +ZEND_API bool zend_is_unwind_exit(zend_object *ex) { return ex->ce == &zend_ce_unwind_exit; } diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index 9d0c18a6da..74724c2424 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -70,7 +70,7 @@ extern ZEND_API void (*zend_throw_exception_hook)(zend_object *ex); ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_API ZEND_COLD void zend_throw_unwind_exit(void); -ZEND_API zend_bool zend_is_unwind_exit(zend_object *ex); +ZEND_API bool zend_is_unwind_exit(zend_object *ex); #include "zend_globals.h" diff --git a/Zend/zend_exceptions.stub.php b/Zend/zend_exceptions.stub.php index 11c1d500d3..89dff788c0 100644 --- a/Zend/zend_exceptions.stub.php +++ b/Zend/zend_exceptions.stub.php @@ -1,6 +1,9 @@ <?php -/** @generate-function-entries */ +/** + * @generate-function-entries + * @generate-class-entries + */ interface Throwable extends Stringable { @@ -22,6 +25,19 @@ interface Throwable extends Stringable class Exception implements Throwable { + /** @var string */ + protected $message = ""; + /** @var string */ + private $string = ""; + /** @var int */ + protected $code = 0; + /** @var string|null */ + protected $file = null; + /** @var int|null */ + protected $line = null; + private array $trace = []; + private ?Throwable $previous = null; + final private function __clone(): void {} public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null) {} @@ -49,13 +65,36 @@ class Exception implements Throwable class ErrorException extends Exception { - public function __construct(string $message = "", int $code = 0, int $severity = E_ERROR, ?string $filename = null, ?int $line = null, ?Throwable $previous = null) {} + /** @var int */ + protected $severity = E_ERROR; + + public function __construct( + string $message = "", + int $code = 0, + int $severity = E_ERROR, + ?string $filename = null, + ?int $line = null, + ?Throwable $previous = null + ) {} final public function getSeverity(): int {} } class Error implements Throwable { + /** @var string */ + protected $message = ""; + /** @var string */ + private $string = ""; + /** @var int */ + protected $code = 0; + /** @var string|null */ + protected $file = null; + /** @var int|null */ + protected $line = null; + private array $trace = []; + private ?Throwable $previous = null; + /** @implementation-alias Exception::__clone */ final private function __clone(): void {} @@ -123,3 +162,7 @@ class ArithmeticError extends Error class DivisionByZeroError extends ArithmeticError { } + +class UnhandledMatchError extends Error +{ +} diff --git a/Zend/zend_exceptions_arginfo.h b/Zend/zend_exceptions_arginfo.h index 5b8dab9b4d..04e3ec753b 100644 --- a/Zend/zend_exceptions_arginfo.h +++ b/Zend/zend_exceptions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3699b51b31e509c11435845c7e0d35a2608dd268 */ + * Stub hash: 053248482a00efc35be505186f8430708bd280e9 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Throwable_getMessage, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -180,3 +180,218 @@ static const zend_function_entry class_ArithmeticError_methods[] = { static const zend_function_entry class_DivisionByZeroError_methods[] = { ZEND_FE_END }; + + +static const zend_function_entry class_UnhandledMatchError_methods[] = { + ZEND_FE_END +}; + +zend_class_entry *register_class_Throwable(zend_class_entry *class_entry_Stringable) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Throwable", class_Throwable_methods); + class_entry = zend_register_internal_interface(&ce); + zend_class_implements(class_entry, 1, class_entry_Stringable); + + return class_entry; +} + +zend_class_entry *register_class_Exception(zend_class_entry *class_entry_Throwable) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Exception", class_Exception_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 1, class_entry_Throwable); + + zval property_message_default_value; + ZVAL_EMPTY_STRING(&property_message_default_value); + zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1); + zend_declare_property_ex(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_message_name); + + zval property_string_default_value; + ZVAL_EMPTY_STRING(&property_string_default_value); + zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1); + zend_declare_property_ex(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL); + zend_string_release(property_string_name); + + zval property_code_default_value; + ZVAL_LONG(&property_code_default_value, 0); + zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1); + zend_declare_property_ex(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_code_name); + + zval property_file_default_value; + ZVAL_NULL(&property_file_default_value); + zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1); + zend_declare_property_ex(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_file_name); + + zval property_line_default_value; + ZVAL_NULL(&property_line_default_value); + zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1); + zend_declare_property_ex(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_line_name); + + zval property_trace_default_value; + ZVAL_EMPTY_ARRAY(&property_trace_default_value); + zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1); + zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_trace_name); + + zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); + zval property_previous_default_value; + ZVAL_NULL(&property_previous_default_value); + zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 1, 0)); + zend_string_release(property_previous_name); + + return class_entry; +} + +zend_class_entry *register_class_ErrorException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ErrorException", class_ErrorException_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception); + + return class_entry; +} + +zend_class_entry *register_class_Error(zend_class_entry *class_entry_Throwable) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Error", class_Error_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + zend_class_implements(class_entry, 1, class_entry_Throwable); + + zval property_message_default_value; + ZVAL_EMPTY_STRING(&property_message_default_value); + zend_string *property_message_name = zend_string_init("message", sizeof("message") - 1, 1); + zend_declare_property_ex(class_entry, property_message_name, &property_message_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_message_name); + + zval property_string_default_value; + ZVAL_EMPTY_STRING(&property_string_default_value); + zend_string *property_string_name = zend_string_init("string", sizeof("string") - 1, 1); + zend_declare_property_ex(class_entry, property_string_name, &property_string_default_value, ZEND_ACC_PRIVATE, NULL); + zend_string_release(property_string_name); + + zval property_code_default_value; + ZVAL_LONG(&property_code_default_value, 0); + zend_string *property_code_name = zend_string_init("code", sizeof("code") - 1, 1); + zend_declare_property_ex(class_entry, property_code_name, &property_code_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_code_name); + + zval property_file_default_value; + ZVAL_NULL(&property_file_default_value); + zend_string *property_file_name = zend_string_init("file", sizeof("file") - 1, 1); + zend_declare_property_ex(class_entry, property_file_name, &property_file_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_file_name); + + zval property_line_default_value; + ZVAL_NULL(&property_line_default_value); + zend_string *property_line_name = zend_string_init("line", sizeof("line") - 1, 1); + zend_declare_property_ex(class_entry, property_line_name, &property_line_default_value, ZEND_ACC_PROTECTED, NULL); + zend_string_release(property_line_name); + + zval property_trace_default_value; + ZVAL_EMPTY_ARRAY(&property_trace_default_value); + zend_string *property_trace_name = zend_string_init("trace", sizeof("trace") - 1, 1); + zend_declare_typed_property(class_entry, property_trace_name, &property_trace_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release(property_trace_name); + + zend_string *property_previous_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); + zval property_previous_default_value; + ZVAL_NULL(&property_previous_default_value); + zend_string *property_previous_name = zend_string_init("previous", sizeof("previous") - 1, 1); + zend_declare_typed_property(class_entry, property_previous_name, &property_previous_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_previous_class_Throwable, 1, 0)); + zend_string_release(property_previous_name); + + return class_entry; +} + +zend_class_entry *register_class_CompileError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "CompileError", class_CompileError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + + return class_entry; +} + +zend_class_entry *register_class_ParseError(zend_class_entry *class_entry_CompileError) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ParseError", class_ParseError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_CompileError); + + return class_entry; +} + +zend_class_entry *register_class_TypeError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "TypeError", class_TypeError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + + return class_entry; +} + +zend_class_entry *register_class_ArgumentCountError(zend_class_entry *class_entry_TypeError) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ArgumentCountError", class_ArgumentCountError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_TypeError); + + return class_entry; +} + +zend_class_entry *register_class_ValueError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ValueError", class_ValueError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + + return class_entry; +} + +zend_class_entry *register_class_ArithmeticError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ArithmeticError", class_ArithmeticError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + + return class_entry; +} + +zend_class_entry *register_class_DivisionByZeroError(zend_class_entry *class_entry_ArithmeticError) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DivisionByZeroError", class_DivisionByZeroError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_ArithmeticError); + + return class_entry; +} + +zend_class_entry *register_class_UnhandledMatchError(zend_class_entry *class_entry_Error) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "UnhandledMatchError", class_UnhandledMatchError_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Error); + + return class_entry; +} + diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 14ebcec1b7..3ac1535734 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -716,12 +716,12 @@ ZEND_API ZEND_COLD void zend_verify_arg_error( zend_string_release(need_msg); } -static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) +static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { zend_long lval; double dval; zend_string *str; - zend_bool bval; + bool bval; /* Type preference order: int -> float -> string -> bool */ if (type_mask & MAY_BE_LONG) { @@ -775,11 +775,11 @@ static bool can_convert_to_string(zval *zv) { } /* Used to sanity-check internal arginfo types without performing any actual type conversions. */ -static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg) +static bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg) { zend_long lval; double dval; - zend_bool bval; + bool bval; if ((type_mask & MAY_BE_LONG) && zend_parse_arg_long_weak(arg, &lval)) { return 1; @@ -797,7 +797,7 @@ static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_m } #endif -ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg) +ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg) { if (UNEXPECTED(strict)) { /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ @@ -851,7 +851,7 @@ static zend_class_entry *resolve_single_class_type(zend_string *name, zend_class } } -static zend_bool zend_check_and_resolve_property_class_type( +static bool zend_check_and_resolve_property_class_type( zend_property_info *info, zend_class_entry *object_ce) { zend_class_entry *ce; if (ZEND_TYPE_HAS_LIST(info->type)) { @@ -890,7 +890,7 @@ static zend_bool zend_check_and_resolve_property_class_type( } } -static zend_always_inline zend_bool i_zend_check_property_type(zend_property_info *info, zval *property, zend_bool strict) +static zend_always_inline bool i_zend_check_property_type(zend_property_info *info, zval *property, bool strict) { ZEND_ASSERT(!Z_ISREF_P(property)); if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { @@ -910,7 +910,7 @@ static zend_always_inline zend_bool i_zend_check_property_type(zend_property_inf return zend_verify_scalar_type_hint(type_mask, property, strict, 0); } -static zend_always_inline zend_bool i_zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) +static zend_always_inline bool i_zend_verify_property_type(zend_property_info *info, zval *property, bool strict) { if (i_zend_check_property_type(info, property, strict)) { return 1; @@ -920,7 +920,7 @@ static zend_always_inline zend_bool i_zend_verify_property_type(zend_property_in return 0; } -ZEND_API zend_bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) { +ZEND_API bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, bool strict) { return i_zend_verify_property_type(info, property, strict); } @@ -939,7 +939,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES()); } -ZEND_API zend_bool zend_value_instanceof_static(zval *zv) { +ZEND_API bool zend_value_instanceof_static(zval *zv) { if (Z_TYPE_P(zv) != IS_OBJECT) { return 0; } @@ -959,9 +959,9 @@ ZEND_API zend_bool zend_value_instanceof_static(zval *zv) { # define HAVE_CACHE_SLOT 1 #endif -static zend_always_inline zend_bool zend_check_type_slow( +static zend_always_inline bool zend_check_type_slow( zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope, - zend_bool is_return_type, zend_bool is_internal) + bool is_return_type, bool is_internal) { uint32_t type_mask; if (ZEND_TYPE_HAS_CLASS(type) && Z_TYPE_P(arg) == IS_OBJECT) { @@ -1039,9 +1039,9 @@ builtin_types: * because this case is already checked at compile-time. */ } -static zend_always_inline zend_bool zend_check_type( +static zend_always_inline bool zend_check_type( zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope, - zend_bool is_return_type, zend_bool is_internal) + bool is_return_type, bool is_internal) { zend_reference *ref = NULL; ZEND_ASSERT(ZEND_TYPE_IS_SET(type)); @@ -1115,7 +1115,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ /* Determine whether an internal call should throw, because the passed arguments violate * an arginfo constraint. This is only checked in debug builds. In release builds, we * trust that arginfo matches what is enforced by zend_parse_parameters. */ -static zend_always_inline zend_bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call) +static zend_always_inline bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call) { if (fbc->internal_function.handler == ZEND_FN(pass)) { /* Be lenient about the special pass function. */ @@ -2100,31 +2100,7 @@ num_undef: } str_index: retval = zend_hash_find_ex(ht, offset_key, ZEND_CONST_COND(dim_type == IS_CONST, 0)); - if (retval) { - /* support for $GLOBALS[...] */ - if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) { - retval = Z_INDIRECT_P(retval); - if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { - switch (type) { - case BP_VAR_R: - zend_undefined_index(offset_key); - /* break missing intentionally */ - case BP_VAR_UNSET: - case BP_VAR_IS: - retval = &EG(uninitialized_zval); - break; - case BP_VAR_RW: - if (UNEXPECTED(zend_undefined_index_write(ht, offset_key))) { - return NULL; - } - /* break missing intentionally */ - case BP_VAR_W: - ZVAL_NULL(retval); - break; - } - } - } - } else { + if (!retval) { switch (type) { case BP_VAR_R: zend_undefined_index(offset_key); @@ -2478,7 +2454,7 @@ num_idx: return zend_hash_index_find(ht, hval); } else if (Z_TYPE_P(offset) == IS_NULL) { str_idx: - return zend_hash_find_ex_ind(ht, ZSTR_EMPTY_ALLOC(), 1); + return zend_hash_find_ex(ht, ZSTR_EMPTY_ALLOC(), 1); } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_idx; @@ -2576,7 +2552,7 @@ str_offset: } } -static zend_never_inline zend_bool ZEND_FASTCALL zend_array_key_exists_fast(HashTable *ht, zval *key OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline bool ZEND_FASTCALL zend_array_key_exists_fast(HashTable *ht, zval *key OPLINE_DC EXECUTE_DATA_DC) { zend_string *str; zend_ulong hval; @@ -2588,11 +2564,11 @@ try_again: goto num_key; } str_key: - return zend_hash_find_ind(ht, str) != NULL; + return zend_hash_exists(ht, str); } else if (EXPECTED(Z_TYPE_P(key) == IS_LONG)) { hval = Z_LVAL_P(key); num_key: - return zend_hash_index_find(ht, hval) != NULL; + return zend_hash_index_exists(ht, hval); } else if (EXPECTED(Z_ISREF_P(key))) { key = Z_REFVAL_P(key); goto try_again; @@ -2636,12 +2612,12 @@ static ZEND_COLD void ZEND_FASTCALL zend_array_key_exists_error( } } -static zend_always_inline zend_bool promotes_to_array(zval *val) { +static zend_always_inline bool promotes_to_array(zval *val) { return Z_TYPE_P(val) <= IS_FALSE || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE); } -static zend_always_inline zend_bool check_type_array_assignable(zend_type type) { +static zend_always_inline bool check_type_array_assignable(zend_type type) { if (!ZEND_TYPE_IS_SET(type)) { return 1; } @@ -2649,7 +2625,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type) } /* Checks whether an array can be assigned to the reference. Throws error if not assignable. */ -ZEND_API zend_bool zend_verify_ref_array_assignable(zend_reference *ref) { +ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref) { zend_property_info *prop; ZEND_ASSERT(ZEND_REF_HAS_TYPE_SOURCES(ref)); ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { @@ -2677,7 +2653,7 @@ static zend_property_info *zend_object_fetch_property_type_info( return zend_get_typed_property_info_for_slot(obj, slot); } -static zend_never_inline zend_bool zend_handle_fetch_obj_flags( +static zend_never_inline bool zend_handle_fetch_obj_flags( zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags) { switch (flags) { @@ -2722,7 +2698,7 @@ static zend_never_inline zend_bool zend_handle_fetch_obj_flags( return 1; } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags, zend_bool init_undef OPLINE_DC EXECUTE_DATA_DC) +static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags, bool init_undef OPLINE_DC EXECUTE_DATA_DC) { zval *ptr; zend_object *zobj; @@ -3063,7 +3039,7 @@ ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info /* 1: valid, 0: invalid, -1: may be valid after type coercion */ static zend_always_inline int i_zend_verify_type_assignable_zval( - zend_property_info *info, zval *zv, zend_bool strict) { + zend_property_info *info, zval *zv, bool strict) { zend_type type = info->type; uint32_t type_mask; zend_uchar zv_type = Z_TYPE_P(zv); @@ -3106,7 +3082,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval( return -1; } -ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict) +ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict) { zend_property_info *prop; @@ -3185,9 +3161,9 @@ static zend_always_inline void i_zval_ptr_dtor_noref(zval *zval_ptr) { } } -ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, zend_uchar value_type, zend_bool strict) +ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, zend_uchar value_type, bool strict) { - zend_bool ret; + bool ret; zval value; zend_refcounted *ref = NULL; @@ -3218,7 +3194,7 @@ ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *orig_value, ze return variable_ptr; } -ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict) { +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, bool strict) { zval *val = orig_val; if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) { int result; @@ -3525,7 +3501,7 @@ static zend_always_inline void zend_init_cvs(uint32_t first, uint32_t last EXECU } } -static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, zend_bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */ +static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */ { uint32_t first_extra_arg, num_args; ZEND_ASSERT(EX(func) == (zend_function*)op_array); @@ -4262,11 +4238,11 @@ already_compiled: } /* }}} */ -static zend_never_inline zend_bool ZEND_FASTCALL zend_fe_reset_iterator(zval *array_ptr, int by_ref OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ +static zend_never_inline bool ZEND_FASTCALL zend_fe_reset_iterator(zval *array_ptr, int by_ref OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ { zend_class_entry *ce = Z_OBJCE_P(array_ptr); zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, by_ref); - zend_bool is_empty; + bool is_empty; if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { if (iter) { diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index ab96d43c4c..3339f3992d 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -43,7 +43,7 @@ ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_ ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value); ZEND_API void execute_ex(zend_execute_data *execute_data); ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value); -ZEND_API zend_bool zend_is_valid_class_name(zend_string *name); +ZEND_API bool zend_is_valid_class_name(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class(zend_string *name); ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *lcname, uint32_t flags); ZEND_API zend_class_entry *zend_get_called_scope(zend_execute_data *ex); @@ -60,21 +60,21 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data * ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc); ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num); -ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict); -ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict); +ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict); +ZEND_API bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, bool strict); ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv); ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv); ZEND_API ZEND_COLD zend_result ZEND_FASTCALL zend_undefined_offset_write(HashTable *ht, zend_long lval); ZEND_API ZEND_COLD zend_result ZEND_FASTCALL zend_undefined_index_write(HashTable *ht, zend_string *offset); -ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg); +ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg); ZEND_API ZEND_COLD void zend_verify_arg_error( const zend_function *zf, const zend_arg_info *arg_info, int arg_num, zval *value); ZEND_API ZEND_COLD void zend_verify_return_error( const zend_function *zf, zval *value); -ZEND_API zend_bool zend_verify_ref_array_assignable(zend_reference *ref); -ZEND_API zend_bool zend_value_instanceof_static(zval *zv); +ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); +ZEND_API bool zend_value_instanceof_static(zval *zv); #define ZEND_REF_TYPE_SOURCES(ref) \ @@ -92,7 +92,7 @@ ZEND_API zend_bool zend_value_instanceof_static(zval *zv); ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop); ZEND_API void ZEND_FASTCALL zend_ref_del_type_source(zend_property_info_source_list *source_list, zend_property_info *prop); -ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict); +ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, bool strict); static zend_always_inline void zend_copy_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type) { @@ -121,7 +121,7 @@ static zend_always_inline void zend_copy_to_variable(zval *variable_ptr, zval *v } } -static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict) +static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, bool strict) { do { if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) { @@ -155,8 +155,8 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval return variable_ptr; } -ZEND_API zend_result zval_update_constant(zval *pp); -ZEND_API zend_result zval_update_constant_ex(zval *pp, zend_class_entry *scope); +ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp); +ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *pp, zend_class_entry *scope); /* dedicated Zend executor functions - do not use! */ struct _zend_vm_stack { @@ -317,7 +317,7 @@ ZEND_API const char *zend_get_executed_filename(void); ZEND_API zend_string *zend_get_executed_filename_ex(void); ZEND_API uint32_t zend_get_executed_lineno(void); ZEND_API zend_class_entry *zend_get_executed_scope(void); -ZEND_API zend_bool zend_is_executing(void); +ZEND_API bool zend_is_executing(void); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg_num); ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals); @@ -418,7 +418,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) -ZEND_API zend_bool zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict); +ZEND_API bool zend_verify_property_type(zend_property_info *info, zval *property, bool strict); ZEND_COLD void zend_verify_property_type_error(zend_property_info *info, zval *property); #define ZEND_REF_ADD_TYPE_SOURCE(ref, source) \ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index b55b2e7385..4a6ff1bdbc 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -258,9 +258,9 @@ void shutdown_executor(void) /* {{{ */ zend_string *key; zval *zv; #if ZEND_DEBUG - zend_bool fast_shutdown = 0; + bool fast_shutdown = 0; #else - zend_bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); + bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); #endif zend_try { @@ -604,13 +604,13 @@ ZEND_API zend_class_entry *zend_get_executed_scope(void) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_is_executing(void) /* {{{ */ +ZEND_API bool zend_is_executing(void) /* {{{ */ { return EG(current_execute_data) != 0; } /* }}} */ -ZEND_API zend_result zval_update_constant_ex(zval *p, zend_class_entry *scope) /* {{{ */ +ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *p, zend_class_entry *scope) /* {{{ */ { if (Z_TYPE_P(p) == IS_CONSTANT_AST) { zend_ast *ast = Z_ASTVAL_P(p); @@ -638,7 +638,7 @@ ZEND_API zend_result zval_update_constant_ex(zval *p, zend_class_entry *scope) / } /* }}} */ -ZEND_API zend_result zval_update_constant(zval *pp) /* {{{ */ +ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp) /* {{{ */ { return zval_update_constant_ex(pp, EG(current_execute_data) ? zend_get_executed_scope() : CG(active_class_entry)); } @@ -759,7 +759,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ for (i=0; i<fci->param_count; i++) { zval *param = ZEND_CALL_ARG(call, i+1); zval *arg = &fci->params[i]; - zend_bool must_wrap = 0; + bool must_wrap = 0; if (UNEXPECTED(Z_ISUNDEF_P(arg))) { /* Allow forwarding undef slots. This is only used by Closure::__invoke(). */ ZVAL_UNDEF(param); @@ -806,9 +806,9 @@ cleanup_args: zend_string *name; zval *arg; uint32_t arg_num = ZEND_CALL_NUM_ARGS(call) + 1; - zend_bool have_named_params = 0; + bool have_named_params = 0; ZEND_HASH_FOREACH_STR_KEY_VAL(fci->named_params, name, arg) { - zend_bool must_wrap = 0; + bool must_wrap = 0; zval *target; if (name) { void *cache_slot[2] = {NULL, NULL}; @@ -1017,7 +1017,7 @@ static const uint32_t valid_chars[8] = { 0xffffffff, }; -ZEND_API zend_bool zend_is_valid_class_name(zend_string *name) { +ZEND_API bool zend_is_valid_class_name(zend_string *name) { for (size_t i = 0; i < ZSTR_LEN(name); i++) { unsigned char c = ZSTR_VAL(name)[i]; if (!ZEND_BIT_TEST(valid_chars, c)) { diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h index 9f96d3b0fa..49ce115548 100644 --- a/Zend/zend_extensions.h +++ b/Zend/zend_extensions.h @@ -44,7 +44,7 @@ You can use the following macro to check the extension API version for compatibi /* The first number is the engine version and the rest is the date (YYYYMMDD). * This way engine 2/3 API no. is always greater than engine 1 API no.. */ -#define ZEND_EXTENSION_API_NO 420200930 +#define ZEND_EXTENSION_API_NO 420201009 typedef struct _zend_extension_version_info { int zend_extension_api_no; @@ -75,11 +75,11 @@ typedef size_t (*op_array_persist_calc_func_t)(zend_op_array *op_array); typedef size_t (*op_array_persist_func_t)(zend_op_array *op_array, void *mem); struct _zend_extension { - char *name; - char *version; - char *author; - char *URL; - char *copyright; + const char *name; + const char *version; + const char *author; + const char *URL; + const char *copyright; startup_func_t startup; shutdown_func_t shutdown; diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index bfc124719c..7a9dc4dd61 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -209,10 +209,10 @@ typedef struct _gc_root_buffer { typedef struct _zend_gc_globals { gc_root_buffer *buf; /* preallocated arrays of buffers */ - zend_bool gc_enabled; - zend_bool gc_active; /* GC currently running, forbid nested GC */ - zend_bool gc_protected; /* GC protected, forbid root additions */ - zend_bool gc_full; + bool gc_enabled; + bool gc_active; /* GC currently running, forbid nested GC */ + bool gc_protected; /* GC protected, forbid root additions */ + bool gc_full; uint32_t unused; /* linked list of unused buffers */ uint32_t first_unused; /* first unused buffer */ @@ -493,9 +493,9 @@ void gc_reset(void) } } -ZEND_API zend_bool gc_enable(zend_bool enable) +ZEND_API bool gc_enable(bool enable) { - zend_bool old_enabled = GC_G(gc_enabled); + bool old_enabled = GC_G(gc_enabled); GC_G(gc_enabled) = enable; if (enable && !old_enabled && GC_G(buf) == NULL) { GC_G(buf) = (gc_root_buffer*) pemalloc(sizeof(gc_root_buffer) * GC_DEFAULT_BUF_SIZE, 1); @@ -507,19 +507,19 @@ ZEND_API zend_bool gc_enable(zend_bool enable) return old_enabled; } -ZEND_API zend_bool gc_enabled(void) +ZEND_API bool gc_enabled(void) { return GC_G(gc_enabled); } -ZEND_API zend_bool gc_protect(zend_bool protect) +ZEND_API bool gc_protect(bool protect) { - zend_bool old_protected = GC_G(gc_protected); + bool old_protected = GC_G(gc_protected); GC_G(gc_protected) = protect; return old_protected; } -ZEND_API zend_bool gc_protected(void) +ZEND_API bool gc_protected(void) { return GC_G(gc_protected); } @@ -740,11 +740,8 @@ tail_call: goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { - if ((zend_array*)ref != &EG(symbol_table)) { - ht = (zend_array*)ref; - } else { - goto next; - } + ZEND_ASSERT((zend_array*)ref != &EG(symbol_table)); + ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); @@ -861,12 +858,8 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { - if (((zend_array*)ref) == &EG(symbol_table)) { - GC_REF_SET_BLACK(ref); - goto next; - } else { - ht = (zend_array*)ref; - } + ZEND_ASSERT(((zend_array*)ref) != &EG(symbol_table)); + ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); @@ -1046,12 +1039,8 @@ tail_call: goto next; } } else if (GC_TYPE(ref) == IS_ARRAY) { - if ((zend_array*)ref == &EG(symbol_table)) { - GC_REF_SET_BLACK(ref); - goto next; - } else { - ht = (zend_array*)ref; - } + ZEND_ASSERT((zend_array*)ref != &EG(symbol_table)); + ht = (zend_array*)ref; } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h index f44786425f..3221335733 100644 --- a/Zend/zend_gc.h +++ b/Zend/zend_gc.h @@ -35,12 +35,12 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref); ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref); /* enable/disable automatic start of GC collection */ -ZEND_API zend_bool gc_enable(zend_bool enable); -ZEND_API zend_bool gc_enabled(void); +ZEND_API bool gc_enable(bool enable); +ZEND_API bool gc_enabled(void); /* enable/disable possible root additions */ -ZEND_API zend_bool gc_protect(zend_bool protect); -ZEND_API zend_bool gc_protected(void); +ZEND_API bool gc_protect(bool protect); +ZEND_API bool gc_protected(void); /* The default implementation of the gc_collect_cycles callback. */ ZEND_API int zend_gc_collect_cycles(void); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 68c1865c00..df94446d6f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -115,7 +115,7 @@ static void zend_generator_cleanup_unfinished_execution( } /* }}} */ -ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution) /* {{{ */ +ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution) /* {{{ */ { if (EXPECTED(generator->execute_data)) { zend_execute_data *execute_data = generator->execute_data; @@ -165,14 +165,14 @@ static void zend_generator_remove_child(zend_generator_node *node, zend_generato { ZEND_ASSERT(node->children >= 1); if (node->children == 1) { - node->child.single.child = NULL; + node->child.single = NULL; } else { HashTable *ht = node->child.ht; zend_hash_index_del(ht, (zend_ulong) child); if (node->children == 2) { zend_generator *other_child; ZEND_HASH_FOREACH_PTR(ht, other_child) { - node->child.single.child = other_child; + node->child.single = other_child; break; } ZEND_HASH_FOREACH_END(); zend_hash_destroy(ht); @@ -479,13 +479,13 @@ static void zend_generator_add_child(zend_generator *generator, zend_generator * zend_generator_node *node = &generator->node; if (node->children == 0) { - node->child.single.child = child; + node->child.single = child; } else { if (node->children == 1) { HashTable *ht = emalloc(sizeof(HashTable)); zend_hash_init(ht, 0, NULL, NULL, 0); zend_hash_index_add_new_ptr(ht, - (zend_ulong) node->child.single.child, node->child.single.child); + (zend_ulong) node->child.single, node->child.single); node->child.ht = ht; } @@ -524,7 +524,7 @@ ZEND_API zend_generator *zend_generator_update_root(zend_generator *generator) static zend_generator *get_new_root(zend_generator *generator, zend_generator *root) { while (!root->execute_data && root->node.children == 1) { - root = root->node.child.single.child; + root = root->node.child.single; } if (root->execute_data) { @@ -614,9 +614,6 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener p = &ht->arData[pos]; value = &p->val; - if (Z_TYPE_P(value) == IS_INDIRECT) { - value = Z_INDIRECT_P(value); - } pos++; } while (Z_ISUNDEF_P(value)); @@ -1115,17 +1112,11 @@ zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *ob void zend_register_generator_ce(void) /* {{{ */ { - zend_class_entry ce; - - INIT_CLASS_ENTRY(ce, "Generator", class_Generator_methods); - zend_ce_generator = zend_register_internal_class(&ce); - zend_ce_generator->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_ce_generator = register_class_Generator(zend_ce_iterator); zend_ce_generator->create_object = zend_generator_create; zend_ce_generator->serialize = zend_class_serialize_deny; zend_ce_generator->unserialize = zend_class_unserialize_deny; - /* get_iterator has to be assigned *after* implementing the interface */ - zend_class_implements(zend_ce_generator, 1, zend_ce_iterator); zend_ce_generator->get_iterator = zend_generator_get_iterator; memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers)); @@ -1135,7 +1126,6 @@ void zend_register_generator_ce(void) /* {{{ */ zend_generator_handlers.clone_obj = NULL; zend_generator_handlers.get_constructor = zend_generator_get_constructor; - INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL); - zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_ce_exception); + zend_ce_ClosedGeneratorException = register_class_ClosedGeneratorException(zend_ce_exception); } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 35ff09c99d..17b25a99b8 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -41,10 +41,7 @@ struct _zend_generator_node { uint32_t children; union { HashTable *ht; /* if multiple children */ - struct { /* if one child */ - zend_generator *leaf; /* TODO: Unused, remove. */ - zend_generator *child; - } single; + zend_generator *single; /* if one child */ } child; /* One generator can cache a direct pointer to the current root. * The leaf member points back to the generator using the root cache. */ @@ -97,7 +94,7 @@ static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4; static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8; void zend_register_generator_ce(void); -ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution); +ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution); ZEND_API void zend_generator_resume(zend_generator *generator); ZEND_API void zend_generator_restore_call_stack(zend_generator *generator); diff --git a/Zend/zend_generators.stub.php b/Zend/zend_generators.stub.php index f2e47616ba..879fdb3642 100644 --- a/Zend/zend_generators.stub.php +++ b/Zend/zend_generators.stub.php @@ -1,7 +1,11 @@ <?php -/** @generate-function-entries */ +/** + * @generate-function-entries + * @generate-class-entries + */ +/** @strict-properties */ final class Generator implements Iterator { public function rewind(): void {} @@ -20,3 +24,7 @@ final class Generator implements Iterator public function getReturn(): mixed {} } + +class ClosedGeneratorException extends Exception +{ +} diff --git a/Zend/zend_generators_arginfo.h b/Zend/zend_generators_arginfo.h index 22084d9feb..c4384a770e 100644 --- a/Zend/zend_generators_arginfo.h +++ b/Zend/zend_generators_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 18d2bb68729ff622a5c0c124a8822f7ee882c2ec */ + * Stub hash: 9d6c2801abbb78d402efb2b2ccdd5242438bd6a1 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Generator_rewind, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() @@ -46,3 +46,31 @@ static const zend_function_entry class_Generator_methods[] = { ZEND_ME(Generator, getReturn, arginfo_class_Generator_getReturn, ZEND_ACC_PUBLIC) ZEND_FE_END }; + + +static const zend_function_entry class_ClosedGeneratorException_methods[] = { + ZEND_FE_END +}; + +zend_class_entry *register_class_Generator(zend_class_entry *class_entry_Iterator) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Generator", class_Generator_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_class_implements(class_entry, 1, class_entry_Iterator); + + return class_entry; +} + +zend_class_entry *register_class_ClosedGeneratorException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", class_ClosedGeneratorException_methods); + class_entry = zend_register_internal_class_ex(&ce, class_entry_Exception); + + return class_entry; +} + diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index e0e8ac7700..6d67325fe1 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -81,19 +81,19 @@ struct _zend_compiler_globals { /* Refer to zend_yytnamerr() in zend_language_parser.y for meaning of values */ zend_uchar parse_error; - zend_bool in_compilation; - zend_bool short_tags; + bool in_compilation; + bool short_tags; - zend_bool unclean_shutdown; + bool unclean_shutdown; - zend_bool ini_parser_unbuffered_errors; + bool ini_parser_unbuffered_errors; zend_llist open_files; struct _zend_ini_parser_param *ini_parser_param; - zend_bool skip_shebang; - zend_bool increment_lineno; + bool skip_shebang; + bool increment_lineno; zend_string *doc_comment; uint32_t extra_fn_flags; @@ -109,9 +109,9 @@ struct _zend_compiler_globals { const zend_encoding **script_encoding_list; size_t script_encoding_list_size; - zend_bool multibyte; - zend_bool detect_unicode; - zend_bool encoding_declared; + bool multibyte; + bool detect_unicode; + bool encoding_declared; zend_ast *ast; zend_arena *ast_arena; @@ -176,13 +176,13 @@ struct _zend_executor_globals { uint32_t persistent_classes_count; HashTable *in_autoload; - zend_bool full_tables_cleanup; + bool full_tables_cleanup; /* for extended information support */ - zend_bool no_extensions; + bool no_extensions; - zend_bool vm_interrupt; - zend_bool timed_out; + bool vm_interrupt; + bool timed_out; zend_long hard_timeout; #ifdef ZEND_WIN32 @@ -218,7 +218,7 @@ struct _zend_executor_globals { struct _zend_module_entry *current_module; - zend_bool active; + bool active; zend_uchar flags; zend_long assertions; @@ -238,7 +238,7 @@ struct _zend_executor_globals { HashTable weakrefs; - zend_bool exception_ignore_args; + bool exception_ignore_args; zend_long exception_string_param_max_len; zend_get_gc_buffer get_gc_buffer; @@ -291,9 +291,9 @@ struct _zend_php_scanner_globals { zend_stack state_stack; zend_ptr_stack heredoc_label_stack; zend_stack nest_location_stack; /* for syntax error reporting */ - zend_bool heredoc_scan_ahead; + bool heredoc_scan_ahead; int heredoc_indentation; - zend_bool heredoc_indentation_uses_spaces; + bool heredoc_indentation_uses_spaces; /* original (unfiltered) script */ unsigned char *script_org; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 6d065cd03e..986866ee52 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -246,7 +246,7 @@ ZEND_API const HashTable zend_empty_array = { .pDestructor = ZVAL_PTR_DTOR }; -static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent) +static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent) { GC_SET_REFCOUNT(ht, 1); GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0); @@ -261,7 +261,7 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize ht->nTableSize = zend_hash_check_size(nSize); } -ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent) +ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent) { _zend_hash_init_int(ht, nSize, pDestructor, persistent); } @@ -310,7 +310,7 @@ static void ZEND_FASTCALL zend_hash_packed_grow(HashTable *ht) HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), HT_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)); } -ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed) +ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed) { IS_CONSISTENT(ht); @@ -365,7 +365,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht) pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); } -ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed) +ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed) { HT_ASSERT_RC1(ht); if (nSize == 0) return; @@ -636,7 +636,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosit } } -static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key, zend_bool known_hash) +static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key, bool known_hash) { zend_ulong h; uint32_t nIndex; @@ -2145,7 +2145,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) } -ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, zend_bool overwrite) +ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite) { uint32_t idx; Bucket *p; @@ -2203,7 +2203,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source } -static zend_bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func) +static bool ZEND_FASTCALL zend_hash_replace_checker_wrapper(HashTable *target, zval *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func) { zend_hash_key hash_key; @@ -2494,7 +2494,7 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q) q->h = h; } -ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, zend_bool renumber) +ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, bool renumber) { Bucket *p; uint32_t i, j; @@ -2566,7 +2566,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, b } } -static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered) { +static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered) { uint32_t idx1, idx2; if (ht1->nNumOfElements != ht2->nNumOfElements) { @@ -2644,7 +2644,7 @@ static zend_always_inline int zend_hash_compare_impl(HashTable *ht1, HashTable * return 0; } -ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered) +ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered) { int result; IS_CONSISTENT(ht1); @@ -2803,7 +2803,7 @@ convert: * a "symtable" (contains integer and non-numeric string keys). * If the proptable didn't need duplicating, its refcount is incremented. */ -ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, zend_bool always_duplicate) +ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate) { zend_ulong num_key; zend_string *str_key; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index ced21a2ca1..64a9220573 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -92,24 +92,24 @@ typedef struct _zend_hash_key { zend_string *key; } zend_hash_key; -typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam); +typedef bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam); BEGIN_EXTERN_C() /* startup/shutdown */ -ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent); +ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent); ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht); ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht); #define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \ _zend_hash_init((ht), (nSize), (pDestructor), (persistent)) -ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed); +ZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, bool packed); ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht); ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht); ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht); ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht); -ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, zend_bool packed); +ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed); ZEND_API void ZEND_FASTCALL zend_hash_discard(HashTable *ht, uint32_t nNumUsed); /* additions/updates/changes */ @@ -178,7 +178,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_index_find(const HashTable *ht, zend_ulo /* The same as zend_hash_find(), but hash value of the key must be already calculated */ ZEND_API zval* ZEND_FASTCALL _zend_hash_find_known_hash(const HashTable *ht, zend_string *key); -static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_string *key, zend_bool known_hash) +static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_string *key, bool known_hash) { if (known_hash) { return _zend_hash_find_known_hash(ht, key); @@ -207,17 +207,17 @@ static zend_always_inline zval *zend_hash_find_ex(const HashTable *ht, zend_stri /* Misc */ -static zend_always_inline zend_bool zend_hash_exists(const HashTable *ht, zend_string *key) +static zend_always_inline bool zend_hash_exists(const HashTable *ht, zend_string *key) { return zend_hash_find(ht, key) != NULL; } -static zend_always_inline zend_bool zend_hash_str_exists(const HashTable *ht, const char *str, size_t len) +static zend_always_inline bool zend_hash_str_exists(const HashTable *ht, const char *str, size_t len) { return zend_hash_str_find(ht, str, len) != NULL; } -static zend_always_inline zend_bool zend_hash_index_exists(const HashTable *ht, zend_ulong h) +static zend_always_inline bool zend_hash_index_exists(const HashTable *ht, zend_ulong h) { return zend_hash_index_find(ht, h) != NULL; } @@ -257,15 +257,15 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Ha /* Copying, merging and sorting */ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor); -ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, zend_bool overwrite); +ZEND_API void ZEND_FASTCALL zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite); ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam); ZEND_API void zend_hash_bucket_swap(Bucket *p, Bucket *q); ZEND_API void zend_hash_bucket_renum_swap(Bucket *p, Bucket *q); ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q); typedef int (*bucket_compare_func_t)(Bucket *a, Bucket *b); -ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered); -ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, zend_bool renumber); +ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered); +ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber); ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, bucket_compare_func_t compar, uint32_t flag); #define zend_hash_sort(ht, compare_func, renumber) \ @@ -303,7 +303,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source); ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht); ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht); ZEND_API HashTable* ZEND_FASTCALL zend_symtable_to_proptable(HashTable *ht); -ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, zend_bool always_duplicate); +ZEND_API HashTable* ZEND_FASTCALL zend_proptable_to_symtable(HashTable *ht, bool always_duplicate); ZEND_API bool ZEND_FASTCALL _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx); @@ -386,7 +386,7 @@ static zend_always_inline zval *zend_hash_find_ind(const HashTable *ht, zend_str } -static zend_always_inline zval *zend_hash_find_ex_ind(const HashTable *ht, zend_string *key, zend_bool known_hash) +static zend_always_inline zval *zend_hash_find_ex_ind(const HashTable *ht, zend_string *key, bool known_hash) { zval *zv; @@ -839,7 +839,7 @@ static zend_always_inline void *zend_hash_find_ptr(const HashTable *ht, zend_str } } -static zend_always_inline void *zend_hash_find_ex_ptr(const HashTable *ht, zend_string *key, zend_bool known_hash) +static zend_always_inline void *zend_hash_find_ex_ptr(const HashTable *ht, zend_string *key, bool known_hash) { zval *zv; @@ -1188,6 +1188,33 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, ZEND_HASH_FILL_FINISH(); \ } while (0) +/* Check if an array is a list */ +static zend_always_inline zend_bool zend_array_is_list(zend_array *array) +{ + zend_long expected_idx = 0; + zend_long num_idx; + zend_string* str_idx; + /* Empty arrays are lists */ + if (zend_hash_num_elements(array) == 0) { + return 1; + } + + /* Packed arrays are lists */ + if (HT_IS_PACKED(array) && HT_IS_WITHOUT_HOLES(array)) { + return 1; + } + + /* Check if the list could theoretically be repacked */ + ZEND_HASH_FOREACH_KEY(array, num_idx, str_idx) { + if (str_idx != NULL || num_idx != expected_idx++) { + return 0; + } + } ZEND_HASH_FOREACH_END(); + + return 1; +} + + static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string *key, zval *zv, bool interned) { uint32_t idx = ht->nNumUsed++; diff --git a/Zend/zend_highlight.h b/Zend/zend_highlight.h index c4f819d508..e54a339ec4 100644 --- a/Zend/zend_highlight.h +++ b/Zend/zend_highlight.h @@ -40,7 +40,7 @@ BEGIN_EXTERN_C() ZEND_API void zend_highlight(zend_syntax_highlighter_ini *syntax_highlighter_ini); ZEND_API void zend_strip(void); ZEND_API zend_result highlight_file(const char *filename, zend_syntax_highlighter_ini *syntax_highlighter_ini); -ZEND_API void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name); +ZEND_API void highlight_string(zend_string *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name); ZEND_API void zend_html_putc(char c); ZEND_API void zend_html_puts(const char *s, size_t len); END_EXTERN_C() diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 4b161692af..16e31a10d8 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -35,7 +35,7 @@ static void add_property_compatibility_obligation( zend_class_entry *ce, const zend_property_info *child_prop, const zend_property_info *parent_prop); -static void zend_type_copy_ctor(zend_type *type, zend_bool persistent) { +static void zend_type_copy_ctor(zend_type *type, bool persistent) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *old_list = ZEND_TYPE_LIST(*type); size_t size = ZEND_TYPE_LIST_SIZE(old_list->num_types); @@ -111,7 +111,7 @@ static zend_function *zend_duplicate_user_function(zend_function *func) /* {{{ * } /* }}} */ -static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce, zend_bool is_interface) /* {{{ */ +static zend_always_inline zend_function *zend_duplicate_function(zend_function *func, zend_class_entry *ce, bool is_interface) /* {{{ */ { if (UNEXPECTED(func->type == ZEND_INTERNAL_FUNCTION)) { return zend_duplicate_internal_function(func, ce); @@ -229,7 +229,7 @@ static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *nam } } -static zend_bool class_visible(zend_class_entry *ce) { +static bool class_visible(zend_class_entry *ce) { if (ce->type == ZEND_INTERNAL_CLASS) { return !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES); } else { @@ -240,7 +240,7 @@ static zend_bool class_visible(zend_class_entry *ce) { } static zend_class_entry *lookup_class( - zend_class_entry *scope, zend_string *name, zend_bool register_unresolved) { + zend_class_entry *scope, zend_string *name, bool register_unresolved) { uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD; zend_class_entry *ce = zend_lookup_class_ex(name, NULL, flags); if (!CG(in_compilation)) { @@ -271,7 +271,7 @@ static zend_class_entry *lookup_class( } /* Instanceof that's safe to use on unlinked classes. */ -static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { +static bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { if (ce1 == ce2) { return 1; } @@ -322,7 +322,7 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce return 0; } -static zend_bool zend_type_contains_traversable(zend_type type) { +static bool zend_type_contains_traversable(zend_type type) { zend_type *single_type; if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) { return 1; @@ -337,7 +337,7 @@ static zend_bool zend_type_contains_traversable(zend_type type) { return 0; } -static zend_bool zend_type_permits_self( +static bool zend_type_permits_self( zend_type type, zend_class_entry *scope, zend_class_entry *self) { if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) { return 1; @@ -371,8 +371,8 @@ typedef enum { static inheritance_status zend_perform_covariant_class_type_check( zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce, zend_class_entry *proto_scope, zend_type proto_type, - zend_bool register_unresolved) { - zend_bool have_unresolved = 0; + bool register_unresolved) { + bool have_unresolved = 0; if (ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_OBJECT) { /* Currently, any class name would be allowed here. We still perform a class lookup * for forward-compatibility reasons, as we may have named types in the future that @@ -458,7 +458,7 @@ static inheritance_status zend_perform_covariant_type_check( } zend_type *single_type; - zend_bool all_success = 1; + bool all_success = 1; /* First try to check whether we can succeed without resolving anything */ ZEND_TYPE_FOREACH(fe_type, single_type) { @@ -540,7 +540,7 @@ static inheritance_status zend_do_perform_implementation_check( { uint32_t i, num_args, proto_num_args, fe_num_args; inheritance_status status, local_status; - zend_bool proto_is_variadic, fe_is_variadic; + bool proto_is_variadic, fe_is_variadic; /* Checks for constructors only if they are declared in an interface, * or explicitly marked as abstract @@ -833,7 +833,7 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( zend_function *child, zend_class_entry *child_scope, zend_function *parent, zend_class_entry *parent_scope, zend_class_entry *ce, zval *child_zv, - zend_bool check_visibility, zend_bool check_only, zend_bool checked) /* {{{ */ + bool check_visibility, bool check_only, bool checked) /* {{{ */ { uint32_t child_flags; uint32_t parent_flags = parent->common.fn_flags; @@ -943,12 +943,12 @@ static zend_always_inline inheritance_status do_inheritance_check_on_method_ex( static zend_never_inline void do_inheritance_check_on_method( zend_function *child, zend_class_entry *child_scope, zend_function *parent, zend_class_entry *parent_scope, - zend_class_entry *ce, zval *child_zv, zend_bool check_visibility) + zend_class_entry *ce, zval *child_zv, bool check_visibility) { do_inheritance_check_on_method_ex(child, child_scope, parent, parent_scope, ce, child_zv, check_visibility, 0, 0); } -static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, zend_bool is_interface, zend_bool checked) /* {{{ */ +static zend_always_inline void do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce, bool is_interface, bool checked) /* {{{ */ { zval *child = zend_hash_find_ex(&ce->function_table, key, 1); @@ -1189,7 +1189,7 @@ void zend_build_properties_info_table(zend_class_entry *ce) } ZEND_HASH_FOREACH_END(); } -ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked) /* {{{ */ +ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked) /* {{{ */ { zend_property_info *property_info; zend_function *func; @@ -1412,7 +1412,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par } /* }}} */ -static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ +static bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ { zval *zv = zend_hash_find_ex(child_constants_table, name, 1); zend_class_constant *old_constant; @@ -1549,11 +1549,13 @@ static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry } } - for (i = 0; i < ce->num_interfaces; i++) { - zend_string_release_ex(ce->interface_names[i].name, 0); - zend_string_release_ex(ce->interface_names[i].lc_name, 0); + if (!(ce->ce_flags & ZEND_ACC_CACHED)) { + for (i = 0; i < ce->num_interfaces; i++) { + zend_string_release_ex(ce->interface_names[i].name, 0); + zend_string_release_ex(ce->interface_names[i].lc_name, 0); + } + efree(ce->interface_names); } - efree(ce->interface_names); ce->num_interfaces = num_interfaces; ce->interfaces = interfaces; @@ -1876,9 +1878,6 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e } aliases[i] = trait; - - /* TODO: try to avoid this assignment (it's necessary only for reflection) */ - cur_method_ref->class_name = zend_string_copy(trait->name); } zend_string_release_ex(lcname, 0); i++; @@ -1952,7 +1951,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent zend_property_info *new_prop; zend_string* prop_name; const char* class_name_unused; - zend_bool not_compatible; + bool not_compatible; zval* prop_value; uint32_t flags; zend_string *doc_comment; @@ -2162,7 +2161,7 @@ void zend_verify_abstract_class(zend_class_entry *ce) /* {{{ */ { zend_function *func; zend_abstract_info ai; - zend_bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0; + bool is_explicit_abstract = (ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) != 0; memset(&ai, 0, sizeof(ai)); ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { @@ -2540,7 +2539,7 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e } /* }}} */ -zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */ +bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */ { inheritance_status status = zend_can_early_bind(ce, parent_ce); diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index e49ec49b6f..e82910f052 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -25,7 +25,7 @@ BEGIN_EXTERN_C() ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface); -ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked); +ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked); #define zend_do_inheritance(ce, parent_ce) \ zend_do_inheritance_ex(ce, parent_ce, 0) @@ -34,7 +34,7 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa void zend_verify_abstract_class(zend_class_entry *ce); void zend_build_properties_info_table(zend_class_entry *ce); -zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding); +bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding); END_EXTERN_C() diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index 75c7faf4c0..b3418b1152 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -311,7 +311,7 @@ ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new zend_ini_entry *ini_entry; zend_string *duplicate; uint8_t modifiable; - zend_bool modified; + bool modified; if ((ini_entry = zend_hash_find_ptr(EG(ini_directives), name)) == NULL) { return FAILURE; @@ -431,7 +431,7 @@ ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig) } /* }}} */ -ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, zend_bool *exists) /* {{{ */ +ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists) /* {{{ */ { zend_ini_entry *ini_entry; @@ -457,7 +457,7 @@ ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig) /* {{{ */ { - zend_bool exists = 1; + bool exists = 1; char *return_value; return_value = zend_ini_string_ex(name, name_length, orig, &exists); @@ -483,7 +483,7 @@ ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */ } /* }}} */ -ZEND_API zend_bool zend_ini_parse_bool(zend_string *str) +ZEND_API bool zend_ini_parse_bool(zend_string *str) { if ((ZSTR_LEN(str) == 4 && strcasecmp(ZSTR_VAL(str), "true") == 0) || (ZSTR_LEN(str) == 3 && strcasecmp(ZSTR_VAL(str), "yes") == 0) @@ -573,7 +573,7 @@ ZEND_INI_DISP(display_link_numbers) /* {{{ */ /* Standard message handlers */ ZEND_API ZEND_INI_MH(OnUpdateBool) /* {{{ */ { - zend_bool *p = (zend_bool *) ZEND_INI_GET_ADDR(); + bool *p = (bool *) ZEND_INI_GET_ADDR(); *p = zend_ini_parse_bool(new_value); return SUCCESS; } diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index d227f3779d..590ff09cef 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -84,9 +84,9 @@ ZEND_API void display_ini_entries(zend_module_entry *module); ZEND_API zend_long zend_ini_long(const char *name, size_t name_length, int orig); ZEND_API double zend_ini_double(const char *name, size_t name_length, int orig); ZEND_API char *zend_ini_string(const char *name, size_t name_length, int orig); -ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, zend_bool *exists); +ZEND_API char *zend_ini_string_ex(const char *name, size_t name_length, int orig, bool *exists); ZEND_API zend_string *zend_ini_get_value(zend_string *name); -ZEND_API zend_bool zend_ini_parse_bool(zend_string *str); +ZEND_API bool zend_ini_parse_bool(zend_string *str); ZEND_API zend_result zend_ini_register_displayer(const char *name, uint32_t name_length, void (*displayer)(zend_ini_entry *ini_entry, int type)); @@ -141,12 +141,12 @@ END_EXTERN_C() #define INI_INT(name) zend_ini_long((name), strlen(name), 0) #define INI_FLT(name) zend_ini_double((name), strlen(name), 0) #define INI_STR(name) zend_ini_string_ex((name), strlen(name), 0, NULL) -#define INI_BOOL(name) ((zend_bool) INI_INT(name)) +#define INI_BOOL(name) ((bool) INI_INT(name)) #define INI_ORIG_INT(name) zend_ini_long((name), strlen(name), 1) #define INI_ORIG_FLT(name) zend_ini_double((name), strlen(name), 1) #define INI_ORIG_STR(name) zend_ini_string((name), strlen(name), 1) -#define INI_ORIG_BOOL(name) ((zend_bool) INI_ORIG_INT(name)) +#define INI_ORIG_BOOL(name) ((bool) INI_ORIG_INT(name)) #define REGISTER_INI_ENTRIES() zend_register_ini_entries(ini_entries, module_number) #define UNREGISTER_INI_ENTRIES() zend_unregister_ini_entries(module_number) @@ -180,8 +180,8 @@ END_EXTERN_C() /* INI parsing engine */ typedef void (*zend_ini_parser_cb_t)(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg); BEGIN_EXTERN_C() -ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg); -ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg); +ZEND_API int zend_parse_ini_file(zend_file_handle *fh, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg); +ZEND_API int zend_parse_ini_string(char *str, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg); END_EXTERN_C() /* INI entries */ diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index f9170d9ecc..6d9dd09e34 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -212,7 +212,7 @@ static ZEND_COLD void ini_error(const char *msg) /* }}} */ /* {{{ zend_parse_ini_file() */ -ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg) +ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg) { int retval; zend_ini_parser_param ini_parser_param; @@ -240,7 +240,7 @@ ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffe /* }}} */ /* {{{ zend_parse_ini_string() */ -ZEND_API zend_result zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg) +ZEND_API zend_result zend_parse_ini_string(char *str, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg) { int retval; zend_ini_parser_param ini_parser_param; diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 0d5af66d92..35e8ebe7e0 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -436,7 +436,7 @@ static int zend_implement_serializable(zend_class_entry *interface, zend_class_e typedef struct { zend_object std; zend_object_iterator *iter; - zend_bool rewind_called; + bool rewind_called; } zend_internal_iterator; static zend_object *zend_internal_iterator_create(zend_class_entry *ce) { diff --git a/Zend/zend_language_scanner.h b/Zend/zend_language_scanner.h index f08dbb4ea9..ca32329a55 100644 --- a/Zend/zend_language_scanner.h +++ b/Zend/zend_language_scanner.h @@ -63,7 +63,7 @@ typedef struct _zend_heredoc_label { char *label; int length; int indentation; - zend_bool indentation_uses_spaces; + bool indentation_uses_spaces; } zend_heredoc_label; /* Track locations of unclosed {, [, (, etc. for better syntax error reporting */ @@ -75,10 +75,10 @@ typedef struct _zend_nest_location { BEGIN_EXTERN_C() ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state); ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state); -ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename); +ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename); ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, const zend_encoding *old_encoding); ZEND_API zend_result zend_multibyte_set_filter(const zend_encoding *onetime_encoding); -ZEND_API zend_result zend_lex_tstring(zval *zv, zend_lexer_ident_ref ident_ref); +ZEND_API zend_result zend_lex_tstring(zval *zv, unsigned char *ident); END_EXTERN_C() diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7e60f5cf05..6f2889aac8 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -307,20 +307,25 @@ ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) } } -ZEND_API zend_result zend_lex_tstring(zval *zv, zend_lexer_ident_ref ident_ref) +ZEND_API zend_result zend_lex_tstring(zval *zv, unsigned char *ident) { - char *ident = (char *) SCNG(yy_start) + ident_ref.offset; - size_t length = ident_ref.len; - if (length == sizeof("<?=")-1 && memcmp(ident, "<?=", sizeof("<?=")-1) == 0) { + unsigned char *end = ident; + while ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || *end == '_') { + end++; + } + + size_t length = end - ident; + if (length == 0) { + ZEND_ASSERT(ident[0] == '<' && ident[1] == '?' && ident[2] == '='); zend_throw_exception(zend_ce_parse_error, "Cannot use \"<?=\" as an identifier", 0); return FAILURE; } if (SCNG(on_event)) { - SCNG(on_event)(ON_FEEDBACK, T_STRING, 0, ident, length, SCNG(on_event_context)); + SCNG(on_event)(ON_FEEDBACK, T_STRING, 0, (char *) ident, length, SCNG(on_event_context)); } - ZVAL_STRINGL(zv, ident, length); + ZVAL_STRINGL(zv, (char *) ident, length); return SUCCESS; } @@ -601,7 +606,7 @@ END_EXTERN_C() static zend_op_array *zend_compile(int type) { zend_op_array *op_array = NULL; - zend_bool original_in_compilation = CG(in_compilation); + bool original_in_compilation = CG(in_compilation); CG(in_compilation) = 1; CG(ast) = NULL; @@ -669,9 +674,9 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type) } ZEND_API zend_ast *zend_compile_string_to_ast( - zend_string *code, zend_arena **ast_arena, const char *filename) { + zend_string *code, zend_arena **ast_arena, zend_string *filename) { zval code_zv; - zend_bool original_in_compilation; + bool original_in_compilation; zend_lex_state original_lex_state; zend_ast *ast; @@ -737,11 +742,10 @@ zend_op_array *compile_filename(int type, zval *filename) return retval; } -ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename) +ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename) { char *buf; size_t size, old_len; - zend_string *new_compiled_filename; /* enforce ZEND_MMAP_AHEAD trailing NULLs for flex... */ old_len = Z_STRLEN_P(str); @@ -773,10 +777,7 @@ ZEND_API void zend_prepare_string_for_scanning(zval *str, const char *filename) } yy_scan_buffer(buf, size); - - new_compiled_filename = zend_string_init(filename, strlen(filename), 0); - zend_set_compiled_filename(new_compiled_filename); - zend_string_release_ex(new_compiled_filename, 0); + zend_set_compiled_filename(filename); CG(zend_lineno) = 1; CG(increment_lineno) = 0; RESET_DOC_COMMENT(); @@ -809,6 +810,7 @@ zend_op_array *compile_string(zend_string *source_string, const char *filename) zend_lex_state original_lex_state; zend_op_array *op_array = NULL; zval tmp; + zend_string *filename_str; if (ZSTR_LEN(source_string) == 0) { return NULL; @@ -817,7 +819,9 @@ zend_op_array *compile_string(zend_string *source_string, const char *filename) ZVAL_STR_COPY(&tmp, source_string); zend_save_lexical_state(&original_lex_state); - zend_prepare_string_for_scanning(&tmp, filename); + filename_str = zend_string_init(filename, strlen(filename), 0); + zend_prepare_string_for_scanning(&tmp, filename_str); + zend_string_release(filename_str); BEGIN(ST_IN_SCRIPTING); op_array = zend_compile(ZEND_EVAL_CODE); @@ -851,17 +855,15 @@ zend_result highlight_file(const char *filename, zend_syntax_highlighter_ini *sy return SUCCESS; } -void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *str_name) +void highlight_string(zend_string *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, const char *filename) { zend_lex_state original_lex_state; - zval tmp; - - if (UNEXPECTED(Z_TYPE_P(str) != IS_STRING)) { - ZVAL_STR(&tmp, zval_get_string_func(str)); - str = &tmp; - } + zval str_zv; + zend_string *filename_str = zend_string_init(filename, strlen(filename), 0); + ZVAL_STR_COPY(&str_zv, str); zend_save_lexical_state(&original_lex_state); - zend_prepare_string_for_scanning(str, str_name); + zend_prepare_string_for_scanning(&str_zv, filename_str); + zend_string_release(filename_str); BEGIN(INITIAL); zend_highlight(syntax_highlighter_ini); if (SCNG(script_filtered)) { @@ -869,9 +871,7 @@ void highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter SCNG(script_filtered) = NULL; } zend_restore_lexical_state(&original_lex_state); - if (UNEXPECTED(str == &tmp)) { - zval_ptr_dtor(&tmp); - } + zval_ptr_dtor(&str_zv); } ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, const zend_encoding *old_encoding) @@ -1023,7 +1023,7 @@ static zend_result zend_scan_escape_string(zval *zendlval, char *str, int len, c /* cache where we started so we can parse after validating */ char *start = s + 1; size_t len = 0; - zend_bool valid = 1; + bool valid = 1; unsigned long codepoint; if (*start != '{') { @@ -1163,9 +1163,9 @@ static const char *next_newline(const char *str, const char *end, size_t *newlin return NULL; } -static zend_bool strip_multiline_string_indentation( - zval *zendlval, int indentation, zend_bool using_spaces, - zend_bool newline_at_start, zend_bool newline_at_end) +static bool strip_multiline_string_indentation( + zval *zendlval, int indentation, bool using_spaces, + bool newline_at_start, bool newline_at_end) { const char *str = Z_STRVAL_P(zendlval), *end = str + Z_STRLEN_P(zendlval); char *copy = Z_STRVAL_P(zendlval); @@ -1372,6 +1372,7 @@ DNUM ({LNUM}?"."{LNUM})|({LNUM}"."{LNUM}?) EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM}) HNUM "0x"[0-9a-fA-F]+(_[0-9a-fA-F]+)* BNUM "0b"[01]+(_[01]+)* +ONUM "0o"[0-7]+(_[0-7]+)* LABEL [a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]* WHITESPACE [ \n\r\t]+ TABS_AND_SPACES [ \t]* @@ -1918,7 +1919,7 @@ NEWLINE ("\r"|"\n"|"\r\n") /* The +/- 2 skips "0b" */ size_t len = yyleng - 2; char *end, *bin = yytext + 2; - zend_bool contains_underscores; + bool contains_underscores; /* Skip any leading 0s */ while (len > 0 && (*bin == '0' || *bin == '_')) { @@ -1956,11 +1957,61 @@ NEWLINE ("\r"|"\n"|"\r\n") } } +<ST_IN_SCRIPTING>{ONUM} { + /* The +/- 2 skips "0o" */ + size_t len = yyleng - 2; + char *end, *octal = yytext + 2; + bool contains_underscores = (memchr(octal, '_', len) != NULL); + + /* Skip any leading 0s */ + while (len > 0 && (*octal == '0' || *octal == '_')) { + ++octal; + --len; + } + + if (len == 0) { + ZVAL_LONG(zendlval, 0); + RETURN_TOKEN_WITH_VAL(T_LNUMBER); + } + + if (contains_underscores) { + octal = estrndup(octal, len); + strip_underscores(octal, &len); + } + + errno = 0; + + ZVAL_LONG(zendlval, ZEND_STRTOL(octal, &end, 8)); + + ZEND_ASSERT(end == octal + len); + + if (!errno) { + if (contains_underscores) { + efree(octal); + } + RETURN_TOKEN_WITH_VAL(T_LNUMBER); + } + + /* Overflow */ + ZEND_ASSERT(errno == ERANGE); + /* Reset errno */ + errno = 0; + + /* zend_oct_strtod skips leading '0' */ + ZVAL_DOUBLE(zendlval, zend_oct_strtod(octal, (const char **)&end)); + ZEND_ASSERT(!errno); + ZEND_ASSERT(end == octal + len); + if (contains_underscores) { + efree(octal); + } + RETURN_TOKEN_WITH_VAL(T_DNUMBER); +} + <ST_IN_SCRIPTING>{LNUM} { size_t len = yyleng; char *end, *lnum = yytext; - zend_bool is_octal = lnum[0] == '0'; - zend_bool contains_underscores = (memchr(lnum, '_', len) != NULL); + bool is_octal = lnum[0] == '0'; + bool contains_underscores = (memchr(lnum, '_', len) != NULL); if (contains_underscores) { lnum = estrndup(lnum, len); @@ -2023,7 +2074,7 @@ NEWLINE ("\r"|"\n"|"\r\n") /* The +/- 2 skips "0x" */ size_t len = yyleng - 2; char *end, *hex = yytext + 2; - zend_bool contains_underscores; + bool contains_underscores; /* Skip any leading 0s */ while (len > 0 && (*hex == '0' || *hex == '_')) { @@ -2077,7 +2128,7 @@ string: RETURN_TOKEN_WITH_VAL(T_NUM_STRING); } -<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM} { /* Offset must be treated as a string */ +<ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM}|{ONUM} { /* Offset must be treated as a string */ if (yyleng == 1) { ZVAL_INTERNED_STR(zendlval, ZSTR_CHAR((zend_uchar)*(yytext))); } else { @@ -2090,7 +2141,7 @@ string: const char *end; size_t len = yyleng; char *dnum = yytext; - zend_bool contains_underscores = (memchr(dnum, '_', len) != NULL); + bool contains_underscores = (memchr(dnum, '_', len) != NULL); if (contains_underscores) { dnum = estrndup(dnum, len); @@ -2528,7 +2579,7 @@ skip_escape_conversion: unsigned char *saved_cursor; int bprefix = (yytext[0] != '<') ? 1 : 0, spacing = 0, indentation = 0; zend_heredoc_label *heredoc_label = emalloc(sizeof(zend_heredoc_label)); - zend_bool is_heredoc = 1; + bool is_heredoc = 1; CG(zend_lineno)++; heredoc_label->length = yyleng-bprefix-3-1-(yytext[yyleng-2]=='\r'?1:0); @@ -2909,7 +2960,7 @@ heredoc_scan_done: ZVAL_STRINGL(zendlval, yytext, yyleng - newline); if (!SCNG(heredoc_scan_ahead) && !EG(exception) && PARSER_MODE()) { - zend_bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r'; + bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r'; zend_string *copy = Z_STR_P(zendlval); if (!strip_multiline_string_indentation( @@ -3010,7 +3061,7 @@ nowdoc_scan_done: ZVAL_STRINGL(zendlval, yytext, yyleng - newline); if (!EG(exception) && spacing != -1 && PARSER_MODE()) { - zend_bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r'; + bool newline_at_start = *(yytext - 1) == '\n' || *(yytext - 1) == '\r'; if (!strip_multiline_string_indentation( zendlval, indentation, spacing == HEREDOC_USING_SPACES, newline_at_start, newline != 0)) { @@ -3050,8 +3101,7 @@ emit_token: emit_token_with_ident: if (PARSER_MODE()) { - elem->ident.offset = SCNG(yy_text) - SCNG(yy_start); - elem->ident.len = SCNG(yy_leng); + elem->ident = SCNG(yy_text); } if (SCNG(on_event)) { SCNG(on_event)(ON_TOKEN, token, start_line, yytext, yyleng, SCNG(on_event_context)); diff --git a/Zend/zend_modules.h b/Zend/zend_modules.h index 30d70bbf53..98957def76 100644 --- a/Zend/zend_modules.h +++ b/Zend/zend_modules.h @@ -31,7 +31,7 @@ #define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module #define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module -#define ZEND_MODULE_API_NO 20200930 +#define ZEND_MODULE_API_NO 20201009 #ifdef ZTS #define USING_ZTS 1 #else diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index f091d679f6..64f1450daa 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -209,7 +209,7 @@ static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zv /* }}} */ -static zend_always_inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */ +static zend_always_inline bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */ { child_class = child_class->parent; while (child_class) { @@ -443,7 +443,7 @@ found: } /* }}} */ -ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, zend_bool is_dynamic) /* {{{ */ +ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, bool is_dynamic) /* {{{ */ { zend_property_info *property_info; const char *class_name = NULL; @@ -691,7 +691,7 @@ exit: } /* }}} */ -static zend_always_inline zend_bool property_uses_strict_types() { +static zend_always_inline bool property_uses_strict_types() { zend_execute_data *execute_data = EG(current_execute_data); return execute_data && execute_data->func @@ -1440,7 +1440,7 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p return zend_std_get_static_property_with_info(ce, property_name, type, &prop_info); } -ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */ +ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */ { zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name)); return 0; @@ -1746,7 +1746,7 @@ ZEND_API int zend_std_cast_object_tostring(zend_object *readobj, zval *writeobj, } /* }}} */ -ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */ +ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ { zval *func; zend_class_entry *ce = obj->ce; diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index fedc70cc80..1eca06f266 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -36,10 +36,6 @@ struct _zend_property_info; #define ZEND_ENCODE_DYN_PROP_OFFSET(offset) ((uintptr_t)(-((intptr_t)(offset) + 2))) -/* The following rule applies to read_property() and read_dimension() implementations: - If you return a zval which is not otherwise referenced by the extension or the engine's - symbol table, its reference count should be 0. -*/ /* Used to fetch property from the object, read-only */ typedef zval *(*zend_object_read_property_t)(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv); @@ -47,13 +43,9 @@ typedef zval *(*zend_object_read_property_t)(zend_object *object, zend_string *m typedef zval *(*zend_object_read_dimension_t)(zend_object *object, zval *offset, int type, zval *rv); -/* The following rule applies to write_property() and write_dimension() implementations: - If you receive a value zval in write_property/write_dimension, you may only modify it if - its reference count is 1. Otherwise, you must create a copy of that zval before making - any changes. You should NOT modify the reference count of the value passed to you. +/* Used to set property of the object You must return the final value of the assigned property. */ -/* Used to set property of the object */ typedef zval *(*zend_object_write_property_t)(zend_object *object, zend_string *member, zval *value, void **cache_slot); /* Used to set dimension of the object */ @@ -118,9 +110,29 @@ typedef zend_array *(*zend_object_get_properties_for_t)(zend_object *object, zen typedef zend_function *(*zend_object_get_method_t)(zend_object **object, zend_string *method, const zval *key); typedef zend_function *(*zend_object_get_constructor_t)(zend_object *object); -/* Object maintenance/destruction */ -typedef void (*zend_object_dtor_obj_t)(zend_object *object); +/* free_obj should release any resources the object holds, without freeing the + * object structure itself. The object does not need to be in a valid state after + * free_obj finishes running. + * + * free_obj will always be invoked, even if the object leaks or a fatal error + * occurs. However, during shutdown it may be called once the executor is no + * longer active, in which case execution of user code may be skipped. + */ typedef void (*zend_object_free_obj_t)(zend_object *object); + +/* dtor_obj is called before free_obj. The object must remain in a valid state + * after dtor_obj finishes running. Unlike free_obj, it is run prior to + * deactivation of the executor during shutdown, which allows user code to run. + * + * This handler is not guaranteed to be called (e.g. on fatal error), and as + * such should not be used to release resources or deallocate memory. Furthermore, + * releasing resources in this handler can break detection of memory leaks, as + * cycles may be broken early. + * + * dtor_obj should be used *only* to call user destruction hooks, such as __destruct. + */ +typedef void (*zend_object_dtor_obj_t)(zend_object *object); + typedef zend_object* (*zend_object_clone_obj_t)(zend_object *object); /* Get class name for display in var_dump and other debugging functions. @@ -138,7 +150,7 @@ typedef int (*zend_object_cast_t)(zend_object *readobj, zval *retval, int type); * Returns FAILURE if the object does not have any sense of overloaded dimensions */ typedef int (*zend_object_count_elements_t)(zend_object *object, zend_long *count); -typedef int (*zend_object_get_closure_t)(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only); +typedef int (*zend_object_get_closure_t)(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); typedef HashTable *(*zend_object_get_gc_t)(zend_object *object, zval **table, int *n); @@ -191,7 +203,7 @@ ZEND_API void zend_class_init_statics(zend_class_entry *ce); ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name_strval, const zval *key); ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend_string *property_name, int type, struct _zend_property_info **prop_info); ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type); -ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name); +ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name); ZEND_API zend_function *zend_std_get_constructor(zend_object *object); ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent); ZEND_API HashTable *zend_std_get_properties(zend_object *object); @@ -210,12 +222,12 @@ ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset); ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key); ZEND_API zend_string *zend_std_get_class_name(const zend_object *zobj); ZEND_API int zend_std_compare_objects(zval *o1, zval *o2); -ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only); +ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); ZEND_API void rebuild_object_properties(zend_object *zobj); ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope); -ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, zend_bool is_dynamic); +ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, bool is_dynamic); ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static); diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 80359b5e1e..104cda6141 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -79,7 +79,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_stor } } -ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown) +ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown) { zend_object **obj_ptr, **end, *obj; diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 85ce701cd7..539850f227 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -54,7 +54,7 @@ BEGIN_EXTERN_C() ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size); ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects); ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects); -ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown); +ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown); ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objects); /* Store API functions */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 6681ef7b68..ae9bf2d74d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -103,7 +103,7 @@ ZEND_API void destroy_zend_function(zend_function *function) zend_function_dtor(&tmp); } -ZEND_API void zend_type_release(zend_type type, zend_bool persistent) { +ZEND_API void zend_type_release(zend_type type, bool persistent) { if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { @@ -280,9 +280,37 @@ ZEND_API void destroy_zend_class(zval *zv) } switch (ce->type) { case ZEND_USER_CLASS: - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { - zend_string_release_ex(ce->parent_name, 0); + if (!(ce->ce_flags & ZEND_ACC_CACHED)) { + if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) { + zend_string_release_ex(ce->parent_name, 0); + } + + zend_string_release_ex(ce->name, 0); + zend_string_release_ex(ce->info.user.filename, 0); + + if (ce->info.user.doc_comment) { + zend_string_release_ex(ce->info.user.doc_comment, 0); + } + + if (ce->attributes) { + zend_hash_release(ce->attributes); + } + + if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) { + uint32_t i; + + for (i = 0; i < ce->num_interfaces; i++) { + zend_string_release_ex(ce->interface_names[i].name, 0); + zend_string_release_ex(ce->interface_names[i].lc_name, 0); + } + efree(ce->interface_names); + } + + if (ce->num_traits > 0) { + _destroy_zend_class_traits_info(ce); + } } + if (ce->default_properties_table) { zval *p = ce->default_properties_table; zval *end = p + ce->default_properties_count; @@ -325,7 +353,6 @@ ZEND_API void destroy_zend_class(zval *zv) } } ZEND_HASH_FOREACH_END(); zend_hash_destroy(&ce->properties_info); - zend_string_release_ex(ce->name, 0); zend_hash_destroy(&ce->function_table); if (zend_hash_num_elements(&ce->constants_table)) { zend_class_constant *c; @@ -343,29 +370,9 @@ ZEND_API void destroy_zend_class(zval *zv) } ZEND_HASH_FOREACH_END(); } zend_hash_destroy(&ce->constants_table); - if (ce->num_interfaces > 0) { - if (!(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) { - uint32_t i; - - for (i = 0; i < ce->num_interfaces; i++) { - zend_string_release_ex(ce->interface_names[i].name, 0); - zend_string_release_ex(ce->interface_names[i].lc_name, 0); - } - } + if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) { efree(ce->interfaces); } - zend_string_release_ex(ce->info.user.filename, 0); - if (ce->info.user.doc_comment) { - zend_string_release_ex(ce->info.user.doc_comment, 0); - } - if (ce->attributes) { - zend_hash_release(ce->attributes); - } - - if (ce->num_traits > 0) { - _destroy_zend_class_traits_info(ce); - } - break; case ZEND_INTERNAL_CLASS: if (ce->default_properties_table) { @@ -754,14 +761,14 @@ static void emit_live_range( emit_live_range_raw(op_array, var_num, kind, start, end); } -static zend_bool is_fake_def(zend_op *opline) { +static bool is_fake_def(zend_op *opline) { /* These opcodes only modify the result, not create it. */ return opline->opcode == ZEND_ROPE_ADD || opline->opcode == ZEND_ADD_ARRAY_ELEMENT || opline->opcode == ZEND_ADD_ARRAY_UNPACK; } -static zend_bool keeps_op1_alive(zend_op *opline) { +static bool keeps_op1_alive(zend_op *opline) { /* These opcodes don't consume their OP1 operand, * it is later freed by something else. */ if (opline->opcode == ZEND_CASE diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index d1f2d553fc..29b2ed79b8 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -285,7 +285,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o } /* }}} */ -static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, zend_bool *failed) /* {{{ */ +static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, bool *failed) /* {{{ */ { *failed = 0; switch (Z_TYPE_P(op)) { @@ -377,7 +377,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze #define convert_op1_op2_long(op1, op1_lval, op2, op2_lval, result, opcode, sigil) \ do { \ if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { \ - zend_bool failed; \ + bool failed; \ if (Z_ISREF_P(op1)) { \ op1 = Z_REFVAL_P(op1); \ if (Z_TYPE_P(op1) == IS_LONG) { \ @@ -400,7 +400,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze } while (0); \ do { \ if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { \ - zend_bool failed; \ + bool failed; \ if (Z_ISREF_P(op2)) { \ op2 = Z_REFVAL_P(op2); \ if (Z_TYPE_P(op2) == IS_LONG) { \ @@ -424,14 +424,6 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(zval *op, ze ZEND_API void ZEND_FASTCALL convert_to_long(zval *op) /* {{{ */ { - if (Z_TYPE_P(op) != IS_LONG) { - convert_to_long_base(op, 10); - } -} -/* }}} */ - -ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base) /* {{{ */ -{ zend_long tmp; try_again: @@ -456,11 +448,7 @@ try_again: case IS_STRING: { zend_string *str = Z_STR_P(op); - if (base == 10) { - ZVAL_LONG(op, zval_get_long(op)); - } else { - ZVAL_LONG(op, ZEND_STRTOL(ZSTR_VAL(str), NULL, base)); - } + ZVAL_LONG(op, zval_get_long(op)); zend_string_release_ex(str, 0); } break; @@ -683,7 +671,7 @@ try_again: } /* }}} */ -ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op) /* {{{ */ +ZEND_API bool ZEND_FASTCALL _try_convert_to_string(zval *op) /* {{{ */ { zend_string *str; @@ -882,7 +870,7 @@ try_again: } /* }}} */ -static zend_always_inline zend_string* __zval_get_string_func(zval *op, zend_bool try) /* {{{ */ +static zend_always_inline zend_string* __zval_get_string_func(zval *op, bool try) /* {{{ */ { try_again: switch (Z_TYPE_P(op)) { @@ -1530,7 +1518,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, } if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_OR); op1_lval = zendi_try_get_long(op1, &failed); if (UNEXPECTED(failed)) { @@ -1544,7 +1532,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_OR); op2_lval = zendi_try_get_long(op2, &failed); if (UNEXPECTED(failed)) { @@ -1612,7 +1600,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, } if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_AND); op1_lval = zendi_try_get_long(op1, &failed); if (UNEXPECTED(failed)) { @@ -1626,7 +1614,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_AND); op2_lval = zendi_try_get_long(op2, &failed); if (UNEXPECTED(failed)) { @@ -1694,7 +1682,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, } if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR); op1_lval = zendi_try_get_long(op1, &failed); if (UNEXPECTED(failed)) { @@ -1708,7 +1696,7 @@ ZEND_API zend_result ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, op1_lval = Z_LVAL_P(op1); } if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { - zend_bool failed; + bool failed; ZEND_TRY_BINARY_OP2_OBJECT_OPERATION(ZEND_BW_XOR); op2_lval = zendi_try_get_long(op2, &failed); if (UNEXPECTED(failed)) { @@ -1910,7 +1898,7 @@ ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval } /* }}} */ -ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, zend_bool case_insensitive) /* {{{ */ +ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool case_insensitive) /* {{{ */ { zend_string *tmp_str1, *tmp_str2; zend_string *str1 = zval_get_tmp_string(op1, &tmp_str1); @@ -2194,7 +2182,7 @@ static int hash_zval_identical_function(zval *z1, zval *z2) /* {{{ */ } /* }}} */ -ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2) /* {{{ */ +ZEND_API bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2) /* {{{ */ { if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) { return 0; @@ -2265,7 +2253,7 @@ ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zv } /* }}} */ -ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce) /* {{{ */ +ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce) /* {{{ */ { uint32_t i; ZEND_ASSERT(!(class_ce->ce_flags & ZEND_ACC_INTERFACE)); @@ -2283,7 +2271,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_clas } /* }}} */ -ZEND_API zend_bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */ +ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */ { ZEND_ASSERT(instance_ce != ce && "Should have been checked already"); if (ce->ce_flags & ZEND_ACC_INTERFACE) { @@ -2954,15 +2942,6 @@ ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2) /* {{{ */ } /* }}} */ -ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC) /* {{{ */ -{ - zend_string *str; - - str = zend_strpprintf(0, "%.*G", (int) EG(precision), (double)Z_DVAL_P(op)); - ZVAL_NEW_STR(op, str); -} -/* }}} */ - ZEND_API zend_string* ZEND_FASTCALL zend_long_to_str(zend_long num) /* {{{ */ { if ((zend_ulong)num <= 9) { diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index d543b7b03c..b3ad598b74 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -54,7 +54,7 @@ ZEND_API zend_result ZEND_FASTCALL shift_left_function(zval *result, zval *op1, ZEND_API zend_result ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *op2); ZEND_API zend_result ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2); -ZEND_API zend_bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2); +ZEND_API bool ZEND_FASTCALL zend_is_identical(zval *op1, zval *op2); ZEND_API zend_result ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2); ZEND_API zend_result ZEND_FASTCALL is_identical_function(zval *result, zval *op1, zval *op2); @@ -63,10 +63,10 @@ ZEND_API zend_result ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1 ZEND_API zend_result ZEND_FASTCALL is_smaller_function(zval *result, zval *op1, zval *op2); ZEND_API zend_result ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, zval *op2); -ZEND_API zend_bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce); -ZEND_API zend_bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce); +ZEND_API bool ZEND_FASTCALL zend_class_implements_interface(const zend_class_entry *class_ce, const zend_class_entry *interface_ce); +ZEND_API bool ZEND_FASTCALL instanceof_function_slow(const zend_class_entry *instance_ce, const zend_class_entry *ce); -static zend_always_inline zend_bool instanceof_function( +static zend_always_inline bool instanceof_function( const zend_class_entry *instance_ce, const zend_class_entry *ce) { return instance_ce == ce || instanceof_function_slow(instance_ce, ce); } @@ -262,7 +262,6 @@ ZEND_API void ZEND_FASTCALL convert_scalar_to_number(zval *op); ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op); ZEND_API void ZEND_FASTCALL convert_to_long(zval *op); ZEND_API void ZEND_FASTCALL convert_to_double(zval *op); -ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base); ZEND_API void ZEND_FASTCALL convert_to_null(zval *op); ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op); ZEND_API void ZEND_FASTCALL convert_to_array(zval *op); @@ -322,8 +321,8 @@ static zend_always_inline zend_string *zval_try_get_tmp_string(zval *op, zend_st /* Like convert_to_string(), but returns whether the conversion succeeded and does not modify the * zval in-place if it fails. */ -ZEND_API zend_bool ZEND_FASTCALL _try_convert_to_string(zval *op); -static zend_always_inline zend_bool try_convert_to_string(zval *op) { +ZEND_API bool ZEND_FASTCALL _try_convert_to_string(zval *op); +static zend_always_inline bool try_convert_to_string(zval *op) { if (Z_TYPE_P(op) == IS_STRING) { return 1; } @@ -406,10 +405,10 @@ again: ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2); -ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2); +ZEND_API zend_result ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2); ZEND_API int ZEND_FASTCALL numeric_compare_function(zval *op1, zval *op2); -ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, zend_bool case_insensitive); +ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool case_insensitive); ZEND_API int ZEND_FASTCALL string_compare_function(zval *op1, zval *op2); ZEND_API int ZEND_FASTCALL string_case_compare_function(zval *op1, zval *op2); ZEND_API int ZEND_FASTCALL string_locale_compare_function(zval *op1, zval *op2); @@ -442,66 +441,14 @@ ZEND_API int ZEND_FASTCALL zend_compare_objects(zval *o1, zval *o2); ZEND_API int ZEND_FASTCALL zend_atoi(const char *str, size_t str_len); ZEND_API zend_long ZEND_FASTCALL zend_atol(const char *str, size_t str_len); -ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC); - -#define convert_to_ex_master(pzv, lower_type, upper_type) \ - if (Z_TYPE_P(pzv)!=upper_type) { \ - convert_to_##lower_type(pzv); \ - } - -#define convert_to_explicit_type(pzv, type) \ - do { \ - switch (type) { \ - case IS_NULL: \ - convert_to_null(pzv); \ - break; \ - case IS_LONG: \ - convert_to_long(pzv); \ - break; \ - case IS_DOUBLE: \ - convert_to_double(pzv); \ - break; \ - case _IS_BOOL: \ - convert_to_boolean(pzv); \ - break; \ - case IS_ARRAY: \ - convert_to_array(pzv); \ - break; \ - case IS_OBJECT: \ - convert_to_object(pzv); \ - break; \ - case IS_STRING: \ - convert_to_string(pzv); \ - break; \ - default: \ - assert(0); \ - break; \ - } \ - } while (0); - -#define convert_to_explicit_type_ex(pzv, str_type) \ - if (Z_TYPE_P(pzv) != str_type) { \ - convert_to_explicit_type(pzv, str_type); \ - } - -#define convert_to_boolean_ex(pzv) do { \ - if (Z_TYPE_INFO_P(pzv) > IS_TRUE) { \ - convert_to_boolean(pzv); \ - } else if (Z_TYPE_INFO_P(pzv) < IS_FALSE) { \ - ZVAL_FALSE(pzv); \ - } \ - } while (0) -#define convert_to_long_ex(pzv) convert_to_ex_master(pzv, long, IS_LONG) -#define convert_to_double_ex(pzv) convert_to_ex_master(pzv, double, IS_DOUBLE) -#define convert_to_string_ex(pzv) convert_to_ex_master(pzv, string, IS_STRING) -#define convert_to_array_ex(pzv) convert_to_ex_master(pzv, array, IS_ARRAY) -#define convert_to_object_ex(pzv) convert_to_ex_master(pzv, object, IS_OBJECT) -#define convert_to_null_ex(pzv) convert_to_ex_master(pzv, null, IS_NULL) - -#define convert_scalar_to_number_ex(pzv) \ - if (Z_TYPE_P(pzv)!=IS_LONG && Z_TYPE_P(pzv)!=IS_DOUBLE) { \ - convert_scalar_to_number(pzv); \ - } +#define convert_to_null_ex(zv) convert_to_null(zv) +#define convert_to_boolean_ex(zv) convert_to_boolean(zv) +#define convert_to_long_ex(zv) convert_to_long(zv) +#define convert_to_double_ex(zv) convert_to_double(zv) +#define convert_to_string_ex(zv) convert_to_string(zv) +#define convert_to_array_ex(zv) convert_to_array(zv) +#define convert_to_object_ex(zv) convert_to_object(zv) +#define convert_scalar_to_number_ex(zv) convert_scalar_to_number(zv) #if defined(ZEND_WIN32) && !defined(ZTS) && defined(_MSC_VER) /* This performance improvement of tolower() on Windows gives 10-18% on bench.php */ @@ -896,7 +843,7 @@ static zend_always_inline bool fast_equal_check_string(zval *op1, zval *op2) return zend_compare(op1, op2) == 0; } -static zend_always_inline zend_bool fast_is_identical_function(zval *op1, zval *op2) +static zend_always_inline bool fast_is_identical_function(zval *op1, zval *op2) { if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) { return 0; @@ -906,7 +853,7 @@ static zend_always_inline zend_bool fast_is_identical_function(zval *op1, zval * return zend_is_identical(op1, op2); } -static zend_always_inline zend_bool fast_is_not_identical_function(zval *op1, zval *op2) +static zend_always_inline bool fast_is_not_identical_function(zval *op1, zval *op2) { if (Z_TYPE_P(op1) != Z_TYPE_P(op2)) { return 1; diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index f4acc9930e..cda61117ea 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -335,7 +335,7 @@ char *alloca(); #if (defined(HAVE_ALLOCA) || (defined (__GNUC__) && __GNUC__ >= 2)) && !(defined(ZTS) && defined(HPUX)) && !defined(DARWIN) # define ZEND_ALLOCA_MAX_SIZE (32 * 1024) # define ALLOCA_FLAG(name) \ - zend_bool name; + bool name; # define SET_ALLOCA_FLAG(name) \ name = 1 # define do_alloca_ex(size, limit, use_heap) \ diff --git a/Zend/zend_ptr_stack.c b/Zend/zend_ptr_stack.c index 2030cc132b..80c77e11d7 100644 --- a/Zend/zend_ptr_stack.c +++ b/Zend/zend_ptr_stack.c @@ -21,7 +21,7 @@ #include "zend_ptr_stack.h" #include <stdarg.h> -ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, zend_bool persistent) +ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, bool persistent) { stack->top_element = stack->elements = NULL; stack->top = stack->max = 0; @@ -96,7 +96,7 @@ ZEND_API void zend_ptr_stack_reverse_apply(zend_ptr_stack *stack, void (*func)(v } -ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), zend_bool free_elements) +ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), bool free_elements) { zend_ptr_stack_apply(stack, func); if (free_elements) { diff --git a/Zend/zend_ptr_stack.h b/Zend/zend_ptr_stack.h index 2298e4f211..fd4b59d151 100644 --- a/Zend/zend_ptr_stack.h +++ b/Zend/zend_ptr_stack.h @@ -24,7 +24,7 @@ typedef struct _zend_ptr_stack { int top, max; void **elements; void **top_element; - zend_bool persistent; + bool persistent; } zend_ptr_stack; @@ -32,13 +32,13 @@ typedef struct _zend_ptr_stack { BEGIN_EXTERN_C() ZEND_API void zend_ptr_stack_init(zend_ptr_stack *stack); -ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, zend_bool persistent); +ZEND_API void zend_ptr_stack_init_ex(zend_ptr_stack *stack, bool persistent); ZEND_API void zend_ptr_stack_n_push(zend_ptr_stack *stack, int count, ...); ZEND_API void zend_ptr_stack_n_pop(zend_ptr_stack *stack, int count, ...); ZEND_API void zend_ptr_stack_destroy(zend_ptr_stack *stack); ZEND_API void zend_ptr_stack_apply(zend_ptr_stack *stack, void (*func)(void *)); ZEND_API void zend_ptr_stack_reverse_apply(zend_ptr_stack *stack, void (*func)(void *)); -ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), zend_bool free_elements); +ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), bool free_elements); ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack *stack); END_EXTERN_C() diff --git a/Zend/zend_signal.h b/Zend/zend_signal.h index 08c9de20c7..ed89851fa8 100644 --- a/Zend/zend_signal.h +++ b/Zend/zend_signal.h @@ -56,8 +56,8 @@ typedef struct _zend_signal_globals_t { int blocked; /* 1==TRUE, 0==FALSE */ int running; /* in signal handler execution */ int active; /* internal signal handling is enabled */ - zend_bool check; /* check for replaced handlers on shutdown */ - zend_bool reset; /* reset signal handlers on each request */ + bool check; /* check for replaced handlers on shutdown */ + bool reset; /* reset signal handlers on each request */ zend_signal_entry_t handlers[NSIG]; zend_signal_queue_t pstorage[ZEND_SIGNAL_QUEUE_SIZE], *phead, *ptail, *pavail; /* pending queue */ } zend_signal_globals_t; diff --git a/Zend/zend_smart_str.h b/Zend/zend_smart_str.h index 9211e54bee..756261e937 100644 --- a/Zend/zend_smart_str.h +++ b/Zend/zend_smart_str.h @@ -54,7 +54,7 @@ ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) END_EXTERN_C() -static zend_always_inline size_t smart_str_alloc(smart_str *str, size_t len, zend_bool persistent) { +static zend_always_inline size_t smart_str_alloc(smart_str *str, size_t len, bool persistent) { if (UNEXPECTED(!str->s)) { goto do_smart_str_realloc; } else { @@ -71,14 +71,14 @@ do_smart_str_realloc: return len; } -static zend_always_inline char* smart_str_extend_ex(smart_str *dest, size_t len, zend_bool persistent) { +static zend_always_inline char* smart_str_extend_ex(smart_str *dest, size_t len, bool persistent) { size_t new_len = smart_str_alloc(dest, len, persistent); char *ret = ZSTR_VAL(dest->s) + ZSTR_LEN(dest->s); ZSTR_LEN(dest->s) = new_len; return ret; } -static zend_always_inline void smart_str_free_ex(smart_str *str, zend_bool persistent) { +static zend_always_inline void smart_str_free_ex(smart_str *str, bool persistent) { if (str->s) { zend_string_release_ex(str->s, persistent); str->s = NULL; @@ -108,35 +108,35 @@ static zend_always_inline zend_string *smart_str_extract(smart_str *str) { } } -static zend_always_inline void smart_str_appendc_ex(smart_str *dest, char ch, zend_bool persistent) { +static zend_always_inline void smart_str_appendc_ex(smart_str *dest, char ch, bool persistent) { size_t new_len = smart_str_alloc(dest, 1, persistent); ZSTR_VAL(dest->s)[new_len - 1] = ch; ZSTR_LEN(dest->s) = new_len; } -static zend_always_inline void smart_str_appendl_ex(smart_str *dest, const char *str, size_t len, zend_bool persistent) { +static zend_always_inline void smart_str_appendl_ex(smart_str *dest, const char *str, size_t len, bool persistent) { size_t new_len = smart_str_alloc(dest, len, persistent); memcpy(ZSTR_VAL(dest->s) + ZSTR_LEN(dest->s), str, len); ZSTR_LEN(dest->s) = new_len; } -static zend_always_inline void smart_str_append_ex(smart_str *dest, const zend_string *src, zend_bool persistent) { +static zend_always_inline void smart_str_append_ex(smart_str *dest, const zend_string *src, bool persistent) { smart_str_appendl_ex(dest, ZSTR_VAL(src), ZSTR_LEN(src), persistent); } -static zend_always_inline void smart_str_append_smart_str_ex(smart_str *dest, const smart_str *src, zend_bool persistent) { +static zend_always_inline void smart_str_append_smart_str_ex(smart_str *dest, const smart_str *src, bool persistent) { if (src->s && ZSTR_LEN(src->s)) { smart_str_append_ex(dest, src->s, persistent); } } -static zend_always_inline void smart_str_append_long_ex(smart_str *dest, zend_long num, zend_bool persistent) { +static zend_always_inline void smart_str_append_long_ex(smart_str *dest, zend_long num, bool persistent) { char buf[32]; char *result = zend_print_long_to_buf(buf + sizeof(buf) - 1, num); smart_str_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent); } -static zend_always_inline void smart_str_append_unsigned_ex(smart_str *dest, zend_ulong num, zend_bool persistent) { +static zend_always_inline void smart_str_append_unsigned_ex(smart_str *dest, zend_ulong num, bool persistent) { char buf[32]; char *result = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, num); smart_str_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent); diff --git a/Zend/zend_smart_string.h b/Zend/zend_smart_string.h index 1f74a63218..d9f484b91e 100644 --- a/Zend/zend_smart_string.h +++ b/Zend/zend_smart_string.h @@ -51,7 +51,7 @@ ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len); ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len); -static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t len, zend_bool persistent) { +static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t len, bool persistent) { if (UNEXPECTED(!str->c) || UNEXPECTED(len >= str->a - str->len)) { if (persistent) { _smart_string_alloc_persistent(str, len); @@ -62,7 +62,7 @@ static zend_always_inline size_t smart_string_alloc(smart_string *str, size_t le return str->len + len; } -static zend_always_inline void smart_string_free_ex(smart_string *str, zend_bool persistent) { +static zend_always_inline void smart_string_free_ex(smart_string *str, bool persistent) { if (str->c) { pefree(str->c, persistent); str->c = NULL; @@ -76,25 +76,25 @@ static zend_always_inline void smart_string_0(smart_string *str) { } } -static zend_always_inline void smart_string_appendc_ex(smart_string *dest, char ch, zend_bool persistent) { +static zend_always_inline void smart_string_appendc_ex(smart_string *dest, char ch, bool persistent) { dest->len = smart_string_alloc(dest, 1, persistent); dest->c[dest->len - 1] = ch; } -static zend_always_inline void smart_string_appendl_ex(smart_string *dest, const char *str, size_t len, zend_bool persistent) { +static zend_always_inline void smart_string_appendl_ex(smart_string *dest, const char *str, size_t len, bool persistent) { size_t new_len = smart_string_alloc(dest, len, persistent); memcpy(dest->c + dest->len, str, len); dest->len = new_len; } -static zend_always_inline void smart_string_append_long_ex(smart_string *dest, zend_long num, zend_bool persistent) { +static zend_always_inline void smart_string_append_long_ex(smart_string *dest, zend_long num, bool persistent) { char buf[32]; char *result = zend_print_long_to_buf(buf + sizeof(buf) - 1, num); smart_string_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent); } -static zend_always_inline void smart_string_append_unsigned_ex(smart_string *dest, zend_ulong num, zend_bool persistent) { +static zend_always_inline void smart_string_append_unsigned_ex(smart_string *dest, zend_ulong num, bool persistent) { char buf[32]; char *result = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, num); smart_string_appendl_ex(dest, result, buf + sizeof(buf) - 1 - result, persistent); diff --git a/Zend/zend_stack.c b/Zend/zend_stack.c index 1255c941a0..f587452bb7 100644 --- a/Zend/zend_stack.c +++ b/Zend/zend_stack.c @@ -141,7 +141,7 @@ ZEND_API void zend_stack_apply_with_argument(zend_stack *stack, int type, int (* } } -ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), zend_bool free_elements) +ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), bool free_elements) { int i; diff --git a/Zend/zend_stack.h b/Zend/zend_stack.h index a5aa962226..c0a325b778 100644 --- a/Zend/zend_stack.h +++ b/Zend/zend_stack.h @@ -40,7 +40,7 @@ ZEND_API void *zend_stack_base(const zend_stack *stack); ZEND_API int zend_stack_count(const zend_stack *stack); ZEND_API void zend_stack_apply(zend_stack *stack, int type, int (*apply_function)(void *element)); ZEND_API void zend_stack_apply_with_argument(zend_stack *stack, int type, int (*apply_function)(void *element, void *arg), void *arg); -ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), zend_bool free_elements); +ZEND_API void zend_stack_clean(zend_stack *stack, void (*func)(void *), bool free_elements); END_EXTERN_C() #define ZEND_STACK_APPLY_TOPDOWN 1 diff --git a/Zend/zend_stream.h b/Zend/zend_stream.h index 023e2d0555..e97a796730 100644 --- a/Zend/zend_stream.h +++ b/Zend/zend_stream.h @@ -58,7 +58,7 @@ typedef struct _zend_file_handle { zend_stream_type type; /* free_filename is used by wincache */ /* TODO: Clean up filename vs opened_path mess */ - zend_bool free_filename; + bool free_filename; char *buf; size_t len; } zend_file_handle; diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 570aeece61..ab074515aa 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -313,7 +313,7 @@ ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_intern interned_string_init_request_handler = init_handler; } -ZEND_API void zend_interned_strings_switch_storage(zend_bool request) +ZEND_API void zend_interned_strings_switch_storage(bool request) { if (request) { zend_new_interned_string = interned_string_request_handler; @@ -325,7 +325,7 @@ ZEND_API void zend_interned_strings_switch_storage(zend_bool request) } #if defined(__GNUC__) && defined(__i386__) -ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) +ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { char *ptr = ZSTR_VAL(s1); size_t delta = (char*)s2 - (char*)s1; @@ -363,7 +363,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_str } #ifdef HAVE_VALGRIND -ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) +ZEND_API bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) { size_t len = ZSTR_LEN(s1); char *ptr1 = ZSTR_VAL(s1); @@ -393,7 +393,7 @@ ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_ #endif #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) -ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) +ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2) { char *ptr = ZSTR_VAL(s1); size_t delta = (char*)s2 - (char*)s1; @@ -431,7 +431,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_str } #ifdef HAVE_VALGRIND -ZEND_API zend_bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) +ZEND_API bool ZEND_FASTCALL I_WRAP_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(zend_string *s1, zend_string *s2) { size_t len = ZSTR_LEN(s1); char *ptr1 = ZSTR_VAL(s1); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 96f1a6f4a0..c7580d63a4 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -47,7 +47,7 @@ ZEND_API void zend_interned_strings_dtor(void); ZEND_API void zend_interned_strings_activate(void); ZEND_API void zend_interned_strings_deactivate(void); ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler); -ZEND_API void zend_interned_strings_switch_storage(zend_bool request); +ZEND_API void zend_interned_strings_switch_storage(bool request); ZEND_API extern zend_string *zend_empty_string; ZEND_API extern zend_string *zend_one_char_string[256]; @@ -332,21 +332,21 @@ static zend_always_inline void zend_string_release_ex(zend_string *s, bool persi #if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__))) BEGIN_EXTERN_C() -ZEND_API zend_bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2); +ZEND_API bool ZEND_FASTCALL zend_string_equal_val(zend_string *s1, zend_string *s2); END_EXTERN_C() #else -static zend_always_inline zend_bool zend_string_equal_val(zend_string *s1, zend_string *s2) +static zend_always_inline bool zend_string_equal_val(zend_string *s1, zend_string *s2) { return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); } #endif -static zend_always_inline zend_bool zend_string_equal_content(zend_string *s1, zend_string *s2) +static zend_always_inline bool zend_string_equal_content(zend_string *s1, zend_string *s2) { return ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2); } -static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_string *s2) +static zend_always_inline bool zend_string_equals(zend_string *s1, zend_string *s2) { return s1 == s2 || zend_string_equal_content(s1, s2); } diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c index e88fbd001f..9173a15031 100644 --- a/Zend/zend_strtod.c +++ b/Zend/zend_strtod.c @@ -4467,9 +4467,6 @@ ZEND_API double zend_oct_strtod(const char *str, const char **endptr) return 0.0; } - /* skip leading zero */ - s++; - while ((c = *s++)) { if (c < '0' || c > '7') { /* break and return the current value if the number is not well-formed diff --git a/Zend/zend_ts_hash.c b/Zend/zend_ts_hash.c index d4e972d0e3..9bbf42c66d 100644 --- a/Zend/zend_ts_hash.c +++ b/Zend/zend_ts_hash.c @@ -57,7 +57,7 @@ static void end_write(TsHashTable *ht) } /* delegates */ -ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent) +ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent) { #ifdef ZTS ht->mx_reader = tsrm_mutex_alloc(); @@ -271,7 +271,7 @@ ZEND_API void zend_ts_hash_sort(TsHashTable *ht, sort_func_t sort_func, bucket_c end_write(ht); } -ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, zend_bool ordered) +ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, bool ordered) { int retval; diff --git a/Zend/zend_ts_hash.h b/Zend/zend_ts_hash.h index d1b41b1d6f..12d3c65eb1 100644 --- a/Zend/zend_ts_hash.h +++ b/Zend/zend_ts_hash.h @@ -35,7 +35,7 @@ BEGIN_EXTERN_C() #define TS_HASH(table) (&(table->hash)) /* startup/shutdown */ -ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent); +ZEND_API void zend_ts_hash_init(TsHashTable *ht, uint32_t nSize, dtor_func_t pDestructor, bool persistent); ZEND_API void zend_ts_hash_destroy(TsHashTable *ht); ZEND_API void zend_ts_hash_clean(TsHashTable *ht); @@ -69,7 +69,7 @@ ZEND_API void zend_ts_hash_copy_to_hash(HashTable *target, TsHashTable *source, ZEND_API void zend_ts_hash_merge(TsHashTable *target, TsHashTable *source, copy_ctor_func_t pCopyConstructor, bool overwrite); ZEND_API void zend_ts_hash_merge_ex(TsHashTable *target, TsHashTable *source, copy_ctor_func_t pCopyConstructor, merge_checker_func_t pMergeSource, void *pParam); ZEND_API void zend_ts_hash_sort(TsHashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber); -ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, zend_bool ordered); +ZEND_API int zend_ts_hash_compare(TsHashTable *ht1, TsHashTable *ht2, compare_func_t compar, bool ordered); ZEND_API zval *zend_ts_hash_minmax(TsHashTable *ht, bucket_compare_func_t compar, int flag); ZEND_API int zend_ts_hash_num_elements(TsHashTable *ht); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 82cf20f1b5..da6792ba7b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -1130,7 +1130,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #endif #if ZEND_RC_DEBUG -extern ZEND_API zend_bool zend_rc_debug; +extern ZEND_API bool zend_rc_debug; # define ZEND_RC_MOD_CHECK(p) do { \ if (zend_rc_debug && zval_gc_type((p)->u.type_info) != IS_OBJECT) { \ ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \ @@ -1346,27 +1346,22 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) { } while (0) #define SEPARATE_ARRAY(zv) do { \ - zval *_zv = (zv); \ - zend_array *_arr = Z_ARR_P(_zv); \ + zval *__zv = (zv); \ + zend_array *_arr = Z_ARR_P(__zv); \ if (UNEXPECTED(GC_REFCOUNT(_arr) > 1)) { \ - if (Z_REFCOUNTED_P(_zv)) { \ + if (Z_REFCOUNTED_P(__zv)) { \ GC_DELREF(_arr); \ } \ - ZVAL_ARR(_zv, zend_array_dup(_arr)); \ - } \ - } while (0) - -#define SEPARATE_ZVAL_IF_NOT_REF(zv) do { \ - zval *__zv = (zv); \ - if (Z_TYPE_P(__zv) == IS_ARRAY) { \ - SEPARATE_ARRAY(__zv); \ + ZVAL_ARR(__zv, zend_array_dup(_arr)); \ } \ } while (0) #define SEPARATE_ZVAL_NOREF(zv) do { \ zval *_zv = (zv); \ ZEND_ASSERT(Z_TYPE_P(_zv) != IS_REFERENCE); \ - SEPARATE_ZVAL_IF_NOT_REF(_zv); \ + if (Z_TYPE_P(_zv) == IS_ARRAY) { \ + SEPARATE_ARRAY(_zv); \ + } \ } while (0) #define SEPARATE_ZVAL(zv) do { \ @@ -1384,13 +1379,8 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) { break; \ } \ } \ - SEPARATE_ZVAL_IF_NOT_REF(_zv); \ - } while (0) - -#define SEPARATE_ARG_IF_REF(varptr) do { \ - ZVAL_DEREF(varptr); \ - if (Z_REFCOUNTED_P(varptr)) { \ - Z_ADDREF_P(varptr); \ + if (Z_TYPE_P(_zv) == IS_ARRAY) { \ + SEPARATE_ARRAY(_zv); \ } \ } while (0) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 75062824f2..2663945a66 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -446,7 +446,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(16, ZEND_IS_IDENTICAL, CONST|TMP|VAR|CV, CONST|T { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); @@ -461,7 +461,7 @@ ZEND_VM_HANDLER(196, ZEND_CASE_STRICT, TMP|VAR, CONST|TMP|VAR|CV) { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); @@ -475,7 +475,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(17, ZEND_IS_NOT_IDENTICAL, CONST|TMP|VAR|CV, CON { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); @@ -1727,8 +1727,9 @@ ZEND_VM_C_LABEL(fetch_this): } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); } else { retval = &EG(uninitialized_zval); @@ -1746,8 +1747,9 @@ ZEND_VM_C_LABEL(fetch_this): } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { ZVAL_NULL(retval); } else { retval = &EG(uninitialized_zval); @@ -3892,7 +3894,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL)) EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; @@ -4010,7 +4012,7 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; @@ -4114,7 +4116,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; @@ -5002,7 +5004,7 @@ ZEND_VM_C_LABEL(send_again): HashTable *ht = Z_ARRVAL_P(args); zval *arg, *top; zend_string *name; - zend_bool have_named_params = 0; + bool have_named_params = 0; zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); @@ -5073,7 +5075,7 @@ ZEND_VM_C_LABEL(send_again): } else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(args); zend_object_iterator *iter; - zend_bool have_named_params = 0; + bool have_named_params = 0; if (!ce || !ce->get_iterator) { zend_type_error("Only arrays and Traversables can be unpacked"); @@ -5235,7 +5237,7 @@ ZEND_VM_C_LABEL(send_array): arg_num = 1; param = ZEND_CALL_ARG(EX(call), 1); ZEND_HASH_FOREACH_VAL(ht, arg) { - zend_bool must_wrap = 0; + bool must_wrap = 0; if (skip > 0) { skip--; continue; @@ -5271,7 +5273,7 @@ ZEND_VM_C_LABEL(send_array): FREE_OP2(); } else { zend_string *name; - zend_bool have_named_params; + bool have_named_params; zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht)); arg_num = 1; param = ZEND_CALL_ARG(EX(call), 1); @@ -5292,7 +5294,7 @@ ZEND_VM_C_LABEL(send_array): HANDLE_EXCEPTION(); } - zend_bool must_wrap = 0; + bool must_wrap = 0; if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { if (UNEXPECTED(!Z_ISREF_P(arg))) { if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) { @@ -6372,11 +6374,8 @@ ZEND_VM_C_LABEL(offset_again): } } ZEND_VM_C_LABEL(str_index_dim): - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); ZEND_VM_C_LABEL(num_index_dim): @@ -6397,6 +6396,7 @@ ZEND_VM_C_LABEL(num_index_dim): hval = 1; ZEND_VM_C_GOTO(num_index_dim); } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); ZEND_VM_C_GOTO(num_index_dim); } else if (OP2_TYPE == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -6528,7 +6528,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR) FREE_OP1_IF_VAR(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); FREE_OP1(); if (UNEXPECTED(EG(exception))) { @@ -6621,7 +6621,7 @@ ZEND_VM_COLD_CONST_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR) FREE_OP1_VAR_PTR(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); @@ -6675,16 +6675,9 @@ ZEND_VM_C_LABEL(fe_fetch_r_exit): pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -6827,16 +6820,9 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -7095,7 +7081,7 @@ ZEND_VM_C_LABEL(isset_again): ZEND_VM_C_GOTO(num_index_prop); } } - value = zend_hash_find_ex_ind(ht, str, OP2_TYPE == IS_CONST); + value = zend_hash_find_ex(ht, str, OP2_TYPE == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); ZEND_VM_C_LABEL(num_index_prop): @@ -7203,7 +7189,7 @@ ZEND_VM_HANDLER(194, ZEND_ARRAY_KEY_EXISTS, CV|TMPVAR|CONST, CV|TMPVAR|CONST) zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -7609,7 +7595,7 @@ ZEND_VM_HANDLER(138, ZEND_INSTANCEOF, TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR, C { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); @@ -8291,7 +8277,7 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY) FREE_OP1(); ZEND_VM_NEXT_OPCODE(); } else { - zend_bool strict; + bool strict; if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); @@ -8545,7 +8531,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif if (ret == NULL) { @@ -8703,6 +8689,16 @@ ZEND_VM_HOT_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED) } } +ZEND_VM_HANDLER(200, ZEND_FETCH_GLOBALS, UNUSED, UNUSED) +{ + USE_OPLINE + + /* For symbol tables we need to deal with exactly the same problems as for property tables. */ + ZVAL_ARR(EX_VAR(opline->result.var), + zend_proptable_to_symtable(&EG(symbol_table), /* always_duplicate */ 1)); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HANDLER(186, ZEND_ISSET_ISEMPTY_THIS, UNUSED, UNUSED) { USE_OPLINE @@ -8919,7 +8915,7 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED) while (1) { if (Z_TYPE_P(op1) == IS_ARRAY) { - count = zend_array_count(Z_ARRVAL_P(op1)); + count = zend_hash_num_elements(Z_ARRVAL_P(op1)); break; } else if (Z_TYPE_P(op1) == IS_OBJECT) { zend_object *zobj = Z_OBJ_P(op1); @@ -9302,7 +9298,7 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_IDENTICAL, op->op1_type == IS_CV && (op->op2_t /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */ USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); @@ -9315,7 +9311,7 @@ ZEND_VM_TYPE_SPEC_HANDLER(ZEND_IS_NOT_IDENTICAL, op->op1_type == IS_CV && (op->o { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); @@ -9639,16 +9635,9 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_FE_FETCH_R, op->op2_type == IS_CV && (op1_inf pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9d7eae0311..e4b1c03aee 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1225,7 +1225,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 0 ? EX_VAR(opline->result.var) : &retval; @@ -1286,7 +1286,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 1 ? EX_VAR(opline->result.var) : &retval; @@ -1451,7 +1451,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 0 ? EX_VAR(opline->result.var) : &retval; @@ -1545,7 +1545,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 1 ? EX_VAR(opline->result.var) : &retval; @@ -1640,7 +1640,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; @@ -1744,7 +1744,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 0 ? EX_VAR(opline->result.var) : &retval; @@ -1852,7 +1852,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = 1 ? EX_VAR(opline->result.var) : &retval; @@ -1960,7 +1960,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval; @@ -2115,7 +2115,7 @@ send_again: HashTable *ht = Z_ARRVAL_P(args); zval *arg, *top; zend_string *name; - zend_bool have_named_params = 0; + bool have_named_params = 0; zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); @@ -2186,7 +2186,7 @@ send_again: } else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(args); zend_object_iterator *iter; - zend_bool have_named_params = 0; + bool have_named_params = 0; if (!ce || !ce->get_iterator) { zend_type_error("Only arrays and Traversables can be unpacked"); @@ -2348,7 +2348,7 @@ send_array: arg_num = 1; param = ZEND_CALL_ARG(EX(call), 1); ZEND_HASH_FOREACH_VAL(ht, arg) { - zend_bool must_wrap = 0; + bool must_wrap = 0; if (skip > 0) { skip--; continue; @@ -2384,7 +2384,7 @@ send_array: FREE_OP(opline->op2_type, opline->op2.var); } else { zend_string *name; - zend_bool have_named_params; + bool have_named_params; zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht)); arg_num = 1; param = ZEND_CALL_ARG(EX(call), 1); @@ -2405,7 +2405,7 @@ send_array: HANDLE_EXCEPTION(); } - zend_bool must_wrap = 0; + bool must_wrap = 0; if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { if (UNEXPECTED(!Z_ISREF_P(arg))) { if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) { @@ -3167,7 +3167,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif if (ret == NULL) { @@ -3303,7 +3303,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ EG(current_execute_data) = call; #if ZEND_DEBUG - zend_bool should_throw = zend_internal_call_should_throw(fbc, call); + bool should_throw = zend_internal_call_should_throw(fbc, call); #endif if (ret == NULL) { @@ -4851,7 +4851,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER( ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); @@ -4943,7 +4943,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_CONST == IS_VAR) { @@ -5254,7 +5254,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST ZEND_VM_NEXT_OPCODE(); } else { - zend_bool strict; + bool strict; if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); @@ -5624,7 +5624,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = RT_CONSTANT(opline, opline->op1); @@ -5639,7 +5639,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = RT_CONSTANT(opline, opline->op1); @@ -7013,7 +7013,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -7121,7 +7121,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_CO zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -9169,7 +9169,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); + value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -9277,7 +9277,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_TM zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -9473,8 +9473,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); } else { retval = &EG(uninitialized_zval); @@ -9492,8 +9493,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { ZVAL_NULL(retval); } else { retval = &EG(uninitialized_zval); @@ -10323,7 +10325,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_ while (1) { if (Z_TYPE_P(op1) == IS_ARRAY) { - count = zend_array_count(Z_ARRVAL_P(op1)); + count = zend_hash_num_elements(Z_ARRVAL_P(op1)); break; } else if (Z_TYPE_P(op1) == IS_OBJECT) { zend_object *zobj = Z_OBJ_P(op1); @@ -11551,7 +11553,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -11659,7 +11661,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CONST_CV zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -14424,7 +14426,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE(); } else { - zend_bool strict; + bool strict; if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); @@ -15697,7 +15699,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -15805,7 +15807,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_C zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -15836,7 +15838,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_CONST_H { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -17089,7 +17091,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); + value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -17197,7 +17199,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_T zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -17229,7 +17231,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_VAR_HAN { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -17314,8 +17316,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); } else { retval = &EG(uninitialized_zval); @@ -17333,8 +17336,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { ZVAL_NULL(retval); } else { retval = &EG(uninitialized_zval); @@ -17510,7 +17514,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMPVAR_UNUSED_ { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -17562,7 +17566,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL while (1) { if (Z_TYPE_P(op1) == IS_ARRAY) { - count = zend_array_count(Z_ARRVAL_P(op1)); + count = zend_hash_num_elements(Z_ARRVAL_P(op1)); break; } else if (Z_TYPE_P(op1) == IS_OBJECT) { zend_object *zobj = Z_OBJ_P(op1); @@ -18399,7 +18403,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -18507,7 +18511,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_TMPVAR_C zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -18871,7 +18875,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); if (UNEXPECTED(EG(exception))) { @@ -18964,7 +18968,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_VAR) { @@ -19133,7 +19137,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HA { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -19148,7 +19152,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CONST_HAN { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -19162,7 +19166,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONS { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -19985,7 +19989,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HAND { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -20000,7 +20004,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_TMP_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -20014,7 +20018,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -20029,7 +20033,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_VAR_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -20462,7 +20466,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CV_HANDLE { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); @@ -21446,7 +21450,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); if (UNEXPECTED(EG(exception))) { @@ -21539,7 +21543,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -21593,16 +21597,9 @@ fe_fetch_r_exit: pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -21745,16 +21742,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -22044,7 +22034,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HA { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -22059,7 +22049,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CONST_HAN { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -22073,7 +22063,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONS { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -24370,11 +24360,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -24395,6 +24382,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -26523,11 +26511,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -26548,6 +26533,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -26759,7 +26745,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HAND { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -26774,7 +26760,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_TMP_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -26788,7 +26774,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -26843,7 +26829,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HAND { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -26858,7 +26844,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_VAR_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -26872,7 +26858,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -28570,7 +28556,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CV_HANDLE { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC); @@ -30537,11 +30523,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -30562,6 +30545,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -30790,16 +30774,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SIMPLE_ pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -30842,16 +30819,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SIMPLE_ pos++; value = &p->val; value_type = Z_TYPE_INFO_P(value); + ZEND_ASSERT(value_type != IS_INDIRECT); if (EXPECTED(value_type != IS_UNDEF)) { - if (UNEXPECTED(value_type == IS_INDIRECT)) { - value = Z_INDIRECT_P(value); - value_type = Z_TYPE_INFO_P(value); - if (EXPECTED(value_type != IS_UNDEF)) { - break; - } - } else { - break; - } + break; } p++; } @@ -35247,6 +35217,16 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_THIS_SPEC_UN } } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* For symbol tables we need to deal with exactly the same problems as for property tables. */ + ZVAL_ARR(EX_VAR(opline->result.var), + zend_proptable_to_symtable(&EG(symbol_table), /* always_duplicate */ 1)); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -38174,7 +38154,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 0 OPLINE_CC EXECUTE_DATA_CC); if (UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); @@ -38266,7 +38246,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } else { - zend_bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); + bool is_empty = zend_fe_reset_iterator(array_ptr, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { @@ -38533,7 +38513,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP ZEND_VM_NEXT_OPCODE(); } else { - zend_bool strict; + bool strict; if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); @@ -38938,7 +38918,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CONST_HAN { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -38953,7 +38933,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CONST { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -41808,11 +41788,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -41833,6 +41810,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -41940,7 +41918,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CONST == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CONST == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -42048,7 +42026,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CONST zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -42079,7 +42057,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_CONST_HANDL { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = EX_VAR(opline->op1.var); @@ -42367,7 +42345,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_C /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */ USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = EX_VAR(opline->op1.var); op2 = RT_CONSTANT(opline, opline->op2); @@ -42380,7 +42358,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = EX_VAR(opline->op1.var); op2 = RT_CONSTANT(opline, opline->op2); @@ -45254,11 +45232,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -45279,6 +45254,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -45388,7 +45364,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); + value = zend_hash_find_ex(ht, str, (IS_TMP_VAR|IS_VAR) == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -45496,7 +45472,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVA zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -45652,7 +45628,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -45667,7 +45643,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_H { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -45722,7 +45698,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_VAR_HANDL { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -45737,7 +45713,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_H { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -45827,7 +45803,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = EX_VAR(opline->op1.var); @@ -45996,8 +45972,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { retval = zend_hash_update(target_symbol_table, name, &EG(uninitialized_zval)); } else { retval = &EG(uninitialized_zval); @@ -46015,8 +45992,9 @@ fetch_this: } else if (type == BP_VAR_IS) { retval = &EG(uninitialized_zval); } else { - zend_error(E_WARNING, "Undefined variable $%s", ZSTR_VAL(name)); - if (type == BP_VAR_RW) { + zend_error(E_WARNING, "Undefined %svariable $%s", + (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); + if (type == BP_VAR_RW && !EG(exception)) { ZVAL_NULL(retval); } else { retval = &EG(uninitialized_zval); @@ -47119,7 +47097,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_UNUSED_HAND { USE_OPLINE zval *expr; - zend_bool result; + bool result; SAVE_OPLINE(); expr = EX_VAR(opline->op1.var); @@ -47394,7 +47372,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z while (1) { if (Z_TYPE_P(op1) == IS_ARRAY) { - count = zend_array_count(Z_ARRVAL_P(op1)); + count = zend_hash_num_elements(Z_ARRVAL_P(op1)); break; } else if (Z_TYPE_P(op1) == IS_OBJECT) { zend_object *zobj = Z_OBJ_P(op1); @@ -47619,7 +47597,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CV_HANDLE { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -47634,7 +47612,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CV_HA { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; SAVE_OPLINE(); op1 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); @@ -50377,11 +50355,8 @@ offset_again: } } str_index_dim: - if (ht == &EG(symbol_table)) { - zend_delete_global_variable(key); - } else { - zend_hash_del(ht, key); - } + ZEND_ASSERT(ht != &EG(symbol_table)); + zend_hash_del(ht, key); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_dim: @@ -50402,6 +50377,7 @@ num_index_dim: hval = 1; goto num_index_dim; } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + zend_use_resource_as_offset(offset); hval = Z_RES_HANDLE_P(offset); goto num_index_dim; } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { @@ -50509,7 +50485,7 @@ isset_again: goto num_index_prop; } } - value = zend_hash_find_ex_ind(ht, str, IS_CV == IS_CONST); + value = zend_hash_find_ex(ht, str, IS_CV == IS_CONST); } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { hval = Z_LVAL_P(offset); num_index_prop: @@ -50617,7 +50593,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_HA zval *key, *subject; HashTable *ht; - zend_bool result; + bool result; SAVE_OPLINE(); @@ -50774,7 +50750,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_C /* (Infinite recursion when comparing arrays is an uncatchable fatal error) */ USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = EX_VAR(opline->op1.var); op2 = EX_VAR(opline->op2.var); @@ -50787,7 +50763,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_ { USE_OPLINE zval *op1, *op2; - zend_bool result; + bool result; op1 = EX_VAR(opline->op1.var); op2 = EX_VAR(opline->op2.var); @@ -53405,6 +53381,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_JMP_NULL_SPEC_TMPVARCV_LABEL, (void*)&&ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_JMP_FORWARD_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -57604,6 +57581,10 @@ zend_leave_helper_SPEC_LABEL: VM_TRACE(ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED) ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED) + ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED) ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -61428,6 +61409,7 @@ void zend_vm_init(void) ZEND_NULL_HANDLER, ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER, ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_JMP_FORWARD_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -62534,7 +62516,8 @@ void zend_vm_init(void) 2536 | SPEC_RULE_OP1, 2541 | SPEC_RULE_OP1, 2546, - 3450 + 2547, + 3451 }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -62707,7 +62690,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2549 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2550 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -62715,7 +62698,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2574 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2575 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -62723,7 +62706,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2599 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2600 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -62734,17 +62717,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2624 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2625 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2649 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2650 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2674 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2675 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -62755,17 +62738,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2699 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2700 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2724 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2725 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2749 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2750 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -62776,14 +62759,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2774 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2849 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2850 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3074 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3075 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -62794,14 +62777,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2924 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2925 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2999 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3000 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3079 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3080 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -62812,12 +62795,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2774 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2849 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2850 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -62828,12 +62811,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2924 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2925 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2999 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3000 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -62841,12 +62824,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3084 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3085 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3159 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3160 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -62854,74 +62837,74 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3234 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3235 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3309 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3310 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3396 | SPEC_RULE_OP1; + spec = 3397 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3401 | SPEC_RULE_OP1; + spec = 3402 | SPEC_RULE_OP1; } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3406 | SPEC_RULE_OP1; + spec = 3407 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3384 | SPEC_RULE_RETVAL; + spec = 3385 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3386 | SPEC_RULE_RETVAL; + spec = 3387 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3388 | SPEC_RULE_RETVAL; + spec = 3389 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3390 | SPEC_RULE_RETVAL; + spec = 3391 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3392; - } else if (op1_info == MAY_BE_LONG) { spec = 3393; + } else if (op1_info == MAY_BE_LONG) { + spec = 3394; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3394; - } else if (op1_info == MAY_BE_LONG) { spec = 3395; + } else if (op1_info == MAY_BE_LONG) { + spec = 3396; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2548; + spec = 2549; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2547; + spec = 2548; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3446; + spec = 3447; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3441 | SPEC_RULE_OP1; + spec = 3442 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3448 | SPEC_RULE_RETVAL; + spec = 3449 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -62929,17 +62912,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3411 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3412 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3447; + spec = 3448; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3436 | SPEC_RULE_OP1; + spec = 3437 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index a2e85dde9b..ed57433e17 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1352,498 +1352,499 @@ _(2543, ZEND_JMP_NULL_SPEC_TMPVARCV) \ _(2545, ZEND_JMP_NULL_SPEC_TMPVARCV) \ _(2546, ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED) \ - _(2547, ZEND_RECV_NOTYPE_SPEC) \ - _(2548, ZEND_JMP_FORWARD_SPEC) \ - _(2554, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2555, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2547, ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED) \ + _(2548, ZEND_RECV_NOTYPE_SPEC) \ + _(2549, ZEND_JMP_FORWARD_SPEC) \ + _(2555, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2556, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2558, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2559, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2560, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2557, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2559, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2560, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2561, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2563, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2569, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2570, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2562, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2564, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2570, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2571, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2579, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2580, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2580, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2581, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2583, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2584, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2585, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2582, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2584, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2585, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2586, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2588, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2594, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2595, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2587, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2589, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2595, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2596, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2604, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2605, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2605, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2606, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2608, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2609, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2610, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2607, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2609, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2610, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2611, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2613, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2619, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2620, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2612, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2614, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2620, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2621, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2625, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2626, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2628, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2629, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2630, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2627, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2629, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2630, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2631, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2633, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2634, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2635, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2632, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2634, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2635, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2636, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2638, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2645, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2637, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2639, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2645, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2650, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2651, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2653, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2654, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2655, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2652, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2654, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2655, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2656, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2658, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2659, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2660, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2657, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2659, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2660, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2661, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2663, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2669, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2670, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2662, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2664, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2670, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2671, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2675, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2676, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2678, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2679, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2680, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2677, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2679, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2680, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2681, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2683, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2684, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2685, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2682, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2684, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2685, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2686, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2688, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2694, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2695, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2687, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2689, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2695, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2696, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2704, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2705, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2705, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2706, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2708, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2709, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2710, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2707, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2709, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2710, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2711, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2713, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2719, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2720, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2712, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2714, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2720, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2721, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2729, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2730, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2730, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2731, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2733, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2734, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2735, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2732, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2734, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2735, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2736, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2738, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2744, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2745, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2737, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2739, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2745, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2746, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2754, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2755, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2755, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2756, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2758, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2759, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2760, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2757, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2759, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2760, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2761, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2763, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2769, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2770, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2762, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2764, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2770, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2771, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2789, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2790, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2791, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2792, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2793, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2794, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2795, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2796, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2797, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2801, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2802, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2803, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2804, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2805, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2806, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2816, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2864, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2865, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2866, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2867, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2868, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2869, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2870, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2871, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2872, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2876, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2877, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2878, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2879, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2880, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2881, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2891, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2939, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2940, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2941, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2942, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2943, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2944, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2945, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2946, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2947, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2951, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2952, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2953, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2954, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2955, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2956, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2966, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3014, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3015, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3016, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3017, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3018, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3019, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3020, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3021, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3022, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3026, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3027, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3028, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3029, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3030, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3031, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3041, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3074, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3078, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3079, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3083, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3087, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3088, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3089, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3090, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3091, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3092, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3096, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3097, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3098, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3099, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3100, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3101, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3102, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3103, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3104, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3105, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3106, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3107, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3111, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3112, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3113, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3114, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3115, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3116, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3117, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3118, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3119, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3120, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3121, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3122, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3162, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3163, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3164, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3165, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3166, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3167, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3171, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3172, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3173, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3174, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3175, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3176, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3177, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3178, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3179, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3180, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3181, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3182, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3193, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3194, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3237, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3238, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3239, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3240, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3241, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3242, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3246, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3247, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3248, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3249, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3250, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3251, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3252, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3253, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3254, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3255, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3256, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3257, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3268, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3269, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3312, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3313, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3314, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3315, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3316, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3317, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3321, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3322, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3323, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3324, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3325, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3326, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3327, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3328, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3329, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3330, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3331, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3332, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3343, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3344, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3384, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3385, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3386, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3387, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3388, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3389, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3390, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3391, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3392, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3393, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3394, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3395, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3396, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3397, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2790, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2791, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2792, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2793, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2794, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2795, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2796, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2797, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2798, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2802, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2803, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2804, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2805, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2806, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2849, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2865, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2866, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2867, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2868, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2869, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2870, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2871, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2872, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2873, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2877, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2878, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2879, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2880, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2881, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2924, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2940, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2941, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2942, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2943, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2944, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2945, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2946, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2947, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2948, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2952, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2953, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2954, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2955, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2956, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2999, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3015, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3016, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3017, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3018, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3019, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3020, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3021, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3022, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3023, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3027, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3028, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3029, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3030, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3031, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3074, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3075, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3079, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3080, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3084, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3088, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3089, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3090, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3091, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3092, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3093, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3097, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3098, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3099, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3100, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3101, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3102, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3103, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3104, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3105, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3106, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3107, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3108, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3112, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3113, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3114, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3115, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3116, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3117, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3118, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3119, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3120, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3121, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3122, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3163, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3164, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3165, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3166, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3167, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3168, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3172, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3173, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3174, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3175, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3176, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3177, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3178, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3179, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3180, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3181, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3182, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3183, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3193, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3194, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3238, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3239, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3240, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3241, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3242, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3243, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3247, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3248, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3249, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3250, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3251, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3252, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3253, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3254, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3255, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3256, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3257, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3258, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3268, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3269, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3313, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3314, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3315, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3316, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3317, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3318, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3322, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3323, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3324, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3325, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3326, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3327, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3328, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3332, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3333, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3343, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3344, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3385, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3386, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3387, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3388, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3389, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3390, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3391, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3392, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3393, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3394, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3395, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3396, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3397, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ _(3398, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3400, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3401, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3402, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3399, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3401, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3402, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ _(3403, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3405, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3406, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3407, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3404, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3406, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3407, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ _(3408, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3410, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3412, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3409, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3411, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ _(3413, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3415, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3416, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3417, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3414, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3416, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3417, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(3418, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3420, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3421, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3422, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3419, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3421, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3422, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(3423, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3425, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3431, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3432, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3424, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3426, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3432, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ _(3433, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3435, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3438, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3440, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3443, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3445, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3446, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3447, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3448, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3449, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3449+1, ZEND_NULL) + _(3434, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3439, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3441, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3444, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3446, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3447, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3448, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3449, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3450, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3450+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 56a31aa19a..b5cb9fd15f 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -22,7 +22,7 @@ #include <zend.h> #include <zend_vm_opcodes.h> -static const char *zend_vm_opcodes_names[200] = { +static const char *zend_vm_opcodes_names[201] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -223,9 +223,10 @@ static const char *zend_vm_opcodes_names[200] = { "ZEND_MATCH_ERROR", "ZEND_JMP_NULL", "ZEND_CHECK_UNDEF_ARGS", + "ZEND_FETCH_GLOBALS", }; -static uint32_t zend_vm_opcodes_flags[200] = { +static uint32_t zend_vm_opcodes_flags[201] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -426,6 +427,7 @@ static uint32_t zend_vm_opcodes_flags[200] = { 0x0000010b, 0x0000200b, 0x00000101, + 0x00000101, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 653e320b85..e7e40c8a85 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -282,7 +282,8 @@ END_EXTERN_C() #define ZEND_MATCH_ERROR 197 #define ZEND_JMP_NULL 198 #define ZEND_CHECK_UNDEF_ARGS 199 +#define ZEND_FETCH_GLOBALS 200 -#define ZEND_VM_LAST_OPCODE 199 +#define ZEND_VM_LAST_OPCODE 200 #endif diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 04c5043210..757d081349 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -181,8 +181,8 @@ static zend_object* zend_weakref_new(zend_class_entry *ce) { return &wr->std; } -static zend_always_inline zend_bool zend_weakref_find(zval *referent, zval *return_value) { - void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), (zend_ulong) Z_OBJ_P(referent)); +static zend_always_inline bool zend_weakref_find(zend_object *referent, zval *return_value) { + void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), (zend_ulong) referent); if (!tagged_ptr) { return 0; } @@ -209,13 +209,13 @@ found_weakref: return 0; } -static zend_always_inline void zend_weakref_create(zval *referent, zval *return_value) { +static zend_always_inline void zend_weakref_create(zend_object *referent, zval *return_value) { zend_weakref *wr; object_init_ex(return_value, zend_ce_weakref); wr = zend_weakref_fetch(return_value); - wr->referent = Z_OBJ_P(referent); + wr->referent = referent; zend_weakref_register(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF)); } @@ -245,10 +245,10 @@ ZEND_COLD ZEND_METHOD(WeakReference, __construct) ZEND_METHOD(WeakReference, create) { - zval *referent; + zend_object *referent; ZEND_PARSE_PARAMETERS_START(1,1) - Z_PARAM_OBJECT(referent) + Z_PARAM_OBJ(referent) ZEND_PARSE_PARAMETERS_END(); if (zend_weakref_find(referent, return_value)) { @@ -402,12 +402,12 @@ static HashTable *zend_weakmap_get_properties_for(zend_object *object, zend_prop zend_ulong obj_addr; zval *val; ZEND_HASH_FOREACH_NUM_KEY_VAL(&wm->ht, obj_addr, val) { + zend_object *obj = (zend_object*)obj_addr; zval pair; - zval obj_zv; array_init(&pair); - ZVAL_OBJ_COPY(&obj_zv, (zend_object *) obj_addr); - add_assoc_zval(&pair, "key", &obj_zv); + GC_ADDREF(obj); + add_assoc_object(&pair, "key", obj); Z_TRY_ADDREF_P(val); add_assoc_zval(&pair, "value", val); |