diff --git a/NEWS b/NEWS index d79257caf334..c8d426ad1fa9 100644 --- a/NEWS +++ b/NEWS @@ -230,6 +230,8 @@ PHP NEWS . Fixed bug GH-24988 (Incorrect opcode range check causes bugs with exceptions in function JIT). (ndossche, Jorg Sowa) . Fix opcache phar check. (ndossche) + . Fixed bug GH-25164 (Function JIT loop calling a user function runs once + or never terminates). (Ilia Alshanetsky) - OpenSSL: . Fixed build and test compatibility with OpenSSL 4.0 and 4.1. diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 53c87ce16da5..dbc7a37ca079 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1073,7 +1073,8 @@ static void zend_jit_allocate_registers(zend_jit_ctx *ctx, const zend_op_array * ra[src].flags |= ZREG_STORE; } } else { - int need_move = 0; + /* The VM may enter an ENTRY block, so its Phi sources must go through memory */ + int need_move = (ssa->cfg.blocks[phi->block].flags & (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) != 0; for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) { src = phi->sources[k]; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 53959a536a4a..1afdb79267ce 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17538,6 +17538,7 @@ static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *bb; zend_ssa_phi *p; bool ret = 1; + bool def_is_pi = 0; zend_worklist worklist; ALLOCA_FLAG(use_heap) @@ -17547,6 +17548,7 @@ static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, } else { ZEND_ASSERT(ssa->vars[var].definition_phi); def_block = ssa->vars[var].definition_phi->block; + def_is_pi = ssa->vars[var].definition_phi->pi >= 0; } ZEND_WORKLIST_ALLOCA(&worklist, ssa->cfg.blocks_count, use_heap); @@ -17564,9 +17566,14 @@ static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa, p = ssa->vars[var].phi_use_chain; while (p) { - use_block = p->block; - if (use_block != def_block) { - bb = &ssa->cfg.blocks[use_block]; + bb = &ssa->cfg.blocks[p->block]; + if (p->pi >= 0) { + /* A Pi uses the value on the single edge from its "pi" block */ + if (p->pi != def_block) { + zend_worklist_push(&worklist, p->pi); + } + } else if (p->block != def_block || !def_is_pi) { + /* Live to the end of the predecessor; a same-block Pi is zero-length */ for (j = 0; j < bb->predecessors_count; j++) { if (p->sources[j] == var) { use_block = ssa->cfg.predecessors[bb->predecessor_offset + j]; diff --git a/ext/opcache/tests/jit/gh25164.phpt b/ext/opcache/tests/jit/gh25164.phpt new file mode 100644 index 000000000000..1809b89775b9 --- /dev/null +++ b/ext/opcache/tests/jit/gh25164.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-25164 (JIT: loop variable kept in a register across a user function call) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=64M +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + 20) { + die("runaway loop\n"); + } + echo "a"; +} + +function driver() { + $i = 0; + while ($i++ < 5) { + leaf(); + } + return $i; +} + +function driver2() { + $i = 0; + while ((($i = ($i + 3) & 15) | 0) != 9) { + leaf(); + echo "-"; + } + return $i; +} + +var_dump(driver()); +var_dump(driver2()); + +?> +--EXPECT-- +aaaaaint(6) +a-a-int(9)