The offset becomes stale if the environment changes. We're currently relying on
other factors in the environment staying constant, e.g. send types. But this
seems to be the worst offender.
Partially addresses GH-17733
Closes GH-20328
The dynamic loader, starting around version 1284, patches the thunk emitted for
thread local variables by the compiler, so that its format changes from
struct Thunk {
void *func;
size_t module;
size_t offset;
}
to
struct Thunk_v2 {
void *func;
uint32_t module;
uint32_t offset;
// other fields
}
which has the same size, but not the same layout.
This is mentionned in
9307719dd8/libdyld/ThreadLocalVariables.h (L90)
As a result, access to thread specific variables in JIT is broken.
Fix by using the new layout when the new dynamic loader is in use.
Closes GH-20121
preload_load() reads EG(class_table) and EG(function_table), but these may not
be initialized. Move these accesses out of preload_load().
Closes GH-20081
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
Both processes race to compile warning_replay.inc. Whichever is first will get
to persist the script. The loser will use the script that is already persisted,
and the script that was just compiled is freed.
However, EG(errors) and persistent_script->warnings still refer to the same
allocation, and EG(errors) becomes a dangling pointer. To solve this, we simply
don't free warnings from free_persistent_script() anymore to maintain exclusive
ownership for EG(errors).
Furthermore, we need to adjust a call to zend_emit_recorded_errors() that would
previously use EG(errors), even when persistent_script has been swapped out.
Fixes GH-19984
Closes GH-19995
zend_jit_fetch_obj_r_slow_ex() may be used by the function JIT, which doesn't
rely on guards to handle references. Therefore it must deref the property value.
Other variants of zend_jit_fetch_obj_*_slow_ex can not be used used in function
JIT.
Fixes GH-19831
Closes GH-19838
If an exception _and_ a warning (or deprecation) is emitted, then the
result is destroyed twice. Use an `else if` to prevent this.
This is tested via zend_test because the deprecation that triggered the
original reproducer may disappear in the future.
Closes GH-19793.
When the assumption that (PRE|POST)_(INC|DEC) overflows turns out to be
false and we exit, effects are lost if op1 or result were in regs.
Fix by updating the stack map before creating the exit point.
Fixes GH-19669
Closes GH-19680
When the assumption that (PRE|POST)_(INC|DEC) overflows turns out to be
false and we exit, effects are lost if op1 or result were in regs.
Fix by updating the stack map before creating the exit point.
Fixes GH-19669
Closes GH-19680
Building opcache into the main executable breaks opcache.huge_code_pages,
as we were relying on the fact that accel_remap_huge_pages() is not in the
same mapping as the main text segment.
Here I ensure that accel_remap_huge_pages() is placed out of the text
segment, and remap only the text segment. This approach is used in [1].
Closes GH-19388.
[1] 676bb7dec3/large_page-c/large_page.c (L260).
On aarch64 we must set IR_USE_FRAME_POINTER to ensure that LR/x30 is
saved. Also, fixed_stack_frame_size must be n*16, not n*16+8 like on x86.
Fixes GH-19601
Closes GH-19630
* opcache: Do not emit “temporary enabling” message when OPcache is already active
An easy way to accidentally enable OPcache “temporarily” is by using
`php_admin_value[opcache.enable]=1` within a FPM pool’s configuration, since
the `php_admin_value` settings mostly behave like settings in php.ini, with
many OPcache INI settings being a notable exception.
As long as OPcache is already enabled within php.ini (or simply by default),
emitting a warning for `php_admin_value[opcache.enable]=1` or similar is going
to be confusing, since is not actually temporarily enabling anything.
A follow-up commit will also try to detect this kind of incorrect configuration
and try to provide better advice for cases where OPcache is actually not yet
enabled.
* opcache: Improve error message when OPcache is enabled dynamically
The error message will now advice on the `php_admin_value[opcache.enable]=1`
mistake. It will also send the message to OPcache’s logging facility instead of
the regular error handling logic during startup so that it will not be made
available to `error_get_last()`, since it is related to a specific request and
thus not actionable by a script either.
php/php-src#19146 made a related change to `opcache.memory_consumption`.
* opcache: Fix typo in warning message
* opcache: Use more formal language in warning message
Blacklisted side traces (aka JIT'ed exits) may return the previous opline
after calling the original op handler. As a result, the op handler is called
again by the VM.
Fix this by always returning the opline returned by the original op handler.
Always use zend_jit_vm_enter(jit, ref) to signal the VM that it must reload
EG(current_execute_data) as it may have changed during the execution of
the trace.
Fixes GH-19486
Closes GH-19535
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