Skip to content

Commit 1fb0b45

Browse files
committed
Fix GH-24391: JIT: switch on constant null takes default instead of case 0
There was simply a mismatch in how the VM and JIT handled the types. I reworked how the jump target is chose, and hopefully made the code easier to understand in the process. Closes GH-24393.
1 parent fd3a97d commit 1fb0b45

3 files changed

Lines changed: 50 additions & 12 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ PHP NEWS
7575
. Fix SIGFPE on LONG_MIN % -1 with a constant divisor. (waroad)
7676
. Fixed bug GH-24388 (Tracing JIT: segfault when a trace runs out of side
7777
exits). (Lazizbek Ergashev)
78+
. Fixed bug GH-24391 (JIT switch on constant null takes default instead of
79+
case 0). (ndossche)
7880

7981
- PDO:
8082
. Fixed a duplicate warning when ATTR_STATEMENT_CLASS is rejected on a

‎ext/opcache/jit/zend_jit_ir.c‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15997,15 +15997,22 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_
1599715997
if (opline->op1_type == IS_CONST) {
1599815998
zval *zv = RT_CONSTANT(opline, opline->op1);
1599915999
zval *jump_zv = NULL;
16000+
const zend_op *target;
1600016001
int b;
1600116002

16003+
/* Wrong type for SWITCH_LONG/SWITCH_STRING falls back to the ZEND_CASE chain */
16004+
target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
1600216005
if (opline->opcode == ZEND_SWITCH_LONG) {
1600316006
if (Z_TYPE_P(zv) == IS_LONG) {
1600416007
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv));
16008+
} else {
16009+
target = opline + 1;
1600516010
}
1600616011
} else if (opline->opcode == ZEND_SWITCH_STRING) {
1600716012
if (Z_TYPE_P(zv) == IS_STRING) {
1600816013
jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv));
16014+
} else {
16015+
target = opline + 1;
1600916016
}
1601016017
} else if (opline->opcode == ZEND_MATCH) {
1601116018
if (Z_TYPE_P(zv) == IS_LONG) {
@@ -16016,21 +16023,13 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_
1601616023
} else {
1601716024
ZEND_UNREACHABLE();
1601816025
}
16026+
if (jump_zv != NULL) {
16027+
target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
16028+
}
1601916029
if (next_opline) {
16020-
const zend_op *target;
16021-
16022-
if (jump_zv != NULL) {
16023-
target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv));
16024-
} else {
16025-
target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
16026-
}
1602716030
ZEND_ASSERT(target == next_opline);
1602816031
} else {
16029-
if (jump_zv != NULL) {
16030-
b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes];
16031-
} else {
16032-
b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes];
16033-
}
16032+
b = ssa->cfg.map[target - op_array->opcodes];
1603416033
_zend_jit_add_predecessor_ref(jit, b, jit->b, ir_END());
1603516034
jit->b = -1;
1603616035
}

‎ext/opcache/tests/jit/gh24391.phpt‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
GH-24391 (JIT: switch on constant of wrong type takes default instead of falling back to the case chain)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.jit=function
9+
opcache.optimization_level=0
10+
--FILE--
11+
<?php
12+
function long_switch() {
13+
switch (null) {
14+
case 0: return 1;
15+
case 1: return 2;
16+
case 2: return 3;
17+
case 3: return 4;
18+
case 4: return 5;
19+
default: return 6;
20+
}
21+
}
22+
23+
function string_switch() {
24+
switch (0) {
25+
case "aa": return 1;
26+
case "bb": return 2;
27+
case "cc": return 3;
28+
default: return 6;
29+
}
30+
}
31+
32+
var_dump(long_switch());
33+
var_dump(string_switch());
34+
?>
35+
--EXPECT--
36+
int(1)
37+
int(6)

0 commit comments

Comments
 (0)