SlideShare a Scribd company logo
JIT のコードを読んでみた
内山 雄司 (@y__uti)
2016-12-11 第7回闇PHP勉強会
自己紹介
内山 雄司 (@y__uti)
◦ http://y-uti.hatenablog.jp/ (phpusers-ja)
仕事
◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています
興味
◦ プログラミング言語処理系
◦ 機械学習
2016-12-11 第七回 闇PHP勉強会 2
JIT for PHP project
2016-12-11 第七回 闇PHP勉強会 3
http://news.php.net/php.internals/95531
(第 107 回 PHP 勉強会での発表資料より)
ベンチマーク結果
2016-12-11 第七回 闇PHP勉強会 4
0.00
0.05
0.10
0.15
0.20
0.25
0.30
0.35
0.40
実行時間(秒)
5.6.27 7.0.12 7.1.0RC4 JIT (0edf1e9)
Intel Core i5-3337U 1.80GHz
2GB Memory
CentOS 7 (VM on Windows7)
各 10 回の実行の平均
JIT による速度向上
2016-12-11 第七回 闇PHP勉強会 5
http://news.php.net/php.internals/96613
開発者 (Dmitry Stogov 氏) による 2016-10-26 の投稿
◦ bench.php では 3 倍の速度向上
◦ "real-life apps" では大きな差はない
本日の発表内容
どのように実装されているかソースコードを追ってみました
1. プログラム実行時に JIT コンパイルを行う仕掛け
◦ OPcache を拡張して opcode handler を差し替え
◦ 関数の実行ごとに JIT 起動条件を確認する
2. JIT コンパイルの処理内容
◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成
◦ 生成されたコードを実行するように opcode handler を再度差し替え
この一枚で理解できてしまったガチ勢はマサカリの準備を!
2016-12-11 第七回 闇PHP勉強会 6
JIT コンパイルの仕掛け
2016-12-11 第七回 闇PHP勉強会 7
PHP の処理の流れ
以下のコマンドを実行すると何が起きるのか
2016-12-11 第七回 闇PHP勉強会 8
$ php foo.php
処理の流れ
1. sapi/cli/php_cli.c の main 関数から処理が始まり
2. Zend/zend.c の zend_execute_scripts 関数が呼ばれ
3. zend_compile_file 関数でコンパイルして
4. zend_execute 関数で実行する
zend_execute_scripts 関数
in Zend/zend.c
PHP 処理系によるプログラム実行の「かなめ」
◦ ファイルをバイトコード命令列にコンパイルする
2016-12-11 第七回 闇PHP勉強会 9
op_array = zend_compile_file(file_handle, type);
zend_execute(op_array, retval);
◦ バイトコード命令列を実行する
op_array 構造体
in Zend/zend_compile.h
バイトコード命令列 + 各種情報
◦ 関数ごとに一つ
◦ トップレベルに書かれたコード用に一つ
2016-12-11 第七回 闇PHP勉強会 10
struct _zend_op {
const void *handler;
znode_op op1;
znode_op op2;
znode_op result;
uint32_t extended_value;
uint32_t lineno;
zend_uchar opcode;
zend_uchar op1_type;
zend_uchar op2_type;
zend_uchar result_type;
};
struct _zend_op_array {
zend_uchar type;
zend_uchar arg_flags[3];
...
uint32_t last;
zend_op *opcodes;
...
};
一対多
last が命令数を表す
execute_ex 関数
in Zend/zend_vm_execute.h
各バイトコード命令 (zend_op 構造体) の handler を実行
2016-12-11 第七回 闇PHP勉強会 11
...
while (1) {
...
if (UNEXPECTED(
(ret = ((opcode_handler_t)OPLINE->handler)()) != 0))
{
...
return;
}
...
}
...
読みやすさのため、一部のマクロを展開して掲載しています
JIT コンパイル付きの実行
op_array (関数) ごとに以下の処理を行う
2016-12-11 第七回 闇PHP勉強会 12
コンパイルする?
元のコードを実行
コード生成
生成された
コードを実行
開始
終了
No Yes
JIT コンパイルの準備
(通常の) コンパイル時に handler を書き換える
2016-12-11 第七回 闇PHP勉強会 13
コンパイルする?
元のコードを実行
コード生成
生成された
コードを実行
開始
終了
No Yes
handler
handler
OPcache の処理
起動時に zend_compile_file 関数を置き換える
in ext/opcache/ZendAccelerator.c
2016-12-11 第七回 闇PHP勉強会 14
static int accel_startup(zend_extension *extension)
{
...
/* Override compiler */
accelerator_orig_compile_file = zend_compile_file;
zend_compile_file = persistent_compile_file;
...
}
persistent_compile_file 関数がやること
◦ コンパイルしたデータ構造をキャッシュ (本来の仕事)
◦ OPcache 独自の最適化 (おまけ?)
◦ JIT コンパイルのための handler 書き換え (JIT を有効にしたときのみ)[New!]
persistent_compile_file 関数
in ext/opcache/ZendAccelerator.c
2016-12-11 第七回 闇PHP勉強会 15
/* zend_compile() replacement */
zend_op_array
*persistent_compile_file(zend_file_handle *file_handle, int type)
{
...
// キャッシュ済みでなければ
if (!persistent_script) {
// コンパイルして
persistent_script = opcache_compile_file(...);
if (persistent_script) {
// キャッシュする
persistent_script = cache_script_in_shared_memory(...);
}
...
JIT までの道のり
in ext/opcache/ZendAccelerator.c
2016-12-11 第七回 闇PHP勉強会 16
static zend_persistent_script *cache_script_in_shared_memory( ... )
in ext/opcache/zend_persist.c
zend_persistent_script *zend_accel_script_persist( ... )
static void zend_persist_op_array_ex( ... )
in ext/opcache/zend_persist.c
in ext/opcache/jit/zend_jit.c
ZEND_API int zend_jit_op_array(
zend_op_array *op_array, zend_script *script)
◦ この関数で handler を差し替えている
JIT コンパイルのトリガー
in ext/opcache/jit/zend_jit.h
いくつかの選択肢が提供されている
◦ ZEND_JIT_ON_SCRIPT_LOAD スクリプトのロード時
◦ ZEND_JIT_ON_FIRST_EXEC 最初の実行
◦ ZEND_JIT_ON_PROF_REQUEST 実行頻度 (割合) の高い関数
◦ ZEND_JIT_ON_HOT_COUNTERS 実行回数が閾値を超えたとき
◦ ZEND_JIT_ON_DOC_COMMENT DocComment の指定に従う
2016-12-11 第七回 闇PHP勉強会 17
ZEND_JIT_ON_FIRST_EXEC
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 18
opline->handler = (const void*)zend_runtime_jit;
zend_runtime_jit 関数は無条件に JIT コンパイルを開始
in ext/opcache/jit/zend_jit.c
static void ZEND_FASTCALL zend_runtime_jit(void)
{
...
opline->handler = ZEND_FUNC_INFO(op_array); // 本来の handler を復元
...
/* perform real JIT for this function */
zend_real_jit_func(op_array, NULL, NULL); // JIT コンパイルを実行
...
}
ZEND_JIT_ON_PROF_REQUEST
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 19
opline->handler = zend_jit_profile_helper;
zend_jit_profile_helper は op_array ごとの実行回数を数える
in ext/opcache/jit/zend_jit_vm_helpers.c
void ZEND_FASTCALL zend_jit_profile_helper(void)
{
zend_op_array *op_array = (zend_op_array*)EX(func);
const void *handler =
(const void*)ZEND_FUNC_INFO(op_array); // 本来の handler を取得
++(ZEND_COUNTER_INFO(op_array)); // この op_array の実行回数
++zend_jit_profile_counter; // 全 op_array の実行回数の総和
return ((zend_vm_opcode_handler_t)handler)();
// 本来の処理を実行
}
ZEND_JIT_ON_PROF_REQUEST
in ext/opcache/jit/zend_jit.c
OPcache が deactivate されるときに JIT コンパイルを行う
2016-12-11 第七回 闇PHP勉強会 20
void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method)
{
...
zend_ulong counter = (zend_ulong)ZEND_COUNTER_INFO(op_array);
if (((double)counter / (double)zend_jit_profile_counter)
> ZEND_JIT_PROF_THRESHOLD) {
zend_real_jit_func(op_array, NULL, NULL);
}
...
}
◦ 実行回数の比率が閾値を超えた op_array を JIT の対象とする
◦ 最初のリクエストの実行で判断
ZEND_JIT_ON_HOT_COUNTERS
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 21
return zend_jit_setup_hot_counters(op_array);
基本ブロック単位で実行回数を計測する
in ext/opcache/jit/zend_jit.c
static int zend_jit_setup_hot_counters(zend_op_array *op_array)
{
... // Control Flow Graph を構築
opline->handler = (const void*)zend_jit_func_counter_helper;
for (i = 0; i < cfg.blocks_count; i++) {
...
op_array->opcodes[cfg.blocks[i].start].handler =
(const void*)zend_jit_loop_counter_helper;
}
}
ZEND_JIT_ON_HOT_COUNTERS
in ext/opcache/jit/zend_jit_vm_helpers.c
実行のたびに hot counter を減じて 0 以下になったらコンパイル
2016-12-11 第七回 闇PHP勉強会 22
void ZEND_FASTCALL zend_jit_func_counter_helper(void)
{
...
zend_jit_hot_counters[n] -= ZEND_JIT_HOT_FUNC_COST;
if (UNEXPECTED(zend_jit_hot_counters[n] <= 0)) {
zend_jit_hot_counters[n] = ZEND_JIT_HOT_COUNTER_INIT;
zend_jit_hot_func(execute_data, opline); // handler を復元して JIT 実行
} else {
zend_vm_opcode_handler_t *handlers =
(zend_vm_opcode_handler_t*)ZEND_FUNC_INFO(&EX(func)->op_array);
handlers[opline - EX(func)->op_array.opcodes]();
}
}
◦ zend_jit_loop_counter_helper も同様の実装
ZEND_JIT_ON_SCRIPT_LOAD
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 23
return zend_real_jit_func(op_array, script, NULL);
OPcache が op_array を処理するタイミングでコンパイルする
その意味で、これは "Just In Time" ではない
ZEND_JIT_ON_DOC_COMMENT
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 24
if (zend_needs_manual_jit(op_array)) {
return zend_real_jit_func(op_array, script, NULL);
} else {
return SUCCESS;
}
OPcache が op_array を処理するタイミングでコンパイルする
DocComment に @jit を指定した関数のみ対象
これも "Just In Time" ではない
ここまでのまとめ
JIT のソースコードを追ってみました
1. プログラム実行時に JIT コンパイルを行う仕掛け
◦ OPcache を拡張して opcode handler を差し替え
◦ 関数の実行ごとに JIT 起動条件を確認する
2016-12-11 第七回 闇PHP勉強会 25
JIT コンパイルの処理内容
2016-12-11 第七回 闇PHP勉強会 26
zend_real_jit_func
in ext/opcache/jit/zend_jit.c
2016-12-11 第七回 闇PHP勉強会 27
static int zend_real_jit_func(
zend_op_array *op_array, zend_script *script, const zend_op *rt_opline)
{
...
JIT コンパイルのための解析 (OPcache の実装を利用)
◦ 制御フローグラフの構築
◦ データフロー解析 (ZEND_JIT_LEVEL_OPT_FUNC 以上)
◦ コールグラフの構築 (ZEND_JIT_LEVEL_OPT_FUNCS 以上)
コード生成
◦ opcode ごとに機械語を生成
◦ コード生成の仕組みには DynASM を利用
コード生成の概要 [1/2]
in ext/opcache/jit/zend_jit.c
JIT コンパイルの本体
2016-12-11 第七回 闇PHP勉強会 28
static int zend_jit(
zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline)
{
... // op_array に含まれる各 opcode をコンパイルする
switch (opline->opcode) {
...
case ZEND_PRE_INC:
case ZEND_PRE_DEC:
case ZEND_POST_INC:
case ZEND_POST_DEC:
if (!zend_jit_inc_dec(&dasm_state, opline, op_array, ssa)) {
goto jit_failure;
}
goto done;
}
コード生成の実装
DynASM のフォーマットで記述
in ext/opcache/jit/zend_jit_x86.dasc
2016-12-11 第七回 闇PHP勉強会 29
static int zend_jit_inc_dec(
dasm_State **Dst, const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa) {
...
|| if ((opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) &&
|| opline->result_type != IS_UNUSED) {
| ZVAL_COPY_VALUE
FP + opline->result.var, FP + opline->op1.var, MAY_BE_LONG, r0, eax, r1
|| }
if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
| inc aword [FP + opline->op1.var]
} else {
| dec aword [FP + opline->op1.var]
}
...
◦ dynasm.lua によって zend_jit_x86.c に変換される
DynASM
is a Dynamic Assembler for code generation engines.
2016-12-11 第七回 闇PHP勉強会 30
https://luajit.org/dynasm.html
生成されるコード
DynASM のフォーマットでの記述から
in ext/opcache/jit/zend_jit_x86.dasc
2016-12-11 第七回 闇PHP勉強会 31
if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
| inc aword [FP + opline->op1.var]
} else {
| dec aword [FP + opline->op1.var]
}
if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) {
//| inc aword [FP + opline->op1.var]
dasm_put(Dst, 749, opline->op1.var);
} else {
//| dec aword [FP + opline->op1.var]
dasm_put(Dst, 755, opline->op1.var);
}
以下のような C のコードが生成される
in ext/opcache/jit/zend_jit_x86.c
大雑把な理解
アセンブリのテンプレートエンジン
2016-12-11 第七回 闇PHP勉強会 32
| inc aword [FP + opline->op1.var]
dasm_put(Dst, 749, opline->op1.var);
static const unsigned char dasm_actions[5499] = {
248,10,198,4,37,237,0,128,60,37,237,0,15,132,
...
"inc aword [FP + ]"
...
}
zend_jit_x86.dasc
zend_jit_x86.c
+
テンプレートの定義
テンプレートの適用
dynasm.lua
コード生成の概要 [2/2]
in ext/opcache/jit/zend_jit.c
JIT コンパイルの本体
2016-12-11 第七回 闇PHP勉強会 33
... // すべての opcode を処理した後
handler = dasm_link_and_encode(
&dasm_state, op_array, &ssa->cfg, rt_opline, NULL);
}
◦ 以後は生成された機械語をハンドラとして実行する
JIT コンパイルの例
2016-12-11 第七回 闇PHP勉強会 34
サンプルプログラム
0 から 9 までの和を計算して表示する
2016-12-11 第七回 闇PHP勉強会 35
<?php
function f($a)
{
$sum = 0;
for ($i = 0; $i < $a; ++$i) {
$sum += $i;
}
return $sum;
}
$a = 10;
$ans = f($a);
echo $ans;
JIT の動作の確認
JIT コンパイルの結果を表示する
2016-12-11 第七回 闇PHP勉強会 36
$ ./sapi/cli/php
-d zend_extension=$(pwd)/modules/opcache.so
-d opcache.enable_cli=1
-d opcache.jit=15
-d opcache.jit_buffer_size=32M
-d opcache.jit_debug=3
sample1.php 2>&1
◦ opcache.jit
◦ トリガー (10 の位) とレベル (1 の位) をまとめて指定
◦ opcache.jit_debug
◦ デバッグ情報の表示内容を指定
CFG 構築結果の表示
ZEND_JIT_DEBUG_SSA (= 2) を立てるとフロー解析結果を表示
2016-12-11 第七回 闇PHP勉強会 37
f: ; (lines=9, args=1, vars=3, tmps=1, ssa_vars=0)
; (JIT)
; /home/y-uti/php-jit-bench/sample-code/sample1.php:3-10
BB0: start lines=[0-0]
; to=(BB1)
; level=0
; children=(BB1)
CV0($a) = RECV 1 // 仮引数 $a に 1 番目の実引数を受け取る
BB1: follow entry lines=[1-3]
; from=(BB0)
; to=(BB3)
; idom=BB0
; level=1
; children=(BB3)
CV1($sum) = QM_ASSIGN int(0) // $sum = 0
CV2($i) = QM_ASSIGN int(0) // $i = 0
JMP BB3 // 基本ブロック BB3 にジャンプ
BB2: target lines=[4-5]
; from=(BB3)
...
関数 f の CFG
2016-12-11 第七回 闇PHP勉強会 38
BB0: CV0($a) = RECV 1 // $a = 第 1 引数
BB1: CV1($sum) = QM_ASSIGN int(0) // $sum = 0
CV2($i) = QM_ASSIGN int(0) // $i = 0
JMP BB3 // jump to BB3
BB2: CV1($sum) = ADD CV1($sum) CV2($i) // $sum = $sum + $i
PRE_INC CV2($i) // ++$i
BB3: T3 = IS_SMALLER CV2($i) CV0($a) // $i < $a ?
JMPNZ T3 BB2 // if true jump to BB2
BB4: RETURN CV1($sum) // return $sum
生成コードの確認
ZEND_JIT_DEBUG_ASM (= 1) を立てると生成されるコードを表示
2016-12-11 第七回 闇PHP勉強会 39
JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample1.php)
sub $0x8, %rsp
call ZEND_RECV_SPEC_HANDLER
cmp $0x0, EG(exception)
jnz JIT$$exception_handler
jmp .L1
.ENTRY1:
sub $0x8, %rsp
.L1:
call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER
call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER
jmp .L3
.L2:
mov $0x7fcd524d6c10, %r15
call ZEND_ADD_SPEC_CV_CV_HANDLER
cmp $0x0, EG(exception)
jnz JIT$$exception_handler
call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER
...
ZEND_JIT_LEVEL_MINIMAL
"Subroutine threading"
2016-12-11 第七回 闇PHP勉強会 40
...
.L1:
call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER
call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER
jmp .L3
.L2:
mov $0x7fcd524d6c10, %r15
call ZEND_ADD_SPEC_CV_CV_HANDLER
cmp $0x0, EG(exception)
jnz JIT$$exception_handler
call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER
cmp $0x0, EG(exception)
jnz JIT$$exception_handler
.L3:
...
◦ PHP のハンドラ (in Zend/zend_vm_execute.h) 呼び出しを並べる
◦ JMP 系の命令は遷移先を直接指定する形に展開
ZEND_JIT_LEVEL_INLINE
"Selective inline threading"
2016-12-11 第七回 闇PHP勉強会 41
...
.L1:
mov $0x0, 0x60(%r14) // val($sum) = 0
mov $0x4, 0x68(%r14) // typeinfo($sum) = IS_LONG (= 4)
mov $0x0, 0x70(%r14) // val($i) = 0
mov $0x4, 0x78(%r14) // typeinfo($i) = IS_LONG
jmp .L3
.L2:
mov $0x7fd46c0d6c10, %r15
call ZEND_ADD_SPEC_CV_CV_HANDLER // $sum = $sum + $i (これは展開されない)
cmp $0x0, EG(exception)
jnz JIT$$exception_handler
cmp $0x4, 0x78(%r14)
jnz .L6
...
◦ 命令の処理を展開して高速に実行
◦ 型推論なし
ZEND_JIT_LEVEL_OPT_FUNC
"Optimized JIT based on Type-Inference"
2016-12-11 第七回 闇PHP勉強会 42
...
.L2:
cmp $0x4, 0x68(%r14) // $sum の型が IS_LONG か調べる
jnz .L10 // IS_LONG でなければ .L10 へ
cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる
jnz .L8 // IS_LONG でなければ .L8 へ
mov 0x60(%r14), %rax // %rax = $sum
add 0x70(%r14), %rax // %rax = %rax + $i
jo .L9 // オーバーフローしたら .L9 へ
mov %rax, 0x60(%r14) // $sum = %rax
.L3:
cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる
jnz .L13 // IS_LONG でなければ .L13 へ
inc 0x70(%r14) // ++$i
jo .L12 // オーバーフローしたら .L12 へ
...
◦ 型推論 (SSA 形式のデータフロー解析) に基づく最適化
より積極的な最適化レベル
ZEND_JIT_LEVEL_OPT_FUNCS
◦ "Optimized JIT based on Type-Inference and call-tree"
ZEND_JIT_LEVEL_OPT_SCRIPT
◦ "Optimized JIT based on Type-Inference and inner-procedure analises"
(サンプルプログラムでは違いが見えなかったので割愛)
2016-12-11 第七回 闇PHP勉強会 43
まとめ
JIT のソースコードを追ってみました
1. プログラム実行時に JIT コンパイルを行う仕掛け
◦ OPcache を拡張して opcode handler を差し替え
◦ 関数の実行ごとに JIT 起動条件を確認する
2. JIT コンパイルの処理内容
◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成
◦ 生成されたコードを実行するように opcode handler を再度差し替え
2016-12-11 第七回 闇PHP勉強会 44
補足
このスライドは下記のコードに基づいて作成しました
◦ Repository: https://github.com/zendtech/php-src.git
◦ Branch: jit-dynasm
◦ Commit: 39a5bd9
2016-12-11 第七回 闇PHP勉強会 45
JIT コンパイルの例 (追加)
2016-12-11 第七回 闇PHP勉強会 46
サンプルプログラム
以下の PHP プログラムを考える
2016-12-11 第七回 闇PHP勉強会 47
<?php
function f()
{
$a = 1;
++$a;
++$a;
++$a;
return $a;
}
echo f();
◦ JIT コンパイラはどのようなコードを生成するか
前提として
最適化コンパイラ (たとえば gcc) なら直接 4 を返せる
2016-12-11 第七回 闇PHP勉強会 48
// sample2.c
int f()
{
int a = 1;
++a;
++a;
++a;
return a;
}
$ gcc -O -S sample2.c
$ cat sample2.s
.file "sample2.c"
.text
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
movl $4, %eax
ret
.cfi_endproc
.LFE0:
.size f, .-f
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)"
.section .note.GNU-stack,"",@progbits
ZEND_JIT_LEVEL_INLINE
「データフロー解析を行わない」とは?
2016-12-11 第七回 闇PHP勉強会 49
BB0: start exit lines=[0-4]
; level=0
CV0($a) = QM_ASSIGN int(1)
PRE_INC CV0($a)
PRE_INC CV0($a)
PRE_INC CV0($a)
RETURN CV0($a)
◦ 最初の $a = 1 は右辺が定数なので整数だと分かる
◦ 後続の ++$a で $a が整数 (IS_LONG) だということは分からない
ZEND_JIT_LEVEL_INLINE
以下のコードが生成される
2016-12-11 第七回 闇PHP勉強会 50
JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php)
sub $0x8, %rsp
mov $0x1, 0x50(%r14)
mov $0x4, 0x58(%r14)
cmp $0x4, 0x58(%r14)
jnz .L5
inc 0x50(%r14)
jo .L4
.L1:
cmp $0x4, 0x58(%r14)
jnz .L11
inc 0x50(%r14)
jo .L10
.L2:
cmp $0x4, 0x58(%r14)
jnz .L17
inc 0x50(%r14)
jo .L16
...
ZEND_JIT_LEVEL_OPT_FUNC
データフロー解析を行う
2016-12-11 第七回 闇PHP勉強会 51
BB0: start exit lines=[0-4]
; level=0
#1.CV0($a) [long] RANGE[1..1] = QM_ASSIGN int(1)
PRE_INC #1.CV0($a) [long] RANGE[1..1] -> #2.CV0($a) [long] RANGE[2..2]
PRE_INC #2.CV0($a) [long] RANGE[2..2] -> #3.CV0($a) [long] RANGE[3..3]
PRE_INC #3.CV0($a) [long] RANGE[3..3] -> #4.CV0($a) [long] RANGE[4..4]
RETURN #4.CV0($a) [long] RANGE[4..4]
◦ 各命令で $a が取り得る型と値が解析されている
ZEND_JIT_LEVEL_OPT_FUNC
以下のコードが生成される
2016-12-11 第七回 闇PHP勉強会 52
JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php)
sub $0x8, %rsp
mov $0x1, 0x50(%r14)
mov $0x4, 0x58(%r14)
inc 0x50(%r14)
inc 0x50(%r14)
inc 0x50(%r14)
mov 0x10(%r14), %rcx
test %rcx, %rcx
jz .L1
mov 0x50(%r14), %rdx
mov %rdx, (%rcx)
mov $0x4, 0x8(%rcx)
.L1:
...
◦ 整数型でありオーバーフローもしないことが分かっている
◦ しかし、あくまでも「バイトコード命令ごとに」コード生成
◦ gcc のように 4 を返すような最適化はしない

