Skip to content

Commit 1c95e22

Browse files
authored
Fixed HASH/PACKED array inference through MAY_BE_ARRAY_EMPTY flag (#12591)
* Fixed HASH/PACKED array inference through MAY_BE_ARRAY_EMPTY flag This fixes GH-12527 * typo
1 parent 4fce8c8 commit 1c95e22

File tree

7 files changed

+86
-31
lines changed

7 files changed

+86
-31
lines changed

Zend/Optimizer/ssa_integrity.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,10 @@ void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *ex
170170
}
171171
} FOREACH_PHI_USE_END();
172172

173-
if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
173+
if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) {
174174
FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
175175
}
176-
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
176+
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) {
177177
FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
178178
}
179179
if ((type & MAY_BE_REF) && ssa->var_info[i].ce) {

Zend/Optimizer/zend_dump.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,21 +244,34 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
244244
}
245245
if (info & MAY_BE_ARRAY) {
246246
if (first) first = 0; else fprintf(stderr, ", ");
247-
if (!(info & MAY_BE_ARRAY_KEY_STRING) || (info & MAY_BE_PACKED_GUARD)) {
248-
if (MAY_BE_PACKED_ONLY(info)) {
249-
if (info & MAY_BE_PACKED_GUARD) {
250-
fprintf(stderr, "!");
251-
}
247+
if (info & MAY_BE_PACKED_GUARD) {
248+
fprintf(stderr, "!");
249+
}
250+
if (MAY_BE_EMPTY_ONLY(info)) {
251+
fprintf(stderr, "empty ");
252+
} else if (MAY_BE_PACKED_ONLY(info)) {
253+
fprintf(stderr, "packed ");
254+
} else if (MAY_BE_HASH_ONLY(info)) {
255+
fprintf(stderr, "hash ");
256+
} else if ((info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY && (info & MAY_BE_ARRAY_KEY_ANY) != 0) {
257+
bool afirst = 1;
258+
fprintf(stderr, "[");
259+
if (info & MAY_BE_ARRAY_EMPTY) {
260+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
261+
fprintf(stderr, "empty ");
262+
}
263+
if (MAY_BE_PACKED(info)) {
264+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
252265
fprintf(stderr, "packed ");
253-
} else if (MAY_BE_HASH_ONLY(info)) {
254-
if (info & MAY_BE_PACKED_GUARD) {
255-
fprintf(stderr, "!");
256-
}
266+
}
267+
if (MAY_BE_HASH(info)) {
268+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
257269
fprintf(stderr, "hash ");
258270
}
271+
fprintf(stderr, "] ");
259272
}
260273
fprintf(stderr, "array");
261-
if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
274+
if ((info & (MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING)) != 0 &&
262275
((info & MAY_BE_ARRAY_KEY_LONG) == 0 ||
263276
(info & MAY_BE_ARRAY_KEY_STRING) == 0)) {
264277
bool afirst = 1;

Zend/Optimizer/zend_func_info.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa
6161
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
6262
&ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
6363
uint32_t t3 = 0;
64-
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY;
64+
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_EMPTY;
6565

6666
if (call_info->num_args == 3) {
6767
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
8787
return tmp;
8888
} else {
8989
/* May throw */
90-
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;
90+
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;
9191
}
9292
}
9393

@@ -120,7 +120,12 @@ uint32_t zend_get_internal_func_info(
120120
if (info->info_func) {
121121
return call_info ? info->info_func(call_info, ssa) : 0;
122122
} else {
123-
return info->info;
123+
uint32_t ret = info->info;
124+
125+
if (ret & MAY_BE_ARRAY) {
126+
ret |= MAY_BE_ARRAY_EMPTY;
127+
}
128+
return ret;
124129
}
125130
}
126131

Zend/Optimizer/zend_inference.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,18 +2105,22 @@ ZEND_API uint32_t ZEND_FASTCALL zend_array_type_info(const zval *zv)
21052105
tmp |= MAY_BE_RCN;
21062106
}
21072107

2108-
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
2109-
if (str) {
2110-
tmp |= MAY_BE_ARRAY_KEY_STRING;
2111-
} else {
2112-
tmp |= MAY_BE_ARRAY_KEY_LONG;
2113-
}
2114-
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
2115-
} ZEND_HASH_FOREACH_END();
2116-
if (HT_IS_PACKED(ht)) {
2117-
tmp &= ~(MAY_BE_ARRAY_NUMERIC_HASH|MAY_BE_ARRAY_STRING_HASH);
2108+
if (zend_hash_num_elements(ht) == 0) {
2109+
tmp |= MAY_BE_ARRAY_EMPTY;
2110+
} else if (HT_IS_PACKED(ht)) {
2111+
tmp |= MAY_BE_ARRAY_PACKED;
2112+
ZEND_HASH_PACKED_FOREACH_VAL(ht, val) {
2113+
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
2114+
} ZEND_HASH_FOREACH_END();
21182115
} else {
2119-
tmp &= ~MAY_BE_ARRAY_PACKED;
2116+
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht, str, val) {
2117+
if (str) {
2118+
tmp |= MAY_BE_ARRAY_STRING_HASH;
2119+
} else {
2120+
tmp |= MAY_BE_ARRAY_NUMERIC_HASH;
2121+
}
2122+
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
2123+
} ZEND_HASH_FOREACH_END();
21202124
}
21212125
return tmp;
21222126
}
@@ -2224,6 +2228,7 @@ static uint32_t assign_dim_array_result_type(
22242228
if (tmp & MAY_BE_ARRAY_KEY_ANY) {
22252229
tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
22262230
}
2231+
tmp &= ~MAY_BE_ARRAY_EMPTY;
22272232
return tmp;
22282233
}
22292234

@@ -3662,6 +3667,9 @@ static zend_always_inline zend_result _zend_update_type_info(
36623667
break;
36633668
}
36643669
}
3670+
if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
3671+
tmp &= ~MAY_BE_ARRAY_EMPTY;
3672+
}
36653673
}
36663674
if (((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY))
36673675
|| opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
@@ -3829,7 +3837,7 @@ static zend_always_inline zend_result _zend_update_type_info(
38293837
UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
38303838
break;
38313839
case ZEND_FUNC_GET_ARGS:
3832-
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);
3840+
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);
38333841
break;
38343842
case ZEND_GET_CLASS:
38353843
case ZEND_GET_CALLED_CLASS:

Zend/Optimizer/zend_inference.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
3232
#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
3333
#define MAY_BE_GUARD (1<<28) /* needs type guard */
34-
//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
3534

3635
#define MAY_HAVE_DTOR \
3736
(MAY_BE_OBJECT|MAY_BE_RESOURCE \

Zend/zend_type_info.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,17 @@
5959
#define MAY_BE_ARRAY_PACKED (1<<21)
6060
#define MAY_BE_ARRAY_NUMERIC_HASH (1<<22) /* hash with numeric keys */
6161
#define MAY_BE_ARRAY_STRING_HASH (1<<23) /* hash with string keys */
62+
#define MAY_BE_ARRAY_EMPTY (1<<29)
6263

6364
#define MAY_BE_ARRAY_KEY_LONG (MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_NUMERIC_HASH)
6465
#define MAY_BE_ARRAY_KEY_STRING MAY_BE_ARRAY_STRING_HASH
65-
#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING)
66+
#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_EMPTY)
6667

6768
#define MAY_BE_PACKED(t) ((t) & MAY_BE_ARRAY_PACKED)
6869
#define MAY_BE_HASH(t) ((t) & (MAY_BE_ARRAY_NUMERIC_HASH | MAY_BE_ARRAY_KEY_STRING))
69-
#define MAY_BE_PACKED_ONLY(t) (MAY_BE_PACKED(t) && !MAY_BE_HASH(t))
70-
#define MAY_BE_HASH_ONLY(t) (MAY_BE_HASH(t) && !MAY_BE_PACKED(t))
70+
#define MAY_BE_PACKED_ONLY(t) (((t) & MAY_BE_ARRAY_KEY_ANY) == MAY_BE_ARRAY_PACKED)
71+
#define MAY_BE_HASH_ONLY(t) (MAY_BE_HASH(t) && !((t) & (MAY_BE_ARRAY_PACKED|MAY_BE_ARRAY_EMPTY)))
72+
#define MAY_BE_EMPTY_ONLY(t) (((t) & MAY_BE_ARRAY_KEY_ANY) == MAY_BE_ARRAY_EMPTY)
7173

7274
#define MAY_BE_CLASS (1<<24)
7375
#define MAY_BE_INDIRECT (1<<25)

ext/opcache/tests/jit/gh12527.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-12527: Incorrect hash/packed inference
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
--FILE--
7+
<?php
8+
function foo(array $a1) {
9+
$a2 = [];
10+
foreach ($a1 as $key => $val) {
11+
if (!$val) {
12+
$a2["bad"][] = $key;
13+
} else {
14+
$a2[0][] = $key;
15+
}
16+
}
17+
foreach ($a2 as $key => $val) {
18+
var_dump($key);
19+
}
20+
}
21+
foo([1, 2, 3]);
22+
foo([1, 2, 3]);
23+
foo([0, 0]);
24+
?>
25+
--EXPECT--
26+
int(0)
27+
int(0)
28+
string(3) "bad"

0 commit comments

Comments
 (0)