1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00
Files
archived-php-src/ext/opcache/jit
Ilia Alshanetsky e1a3a4c9a4 Fix GH-21267: JIT infinite loop on FETCH_OBJ_R with IS_UNDEF property (#21368)
When the JIT defers the IS_UNDEF check for FETCH_OBJ_R to the result
type guard, the deoptimization escape path dispatches to opline->handler
via the trace_escape stub. If opline->handler has been overwritten with
JIT code (e.g. a function entry trace), this creates an infinite loop.

Fix by dispatching to the original VM handler (orig_handler from the
trace extension) instead of going through the trace_escape stub. This
avoids the extra IS_UNDEF guard on every property read while correctly
handling the rare IS_UNDEF case during deoptimization.

Also set current_op_array in zend_jit_trace_exit_to_vm so that the
blacklisted exit deoptimizer can resolve orig_handler, covering the
case where side trace compilation is exhausted.

Closes GH-21368.
2026-03-16 21:31:49 +03:00
..
2026-02-25 17:18:25 +03:00
2024-01-08 23:32:30 +01:00
2025-10-13 16:16:39 +02:00

Opcache JIT

This is the implementation of Opcache's JIT (Just-In-Time compiler), This converts the PHP Virtual Machine's opcodes into Intermediate Representation and uses IR - Lightweight JIT Compilation Framework to produce optimized native code. The necessary part of the IR Framework is embedded into php-src.

Running tests of the JIT

Then, to test the JIT, e.g. with opcache.jit=tracing, an example command based on what is used to test in CI:

make test TESTS="-d opcache.jit_buffer_size=16M -d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit=tracing --repeat 2 --show-diff -j$(nproc) ext/opcache Zend"
  • opcache.jit_buffer_size=16M enables the JIT in tests by providing 16 megabytes of memory to use with the JIT to test with.
  • opcache.protect_memory=1 will detect writing to memory that is meant to be read-only, which is sometimes the cause of opcache bugs.
  • --repeat 2 is optional, but used in CI since some JIT bugs only show up after processing a request multiple times (the first request compiles the trace and the second executes it)
  • -j$(nproc) runs as many workers to run tests as there are CPUs.
  • ext/opcache/ and Zend are the folders with the tests to run, in this case opcache and the Zend engine itself. If no folders are provided, all tests are run.

When investigating test failures such as segmentation faults, configuring the build of php with --enable-address-sanitizer to enable AddressSanitizer is often useful.

Some of the time, adding -m --show-mem to the TESTS configuration is also useful to test with valgrind to detect out of bounds memory accesses. Using valgrind is slower at detecting invalid memory read/writes than AddressSanitizer when running large numbers of tests, but does not require rebuilding php.

Note that the JIT supports 3 different architectures: X86_64, i386, and arm64.

Miscellaneous

How to build 32-bit builds on x86_64 environments

Refer to ../../../.github/workflows/push.yml for examples of dependencies to install.

If you are running this natively (outside of Docker or a VM):

  • Consider running in docker/a VM instead if you are unfamiliar with this.
  • Avoid purging packages.
  • Avoid -y - if the package manager warns you that the dependencies conflict then don't try to force install them.

Prerequisites for 32-bit builds

This assumes you are using a Debian-based Linux distribution and have already set up prerequisites for regular development.

sudo dpkg --add-architecture i386
sudo apt-get update -y
# As well as anything else from .github/actions/apt-x32/action.yml that you're testing locally
sudo apt-get install \
    gcc-multilib g++-multilib \
    libxml2-dev:i386 \
    libc6:i386

Compiling 32-bit builds

This assumes you are using a Debian-based Linux distribution and have already set up prerequisites for 32-bit development.

export LDFLAGS=-L/usr/lib/i386-linux-gnu
export CFLAGS='-m32'
export CXXFLAGS='-m32'
export PKG_CONFIG=/usr/bin/i686-linux-gnu-pkg-config
./configure --disable-all --enable-opcache --build=i686-pc-linux-gnu
make -j$(nproc)

Running tests of the JIT on 32-bit builds

See the section "Running tests of the JIT".

Testing the jit with arm64 on x86 computers

https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ may be useful for local development.

Note that this is slower than compiling and testing natively.

# After following steps in https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
cp .gitignore .dockerignore
echo .git >> .dockerignore

docker build --network=host -t php-src-arm64-example -f ext/opcache/jit/Dockerfile.arm64.example .
docker run -it --rm php-src-arm64-example

Then, the docker image can be used to run tests with make test. For example, to test ext/opcache in parallel with the tracing JIT enabled:

docker run -it php-src-arms-example make test TESTS="-d opcache.jit_buffer_size=16M -d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit=tracing --repeat 2 --show-diff -j$(nproc) ext/opcache"