More Related Content

What's hot

Gstreamer Basics
Gstreamer BasicsGstreamer Basics
Gstreamer Basics
Seiji Hiraki
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
Kota Saito
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
shinjiigarashi
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
yohhoy
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
Taku Miyakawa
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Genya Murakami
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
Unity Technologies Japan K.K.
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
Masahito Zembutsu
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
Takaaki Suzuki
 
Androidの新ビルドシステム
Androidの新ビルドシステムAndroidの新ビルドシステム
Androidの新ビルドシステム
l_b__
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
Takuto Wada
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
Kazuhiro Suga
 
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようPHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
Shohei Okada
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
NTT DATA Technology & Innovation
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり
Kazuyuki TAKASE
 
TRICK 2022 Results
TRICK 2022 ResultsTRICK 2022 Results
TRICK 2022 Results
mametter
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門
大樹 小倉
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
Masahito Zembutsu
 

What's hot (20)

Gstreamer Basics
Gstreamer BasicsGstreamer Basics
Gstreamer Basics
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチン
 
Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方Javaのログ出力: 道具と考え方
Javaのログ出力: 道具と考え方
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
 
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
【Unite Tokyo 2018】さては非同期だなオメー!async/await完全に理解しよう
 
Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版Dockerfileを改善するためのBest Practice 2019年版
Dockerfileを改善するためのBest Practice 2019年版
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
Androidの新ビルドシステム
Androidの新ビルドシステムAndroidの新ビルドシステム
Androidの新ビルドシステム
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
HTTP/2 入門
HTTP/2 入門HTTP/2 入門
HTTP/2 入門
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
 
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようPHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり関数型プログラミングのデザインパターンひとめぐり
関数型プログラミングのデザインパターンひとめぐり
 
