diff --git a/Zend/Optimizer/ssa_integrity.c b/Zend/Optimizer/ssa_integrity.c index 69c6e6e901a72..d92fc2e18d1fc 100644 --- a/Zend/Optimizer/ssa_integrity.c +++ b/Zend/Optimizer/ssa_integrity.c @@ -170,10 +170,10 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex } } FOREACH_PHI_USE_END(); - if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) { + if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); } - if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) { + if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); } if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { diff --git a/Zend/Optimizer/zend_dump.c b/Zend/Optimizer/zend_dump.c index 9eaca19f18f57..c44b54ec735e2 100644 --- a/Zend/Optimizer/zend_dump.c +++ b/Zend/Optimizer/zend_dump.c @@ -244,21 +244,34 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst } if (info & MAY_BE_ARRAY) { if (first) first = 0; else fprintf(stderr, ", "); - if (!(info & MAY_BE_ARRAY_KEY_STRING) || (info & MAY_BE_PACKED_GUARD)) { - if (MAY_BE_PACKED_ONLY(info)) { - if (info & MAY_BE_PACKED_GUARD) { - fprintf(stderr, "!"); - } + if (info & MAY_BE_PACKED_GUARD) { + fprintf(stderr, "!"); + } + if (MAY_BE_EMPTY_ONLY(info)) { + fprintf(stderr, "empty "); + } else if (MAY_BE_PACKED_ONLY(info)) { + fprintf(stderr, "packed "); + } else if (MAY_BE_HASH_ONLY(info)) { + fprintf(stderr, "hash "); + } else if ((info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY && (info & MAY_BE_ARRAY_KEY_ANY) != 0) { + bool afirst = 1; + fprintf(stderr, "["); + if (info & MAY_BE_ARRAY_EMPTY) { + if (afirst) afirst = 0; else fprintf(stderr, ", "); + fprintf(stderr, "empty "); + } + if (MAY_BE_PACKED(info)) { + if (afirst) afirst = 0; else fprintf(stderr, ", "); fprintf(stderr, "packed "); - } else if (MAY_BE_HASH_ONLY(info)) { - if (info & MAY_BE_PACKED_GUARD) { - fprintf(stderr, "!"); - } + } + if (MAY_BE_HASH(info)) { + if (afirst) afirst = 0; else fprintf(stderr, ", "); fprintf(stderr, "hash "); } + fprintf(stderr, "] "); } fprintf(stderr, "array"); - if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 && + if ((info & (MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING)) != 0 && ((info & MAY_BE_ARRAY_KEY_LONG) == 0 || (info & MAY_BE_ARRAY_KEY_STRING) == 0)) { bool afirst = 1; diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index f125f4956b11e..e2c746bbf539c 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -61,7 +61,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]); uint32_t t3 = 0; - uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY; + uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_EMPTY; if (call_info->num_args == 3) { t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, @@ -87,7 +87,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa return tmp; } else { /* May throw */ - return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; + return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_EMPTY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; } } @@ -120,7 +120,12 @@ uint32_t zend_get_internal_func_info( if (info->info_func) { return call_info ? info->info_func(call_info, ssa) : 0; } else { - return info->info; + uint32_t ret = info->info; + + if (ret & MAY_BE_ARRAY) { + ret |= MAY_BE_ARRAY_EMPTY; + } + return ret; } } diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 9b6b201551f33..77659f1b9ecf2 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -2105,18 +2105,22 @@ ZEND_API uint32_t ZEND_FASTCALL zend_array_type_info(const zval *zv) tmp |= MAY_BE_RCN; } - ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) { - if (str) { - tmp |= MAY_BE_ARRAY_KEY_STRING; - } else { - tmp |= MAY_BE_ARRAY_KEY_LONG; - } - tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); - } ZEND_HASH_FOREACH_END(); - if (HT_IS_PACKED(ht)) { - tmp &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH); + if (zend_hash_num_elements(ht) == 0) { + tmp |= MAY_BE_ARRAY_EMPTY; + } else if (HT_IS_PACKED(ht)) { + tmp |= MAY_BE_ARRAY_PACKED; + ZEND_HASH_PACKED_FOREACH_VAL(ht, val) { + tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); + } ZEND_HASH_FOREACH_END(); } else { - tmp &= ~MAY_BE_ARRAY_PACKED; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht, str, val) { + if (str) { + tmp |= MAY_BE_ARRAY_STRING_HASH; + } else { + tmp |= MAY_BE_ARRAY_NUMERIC_HASH; + } + tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); + } ZEND_HASH_FOREACH_END(); } return tmp; } @@ -2224,6 +2228,7 @@ static uint32_t assign_dim_array_result_type( if (tmp & MAY_BE_ARRAY_KEY_ANY) { tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT; } + tmp &= ~MAY_BE_ARRAY_EMPTY; return tmp; } @@ -3661,6 +3666,9 @@ static zend_always_inline zend_result _zend_update_type_info( break; } } + if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) { + tmp &= ~MAY_BE_ARRAY_EMPTY; + } } if (((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) || opline->opcode == ZEND_FETCH_DIM_FUNC_ARG @@ -3828,7 +3836,7 @@ static zend_always_inline zend_result _zend_update_type_info( UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); break; case ZEND_FUNC_GET_ARGS: - UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); + UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN|MAY_BE_ARRAY|MAY_BE_ARRAY_EMPTY|MAY_BE_ARRAY_PACKED|MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); break; case ZEND_GET_CLASS: case ZEND_GET_CALLED_CLASS: diff --git a/Zend/Optimizer/zend_inference.h b/Zend/Optimizer/zend_inference.h index 4e768c4f6d524..ffcf4f0ae1379 100644 --- a/Zend/Optimizer/zend_inference.h +++ b/Zend/Optimizer/zend_inference.h @@ -31,7 +31,6 @@ #define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */ #define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */ #define MAY_BE_GUARD (1<<28) /* needs type guard */ -//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */ #define MAY_HAVE_DTOR \ (MAY_BE_OBJECT|MAY_BE_RESOURCE \ diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h index f9780181a4c67..eeab7bf0431da 100644 --- a/Zend/zend_type_info.h +++ b/Zend/zend_type_info.h @@ -59,15 +59,17 @@ #define MAY_BE_ARRAY_PACKED (1<<21) #define MAY_BE_ARRAY_NUMERIC_HASH (1<<22) /* hash with numeric keys */ #define MAY_BE_ARRAY_STRING_HASH (1<<23) /* hash with string keys */ +#define MAY_BE_ARRAY_EMPTY (1<<29) #define MAY_BE_ARRAY_KEY_LONG (MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_NUMERIC_HASH) #define MAY_BE_ARRAY_KEY_STRING MAY_BE_ARRAY_STRING_HASH -#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING) +#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_EMPTY) #define MAY_BE_PACKED(t) ((t) & MAY_BE_ARRAY_PACKED) #define MAY_BE_HASH(t) ((t) & (MAY_BE_ARRAY_NUMERIC_HASH | MAY_BE_ARRAY_KEY_STRING)) -#define MAY_BE_PACKED_ONLY(t) (MAY_BE_PACKED(t) && !MAY_BE_HASH(t)) -#define MAY_BE_HASH_ONLY(t) (MAY_BE_HASH(t) && !MAY_BE_PACKED(t)) +#define MAY_BE_PACKED_ONLY(t) (((t) & MAY_BE_ARRAY_KEY_ANY) == MAY_BE_ARRAY_PACKED) +#define MAY_BE_HASH_ONLY(t) (MAY_BE_HASH(t) && !((t) & (MAY_BE_ARRAY_PACKED|MAY_BE_ARRAY_EMPTY))) +#define MAY_BE_EMPTY_ONLY(t) (((t) & MAY_BE_ARRAY_KEY_ANY) == MAY_BE_ARRAY_EMPTY) #define MAY_BE_CLASS (1<<24) #define MAY_BE_INDIRECT (1<<25) diff --git a/ext/opcache/tests/jit/gh12527.phpt b/ext/opcache/tests/jit/gh12527.phpt new file mode 100644 index 0000000000000..31ccba485364a --- /dev/null +++ b/ext/opcache/tests/jit/gh12527.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-12527: Incorrect hash/packed inference +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- + $val) { + if (!$val) { + $a2["bad"][] = $key; + } else { + $a2[0][] = $key; + } + } + foreach ($a2 as $key => $val) { + var_dump($key); + } +} +foo([1, 2, 3]); +foo([1, 2, 3]); +foo([0, 0]); +?> +--EXPECT-- +int(0) +int(0) +string(3) "bad"