php-src/ext/opcache/jit
2024-04-08 15:09:14 +02:00
..
dynasm JIT: Add IBT support (#8774) 2022-06-28 08:42:19 +03:00
libudis86 opcache jit fix message format for OpenBSD. 2022-08-19 16:40:29 +01:00
vtune opcache/pcntl/cli: Fixes few functions signatures. 2023-02-09 19:57:48 +00:00
Dockerfile.arm64.example [skip ci] Document how to quickly check if jit .dasc files transpile, how to test the jit in different architectures. (#7768) 2021-12-19 10:12:35 -05:00
Makefile.frag Initial support of JIT/arm64 2021-05-18 15:32:23 +03:00
Makefile.frag.w32 Use capstone disassembler, if available. 2021-03-23 12:39:42 +03:00
README.md [skip ci] Document how to quickly check if jit .dasc files transpile, how to test the jit in different architectures. (#7768) 2021-12-19 10:12:35 -05:00
zend_elf.c Merge branch 'PHP-8.0' 2021-05-06 14:34:28 +02:00
zend_elf.h Update http->https in license (#6945) 2021-05-06 12:16:35 +02:00
zend_jit_arm64.dasc Always load EX(opline) into the current frame in JIT when observers are enabled 2024-04-08 15:09:14 +02:00
zend_jit_arm64.h "http://" -> "https://" 2021-06-03 14:27:41 +03:00
zend_jit_disasm.c Fixed GH-12812: Integer string in variable used as offset produces wrong undefined array key warning (#12817) 2023-11-28 21:19:57 +03:00
zend_jit_gdb.c JIT: Fix .debug_abbrev section in GDB JIT API. 2023-12-11 10:08:55 +03:00
zend_jit_gdb.h Don't directly include zend_jit_gdb.c 2021-06-28 10:08:45 +02:00
zend_jit_helpers.c jit: fixed JIT "Attempt to assign property of non-object" warning emitted at the same time as Error is being thrown 2023-11-27 16:19:35 +00:00
zend_jit_internal.h Fixed GH-12812: Integer string in variable used as offset produces wrong undefined array key warning (#12817) 2023-11-28 21:19:57 +03:00
zend_jit_oprofile.c Update http->https in license (#6945) 2021-05-06 12:16:35 +02:00
zend_jit_perf_dump.c GH-9157: opcache fix build on older macOs releases. 2022-07-27 09:37:54 +01:00
zend_jit_trace.c Fix GH-13508: JITed QM_ASSIGN may be optimized out when op1 is null (#13610) 2024-03-11 15:10:12 +01:00
zend_jit_vm_helpers.c Prevent recording traces started from usupported VM instruction 2024-02-19 13:22:30 +03:00
zend_jit_vtune.c Fixed some compilation warnings 2021-05-18 15:32:31 +03:00
zend_jit_x86.dasc Always load EX(opline) into the current frame in JIT when observers are enabled 2024-04-08 15:09:14 +02:00
zend_jit_x86.h Enable register allocator (it was disabled because ZREG_NUM wasn't 2021-05-18 15:32:31 +03:00
zend_jit.c Fix GH-13232: Segmentation fault will be reported when JIT is off but JIT_debug is still on 2024-01-24 17:47:40 +01:00
zend_jit.h Fix typos in opcache code comments, tests (#7794) 2021-12-19 10:04:09 -05:00

Opcache JIT

This is the implementation of Opcache's JIT (Just-In-Time compiler), This converts the PHP Virtual Machine's opcodes into x64/x86 assembly, on POSIX platforms and Windows.

It generates native code directly from PHP byte-code and information collected by the SSA static analysis framework (a part of the opcache optimizer). Code is usually generated separately for each PHP byte-code instruction. Only a few combinations are considered together (e.g. compare + conditional jump).

See the JIT RFC for more details.

DynAsm

This uses DynAsm (developed for LuaJIT project) for the generation of native code. It's a very lightweight and advanced tool, but does assume good, and very low-level development knowledge of target assembler languages. In the past we tried LLVM, but its code generation speed was almost 100 times slower, making it prohibitively expensive to use.

The unofficial DynASM Documentation has a tutorial, reference, and instruction listing.

In x86 builds, zend_jit_x86.dasc gets automatically converted to zend_jit_x86.c by the bundled dynasm during make.

In arm64 builds, zend_jit_arm64.dasc gets automatically converted to zend_jit_arm64.c by the bundled dynasm during make.

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 Azure 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

Checking dasc files for in a different architecture

The following command can be run to manually check if the modified .dasc code is at least transpilable for an architecture you're not using, e.g.:

For arm64: ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -D ARM64=1 -o ext/opcache/jit/zend_jit_arm64.ignored.c ext/opcache/jit/zend_jit_arm64.dasc

For x86_64: ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -D X64=1 -o ext/opcache/jit/zend_jit_x86.ignored.c ext/opcache/jit/zend_jit_x86.dasc

For i386 (i.e. 32-bit): ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -o ext/opcache/jit/zend_jit_x86.ignored.c ext/opcache/jit/zend_jit_x86.dasc

How to build 32-bit builds on x86_64 environments

Refer to ../../../azure/i386 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 azure/i386/apt.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"