TRICK 2022 Results
TRICK 2022 ResultsTRICK 2022 Results
TRICK 2022 Results
 
Pythonによる黒魔術入門
Pythonによる黒魔術入門Pythonによる黒魔術入門
Pythonによる黒魔術入門
 
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
 

Similar to JIT のコードを読んでみた

php and sapi and zendengine2 and...
php and sapi and zendengine2 and...php and sapi and zendengine2 and...
php and sapi and zendengine2 and...
do_aki
 
mod_auth_ticket - Bringing Single-Sign-On to lighttpd
mod_auth_ticket - Bringing Single-Sign-On to lighttpdmod_auth_ticket - Bringing Single-Sign-On to lighttpd
mod_auth_ticket - Bringing Single-Sign-On to lighttpd
Taisuke Yamada
 
スタート低レイヤー #0
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0
Kiwamu Okabe
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
Takuro Iizuka
 
Let's play with Goldfish
Let's play with GoldfishLet's play with Goldfish
Let's play with Goldfish
Tetsuyuki Kobayashi
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!
Yohei Fushii
 
perfを使ったPostgreSQLの解析(後編)
perfを使ったPostgreSQLの解析(後編)perfを使ったPostgreSQLの解析(後編)
perfを使ったPostgreSQLの解析(後編)
NTT DATA OSS Professional Services
 
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
Etsuji Nakai
 
