Skip to content

Instantly share code, notes, and snippets.

@shqking
Last active November 23, 2021 09:25
Show Gist options
  • Save shqking/57064db662159f15a496982395c8ce59 to your computer and use it in GitHub Desktop.
Save shqking/57064db662159f15a496982395c8ce59 to your computer and use it in GitHub Desktop.
Try out PHP JIT on AArch64 platform

Try out PHP JIT on AArch64 platform

Pre-reading

I assume you probably tried out the JIT compiler with PHP 8.0 particularly under x86/x86_64 platforms. If not, you can have a basic understanding of PHP JIT with these two blogs.

AArch64 backend

We started this porting task early 2021, and implemented the basic features in May. After months of optimizations and bugfixes, PHP JIT AArch64 backend is expected to be released with PHP 8.1 November, 2021. The latest code can be found in the PHP-8.1 branch or the master branch.

The building and running of JIT/AArch64 are similar to JIT/x86 actually, as they share all the configuration and execution options, except that library capstone should be installed as the disassembler for JIT/AArch64 if you want to check/debug the compiled machine code.

In this blog, I'd like to share my experience on Mac Mini(macOS on Apple silicon). Note that:

  • If you want to cross compile JIT/AArch64 and run it in QEMU, this doc(written by Dmitry Stogov) shows the details.

  • The building of JIT/AArch64 on Linux/AArch64 platform is almost the same to Linux/x86. You may want to refer to the Travis CI setup process in upstream.

How to build JIT/AArch64 on Mac Mini?

System information
$ uname -a
Darwin 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec  2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 arm64 arm64 Macmini9,1 Darwin
$ sw_vers
ProductName:    macOS
ProductVersion: 11.1
BuildVersion:   20C69
$ clang -v
Apple clang version 12.0.0 (clang-1200.0.32.29)
Target: arm64-apple-darwin20.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Install PHP build dependencies with brew
$ brew install re2c bison pkg-config libxml2 apache2 libpq libgd autoconf automake libtool bison libiconv apr-util pcre2 capstone
Build
# Download the source code
$ git clone --branch master --depth 10 https://github.com/php/php-src.git

# Export environments
# Use Bison 3. See https://github.com/asdf-community/asdf-php/issues/61
export PATH="$(brew --prefix bison)/bin:$PATH"
# Export the include/library path. See https://www.capstone-engine.org/documentation.html
export C_INCLUDE_PATH="$(brew --prefix capstone)/include:$C_INCLUDE_PATH"
export DYLD_LIBRARY_PATH="$(brew --prefix capstone)/lib:$DYLD_LIBRARY_PATH"

# Configure
# --enable-debug can be passed for DEBUG mode
# --enable-zts can be passed for ZTS build
# --with-external-pcre: Fix "PEAR package PHP_Archive not installed" issue. See https://qiita.com/shirai_suguru/items/894dc1e71cbc96f4a857
# --with-iconv: Fix "configure: error: Please specify the install prefix of iconv"
$ cd php-src
$ ./buildconf -f
$ ./configure --with-external-pcre=$(brew --prefix pcre2) --with-iconv=$(brew --prefix libiconv)

# Build
$ make -j8
$ ./sapi/cli/php -v
PHP 8.2.0-dev (cli) (built: Oct 10 2021 20:22:43) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.0-dev, Copyright (c) Zend Technologies

# Test
$ make test
Limitations on macOS/AArch64 platform
  • PHP JIT supports three configurations: HYRBID, CALL with global register variables feature(CALL+GRV for short), and CALL+noGRV. Both HYBRID and CALL+GRV enable global register variables feature, however, the default compiler on macOS, i.e. Clang, doesn't support this feature. Hence, only CALL+noGRV is supported.

  • ZTS mode is partially supported on macOS due to the write protection issue.

    Note that Linux/AArch64 platform doesn't have such limitations.

How the JIT-ed machine code looks like?

We follow the same code example used in Try out JIT compiler with PHP 8.0.

$ cat test.php
<?php

$a = 0;

for ($i = 0; $i < 10000; $i++) {
    $a += $i;
}

echo $a;

