1
0
mirror of https://github.com/php/php-src.git synced 2026-04-29 03:03:26 +02:00
Commit Graph

2375 Commits

Author SHA1 Message Date
Arnaud Le Blanc 7b4c14dc10 Make OPcache non-optional
This removes the --enable-opcache/--disable-opcache configure switch. OPcache
is now always builtin. The default value of opcache.enable and
opcache.enable_cli is unchanged.

RFC: https://wiki.php.net/rfc/make_opcache_required

Closes GH-18961.

Co-authored-by: Tim Düsterhus <tim@tideways-gmbh.com>
2025-07-27 09:40:22 +02:00
Arnaud Le Blanc 73b1ebfa20 Fix linker failure when building Opcache statically
We use linker relocations to fetch the TLS index and offset of _tsrm_ls_cache.
When building Opcache statically, linkers may attempt to optimize that into a
more efficient code sequence (relaxing from "General Dynamic" to "Local Exec"
model [1]). Unfortunately, linkers will fail, rather than ignore our
relocations, when they don't recognize the exact code sequence they are
expecting.

This results in errors as reported by GH-15074:

    TLS transition from R_X86_64_TLSGD to R_X86_64_GOTTPOFF against
    `_tsrm_ls_cache' at 0x12fc3 in section `.text' failed"

Here I take a different approach:

 * Emit the exact full code sequence expected by linkers
 * Extract the TLS index/offset by inspecting the linked ASM code, rather than
   executing it (execution would give us the thread-local address).
 * We detect when the code was relaxed, in which case we can extract the TCB
   offset instead.
 * This is done in a conservative way so that if the linker did something we
   didn't expect, we fallback to a safer (but slower) mechanism.

One additional benefit of that is we are now able to use the Local Exec model in
more cases, in JIT'ed code. This makes non-glibc builds faster in these cases.

Closes GH-18939.

Related RFC: https://wiki.php.net/rfc/make_opcache_required.

[1] https://www.akkadia.org/drepper/tls.pdf
2025-07-26 16:43:41 +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
Dmitry Stogov 2beb44a80b Merge branch 'PHP-8.4'
* PHP-8.4:
  Revert "Update IR"
2025-07-14 14:28:55 +03:00
Dmitry Stogov 9abb0fb0c4 Revert "Update IR"
This reverts commit e8ae27bf8a.

Something wrong in irrducable loops habdling that causes ir_find_loop()
to stuck. See https://github.com/php/php-src/issues/19104
2025-07-14 14:27:05 +03:00
Dmitry Stogov dd69b65638 Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-07-07 14:03:36 +03:00
Dmitry Stogov e8ae27bf8a Update IR
IR commit: af6dc83bcd91c3123f40efcdcbeba8794b9b2abf
2025-07-07 14:03:11 +03: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 6eed02bacc Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-18899: JIT function crash when emitting undefined variable warning and opline is not set yet
2025-06-23 20:10:27 +02:00
Niels Dossche 56c4ddfaf6 Fix GH-18899: JIT function crash when emitting undefined variable warning and opline is not set yet
The crash happens because EX(opline) is attempted to be accessed but
it's not set yet.

Closes GH-18904.
2025-06-23 20:10:09 +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
Niels Dossche 4c7220322b Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-18743: Incompatibility in Inline TLS Assembly on Alpine 3.22
2025-06-09 11:41:08 +02:00
Niels Dossche 7a0beb4867 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Fix GH-18743: Incompatibility in Inline TLS Assembly on Alpine 3.22
2025-06-09 11:41:02 +02:00
Niels Dossche b3c8afe272 Fix GH-18743: Incompatibility in Inline TLS Assembly on Alpine 3.22
GAS started checking the relocation for tlsgd: it must use the %rdi
register. However, the inline assembly now uses %rax instead.
Fix it by changing the "=a" output register to "=D".
Source: https://github.com/bminor/binutils-gdb/blob/ec181e1710e37007a8d95c284609bfaa5868d086/gas/config/tc-i386.c#L6793

gottpoff is unaffected.

Closes GH-18779.
2025-06-09 11:39:34 +02:00
Dmitry Stogov baeb981746 Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-06-02 09:24:10 +03:00
Dmitry Stogov 81593cfc6a Update IR
IR commit: e4343be0082897510c40a1b57baff427c6858878
2025-06-02 09:23:39 +03:00
Arnaud Le Blanc 16ca097ef2 Do not exit to VM when setting undefined prop in constructor
JIT'ed ASSIGN_OBJ expressions will exit to VM when the prop is undef. However,
in a constructor it's very likely the case. Therefore most traces with `new`
expressions will exit to VM.

Here I ensure that we don't need to exit to VM when it's likely that the
prop will be undef.

In the function JIT we compile a slow path to handle such properties,
but not in the tracing JIT, assumingly to reduce code size. Here I enable
compilation of the slow path in the tracing JIT when it's likely the prop
will be undef. Quite conveniently we already record the prop type during
tracing, so I use that to make the decision.

This results in a 1.20% wall time improvement on the symfony demo benchmark
with 20 warmup requests.

Closes GH-18576
2025-05-19 12:42:11 +02:00
Arnaud Le Blanc 1de16c7f15 Merge branch 'PHP-8.4'
* PHP-8.4:
  Snapshotted poly_func / poly_this may be spilled
2025-05-14 12:30:36 +02:00
Arnaud Le Blanc 18276a8b42 Snapshotted poly_func / poly_this may be spilled
Polymorphic calls pass this and the function to side traces via snapshotting.
However, we assume that this/func are in registers, when in fact they may be
spilled.

Here I update snapshotting of poly_func/poly_this to support spilling:

 - In zend_jit_snapshot_handler, keep track of the C stack offset
   of the spilled register, in a way similar to how stack variables.
 - In zend_jit_start, do not pre-load the registers if they were spilled.
 - In zend_jit_trace_exit / zend_jit_trace_deoptimization, load from the
   stack if the register was spilled.
 - Store a reference to poly_func/poly_this in zend_jit_ctx so we can use that
   directly in the side trace.