OSS開発勉強会-03
OSS開発勉強会-03OSS開発勉強会-03
OSS開発勉強会-03
Kohei KaiGai
 
Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋
Mori Shingo
 
Php in ruby
Php in rubyPhp in ruby
Php in ruby
do_aki
 
PHPの今とこれから2020
PHPの今とこれから2020PHPの今とこれから2020
PHPの今とこれから2020
Rui Hirokawa
 
PECL operator で演算子オーバーロード
PECL operator で演算子オーバーロードPECL operator で演算子オーバーロード
PECL operator で演算子オーバーロード
y-uti
 
Android デバッグ小ネタ
Android デバッグ小ネタAndroid デバッグ小ネタ
Android デバッグ小ネタ
l_b__
 
Gingerbread
GingerbreadGingerbread
Gingerbread
android sola
 
Osc10do linux nextstep
Osc10do linux nextstepOsc10do linux nextstep
Osc10do linux nextstep
smokey monkey
 
SystemV IPC
SystemV IPCSystemV IPC
SystemV IPC
Masami Ichikawa
 
signal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かsignal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何か
do_aki
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputing
Noboru Irieda
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
Masanori Oobayashi
 

Similar to JIT のコードを読んでみた (20)

php and sapi and zendengine2 and...
php and sapi and zendengine2 and...php and sapi and zendengine2 and...
php and sapi and zendengine2 and...
 
mod_auth_ticket - Bringing Single-Sign-On to lighttpd
mod_auth_ticket - Bringing Single-Sign-On to lighttpdmod_auth_ticket - Bringing Single-Sign-On to lighttpd
mod_auth_ticket - Bringing Single-Sign-On to lighttpd
 
スタート低レイヤー #0
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU
 
Let's play with Goldfish
Let's play with GoldfishLet's play with Goldfish
Let's play with Goldfish
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!
 
perfを使ったPostgreSQLの解析(後編)
perfを使ったPostgreSQLの解析(後編)perfを使ったPostgreSQLの解析(後編)
perfを使ったPostgreSQLの解析(後編)
 
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
 
OSS開発勉強会-03
OSS開発勉強会-03OSS開発勉強会-03
OSS開発勉強会-03
 
Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋Node予備校 vol.1 名古屋
Node予備校 vol.1 名古屋
 
Php in ruby
Php in rubyPhp in ruby
Php in ruby
 
PHPの今とこれから2020
PHPの今とこれから2020PHPの今とこれから2020
PHPの今とこれから2020
 
PECL operator で演算子オーバーロード
PECL operator で演算子オーバーロードPECL operator で演算子オーバーロード
PECL operator で演算子オーバーロード
 
Android デバッグ小ネタ
Android デバッグ小ネタAndroid デバッグ小ネタ
Android デバッグ小ネタ
 
Gingerbread
GingerbreadGingerbread
Gingerbread
 
Osc10do linux nextstep
Osc10do linux nextstepOsc10do linux nextstep
Osc10do linux nextstep
 
SystemV IPC
SystemV IPCSystemV IPC
SystemV IPC
 
signal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かsignal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何か
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputing
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
 

More from y-uti

潜在ディリクレ配分法
潜在ディリクレ配分法潜在ディリクレ配分法
潜在ディリクレ配分法
y-uti
 
Active Object
Active ObjectActive Object
Active Object
y-uti
 
目で見る過学習と正則化
目で見る過学習と正則化目で見る過学習と正則化
目で見る過学習と正則化
y-uti
 
ロジスティック回帰入門
ロジスティック回帰入門ロジスティック回帰入門
ロジスティック回帰入門
y-uti
 
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
y-uti
 
PHP-ML で手書き数字認識
PHP-ML で手書き数字認識PHP-ML で手書き数字認識
PHP-ML で手書き数字認識
y-uti
 
OPcache の最適化器の今
OPcache の最適化器の今OPcache の最適化器の今
OPcache の最適化器の今
y-uti
 