JIT/AArch64 shares the same JIT control options with JIT/x86. Here shows part of the machine code generated by function JIT:

$ ./sapi/cli/php -d zend_extension=`pwd`/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit_debug=1 -d opcache.jit_buffer_size=32M -d opcache.jit=1205  test.php
JIT$/Users/haosun/work/temp/php-src/test.php: ; (/Users/haosun/work/temp/php-src/test.php)
        stp x29, x30, [sp, #-0x30]!
        stp x27, x28, [sp, #0x20]
        mov x27, x0
        mov x0, x27
        adrp x8, #0x100e59000
        add x8, x8, #0xfc0
        blr x8
        adrp x15, #0x10173e000
        ldr x16, [x15, #0x608]
        cbnz x16, #JIT$$exception_handler
        mov x0, x27
        adrp x8, #0x100e59000
        add x8, x8, #0xfc0
        blr x8
        adrp x15, #0x10173e000
        ldr x16, [x15, #0x608]
        cbnz x16, #JIT$$exception_handler
        b #.L2
.L1:
        adrp x28, #0x102d0a000
        add x28, x28, #0x650
        str x28, [x27]
        mov x0, x27
        adrp x8, #0x100e68000
        add x8, x8, #0x9d4
        blr x8
        adrp x15, #0x10173e000
        ldr x16, [x15, #0x608]
        cbnz x16, #JIT$$exception_handler
        ldrb w15, [x27, #0x68]
        cmp w15, #4
        b.ne #.L5
        ldr x15, [x27, #0x60]
        adds x15, x15, #1
        str x15, [x27, #0x60]
        b.vs #.L4
...

Performance

As shown in our initial evaluation on Kunpeng 920 CPU and Máté Kocsis's evaluation on AWS c6g.4xlarge, JIT/AArch64 can provide similar performance improvements compared to JIT/x86, i.e. significant uplifts to micro benchmarks, but not that much to real-world applications.

Here shows the test results of one micro benchmark, i.e. ./Zend/bench.php.

# Interpreter: jit=0
$ ./sapi/cli/php -d zend_extension=`pwd`/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit_buffer_size=32M -d opcache.jit=0  ./Zend/bench.php
simple             0.020
simplecall         0.008
simpleucall        0.007
simpleudcall       0.007
mandel             0.045
mandel2            0.070
ackermann(7)       0.018
ary(50000)         0.002
ary2(50000)        0.002
ary3(2000)         0.031
fibo(30)           0.059
hash1(50000)       0.004
hash2(500)         0.006
heapsort(20000)    0.016
matrix(20)         0.016
nestedloop(12)     0.031
sieve(30)          0.009
strcat(200000)     0.003
------------------------
Total              0.355

# Function JIT: jit=1205
$ ./sapi/cli/php -d zend_extension=`pwd`/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit_buffer_size=32M -d opcache.jit=1205  ./Zend/bench.php
simple             0.002
simplecall         0.001
simpleucall        0.001
simpleudcall       0.001
mandel             0.009
mandel2            0.011
ackermann(7)       0.009
ary(50000)         0.001
ary2(50000)        0.001
ary3(2000)         0.007
fibo(30)           0.021
hash1(50000)       0.002
hash2(500)         0.003
heapsort(20000)    0.006
matrix(20)         0.006
nestedloop(12)     0.006
sieve(30)          0.002
strcat(200000)     0.002
------------------------
Total              0.091

# Tracing JIT: jit=1254
$ ./sapi/cli/php -d zend_extension=`pwd`/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit_buffer_size=32M -d opcache.jit=1254  ./Zend/bench
.php
simple             0.002
simplecall         0.001
simpleucall        0.001
simpleudcall       0.001
mandel             0.010
mandel2            0.011
ackermann(7)       0.005
ary(50000)         0.002
ary2(50000)        0.001
ary3(2000)         0.006
fibo(30)           0.013
hash1(50000)       0.002
hash2(500)         0.003
heapsort(20000)    0.005
matrix(20)         0.003
nestedloop(12)     0.003
sieve(30)          0.003
strcat(200000)     0.002
------------------------
Total              0.073

TL;DR

Feel free to contact me if you have any issue on JIT/AArch64 backend. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment