1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00
Commit Graph

434 Commits

Author SHA1 Message Date
Gina Peter Banyard
f40b356ad9 Use smart_str_append() if we have a zend_string* (#21414) 2026-03-21 17:06:14 +00:00
Arnaud Le Blanc
5c0a1ef7c7 Merge branch 'PHP-8.5'
* PHP-8.5:
  Fix race condition in zend_runtime_jit(), zend_jit_hot_func()
2025-10-07 10:54:05 +02:00
Arnaud Le Blanc
294e408ca2 Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
  Fix race condition in zend_runtime_jit(), zend_jit_hot_func()
2025-10-07 10:53:53 +02:00
Arnaud Le Blanc
359ad80c4a Fix race condition in zend_runtime_jit(), zend_jit_hot_func()
zend_runtime_jit() prevents concurrent compilation with
zend_shared_alloc_lock(), but this doesn't prevent blocked threads from
trying to compile the function again after they acquire the lock.

In the case of GH-19889, one of the function entries is compiled with
zend_jit_handler(), which fails when the op handler has already been replaced by
a JIT'ed handler.

Fix by marking compiled functions with a new flag ZEND_FUNC_JITED, and
skipping compilation of marked functions. The same fix is applied to
zend_jit_hot_func().

Fixes GH-19889
Closes GH-19971
2025-10-07 10:48:44 +02:00
Gina Peter Banyard
a94dc48567 Zend: Convert _zend_op_array.last_live_range field to uint32_t 2025-09-29 15:53:58 +01:00
Tim Düsterhus
3ef2505092 opcache: Use true / false instead of 1 / 0 for bool parameters
Changes done with Coccinelle:

    @r1@
    identifier F;
    identifier p;
    typedef bool;
    parameter list [n1] PL1;
    parameter list [n2] PL2;
    @@

    F(PL1, bool p, PL2) {
    ...
    }

    @r2@
    identifier r1.F;
    expression list [r1.n1] EL1;
    expression list [r1.n2] EL2;
    @@

    F(EL1,
    (
    - 1
    + true
    |
    - 0
    + false
    )
    , EL2)
2025-09-24 18:51:40 +02:00
Tim Düsterhus
066b63e7d9 opcache: Use true / false instead of 1 / 0 when assigning to bool
Changes done with Coccinelle:

    @@
    bool b;
    @@

    - b = 0
    + b = false

    @@
    bool b;
    @@

    - b = 1
    + b = true
2025-09-24 18:51:40 +02:00
Arnaud Le Blanc
0d4ff662e6 Expose the source of exit points in zend_jit_dump_exit_info()
This adds a new flag: ZEND_JIT_DEBUG_TRACE_EXIT_INFO_SRC. When the flag is set,
zend_jit_dump_exit_info() exposes the source of exit points, in debug builds.

Closes GH-19700
2025-09-24 09:26:14 +02:00
Arnaud Le Blanc
73b98a3858 TAILCALL VM
Introduce the TAILCALL VM, a more efficient variant of the CALL VM:

 * Each opcode handler tailcalls the next opcode handler directly instead of
   returning to the interpreter loop. This eliminates call and interpreter loop
   overhead.
 * Opcode handlers use the preserve_none calling convention to eliminate
   register saving overhead.
 * preserve_none uses non-volatile registers for its first arguments, so
   execute_data and opline are usually kept in these registers and no code is
   required to forward them to the next handlers.

Generated machine code is similar to a direct-threaded VM with register pinning,
like the HYBRID VM.

JIT+TAILCALL VM also benefits from this compared to JIT+CALL VM:

 * JIT uses the registers of the execute_data and opline args as fixed regs,
   eliminating the need to move them in prologue.
 * Traces exit by tailcalling the next handler. No code is needed to forward
   execute_data and opline.
 * No register saving/restoring in epilogue/prologue.

The TAILCALL VM is used when the HYBRID VM is not supported, and the compiler
supports the musttail and preserve_none attributes: The HYBRID VM is used when
compiling with GCC, the TAILCALL VM when compiling with Clang>=19 on x86_64 or
aarch64, and the CALL VM otherwise.

This makes binaries built with Clang>=19 as fast as binaries built with GCC.
Before, these were considerably slower (by 2.8% to 44% depending on benchmark,
and by 5% to 77% before 76d7c616bb).

Closes GH-17849
Closes GH-18720
2025-08-22 18:05:52 +02:00
Arnaud Le Blanc
3ddbad9589 Allocate a fast thread-safe-resource id for opcache
Closes GH-19347
2025-08-06 18:02:43 +02:00
Arnaud Le Blanc
9aa8e8825f Remove zend_jit_vm_kind (#19299)
JIT used to not have a compile-time dependency on VM kind, such that a single
build of opcache could work with different VM kinds at runtime. This has been broken
over time and would be difficult to restore. Additionally, as opcache is now
built-in, this would not be useful anymore.

Remove the zend_jit_vm_kind variable.
2025-08-04 15:52:09 +02:00
Niels Dossche
15990de89e Refactor op array loops in JIT (#19335)
Reuse the helper zend_foreach_op_array() that we move to the
zend_optimizer.h header to be usable in opcache.
Note that applying this to other op_array loops is not easy because they either:
- start from EG(persistent_classes_count)
- or only apply to classes
2025-07-31 22:10:06 +02:00
Niels Dossche
0591defd6f Merge branch 'PHP-8.4'
* PHP-8.4:
  Remove dynamic defs from property hooks
  Add missing hooks JIT restart code
2025-07-31 20:22:20 +02:00
Niels Dossche
9ce51dad8b Add missing hooks JIT restart code
Closes GH-19207.
2025-07-31 20:21:40 +02:00
Niels Dossche
16b2fc41a3 Merge branch 'PHP-8.4'
* PHP-8.4:
  Reset global pointers to prevent use-after-free
2025-07-30 09:23:38 +02:00
Niels Dossche
6fda0a5617 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Reset global pointers to prevent use-after-free
2025-07-30 09:23:33 +02:00
Niels Dossche
be9f1d3d56 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Reset global pointers to prevent use-after-free
2025-07-30 09:23:12 +02:00
Niels Dossche
7016ad558b Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1:
  Reset global pointers to prevent use-after-free
2025-07-30 09:22:50 +02:00
Florian Engelhardt
3aaa8d3526 Reset global pointers to prevent use-after-free
Closes GH-19212.
2025-07-30 09:22:15 +02:00
Arnaud Le Blanc
7f7b3cdb90 Introduce zend_vm_opcode_handler_t / zend_vm_opcode_handler_func_t
This reduces the chances of confusion between opcode handlers used by the
VM, and opcode handler functions used for tracing or debugging. Depending
on the VM, zend_vm_opcode_handler_t may not be a function. For instance in
the HYBRID VM this is a label pointer.

Closes GH-19006
2025-07-26 13:20:59 +02:00
Niels Dossche
4a98b36416 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-18898: SEGV zend_jit_op_array_hot with property hooks and preloading
2025-06-30 18:38:30 +02:00
Niels Dossche
53f2aa93ae Fix GH-18898: SEGV zend_jit_op_array_hot with property hooks and preloading
Property hooks were not handled for JIT+trait+preloading.
Split the existing functions that handle op arrays, and add iterations
for property hooks.

Closes GH-18923.
2025-06-30 18:38:11 +02:00
Niels Dossche
9b7252b8bd Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-18639: Internal class aliases can break preloading + JIT
2025-06-23 20:01:49 +02:00
Niels Dossche
ee2c0d7e7f Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-18639: Internal class aliases can break preloading + JIT
2025-06-23 20:01:40 +02:00
Niels Dossche
8e731ca622 Fix GH-18639: Internal class aliases can break preloading + JIT
ZEND_FUNC_INFO() can not be used on internal CE's. If preloading makes a
CE that's an alias of an internal class, the invalid access happens when
setting the FUNC_INFO.

While we could check the class type to be of user code, we can just skip
aliases altogether anyway which may be faster.

Closes GH-18915.
2025-06-23 20:01:15 +02:00
Arnaud Le Blanc
76d7c616bb Pass opline as argument to opcode handlers in CALL VM
This changes the signature of opcode handlers in the CALL VM so that the opline
is passed directly via arguments. This reduces the number of memory operations
on EX(opline), and makes the CALL VM considerably faster.

Additionally, this unifies the CALL and HYBRID VMs a bit, as EX(opline) is now
handled in the same way in both VMs.

This is a part of GH-17849.

Currently we have two VMs:

 * HYBRID: Used when compiling with GCC. execute_data and opline are global
   register variables
 * CALL: Used when compiling with something else. execute_data is passed as
   opcode handler arg, but opline is passed via execute_data->opline
   (EX(opline)).

The Call VM looks like this:

    while (1) {
        ret = execute_data->opline->handler(execute_data);
        if (UNEXPECTED(ret != 0)) {
            if (ret > 0) { // returned by ZEND_VM_ENTER() / ZEND_VM_LEAVE()
                execute_data = EG(current_execute_data);
            } else {       // returned by ZEND_VM_RETURN()
                return;
            }
        }
    }

    // example op handler
    int ZEND_INIT_FCALL_SPEC_CONST_HANDLER(zend_execute_data *execute_data) {
        // load opline
        const zend_op *opline = execute_data->opline;

        // instruction execution

        // dispatch
        // ZEND_VM_NEXT_OPCODE():
        execute_data->opline++;
        return 0; // ZEND_VM_CONTINUE()
    }

Opcode handlers return a positive value to signal that the loop must load a
new execute_data from EG(current_execute_data), typically when entering
or leaving a function.

Here I make the following changes:

 * Pass opline as opcode handler argument
 * Return next opline from opcode handlers
 * ZEND_VM_ENTER / ZEND_VM_LEAVE return opline|(1<<0) to signal that
   execute_data must be reloaded from EG(current_execute_data)

This gives us:

    while (1) {
        opline = opline->handler(execute_data, opline);
        if (UNEXPECTED((uintptr_t) opline & ZEND_VM_ENTER_BIT) {
            opline = opline & ~ZEND_VM_ENTER_BIT;
            if (opline != 0) { // ZEND_VM_ENTER() / ZEND_VM_LEAVE()
                execute_data = EG(current_execute_data);
            } else {           // ZEND_VM_RETURN()
                return;
            }
        }
    }

    // example op handler
    const zend_op * ZEND_INIT_FCALL_SPEC_CONST_HANDLER(zend_execute_data *execute_data, const zend_op *opline) {
        // opline already loaded

        // instruction execution

        // dispatch
        // ZEND_VM_NEXT_OPCODE():
        return ++opline;
    }

bench.php is 23% faster on Linux / x86_64, 18% faster on MacOS / M1.

Symfony Demo is 2.8% faster.

When using the HYBRID VM, JIT'ed code stores execute_data/opline in two fixed
callee-saved registers and rarely touches EX(opline), just like the VM.

Since the registers are callee-saved, the JIT'ed code doesn't have to
save them before calling other functions, and can assume they always
contain execute_data/opline. The code also avoids saving/restoring them in
prologue/epilogue, as execute_ex takes care of that (JIT'ed code is called
exclusively from there).

The CALL VM can now use a fixed register for execute_data/opline as well, but
we can't rely on execute_ex to save the registers for us as it may use these
registers itself. So we have to save/restore the two registers in JIT'ed code
prologue/epilogue.

Closes GH-17952
2025-04-15 18:51:54 +02:00
Arnaud Le Blanc
49891d89fc Merge branch 'PHP-8.4'
* PHP-8.4:
  Save opline in zend_jit_hot_func()
2025-04-15 14:11:53 +02:00
Florian Engelhardt
061b46e09d Save opline in zend_jit_hot_func()
Closes GH-18289
2025-04-15 14:11:32 +02:00
Niels Dossche
5009c236fc Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-15834: Segfault with hook "simple get" cache slot and minimal JIT
2025-03-06 19:37:29 +01:00
Niels Dossche
f6c2e40a11 Fix GH-15834: Segfault with hook "simple get" cache slot and minimal JIT
The FETCH_OBJ_R VM handler has an optimization that directly enters into
a hook if it is a simpler getter hook. This is not compatible with the
minimal JIT because the minimal JIT will try to continue executing the
opcodes after the FETCH_OBJ_R.
To solve this, we check whether the opcode is still the expected one
after the execution of the VM handler. If it is not, we know that we are
going to execute a simple hook. In that case, exit to the VM.

Closes GH-17909.
2025-03-06 19:37:21 +01:00
Niels Dossche
650e59a1c2 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-17428: Assertion failure ext/opcache/jit/zend_jit_ir.c:8940
2025-01-14 22:38:18 +01:00
Niels Dossche
3524702fe1 Fix GH-17428: Assertion failure ext/opcache/jit/zend_jit_ir.c:8940
The code to update the call_level in that case skips the opline itself,
as that's handled by the tail handler, and then wants to set the opline
to the last opline of the block because the code below the switch will
update the call_level for that opline.
However, the test has a block with a single opline (THROW). The block
after that has ZEND_INIT_FCALL, because `i` points to ZEND_INIT_FCALL
now, it erroneously causes the call_level after the switch.

Closes GH-17438.
2025-01-14 22:37:41 +01:00
Christoph M. Becker
1675d32261 Fix printf style issues in Windows specific code (GH-17452)
A couple of calls pass strings as formats (`-Wformat-security`), and
some others mix up types (`-Wformat`).
2025-01-13 11:50:05 +01:00
Niels Dossche
8c443016e9 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-17307: Internal closure causes JIT failure
  Generate inline frameless icall handlers only if the optimization level is set to inline
  Fix GH-15981: Segfault with frameless jumps and minimal JIT
  Fix GH-15833: Segmentation fault (access null pointer) in ext/spl/spl_array.c
2025-01-09 20:01:16 +01:00
Niels Dossche
c790c5b2e7 Generate inline frameless icall handlers only if the optimization level is set to inline 2025-01-09 19:59:10 +01:00
Niels Dossche
72184abd2f Fix GH-15981: Segfault with frameless jumps and minimal JIT
Minimal JIT shouldn't generate a call to the complex handler, but
instead rely on the VM and then check for a two-way jump.
This moves the frameless codegen under the check `JIT_G(opt_level) >=
ZEND_JIT_LEVEL_INLINE`.
2025-01-09 19:59:03 +01:00
Dmitry Stogov
c630801ae7 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-15709: Crashing tests on Windows x64  (#17095)
2024-12-13 02:06:14 +03:00
Dmitry Stogov
ccc6c0f78c Fix GH-15709: Crashing tests on Windows x64 (#17095)
This is a quick fix for the problem.
It'll work while all the JIT-ed functions have the same "fixed stack frame".
Unwinder uses hard-coded unwind data for this "fixed stack frame".

* Preallocate space for Win64 shadow args

* typo

* Setup unwinder for JIT functions

* Revert "Dynamically xfail test case which fails on CI"

This reverts commit 7cc327fd5a.

* Revert "Dynamically xfail test case which fails on CI"

This reverts commit bdde797159.

* Revert "Dynamically xfail test cases which fail on CI (GH-15710)"

This reverts commit 6d5962074f.

* Remove XFAIL sections

* Add hard-coded SEH unwind data for EXITCALL

* Fix unwind data

* Fix Windows multi-process support

* Typo
2024-12-13 02:05:45 +03:00
Niels Dossche
30ec2de83f Merge branch 'PHP-8.4'
* PHP-8.4:
  Extract call_level conditions out to separate functions (#16949)
2024-11-26 21:23:59 +01:00
Niels Dossche
ed556939df Extract call_level conditions out to separate functions (#16949)
These are repeated a couple of times, so centralise it in 2 functions to
reduce repetition and make updating this less error-prone.
2024-11-26 21:22:12 +01:00
Niels Dossche
b3db7f7a9c Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-16879: JIT dead code skipping does not update call_level
2024-11-26 19:27:29 +01:00
Niels Dossche
de30ba5042 Fix GH-16879: JIT dead code skipping does not update call_level
We intend to execute `MATCH_ERROR` in the VM and return to trace a hot
function in BB1. We generate a tail handler and skip all remaining
oplines of BB0. That means the `INIT_FCALL` in BB0 is missed and
`call_level` is not increased to 1. This leads to the assertion
failure.
This patch fixes the issue by updating the `call_level` for the skipped
oplines.

Closes GH-16939.
2024-11-26 19:27:17 +01:00
Christoph M. Becker
91f0b3bc04 Merge branch 'PHP-8.4'
* PHP-8.4:
  Reapply "Merge branch 'PHP-8.3' into PHP-8.4"
2024-11-20 23:25:33 +01:00
Christoph M. Becker
da81b5c8d2 Reapply "Merge branch 'PHP-8.3' into PHP-8.4"
This reverts commit 83ca37483c, and
fixes the previous bad merge.
2024-11-20 23:24:43 +01:00
Christoph M. Becker
7b99da42ee Merge branch 'PHP-8.4'
* PHP-8.4:
  Revert "Merge branch 'PHP-8.3' into PHP-8.4"
2024-11-20 19:46:14 +01:00
Christoph M. Becker
83ca37483c Revert "Merge branch 'PHP-8.3' into PHP-8.4"
This reverts commit ae62779386, reversing
changes made to 19e685ecc4.

This was a bad merge; I'll have a look shortly.
2024-11-20 19:45:36 +01:00
Christoph M. Becker
81779c2863 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-16851: JIT_G(enabled) not set correctly on other threads
2024-11-20 19:15:19 +01:00
Christoph M. Becker
ae62779386 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-16851: JIT_G(enabled) not set correctly on other threads
2024-11-20 19:14:57 +01:00
Christoph M. Becker
58ed759ba7 Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2:
  Fix GH-16851: JIT_G(enabled) not set correctly on other threads
2024-11-20 19:12:40 +01:00
Dylan K. Taylor
ff3b4eca0e Fix GH-16851: JIT_G(enabled) not set correctly on other threads
There doesn't seem to be a thread post-startup hook that runs after
zend_startup_cb() that could be used for this

this fix is similar to accel_startup_ok() as seen here: fc1db70f10/ext/opcache/ZendAccelerator.c (L2631-L2634)

Closes GH-16853.
2024-11-20 19:11:44 +01:00