SlideShare a Scribd company logo
1 of 52
Download to read offline
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

イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化Gosuke Miyashita
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)NTT DATA Technology & Innovation
 
PHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイントPHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイントYoshio Hanawa
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMPYusuke Kagata
 
Zend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るZend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るYoshio Hanawa
 
PostgreSQL Unconference #5 ICU Collation
PostgreSQL Unconference #5 ICU CollationPostgreSQL Unconference #5 ICU Collation
PostgreSQL Unconference #5 ICU CollationNoriyoshi Shinoda
 
HTTP/2で 速くなるとき ならないとき
HTTP/2で 速くなるとき ならないときHTTP/2で 速くなるとき ならないとき
HTTP/2で 速くなるとき ならないときKazuho Oku
 
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)Tetsuya Hasegawa
 
ZabbixのAPIを使って運用を楽しくする話
ZabbixのAPIを使って運用を楽しくする話ZabbixのAPIを使って運用を楽しくする話
ZabbixのAPIを使って運用を楽しくする話Masahito Zembutsu
 
UEFI時代のブートローダ
UEFI時代のブートローダUEFI時代のブートローダ
UEFI時代のブートローダTakuya ASADA
 
realpathキャッシュと OPcacheの面倒すぎる関係
realpathキャッシュと OPcacheの面倒すぎる関係realpathキャッシュと OPcacheの面倒すぎる関係
realpathキャッシュと OPcacheの面倒すぎる関係Yoshio Hanawa
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugMasatoshi Tada
 
Format string Attack
Format string AttackFormat string Attack
Format string Attackicchy
 
HTTPを理解する
HTTPを理解するHTTPを理解する
HTTPを理解するIIJ
 
こわくない Git
こわくない Gitこわくない Git
こわくない GitKota Saito
 
WebAssemblyのWeb以外のことぜんぶ話す
WebAssemblyのWeb以外のことぜんぶ話すWebAssemblyのWeb以外のことぜんぶ話す
WebAssemblyのWeb以外のことぜんぶ話すTakaya Saeki
 
このPHP QAツールがすごい!2019
このPHP QAツールがすごい!2019 このPHP QAツールがすごい!2019
このPHP QAツールがすごい!2019 sasezaki
 

What's hot (20)

イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化
 
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
pg_bigmで全文検索するときに気を付けたい5つのポイント(第23回PostgreSQLアンカンファレンス@オンライン 発表資料)
 
PHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイントPHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイント
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMP
 
Zend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探るZend OPcacheの速さの秘密を探る
Zend OPcacheの速さの秘密を探る
 
PostgreSQL Unconference #5 ICU Collation
PostgreSQL Unconference #5 ICU CollationPostgreSQL Unconference #5 ICU Collation
PostgreSQL Unconference #5 ICU Collation
 
HTTP/2で 速くなるとき ならないとき
HTTP/2で 速くなるとき ならないときHTTP/2で 速くなるとき ならないとき
HTTP/2で 速くなるとき ならないとき
 
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
SSHパケットの復号ツールを作ろう_v1(Decrypt SSH .pcap File)
 
Plan 9のお話
Plan 9のお話Plan 9のお話
Plan 9のお話
 
ZabbixのAPIを使って運用を楽しくする話
ZabbixのAPIを使って運用を楽しくする話ZabbixのAPIを使って運用を楽しくする話
ZabbixのAPIを使って運用を楽しくする話
 
UEFI時代のブートローダ
UEFI時代のブートローダUEFI時代のブートローダ
UEFI時代のブートローダ
 
realpathキャッシュと OPcacheの面倒すぎる関係
realpathキャッシュと OPcacheの面倒すぎる関係realpathキャッシュと OPcacheの面倒すぎる関係
realpathキャッシュと OPcacheの面倒すぎる関係
 
Java ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsugJava ORマッパー選定のポイント #jsug
Java ORマッパー選定のポイント #jsug
 
CPUから見たG1GC
CPUから見たG1GCCPUから見たG1GC
CPUから見たG1GC
 
Format string Attack
Format string AttackFormat string Attack
Format string Attack
 
HTTPを理解する
HTTPを理解するHTTPを理解する
HTTPを理解する
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
Gstreamer Basics
Gstreamer BasicsGstreamer Basics
Gstreamer Basics
 
WebAssemblyのWeb以外のことぜんぶ話す
WebAssemblyのWeb以外のことぜんぶ話すWebAssemblyのWeb以外のことぜんぶ話す
WebAssemblyのWeb以外のことぜんぶ話す
 