スパース推定
スパース推定スパース推定
スパース推定
y-uti
 
Kaggle の Titanic チュートリアルに挑戦した話
Kaggle の Titanic チュートリアルに挑戦した話Kaggle の Titanic チュートリアルに挑戦した話
Kaggle の Titanic チュートリアルに挑戦した話
y-uti
 
PHP カンファレンス福岡 2017 参加報告
PHP カンファレンス福岡 2017 参加報告PHP カンファレンス福岡 2017 参加報告
PHP カンファレンス福岡 2017 参加報告
y-uti
 
分類問題 - 機械学習ライブラリ scikit-learn の活用
分類問題 - 機械学習ライブラリ scikit-learn の活用分類問題 - 機械学習ライブラリ scikit-learn の活用
分類問題 - 機械学習ライブラリ scikit-learn の活用
y-uti
 
JIT for PHP を試した
JIT for PHP を試したJIT for PHP を試した
JIT for PHP を試した
y-uti
 
Task Spooler を試した
Task Spooler を試したTask Spooler を試した
Task Spooler を試した
y-uti
 
anyenv + phpenv + php-build が便利すぎる件
anyenv + phpenv + php-build が便利すぎる件anyenv + phpenv + php-build が便利すぎる件
anyenv + phpenv + php-build が便利すぎる件
y-uti
 
PHP カンファレンス福岡 参加報告
PHP カンファレンス福岡 参加報告PHP カンファレンス福岡 参加報告
PHP カンファレンス福岡 参加報告
y-uti
 
RFC: "var" Deprecation
RFC: "var" DeprecationRFC: "var" Deprecation
RFC: "var" Deprecation
y-uti
 
最近の PHP の話
最近の PHP の話最近の PHP の話
最近の PHP の話
y-uti
 
Windows で拡張モジュールをビルドしてみた
Windows で拡張モジュールをビルドしてみたWindows で拡張モジュールをビルドしてみた
Windows で拡張モジュールをビルドしてみた
y-uti
 
PECL を数えてみた
PECL を数えてみたPECL を数えてみた
PECL を数えてみた
y-uti
 
Windows で PHP をビルドしてみた
Windows で PHP をビルドしてみたWindows で PHP をビルドしてみた
Windows で PHP をビルドしてみた
y-uti
 

More from y-uti (20)

潜在ディリクレ配分法
潜在ディリクレ配分法潜在ディリクレ配分法
潜在ディリクレ配分法
 
Active Object
Active ObjectActive Object
Active Object
 
目で見る過学習と正則化
目で見る過学習と正則化目で見る過学習と正則化
目で見る過学習と正則化
 
ロジスティック回帰入門
ロジスティック回帰入門ロジスティック回帰入門
ロジスティック回帰入門
 
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
 
PHP-ML で手書き数字認識
PHP-ML で手書き数字認識PHP-ML で手書き数字認識
PHP-ML で手書き数字認識
 
OPcache の最適化器の今
OPcache の最適化器の今OPcache の最適化器の今
OPcache の最適化器の今
 
スパース推定
スパース推定スパース推定
スパース推定
 
Kaggle の Titanic チュートリアルに挑戦した話
Kaggle の Titanic チュートリアルに挑戦した話Kaggle の Titanic チュートリアルに挑戦した話
Kaggle の Titanic チュートリアルに挑戦した話
 
PHP カンファレンス福岡 2017 参加報告
PHP カンファレンス福岡 2017 参加報告PHP カンファレンス福岡 2017 参加報告
PHP カンファレンス福岡 2017 参加報告
 
分類問題 - 機械学習ライブラリ scikit-learn の活用
分類問題 - 機械学習ライブラリ scikit-learn の活用分類問題 - 機械学習ライブラリ scikit-learn の活用
分類問題 - 機械学習ライブラリ scikit-learn の活用
 
JIT for PHP を試した
JIT for PHP を試したJIT for PHP を試した
JIT for PHP を試した
 
Task Spooler を試した
Task Spooler を試したTask Spooler を試した
Task Spooler を試した
 
anyenv + phpenv + php-build が便利すぎる件
anyenv + phpenv + php-build が便利すぎる件anyenv + phpenv + php-build が便利すぎる件
anyenv + phpenv + php-build が便利すぎる件
 
PHP カンファレンス福岡 参加報告
PHP カンファレンス福岡 参加報告PHP カンファレンス福岡 参加報告
PHP カンファレンス福岡 参加報告
 
RFC: "var" Deprecation
RFC: "var" DeprecationRFC: "var" Deprecation
RFC: "var" Deprecation
 
最近の PHP の話
最近の PHP の話最近の PHP の話
最近の PHP の話
 
Windows で拡張モジュールをビルドしてみた
Windows で拡張モジュールをビルドしてみたWindows で拡張モジュールをビルドしてみた
Windows で拡張モジュールをビルドしてみた
 
PECL を数えてみた
PECL を数えてみたPECL を数えてみた
PECL を数えてみた
 
Windows で PHP をビルドしてみた
Windows で PHP をビルドしてみたWindows で PHP をビルドしてみた
Windows で PHP をビルドしてみた
 

Recently uploaded

"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ..."ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
たけおか しょうぞう
 
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログLoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
CRI Japan, Inc.
 
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
Sony - Neural Network Libraries
 
Matsuo-Iwasawa lab. Research Unit Introduction
Matsuo-Iwasawa lab. Research Unit IntroductionMatsuo-Iwasawa lab. Research Unit Introduction
Matsuo-Iwasawa lab. Research Unit Introduction
Matsuo Lab
 
Kyndryl Developer Services のご紹介 2024年7月
Kyndryl Developer Services のご紹介  2024年7月Kyndryl Developer Services のご紹介  2024年7月
Kyndryl Developer Services のご紹介 2024年7月
Takayuki Nakayama
 
Matsuo-Iwasawa Lab. | Research unit Introduction
Matsuo-Iwasawa Lab. | Research unit IntroductionMatsuo-Iwasawa Lab. | Research unit Introduction
Matsuo-Iwasawa Lab. | Research unit Introduction
Matsuo Lab
 
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
chisatotakane
 
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
Toru Tamaki
 
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
Toru Tamaki
 
Matsuo-Iwasawa Lab. Research unit Introduction
Matsuo-Iwasawa Lab. Research unit IntroductionMatsuo-Iwasawa Lab. Research unit Introduction
Matsuo-Iwasawa Lab. Research unit Introduction
Matsuo Lab
 
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
shogotaguchi
 
Imitation learning for robotics 勉強会資料(20240701)
Imitation learning for robotics 勉強会資料(20240701)Imitation learning for robotics 勉強会資料(20240701)
Imitation learning for robotics 勉強会資料(20240701)
Natsutani Minoru
 

Recently uploaded (12)

"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ..."ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
"ros2rapper", Hardware implimentation of ROS2 communication Protocol without ...
 
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログLoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
LoRaWAN AI Image Sensorエンドデバイス AIG01カタログ
 
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
【AI論文解説】LLMの事前学習をvisionに適用する手法Autoregressive Image Models
 
Matsuo-Iwasawa lab. Research Unit Introduction
Matsuo-Iwasawa lab. Research Unit IntroductionMatsuo-Iwasawa lab. Research Unit Introduction
Matsuo-Iwasawa lab. Research Unit Introduction
 
Kyndryl Developer Services のご紹介 2024年7月
Kyndryl Developer Services のご紹介  2024年7月Kyndryl Developer Services のご紹介  2024年7月
Kyndryl Developer Services のご紹介 2024年7月
 
Matsuo-Iwasawa Lab. | Research unit Introduction
Matsuo-Iwasawa Lab. | Research unit IntroductionMatsuo-Iwasawa Lab. | Research unit Introduction
Matsuo-Iwasawa Lab. | Research unit Introduction
 
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
最速の組織を目指して全社で大規模スクラムを導入してみた話 #dxd2024 #medicalforce
 
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
論文紹介:Task-aligned Part-aware Panoptic Segmentation through Joint Object-Part ...
 
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
論文紹介:BAM-DETR: Boundary-Aligned Moment Detection Transformer for Temporal Sen...
 
