diff options
Diffstat (limited to 'ext/opcache/Optimizer')
25 files changed, 1720 insertions, 2332 deletions
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 3327ec86df..b4cd2407ba 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -33,27 +33,9 @@ /* 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; - char *lookup_name; - int retval = 1; - ALLOCA_FLAG(use_heap); - - if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) { - lookup_name = do_alloca(ZSTR_LEN(name) + 1, use_heap); - memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1); - zend_str_tolower(lookup_name, ZSTR_LEN(name)); - - if ((c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, ZSTR_LEN(name))) != NULL) { - if (!(ZEND_CONSTANT_FLAGS(c) & CONST_CT_SUBST) || (ZEND_CONSTANT_FLAGS(c) & CONST_CS)) { - retval = 0; - } - } else { - retval = 0; - } - free_alloca(lookup_name, use_heap); - } - - if (retval) { + zval *zv; + 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_NO_FILE_CACHE) || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) { @@ -61,23 +43,23 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int if (copy) { Z_TRY_ADDREF_P(result); } + return 1; } else { - retval = 0; + return 0; } } - return retval; + /* Special constants null/true/false can always be substituted. */ + zv = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name)); + if (zv) { + ZVAL_COPY_VALUE(result, zv); + return 1; + } + return 0; } -/* CFG back references management */ - -#define DEL_SOURCE(from, to) -#define ADD_SOURCE(from, to) - /* Data dependencies macros */ -#define VAR_NUM_EX(op) VAR_NUM((op).var) - #define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)] #define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline @@ -86,15 +68,6 @@ static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b) zend_op *opcodes = op_array->opcodes; do { - /* check if NOP breaks incorrect smart branch */ - if (b->len == 2 - && (opcodes[b->start + 1].opcode == ZEND_JMPZ - || opcodes[b->start + 1].opcode == ZEND_JMPNZ) - && (opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST)) - && b->start > 0 - && zend_is_smart_branch(opcodes + b->start - 1)) { - break; - } b->start++; b->len--; } while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP); @@ -125,14 +98,6 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b) } j++; } - if (i + 1 < b->start + b->len - && (op_array->opcodes[i+1].opcode == ZEND_JMPZ - || op_array->opcodes[i+1].opcode == ZEND_JMPNZ) - && op_array->opcodes[i+1].op1_type & (IS_CV|IS_CONST) - && zend_is_smart_branch(op_array->opcodes + j - 1)) { - /* don't remove NOP, that splits incorrect smart branch */ - j++; - } i++; } b->len = j - b->start; @@ -203,52 +168,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array literal_dtor(&ZEND_OP1_LITERAL(src)); MAKE_NOP(src); ++(*opt_count); - switch (opline->opcode) { - case ZEND_JMPZ: - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - MAKE_NOP(opline); - DEL_SOURCE(block, block->successors[0]); - block->successors_count = 1; - block->successors[0] = block->successors[1]; - } else { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - DEL_SOURCE(block, block->successors[1]); - block->successors_count = 1; - } - break; - case ZEND_JMPNZ: - if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { - opline->opcode = ZEND_JMP; - COPY_NODE(opline->op1, opline->op2); - DEL_SOURCE(block, block->successors[1]); - block->successors_count = 1; - } else { - MAKE_NOP(opline); - DEL_SOURCE(block, block->successors[0]); - block->successors_count = 1; - block->successors[0] = block->successors[1]; - } - break; - case ZEND_JMPZNZ: - 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); - DEL_SOURCE(block, block->successors[0]); - 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); - DEL_SOURCE(block, block->successors[0]); - } - block->successors_count = 1; - opline->op1_type = IS_UNUSED; - opline->extended_value = 0; - opline->opcode = ZEND_JMP; - break; - default: - break; - } } else { zval_ptr_dtor_nogc(&c); } @@ -278,64 +197,55 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } } - if (opline->opcode == 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); - } - } - - if (opline->op1_type == IS_CONST) { - if (last_op && last_op->opcode == ZEND_ECHO && - last_op->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE && - Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) { - /* compress consecutive ECHO's. - * Float to string conversion may be affected by current - * locale setting. - */ - int l, 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)); + 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); } - 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); + } 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); } - 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; } - last_op = opline; - } else { - last_op = NULL; - } - } else { - last_op = NULL; - } - - switch (opline->opcode) { + break; case ZEND_FREE: if (opline->op1_type == IS_TMP_VAR) { @@ -592,6 +502,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array 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: @@ -600,6 +511,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array case ZEND_TYPE_CHECK: case ZEND_DEFINED: case ZEND_IN_ARRAY: + case ZEND_ARRAY_KEY_EXISTS: if (opline->opcode == ZEND_BOOL_NOT) { break; } @@ -615,56 +527,135 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array case ZEND_JMPZ: case ZEND_JMPNZ: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - case ZEND_JMPZNZ: - optimize_jmpznz: - if (opline->op1_type == IS_TMP_VAR && - (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) || - (opline->result_type == opline->op1_type && - opline->result.var == opline->op1.var))) { - src = VAR_SOURCE(opline->op1); - if (src) { - if (src->opcode == ZEND_BOOL_NOT && - opline->opcode != ZEND_JMPZ_EX && - opline->opcode != ZEND_JMPNZ_EX) { - VAR_SOURCE(opline->op1) = NULL; - COPY_NODE(opline->op1, src->op1); - if (opline->opcode == ZEND_JMPZ) { + 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 = ZEND_JMPNZ; - } else if (opline->opcode == ZEND_JMPNZ) { - /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */ - opline->opcode = ZEND_JMPZ; -#if 0 - } else if (opline->opcode == ZEND_JMPZ_EX) { - /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */ - opline->opcode = ZEND_JMPNZ_EX; - } else if (opline->opcode == ZEND_JMPNZ_EX) { - /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */ - opline->opcode = ZEND_JMPZ; -#endif - } else { + 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; - ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ); + 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; } - MAKE_NOP(src); - ++(*opt_count); - goto optimize_jmpznz; - } 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); + } + } + 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); - goto optimize_jmpznz; + 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; @@ -894,6 +885,23 @@ optimize_const_unary_op: /* 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; } @@ -1089,11 +1097,84 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op } } -static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t, uint32_t *opt_count) +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 *blocks = cfg->blocks; - zend_op *last_op; + zend_basic_block *target_block, *follow_block, *next_block; + zend_op *last_op, *target; + int next, jmp_hitlist_count; if (block->len == 0) { return; @@ -1102,35 +1183,32 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr last_op = op_array->opcodes + block->start + block->len - 1; switch (last_op->opcode) { case ZEND_JMP: - { - zend_basic_block *target_block = blocks + block->successors[0]; - zend_op *target = op_array->opcodes + target_block->start; - int next = (block - blocks) + 1; - - while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) { - /* find used one */ - next++; - } + jmp_hitlist_count = 0; - /* JMP(next) -> NOP */ - if (block->successors[0] == next) { - MAKE_NOP(last_op); - ++(*opt_count); - block->len--; + 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); + } - if (target->opcode == ZEND_JMP && - block->successors[0] != target_block->successors[0] && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMP L, L: JMP L1 -> JMP L1 */ - *last_op = *target; - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMPZNZ && - !(target_block->flags & ZEND_BB_PROTECTED)) { + 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) { @@ -1138,15 +1216,14 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op)); last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv); } - DEL_SOURCE(block, block->successors[0]); block->successors_count = 2; block->successors[0] = target_block->successors[0]; block->successors[1] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ADD_SOURCE(block, 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 */ @@ -1156,96 +1233,68 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op)); last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv); } - DEL_SOURCE(block, block->successors[0]); block->successors_count = 0; ++(*opt_count); -#if 0 - /* Temporarily disabled - see bug #0025274 */ - } else if (0&& block->op1_to != block && - block->op1_to != blocks && - op_array->last_try_catch == 0 && - target->opcode != ZEND_FREE) { - /* Block Reordering (saves one JMP on each "for" loop iteration) - * It is disabled for some cases (ZEND_FREE) - * which may break register allocation. - */ - zend_bool can_reorder = 0; - zend_block_source *cs = block->op1_to->sources; - - /* the "target" block doesn't had any followed block */ - while(cs) { - if (cs->from->follow_to == block->op1_to) { - can_reorder = 0; - break; - } - cs = cs->next; - } - if (can_reorder) { - next = block->op1_to; - /* the "target" block is not followed by current "block" */ - while (next->follow_to != NULL) { - if (next->follow_to == block) { - can_reorder = 0; - break; - } - next = next->follow_to; - } - if (can_reorder) { - zend_basic_block *prev = blocks; + } + } + break; - while (prev->next != block->op1_to) { - prev = prev->next; - } - prev->next = next->next; - next->next = block->next; - block->next = block->op1_to; + case ZEND_JMP_SET: + case ZEND_COALESCE: + jmp_hitlist_count = 0; - block->follow_to = block->op1_to; - block->op1_to = NULL; - MAKE_NOP(last_op); - block->len--; - if(block->len == 0) { - /* this block is nothing but NOP now */ - delete_code_block(block, ctx); - } - break; - } - } -#endif + 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: - /* constant conditional JMPs */ - if (last_op->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); + jmp_hitlist_count = 0; - if (last_op->opcode == ZEND_JMPZ) { - should_jmp = !should_jmp; - } - literal_dtor(&ZEND_OP1_LITERAL(last_op)); - last_op->op1_type = IS_UNUSED; - if (should_jmp) { - /* JMPNZ(true) -> JMP */ - last_op->opcode = ZEND_JMP; - DEL_SOURCE(block, block->successors[1]); - block->successors_count = 1; + 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 { - /* JMPNZ(false) -> NOP */ - MAKE_NOP(last_op); - DEL_SOURCE(block, block->successors[0]); - block->successors_count = 1; - block->successors[0] = block->successors[1]; + break; } + CHECK_LOOP(next); + block->successors[0] = next; ++(*opt_count); - break; + target_block = get_target_block(cfg, block, 0, opt_count); } - if (block->successors[0] == block->successors[1]) { + 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; @@ -1254,122 +1303,55 @@ static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_arr last_op->op2.num = 0; } else { MAKE_NOP(last_op); + block->len--; } block->successors_count = 1; ++(*opt_count); - break; - } + } 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_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 (1) { - zend_uchar same_type = last_op->op1_type; - uint32_t same_var = VAR_NUM_EX(last_op->op1); - zend_op *target; - zend_op *target_end; - zend_basic_block *target_block = blocks + block->successors[0]; + if (target_block == next_block) { + /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/ -next_target: - target = op_array->opcodes + target_block->start; - target_end = target + target_block->len; - while (target < target_end && target->opcode == ZEND_NOP) { - target++; - } + last_op->opcode = INV_COND(last_op->opcode); - /* next block is only NOP's */ - if (target == target_end) { - target_block = blocks + target_block->successors[0]; - ++(*opt_count); - goto next_target; - } else if (target->opcode == INV_COND(last_op->opcode) && - /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */ - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED) - ) { - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == INV_COND_EX(last_op->opcode) && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */ - last_op->opcode += 3; - COPY_NODE(last_op->result, target->result); - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == last_op->opcode && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMP && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMPZNZ && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */ - DEL_SOURCE(block, block->successors[0]); - if (last_op->opcode == ZEND_JMPZ) { - block->successors[0] = target_block->successors[0]; - } else { - block->successors[0] = target_block->successors[1]; - } - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } - } + block->successors[0] = follow_block->successors[0]; + block->successors[1] = next_block - cfg->blocks; - if (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ) { - zend_op *target; - zend_op *target_end; - zend_basic_block *target_block; + follow_block->flags &= ~ZEND_BB_REACHABLE; + MAKE_NOP(target); + follow_block->len = 0; - while (1) { - target_block = blocks + block->successors[1]; - target = op_array->opcodes + target_block->start; - target_end = op_array->opcodes + target_block->start + 1; - while (target < target_end && target->opcode == ZEND_NOP) { - target++; - } + next_block->flags |= ZEND_BB_FOLLOW; - /* next block is only NOP's */ - if (target == target_end && !(target_block->flags & ZEND_BB_PROTECTED)) { - DEL_SOURCE(block, block->successors[1]); - block->successors[1] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[1]); - ++(*opt_count); - } else { - break; + break; + } } - } - /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ - if (target->opcode == ZEND_JMP && - !(target_block->flags & ZEND_BB_PROTECTED)) { - DEL_SOURCE(block, block->successors[1]); + + /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ if (last_op->opcode == ZEND_JMPZ) { - block->successors[1] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[1]); + block->successors[1] = follow_block->successors[0]; } else { block->successors[1] = block->successors[0]; - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); + block->successors[0] = follow_block->successors[0]; } last_op->opcode = ZEND_JMPZNZ; ++(*opt_count); @@ -1379,212 +1361,153 @@ next_target: case ZEND_JMPNZ_EX: case ZEND_JMPZ_EX: - /* constant conditional JMPs */ - if (last_op->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); - - if (last_op->opcode == ZEND_JMPZ_EX) { - should_jmp = !should_jmp; - } - if (!should_jmp) { - /* T = JMPZ_EX(true,L) -> T = QM_ASSIGN(true) - * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false) - */ - last_op->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(last_op->op2); - DEL_SOURCE(block, block->successors[0]); - block->successors_count = 1; - block->successors[0] = block->successors[1]; - ++(*opt_count); - } - break; - } - - if (1) { - zend_op *target, *target_end; - zend_basic_block *target_block; - int var_num = op_array->last_var + op_array->T; + jmp_hitlist_count = 0; - if (var_num <= 0) { - return; - } - memset(same_t, 0, var_num); - same_t[VAR_NUM_EX(last_op->op1)] |= last_op->op1_type; - same_t[VAR_NUM_EX(last_op->result)] |= last_op->result_type; - target_block = blocks + block->successors[0]; -next_target_ex: + target_block = get_target_block(cfg, block, 0, opt_count); + while (target_block->len == 1) { target = op_array->opcodes + target_block->start; - target_end = target + target_block->len; - while (target < target_end && target->opcode == ZEND_NOP) { - target++; - } - /* next block is only NOP's */ - if (target == target_end) { - target_block = blocks + target_block->successors[0]; - ++(*opt_count); - goto next_target_ex; + + 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 && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 && - !(target_block->flags & ZEND_BB_PROTECTED)) { + (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) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == INV_EX_COND(last_op->opcode) && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 && - (same_t[VAR_NUM_EX(target->result)] & target->result_type) != 0 && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); + next = target_block->successors[0]; } else if (target->opcode == last_op->opcode && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 && - (same_t[VAR_NUM_EX(target->result)] & target->result_type) != 0 && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMP && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); + 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 && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - (same_t[VAR_NUM_EX(target->op1)] & target->op1_type) != 0 && - !(target_block->flags & ZEND_BB_PROTECTED)) { + (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) */ - DEL_SOURCE(block, block->successors[0]); - if (last_op->opcode == ZEND_JMPZ_EX) { - block->successors[0] = target_block->successors[0]; - } else { - block->successors[0] = target_block->successors[1]; - } - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); + 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 breakes 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: { - int next = (block - blocks) + 1; +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; - while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) { - /* find first accessed one */ - next++; + 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); } - if (last_op->op1_type == IS_CONST) { - if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) { - /* JMPZNZ(false,L1,L2) -> JMP(L1) */ - literal_dtor(&ZEND_OP1_LITERAL(last_op)); - last_op->opcode = ZEND_JMP; - SET_UNUSED(last_op->op1); - SET_UNUSED(last_op->op2); - DEL_SOURCE(block, block->successors[1]); - block->successors_count = 1; + 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), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */ + next = follow_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 = 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), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ + next = target_block->successors[1]; } else { - /* JMPZNZ(true,L1,L2) -> JMP(L2) */ - literal_dtor(&ZEND_OP1_LITERAL(last_op)); - last_op->opcode = ZEND_JMP; - SET_UNUSED(last_op->op1); - SET_UNUSED(last_op->op2); - DEL_SOURCE(block, block->successors[0]); - block->successors_count = 1; - block->successors[0] = block->successors[1]; + break; } + CHECK_LOOP(next); + block->successors[1] = next; ++(*opt_count); - } else if (block->successors[0] == block->successors[1]) { - /* both goto the same one - it's JMP */ - if (!(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); - block->successors_count = 1; - ++(*opt_count); - } - } else if (block->successors[0] == next) { + 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] = next; + block->successors[1] = tmp; ++(*opt_count); - /* no need to add source */ - } else if (block->successors[1] == next) { + } 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); - /* no need to add source */ - } - - if (last_op->opcode == ZEND_JMPZNZ) { - zend_uchar same_type = last_op->op1_type; - zend_uchar same_var = VAR_NUM_EX(last_op->op1); - zend_op *target; - zend_op *target_end; - zend_basic_block *target_block = blocks + block->successors[0]; - -next_target_znz: - target = op_array->opcodes + target_block->start; - target_end = target + target_block->len; - while (target < target_end && target->opcode == ZEND_NOP) { - target++; - } - /* next block is only NOP's */ - if (target == target_end) { - target_block = blocks + target_block->successors[0]; - ++(*opt_count); - goto next_target_znz; - } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMPNZ && - (target->op1_type & (IS_TMP_VAR|IS_CV)) && - same_type == target->op1_type && - same_var == VAR_NUM_EX(target->op1) && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[1]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } else if (target->opcode == ZEND_JMP && - !(target_block->flags & ZEND_BB_PROTECTED)) { - /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */ - DEL_SOURCE(block, block->successors[0]); - block->successors[0] = target_block->successors[0]; - ADD_SOURCE(block, block->successors[0]); - ++(*opt_count); - } } break; } @@ -1747,6 +1670,8 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use 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; @@ -1884,8 +1809,8 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) zend_bitset usage; void *checkpoint; zend_op **Tsource; - zend_uchar *same_t; uint32_t opt_count; + int *jmp_hitlist; /* Build CFG */ checkpoint = zend_arena_checkpoint(ctx->arena); @@ -1905,8 +1830,8 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) 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 *)); - same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T); 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; @@ -1938,10 +1863,12 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } + 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, same_t, &opt_count); + zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count); } } @@ -1956,8 +1883,6 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } - zend_bitset_clear(usage, bitset_len); - zend_t_usage(&cfg, op_array, usage, ctx); assemble_code_blocks(&cfg, op_array, ctx); if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) { diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index f754dbaa44..0a99ac4140 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -168,13 +168,13 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2); break; case ZEND_DEFINED: - LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 2); + LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1); break; case ZEND_FETCH_CONSTANT: - if ((opline->op1.num & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) { - LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 5); - } else { + 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: @@ -503,15 +503,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } switch (opline->opcode) { case ZEND_RECV_INIT: - if (class_name_type_hint(op_array, opline->op1.num)) { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - } - break; case ZEND_RECV: case ZEND_RECV_VARIADIC: if (class_name_type_hint(op_array, opline->op1.num)) { - opline->op2.num = cache_size; + opline->extended_value = cache_size; cache_size += sizeof(void *); } break; diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c index 8370bdc779..2992bb60b3 100644 --- a/ext/opcache/Optimizer/dce.c +++ b/ext/opcache/Optimizer/dce.c @@ -241,6 +241,8 @@ static inline zend_bool may_have_side_effects( } } return 0; + case ZEND_CHECK_VAR: + return (OP1_INFO() & MAY_BE_UNDEF) != 0; default: /* For everything we didn't handle, assume a side-effect */ return 1; diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 5401c9df6a..d753c54a00 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -125,33 +125,6 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, return SUCCESS; } -static zend_bool is_smart_branch_inhibiting_nop( - zend_op_array *op_array, uint32_t target, uint32_t current, - zend_basic_block *b, zend_basic_block *blocks_end) -{ - uint32_t next; - /* Target points one past the last non-nop instruction. Make sure there is one. */ - if (target == 0) { - return 0; - } - - /* Find the next instruction, skipping unreachable or empty blocks. */ - next = current + 1; - if (next >= b->start + b->len) { - do { - b++; - if (b == blocks_end) { - return 0; - } - } while (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0); - next = b->start; - } - - return (op_array->opcodes[next].opcode == ZEND_JMPZ || - op_array->opcodes[next].opcode == ZEND_JMPNZ) && - zend_is_smart_branch(op_array->opcodes + target - 1); -} - 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; @@ -199,8 +172,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op old_end = b->start + b->len; while (i < old_end) { shiftlist[i] = i - target; - if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) || - is_smart_branch_inhibiting_nop(op_array, target, i, b, blocks_end)) { + 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]; @@ -312,6 +284,8 @@ static inline zend_bool can_elide_return_type_check( 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; } @@ -322,7 +296,8 @@ static inline zend_bool can_elide_return_type_check( } /* These types are not represented exactly */ - if (ZEND_TYPE_CODE(info->type) == IS_CALLABLE || ZEND_TYPE_CODE(info->type) == IS_ITERABLE) { + if (ZEND_TYPE_IS_MASK(info->type) + && (ZEND_TYPE_MASK(info->type) & (MAY_BE_CALLABLE|MAY_BE_ITERABLE))) { return 0; } @@ -481,6 +456,24 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa) 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 (ssa->vars[var].phi_use_chain == NULL) { + if (ssa->ops[use].op1_use == var + && ssa->ops[use].op1_use_chain == -1) { + call_info->caller_call_opline->result_type = IS_TMP_VAR; + op_array->opcodes[use].op1_type = IS_TMP_VAR; + } else if (ssa->ops[use].op2_use == var + && ssa->ops[use].op2_use_chain == -1) { + call_info->caller_call_opline->result_type = IS_TMP_VAR; + op_array->opcodes[use].op2_type = IS_TMP_VAR; + } + } + } } } } @@ -530,8 +523,7 @@ 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 == 1 || !zend_is_smart_branch(opline - 1))) { + if (opline->opcode == ZEND_NOP) { block->len--; } else { break; @@ -1165,8 +1157,64 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx /* 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)) diff --git a/ext/opcache/Optimizer/escape_analysis.c b/ext/opcache/Optimizer/escape_analysis.c index c561bec9dc..a13f69cdf0 100644 --- a/ext/opcache/Optimizer/escape_analysis.c +++ b/ext/opcache/Optimizer/escape_analysis.c @@ -176,7 +176,7 @@ static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, in 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_EX(op_array, opline, opline->op1, ssa->rt_constants)+1)); + zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1)); uint32_t forbidden_flags = ZEND_ACC_INHERITED /* These flags will always cause an exception */ | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS @@ -191,7 +191,7 @@ static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, in break; case ZEND_QM_ASSIGN: if (opline->op1_type == IS_CONST - && Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)) == IS_ARRAY) { + && Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) { return 1; } if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) { @@ -208,7 +208,7 @@ static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, in switch (opline->opcode) { case ZEND_ASSIGN: if (opline->op2_type == IS_CONST - && Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)) == IS_ARRAY) { + && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) { return 1; } if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) { @@ -245,7 +245,7 @@ static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var 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_EX(op_array, opline, opline->op1, ssa->rt_constants)+1)); + 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->ce_flags & ZEND_ACC_INHERITED)) { diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c index ae707a2409..ea2b904a0f 100644 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -126,7 +126,7 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o i = fcall->extended_value; do { - if (Z_TYPE_P(RT_CONSTANT(&func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) { + 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++; @@ -136,7 +136,7 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o if (RETURN_VALUE_USED(opline)) { zval zv; - ZVAL_COPY(&zv, RT_CONSTANT(ret_opline, ret_opline->op1)); + 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); @@ -173,7 +173,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_INIT_FCALL: case ZEND_NEW: call_stack[call].func = zend_optimizer_get_called_func( - ctx->script, op_array, opline, 0); + ctx->script, op_array, opline); call_stack[call].try_inline = opline->opcode != ZEND_NEW; /* break missing intentionally */ case ZEND_INIT_DYNAMIC_CALL: diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1.c index 8ae1d34fc0..2e9e547175 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1.c @@ -19,10 +19,11 @@ +----------------------------------------------------------------------+ */ -/* pass 1 - * - substitute persistent constants (true, false, null, etc) - * - perform compile-time evaluation of constant binary and unary operations - * - convert CAST(IS_BOOL,x) into BOOL(x) +/* 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 */ @@ -37,7 +38,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) { - int i = 0; zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; zend_bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)? @@ -49,21 +49,80 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: - case ZEND_MOD: case ZEND_POW: + if (opline->op1_type == IS_CONST) { + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { + /* don't optimise if it should produce a runtime numeric string error */ + if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) { + convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); + } + } + } + if (opline->op2_type == IS_CONST) { + 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)); + } + } + if (opline->op1_type == IS_CONST) { + goto constant_binary_op; + } + } + break; + + case ZEND_MOD: case ZEND_SL: case ZEND_SR: + if (opline->op1_type == IS_CONST) { + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { + /* don't optimise if it should produce a runtime numeric string error */ + if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING + && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) { + convert_to_long(&ZEND_OP1_LITERAL(opline)); + } + } + } + if (opline->op2_type == IS_CONST) { + 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)); + } + } + if (opline->op1_type == IS_CONST) { + goto constant_binary_op; + } + } + break; + 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_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_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: case ZEND_BOOL_XOR: case ZEND_SPACESHIP: case ZEND_CASE: @@ -72,6 +131,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* 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)); @@ -86,6 +146,37 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } 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 */ @@ -103,12 +194,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } } - - if (opline->extended_value == _IS_BOOL) { - /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ - opline->opcode = ZEND_BOOL; - opline->extended_value = 0; - } break; case ZEND_BW_NOT: @@ -559,6 +644,71 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; + 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: @@ -568,11 +718,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_FAST_CALL: case ZEND_FAST_RET: case ZEND_JMP: - case ZEND_JMPZNZ: - 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_FE_FETCH_R: @@ -584,6 +729,5 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; } opline++; - i++; } } diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c deleted file mode 100644 index 01e118e7e3..0000000000 --- a/ext/opcache/Optimizer/pass2.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Zend OPcache | - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@php.net> | - | Zeev Suraski <zeev@php.net> | - | Stanislav Malyshev <stas@zend.com> | - | Dmitry Stogov <dmitry@php.net> | - +----------------------------------------------------------------------+ -*/ - -/* pass 2: - * - convert non-numeric constants to numeric constants in numeric operators - * - optimize constant conditional 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" - -void zend_optimizer_pass2(zend_op_array *op_array) -{ - zend_op *opline; - zend_op *end = op_array->opcodes + op_array->last; - - opline = op_array->opcodes; - while (opline < end) { - switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_POW: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) { - /* don't optimise if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP1_LITERAL(opline)); - } - } - } - if (opline->op2_type == IS_CONST) { - 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)); - } - } - } - break; - - case ZEND_MOD: - case ZEND_SL: - case ZEND_SR: - if (opline->op1_type == IS_CONST) { - if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_LONG) { - /* don't optimise if it should produce a runtime numeric string error */ - if (!(Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING - && !is_numeric_string(Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)), NULL, NULL, 0))) { - convert_to_long(&ZEND_OP1_LITERAL(opline)); - } - } - } - if (opline->op2_type == IS_CONST) { - 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)); - } - } - } - break; - - 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)); - } - } - 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_JMPZ_EX: - case ZEND_JMPNZ_EX: - /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ -#if 0 - /* Disabled unsafe pattern: in conjunction with - * ZEND_VM_SMART_BRANCH() this may improperly eliminate - * assignment to Ti. - */ - if (opline->op1_type == IS_TMP_VAR && - opline->result_type == IS_TMP_VAR && - opline->op1.var == opline->result.var) { - opline->opcode -= 3; - SET_UNUSED(opline->result); - } else -#endif - /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) - in case we know it wouldn't jump */ - if (opline->op1_type == IS_CONST) { - int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); - if (opline->opcode == ZEND_JMPZ_EX) { - should_jmp = !should_jmp; - } - if (!should_jmp) { - opline->opcode = ZEND_QM_ASSIGN; - SET_UNUSED(opline->op2); - } - } - break; - - case ZEND_JMPZ: - case ZEND_JMPNZ: - if (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); - } else { - MAKE_NOP(opline); - } - break; - } - if ((opline + 1)->opcode == ZEND_JMP) { - /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ - /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ - if (ZEND_OP2_JMP_ADDR(opline) == ZEND_OP1_JMP_ADDR(opline + 1)) { - /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ - 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); - } - } else { - if (opline->opcode == ZEND_JMPZ) { - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(opline + 1)); - } else { - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP2_JMP_ADDR(opline)); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(opline + 1)); - } - opline->opcode = ZEND_JMPZNZ; - } - } - 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; - } - break; - } - opline++; - } -} diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index 5bbb2b0854..a4abe2f8d3 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -19,10 +19,8 @@ +----------------------------------------------------------------------+ */ -/* pass 3: - * - optimize $i = $i+expr to $i+=expr +/* pass 3: (Jump optimization) * - optimize series of JMPs - * - change $i++ to ++$i where possible */ #include "php.h" @@ -34,388 +32,325 @@ #include "zend_vm.h" /* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ -#define CHECK_JMP(target, label) \ - for (i=0; i<jmp_hitlist_count; i++) { \ - if (jmp_hitlist[i] == ZEND_OP1_JMP_ADDR(target)) { \ - goto label; \ - } \ - } \ - jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1_JMP_ADDR(target); +static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count) +{ + int i; -#define CHECK_JMP2(target, label) \ - for (i=0; i<jmp_hitlist_count; i++) { \ - if (jmp_hitlist[i] == ZEND_OP2_JMP_ADDR(target)) { \ - goto label; \ - } \ - } \ - jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2_JMP_ADDR(target); + 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 = op_array->opcodes + op_array->last; + zend_op *end; + zend_op *target; zend_op **jmp_hitlist; int jmp_hitlist_count; - int i; - uint32_t opline_num = 0; ALLOCA_FLAG(use_heap); jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); opline = op_array->opcodes; + end = opline + op_array->last; while (opline < end) { - jmp_hitlist_count = 0; switch (opline->opcode) { - case ZEND_ADD: - case ZEND_SUB: - case ZEND_MUL: - case ZEND_DIV: - case ZEND_MOD: - case ZEND_POW: - case ZEND_CONCAT: - case ZEND_SL: - case ZEND_SR: - case ZEND_BW_OR: - case ZEND_BW_AND: - case ZEND_BW_XOR: - { - zend_op *next_opline = opline + 1; - - while (next_opline < end && next_opline->opcode == ZEND_NOP) { - ++next_opline; - } + case ZEND_JMP: + jmp_hitlist_count = 0; - if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { + 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; } - - /* change $i=expr+$i to $i=$i+expr so that the following optimization - * works on it. Only do this if we are ignoring operator overloading, - * as operand order might be significant otherwise. */ - if ((ctx->optimization_level & ZEND_OPTIMIZER_IGNORE_OVERLOADING) - && (opline->op2_type & (IS_VAR | IS_CV)) - && opline->op2.var == next_opline->op1.var && - (opline->opcode == ZEND_ADD || - opline->opcode == ZEND_MUL || - opline->opcode == ZEND_BW_OR || - opline->opcode == ZEND_BW_AND || - opline->opcode == ZEND_BW_XOR)) { - zend_uchar tmp_type = opline->op1_type; - znode_op tmp = opline->op1; - - if (opline->opcode != ZEND_ADD - || (opline->op1_type == IS_CONST - && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_ARRAY)) { - /* protection from array add: $a = array + $a is not commutative! */ - COPY_NODE(opline->op1, opline->op2); - COPY_NODE(opline->op2, tmp); - } - } - - if (ZEND_IS_BINARY_ASSIGN_OP_OPCODE(opline->opcode) - && (opline->op1_type & (IS_VAR | IS_CV)) - && opline->op1.var == next_opline->op1.var - && opline->op1_type == next_opline->op1_type) { - opline->extended_value = opline->opcode; - opline->opcode = ZEND_ASSIGN_OP; - COPY_NODE(opline->result, next_opline->result); - MAKE_NOP(next_opline); - opline++; - opline_num++; - } + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); } - break; - case ZEND_JMP: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - - /* convert L: JMP L+1 to NOP */ - if (ZEND_OP1_JMP_ADDR(opline) == opline + 1) { + if (target == opline + 1) { + /* convert L: JMP L+1 to NOP */ MAKE_NOP(opline); - goto done_jmp_optimization; - } - - /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ - while (ZEND_OP1_JMP_ADDR(opline) < end - && ZEND_OP1_JMP_ADDR(opline)->opcode == ZEND_JMP) { - zend_op *target = ZEND_OP1_JMP_ADDR(opline); - CHECK_JMP(target, done_jmp_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(target)); + } 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: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } + jmp_hitlist_count = 0; - while (ZEND_OP2_JMP_ADDR(opline) < end) { - zend_op *target = ZEND_OP2_JMP_ADDR(opline); + target = ZEND_OP2_JMP_ADDR(opline); + while (1) { if (target->opcode == ZEND_JMP) { - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); + 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: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - - while (ZEND_OP2_JMP_ADDR(opline) < end) { - zend_op *target = ZEND_OP2_JMP_ADDR(opline); + 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) */ - CHECK_JMP(target, done_jmp_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); + 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) */ - CHECK_JMP2(target, done_jmp_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); - } else if (target->opcode == opline->opcode + 3 && - SAME_VAR(opline->op1, target->op1)) { - /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to - T = JMPZ_EX(X, L2) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); - opline->opcode += 3; - COPY_NODE(opline->result, target->result); - break; + 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) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - break; - } else if (target->opcode == INV_COND_EX(opline->opcode) && + target = target + 1; + } else if (target->opcode == ZEND_JMPZNZ && SAME_VAR(opline->op1, target->op1)) { - /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to - T = JMPZ_EX(X,L1+1) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - opline->opcode += 3; - COPY_NODE(opline->result, target->result); - break; + 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: { - zend_uchar T_type = opline->result_type; - znode_op T = opline->result; + case ZEND_JMPNZ_EX: + jmp_hitlist_count = 0; - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { + 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 breakes 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) */ - /* convert L: T = JMPZ_EX T,L+1 to NOP */ - if (ZEND_OP2_JMP_ADDR(opline) == opline + 1) { - if (opline->op1.var == opline->result.var) { - MAKE_NOP(opline); - } else { - opline->opcode = ZEND_BOOL; - SET_UNUSED(opline->op2); - } - goto done_jmp_optimization; - } - - while (ZEND_OP2_JMP_ADDR(opline) < end) { - zend_op *target = ZEND_OP2_JMP_ADDR(opline); - - if (target->opcode == opline->opcode-3 && - SAME_VAR(target->op1, T)) { - /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to - JMPZ_EX(X,L2) */ - CHECK_JMP2(target, continue_jmp_ex_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); - } else if (target->opcode == opline->opcode && - SAME_VAR(target->op1, T) && - SAME_VAR(target->result, T)) { - /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to - JMPZ_EX(X,L2) */ - CHECK_JMP2(target, continue_jmp_ex_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(target)); - } else if (target->opcode == ZEND_JMPZNZ && - SAME_VAR(target->op1, T)) { - /* Check for JMPZNZ with same cond variable */ - zend_op *new_target; - - CHECK_JMP2(target, continue_jmp_ex_optimization); - if (opline->opcode == ZEND_JMPZ_EX) { - new_target = ZEND_OP2_JMP_ADDR(target); - } else { - /* JMPNZ_EX */ - new_target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); - } - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_target); - } else if ((target->opcode == INV_EX_COND_EX(opline->opcode) || - target->opcode == INV_EX_COND(opline->opcode)) && - SAME_VAR(opline->op1, target->op1)) { - /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to - JMPZ_EX(X,L1+1) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - break; - } else if (target->opcode == INV_EX_COND(opline->opcode) && - SAME_VAR(target->op1, T)) { - /* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to - JMPZ_EX(X,L1+1) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - break; - } else if (target->opcode == INV_EX_COND_EX(opline->opcode) && - SAME_VAR(target->op1, T) && - SAME_VAR(target->result, T)) { - /* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to - JMPZ_EX(X,L1+1) */ - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - break; - } else if (target->opcode == ZEND_BOOL && - SAME_VAR(opline->result, target->op1)) { - /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to - Z = JMPZ_EX(X,L1+1) */ - opline->result.var = target->result.var; - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target + 1); - break; - } else { - break; - } - } /* while */ -continue_jmp_ex_optimization: - break; -#if 0 - /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */ - { - zend_op *op; - for(op = opline+1; op<end; op++) { - if(op->result_type == IS_TMP_VAR && - op->result.var == opline->result.var) { - break; /* can pass to part 2 */ - } - - if(op->opcode == ZEND_JMP || - op->opcode == ZEND_JMPZ || - op->opcode == ZEND_JMPZ_EX || - op->opcode == ZEND_JMPNZ || - op->opcode == ZEND_JMPNZ_EX || - op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_CASE || - op->opcode == ZEND_RETURN || - op->opcode == ZEND_RETURN_BY_REF || - op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH_R || - op->opcode == ZEND_FE_FETCH_RW || - op->opcode == ZEND_EXIT) { - break; - } - - if(op->op1_type == IS_TMP_VAR && - op->op1.var == opline->result.var) { - goto done_jmp_optimization; - } - - if(op->op2_type == IS_TMP_VAR && - op->op2.var == opline->result.var) { - goto done_jmp_optimization; - } - } /* for */ - - for(op = &op_array->opcodes[opline->op2.opline_num]; op<end; op++) { - - if(op->result_type == IS_TMP_VAR && - op->result.var == opline->result.var) { - break; /* can pass to optimization */ - } - - if(op->opcode == ZEND_JMP || - op->opcode == ZEND_JMPZ || - op->opcode == ZEND_JMPZ_EX || - op->opcode == ZEND_JMPNZ || - op->opcode == ZEND_JMPNZ_EX || - op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_CASE || - op->opcode == ZEND_RETURN || - op->opcode == ZEND_RETURN_BY_REF || - op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH_R || - op->opcode == ZEND_FE_FETCH_RW || - op->opcode == ZEND_EXIT) { - break; - } - - if(op->op1_type == IS_TMP_VAR && - op->op1.var == opline->result.var) { - goto done_jmp_optimization; - } - - if(op->op2_type == IS_TMP_VAR && - op->op2.var == opline->result.var) { - goto done_jmp_optimization; - } - } - - opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ - SET_UNUSED(opline->result); - break; - } -#endif + /* 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: - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - break; - } - - /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ - while (ZEND_OP2_JMP_ADDR(opline) < end - && ZEND_OP2_JMP_ADDR(opline)->opcode == ZEND_JMP) { - zend_op *target = ZEND_OP2_JMP_ADDR(opline); - CHECK_JMP(target, continue_jmpznz_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP1_JMP_ADDR(target)); - } -continue_jmpznz_optimization: - /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ - while (ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) < end - && ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value)->opcode == ZEND_JMP) { - zend_op *target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - CHECK_JMP(target, done_jmp_optimization); - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, ZEND_OP1_JMP_ADDR(target)); +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); } - break; - - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_POST_INC: - case ZEND_POST_DEC: { - /* POST_INC, FREE => PRE_INC */ - zend_op *next_op = opline + 1; - if (next_op >= end) { + 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; } - if (next_op->opcode == ZEND_FREE && - next_op->op1.var == opline->result.var) { - MAKE_NOP(next_op); - opline->opcode -= 2; - opline->result_type = IS_UNUSED; - } + 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; } -done_jmp_optimization: opline++; - opline_num++; } free_alloca(jmp_hitlist, use_heap); } diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 6f8de3d43f..57ae48b021 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -740,13 +740,12 @@ static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval * res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC()); } else { zend_string *key; - zval key_tmp, result_tmp; + zval key_tmp; res = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + if (zend_compare(op1, &key_tmp) == 0) { res = 1; break; } @@ -892,8 +891,7 @@ static inline int ct_eval_func_call( } else if (zend_string_equals_literal(name, "strpos")) { if (Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_STRING - || !Z_STRLEN_P(args[1]) - || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) { + || !Z_STRLEN_P(args[1])) { return FAILURE; } /* pass */ @@ -972,8 +970,7 @@ static inline int ct_eval_func_call( /* pass */ } else if (zend_string_equals_literal(name, "substr")) { if (Z_TYPE_P(args[0]) != IS_STRING - || Z_TYPE_P(args[1]) != IS_LONG - || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) { + || Z_TYPE_P(args[1]) != IS_LONG) { return FAILURE; } /* pass */ @@ -1017,8 +1014,7 @@ static inline int ct_eval_func_call( } else if (zend_string_equals_literal(name, "substr")) { if (Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_LONG - || Z_TYPE_P(args[2]) != IS_LONG - || (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) { + || Z_TYPE_P(args[2]) != IS_LONG) { return FAILURE; } /* pass */ diff --git a/ext/opcache/Optimizer/ssa_integrity.c b/ext/opcache/Optimizer/ssa_integrity.c index ede40be59a..4f042cae74 100644 --- a/ext/opcache/Optimizer/ssa_integrity.c +++ b/ext/opcache/Optimizer/ssa_integrity.c @@ -87,7 +87,7 @@ static inline zend_bool is_in_successors(zend_basic_block *block, int check) { } static inline zend_bool is_var_type(zend_uchar type) { - return type == IS_CV || type == IS_VAR || type == IS_TMP_VAR; + return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0; } #define FAIL(...) do { \ diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c index 8d677c1b86..28b20d10b8 100644 --- a/ext/opcache/Optimizer/zend_call_graph.c +++ b/ext/opcache/Optimizer/zend_call_graph.c @@ -103,7 +103,7 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f case ZEND_INIT_STATIC_METHOD_CALL: call_stack[call] = call_info; func = zend_optimizer_get_called_func( - script, op_array, opline, (build_flags & ZEND_RT_CONSTANTS) != 0); + script, op_array, opline); if (func) { 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; @@ -250,10 +250,8 @@ static void zend_sort_op_arrays(zend_call_graph *call_graph) // TODO: perform topological sort of cyclic call graph } -int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph) /* {{{ */ +int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */ { - int i; - call_graph->op_arrays_count = 0; if (zend_foreach_op_array(call_graph, script, zend_op_array_calc) != SUCCESS) { return FAILURE; @@ -264,13 +262,20 @@ int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t buil if (zend_foreach_op_array(call_graph, script, zend_op_array_collect) != SUCCESS) { return FAILURE; } + + return SUCCESS; +} +/* }}} */ + +void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */ +{ + int i; + for (i = 0; i < call_graph->op_arrays_count; i++) { - zend_analyze_calls(arena, script, build_flags, call_graph->op_arrays[i], call_graph->func_infos + 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); - - return SUCCESS; } /* }}} */ diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h index 033c675b63..8d2b866fd0 100644 --- a/ext/opcache/Optimizer/zend_call_graph.h +++ b/ext/opcache/Optimizer/zend_call_graph.h @@ -69,7 +69,8 @@ typedef struct _zend_call_graph { BEGIN_EXTERN_C() -int zend_build_call_graph(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_call_graph *call_graph); +int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); +void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph); zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, zend_op_array *op_array); int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info); diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h index 7d6ef25eee..ec7a10462c 100644 --- a/ext/opcache/Optimizer/zend_cfg.h +++ b/ext/opcache/Optimizer/zend_cfg.h @@ -92,7 +92,6 @@ typedef struct _zend_cfg { } zend_cfg; /* Build Flags */ -#define ZEND_RT_CONSTANTS (1U<<31) #define ZEND_CFG_STACKLESS (1<<30) #define ZEND_SSA_DEBUG_LIVENESS (1<<29) #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28) @@ -102,15 +101,15 @@ typedef struct _zend_cfg { #define ZEND_CALL_TREE (1<<23) #define ZEND_SSA_USE_CV_RESULTS (1<<22) -#define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \ - ((rt_constants) ? \ +#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, (build_flags & ZEND_RT_CONSTANTS)) + CRT_CONSTANT_EX(op_array, opline, node) #define RETURN_VALUE_USED(opline) \ ((opline)->result_type != IS_UNUSED) diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index e995b673b7..3bb76fb05c 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -123,10 +123,6 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg 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_LIST_W: case ZEND_VERIFY_RETURN_TYPE: case ZEND_PRE_INC_OBJ: diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index d6ef63415a..68b3d4a99f 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -127,11 +127,8 @@ static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t fla } 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) { - fprintf(stderr, " (unqualified)"); - } - if (op.num & IS_CONSTANT_IN_NAMESPACE) { - fprintf(stderr, " (in-namespace)"); + if (op.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) { + fprintf(stderr, " (unqualified-in-namespace)"); } } } @@ -142,7 +139,7 @@ void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_n 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_TMP_VAR) { + } 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); @@ -314,10 +311,6 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst fprintf(stderr, "resource"); } } - if (info & MAY_BE_ERROR) { - if (first) first = 0; else fprintf(stderr, ", "); - fprintf(stderr, "error"); - } //TODO: this is useful only for JIT??? if (info & MAY_BE_IN_REG) { if (first) first = 0; else fprintf(stderr, ", "); @@ -581,20 +574,18 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * fprintf(stderr, " (ref)"); } } - if ((ZEND_VM_EXT_DIM_OBJ_WRITE|ZEND_VM_EXT_FETCH_REF) & flags) { + 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)"); - } else if (obj_flags == ZEND_FETCH_OBJ_WRITE) { - fprintf(stderr, " (obj write)"); } } } if (opline->op1_type == IS_CONST) { - zend_dump_const(CRT_CONSTANT_EX(op_array, opline, opline->op1, (dump_flags & ZEND_DUMP_RT_CONSTANTS))); + zend_dump_const(CRT_CONSTANT(opline->op1)); } else if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { if (ssa && ssa->ops) { int ssa_var_num = ssa->ops[opline - op_array->opcodes].op1_use; @@ -630,7 +621,7 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * } if (opline->op2_type == IS_CONST) { - zval *op = CRT_CONSTANT_EX(op_array, opline, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)); + zval *op = CRT_CONSTANT(opline->op2); if (opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING) { HashTable *jumptable = Z_ARRVAL_P(op); zend_string *key; @@ -696,7 +687,13 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * } } if (opline->result_type == IS_CONST) { - zend_dump_const(CRT_CONSTANT_EX(op_array, opline, opline->result, (dump_flags & ZEND_DUMP_RT_CONSTANTS))); + 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 && ssa->ops && ssa->ops[opline - op_array->opcodes].result_use >= 0) { if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { if (ssa && ssa->ops) { @@ -729,6 +726,9 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags 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"); } diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c index d9ff58e75e..96fe6594f9 100644 --- a/ext/opcache/Optimizer/zend_func_info.c +++ b/ext/opcache/Optimizer/zend_func_info.c @@ -39,66 +39,19 @@ typedef struct _func_info_t { info_func_t info_func; } func_info_t; -/* MSVC defines its own IN macro, undefine it here */ -#undef IN - #define F0(name, info) \ - {name, sizeof(name)-1, (FUNC_MAY_WARN | (info)), NULL} + {name, sizeof(name)-1, (info), NULL} #define F1(name, info) \ - {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | (info)), NULL} + {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL} #define FN(name, info) \ - {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL} + {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL} #define FR(name, info) \ - {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_REF | (info)), NULL} + {name, sizeof(name)-1, (MAY_BE_REF | (info)), NULL} #define FX(name, info) \ - {name, sizeof(name)-1, (FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | (info)), NULL} -#define I0(name, info) \ - {name, sizeof(name)-1, (info), NULL} -#define I1(name, info) \ - {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL} -#define IN(name, info) \ - {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL} + {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_strlen_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 1) { - uint32_t tmp = 0; - if (call_info->arg_info[0].opline) { - uint32_t arg_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline); - - if (arg_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) { - tmp |= MAY_BE_LONG; - } - if (arg_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - /* warning, and returns NULL */ - tmp |= FUNC_MAY_WARN | MAY_BE_NULL; - } - } else { - tmp |= MAY_BE_LONG | FUNC_MAY_WARN | MAY_BE_NULL; - } - return tmp; - } else if (call_info->num_args != -1) { - /* warning, and returns NULL */ - return FUNC_MAY_WARN | MAY_BE_NULL; - } else { - return MAY_BE_LONG | FUNC_MAY_WARN | MAY_BE_NULL; - } -} - -static uint32_t zend_dechex_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 1) { - return MAY_BE_RC1 | MAY_BE_STRING; - } else if (call_info->num_args != -1) { - /* warning, and returns NULL */ - return FUNC_MAY_WARN | MAY_BE_NULL; - } else { - return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_STRING | MAY_BE_NULL; - } -} - static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa) { if (call_info->num_args == 2 || call_info->num_args == 3) { @@ -106,7 +59,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa uint32_t t1 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline); uint32_t t2 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline); uint32_t t3 = 0; - uint32_t tmp = FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG; + uint32_t tmp = MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG; if (call_info->num_args == 3) { t3 = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline); @@ -127,87 +80,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa return tmp; } else { /* may warning, and return FALSE */ - return FUNC_MAY_WARN | MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; - } -} - -static uint32_t zend_is_type_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 1) { - return MAY_BE_FALSE | MAY_BE_TRUE; - } else { - return MAY_BE_FALSE | MAY_BE_TRUE | FUNC_MAY_WARN; - } -} - -static uint32_t zend_l_ss_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 2) { - - uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline); - uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline); - uint32_t tmp = 0; - - if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) && - (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) { - tmp |= MAY_BE_LONG; - } - if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { - /* warning, and returns NULL */ - tmp |= FUNC_MAY_WARN | MAY_BE_NULL; - } - return tmp; - } else { - /* warning, and returns NULL */ - return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_LONG; - } -} - -static uint32_t zend_lb_ssn_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 3) { - uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline); - uint32_t arg2_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[1].opline); - uint32_t arg3_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[2].opline); - uint32_t tmp = 0; - - if ((arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) && - (arg2_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) && - (arg3_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT))) { - tmp |= MAY_BE_LONG | MAY_BE_FALSE; - } - if ((arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (arg2_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) || - (arg3_info & (MAY_BE_STRING|MAY_BE_RESOURCE|MAY_BE_ARRAY|MAY_BE_OBJECT))) { - /* warning, and returns NULL */ - tmp |= FUNC_MAY_WARN | MAY_BE_NULL; - } - return tmp; - } else { - /* warning, and returns NULL */ - return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_LONG; - } -} - -static uint32_t zend_b_s_info(const zend_call_info *call_info, const zend_ssa *ssa) -{ - if (call_info->num_args == 1) { - - uint32_t arg1_info = _ssa_op1_info(call_info->caller_op_array, ssa, call_info->arg_info[0].opline); - uint32_t tmp = 0; - - if (arg1_info & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_OBJECT)) { - tmp |= MAY_BE_FALSE | MAY_BE_TRUE; - } - if (arg1_info & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - /* warning, and returns NULL */ - tmp |= FUNC_MAY_WARN | MAY_BE_NULL; - } - return tmp; - } else { - /* warning, and returns NULL */ - return FUNC_MAY_WARN | MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE; + return MAY_BE_RC1 | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; } } @@ -215,191 +88,158 @@ static uint32_t zend_b_s_info(const zend_call_info *call_info, const zend_ssa *s static const func_info_t func_infos[] = { /* zend */ - I1("zend_version", MAY_BE_STRING), - I0("gc_collect_cycles", MAY_BE_LONG), - I0("gc_enabled", MAY_BE_FALSE | MAY_BE_TRUE), - F0("gc_enable", MAY_BE_NULL), - F0("gc_disable", MAY_BE_NULL), - F0("func_num_args", MAY_BE_LONG), + F1("zend_version", MAY_BE_STRING), FN("func_get_arg", UNKNOWN_INFO), F1("func_get_args", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - FC("strlen", zend_strlen_info), - FC("strcmp", zend_l_ss_info), - FC("strncmp", zend_lb_ssn_info), - FC("strcasecmp", zend_l_ss_info), - FC("strncasecmp", zend_lb_ssn_info), - F1("each", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_KEY_ANY), - F0("error_reporting", MAY_BE_NULL | MAY_BE_LONG), - F0("define", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_NULL), // TODO: inline - FC("defined", zend_b_s_info), // TODO: inline + F0("strncmp", MAY_BE_FALSE | MAY_BE_LONG), + F0("strncasecmp", MAY_BE_FALSE | MAY_BE_LONG), FN("get_class", MAY_BE_FALSE | MAY_BE_STRING), FN("get_called_class", MAY_BE_FALSE | MAY_BE_STRING), - FN("get_parent_class", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_NULL), - F0("is_subclass_of", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline - F0("is_a", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline - F1("get_class_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - FN("get_object_vars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - FN("get_mangled_object_vars", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - F1("get_class_methods", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F0("method_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("property_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("class_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("interface_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("trait_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - FC("function_exists", zend_b_s_info), // TODO: inline - F0("class_alias", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - I1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F0("trigger_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("user_error", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + FN("get_parent_class", MAY_BE_FALSE | MAY_BE_STRING), + F1("get_class_vars", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), + FN("get_object_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), + FN("get_mangled_object_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), + F1("get_class_methods", MAY_BE_NULL | 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), - I0("restore_error_handler", MAY_BE_TRUE), - I0("restore_exception_handler", MAY_BE_TRUE), - I1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - I1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - I1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_defined_functions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - I1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - FN("create_function", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("get_resource_type", MAY_BE_NULL | MAY_BE_STRING), - F1("get_defined_constants", MAY_BE_NULL | 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), - F0("debug_print_backtrace", MAY_BE_NULL), - F1("debug_backtrace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), - F1("get_loaded_extensions", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - FC("extension_loaded", zend_b_s_info), - F1("get_extension_funcs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + 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_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("hex2bin", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("bin2hex", MAY_BE_FALSE | MAY_BE_STRING), + F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING), F0("sleep", MAY_BE_FALSE | MAY_BE_LONG), F0("usleep", MAY_BE_NULL | MAY_BE_FALSE), #if HAVE_NANOSLEEP - F0("time_nanosleep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), - F0("time_sleep_until", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), + F0("time_sleep_until", MAY_BE_FALSE | MAY_BE_TRUE), #endif #if HAVE_STRPTIME - F1("strptime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), + F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), #endif F0("flush", MAY_BE_NULL), - F1("wordwrap", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("htmlspecialchars", MAY_BE_NULL | MAY_BE_STRING), - F1("htmlentities", MAY_BE_NULL | MAY_BE_STRING), - FN("html_entity_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("htmlspecialchars_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("get_html_translation_table", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("sha1", MAY_BE_NULL | MAY_BE_STRING), - F1("sha1_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("md5", MAY_BE_NULL | MAY_BE_STRING), - F1("md5_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("crc32", MAY_BE_NULL | MAY_BE_LONG), - F1("iptcparse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F1("iptcembed", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("getimagesize", MAY_BE_NULL | 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_NULL | 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_NULL | MAY_BE_STRING), + F1("wordwrap", MAY_BE_FALSE | MAY_BE_STRING), + F1("htmlspecialchars", MAY_BE_STRING), + F1("htmlentities", MAY_BE_STRING), + FN("html_entity_decode", MAY_BE_FALSE | MAY_BE_STRING), + FN("htmlspecialchars_decode", MAY_BE_FALSE | 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_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_NULL | MAY_BE_TRUE), - F1("phpversion", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("phpcredits", MAY_BE_NULL | MAY_BE_TRUE), - I1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING), - F1("php_uname", MAY_BE_NULL | MAY_BE_STRING), - I1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING), - I1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING), - F0("strnatcmp", MAY_BE_NULL | MAY_BE_LONG), - F0("strnatcasecmp", MAY_BE_NULL | MAY_BE_LONG), - F0("substr_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("strspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("strcspn", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("strtok", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("strtoupper", MAY_BE_NULL | MAY_BE_STRING), - FN("strtolower", MAY_BE_NULL | MAY_BE_STRING), - F0("strpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("stripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("strrpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("strripos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("strrev", MAY_BE_NULL | MAY_BE_STRING), - F1("hebrev", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("hebrevc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("nl2br", MAY_BE_NULL | MAY_BE_STRING), - F1("basename", MAY_BE_NULL | 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), + F0("substr_count", MAY_BE_FALSE | MAY_BE_LONG), + F0("strspn", MAY_BE_FALSE | MAY_BE_LONG), + F0("strcspn", MAY_BE_FALSE | MAY_BE_LONG), + F1("strtok", MAY_BE_FALSE | MAY_BE_STRING), + FN("strtoupper", MAY_BE_STRING), + FN("strtolower", MAY_BE_STRING), + F0("strpos", MAY_BE_FALSE | MAY_BE_LONG), + F0("stripos", MAY_BE_FALSE | MAY_BE_LONG), + F0("strrpos", MAY_BE_FALSE | MAY_BE_LONG), + F0("strripos", MAY_BE_FALSE | MAY_BE_LONG), + F1("strrev", MAY_BE_STRING), + F1("hebrev", MAY_BE_STRING), + F1("hebrevc", MAY_BE_STRING), + FN("nl2br", MAY_BE_STRING), + F1("basename", MAY_BE_STRING), F1("dirname", MAY_BE_NULL | MAY_BE_STRING), - F1("pathinfo", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F1("stripslashes", MAY_BE_NULL | MAY_BE_STRING), - F1("stripcslashes", MAY_BE_NULL | MAY_BE_STRING), - F1("strstr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("stristr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("strrchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("str_shuffle", MAY_BE_NULL | MAY_BE_STRING), - F1("str_word_count", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("str_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_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_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F1("str_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING), F0("substr_compare", MAY_BE_FALSE | MAY_BE_LONG), - F0("strcoll", MAY_BE_NULL | MAY_BE_LONG), #ifdef HAVE_STRFMON - F1("money_format", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("money_format", MAY_BE_FALSE | MAY_BE_STRING), #endif - FN("substr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("substr_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), - F1("quotemeta", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("ucfirst", MAY_BE_NULL | MAY_BE_STRING), - FN("lcfirst", MAY_BE_NULL | MAY_BE_STRING), - F1("ucwords", MAY_BE_NULL | MAY_BE_STRING), - FN("strtr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("addslashes", MAY_BE_NULL | MAY_BE_STRING), - F1("addcslashes", MAY_BE_NULL | MAY_BE_STRING), - FN("rtrim", MAY_BE_NULL | MAY_BE_STRING), - FN("str_replace", MAY_BE_NULL | 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_NULL | 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("substr", MAY_BE_FALSE | MAY_BE_STRING), + FN("substr_replace", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), + F1("quotemeta", MAY_BE_STRING), + FN("ucfirst", MAY_BE_STRING), + FN("lcfirst", MAY_BE_STRING), + F1("ucwords", MAY_BE_STRING), + FN("strtr", MAY_BE_FALSE | MAY_BE_STRING), + FN("addslashes", MAY_BE_STRING), + F1("addcslashes", MAY_BE_STRING), + FN("rtrim", MAY_BE_STRING), + FN("chop", 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_NULL | MAY_BE_STRING), - F1("count_chars", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("chunk_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - FN("trim", MAY_BE_NULL | MAY_BE_STRING), - FN("ltrim", MAY_BE_NULL | MAY_BE_STRING), - F1("strip_tags", MAY_BE_NULL | MAY_BE_STRING), - F0("similar_text", MAY_BE_NULL | MAY_BE_LONG), - F1("explode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - FN("implode", MAY_BE_NULL | MAY_BE_STRING), - FN("join", MAY_BE_NULL | MAY_BE_STRING), - FN("setlocale", MAY_BE_NULL | MAY_BE_FALSE | 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_FALSE | MAY_BE_STRING), + FN("trim", MAY_BE_STRING), + FN("ltrim", MAY_BE_STRING), + F1("strip_tags", MAY_BE_STRING), + F1("explode", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + FN("implode", MAY_BE_STRING), + FN("join", MAY_BE_STRING), + FN("setlocale", MAY_BE_FALSE | MAY_BE_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_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("nl_langinfo", MAY_BE_FALSE | MAY_BE_STRING), #endif - F1("soundex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("levenshtein", MAY_BE_NULL | MAY_BE_LONG), - F1("chr", MAY_BE_NULL | MAY_BE_STRING), - F0("ord", MAY_BE_NULL | MAY_BE_LONG), - F0("parse_str", MAY_BE_NULL), + F1("soundex", MAY_BE_FALSE | MAY_BE_STRING), + F0("levenshtein", MAY_BE_LONG), + F1("chr", MAY_BE_STRING), F1("str_getcsv", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), F1("str_pad", MAY_BE_NULL | MAY_BE_STRING), - F1("chop", MAY_BE_NULL | MAY_BE_STRING), - F1("strchr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("strchr", MAY_BE_FALSE | MAY_BE_STRING), F1("sprintf", MAY_BE_FALSE | MAY_BE_STRING), F0("printf", MAY_BE_FALSE | MAY_BE_LONG), F0("vprintf", MAY_BE_FALSE | MAY_BE_LONG), F1("vsprintf", MAY_BE_FALSE | MAY_BE_STRING), - F0("fprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("vfprintf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + F0("fprintf", MAY_BE_FALSE | MAY_BE_LONG), + F0("vfprintf", MAY_BE_FALSE | MAY_BE_LONG), 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_NULL | MAY_BE_STRING), - F1("urldecode", MAY_BE_NULL | MAY_BE_STRING), - F1("rawurlencode", MAY_BE_NULL | MAY_BE_STRING), - F1("rawurldecode", MAY_BE_NULL | MAY_BE_STRING), + F1("parse_url", 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_FALSE | MAY_BE_STRING), #if defined(HAVE_SYMLINK) || defined(PHP_WIN32) - F1("readlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("linkinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("symlink", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F1("readlink", MAY_BE_FALSE | MAY_BE_STRING), + F0("linkinfo", MAY_BE_FALSE | MAY_BE_LONG), + F0("symlink", MAY_BE_FALSE | MAY_BE_TRUE), + F0("link", MAY_BE_FALSE | MAY_BE_TRUE), #endif F0("unlink", MAY_BE_FALSE | MAY_BE_TRUE), F1("exec", MAY_BE_FALSE | MAY_BE_STRING), F1("system", MAY_BE_FALSE | MAY_BE_STRING), - F1("escapeshellcmd", MAY_BE_NULL | MAY_BE_STRING), - F1("escapeshellarg", MAY_BE_NULL | MAY_BE_STRING), + F1("escapeshellcmd", MAY_BE_STRING), + F1("escapeshellarg", MAY_BE_STRING), F1("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 @@ -418,111 +258,89 @@ static const func_info_t func_infos[] = { F0("getrandmax", MAY_BE_NULL | MAY_BE_LONG), F0("mt_rand", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("mt_srand", MAY_BE_NULL), - I0("mt_getrandmax", MAY_BE_LONG), + F0("mt_getrandmax", MAY_BE_LONG), #if HAVE_GETSERVBYNAME - F0("getservbyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + F0("getservbyname", MAY_BE_FALSE | MAY_BE_LONG), #endif #if HAVE_GETSERVBYPORT - F1("getservbyport", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING), #endif #if HAVE_GETPROTOBYNAME - F0("getprotobyname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + F0("getprotobyname", MAY_BE_FALSE | MAY_BE_LONG), #endif #if HAVE_GETPROTOBYNUMBER - F1("getprotobynumber", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING), #endif F0("getmyuid", MAY_BE_FALSE | MAY_BE_LONG), F0("getmygid", MAY_BE_FALSE | MAY_BE_LONG), F0("getmypid", MAY_BE_FALSE | MAY_BE_LONG), F0("getmyinode", MAY_BE_FALSE | MAY_BE_LONG), - F0("getlastmod", MAY_BE_FALSE | MAY_BE_LONG), - F1("base64_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("base64_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("password_hash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("password_get_info", MAY_BE_NULL | 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), - F0("password_needs_rehash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING), + F1("base64_encode", MAY_BE_STRING), + F1("password_hash", MAY_BE_NULL | 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), + F0("password_needs_rehash", MAY_BE_FALSE | MAY_BE_TRUE), F0("password_verify", MAY_BE_FALSE | MAY_BE_TRUE), F1("convert_uuencode", MAY_BE_FALSE | MAY_BE_STRING), F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING), - F0("abs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE), - F0("ceil", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("floor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("round", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("sin", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("cos", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("tan", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("asin", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("acos", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("atan", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("atanh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("atan2", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("sinh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("cosh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("tanh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("asinh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("acosh", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("expm1", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("log1p", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("pi", MAY_BE_DOUBLE), - F0("is_finite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_nan", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_infinite", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("pow", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE), - F0("exp", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("log10", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("sqrt", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("hypot", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("deg2rad", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("rad2deg", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("bindec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE), - F0("hexdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE), - F0("octdec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE), - F1("decbin", MAY_BE_NULL | MAY_BE_STRING), - F1("decoct", MAY_BE_NULL | MAY_BE_STRING), - FC("dechex", zend_dechex_info), - F1("base_convert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("number_format", MAY_BE_NULL | MAY_BE_STRING), - F0("fmod", MAY_BE_NULL | MAY_BE_DOUBLE), + F0("abs", MAY_BE_LONG | MAY_BE_DOUBLE), + F0("ceil", MAY_BE_DOUBLE), + F0("floor", MAY_BE_DOUBLE), + F0("round", MAY_BE_FALSE | MAY_BE_DOUBLE), + F0("expm1", MAY_BE_DOUBLE), + F0("log1p", MAY_BE_DOUBLE), + F1("pow", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT), + F0("exp", MAY_BE_DOUBLE), + F0("log", MAY_BE_FALSE | MAY_BE_DOUBLE), + F0("log10", MAY_BE_DOUBLE), + F0("sqrt", MAY_BE_DOUBLE), + F0("hypot", MAY_BE_DOUBLE), + F0("deg2rad", MAY_BE_DOUBLE), + F0("rad2deg", MAY_BE_DOUBLE), + F0("bindec", MAY_BE_LONG | MAY_BE_DOUBLE), + F0("hexdec", MAY_BE_LONG | MAY_BE_DOUBLE), + F0("octdec", MAY_BE_LONG | MAY_BE_DOUBLE), + F1("decbin", MAY_BE_STRING), + F1("decoct", MAY_BE_STRING), + F1("dechex", MAY_BE_STRING), + F1("base_convert", MAY_BE_FALSE | MAY_BE_STRING), + F1("number_format", MAY_BE_STRING), + F0("fmod", MAY_BE_DOUBLE), #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 - F0("ip2long", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("long2ip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F0("ip2long", MAY_BE_FALSE | MAY_BE_LONG), + 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 - F0("putenv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("putenv", MAY_BE_FALSE | MAY_BE_TRUE), #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_NULL | MAY_BE_DOUBLE | MAY_BE_STRING), - F1("gettimeofday", MAY_BE_NULL | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), + 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_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), + 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_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("uniqid", MAY_BE_STRING), #endif - F1("quoted_printable_decode", MAY_BE_NULL | MAY_BE_STRING), - F1("quoted_printable_encode", MAY_BE_NULL | MAY_BE_STRING), - F1("convert_cyr_string", MAY_BE_NULL | MAY_BE_STRING), - I1("get_current_user", MAY_BE_STRING), - F0("set_time_limit", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("header_register_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("get_cfg_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - I0("magic_quotes_runtime", MAY_BE_FALSE), - I0("set_magic_quotes_runtime", MAY_BE_FALSE), - I0("get_magic_quotes_gpc", MAY_BE_FALSE), - I0("get_magic_quotes_runtime", MAY_BE_FALSE), - F0("error_log", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - I1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), + F1("quoted_printable_decode", MAY_BE_STRING), + F1("quoted_printable_encode", MAY_BE_STRING), + F1("convert_cyr_string", 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), + F0("get_magic_quotes_gpc", MAY_BE_FALSE), + F0("get_magic_quotes_runtime", MAY_BE_FALSE), + F0("error_log", MAY_BE_FALSE | MAY_BE_TRUE), + 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), @@ -543,73 +361,50 @@ static const func_info_t func_infos[] = { F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), F1("show_source", MAY_BE_FALSE | MAY_BE_STRING), F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), - F1("php_strip_whitespace", MAY_BE_FALSE | MAY_BE_STRING), - FN("ini_get", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("ini_get_all", MAY_BE_NULL | 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), - FN("ini_set", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("php_strip_whitespace", MAY_BE_STRING), + FN("ini_get", MAY_BE_FALSE | 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), + FN("ini_set", MAY_BE_FALSE | MAY_BE_STRING), F1("ini_alter", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F0("ini_restore", MAY_BE_NULL), - I1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING), - F1("set_include_path", MAY_BE_NULL | 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), F0("restore_include_path", MAY_BE_NULL), - F0("setcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("setrawcookie", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("header", MAY_BE_NULL), - F0("header_remove", MAY_BE_NULL), - F0("headers_sent", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F0("http_response_code", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("connection_aborted", MAY_BE_LONG), F0("connection_status", MAY_BE_LONG), - F0("ignore_user_abort", MAY_BE_NULL | MAY_BE_LONG), + F0("ignore_user_abort", MAY_BE_LONG), 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 - F0("is_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("move_uploaded_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("gethostbyaddr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("gethostbyname", MAY_BE_NULL | MAY_BE_STRING), - F1("gethostbynamel", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F0("move_uploaded_file", MAY_BE_FALSE | MAY_BE_TRUE), + 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 - F0("dns_check_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("checkdnsrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("dns_check_record", MAY_BE_FALSE | MAY_BE_TRUE), + F0("checkdnsrr", MAY_BE_FALSE | MAY_BE_TRUE), # if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS - F0("dns_get_mx", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("getmxrr", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("dns_get_record", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), + F0("dns_get_mx", MAY_BE_FALSE | MAY_BE_TRUE), + F0("getmxrr", MAY_BE_FALSE | MAY_BE_TRUE), + F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), # endif #endif - F0("intval", MAY_BE_NULL | MAY_BE_LONG), - F0("floatval", MAY_BE_NULL | MAY_BE_DOUBLE), - F0("doubleval", MAY_BE_NULL | MAY_BE_DOUBLE), - FN("strval", MAY_BE_NULL | MAY_BE_STRING), - F0("boolval", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - FN("gettype", MAY_BE_NULL | MAY_BE_STRING), - F0("settype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - FC("is_null", zend_is_type_info), - F0("is_resource", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for closed resources - FC("is_bool", zend_is_type_info), - FC("is_long", zend_is_type_info), - FC("is_float", zend_is_type_info), - FC("is_int", zend_is_type_info), - FC("is_integer", zend_is_type_info), - FC("is_double", zend_is_type_info), - FC("is_real", zend_is_type_info), - F0("is_numeric", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - FC("is_string", zend_is_type_info), - FC("is_array", zend_is_type_info), - F0("is_object", MAY_BE_FALSE | MAY_BE_TRUE), // TODO: inline with support for incomplete class - F0("is_scalar", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_callable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_countable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_iterable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("intval", MAY_BE_LONG), + F0("floatval", MAY_BE_DOUBLE), + F0("doubleval", MAY_BE_DOUBLE), + FN("strval", MAY_BE_STRING), + F0("boolval", MAY_BE_FALSE | MAY_BE_TRUE), + FN("gettype", MAY_BE_STRING), + F0("settype", MAY_BE_FALSE | MAY_BE_TRUE), F0("pclose", MAY_BE_FALSE | MAY_BE_LONG), - F1("popen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), + F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE), F0("readfile", MAY_BE_FALSE | MAY_BE_LONG), F0("rewind", MAY_BE_FALSE | MAY_BE_TRUE), F0("rmdir", MAY_BE_FALSE | MAY_BE_TRUE), @@ -618,7 +413,6 @@ static const func_info_t func_infos[] = { F0("feof", MAY_BE_FALSE | MAY_BE_TRUE), F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING), F1("fgets", MAY_BE_FALSE | MAY_BE_STRING), - F1("fgetss", MAY_BE_FALSE | MAY_BE_STRING), F1("fread", MAY_BE_FALSE | MAY_BE_STRING), F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE), F0("fpassthru", MAY_BE_FALSE | MAY_BE_LONG), @@ -631,13 +425,13 @@ static const func_info_t func_infos[] = { F0("fputs", MAY_BE_FALSE | MAY_BE_LONG), F0("mkdir", MAY_BE_FALSE | MAY_BE_TRUE), F0("rename", MAY_BE_FALSE | MAY_BE_TRUE), - F0("copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("tempnam", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F0("copy", MAY_BE_FALSE | MAY_BE_TRUE), + F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING), F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("file_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("file_put_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("stream_select", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + 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), + F0("file_put_contents", MAY_BE_FALSE | MAY_BE_LONG), + F0("stream_select", MAY_BE_FALSE | MAY_BE_LONG), F1("stream_context_create", MAY_BE_FALSE | MAY_BE_RESOURCE), F0("stream_context_set_params", MAY_BE_FALSE | MAY_BE_TRUE), F1("stream_context_get_params", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), @@ -665,44 +459,40 @@ static const func_info_t func_infos[] = { F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING), F0("stream_supports_lock", MAY_BE_FALSE | MAY_BE_TRUE), F1("fgetcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING), - F0("fputcsv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("flock", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("get_meta_tags", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), + F0("fputcsv", MAY_BE_FALSE | MAY_BE_LONG), + F0("flock", MAY_BE_FALSE | MAY_BE_TRUE), + F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), F0("stream_set_read_buffer", MAY_BE_FALSE | MAY_BE_LONG), F0("stream_set_write_buffer", MAY_BE_FALSE | MAY_BE_LONG), F0("set_file_buffer", MAY_BE_FALSE | MAY_BE_LONG), F0("stream_set_chunk_size", MAY_BE_FALSE | MAY_BE_LONG), - F0("stream_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("socket_set_blocking", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("stream_get_meta_data", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), + F0("stream_set_blocking", MAY_BE_FALSE | MAY_BE_TRUE), + F0("socket_set_blocking", MAY_BE_FALSE | MAY_BE_TRUE), + F1("stream_get_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING), - F0("stream_wrapper_register", MAY_BE_FALSE | MAY_BE_TRUE), - F0("stream_register_wrapper", MAY_BE_FALSE | MAY_BE_TRUE), - F0("stream_wrapper_unregister", MAY_BE_FALSE | MAY_BE_TRUE), - F0("stream_wrapper_restore", MAY_BE_FALSE | MAY_BE_TRUE), F1("stream_get_wrappers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("stream_get_transports", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("stream_resolve_include_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING), F0("stream_is_local", MAY_BE_FALSE | MAY_BE_TRUE), - F1("get_headers", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), + F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), #if HAVE_SYS_TIME_H || defined(PHP_WIN32) - F0("stream_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("socket_set_timeout", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("stream_set_timeout", MAY_BE_FALSE | MAY_BE_TRUE), + F0("socket_set_timeout", MAY_BE_FALSE | MAY_BE_TRUE), #endif - F1("socket_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), + F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), #if HAVE_REALPATH || defined(ZTS) - F1("realpath", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("realpath", MAY_BE_FALSE | MAY_BE_STRING), #endif #ifdef HAVE_FNMATCH - F0("fnmatch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("fnmatch", MAY_BE_FALSE | MAY_BE_TRUE), #endif F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("pack", MAY_BE_FALSE | MAY_BE_STRING), - F1("unpack", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), + 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_NULL | MAY_BE_STRING), - FN("opendir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), + F1("crypt", MAY_BE_STRING), + FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE), F0("closedir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("chdir", MAY_BE_FALSE | MAY_BE_TRUE), #if defined(HAVE_CHROOT) && !defined(ZTS) && ENABLE_CHROOT_FUNC @@ -710,86 +500,54 @@ static const func_info_t func_infos[] = { #endif F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING), F0("rewinddir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("readdir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), - F1("scandir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_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_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), #endif - F0("fileatime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("filectime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("filegroup", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("fileinode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("filemtime", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("fileowner", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("fileperms", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("filesize", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("filetype", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("file_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_writable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_readable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_executable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_dir", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("is_link", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("stat", MAY_BE_NULL | 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_NULL | 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), - F0("chown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("chgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("fileatime", MAY_BE_FALSE | MAY_BE_LONG), + F0("filectime", MAY_BE_FALSE | MAY_BE_LONG), + F0("filegroup", MAY_BE_FALSE | MAY_BE_LONG), + F0("fileinode", MAY_BE_FALSE | MAY_BE_LONG), + F0("filemtime", MAY_BE_FALSE | MAY_BE_LONG), + F0("fileowner", MAY_BE_FALSE | MAY_BE_LONG), + F0("fileperms", MAY_BE_FALSE | MAY_BE_LONG), + F0("filesize", MAY_BE_FALSE | MAY_BE_LONG), + F1("filetype", MAY_BE_FALSE | MAY_BE_STRING), + F0("file_exists", MAY_BE_FALSE | MAY_BE_TRUE), + 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), + F0("chown", MAY_BE_FALSE | MAY_BE_TRUE), + F0("chgrp", MAY_BE_FALSE | MAY_BE_TRUE), #if HAVE_LCHOWN - F0("lchown", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("lchown", MAY_BE_FALSE | MAY_BE_TRUE), #endif #if HAVE_LCHOWN - F0("lchgrp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("lchgrp", MAY_BE_FALSE | MAY_BE_TRUE), #endif - F0("chmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("chmod", MAY_BE_FALSE | MAY_BE_TRUE), #if HAVE_UTIME - F0("touch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F0("touch", MAY_BE_FALSE | MAY_BE_TRUE), #endif F0("clearstatcache", MAY_BE_NULL), - F0("disk_total_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("disk_free_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - F0("diskfreespace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_DOUBLE), - I0("realpath_cache_size", MAY_BE_LONG), - I1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), - F0("mail", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ezmlm_hash", MAY_BE_NULL | MAY_BE_LONG), + F0("disk_total_space", MAY_BE_FALSE | MAY_BE_DOUBLE), + F0("disk_free_space", MAY_BE_FALSE | MAY_BE_DOUBLE), + F0("diskfreespace", MAY_BE_FALSE | MAY_BE_DOUBLE), + F0("realpath_cache_size", MAY_BE_LONG), + F1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), + F0("mail", MAY_BE_FALSE | MAY_BE_TRUE), + F0("ezmlm_hash", MAY_BE_LONG), #ifdef HAVE_SYSLOG_H - F0("openlog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("syslog", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("closelog", MAY_BE_TRUE), #endif - F0("lcg_value", MAY_BE_DOUBLE), - F1("metaphone", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("ob_start", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ob_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ob_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ob_end_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ob_end_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("ob_get_flush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("ob_get_clean", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("ob_get_length", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("ob_get_level", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F1("ob_get_status", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - FN("ob_get_contents", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("ob_implicit_flush", MAY_BE_NULL), - F1("ob_list_handlers", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F0("ksort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("krsort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("natsort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("natcasesort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("asort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("arsort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("sort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("rsort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("usort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("uasort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("uksort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("shuffle", MAY_BE_FALSE | MAY_BE_TRUE), - F0("array_walk", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("array_walk_recursive", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("count", MAY_BE_NULL | MAY_BE_LONG), + F1("metaphone", MAY_BE_FALSE | MAY_BE_STRING), + F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING), + F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING), + F0("ob_get_length", MAY_BE_FALSE | MAY_BE_LONG), + 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), + FN("ob_get_contents", MAY_BE_FALSE | MAY_BE_STRING), + F1("ob_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), FN("end", UNKNOWN_INFO), FN("prev", UNKNOWN_INFO), FN("next", UNKNOWN_INFO), @@ -798,129 +556,111 @@ static const func_info_t func_infos[] = { FN("key", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING), FN("min", UNKNOWN_INFO), FN("max", UNKNOWN_INFO), - F0("in_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), FN("array_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F0("extract", MAY_BE_NULL | MAY_BE_LONG), - F1("compact", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_fill", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), - F1("array_fill_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + 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_FALSE | 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), - F0("array_multisort", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("array_push", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), FN("array_pop", UNKNOWN_INFO), FN("array_shift", UNKNOWN_INFO), - F0("array_unshift", MAY_BE_NULL | MAY_BE_LONG), - F1("array_splice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_slice", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_merge", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_merge_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_replace", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_replace_recursive", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_keys", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), - FN("array_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_count_values", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), - F1("array_column", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_reverse", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + 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), + FN("array_merge", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + F1("array_merge_recursive", 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), - FN("array_pad", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_flip", MAY_BE_NULL | 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_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + FN("array_pad", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + 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), F1("array_rand", UNKNOWN_INFO), - FN("array_unique", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_intersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_uintersect_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_diff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_key", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_ukey", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_diff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_udiff_uassoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F0("array_sum", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE), - F0("array_product", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_DOUBLE), - F1("array_filter", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - FN("array_map", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_chunk", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F1("array_combine", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F0("array_key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + FN("array_unique", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + 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), + FN("array_diff", 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), + F0("array_sum", MAY_BE_LONG | MAY_BE_DOUBLE), + F0("array_product", MAY_BE_LONG | MAY_BE_DOUBLE), + F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), + FN("array_map", 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), FN("array_key_first", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING), FN("array_key_last", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_STRING), F1("pos", UNKNOWN_INFO), - F0("sizeof", MAY_BE_NULL | MAY_BE_LONG), - F0("key_exists", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("assert", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("assert_options", MAY_BE_NULL | MAY_BE_FALSE | 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), F0("version_compare", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG), -#if HAVE_FTOK - F0("ftok", MAY_BE_NULL | MAY_BE_LONG), -#endif - F1("str_rot13", MAY_BE_NULL | MAY_BE_STRING), - I1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F1("str_rot13", MAY_BE_STRING), + F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F0("stream_filter_register", MAY_BE_FALSE | MAY_BE_TRUE), - F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), + F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT), F1("stream_bucket_prepend", MAY_BE_FALSE | MAY_BE_OBJECT), F1("stream_bucket_append", MAY_BE_FALSE | MAY_BE_OBJECT), F1("stream_bucket_new", MAY_BE_FALSE | MAY_BE_OBJECT), - F0("output_add_rewrite_var", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("output_reset_rewrite_vars", MAY_BE_FALSE), - I1("sys_get_temp_dir", MAY_BE_STRING), + F1("sys_get_temp_dir", MAY_BE_STRING), /* ext/date */ F0("strtotime", MAY_BE_FALSE | MAY_BE_LONG), - F1("date", MAY_BE_FALSE | MAY_BE_STRING), + F1("date", MAY_BE_STRING), F0("idate", MAY_BE_FALSE | MAY_BE_LONG), - F1("gmdate", MAY_BE_FALSE | MAY_BE_STRING), + F1("gmdate", MAY_BE_STRING), F0("mktime", MAY_BE_FALSE | MAY_BE_LONG), F0("gmmktime", MAY_BE_FALSE | MAY_BE_LONG), - F0("checkdate", MAY_BE_FALSE | MAY_BE_TRUE), F1("strftime", MAY_BE_FALSE | MAY_BE_STRING), F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING), - F0("time", MAY_BE_LONG), - F1("localtime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), - F1("getdate", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_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_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - F1("date_parse_from_format", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), + 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_FALSE | MAY_BE_STRING), + F1("date_format", MAY_BE_STRING), FN("date_modify", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_add", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_sub", MAY_BE_FALSE | MAY_BE_OBJECT), + FN("date_add", MAY_BE_OBJECT), + FN("date_sub", MAY_BE_OBJECT), F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_timezone_set", MAY_BE_FALSE | MAY_BE_OBJECT), - F0("date_offset_get", MAY_BE_FALSE | MAY_BE_LONG), - F1("date_diff", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_time_set", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_date_set", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_isodate_set", MAY_BE_FALSE | MAY_BE_OBJECT), - FN("date_timestamp_set", MAY_BE_FALSE | MAY_BE_OBJECT), + FN("date_timezone_set", MAY_BE_OBJECT), + F1("date_diff", MAY_BE_OBJECT), + FN("date_time_set", MAY_BE_OBJECT), + FN("date_date_set", MAY_BE_OBJECT), + FN("date_isodate_set", MAY_BE_OBJECT), + FN("date_timestamp_set", MAY_BE_OBJECT), F0("date_timestamp_get", MAY_BE_FALSE | MAY_BE_LONG), F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT), - F1("timezone_name_get", MAY_BE_FALSE | MAY_BE_STRING), + F1("timezone_name_get", MAY_BE_STRING), F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING), - F0("timezone_offset_get", MAY_BE_FALSE | MAY_BE_LONG), 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_FALSE | 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_FALSE | MAY_BE_STRING), - F0("date_default_timezone_set", MAY_BE_FALSE | MAY_BE_TRUE), + 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_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG), + 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 */ F0("preg_match", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), @@ -929,15 +669,14 @@ static const func_info_t func_infos[] = { FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), F1("preg_filter", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING), F1("preg_split", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), - FN("preg_quote", MAY_BE_NULL | MAY_BE_STRING), + FN("preg_quote", MAY_BE_STRING), F1("preg_grep", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY), - F0("preg_last_error", MAY_BE_NULL | MAY_BE_LONG), /* ext/mysqli */ F1("mysqli_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F0("mysqli_close", MAY_BE_NULL | MAY_BE_TRUE), - I1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING), - I0("mysqli_connect_errno", MAY_BE_LONG), + F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING), + F0("mysqli_connect_errno", MAY_BE_LONG), F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), F1("mysqli_error_list", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), F1("mysqli_get_links_stats", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), @@ -981,8 +720,8 @@ static const func_info_t func_infos[] = { F0("mysqli_field_count", MAY_BE_NULL | MAY_BE_LONG), F0("mysqli_field_seek", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("mysqli_field_tell", MAY_BE_NULL | MAY_BE_LONG), - I1("mysqli_get_client_info", MAY_BE_STRING), - I0("mysqli_get_client_version", MAY_BE_LONG), + F1("mysqli_get_client_info", MAY_BE_STRING), + F0("mysqli_get_client_version", MAY_BE_LONG), F1("mysqli_get_host_info", MAY_BE_NULL | MAY_BE_STRING), F0("mysqli_get_proto_info", MAY_BE_NULL | MAY_BE_LONG), F1("mysqli_get_server_info", MAY_BE_NULL | MAY_BE_STRING), @@ -1030,7 +769,7 @@ static const func_info_t func_infos[] = { F1("mysqli_stmt_sqlstate", MAY_BE_NULL | MAY_BE_STRING), F1("mysqli_store_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F0("mysqli_thread_id", MAY_BE_NULL | MAY_BE_LONG), - I0("mysqli_thread_safe", MAY_BE_FALSE | MAY_BE_TRUE), + F0("mysqli_thread_safe", MAY_BE_FALSE | MAY_BE_TRUE), F1("mysqli_use_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F0("mysqli_warning_count", MAY_BE_NULL | MAY_BE_LONG), @@ -1038,31 +777,19 @@ static const func_info_t func_infos[] = { F1("curl_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("curl_copy_handle", MAY_BE_NULL | MAY_BE_RESOURCE), F1("curl_version", MAY_BE_NULL | 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), - F0("curl_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("curl_setopt_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), FN("curl_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("curl_getinfo", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("curl_error", MAY_BE_NULL | MAY_BE_STRING), - F0("curl_errno", MAY_BE_NULL | MAY_BE_LONG), - F0("curl_close", MAY_BE_NULL), + 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), - F0("curl_reset", MAY_BE_NULL), F1("curl_escape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("curl_unescape", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("curl_pause", MAY_BE_NULL | MAY_BE_LONG), F1("curl_multi_init", MAY_BE_RESOURCE), - F0("curl_multi_add_handle", MAY_BE_NULL | MAY_BE_LONG), - F0("curl_multi_remove_handle", MAY_BE_NULL | MAY_BE_LONG), - F0("curl_multi_select", MAY_BE_NULL | MAY_BE_LONG), - F0("curl_multi_exec", MAY_BE_NULL | MAY_BE_LONG), FN("curl_multi_getcontent", MAY_BE_NULL | MAY_BE_STRING), F1("curl_multi_info_read", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_RESOURCE), F0("curl_multi_close", MAY_BE_NULL | MAY_BE_FALSE), F0("curl_multi_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - I1("curl_share_init", MAY_BE_RESOURCE), - F0("curl_share_close", MAY_BE_NULL), - F0("curl_share_setopt", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), + F1("curl_share_init", MAY_BE_RESOURCE), F1("curl_file_create", MAY_BE_OBJECT), /* ext/mbstring */ @@ -1094,7 +821,7 @@ static const func_info_t func_infos[] = { F1("mb_strimwidth", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("mb_convert_encoding", MAY_BE_NULL | 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_NULL | MAY_BE_FALSE | MAY_BE_STRING), - I1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("mb_encoding_aliases", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("mb_convert_kana", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("mb_encode_mimeheader", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), @@ -1123,25 +850,9 @@ static const func_info_t func_infos[] = { F0("mb_ereg_search_getpos", MAY_BE_LONG), F0("mb_ereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mbregex_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("mbereg", MAY_BE_FALSE | MAY_BE_LONG), - F0("mberegi", MAY_BE_FALSE | MAY_BE_LONG), - F1("mbereg_replace", MAY_BE_FALSE | MAY_BE_STRING), - F1("mberegi_replace", MAY_BE_FALSE | MAY_BE_STRING), - F1("mbsplit", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F0("mbereg_match", MAY_BE_FALSE | MAY_BE_TRUE), - F0("mbereg_search", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mbereg_search_pos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F1("mbereg_search_regs", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING), - F0("mbereg_search_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("mbereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING), - F0("mbereg_search_getpos", MAY_BE_LONG), - F0("mbereg_search_setpos", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - /* ext/iconv */ F1("iconv", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("iconv_get_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING), - F0("iconv_set_encoding", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("iconv_strlen", MAY_BE_FALSE | MAY_BE_LONG), F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING), F0("iconv_strpos", MAY_BE_FALSE | MAY_BE_LONG), @@ -1153,47 +864,22 @@ static const func_info_t func_infos[] = { /* ext/json */ F1("json_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - I0("json_last_error", MAY_BE_LONG), - I1("json_last_error_msg", MAY_BE_STRING), + F1("json_last_error_msg", MAY_BE_STRING), /* ext/xml */ - FN("xml_parser_create", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("xml_parser_create_ns", MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("xml_set_object", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_element_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_character_data_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_processing_instruction_handler",MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_default_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_unparsed_entity_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_notation_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_external_entity_ref_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_start_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_set_end_namespace_decl_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_parse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("xml_parse_into_struct", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("xml_get_error_code", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), + FN("xml_parser_create", MAY_BE_FALSE | MAY_BE_OBJECT), + FN("xml_parser_create_ns", MAY_BE_FALSE | MAY_BE_OBJECT), F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING), - F0("xml_get_current_line_number", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("xml_get_current_column_number", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("xml_get_current_byte_index", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("xml_parser_free", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("xml_parser_set_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("xml_parser_get_option", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING), - F1("utf8_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("utf8_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), + F1("utf8_encode", MAY_BE_STRING), + F1("utf8_decode", MAY_BE_STRING), /* ext/zlib */ F0("readgzfile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("gzrewind", MAY_BE_FALSE | MAY_BE_TRUE), - F0("gzclose", MAY_BE_FALSE | MAY_BE_TRUE), - F0("gzeof", MAY_BE_FALSE | MAY_BE_TRUE), F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING), F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING), - F1("gzgetss", 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), - F0("gzpassthru", MAY_BE_FALSE | MAY_BE_LONG), - F0("gzseek", MAY_BE_FALSE | MAY_BE_LONG), F0("gztell", MAY_BE_FALSE | MAY_BE_LONG), F0("gzwrite", MAY_BE_FALSE | MAY_BE_LONG), F0("gzputs", MAY_BE_FALSE | MAY_BE_LONG), @@ -1206,21 +892,17 @@ static const func_info_t func_infos[] = { F1("gzdecode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("zlib_encode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("zlib_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - I1("zlib_get_coding_type", 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_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("hash_equals", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("hash_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("hash_hmac", MAY_BE_NULL | 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_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("hash_hkdf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("hash_init", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), - F0("hash_update", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("hash_update_stream", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("hash_update_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("hash_final", MAY_BE_NULL | MAY_BE_STRING), F1("hash_copy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_OBJECT), F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), @@ -1228,7 +910,7 @@ static const func_info_t func_infos[] = { F1("mhash_keygen_s2k", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F0("mhash_get_block_size", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F1("mhash_get_hash_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - I0("mhash_count", MAY_BE_LONG), + F0("mhash_count", MAY_BE_LONG), F1("mhash", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), /* ext/sodium */ @@ -1310,28 +992,17 @@ static const func_info_t func_infos[] = { F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING), /* ext/session */ - F0("session_set_cookie_params", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - I1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), + F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("session_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("session_module_name", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("session_set_save_handler", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("session_save_path", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), FN("session_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F0("session_regenerate_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("session_create_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("session_cache_limiter", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F0("session_cache_expire", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - I1("session_encode", MAY_BE_FALSE | MAY_BE_STRING), - F0("session_decode", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("session_start", MAY_BE_FALSE | MAY_BE_TRUE), - I0("session_destroy", MAY_BE_FALSE | MAY_BE_TRUE), - I0("session_unset", MAY_BE_FALSE | MAY_BE_TRUE), + F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING), F0("session_gc", MAY_BE_FALSE | MAY_BE_LONG), - F0("session_write_close", MAY_BE_FALSE | MAY_BE_TRUE), F0("session_abort", MAY_BE_FALSE | MAY_BE_TRUE), - F0("session_reset", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("session_status", MAY_BE_NULL | MAY_BE_LONG), - I0("session_register_shutdown", MAY_BE_NULL), /* ext/pgsql */ F1("pg_connect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), @@ -1423,16 +1094,14 @@ static const func_info_t func_infos[] = { F1("pg_select", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING), /* ext/bcmath */ - F1("bcadd", MAY_BE_NULL | MAY_BE_STRING), - F1("bcsub", MAY_BE_NULL | MAY_BE_STRING), - F1("bcmul", MAY_BE_NULL | MAY_BE_STRING), + F1("bcadd", MAY_BE_STRING), + F1("bcsub", MAY_BE_STRING), + F1("bcmul", MAY_BE_STRING), F1("bcdiv", MAY_BE_NULL | MAY_BE_STRING), F1("bcmod", MAY_BE_NULL | MAY_BE_STRING), F1("bcpowmod", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), - F1("bcpow", MAY_BE_NULL | MAY_BE_STRING), + F1("bcpow", MAY_BE_STRING), F1("bcsqrt", MAY_BE_NULL | MAY_BE_STRING), - F0("bccomp", MAY_BE_NULL | MAY_BE_LONG), - F0("bcscale", MAY_BE_NULL | MAY_BE_LONG), /* ext/exif */ F1("exif_tagname", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), @@ -1446,7 +1115,7 @@ static const func_info_t func_infos[] = { 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), - I1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), + F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F0("filter_id", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), /* ext/gettext */ @@ -1466,23 +1135,8 @@ static const func_info_t func_infos[] = { F1("bind_textdomain_codeset", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), #endif - /* ext/ctype */ - F0("ctype_alnum", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_alpha", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_cntrl", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_digit", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_lower", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_graph", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_print", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_punct", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_space", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_upper", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("ctype_xdigit", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - /* ext/fileinfo */ F1("finfo_open", MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("finfo_close", MAY_BE_FALSE | MAY_BE_TRUE), - F0("finfo_set_flags", MAY_BE_FALSE | MAY_BE_TRUE), F1("finfo_file", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("finfo_buffer", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), F1("mime_content_type", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), @@ -1490,43 +1144,24 @@ static const func_info_t func_infos[] = { /* 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), F0("imageloadfont", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagesetstyle", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("imagecreatetruecolor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("imageistruecolor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagetruecolortopalette", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagepalettetotruecolor", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecolormatch", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagesetthickness", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilledellipse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilledarc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagealphablending", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagesavealpha", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagelayereffect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("imagecolorallocatealpha", MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorresolvealpha", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorclosestalpha", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorexactalpha", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagecopyresampled", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), #ifdef PHP_WIN32 F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_RESOURCE), #endif F1("imagerotate", MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("imagesettile", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagesetbrush", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("imagecreate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - I0("imagetypes", MAY_BE_LONG), F1("imagecreatefromstring", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imagecreatefromgif", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), #ifdef HAVE_GD_JPG F1("imagecreatefromjpeg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("imagejpeg", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("jpeg2wbmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), #endif #ifdef HAVE_GD_PNG F1("imagecreatefrompng", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("imagepng", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("png2wbmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), #endif #ifdef HAVE_GD_WEBP F1("imagecreatefromwebp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), @@ -1542,70 +1177,26 @@ static const func_info_t func_infos[] = { F1("imagecreatefromgd2part", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), #if defined(HAVE_GD_BMP) F1("imagecreatefrombmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), - F0("imagebmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), #endif - F0("imagexbm", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagegif", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagewbmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagegd", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagegd2", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagedestroy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("imagecolorallocate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagepalettecopy", MAY_BE_NULL | MAY_BE_FALSE), F0("imagecolorat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorclosest", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorclosesthwb", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagecolordeallocate", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F0("imagecolorresolve", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorexact", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE), F1("imagecolorsforindex", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), - F0("imagegammacorrect", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagesetpixel", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageline", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagedashedline", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagerectangle", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilledrectangle", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagearc", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageellipse", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilltoborder", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefill", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecolorstotal", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagecolortransparent", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imageinterlace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagepolygon", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageopenpolygon", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilledpolygon", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefontwidth", MAY_BE_NULL | MAY_BE_LONG), - F0("imagefontheight", MAY_BE_NULL | MAY_BE_LONG), - F0("imagechar", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecharup", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagestring", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagestringup", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecopy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecopymerge", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecopymergegray", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagecopyresized", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagesx", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagesy", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG), - F0("imagesetclip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F1("imagegetclip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), + F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), F1("imageftbbox", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), F1("imagefttext", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), F1("imagettfbbox", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), F1("imagettftext", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), - F0("image2wbmp", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imagefilter", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageconvolution", MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageflip", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), - F0("imageantialias", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("imagecrop", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imagecropauto", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imagescale", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imageaffine", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE), F1("imageaffinematrixget", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), F1("imageaffinematrixconcat", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE), - F0("imagesetinterpolation", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE), F1("imageresolution", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG), /* ext/spl */ @@ -1631,31 +1222,33 @@ uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa if (callee_func->type == ZEND_INTERNAL_FUNCTION) { zval *zv; - func_info_t *info; + 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)); - zv = zend_hash_find_ex(&func_info, Z_STR_P(CRT_CONSTANT_EX(call_info->caller_op_array, call_info->caller_init_opline, call_info->caller_init_opline->op2, ssa->rt_constants)), 1); + zv = zend_hash_find_ex(&func_info, lcname, 1); if (zv) { - info = Z_PTR_P(zv); + func_info_t *info = Z_PTR_P(zv); if (UNEXPECTED(zend_optimizer_is_disabled_func(info->name, info->name_len))) { ret = MAY_BE_NULL; } else if (info->info_func) { ret = info->info_func(call_info, ssa); - } else if (/*callee_func->common.arg_info && */ - callee_func->common.num_args == 0 && - callee_func->common.required_num_args == 0 && - !(callee_func->common.fn_flags & ZEND_ACC_VARIADIC)) { - if (call_info->num_args == 0) { - ret = info->info; - } else { - ret = FUNC_MAY_WARN | MAY_BE_NULL; - } } else { ret = info->info; } -#if 0 + return ret; + } + + if (callee_func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + zend_class_entry *ce; // TODO: Use the CE. + ret = zend_fetch_arg_info_type(NULL, callee_func->common.arg_info - 1, &ce); } 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; } } else { // FIXME: the order of functions matters!!! @@ -1663,18 +1256,14 @@ uint32_t zend_get_func_info(const zend_call_info *call_info, const zend_ssa *ssa if (info) { ret = info->return_info.type; } - } - if (!ret) { - ret = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - if (callee_func->type == ZEND_INTERNAL_FUNCTION) { - ret |= FUNC_MAY_WARN; - } - if (callee_func->common.fn_flags & ZEND_ACC_GENERATOR) { - ret = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_OBJECT; - } else if (callee_func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { - ret |= MAY_BE_REF; - } else { - ret |= MAY_BE_RC1 | MAY_BE_RCN; + 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; diff --git a/ext/opcache/Optimizer/zend_func_info.h b/ext/opcache/Optimizer/zend_func_info.h index 7eeb363da5..0f4fcea092 100644 --- a/ext/opcache/Optimizer/zend_func_info.h +++ b/ext/opcache/Optimizer/zend_func_info.h @@ -34,11 +34,6 @@ #define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10) #define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11) -/* The following flags are valid only for return values of internal functions - * returned by zend_get_func_info() - */ -#define FUNC_MAY_WARN (1<<30) - typedef struct _zend_func_info zend_func_info; typedef struct _zend_call_info zend_call_info; diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index f89b133b92..51872dcc61 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -1419,18 +1419,22 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int return 1; } else if (op_array->arg_info && opline->op1.num <= op_array->num_args) { - if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == IS_LONG) { - tmp->underflow = 0; - tmp->min = ZEND_LONG_MIN; - tmp->max = ZEND_LONG_MAX; - tmp->overflow = 0; - return 1; - } else if (ZEND_TYPE_CODE(op_array->arg_info[opline->op1.num-1].type) == _IS_BOOL) { - tmp->underflow = 0; - tmp->min = 0; - tmp->max = 1; - tmp->overflow = 0; - return 1; + zend_type type = op_array->arg_info[opline->op1.num-1].type; + if (ZEND_TYPE_IS_MASK(type)) { + uint32_t mask = ZEND_TYPE_MASK(ZEND_TYPE_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_FALSE|MAY_BE_TRUE)) { + tmp->underflow = 0; + tmp->min = 0; + tmp->max = 1; + tmp->overflow = 0; + return 1; + } } } } @@ -1833,9 +1837,7 @@ static int zend_infer_ranges(const zend_op_array *op_array, zend_ssa *ssa) /* {{ /* }}} */ static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) { - if (alias == PHP_ERRORMSG_ALIAS) { - return MAY_BE_STRING | MAY_BE_RC1 | MAY_BE_RCN; - } else if (alias == HTTP_RESPONSE_HEADER_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; @@ -2063,16 +2065,10 @@ uint32_t zend_array_element_type(uint32_t t1, int write, int insert) } if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { tmp |= MAY_BE_NULL; - if (t1 & MAY_BE_ERROR) { - if (write) { - tmp |= MAY_BE_ERROR; - } - } } if (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_NULL; - if (write) { - tmp |= MAY_BE_ERROR; + if (!write) { + tmp |= MAY_BE_NULL; } } return tmp; @@ -2234,25 +2230,26 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_ return NULL; } -static uint32_t zend_convert_type_code_to_may_be(zend_uchar type_code) { - switch (type_code) { - case IS_VOID: - return MAY_BE_NULL; - case IS_CALLABLE: - return MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case IS_ITERABLE: - return MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case IS_ARRAY: - return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - case _IS_BOOL: - return MAY_BE_TRUE|MAY_BE_FALSE; - default: - ZEND_ASSERT(type_code < IS_REFERENCE); - return 1 << type_code; +static uint32_t zend_convert_type_declaration_mask(uint32_t type_mask) { + if (type_mask & MAY_BE_VOID) { + type_mask &= ~MAY_BE_VOID; + type_mask |= MAY_BE_NULL; + } + if (type_mask & MAY_BE_CALLABLE) { + type_mask &= ~MAY_BE_CALLABLE; + type_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) { + type_mask &= ~MAY_BE_ITERABLE; + type_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_ARRAY) { + type_mask |= MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } + return type_mask; } -static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) +uint32_t zend_fetch_arg_info_type(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) { uint32_t tmp = 0; @@ -2263,14 +2260,17 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar tmp |= MAY_BE_OBJECT; *pce = get_class_entry(script, lcname); zend_string_release_ex(lcname, 0); - } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - tmp |= zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(arg_info->type)); + } else if (ZEND_TYPE_IS_MASK(arg_info->type)) { + tmp |= zend_convert_type_declaration_mask(ZEND_TYPE_MASK(arg_info->type)); } else { tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } if (ZEND_TYPE_ALLOW_NULL(arg_info->type)) { tmp |= MAY_BE_NULL; } + if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { + tmp |= MAY_BE_RC1 | MAY_BE_RCN; + } return tmp; } @@ -2314,7 +2314,7 @@ static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, z } if (ce) { prop_info = lookup_prop_info(ce, - Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)), + Z_STR_P(CRT_CONSTANT(opline->op2)), op_array->scope); if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) { prop_info = NULL; @@ -2345,12 +2345,12 @@ static zend_property_info *zend_fetch_static_prop_info(const zend_script *script break; } } else if (opline->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants); + zval *zv = CRT_CONSTANT(opline->op2); ce = get_class_entry(script, Z_STR_P(zv + 1)); } if (ce) { - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants); + 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; @@ -2365,7 +2365,7 @@ static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_in if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { uint32_t type = ZEND_TYPE_IS_CLASS(prop_info->type) ? MAY_BE_OBJECT - : zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(prop_info->type)); + : zend_convert_type_declaration_mask(ZEND_TYPE_MASK(prop_info->type)); if (ZEND_TYPE_ALLOW_NULL(prop_info->type)) { type |= MAY_BE_NULL; @@ -2419,8 +2419,8 @@ static int zend_update_type_info(const zend_op_array *op_array, /* 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|MAY_BE_ERROR)) - || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS|MAY_BE_ERROR))) { + 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_ops[i].result_def >= 0) { UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); @@ -2711,9 +2711,6 @@ static int zend_update_type_info(const zend_op_array *op_array, tmp |= MAY_BE_LONG; } } else { - if (t1 & MAY_BE_ERROR) { - tmp |= MAY_BE_NULL; - } if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) { if (opline->opcode == ZEND_PRE_INC) { tmp |= MAY_BE_LONG; @@ -2746,7 +2743,7 @@ static int zend_update_type_info(const zend_op_array *op_array, if (t1 & (MAY_BE_RC1|MAY_BE_RCN)) { tmp |= MAY_BE_RC1|MAY_BE_RCN; } - tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_REF|MAY_BE_RCN); + tmp |= t1 & ~(MAY_BE_UNDEF|MAY_BE_REF|MAY_BE_RCN); if (t1 & MAY_BE_UNDEF) { tmp |= MAY_BE_NULL; } @@ -2773,9 +2770,6 @@ static int zend_update_type_info(const zend_op_array *op_array, tmp |= MAY_BE_LONG; } } else { - if (t1 & MAY_BE_ERROR) { - tmp |= MAY_BE_NULL; - } if (t1 & (MAY_BE_UNDEF | MAY_BE_NULL)) { if (opline->opcode == ZEND_POST_INC) { tmp |= MAY_BE_LONG; @@ -2953,7 +2947,7 @@ static int zend_update_type_info(const zend_op_array *op_array, 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_ERROR|MAY_BE_RC1|MAY_BE_RCN); + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); } if (t2 & MAY_BE_UNDEF) { tmp |= MAY_BE_NULL; @@ -2981,7 +2975,7 @@ static int zend_update_type_info(const zend_op_array *op_array, 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_ERROR|MAY_BE_RC1|MAY_BE_RCN); + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); } if (t2 & MAY_BE_UNDEF) { tmp |= MAY_BE_NULL; @@ -3102,16 +3096,9 @@ static int zend_update_type_info(const zend_op_array *op_array, ce = NULL; if (arg_info) { - tmp = zend_fetch_arg_info(script, arg_info, &ce); - if (opline->opcode == ZEND_RECV_INIT && - Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)) == IS_CONSTANT_AST) { - /* The constant may resolve to NULL */ - tmp |= MAY_BE_NULL; - } + tmp = zend_fetch_arg_info_type(script, arg_info, &ce); if (arg_info->pass_by_reference) { tmp |= MAY_BE_REF; - } else if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1|MAY_BE_RCN; } } else { 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; @@ -3149,7 +3136,7 @@ static int zend_update_type_info(const zend_op_array *op_array, } case ZEND_DECLARE_ANON_CLASS: UPDATE_SSA_TYPE(MAY_BE_CLASS, ssa_ops[i].result_def); - if (script && (ce = zend_hash_find_ptr(&script->class_table, Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants)))) != NULL) { + 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_ops[i].result_def); } break; @@ -3177,7 +3164,7 @@ static int zend_update_type_info(const zend_op_array *op_array, break; } } else if (opline->op2_type == IS_CONST) { - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants); + 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_ops[i].result_def); @@ -3191,7 +3178,7 @@ static int zend_update_type_info(const zend_op_array *op_array, 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_EX(op_array, opline, opline->op1, ssa->rt_constants)+1))) != NULL) { + (ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1))) != NULL) { UPDATE_SSA_OBJ_TYPE(ce, 0, ssa_ops[i].result_def); } else if ((t1 & MAY_BE_CLASS) && ssa_ops[i].op1_use >= 0 && ssa_var_info[ssa_ops[i].op1_use].ce) { UPDATE_SSA_OBJ_TYPE(ssa_var_info[ssa_ops[i].op1_use].ce, ssa_var_info[ssa_ops[i].op1_use].is_instanceof, ssa_ops[i].result_def); @@ -3362,6 +3349,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_FETCH_LIST_R: case ZEND_FETCH_LIST_W: if (ssa_ops[i].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 || @@ -3383,20 +3371,20 @@ static int zend_update_type_info(const zend_op_array *op_array, tmp |= t1 & (MAY_BE_RC1|MAY_BE_RCN); } if (opline->op2_type == IS_UNUSED) { - tmp |= MAY_BE_ARRAY_KEY_LONG; + 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)) { - tmp |= MAY_BE_ARRAY_KEY_LONG; + key_type |= MAY_BE_ARRAY_KEY_LONG; } if (t2 & MAY_BE_STRING) { - tmp |= MAY_BE_ARRAY_KEY_STRING; + key_type |= MAY_BE_ARRAY_KEY_STRING; if (opline->op2_type != IS_CONST) { // FIXME: numeric string - tmp |= MAY_BE_ARRAY_KEY_LONG; + key_type |= MAY_BE_ARRAY_KEY_LONG; } } if (t2 & (MAY_BE_UNDEF | MAY_BE_NULL)) { - tmp |= MAY_BE_ARRAY_KEY_STRING; + key_type |= MAY_BE_ARRAY_KEY_STRING; } } } else if (opline->opcode == ZEND_FETCH_DIM_UNSET) { @@ -3420,19 +3408,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_FETCH_LIST_W: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_DIM_OP: - tmp |= MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY; - 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: - tmp |= MAY_BE_ARRAY_OF_OBJECT; + tmp |= key_type | MAY_BE_ARRAY | MAY_BE_ARRAY_OF_ARRAY; break; case ZEND_SEND_VAR_EX: case ZEND_SEND_FUNC_ARG: @@ -3447,7 +3423,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_VERIFY_RETURN_TYPE: case ZEND_MAKE_REF: case ZEND_FE_RESET_RW: - tmp |= MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + tmp |= key_type | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; break; case ZEND_PRE_INC: case ZEND_PRE_DEC: @@ -3455,11 +3431,24 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_POST_DEC: if (tmp & MAY_BE_ARRAY_OF_LONG) { /* may overflow */ - tmp |= MAY_BE_ARRAY_OF_DOUBLE; + tmp |= key_type | MAY_BE_ARRAY_OF_DOUBLE; } else if (!(tmp & (MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE))) { - 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. */ @@ -3483,18 +3472,7 @@ static int zend_update_type_info(const zend_op_array *op_array, opline->opcode != ZEND_FETCH_LIST_R ? t1 : ((t1 & ~MAY_BE_STRING) | MAY_BE_NULL), opline->result_type == IS_VAR, opline->op2_type == IS_UNUSED); - 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_ERROR|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE|MAY_BE_OBJECT)) { - tmp |= MAY_BE_ERROR; - } else if (opline->op2_type == IS_UNUSED) { - tmp |= MAY_BE_ERROR; - } else if (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) { - tmp |= MAY_BE_ERROR; - } - } else if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) { + if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) { tmp |= MAY_BE_NULL; } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); @@ -3509,26 +3487,11 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_UNSET: case ZEND_FETCH_OBJ_FUNC_ARG: - if (ssa_ops[i].op1_def >= 0) { - tmp = t1; - if (opline->opcode == ZEND_FETCH_OBJ_W || - opline->opcode == ZEND_FETCH_OBJ_RW || - opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) { - if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) { - if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { - tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE); - tmp |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN; - } - } - } - UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); - COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); - } if (ssa_ops[i].result_def >= 0) { tmp = zend_fetch_prop_type(script, zend_fetch_prop_info(op_array, ssa, opline, i), &ce); if (opline->result_type != IS_TMP_VAR) { - tmp |= MAY_BE_REF | MAY_BE_ERROR; + tmp |= MAY_BE_REF; } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); if (ce) { @@ -3545,7 +3508,7 @@ static int zend_update_type_info(const zend_op_array *op_array, 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_ERROR; + tmp |= MAY_BE_REF; } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); if (ce) { @@ -3567,7 +3530,7 @@ static int zend_update_type_info(const zend_op_array *op_array, if (!call_info) { goto unknown_opcode; } - tmp = zend_get_func_info(call_info, ssa) & ~FUNC_MAY_WARN; + tmp = zend_get_func_info(call_info, ssa); UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); if (call_info->callee_func->type == ZEND_USER_FUNCTION) { func_info = ZEND_FUNC_INFO(&call_info->callee_func->op_array); @@ -3615,11 +3578,7 @@ static int zend_update_type_info(const zend_op_array *op_array, ce = NULL; } else { zend_arg_info *ret_info = op_array->arg_info - 1; - - tmp = zend_fetch_arg_info(script, ret_info, &ce); - if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } + 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_ops[i].op1_def); @@ -3886,7 +3845,7 @@ static zend_bool can_convert_to_double( 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_EX(op_array, opline, opline->op1, ssa->rt_constants); + 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); @@ -3899,7 +3858,7 @@ static zend_bool can_convert_to_double( 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_EX(op_array, opline, opline->op2, ssa->rt_constants); + 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); @@ -3987,7 +3946,7 @@ static int zend_type_narrowing(const zend_op_array *op_array, const zend_script * 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_EX(op_array, opline, opline->op2, ssa->rt_constants); + zval *value = CRT_CONSTANT(opline->op2); zend_bitset_clear(visited, bitset_len); if (can_convert_to_double(op_array, ssa, v, value, visited)) { @@ -4046,11 +4005,9 @@ void zend_init_func_return_info(const zend_op_array *op_array, 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(script, ret_info, &ret->ce); + 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; - } else if (ret->type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { - ret->type |= MAY_BE_RC1|MAY_BE_RCN; } ret->is_instanceof = (ret->ce) ? 1 : 0; ret->range = tmp_range; @@ -4141,7 +4098,7 @@ void zend_func_return_info(const zend_op_array *op_array, } if (opline->op1_type == IS_CONST) { - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op1, info->ssa.rt_constants); + zval *zv = CRT_CONSTANT(opline->op1); if (Z_TYPE_P(zv) == IS_NULL) { if (tmp_has_range < 0) { @@ -4363,7 +4320,7 @@ void zend_inference_check_recursive_dependencies(zend_op_array *op_array) free_alloca(worklist, use_heap); } -int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa) +int zend_may_throw(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa) { uint32_t t1 = OP1_INFO(); uint32_t t2 = OP2_INFO(); @@ -4620,7 +4577,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa if (opline->op2_type == IS_CONST) { prop_info = zend_hash_find_ptr(&ce->properties_info, - Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants))); + Z_STR_P(CRT_CONSTANT(opline->op2))); if (prop_info && !(prop_info->flags & ZEND_ACC_PUBLIC)) { return 1; } @@ -4650,7 +4607,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa case ZEND_COUNT: return (t1 & MAY_BE_ANY) != MAY_BE_ARRAY; case ZEND_RECV_INIT: - if (Z_TYPE_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)) == IS_CONSTANT_AST) { + if (Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_CONSTANT_AST) { return 1; } if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) { diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index ec98fcbef9..f01f6cc786 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -26,11 +26,11 @@ /* Bitmask for type inference (zend_ssa_var_info.type) */ #include "zend_type_info.h" -#define MAY_BE_IN_REG (1<<25) /* value allocated in CPU register */ +#define MAY_BE_IN_REG (1<<29) /* value allocated in CPU register */ //TODO: remome MAY_BE_RC1, MAY_BE_RCN??? -#define MAY_BE_RC1 (1<<27) /* may be non-reference with refcount == 1 */ -#define MAY_BE_RCN (1<<28) /* may be non-reference with refcount > 1 */ +#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 \ @@ -40,7 +40,7 @@ static zend_always_inline zend_bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants); \ + 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 && \ @@ -56,7 +56,7 @@ static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants); \ + 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) { \ @@ -80,7 +80,7 @@ static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants); \ + 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) { \ @@ -104,7 +104,7 @@ static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants); \ + 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; \ } \ @@ -122,7 +122,7 @@ static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants); \ + 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; \ } \ @@ -199,7 +199,7 @@ static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa 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_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ERROR; + 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; } } @@ -207,7 +207,7 @@ static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline) \ { \ if (opline->opN##_type == IS_CONST) { \ - return _const_op_type(CRT_CONSTANT_EX(op_array, opline, opline->opN, ssa->rt_constants)); \ + return _const_op_type(CRT_CONSTANT(opline->opN)); \ } else { \ return get_ssa_var_info(ssa, ssa->ops ? ssa->ops[opline - op_array->opcodes].opN##_use : -1); \ } \ @@ -263,6 +263,8 @@ void zend_inference_check_recursive_dependencies(zend_op_array *op_array); int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level); +uint32_t zend_fetch_arg_info_type( + const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce); void zend_init_func_return_info(const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret); @@ -272,7 +274,7 @@ void zend_func_return_info(const zend_op_array *op_array, int widening, zend_ssa_var_info *ret); -int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa); +int zend_may_throw(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa); END_EXTERN_C() diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index a922d0f597..aa638032ca 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -32,10 +32,6 @@ #include "zend_inference.h" #include "zend_dump.h" -#ifndef HAVE_DFA_PASS -# define HAVE_DFA_PASS 1 -#endif - static void zend_optimizer_zval_dtor_wrapper(zval *zvalue) { zval_ptr_dtor_nogc(zvalue); @@ -657,8 +653,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, case ZEND_VERIFY_RETURN_TYPE: { zend_arg_info *ret_info = op_array->arg_info - 1; if (ZEND_TYPE_IS_CLASS(ret_info->type) - || ZEND_TYPE_CODE(ret_info->type) == IS_CALLABLE - || !ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(ret_info->type), Z_TYPE_P(val)) + || (ZEND_TYPE_IS_MASK(ret_info->type) + && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))) || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { return 0; } @@ -777,9 +773,9 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_ } static zend_class_entry *get_class_entry_from_op1( - zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) { + zend_script *script, zend_op_array *op_array, zend_op *opline) { if (opline->op1_type == IS_CONST) { - zval *op1 = CRT_CONSTANT_EX(op_array, opline, opline->op1, rt_constants); + 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; @@ -804,13 +800,12 @@ static zend_class_entry *get_class_entry_from_op1( } zend_function *zend_optimizer_get_called_func( - zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) + zend_script *script, zend_op_array *op_array, zend_op *opline) { -#define GET_OP(op) CRT_CONSTANT_EX(op_array, opline, opline->op, rt_constants) switch (opline->opcode) { case ZEND_INIT_FCALL: { - zend_string *function_name = Z_STR_P(GET_OP(op2)); + 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; @@ -827,8 +822,8 @@ zend_function *zend_optimizer_get_called_func( } case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: - if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) { - zval *function_name = GET_OP(op2) + 1; + 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; @@ -844,11 +839,11 @@ zend_function *zend_optimizer_get_called_func( } break; case ZEND_INIT_STATIC_METHOD_CALL: - if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) { + 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, rt_constants); + script, op_array, opline); if (ce) { - zend_string *func_name = Z_STR_P(GET_OP(op2) + 1); + 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) { zend_bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0; @@ -862,9 +857,9 @@ zend_function *zend_optimizer_get_called_func( break; case ZEND_INIT_METHOD_CALL: if (opline->op1_type == IS_UNUSED - && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING + && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) { - zend_string *method_name = Z_STR_P(GET_OP(op2) + 1); + 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) { @@ -881,7 +876,7 @@ zend_function *zend_optimizer_get_called_func( case ZEND_NEW: { zend_class_entry *ce = get_class_entry_from_op1( - script, op_array, opline, rt_constants); + script, op_array, opline); if (ce && ce->type == ZEND_USER_CLASS) { return ce->constructor; } @@ -889,7 +884,6 @@ zend_function *zend_optimizer_get_called_func( } } return NULL; -#undef GET_OP } uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) { @@ -897,14 +891,8 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) 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, "parse_str") && num_args <= 1) { - return ZEND_FUNC_INDIRECT_VAR_ACCESS; - } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) { - 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, "assert")) { - 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")) { @@ -940,12 +928,13 @@ static void zend_optimize(zend_op_array *op_array, zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); } - /* pass 1 - * - substitute persistent constants (true, false, null, etc) - * - perform compile-time evaluation of constant binary and unary operations - * - optimize series of ADD_STRING and/or ADD_CHAR - * - convert CAST(IS_BOOL,x) into BOOL(x) - * - pre-evaluate constant function calls + /* 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); @@ -954,21 +943,8 @@ static void zend_optimize(zend_op_array *op_array, } } - /* pass 2: - * - convert non-numeric constants to numeric constants in numeric operators - * - optimize constant conditional JMPs - */ - if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) { - zend_optimizer_pass2(op_array); - if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) { - zend_dump_op_array(op_array, 0, "after pass 2", NULL); - } - } - - /* pass 3: - * - optimize $i = $i+expr to $i+=expr + /* pass 3: (Jump optimization) * - optimize series of JMPs - * - change $i++ to ++$i where possible */ if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) { zend_optimizer_pass3(op_array, ctx); @@ -997,7 +973,6 @@ static void zend_optimize(zend_op_array *op_array, } } -#if HAVE_DFA_PASS /* pass 6: * - DFA optimization */ @@ -1008,7 +983,6 @@ static void zend_optimize(zend_op_array *op_array, zend_dump_op_array(op_array, 0, "after pass 6", NULL); } } -#endif /* pass 9: * - Optimize temp variables usage @@ -1064,6 +1038,8 @@ 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) { @@ -1073,6 +1049,8 @@ static void zend_revert_pass_two(zend_op_array *op_array) 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 @@ -1082,6 +1060,8 @@ static void zend_revert_pass_two(zend_op_array *op_array) 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) @@ -1091,6 +1071,8 @@ static void zend_redo_pass_two(zend_op_array *op_array) 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, @@ -1117,48 +1099,79 @@ static void zend_redo_pass_two(zend_op_array *op_array) 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 - if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { - /* fix jumps to point to new array */ - switch (opline->opcode) { - 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: + 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: + 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_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: + /* 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_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; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - /* relative extended_value don't have to be changed */ - break; - } + } + break; } -#endif ZEND_VM_SET_OPCODE_HANDLER(opline); opline++; } + + op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; } -#if HAVE_DFA_PASS static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) { zend_op *opline, *end; @@ -1166,6 +1179,8 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) 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, @@ -1203,47 +1218,78 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa) ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2); } - zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info); + /* fix jumps to point to new array */ + switch (opline->opcode) { #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR - if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { - /* fix jumps to point to new array */ - switch (opline->opcode) { - 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: + 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: + 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_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: + /* 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_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; - case ZEND_FE_FETCH_R: - case ZEND_FE_FETCH_RW: - case ZEND_SWITCH_LONG: - case ZEND_SWITCH_STRING: - /* relative extended_value don't have to be changed */ - break; - } + } + break; } -#endif + zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info); opline++; } + + op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; } -#endif static void zend_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx) @@ -1258,12 +1304,6 @@ static void zend_optimize_op_array(zend_op_array *op_array, zend_redo_pass_two(op_array); if (op_array->live_range) { -#if HAVE_DFA_PASS - if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) && - (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) { - return; - } -#endif zend_recalc_live_ranges(op_array, NULL); } } @@ -1288,7 +1328,6 @@ static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer } } -#if HAVE_DFA_PASS static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) { zend_func_info *func_info = ZEND_FUNC_INFO(op_array); @@ -1316,7 +1355,6 @@ static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) } return 1; } -#endif int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) { @@ -1325,9 +1363,7 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend zend_op_array *op_array; zend_string *name; zend_optimizer_ctx ctx; -#if HAVE_DFA_PASS zend_call_graph call_graph; -#endif ctx.arena = zend_arena_create(64 * 1024); ctx.script = script; @@ -1335,38 +1371,20 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend ctx.optimization_level = optimization_level; ctx.debug_level = debug_level; - zend_optimize_op_array(&script->main_op_array, &ctx); - - ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - zend_optimize_op_array(op_array, &ctx); - } 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_STR_KEY_PTR(&ce->function_table, name, op_array) { - if (op_array->scope == ce - && op_array->type == ZEND_USER_FUNCTION - && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - zend_optimize_op_array(op_array, &ctx); - } - } ZEND_HASH_FOREACH_END(); - } ZEND_HASH_FOREACH_END(); - -#if HAVE_DFA_PASS if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) && (ZEND_OPTIMIZER_PASS_7 & optimization_level) && - zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) { + zend_build_call_graph(&ctx.arena, script, &call_graph) == SUCCESS) { /* Optimize using call-graph */ - void *checkpoint = zend_arena_checkpoint(ctx.arena); 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) { @@ -1445,16 +1463,11 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend for (i = 0; i < call_graph.op_arrays_count; i++) { ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); } - - zend_arena_release(&ctx.arena, checkpoint); - } else -#endif - - if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { - zend_adjust_fcall_stack_size(&script->main_op_array, &ctx); + } else { + zend_optimize_op_array(&script->main_op_array, &ctx); ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - zend_adjust_fcall_stack_size(op_array, &ctx); + zend_optimize_op_array(op_array, &ctx); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { @@ -1465,10 +1478,31 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend if (op_array->scope == ce && op_array->type == ZEND_USER_FUNCTION && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - zend_adjust_fcall_stack_size(op_array, &ctx); + zend_optimize_op_array(op_array, &ctx); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); + + if (ZEND_OPTIMIZER_PASS_12 & optimization_level) { + zend_adjust_fcall_stack_size(&script->main_op_array, &ctx); + + ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { + zend_adjust_fcall_stack_size(op_array, &ctx); + } 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_STR_KEY_PTR(&ce->function_table, name, op_array) { + if (op_array->scope == ce + && op_array->type == ZEND_USER_FUNCTION + && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + zend_adjust_fcall_stack_size(op_array, &ctx); + } + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + } } ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { @@ -1498,11 +1532,11 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { zend_dump_op_array(&script->main_op_array, - ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); + ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { zend_dump_op_array(op_array, - ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); + ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { @@ -1511,7 +1545,7 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend && op_array->type == ZEND_USER_FUNCTION && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_dump_op_array(op_array, - ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); + ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h index 2841d018a5..04528d33e2 100644 --- a/ext/opcache/Optimizer/zend_optimizer.h +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -25,9 +25,9 @@ #include "zend.h" #include "zend_compile.h" -#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */ -#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jumps */ -#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */ +#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 */ diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 9ab18f6398..43ac1ea07f 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -93,7 +93,6 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, 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_pass2(zend_op_array *op_array); 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); @@ -106,7 +105,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx void zend_optimizer_compact_vars(zend_op_array *op_array); int zend_optimizer_is_disabled_func(const char *name, size_t len); zend_function *zend_optimizer_get_called_func( - zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants); + zend_script *script, zend_op_array *op_array, zend_op *opline); 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); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 9d3b81ca83..99d577dd2c 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -192,14 +192,14 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f } } 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, (build_flags & ZEND_RT_CONSTANTS)); + 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, (build_flags & ZEND_RT_CONSTANTS)); + 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); @@ -208,7 +208,7 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f } } 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, (build_flags & ZEND_RT_CONSTANTS)); + 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); @@ -293,7 +293,7 @@ static void place_essa_pis( } 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, (build_flags & ZEND_RT_CONSTANTS)); + 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); @@ -315,9 +315,9 @@ static void place_essa_pis( } 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, (build_flags & ZEND_RT_CONSTANTS)); + 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, (build_flags & ZEND_RT_CONSTANTS))); + 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) { @@ -463,10 +463,10 @@ static void place_essa_pis( 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, (build_flags & ZEND_RT_CONSTANTS)); + 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, (build_flags & ZEND_RT_CONSTANTS)); + val = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1); } else { continue; } @@ -497,7 +497,7 @@ static void place_essa_pis( 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, (build_flags & ZEND_RT_CONSTANTS)) + 1); + 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); @@ -742,10 +742,6 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, 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_LIST_W: if (opline->op1_type == IS_CV) { ssa_ops[k].op1_def = ssa_vars_count; @@ -885,7 +881,6 @@ int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_ return FAILURE; } - ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS); ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block)); ssa->blocks = ssa_blocks; @@ -1125,8 +1120,6 @@ int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_ 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], "php_errormsg")) { - ssa_vars[i].alias = PHP_ERRORMSG_ALIAS; } else if (zend_string_equals_literal(op_array->vars[i], "http_response_header")) { ssa_vars[i].alias = HTTP_RESPONSE_HEADER_ALIAS; } diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h index a921a5bbe0..1987a47fc4 100644 --- a/ext/opcache/Optimizer/zend_ssa.h +++ b/ext/opcache/Optimizer/zend_ssa.h @@ -95,7 +95,6 @@ typedef struct _zend_ssa_op { typedef enum _zend_ssa_alias_kind { NO_ALIAS, SYMTABLE_ALIAS, - PHP_ERRORMSG_ALIAS, HTTP_RESPONSE_HEADER_ALIAS } zend_ssa_alias_kind; @@ -132,12 +131,11 @@ typedef struct _zend_ssa_var_info { typedef struct _zend_ssa { zend_cfg cfg; /* control flow graph */ - int rt_constants; /* run-time or compile-time */ 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 */ - int sccs; /* number of SCCs */ zend_ssa_var_info *var_info; } zend_ssa; |