Upload
y-uti
View
3.129
Download
0
Embed Size (px)
Citation preview
自己紹介内山雄司 (@y__uti)
◦ http://y-uti.hatenablog.jp/ (phpusers-ja)
仕事◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています
興味◦ プログラミング言語処理系
◦ 機械学習
2016-12-11 第七回闇PHP勉強会 2
(第 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
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_EXECin 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_REQUESTin 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_REQUESTin 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_COUNTERSin 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_COUNTERSin 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_LOADin 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_COMMENTin 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
zend_real_jit_funcin 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 に変換される
DynASMis 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);
}
◦ 以後は生成された機械語をハンドラとして実行する
サンプルプログラム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=3sample1.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 = 0CV2($i) = QM_ASSIGN int(0) // $i = 0JMP 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 = 0CV2($i) = QM_ASSIGN int(0) // $i = 0JMP BB3 // jump to BB3
BB2: CV1($sum) = ADD CV1($sum) CV2($i) // $sum = $sum + $iPRE_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, %rspcall ZEND_RECV_SPEC_HANDLERcmp $0x0, EG(exception)jnz JIT$$exception_handlerjmp .L1
.ENTRY1:sub $0x8, %rsp
.L1:call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLERcall ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLERjmp .L3
.L2:mov $0x7fcd524d6c10, %r15call ZEND_ADD_SPEC_CV_CV_HANDLERcmp $0x0, EG(exception)jnz JIT$$exception_handlercall 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_HANDLERcall ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLERjmp .L3
.L2:mov $0x7fcd524d6c10, %r15call ZEND_ADD_SPEC_CV_CV_HANDLERcmp $0x0, EG(exception)jnz JIT$$exception_handlercall ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLERcmp $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) = 0mov $0x4, 0x68(%r14) // typeinfo($sum) = IS_LONG (= 4)mov $0x0, 0x70(%r14) // val($i) = 0mov $0x4, 0x78(%r14) // typeinfo($i) = IS_LONGjmp .L3
.L2:mov $0x7fd46c0d6c10, %r15call ZEND_ADD_SPEC_CV_CV_HANDLER // $sum = $sum + $i (これは展開されない)
cmp $0x0, EG(exception)jnz JIT$$exception_handlercmp $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 = $sumadd 0x70(%r14), %rax // %rax = %rax + $ijo .L9 // オーバーフローしたら .L9 へ
mov %rax, 0x60(%r14) // $sum = %rax.L3:
cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べるjnz .L13 // IS_LONG でなければ .L13 へ
inc 0x70(%r14) // ++$ijo .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
サンプルプログラム以下の 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, @functionf:.LFB0:
.cfi_startprocmovl $4, %eaxret.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, %rspmov $0x1, 0x50(%r14)mov $0x4, 0x58(%r14)cmp $0x4, 0x58(%r14)jnz .L5inc 0x50(%r14)jo .L4
.L1:cmp $0x4, 0x58(%r14)jnz .L11inc 0x50(%r14)jo .L10
.L2:cmp $0x4, 0x58(%r14)jnz .L17inc 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, %rspmov $0x1, 0x50(%r14)mov $0x4, 0x58(%r14)inc 0x50(%r14)inc 0x50(%r14)inc 0x50(%r14)mov 0x10(%r14), %rcxtest %rcx, %rcxjz .L1mov 0x50(%r14), %rdxmov %rdx, (%rcx)mov $0x4, 0x8(%rcx)
.L1:
...
◦ 整数型でありオーバーフローもしないことが分かっている
◦ しかし、あくまでも「バイトコード命令ごとに」コード生成◦ gccのように 4 を返すような最適化はしない