Matsuo-Iwasawa Lab. Research unit Introduction
Matsuo-Iwasawa Lab. Research unit IntroductionMatsuo-Iwasawa Lab. Research unit Introduction
Matsuo-Iwasawa Lab. Research unit Introduction
 
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
「福利厚生をコストから投資へ」AIで社員1人ひとりに最適な支援を届ける 全く新しいカフェテリアプラン
 
Imitation learning for robotics 勉強会資料(20240701)
Imitation learning for robotics 勉強会資料(20240701)Imitation learning for robotics 勉強会資料(20240701)
Imitation learning for robotics 勉強会資料(20240701)
 

JIT のコードを読んでみた

  • 1. JIT のコードを読んでみた 内山 雄司 (@y__uti) 2016-12-11 第7回闇PHP勉強会
  • 2. 自己紹介 内山 雄司 (@y__uti) ◦ http://y-uti.hatenablog.jp/ (phpusers-ja) 仕事 ◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています 興味 ◦ プログラミング言語処理系 ◦ 機械学習 2016-12-11 第七回 闇PHP勉強会 2
  • 3. JIT for PHP project 2016-12-11 第七回 闇PHP勉強会 3 http://news.php.net/php.internals/95531
  • 4. (第 107 回 PHP 勉強会での発表資料より) ベンチマーク結果 2016-12-11 第七回 闇PHP勉強会 4 0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 実行時間(秒) 5.6.27 7.0.12 7.1.0RC4 JIT (0edf1e9) Intel Core i5-3337U 1.80GHz 2GB Memory CentOS 7 (VM on Windows7) 各 10 回の実行の平均
  • 5. JIT による速度向上 2016-12-11 第七回 闇PHP勉強会 5 http://news.php.net/php.internals/96613 開発者 (Dmitry Stogov 氏) による 2016-10-26 の投稿 ◦ bench.php では 3 倍の速度向上 ◦ "real-life apps" では大きな差はない
  • 6. 本日の発表内容 どのように実装されているかソースコードを追ってみました 1. プログラム実行時に JIT コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2. JIT コンパイルの処理内容 ◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成 ◦ 生成されたコードを実行するように opcode handler を再度差し替え この一枚で理解できてしまったガチ勢はマサカリの準備を! 2016-12-11 第七回 闇PHP勉強会 6
  • 8. PHP の処理の流れ 以下のコマンドを実行すると何が起きるのか 2016-12-11 第七回 闇PHP勉強会 8 $ php foo.php 処理の流れ 1. sapi/cli/php_cli.c の main 関数から処理が始まり 2. Zend/zend.c の zend_execute_scripts 関数が呼ばれ 3. zend_compile_file 関数でコンパイルして 4. zend_execute 関数で実行する
  • 9. zend_execute_scripts 関数 in Zend/zend.c PHP 処理系によるプログラム実行の「かなめ」 ◦ ファイルをバイトコード命令列にコンパイルする 2016-12-11 第七回 闇PHP勉強会 9 op_array = zend_compile_file(file_handle, type); zend_execute(op_array, retval); ◦ バイトコード命令列を実行する
  • 10. op_array 構造体 in Zend/zend_compile.h バイトコード命令列 + 各種情報 ◦ 関数ごとに一つ ◦ トップレベルに書かれたコード用に一つ 2016-12-11 第七回 闇PHP勉強会 10 struct _zend_op { const void *handler; znode_op op1; znode_op op2; znode_op result; uint32_t extended_value; uint32_t lineno; zend_uchar opcode; zend_uchar op1_type; zend_uchar op2_type; zend_uchar result_type; }; struct _zend_op_array { zend_uchar type; zend_uchar arg_flags[3]; ... uint32_t last; zend_op *opcodes; ... }; 一対多 last が命令数を表す
  • 11. execute_ex 関数 in Zend/zend_vm_execute.h 各バイトコード命令 (zend_op 構造体) の handler を実行 2016-12-11 第七回 闇PHP勉強会 11 ... while (1) { ... if (UNEXPECTED( (ret = ((opcode_handler_t)OPLINE->handler)()) != 0)) { ... return; } ... } ... 読みやすさのため、一部のマクロを展開して掲載しています
  • 12. JIT コンパイル付きの実行 op_array (関数) ごとに以下の処理を行う 2016-12-11 第七回 闇PHP勉強会 12 コンパイルする? 元のコードを実行 コード生成 生成された コードを実行 開始 終了 No Yes
  • 13. JIT コンパイルの準備 (通常の) コンパイル時に handler を書き換える 2016-12-11 第七回 闇PHP勉強会 13 コンパイルする? 元のコードを実行 コード生成 生成された コードを実行 開始 終了 No Yes handler handler
  • 14. OPcache の処理 起動時に zend_compile_file 関数を置き換える in ext/opcache/ZendAccelerator.c 2016-12-11 第七回 闇PHP勉強会 14 static int accel_startup(zend_extension *extension) { ... /* Override compiler */ accelerator_orig_compile_file = zend_compile_file; zend_compile_file = persistent_compile_file; ... } persistent_compile_file 関数がやること ◦ コンパイルしたデータ構造をキャッシュ (本来の仕事) ◦ OPcache 独自の最適化 (おまけ?) ◦ JIT コンパイルのための handler 書き換え (JIT を有効にしたときのみ)[New!]
  • 15. persistent_compile_file 関数 in ext/opcache/ZendAccelerator.c 2016-12-11 第七回 闇PHP勉強会 15 /* zend_compile() replacement */ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) { ... // キャッシュ済みでなければ if (!persistent_script) { // コンパイルして persistent_script = opcache_compile_file(...); if (persistent_script) { // キャッシュする persistent_script = cache_script_in_shared_memory(...); } ...
  • 16. JIT までの道のり in ext/opcache/ZendAccelerator.c 2016-12-11 第七回 闇PHP勉強会 16 static zend_persistent_script *cache_script_in_shared_memory( ... ) in ext/opcache/zend_persist.c zend_persistent_script *zend_accel_script_persist( ... ) static void zend_persist_op_array_ex( ... ) in ext/opcache/zend_persist.c in ext/opcache/jit/zend_jit.c ZEND_API int zend_jit_op_array( zend_op_array *op_array, zend_script *script) ◦ この関数で handler を差し替えている
  • 17. JIT コンパイルのトリガー in ext/opcache/jit/zend_jit.h いくつかの選択肢が提供されている ◦ ZEND_JIT_ON_SCRIPT_LOAD スクリプトのロード時 ◦ ZEND_JIT_ON_FIRST_EXEC 最初の実行 ◦ ZEND_JIT_ON_PROF_REQUEST 実行頻度 (割合) の高い関数 ◦ ZEND_JIT_ON_HOT_COUNTERS 実行回数が閾値を超えたとき ◦ ZEND_JIT_ON_DOC_COMMENT DocComment の指定に従う 2016-12-11 第七回 闇PHP勉強会 17
  • 18. ZEND_JIT_ON_FIRST_EXEC in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 18 opline->handler = (const void*)zend_runtime_jit; zend_runtime_jit 関数は無条件に JIT コンパイルを開始 in ext/opcache/jit/zend_jit.c static void ZEND_FASTCALL zend_runtime_jit(void) { ... opline->handler = ZEND_FUNC_INFO(op_array); // 本来の handler を復元 ... /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, NULL); // JIT コンパイルを実行 ... }
  • 19. ZEND_JIT_ON_PROF_REQUEST in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 19 opline->handler = zend_jit_profile_helper; zend_jit_profile_helper は op_array ごとの実行回数を数える in ext/opcache/jit/zend_jit_vm_helpers.c void ZEND_FASTCALL zend_jit_profile_helper(void) { zend_op_array *op_array = (zend_op_array*)EX(func); const void *handler = (const void*)ZEND_FUNC_INFO(op_array); // 本来の handler を取得 ++(ZEND_COUNTER_INFO(op_array)); // この op_array の実行回数 ++zend_jit_profile_counter; // 全 op_array の実行回数の総和 return ((zend_vm_opcode_handler_t)handler)(); // 本来の処理を実行 }
  • 20. ZEND_JIT_ON_PROF_REQUEST in ext/opcache/jit/zend_jit.c OPcache が deactivate されるときに JIT コンパイルを行う 2016-12-11 第七回 闇PHP勉強会 20 void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method) { ... zend_ulong counter = (zend_ulong)ZEND_COUNTER_INFO(op_array); if (((double)counter / (double)zend_jit_profile_counter) > ZEND_JIT_PROF_THRESHOLD) { zend_real_jit_func(op_array, NULL, NULL); } ... } ◦ 実行回数の比率が閾値を超えた op_array を JIT の対象とする ◦ 最初のリクエストの実行で判断
  • 21. ZEND_JIT_ON_HOT_COUNTERS in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 21 return zend_jit_setup_hot_counters(op_array); 基本ブロック単位で実行回数を計測する in ext/opcache/jit/zend_jit.c static int zend_jit_setup_hot_counters(zend_op_array *op_array) { ... // Control Flow Graph を構築 opline->handler = (const void*)zend_jit_func_counter_helper; for (i = 0; i < cfg.blocks_count; i++) { ... op_array->opcodes[cfg.blocks[i].start].handler = (const void*)zend_jit_loop_counter_helper; } }
  • 22. ZEND_JIT_ON_HOT_COUNTERS in ext/opcache/jit/zend_jit_vm_helpers.c 実行のたびに hot counter を減じて 0 以下になったらコンパイル 2016-12-11 第七回 闇PHP勉強会 22 void ZEND_FASTCALL zend_jit_func_counter_helper(void) { ... zend_jit_hot_counters[n] -= ZEND_JIT_HOT_FUNC_COST; if (UNEXPECTED(zend_jit_hot_counters[n] <= 0)) { zend_jit_hot_counters[n] = ZEND_JIT_HOT_COUNTER_INIT; zend_jit_hot_func(execute_data, opline); // handler を復元して JIT 実行 } else { zend_vm_opcode_handler_t *handlers = (zend_vm_opcode_handler_t*)ZEND_FUNC_INFO(&EX(func)->op_array); handlers[opline - EX(func)->op_array.opcodes](); } } ◦ zend_jit_loop_counter_helper も同様の実装
  • 23. ZEND_JIT_ON_SCRIPT_LOAD in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 23 return zend_real_jit_func(op_array, script, NULL); OPcache が op_array を処理するタイミングでコンパイルする その意味で、これは "Just In Time" ではない
  • 24. ZEND_JIT_ON_DOC_COMMENT in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 24 if (zend_needs_manual_jit(op_array)) { return zend_real_jit_func(op_array, script, NULL); } else { return SUCCESS; } OPcache が op_array を処理するタイミングでコンパイルする DocComment に @jit を指定した関数のみ対象 これも "Just In Time" ではない
  • 25. ここまでのまとめ JIT のソースコードを追ってみました 1. プログラム実行時に JIT コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2016-12-11 第七回 闇PHP勉強会 25
  • 27. zend_real_jit_func in ext/opcache/jit/zend_jit.c 2016-12-11 第七回 闇PHP勉強会 27 static int zend_real_jit_func( zend_op_array *op_array, zend_script *script, const zend_op *rt_opline) { ... JIT コンパイルのための解析 (OPcache の実装を利用) ◦ 制御フローグラフの構築 ◦ データフロー解析 (ZEND_JIT_LEVEL_OPT_FUNC 以上) ◦ コールグラフの構築 (ZEND_JIT_LEVEL_OPT_FUNCS 以上) コード生成 ◦ opcode ごとに機械語を生成 ◦ コード生成の仕組みには DynASM を利用
  • 28. コード生成の概要 [1/2] in ext/opcache/jit/zend_jit.c JIT コンパイルの本体 2016-12-11 第七回 闇PHP勉強会 28 static int zend_jit( zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline) { ... // op_array に含まれる各 opcode をコンパイルする switch (opline->opcode) { ... case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: if (!zend_jit_inc_dec(&dasm_state, opline, op_array, ssa)) { goto jit_failure; } goto done; }
  • 29. コード生成の実装 DynASM のフォーマットで記述 in ext/opcache/jit/zend_jit_x86.dasc 2016-12-11 第七回 闇PHP勉強会 29 static int zend_jit_inc_dec( dasm_State **Dst, const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa) { ... || if ((opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) && || opline->result_type != IS_UNUSED) { | ZVAL_COPY_VALUE FP + opline->result.var, FP + opline->op1.var, MAY_BE_LONG, r0, eax, r1 || } if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { | inc aword [FP + opline->op1.var] } else { | dec aword [FP + opline->op1.var] } ... ◦ dynasm.lua によって zend_jit_x86.c に変換される
  • 30. DynASM is a Dynamic Assembler for code generation engines. 2016-12-11 第七回 闇PHP勉強会 30 https://luajit.org/dynasm.html
  • 31. 生成されるコード DynASM のフォーマットでの記述から in ext/opcache/jit/zend_jit_x86.dasc 2016-12-11 第七回 闇PHP勉強会 31 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { | inc aword [FP + opline->op1.var] } else { | dec aword [FP + opline->op1.var] } if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { //| inc aword [FP + opline->op1.var] dasm_put(Dst, 749, opline->op1.var); } else { //| dec aword [FP + opline->op1.var] dasm_put(Dst, 755, opline->op1.var); } 以下のような C のコードが生成される in ext/opcache/jit/zend_jit_x86.c
  • 32. 大雑把な理解 アセンブリのテンプレートエンジン 2016-12-11 第七回 闇PHP勉強会 32 | inc aword [FP + opline->op1.var] dasm_put(Dst, 749, opline->op1.var); static const unsigned char dasm_actions[5499] = { 248,10,198,4,37,237,0,128,60,37,237,0,15,132, ... "inc aword [FP + ]" ... } zend_jit_x86.dasc zend_jit_x86.c + テンプレートの定義 テンプレートの適用 dynasm.lua
  • 33. コード生成の概要 [2/2] in ext/opcache/jit/zend_jit.c JIT コンパイルの本体 2016-12-11 第七回 闇PHP勉強会 33 ... // すべての opcode を処理した後 handler = dasm_link_and_encode( &dasm_state, op_array, &ssa->cfg, rt_opline, NULL); } ◦ 以後は生成された機械語をハンドラとして実行する
  • 35. サンプルプログラム 0 から 9 までの和を計算して表示する 2016-12-11 第七回 闇PHP勉強会 35 <?php function f($a) { $sum = 0; for ($i = 0; $i < $a; ++$i) { $sum += $i; } return $sum; } $a = 10; $ans = f($a); echo $ans;
  • 36. JIT の動作の確認 JIT コンパイルの結果を表示する 2016-12-11 第七回 闇PHP勉強会 36 $ ./sapi/cli/php -d zend_extension=$(pwd)/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit=15 -d opcache.jit_buffer_size=32M -d opcache.jit_debug=3 sample1.php 2>&1 ◦ opcache.jit ◦ トリガー (10 の位) とレベル (1 の位) をまとめて指定 ◦ opcache.jit_debug ◦ デバッグ情報の表示内容を指定
  • 37. CFG 構築結果の表示 ZEND_JIT_DEBUG_SSA (= 2) を立てるとフロー解析結果を表示 2016-12-11 第七回 闇PHP勉強会 37 f: ; (lines=9, args=1, vars=3, tmps=1, ssa_vars=0) ; (JIT) ; /home/y-uti/php-jit-bench/sample-code/sample1.php:3-10 BB0: start lines=[0-0] ; to=(BB1) ; level=0 ; children=(BB1) CV0($a) = RECV 1 // 仮引数 $a に 1 番目の実引数を受け取る BB1: follow entry lines=[1-3] ; from=(BB0) ; to=(BB3) ; idom=BB0 ; level=1 ; children=(BB3) CV1($sum) = QM_ASSIGN int(0) // $sum = 0 CV2($i) = QM_ASSIGN int(0) // $i = 0 JMP BB3 // 基本ブロック BB3 にジャンプ BB2: target lines=[4-5] ; from=(BB3) ...
  • 38. 関数 f の CFG 2016-12-11 第七回 闇PHP勉強会 38 BB0: CV0($a) = RECV 1 // $a = 第 1 引数 BB1: CV1($sum) = QM_ASSIGN int(0) // $sum = 0 CV2($i) = QM_ASSIGN int(0) // $i = 0 JMP BB3 // jump to BB3 BB2: CV1($sum) = ADD CV1($sum) CV2($i) // $sum = $sum + $i PRE_INC CV2($i) // ++$i BB3: T3 = IS_SMALLER CV2($i) CV0($a) // $i < $a ? JMPNZ T3 BB2 // if true jump to BB2 BB4: RETURN CV1($sum) // return $sum
  • 39. 生成コードの確認 ZEND_JIT_DEBUG_ASM (= 1) を立てると生成されるコードを表示 2016-12-11 第七回 闇PHP勉強会 39 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample1.php) sub $0x8, %rsp call ZEND_RECV_SPEC_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler jmp .L1 .ENTRY1: sub $0x8, %rsp .L1: call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER jmp .L3 .L2: mov $0x7fcd524d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER ...
  • 40. ZEND_JIT_LEVEL_MINIMAL "Subroutine threading" 2016-12-11 第七回 闇PHP勉強会 40 ... .L1: call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER jmp .L3 .L2: mov $0x7fcd524d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler .L3: ... ◦ PHP のハンドラ (in Zend/zend_vm_execute.h) 呼び出しを並べる ◦ JMP 系の命令は遷移先を直接指定する形に展開
  • 41. ZEND_JIT_LEVEL_INLINE "Selective inline threading" 2016-12-11 第七回 闇PHP勉強会 41 ... .L1: mov $0x0, 0x60(%r14) // val($sum) = 0 mov $0x4, 0x68(%r14) // typeinfo($sum) = IS_LONG (= 4) mov $0x0, 0x70(%r14) // val($i) = 0 mov $0x4, 0x78(%r14) // typeinfo($i) = IS_LONG jmp .L3 .L2: mov $0x7fd46c0d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER // $sum = $sum + $i (これは展開されない) cmp $0x0, EG(exception) jnz JIT$$exception_handler cmp $0x4, 0x78(%r14) jnz .L6 ... ◦ 命令の処理を展開して高速に実行 ◦ 型推論なし
  • 42. ZEND_JIT_LEVEL_OPT_FUNC "Optimized JIT based on Type-Inference" 2016-12-11 第七回 闇PHP勉強会 42 ... .L2: cmp $0x4, 0x68(%r14) // $sum の型が IS_LONG か調べる jnz .L10 // IS_LONG でなければ .L10 へ cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる jnz .L8 // IS_LONG でなければ .L8 へ mov 0x60(%r14), %rax // %rax = $sum add 0x70(%r14), %rax // %rax = %rax + $i jo .L9 // オーバーフローしたら .L9 へ mov %rax, 0x60(%r14) // $sum = %rax .L3: cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる jnz .L13 // IS_LONG でなければ .L13 へ inc 0x70(%r14) // ++$i jo .L12 // オーバーフローしたら .L12 へ ... ◦ 型推論 (SSA 形式のデータフロー解析) に基づく最適化
  • 43. より積極的な最適化レベル ZEND_JIT_LEVEL_OPT_FUNCS ◦ "Optimized JIT based on Type-Inference and call-tree" ZEND_JIT_LEVEL_OPT_SCRIPT ◦ "Optimized JIT based on Type-Inference and inner-procedure analises" (サンプルプログラムでは違いが見えなかったので割愛) 2016-12-11 第七回 闇PHP勉強会 43
  • 44. まとめ JIT のソースコードを追ってみました 1. プログラム実行時に JIT コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2. JIT コンパイルの処理内容 ◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成 ◦ 生成されたコードを実行するように opcode handler を再度差し替え 2016-12-11 第七回 闇PHP勉強会 44
  • 46. JIT コンパイルの例 (追加) 2016-12-11 第七回 闇PHP勉強会 46
  • 47. サンプルプログラム 以下の PHP プログラムを考える 2016-12-11 第七回 闇PHP勉強会 47 <?php function f() { $a = 1; ++$a; ++$a; ++$a; return $a; } echo f(); ◦ JIT コンパイラはどのようなコードを生成するか
  • 48. 前提として 最適化コンパイラ (たとえば gcc) なら直接 4 を返せる 2016-12-11 第七回 闇PHP勉強会 48 // sample2.c int f() { int a = 1; ++a; ++a; ++a; return a; } $ gcc -O -S sample2.c $ cat sample2.s .file "sample2.c" .text .globl f .type f, @function f: .LFB0: .cfi_startproc movl $4, %eax ret .cfi_endproc .LFE0: .size f, .-f .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)" .section .note.GNU-stack,"",@progbits
  • 49. ZEND_JIT_LEVEL_INLINE 「データフロー解析を行わない」とは? 2016-12-11 第七回 闇PHP勉強会 49 BB0: start exit lines=[0-4] ; level=0 CV0($a) = QM_ASSIGN int(1) PRE_INC CV0($a) PRE_INC CV0($a) PRE_INC CV0($a) RETURN CV0($a) ◦ 最初の $a = 1 は右辺が定数なので整数だと分かる ◦ 後続の ++$a で $a が整数 (IS_LONG) だということは分からない
  • 50. ZEND_JIT_LEVEL_INLINE 以下のコードが生成される 2016-12-11 第七回 闇PHP勉強会 50 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php) sub $0x8, %rsp mov $0x1, 0x50(%r14) mov $0x4, 0x58(%r14) cmp $0x4, 0x58(%r14) jnz .L5 inc 0x50(%r14) jo .L4 .L1: cmp $0x4, 0x58(%r14) jnz .L11 inc 0x50(%r14) jo .L10 .L2: cmp $0x4, 0x58(%r14) jnz .L17 inc 0x50(%r14) jo .L16 ...
  • 51. ZEND_JIT_LEVEL_OPT_FUNC データフロー解析を行う 2016-12-11 第七回 闇PHP勉強会 51 BB0: start exit lines=[0-4] ; level=0 #1.CV0($a) [long] RANGE[1..1] = QM_ASSIGN int(1) PRE_INC #1.CV0($a) [long] RANGE[1..1] -> #2.CV0($a) [long] RANGE[2..2] PRE_INC #2.CV0($a) [long] RANGE[2..2] -> #3.CV0($a) [long] RANGE[3..3] PRE_INC #3.CV0($a) [long] RANGE[3..3] -> #4.CV0($a) [long] RANGE[4..4] RETURN #4.CV0($a) [long] RANGE[4..4] ◦ 各命令で $a が取り得る型と値が解析されている
  • 52. ZEND_JIT_LEVEL_OPT_FUNC 以下のコードが生成される 2016-12-11 第七回 闇PHP勉強会 52 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php) sub $0x8, %rsp mov $0x1, 0x50(%r14) mov $0x4, 0x58(%r14) inc 0x50(%r14) inc 0x50(%r14) inc 0x50(%r14) mov 0x10(%r14), %rcx test %rcx, %rcx jz .L1 mov 0x50(%r14), %rdx mov %rdx, (%rcx) mov $0x4, 0x8(%rcx) .L1: ... ◦ 整数型でありオーバーフローもしないことが分かっている ◦ しかし、あくまでも「バイトコード命令ごとに」コード生成 ◦ gcc のように 4 を返すような最適化はしない