From fd1ea3cd36db73e0dac24cfd36273d0db643003b Mon Sep 17 00:00:00 2001 From: Alessio Attilio Date: Sat, 24 Oct 2026 15:34:08 +0200 Subject: [PATCH 1/2] Fix GH-24214: Bind shared JIT assignment values for OSR Co-authored-by: Nora Dossche <7771979+ndossche@users.noreply.github.com> --- NEWS | 2 ++ ext/opcache/jit/zend_jit_ir.c | 15 +++++----- ext/opcache/tests/jit/gh24214.phpt | 39 ++++++++++++++++++++++++++ ext/opcache/tests/jit/gh24214_002.phpt | 39 ++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 ext/opcache/tests/jit/gh24214.phpt create mode 100644 ext/opcache/tests/jit/gh24214_002.phpt diff --git a/NEWS b/NEWS index 24432565e6e0..a9ce890a8346 100644 --- a/NEWS +++ b/NEWS @@ -163,6 +163,8 @@ PHP NEWS - Opcache: . Fixed bug GH-24397 (Assertion failure when dumping JIT IR). (Ilia Alshanetsky) + . Fixed bug GH-24214 (Function JIT fails to restore shared assignment values + at loop entries when DFA optimization is disabled). (Alessio Attilio) . Fixed tracing JIT returning a wrapped integer instead of a float on integer overflow after an integer was compared with a float or numeric-string literal. (Ilia Alshanetsky) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 53959a536a4a..f412cdbc280f 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1354,13 +1354,6 @@ static bool zend_jit_spilling_may_cause_conflict(zend_jit_ctx *jit, int var, ir_ && (jit->ssa->cfg.blocks[jit->ssa->vars[jit->ssa->ops[jit->ssa->vars[var].definition].op1_use].definition_phi->block].flags & ZEND_BB_LOOP_HEADER)) { /* Avoid moving spill store out of loop */ return 1; - } else if (jit->ssa->vars[var].definition >= 0 - && jit->ssa->ops[jit->ssa->vars[var].definition].op1_def == var - && jit->ssa->ops[jit->ssa->vars[var].definition].op1_use >= 0 - && jit->ssa->ops[jit->ssa->vars[var].definition].op2_use >= 0 - && jit->ra[jit->ssa->ops[jit->ssa->vars[var].definition].op2_use].ref == val) { - /* Avoid spill conflict between of ASSIGN.op1_def and ASSIGN.op1_use */ - return 1; } return 0; } @@ -1387,6 +1380,14 @@ static void zend_jit_def_reg(zend_jit_ctx *jit, zend_jit_addr addr, ir_ref val) val = ir_emit2(&jit->ctx, IR_OPT(IR_COPY, jit->ctx.ir_base[val].type), val, 1); } } + if (jit->ssa->vars[var].var < jit->current_op_array->last_var + && jit->ssa->vars[var].definition >= 0 + && jit->ssa->ops[jit->ssa->vars[var].definition].op1_def == var + && jit->ssa->ops[jit->ssa->vars[var].definition].op1_use >= 0 + && jit->ssa->ops[jit->ssa->vars[var].definition].op2_use >= 0 + && jit->ra[jit->ssa->ops[jit->ssa->vars[var].definition].op2_use].ref == val) { + val = ir_emit2(&jit->ctx, IR_OPT(IR_COPY, jit->ctx.ir_base[val].type), val, 1); + } if (!zend_jit_spilling_may_cause_conflict(jit, var, val)) { val = ir_bind(&jit->ctx, -EX_NUM_TO_VAR(jit->ssa->vars[var].var), val); } diff --git a/ext/opcache/tests/jit/gh24214.phpt b/ext/opcache/tests/jit/gh24214.phpt new file mode 100644 index 000000000000..15fd6fc49683 --- /dev/null +++ b/ext/opcache/tests/jit/gh24214.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-24214 (Shared assignment values must be restored at function JIT loop entries) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1235 +opcache.jit_buffer_size=64M +opcache.jit_hot_loop=1 +opcache.optimization_level=0x7FFEBFDF +--FILE-- + true], [4 => true], [4 => true]]); + +foreach ([3, 0, 1, 64] as $size) { + $tokens = markTokens(array_fill(0, $size, [])); + var_dump(count($tokens) === $size); + var_dump($tokens === array_fill(0, $size, [4 => true])); +} +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/opcache/tests/jit/gh24214_002.phpt b/ext/opcache/tests/jit/gh24214_002.phpt new file mode 100644 index 000000000000..0be17248e663 --- /dev/null +++ b/ext/opcache/tests/jit/gh24214_002.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-24214 (Shared floating-point assignment values must be restored at function JIT loop entries) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1235 +opcache.jit_buffer_size=64M +opcache.jit_hot_loop=1 +opcache.optimization_level=0x7FFEBFDF +--FILE-- + true], [4 => true], [4 => true]]); + +foreach ([3, 0, 1, 64] as $size) { + $tokens = markTokens(array_fill(0, $size, [])); + var_dump(count($tokens) === $size); + var_dump($tokens === array_fill(0, $size, [4 => true])); +} +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) From d47b9171171d6c5a28a51ca3ad3a3ec5136bc6b3 Mon Sep 17 00:00:00 2001 From: Alessio Attilio Date: Wed, 11 Nov 2026 18:47:08 +0100 Subject: [PATCH 2/2] Avoid assuming free disk space is monotonic --- ext/standard/tests/file/disk_free_space_basic.phpt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ext/standard/tests/file/disk_free_space_basic.phpt b/ext/standard/tests/file/disk_free_space_basic.phpt index 0d93effdd464..f6afd7569a32 100644 --- a/ext/standard/tests/file/disk_free_space_basic.phpt +++ b/ext/standard/tests/file/disk_free_space_basic.phpt @@ -30,12 +30,10 @@ echo "\n Free Space after writing to a file\n"; $space2 = disk_free_space($file_path.$dir); var_dump( $space2 ); -// Some file systems (like BTRFS) have a fuzzy notion of "free space" and will thus claim the same amount of free space -if ($space1 >= $space2) - echo "\n Free Space Value Is Correct\n"; -else { - echo "\n Free Space Value Is Incorrect\n"; - var_dump($space1, $space2); +if ($space1 !== false && $space2 !== false) { + echo "\n Free Space Queries Succeeded\n"; +} else { + echo "\n Free Space Query Failed\n"; } echo "*** Testing with Binary Input ***\n"; @@ -61,7 +59,7 @@ float(%f) Free Space after writing to a file float(%f) - Free Space Value Is Correct + Free Space Queries Succeeded *** Testing with Binary Input *** float(%f)