このPHP QAツールがすごい!2019
このPHP QAツールがすごい!2019 このPHP QAツールがすごい!2019
このPHP QAツールがすごい!2019
 

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 lighttpdTaisuke Yamada
 
スタート低レイヤー #0
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0Kiwamu Okabe
 
関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPU関東GPGPU勉強会 LLVM meets GPU
関東GPGPU勉強会 LLVM meets GPUTakuro Iizuka
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!Yohei Fushii
 
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1
OpenStackクラウド基盤構築ハンズオンセミナー 第1日:ハンズオンNo1Etsuji Nakai
 
OSS開発勉強会-03
OSS開発勉強会-03OSS開発勉強会-03
OSS開発勉強会-03Kohei 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 rubydo_aki
 
PHPの今とこれから2020
PHPの今とこれから2020PHPの今とこれから2020
PHPの今とこれから2020Rui Hirokawa
 
PECL operator で演算子オーバーロード
PECL operator で演算子オーバーロードPECL operator で演算子オーバーロード
PECL operator で演算子オーバーロードy-uti
 
Android デバッグ小ネタ
Android デバッグ小ネタAndroid デバッグ小ネタ
Android デバッグ小ネタl_b__
 
Osc10do linux nextstep
Osc10do linux nextstepOsc10do linux nextstep
Osc10do linux nextstepsmokey monkey
 
signal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かsignal の話 或いは Zend Signals とは何か
signal の話 或いは Zend Signals とは何かdo_aki
 
Python physicalcomputing
Python physicalcomputingPython physicalcomputing
Python physicalcomputingNoboru Irieda
 
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterWTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniterMasanori 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 Objecty-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
 
スパース推定
スパース推定スパース推定
スパース推定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" Deprecationy-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
 
逐次ベイズ学習 - サンプリング近似法の場合 -
逐次ベイズ学習 - サンプリング近似法の場合 -逐次ベイズ学習 - サンプリング近似法の場合 -
逐次ベイズ学習 - サンプリング近似法の場合 -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 で手書き数字認識
 
スパース推定
スパース推定スパース推定
スパース推定
 
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

情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法
情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法
情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法ssuser370dd7
 
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~arts yokohama
 
2024 01 Virtual_Counselor
2024 01 Virtual_Counselor 2024 01 Virtual_Counselor
2024 01 Virtual_Counselor arts yokohama
 
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)ssuser539845
 
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見Shumpei Kishi
 
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdf
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdfTaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdf
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdfMatsushita Laboratory
 
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-LoopへTetsuya Nihonmatsu
 
20240326_IoTLT_vol109_kitazaki_v1___.pdf
20240326_IoTLT_vol109_kitazaki_v1___.pdf20240326_IoTLT_vol109_kitazaki_v1___.pdf
20240326_IoTLT_vol109_kitazaki_v1___.pdfAyachika Kitazaki
 

Recently uploaded (11)

2024 04 minnanoito
2024 04 minnanoito2024 04 minnanoito
2024 04 minnanoito
 
情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法
情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法
情報処理学会86回全国大会_Generic OAMをDeep Learning技術によって実現するための課題と解決方法
 
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~
2024 02 Nihon-Tanken ~Towards a More Inclusive Japan~
 
2024 01 Virtual_Counselor
2024 01 Virtual_Counselor 2024 01 Virtual_Counselor
2024 01 Virtual_Counselor
 
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)
IFIP IP3での資格制度を対象とする国際認定(IPSJ86全国大会シンポジウム)
 
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見
持続可能なDrupal Meetupのコツ - Drupal Meetup Tokyoの知見
 
What is the world where you can make your own semiconductors?
What is the world where you can make your own semiconductors?What is the world where you can make your own semiconductors?
What is the world where you can make your own semiconductors?
 
2024 03 CTEA
2024 03 CTEA2024 03 CTEA
2024 03 CTEA
 
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdf
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdfTaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdf
TaketoFujikawa_台本中の動作表現に基づくアニメーション原画システムの提案_SIGEC71.pdf
 
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ
「今からでも間に合う」GPTsによる 活用LT会 - 人とAIが協調するHumani-in-the-Loopへ
 
20240326_IoTLT_vol109_kitazaki_v1___.pdf
20240326_IoTLT_vol109_kitazaki_v1___.pdf20240326_IoTLT_vol109_kitazaki_v1___.pdf
20240326_IoTLT_vol109_kitazaki_v1___.pdf
 

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 を返すような最適化はしない