summaryrefslogtreecommitdiff
path: root/ext/opcache/Optimizer/zend_inference.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/opcache/Optimizer/zend_inference.c')
-rw-r--r--ext/opcache/Optimizer/zend_inference.c325
1 files changed, 312 insertions, 13 deletions
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
index 5234bc8ef6..1e52474897 100644
--- a/ext/opcache/Optimizer/zend_inference.c
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -282,7 +282,8 @@ int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ss
}
} else {
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
- if (p->sources[j] >= 0 && ssa->vars[p->sources[j]].no_val) {
+ ZEND_ASSERT(p->sources[j] >= 0);
+ if (ssa->vars[p->sources[j]].no_val) {
ssa_vars[p->sources[j]].no_val = 0; /* used indirectly */
zend_bitset_incl(worklist, p->sources[j]);
}
@@ -854,7 +855,8 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
}
} else {
for (i = 0; i < ssa->cfg.blocks[p->block].predecessors_count; i++) {
- if (p->sources[i] >= 0 && ssa->var_info[p->sources[i]].has_range) {
+ ZEND_ASSERT(p->sources[i] >= 0);
+ if (ssa->var_info[p->sources[i]].has_range) {
/* union */
tmp->underflow |= ssa->var_info[p->sources[i]].range.underflow;
tmp->min = MIN(tmp->min, ssa->var_info[p->sources[i]].range.min);
@@ -3328,17 +3330,18 @@ int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script
}
UPDATE_SSA_TYPE(tmp, j);
for (i = 0; i < blocks[p->block].predecessors_count; i++) {
- if (p->sources[i] >= 0) {
- zend_ssa_var_info *info = &ssa_var_info[p->sources[i]];
- if (info->type & MAY_BE_OBJECT) {
- if (first) {
- ce = info->ce;
- is_instanceof = info->is_instanceof;
- first = 0;
- } else {
- is_instanceof |= info->is_instanceof;
- ce = join_class_entries(ce, info->ce, &is_instanceof);
- }
+ zend_ssa_var_info *info;
+
+ ZEND_ASSERT(p->sources[i] >= 0);
+ info = &ssa_var_info[p->sources[i]];
+ if (info->type & MAY_BE_OBJECT) {
+ if (first) {
+ ce = info->ce;
+ is_instanceof = info->is_instanceof;
+ first = 0;
+ } else {
+ is_instanceof |= info->is_instanceof;
+ ce = join_class_entries(ce, info->ce, &is_instanceof);
}
}
}
@@ -3924,6 +3927,302 @@ 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)
+{
+ uint32_t t1 = OP1_INFO();
+ uint32_t t2 = OP2_INFO();
+
+ if (opline->op1_type == IS_CV) {
+ if (t1 & MAY_BE_UNDEF) {
+ switch (opline->opcode) {
+ case ZEND_UNSET_VAR:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ if (opline->extended_value & ZEND_QUICK_SET) {
+ break;
+ }
+ return 1;
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ case ZEND_ISSET_ISEMPTY_PROP_OBJ:
+ case ZEND_ASSIGN:
+ case ZEND_ASSIGN_DIM:
+ case ZEND_ASSIGN_REF:
+ case ZEND_BIND_GLOBAL:
+ case ZEND_FETCH_DIM_IS:
+ case ZEND_FETCH_OBJ_IS:
+ case ZEND_SEND_REF:
+ break;
+ default:
+ /* undefined variable warning */
+ return 1;
+ }
+ }
+ } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
+ if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) {
+ switch (opline->opcode) {
+ case ZEND_CASE:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_FETCH_LIST:
+ case ZEND_QM_ASSIGN:
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEPARATE:
+ case ZEND_END_SILENCE:
+ break;
+ default:
+ /* destructor may be called */
+ return 1;
+ }
+ }
+ }
+
+ if (opline->op2_type == IS_CV) {
+ if (t2 & MAY_BE_UNDEF) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN_REF:
+ break;
+ default:
+ /* undefined variable warning */
+ return 1;
+ }
+ }
+ } else if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ if (t2 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) {
+ switch (opline->opcode) {
+ case ZEND_ASSIGN:
+ break;
+ default:
+ /* destructor may be called */
+ return 1;
+ }
+ }
+ }
+
+ switch (opline->opcode) {
+ case ZEND_NOP:
+ case ZEND_IS_IDENTICAL:
+ case ZEND_IS_NOT_IDENTICAL:
+ case ZEND_QM_ASSIGN:
+ case ZEND_JMP:
+ case ZEND_CHECK_VAR:
+ case ZEND_MAKE_REF:
+ case ZEND_SEND_VAR:
+ case ZEND_BEGIN_SILENCE:
+ case ZEND_END_SILENCE:
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_FREE:
+ case ZEND_SEPARATE:
+ case ZEND_TYPE_CHECK:
+ case ZEND_DEFINED:
+ case ZEND_ISSET_ISEMPTY_THIS:
+ case ZEND_COALESCE:
+ case ZEND_SWITCH_LONG:
+ case ZEND_SWITCH_STRING:
+ case ZEND_ISSET_ISEMPTY_VAR:
+ return 0;
+ case ZEND_INIT_FCALL:
+ /* can't throw, because call is resolved at compile time */
+ return 0;
+ case ZEND_BIND_GLOBAL:
+ if ((opline+1)->opcode == ZEND_BIND_GLOBAL) {
+ return zend_may_throw(opline + 1, op_array, ssa);
+ }
+ return 0;
+ case ZEND_ADD:
+ if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY
+ && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
+ return 0;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_DIV:
+ case ZEND_MOD:
+ if (!OP2_HAS_RANGE() ||
+ (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) {
+ /* Division by zero */
+ return 1;
+ }
+ /* break missing intentionally */
+ case ZEND_SUB:
+ case ZEND_MUL:
+ case ZEND_POW:
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_SL:
+ case ZEND_SR:
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ !OP2_HAS_RANGE() ||
+ OP2_MIN_RANGE() < 0;
+ case ZEND_CONCAT:
+ case ZEND_FAST_CONCAT:
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_BW_OR:
+ case ZEND_BW_AND:
+ case ZEND_BW_XOR:
+ if ((t1 & MAY_BE_ANY) == MAY_BE_STRING
+ && (t2 & MAY_BE_ANY) == MAY_BE_STRING) {
+ return 0;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_BW_NOT:
+ return (t1 & (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_BOOL_NOT:
+ case ZEND_PRE_INC:
+ case ZEND_POST_INC:
+ case ZEND_PRE_DEC:
+ case ZEND_POST_DEC:
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_BOOL:
+ case ZEND_JMP_SET:
+ return (t1 & MAY_BE_OBJECT);
+ case ZEND_BOOL_XOR:
+ return (t1 & MAY_BE_OBJECT) || (t2 & MAY_BE_OBJECT);
+ case ZEND_IS_EQUAL:
+ case ZEND_IS_NOT_EQUAL:
+ case ZEND_IS_SMALLER:
+ case ZEND_IS_SMALLER_OR_EQUAL:
+ case ZEND_CASE:
+ case ZEND_SPACESHIP:
+ if ((t1 & MAY_BE_ANY) == MAY_BE_NULL
+ || (t2 & MAY_BE_ANY) == MAY_BE_NULL) {
+ return 0;
+ }
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT)) || (t2 & (MAY_BE_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT));
+ case ZEND_ASSIGN_ADD:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_ARRAY
+ && (t2 & MAY_BE_ANY) == MAY_BE_ARRAY) {
+ return 0;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ASSIGN_DIV:
+ case ZEND_ASSIGN_MOD:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ if (!OP2_HAS_RANGE() ||
+ (OP2_MIN_RANGE() <= 0 && OP2_MAX_RANGE() >= 0)) {
+ /* Division by zero */
+ return 1;
+ }
+ /* break missing intentionally */
+ case ZEND_ASSIGN_SUB:
+ case ZEND_ASSIGN_MUL:
+ case ZEND_ASSIGN_POW:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ASSIGN_SL:
+ case ZEND_ASSIGN_SR:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ !OP2_HAS_RANGE() ||
+ OP2_MIN_RANGE() < 0;
+ case ZEND_ASSIGN_CONCAT:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ASSIGN_BW_OR:
+ case ZEND_ASSIGN_BW_AND:
+ case ZEND_ASSIGN_BW_XOR:
+ if (opline->extended_value != 0) {
+ return 1;
+ }
+ if ((t1 & MAY_BE_ANY) == MAY_BE_STRING
+ && (t2 & MAY_BE_ANY) == MAY_BE_STRING) {
+ return 0;
+ }
+ return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) ||
+ (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ASSIGN:
+ case ZEND_UNSET_VAR:
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY));
+ case ZEND_ASSIGN_DIM:
+ return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE)) || opline->op2_type == IS_UNUSED ||
+ (t2 & (MAY_BE_UNDEF|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_ROPE_INIT:
+ case ZEND_ROPE_ADD:
+ case ZEND_ROPE_END:
+ return t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT);
+ case ZEND_INIT_ARRAY:
+ case ZEND_ADD_ARRAY_ELEMENT:
+ return (opline->op2_type != IS_UNUSED) && (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_STRLEN:
+ return (t1 & MAY_BE_ANY) != MAY_BE_STRING;
+ case ZEND_COUNT:
+ return (t1 & MAY_BE_ANY) != MAY_BE_ARRAY;
+ case ZEND_RECV_INIT:
+ if (Z_CONSTANT_P(CRT_CONSTANT_EX(op_array, opline->op2, ssa->rt_constants))) {
+ return 1;
+ }
+ if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
+ uint32_t arg_num = opline->op1.num;
+ zend_arg_info *cur_arg_info;
+
+ if (EXPECTED(arg_num <= op_array->num_args)) {
+ cur_arg_info = &op_array->arg_info[arg_num-1];
+ } else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
+ cur_arg_info = &op_array->arg_info[op_array->num_args];
+ } else {
+ return 0;
+ }
+ return ZEND_TYPE_IS_SET(cur_arg_info->type);
+ } else {
+ return 0;
+ }
+ case ZEND_FETCH_IS:
+ return (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_ISSET_ISEMPTY_DIM_OBJ:
+ return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case ZEND_FETCH_DIM_IS:
+ return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE));
+ case ZEND_CAST:
+ switch (opline->extended_value) {
+ case IS_NULL:
+ return 0;
+ case _IS_BOOL:
+ return (t1 & MAY_BE_OBJECT);
+ case IS_LONG:
+ case IS_DOUBLE:
+ return (t1 & MAY_BE_OBJECT);
+ case IS_STRING:
+ return (t1 & (MAY_BE_ARRAY|MAY_BE_OBJECT));
+ case IS_ARRAY:
+ return (t1 & MAY_BE_OBJECT);
+ case IS_OBJECT:
+ return (t1 & MAY_BE_ARRAY);
+ default:
+ return 1;
+ }
+ default:
+ return 1;
+ }
+}
+
/*
* Local variables:
* tab-width: 4