xref: /PHP-8.2/ext/opcache/jit/README.md (revision 9a594174)
1Opcache JIT
2===========
3
4This is the implementation of Opcache's JIT (Just-In-Time compiler),
5This converts the PHP Virtual Machine's opcodes into x64/x86 assembly,
6on POSIX platforms and Windows.
7
8It generates native code directly from PHP byte-code and information collected
9by the SSA static analysis framework (a part of the opcache optimizer).
10Code is usually generated separately for each PHP byte-code instruction. Only
11a few combinations are considered together (e.g. compare + conditional jump).
12
13See [the JIT RFC](https://wiki.php.net/rfc/jit) for more details.
14
15DynAsm
16------
17
18This uses [DynAsm](https://luajit.org/dynasm.html) (developed for LuaJIT project)
19for the generation of native code.  It's a very lightweight and advanced tool,
20but does assume good, and very low-level development knowledge of target
21assembler languages. In the past we tried LLVM, but its code generation speed
22was almost 100 times slower, making it prohibitively expensive to use.
23
24[The unofficial DynASM Documentation](https://corsix.github.io/dynasm-doc/tutorial.html)
25has a tutorial, reference, and instruction listing.
26
27In x86 builds, `zend_jit_x86.dasc` gets automatically converted to `zend_jit_x86.c` by the bundled
28`dynasm` during `make`.
29
30In arm64 builds, `zend_jit_arm64.dasc` gets automatically converted to `zend_jit_arm64.c` by the bundled
31`dynasm` during `make`.
32
33Running tests of the JIT
34------------------------
35
36Then, to test the JIT, e.g. with opcache.jit=tracing, an example command
37based on what is used to test in Azure CI:
38
39```
40make 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"
41```
42
43- `opcache.jit_buffer_size=16M` enables the JIT in tests by providing 16 megabytes of
44  memory to use with the JIT to test with.
45- `opcache.protect_memory=1` will detect writing to memory that is meant to be
46  read-only, which is sometimes the cause of opcache bugs.
47- `--repeat 2` is optional, but used in CI since some JIT bugs only show up after processing a
48  request multiple times (the first request compiles the trace and the second executes it)
49- `-j$(nproc)` runs as many workers to run tests as there are CPUs.
50- `ext/opcache/` and `Zend` are the folders with the tests to run, in this case opcache
51  and the Zend engine itself.  If no folders are provided, all tests are run.
52
53When investigating test failures such as segmentation faults,
54configuring the build of php with `--enable-address-sanitizer` to enable
55[AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) is often useful.
56
57Some of the time, adding `-m --show-mem` to the `TESTS` configuration is also useful to test with [valgrind](https://valgrind.org/) to detect out of bounds memory accesses.
58Using valgrind is slower at detecting invalid memory read/writes than AddressSanitizer when running large numbers of tests, but does not require rebuilding php.
59
60Note that the JIT supports 3 different architectures: `X86_64`, `i386`, and `arm64`.
61
62Miscellaneous
63-------------
64
65### Checking dasc files for in a different architecture
66
67The following command can be run to manually check if the modified `.dasc code` is at least transpilable
68for an architecture you're not using, e.g.:
69
70For 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`
71
72For 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`
73
74For 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`
75
76### How to build 32-bit builds on x86_64 environments
77
78Refer to [../../../azure/i386](../../../azure/i386/apt.yml) for examples of
79dependencies to install.
80
81If you are running this natively (outside of Docker or a VM):
82
83- Consider running in docker/a VM instead if you are unfamiliar with this.
84- Avoid purging packages.
85- Avoid `-y` - if the package manager warns you that the dependencies conflict
86  then **don't** try to force install them.
87
88#### Prerequisites for 32-bit builds
89
90This assumes you are using a Debian-based Linux distribution and have already
91set up prerequisites for regular development.
92
93```
94sudo dpkg --add-architecture i386
95sudo apt-get update -y
96# As well as anything else from azure/i386/apt.yml that you're testing locally
97sudo apt-get install \
98    gcc-multilib g++-multilib \
99    libxml2-dev:i386 \
100    libc6:i386
101```
102
103#### Compiling 32-bit builds
104
105This assumes you are using a Debian-based Linux distribution and have already
106set up prerequisites for 32-bit development.
107
108```
109export LDFLAGS=-L/usr/lib/i386-linux-gnu
110export CFLAGS='-m32'
111export CXXFLAGS='-m32'
112export PKG_CONFIG=/usr/bin/i686-linux-gnu-pkg-config
113./configure --disable-all --enable-opcache --build=i686-pc-linux-gnu
114make -j$(nproc)
115```
116
117#### Running tests of the JIT on 32-bit builds
118
119See the section "Running tests of the JIT".
120
121### Testing the jit with arm64 on x86 computers
122
123https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
124may be useful for local development.
125
126Note that this is slower than compiling and testing natively.
127
128```
129# After following steps in https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
130cp .gitignore .dockerignore
131echo .git >> .dockerignore
132
133docker build --network=host -t php-src-arm64-example -f ext/opcache/jit/Dockerfile.arm64.example .
134docker run -it --rm php-src-arm64-example
135```
136
137Then, the docker image can be used to run tests with `make test`.
138For example, to test `ext/opcache` in parallel with the tracing JIT enabled:
139
140```
141docker 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"
142```
143