Closes GH-18408
2025-05-14 12:27:57 +02:00
Ilija Tovilo 59056937bf Fix use-of-uninitialized-value with exception on deprecated const access
Closes GH-18478
2025-05-02 11:57:16 +02:00
Niels Dossche 39a56a1687 Fix opcode length of ZEND_DECLARE_ATTRIBUTED_CONST in JIT-IR component (#18457)
Introduced in 3f03f7ed.
2025-04-30 08:17:56 +02:00
DanielEScherzer 3f03f7ed3d [RFC] Add support for attributes on compile-time constants
https://wiki.php.net/rfc/attributes-on-constants
2025-04-29 11:53:09 -07:00
Arnaud Le Blanc 2fea4efa8f Fix merge error (#18453) 2025-04-29 17:06:59 +02:00
Arnaud Le Blanc 3737e0aa6f Merge branch 'PHP-8.4'
* PHP-8.4:
  JIT: Check exception on exit
2025-04-29 10:55:38 +02:00
Arnaud Le Blanc 978c01ce15 JIT: Check exception on exit
Add a new exit flag (ZEND_JIT_EXIT_CHECK_EXCEPTION) that enables exception
checking during exit/deoptimization.

We already checked for exceptions during exit/deoptimization, but only when
ZEND_JIT_EXIT_FREE_OP1 or ZEND_JIT_EXIT_FREE_OP2 were set (presumably to
handle exceptions thrown during dtor). The new flag makes it possible to request
it explicitly.

This also fixes two issues in zend_jit_trace_exit():

- By returning 1, we were telling the caller (zend_jit_trace_exit_stub()) to
  execute the original op handler of EG(current_execute_data)->opline, but in
  reality we want to execute EX(opline), which should be EG(exception_op).

- EX(opline) is set to the value of %r15 in zend_jit_trace_exit_stub() before
  calling zend_jit_trace_exit(), but this may be the address of a
  zend_execute_data when the register is being reused to cache EX(call).

Fixes GH-18262
Closes GH-18297
2025-04-29 10:55:31 +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 4a12a9f3e9 Fix GH-18294: assertion failure zend_jit_ir.c
The JIT helper `zend_jit_assign_op_to_typed_ref` expects a `zval*` as an
argument, so we have to store to the stack if OP1_DATA(=op3) is in a
register.

Closes GH-18299.
2025-04-11 17:54:19 +02:00
Niels Dossche e11a47f18b Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix reproducibility of test GH-17190
  Fix GH-18294: assertion failure zend_jit_ir.c
2025-04-11 17:54:35 +02:00
Niels Dossche a32f491855 Remove cache slot from ZEND_VERIFY_TYPE and arg RECV opcodes (#18258) 2025-04-07 19:50:48 +02:00
Dmitry Stogov cb3bca249a Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-04-02 16:20:49 +03:00
Dmitry Stogov 79dc7a2d26 Update IR
IR commit: 8d17022fb61ebfed9f6be81a8182ea31202697ed
2025-04-02 16:20:09 +03:00
Tim Düsterhus 5544be7018 RFC: Marking return values as important (#[\NoDiscard]) (#17599)
RFC: https://wiki.php.net/rfc/marking_return_value_as_important

Co-authored-by: Volker Dusch <volker@tideways-gmbh.com>
2025-04-02 09:35:29 +02:00
Dmitry Stogov fd454300b6 Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-03-27 22:25:11 +03:00
Dmitry Stogov 2b9840894d Update IR
IR commit: dd228777b67334d8ed51de44f427d66d4ac99c08
2025-03-27 22:24:46 +03:00
Dmitry Stogov 7b069790ca Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-03-20 01:11:33 +03:00
Dmitry Stogov b932c267f8 Update IR
IR commit: 3d0124a06ee4321e1305f893b74840033d939e88
2025-03-20 01:10:56 +03:00
Niels Dossche b6e55d9856 Merge branch 'PHP-8.4'
* PHP-8.4:
  Correct check for maximum string length in JIT helpers
2025-03-13 23:49:48 +01:00
Niels Dossche f4ba3564f5 Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3:
  Correct check for maximum string length in JIT helpers
2025-03-13 23:49:42 +01:00
Niels Dossche f18d0a1212 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix GH-18037: SEGV Zend/zend_execute.c
2025-03-13 23:48:35 +01:00
Niels Dossche 413938143b Fix GH-18037: SEGV Zend/zend_execute.c
A frameless icall with 3 arguments is a special case because it uses
OP_DATA, but this was not added to the list, so the opline pointed to
the wrong address resulting in UBSAN report or crash.

Closes GH-18048.
2025-03-13 23:48:24 +01:00
Niels Dossche a7d2703246 Correct check for maximum string length in JIT helpers
This is a bit of a theoretical issue, but the maximum string length is
actually ZSTR_MAX_LEN instead of SIZE_MAX. The resulting check is a bit
slower but should still be relatively cheap.

Closes GH-18049.
2025-03-13 23:47:45 +01:00
Dmitry Stogov 7faa3decd9 Merge branch 'PHP-8.4'
* PHP-8.4:
  Update IR
2025-03-13 03:08:16 +03:00
Dmitry Stogov ae67eb0490 Update IR
IR commit: 8bb0acca45a7b0f12691f4258e41462599efbd74
2025-03-13 03:07:43 +03:00
Dmitry Stogov af704ae067 Merge branch 'PHP-8.4'
* PHP-8.4:
  Fix register allocarion for result of ASSIGN_OBJ (#17994)
2025-03-07 19:30:47 +03:00
Dmitry Stogov 1b9d659c3f Fix register allocarion for result of ASSIGN_OBJ (#17994)
This fixes few failures in Symfony unit tests with function JIT
2025-03-07 19:28:51 +03:00