From d8fbfd3e17b27ecf18d09d62082d7c7c88c0abfe Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 18 Oct 2026 00:51:18 +0200 Subject: [PATCH] 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. --- ext/opcache/jit/zend_jit_ir.c | 23 +++++++++---------- ext/opcache/tests/jit/gh24391.phpt | 37 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 ext/opcache/tests/jit/gh24391.phpt diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 92a068dbfaca..0696dd3ea4ad 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -15988,15 +15988,22 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_ if (opline->op1_type == IS_CONST) { zval *zv = RT_CONSTANT(opline, opline->op1); zval *jump_zv = NULL; + const zend_op *target; int b; + /* Wrong type for SWITCH_LONG/SWITCH_STRING falls back to the ZEND_CASE chain */ + target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); if (opline->opcode == ZEND_SWITCH_LONG) { if (Z_TYPE_P(zv) == IS_LONG) { jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(zv)); + } else { + target = opline + 1; } } else if (opline->opcode == ZEND_SWITCH_STRING) { if (Z_TYPE_P(zv) == IS_STRING) { jump_zv = zend_hash_find_known_hash(jumptable, Z_STR_P(zv)); + } else { + target = opline + 1; } } else if (opline->opcode == ZEND_MATCH) { if (Z_TYPE_P(zv) == IS_LONG) { @@ -16007,21 +16014,13 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_ } else { ZEND_UNREACHABLE(); } + if (jump_zv != NULL) { + target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); + } if (next_opline) { - const zend_op *target; - - if (jump_zv != NULL) { - target = ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)); - } else { - target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - } ZEND_ASSERT(target == next_opline); } else { - if (jump_zv != NULL) { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, Z_LVAL_P(jump_zv)) - op_array->opcodes]; - } else { - b = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value) - op_array->opcodes]; - } + b = ssa->cfg.map[target - op_array->opcodes]; _zend_jit_add_predecessor_ref(jit, b, jit->b, ir_END()); jit->b = -1; } diff --git a/ext/opcache/tests/jit/gh24391.phpt b/ext/opcache/tests/jit/gh24391.phpt new file mode 100644 index 000000000000..abad5408e5d0 --- /dev/null +++ b/ext/opcache/tests/jit/gh24391.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-24391 (JIT: switch on constant of wrong type takes default instead of falling back to the case chain) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=function +opcache.optimization_level=0 +--FILE-- + +--EXPECT-- +int(1) +int(6)