Skip to content

Commit 6bf4041

Browse files
committed
Backport fix for 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 304e482 commit 6bf4041

File tree

7 files changed

+87
-30
lines changed

7 files changed

+87
-30
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
@@ -239,21 +239,34 @@ static void zend_dump_type_info(uint32_t info, zend_class_entry *ce, int is_inst
239239
}
240240
if (info & MAY_BE_ARRAY) {
241241
if (first) first = 0; else fprintf(stderr, ", ");
242-
if (!(info & MAY_BE_ARRAY_KEY_STRING) || (info & MAY_BE_PACKED_GUARD)) {
243-
if (MAY_BE_PACKED_ONLY(info)) {
244-
if (info & MAY_BE_PACKED_GUARD) {
245-
fprintf(stderr, "!");
246-
}
242+
if (info & MAY_BE_PACKED_GUARD) {
243+
fprintf(stderr, "!");
244+
}
245+
if (MAY_BE_EMPTY_ONLY(info)) {
246+
fprintf(stderr, "empty ");
247+
} else if (MAY_BE_PACKED_ONLY(info)) {
248+
fprintf(stderr, "packed ");
249+
} else if (MAY_BE_HASH_ONLY(info)) {
250+
fprintf(stderr, "hash ");
251+
} else if ((info & MAY_BE_ARRAY_KEY_ANY) != MAY_BE_ARRAY_KEY_ANY && (info & MAY_BE_ARRAY_KEY_ANY) != 0) {
252+
bool afirst = 1;
253+
fprintf(stderr, "[");
254+
if (info & MAY_BE_ARRAY_EMPTY) {
255+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
256+
fprintf(stderr, "empty ");
257+
}
258+
if (MAY_BE_PACKED(info)) {
259+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
247260
fprintf(stderr, "packed ");
248-
} else if (MAY_BE_HASH_ONLY(info)) {
249-
if (info & MAY_BE_PACKED_GUARD) {
250-
fprintf(stderr, "!");
251-
}
261+
}
262+
if (MAY_BE_HASH(info)) {
263+
if (afirst) afirst = 0; else fprintf(stderr, ", ");
252264
fprintf(stderr, "hash ");
253265
}
266+
fprintf(stderr, "] ");
254267
}
255268
fprintf(stderr, "array");
256-
if ((info & MAY_BE_ARRAY_KEY_ANY) != 0 &&
269+
if ((info & (MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING)) != 0 &&
257270
((info & MAY_BE_ARRAY_KEY_LONG) == 0 ||
258271
(info & MAY_BE_ARRAY_KEY_STRING) == 0)) {
259272
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: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2105,16 +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();
2115+
} else {
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();
21182124
}
21192125
return tmp;
21202126
}
@@ -2222,6 +2228,7 @@ static uint32_t assign_dim_array_result_type(
22222228
if (tmp & MAY_BE_ARRAY_KEY_ANY) {
22232229
tmp |= (value_type & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT;
22242230
}
2231+
tmp &= ~MAY_BE_ARRAY_EMPTY;
22252232
return tmp;
22262233
}
22272234

@@ -3656,6 +3663,9 @@ static zend_always_inline zend_result _zend_update_type_info(
36563663
break;
36573664
}
36583665
}
3666+
if (opline->opcode != ZEND_FETCH_DIM_FUNC_ARG) {
3667+
tmp &= ~MAY_BE_ARRAY_EMPTY;
3668+
}
36593669
}
36603670
if (((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY))
36613671
|| opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
@@ -3829,7 +3839,7 @@ static zend_always_inline zend_result _zend_update_type_info(
38293839
UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
38303840
break;
38313841
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);
3842+
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);
38333843
break;
38343844
case ZEND_GET_CLASS:
38353845
case ZEND_GET_CALLED_CLASS:

Zend/Optimizer/zend_inference.h

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

3433
#define MAY_HAVE_DTOR \
3534
(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)