Clojureの今によると、 Clojure
1.3 からプリミティブ型による型注釈が可能になったようです。プリミティブ
型による型注釈は、最適化において最も重要な情報の一つですから、当然なが
らその結果として、プリミティブな演算を伴う処理の高速化が期待できるわけ
です。スライドでは、以下の fib
関数を例にとって、型注釈を与えた場合と
与えなかった場合とで、どれだけ実行速度が違うかを示しています。
;; 型注釈なし
(defn fib [n]
(if (<= n 1)
1
(+ (fib (dec n)) (fib (- n 2)))))
(time (fib 38)) ;=> Elapsed time: 4100.272 msecs
;; 型注釈あり
(defn fib ^long [^long n]
(if (<= n 1)
1
(+ (fib (dec n)) (fib (- n 2)))))
(time (fib 38)) ;=> Elapsed time: 674.706 msecs
この手の情報を安易に鵜呑みにするのはいけないので、実際に手元の環境で確 認してみました。
$ uname -a
Linux thinkpad 2.6.38-11-generic #50-Ubuntu SMP Mon Sep 12 21:17:25 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
$ java -version
java version "1.6.0_22"
OpenJDK Runtime Environment (IcedTea6 1.10.2) (6b22-1.10.2-0ubuntu1~11.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)
$ java -jar ~/opt/clojure-1.3.0-RC0/clojure-1.3.0-RC0.jar
Clojure 1.3.0-RC0
user=> (defn fib [n] (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (dotimes [_ 3] (time (fib 38)))
"Elapsed time: 3927.97485 msecs"
"Elapsed time: 3722.368325 msecs"
"Elapsed time: 3828.55781 msecs"
nil
user=> ^D
$ java -jar ~/opt/clojure-1.3.0-RC0/clojure-1.3.0-RC0.jar
Clojure 1.3.0-RC0
user=> (defn fib ^long [^long n] (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (dotimes [_ 3] (time (fib 38)))
"Elapsed time: 803.010916 msecs"
"Elapsed time: 793.236553 msecs"
"Elapsed time: 791.748801 msecs"
nil
user=>
確かに早くなっていますね。ところで我らが SBCL はどれぐらい速いのでしょ うか。
$ sbcl
This is SBCL 1.0.51, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
*
(defun fib (n)
(declare (optimize (speed 3) (safety 0))
(fixnum n))
(if (<= n 1)
1
(the fixnum (+ (fib (1- n)) (fib (- n 2))))))
FIB
* (dotimes '3 (time (fib 38)))
Evaluation took:
1.211 seconds of real time
1.210000 seconds of total run time (1.200000 user, 0.010000 system)
99.92% CPU
1,932,215,688 processor cycles
0 bytes consed
Evaluation took:
1.205 seconds of real time
1.210000 seconds of total run time (1.210000 user, 0.000000 system)
100.41% CPU
1,922,833,256 processor cycles
0 bytes consed
Evaluation took:
1.211 seconds of real time
1.210000 seconds of total run time (1.210000 user, 0.000000 system)
99.92% CPU
1,933,261,128 processor cycles
0 bytes consed
NIL
なぜか Clojure に負ける SBCL 。悔しいので詳しく調べてみます。とりあえず
DISASSEMBLE
してみましょう。
* (disassemble 'fib)
; disassembly for FIB
; 030680F2: 4883FA08 CMP RDX, 8 ; no-arg-parsing entry point
; 0F6: 7F0A JNLE L1
; 0F8: B908000000 MOV ECX, 8
; 0FD: L0: 488BE5 MOV RSP, RBP
; 100: 5D POP RBP
; 101: C3 RET
; 102: L1: 488955F0 MOV [RBP-16], RDX
; 106: 488BCA MOV RCX, RDX
; 109: 4883E908 SUB RCX, 8
; 10D: 488BDD MOV RBX, RBP
; 110: 488D4424F0 LEA RAX, [RSP-16]
; 115: 4883EC40 SUB RSP, 64
; 119: 488BD1 MOV RDX, RCX
; 11C: 488918 MOV [RAX], RBX
; 11F: 488BE8 MOV RBP, RAX
; 122: E8C8FFFFFF CALL #x10030680EF
; 127: 488B55F0 MOV RDX, [RBP-16]
; 12B: 48894DF8 MOV [RBP-8], RCX
; 12F: 4883EA10 SUB RDX, 16
; 133: 488BCD MOV RCX, RBP
; 136: 488D4424F0 LEA RAX, [RSP-16]
; 13B: 4883EC40 SUB RSP, 64
; 13F: 488908 MOV [RAX], RCX
; 142: 488BE8 MOV RBP, RAX
; 145: E8A5FFFFFF CALL #x10030680EF
; 14A: 488B55F8 MOV RDX, [RBP-8]
; 14E: 4801CA ADD RDX, RCX
; 151: 488BCA MOV RCX, RDX
; 154: EBA7 JMP L0
NIL
至って単純ですし、これ以上に速くなるのが信じられません。参考として C で 書いたものも計測してみましょう。
$ cat fib.c
int fib(int n) {
if (n <= 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main(int argc, char *argv[]) {
fib(38);
return 0;
}
$ gcc -O2 fib.c
$ time ./a.out
./a.out 1.01s user 0.00s system 98% cpu 1.026 total
Clojure のほうが速いですが、そんなことはありえないから -O3
で再挑戦し
ます。
$ gcc -O3 fib.c
$ time ./a.out
./a.out 0.38s user 0.00s system 97% cpu 0.390 total
当然の結果です。長くなるので詳細は省きますが、両者の実行可能ファイルを
objdump
して比べてみると、 -O3
でコンパイルしたほうは明らかにコード
サイズ量が大きくなっており、インライン化された形跡が見られます。たぶん
これと同じことが Clojure vs. SBCL にも起きているのでしょう。実際に確か
めてみます。
ところで Clojure には disassemble
関数とか、 javap
のような
decompile tool はないのでしょうか。いくら探しても見つからないので、皆困っ
てないのかもしれませんが、 Clojure ほどの抽象化されたプログラミング言語
で、その手の解析ツールがないのは、結構致命的だと思ったりします。少なく
とも僕は好きになれません。また、コンパイラに関しても、 Python で言うと
ころのバイトコードを、クラスファイルに取り繕うだけのおもちゃみたいなコ
ンパイラで、期待外れでがっかりしました。まあこれから改善されるのでしょ
う。
以上のような状況ですから、 Clojure のコードを容易には解析できません。た
だし、今回に関して言えば、 fib
関数がインライン化されているかどうかが
分かれば良いわけですから、それほど苦労はしませんでした。
まず、予想を確かめるために -XX:-Inline
で実験します。ちなみにこのオプ
ションはインライン化を無効にするものです。グローバルに無効になるので、
正確な解析にはなりませんが、ちょっとした目安にはなるでしょう。
$ java -XX:-Inline -jar ~/opt/clojure-1.3.0-RC0/clojure-1.3.0-RC0.jar
Clojure 1.3.0-RC0
user=> (defn fib ^long [^long n] (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (dotimes [_ 3] (time (fib 38)))
"Elapsed time: 3107.450674 msecs"
"Elapsed time: 3086.921525 msecs"
"Elapsed time: 3073.992303 msecs"
nil
すごく遅くなってますね。やはりインライン化は効いているようです。それで
は実際にインライン化が効いているかを確かめましょう。そのためには
-XX:+PrintInlining
や -XX:+PrintAssembly
を利用します。ただし、両者
ともデバッグビルドされた HotSpot JDK でしか利用できません。
OpenJDK 6 あるいは
OpenJDK 7 のソースコードをダ
ウンロードして、以下のようにビルドしてください。ちなみに僕は OpenJDK 7
をインストールしました。新しい物好きです。
$ unzip openjdk-7-*.zip
$ cd openjdk
$ make sanity
$ make debug_build
...
$ build/linux-amd64-debug/bin/java -version
openjdk version "1.7.0-internal-debug"
OpenJDK Runtime Environment (build 1.7.0-internal-debug-tomo_2011_09_24_13_54-b00)
OpenJDK 64-Bit Server VM (build 21.0-b17-jvmg, mixed mode)
まず -XX:-Inline
と -XX:+PrintAssembly
で fib
関数のアセンブリを確
認してみます。
$ ~/opt/openjdk/build/linux-amd64-debug/bin/java -XX:-Inline -XX:+PrintAssembly -jar ~/opt/clojure-1.3.0-RC0/clojure-1.3.0-RC0.jar
user=> (defn fib ^long [^long n] (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (fib 38)
;; fib 関数のアセンブリ
{method}
- klass: {other class}
- this oop: 0x00000000bebd81e8
- method holder: 'user$fib'
- constants: 0x00000000bebd7aa0 constant pool [104] for 'user$fib' cache=0x00000000bebd8838
- access: 0x81000011 public final
- name: 'invokePrim'
- signature: '(J)J'
- max stack: 7
- max locals: 3
- size of params: 3
- method size: 17
- vtable index: -2
- i2i entry: 0x00007f87a323e060
- adapter: 0x0000000002040c90
- compiled entry 0x00007f87a32eeeb1
- code size: 54
- code start: 0x00000000bebd8190
- code end (excl): 0x00000000bebd81c6
- method data: 0x00000000bebd95b8
- checked ex length: 0
- linenumber start: 0x00000000bebd81c6
- localvar length: 2
- localvar start: 0x00000000bebd81ce
#
# long/half ( user$fib:NotNull:exact *, long, half )
#
#r018 rsi:rsi : parm 0: user$fib:NotNull:exact *
#r016 rdx:rdx : parm 1: long
# -- Old rsp -- Framesize: 48 --
#r089 rsp+44: pad2, in_preserve
#r088 rsp+40: pad2, in_preserve
#r087 rsp+36: pad2, in_preserve
#r086 rsp+32: pad2, in_preserve
#r085 rsp+28: pad2, in_preserve
#r084 rsp+24: return address
#r083 rsp+20: Fixed slot 1
#r082 rsp+16: Fixed slot 0
#r093 rsp+12: spill
#r092 rsp+ 8: spill
#r091 rsp+ 4: spill
#r090 rsp+ 0: spill
#
000 N215: # B1 <- BLOCK HEAD IS JUNK Freq: 1
000 movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()] # compressed klass
cmpq rax, rscratch1 # Inline cache check
jne SharedRuntime::_ic_miss_stub
nop # nops to align entry point
000
010 B1: # B12 B2 <- BLOCK HEAD IS JUNK Freq: 1
010 # stack bang
pushq rbp
subq rsp, #32 # Create frame
01c movq [rsp + #0], RDX # spill
020 cmpq RDX, #1
024 jle B12 P=0.500340 C=7351.000000
024
02a B2: # B22 B3 <- B1 Freq: 0.49966
02a movq R10, java/lang/Class:exact * # ptr
034 movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
038 movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
03c MEMBAR-acquire ! (empty encoding)
03c movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
040 NullCheck RBP
040
040 B3: # B14 B4 <- B2 Freq: 0.499659
040 cmpl R10, narrowoop: precise klass user$fib: 0x00007f879c19ac68:Constant:exact * # compressed ptr
047 jne,u B14 P=0.000000 C=-1.000000
047
04d B4: # B19 B5 <- B3 Freq: 0.499659
04d decode_heap_oop_not_null RBP,RBP
04d # checkcastPP of RBP
04d movq RSI, RDX # spill
050 nop # 3 bytes pad for loops and calls
053 call,static clojure.lang.Numbers::dec
# user$fib::invokePrim @ bci:21 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=RBP
# OopMap{rbp=Oop off=88}
058
058 B5: # B20 B6 <- B4 Freq: 0.499649
# Block is sole successor of call
058 movq RDX, RAX # spill
05b movq RSI, RBP # spill
05e nop # 1 bytes pad for loops and calls
05f call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{off=100}
064
064 B6: # B23 B7 <- B5 Freq: 0.499639
# Block is sole successor of call
064 movq [rsp + #8], RAX # spill
069 movq R10, java/lang/Class:exact * # ptr
073 movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
077 movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
07b MEMBAR-acquire ! (empty encoding)
07b movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
07f NullCheck RBP
07f
07f B7: # B15 B8 <- B6 Freq: 0.499639
07f cmpl R10, narrowoop: precise klass user$fib: 0x00007f879c19ac68:Constant:exact * # compressed ptr
086 jne,us B15 P=0.000000 C=-1.000000
086
088 B8: # B18 B9 <- B7 Freq: 0.499639
088 decode_heap_oop_not_null RBP,RBP
088 # checkcastPP of RBP
088 movl RDX, #2 # long (unsigned 32-bit)
08d movq RSI, [rsp + #0] # spill
091 nop # 2 bytes pad for loops and calls
093 call,static clojure.lang.Numbers::minus
# user$fib::invokePrim @ bci:42 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #8 STK[1]=_ STK[2]=RBP
# OopMap{rbp=Oop off=152}
098
098 B9: # B17 B10 <- B8 Freq: 0.499629
# Block is sole successor of call
098 movq RSI, RBP # spill
09b movq RDX, RAX # spill
09e nop # 1 bytes pad for loops and calls
09f call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #8 STK[1]=_
# OopMap{off=164}
0a4
0a4 B10: # B16 B11 <- B9 Freq: 0.499619
# Block is sole successor of call
0a4 movq RSI, [rsp + #8] # spill
0a9 movq RDX, RAX # spill
0ac nop # 3 bytes pad for loops and calls
0af call,static clojure.lang.Numbers::add
# user$fib::invokePrim @ bci:50 L[0]=_ L[1]=_ L[2]=_
# OopMap{off=180}
0b4
0b4 B11: # B13 <- B10 Freq: 0.499609
# Block is sole successor of call
0b4 jmp,s B13
0b4
0b6 B12: # B13 <- B1 Freq: 0.50034
0b6 movl RAX, #1 # long (unsigned 32-bit)
0b6
0bb B13: # N215 <- B11 B12 Freq: 0.999949
0bb addq rsp, 32 # Destroy frame
popq rbp
testl rax, [rip + #offset_to_poll_page] # Safepoint: poll for GC
0c6 ret
0c6
0c7 B14: # N215 <- B3 Freq: 1e-35
0c7 movl RSI, #-34 # int
0cc nop # 3 bytes pad for loops and calls
0cf call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=RBP
# OopMap{rbp=NarrowOop off=212}
0d4 int3 # ShouldNotReachHere
0d4
0d9 B15: # N215 <- B7 Freq: 1e-35
0d9 movl RSI, #-34 # int
0de nop # 1 bytes pad for loops and calls
0df call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=rsp + #8 STK[1]=_ STK[2]=RBP
# OopMap{rbp=NarrowOop off=228}
0e4 int3 # ShouldNotReachHere
0e4
0e9 B16: # B21 <- B10 Freq: 4.99619e-06
0e9 # exception oop is in rax; no code emitted
0e9 movq RSI, RAX # spill
0ec jmp,s B21
0ec
0ee B17: # B21 <- B9 Freq: 4.99629e-06
0ee # exception oop is in rax; no code emitted
0ee movq RSI, RAX # spill
0f1 jmp,s B21
0f1
0f3 B18: # B21 <- B8 Freq: 4.99639e-06
0f3 # exception oop is in rax; no code emitted
0f3 movq RSI, RAX # spill
0f6 jmp,s B21
0f6
0f8 B19: # B21 <- B4 Freq: 4.99659e-06
0f8 # exception oop is in rax; no code emitted
0f8 movq RSI, RAX # spill
0fb jmp,s B21
0fb
0fd B20: # B21 <- B5 Freq: 4.99649e-06
0fd # exception oop is in rax; no code emitted
0fd movq RSI, RAX # spill
0fd
100 B21: # N215 <- B19 B20 B18 B17 B16 Freq: 2.4982e-05
100 addq rsp, 32 # Destroy frame
popq rbp
105 jmp rethrow_stub
105
10a B22: # N215 <- B2 Freq: 5.06295e-07
10a movl RSI, #-12 # int
10f movq RBP, RDX # spill
112 nop # 1 bytes pad for loops and calls
113 call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=RBP L[2]=_ STK[0]=#NULL
# OopMap{off=280}
118 int3 # ShouldNotReachHere
118
11d B23: # N215 <- B6 Freq: 5.06274e-07
11d movl RSI, #-12 # int
122 movq RBP, [rsp + #0] # spill
126 nop # 1 bytes pad for loops and calls
127 call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=RBP L[2]=_ STK[0]=rsp + #8 STK[1]=_ STK[2]=#NULL
# OopMap{off=300}
12c int3 # ShouldNotReachHere
12c
次に -XX:+Inline
した場合で fib
関数のアセンブリを確認してみます。
$ ~/opt/openjdk/build/linux-amd64-debug/bin/java -XX:+Inline -XX:+PrintAssembly -jar ~/opt/clojure-1.3.0-RC0/clojure-1.3.0-RC0.jar
user=> (defn fib ^long [^long n] (if (<= n 1) 1 (+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (fib 38)
;; fib 関数のアセンブリ
{method}
- klass: {other class}
- this oop: 0x00000000bebced68
- method holder: 'user$fib'
- constants: 0x00000000bebce620 constant pool [104] for 'user$fib' cache=0x00000000bebcf3b8
- access: 0x81000011 public final
- name: 'invokePrim'
- signature: '(J)J'
- max stack: 7
- max locals: 3
- size of params: 3
- method size: 17
- vtable index: -2
- i2i entry: 0x00007fd949404060
- adapter: 0x0000000000ba7c90
- compiled entry 0x00007fd9494b4eb1
- code size: 54
- code start: 0x00000000bebced10
- code end (excl): 0x00000000bebced46
- method data: 0x00000000bebd0158
- checked ex length: 0
- linenumber start: 0x00000000bebced46
- localvar length: 2
- localvar start: 0x00000000bebced4e
#
# long/half ( user$fib:NotNull:exact *, long, half )
#
#r018 rsi:rsi : parm 0: user$fib:NotNull:exact *
#r016 rdx:rdx : parm 1: long
# -- Old rsp -- Framesize: 64 --
#r089 rsp+60: pad2, in_preserve
#r088 rsp+56: pad2, in_preserve
#r087 rsp+52: pad2, in_preserve
#r086 rsp+48: pad2, in_preserve
#r085 rsp+44: pad2, in_preserve
#r084 rsp+40: return address
#r083 rsp+36: Fixed slot 1
#r082 rsp+32: Fixed slot 0
#r097 rsp+28: spill
#r096 rsp+24: spill
#r095 rsp+20: spill
#r094 rsp+16: spill
#r093 rsp+12: spill
#r092 rsp+ 8: spill
#r091 rsp+ 4: spill
#r090 rsp+ 0: spill
#
000 N805: # B1 <- BLOCK HEAD IS JUNK Freq: 1
000 movl rscratch1, [j_rarg0 + oopDesc::klass_offset_in_bytes()] # compressed klass
cmpq rax, rscratch1 # Inline cache check
jne SharedRuntime::_ic_miss_stub
nop # nops to align entry point
000
010 B1: # B39 B2 <- BLOCK HEAD IS JUNK Freq: 1
010 # stack bang
pushq rbp
subq rsp, #48 # Create frame
01c movq R8, RDX # spill
01f cmpq RDX, #1
023 jle B39 P=0.500344 C=7273.000000
023
029 B2: # B68 B3 <- B1 Freq: 0.499656
029 movq R10, java/lang/Class:exact * # ptr
033 movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
037 movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
03b MEMBAR-acquire ! (empty encoding)
03b movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
03f NullCheck RBP
03f
03f B3: # B62 B4 <- B2 Freq: 0.499656
03f cmpl R10, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
046 jne,u B62 P=0.000000 C=-1.000000
046
04c B4: # B41 B5 <- B3 Freq: 0.499656
04c movq R10, #-9223372036854775808 # long
056 cmpq RDX, R10
059 je B41 P=0.000000 C=7240.000000
059
05f B5: # B6 <- B4 Freq: 0.499656
05f movq R10, RDX # spill
062 decq R10 # long
065
065 B6: # B18 B7 <- B42 B5 Freq: 0.499656
065 movq R11, java/lang/Class:exact * # ptr
06f movl R11, [R11 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
073 cmpq R10, #1
077 jle B18 P=0.500344 C=7273.000000
077
07d B7: # B70 B8 <- B6 Freq: 0.249656
07d movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
081 MEMBAR-acquire ! (empty encoding)
081 movl R11, [RBP + #8 (8-bit)] # compressed klass ptr
085 NullCheck RBP
085
085 B8: # B63 B9 <- B7 Freq: 0.249656
085 cmpl R11, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
08c jne,u B63 P=0.000000 C=-1.000000
08c
092 B9: # B46 B10 <- B8 Freq: 0.249656
092 decode_heap_oop_not_null RSI,RBP
095 # checkcastPP of RSI
095 movq R11, #-9223372036854775808 # long
09f cmpq R10, R11
0a2 je B46 P=0.000000 C=7240.000000
0a2
0a8 B10: # B11 <- B9 Freq: 0.249656
0a8 movq RDX, R10 # spill
0ab decq RDX # long
0ae
0ae B11: # B83 B12 <- B47 B10 Freq: 0.249656
0ae movq [rsp + #8], R10 # spill
0b3 movq [rsp + #0], R8 # spill
0b7 call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #8 L[2]=_
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{off=188}
0bc
0bc B12: # B71 B13 <- B11 Freq: 0.249651
# Block is sole successor of call
0bc movq [rsp + #16], RAX # spill
0c1 movq R10, java/lang/Class:exact * # ptr
0cb movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
0cf movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
0d3 MEMBAR-acquire ! (empty encoding)
0d3 movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
0d7 NullCheck RBP
0d7
0d7 B13: # B64 B14 <- B12 Freq: 0.249651
0d7 cmpl R10, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
0de jne,u B64 P=0.000000 C=-1.000000
0de
0e4 B14: # B48 B15 <- B13 Freq: 0.249651
0e4 decode_heap_oop_not_null RSI,RBP
0e7 # checkcastPP of RSI
0e7 movq RDX, [rsp + #8] # spill
0ec addq RDX, #-2 # long
0f0 movq R10, [rsp + #8] # spill
0f5 xorq R10, RDX # long
0f8 testq R10, R10
0fb jl B48 P=0.000000 C=8268.000000
0fb
101 B15: # B84 B16 <- B50 B48 B14 Freq: 0.249651
101 nop # 2 bytes pad for loops and calls
103 call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #16 STK[1]=_
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{off=264}
108
108 B16: # B53 B17 <- B15 Freq: 0.249646
# Block is sole successor of call
108 movq R9, [rsp + #16] # spill
10d addq R9, RAX # long
110 movq R11, [rsp + #16] # spill
115 xorq R11, R9 # long
118 testq R11, R11
11b jl B53 P=0.000000 C=8899.000000
11b
121 B17: # B19 <- B55 B53 B16 Freq: 0.249646
121 movq R10, java/lang/Class:exact * # ptr
12b movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
12f movq R8, [rsp + #0] # spill
133 jmp,s B19
133
135 B18: # B19 <- B6 Freq: 0.25
135 movl R9, #1 # long (unsigned 32-bit)
135
13b B19: # B69 B20 <- B17 B18 Freq: 0.499645
13b movl R10, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
13f MEMBAR-acquire ! (empty encoding)
13f movl RCX, [R10 + #8 (8-bit)] # compressed klass ptr
143 NullCheck R10
143
143 B20: # B65 B21 <- B19 Freq: 0.499645
143 cmpl RCX, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
149 jne,u B65 P=0.000000 C=-1.000000
149
14f B21: # B43 B22 <- B20 Freq: 0.499645
14f movq RCX, R8 # spill
152 addq RCX, #-2 # long
156 xorq R8, RCX # long
159 testq R8, R8
15c jl B43 P=0.000000 C=8268.000000
15c
162 B22: # B34 B23 <- B45 B43 B21 Freq: 0.499645
162 cmpq RCX, #1
166 jle B34 P=0.500344 C=7273.000000
166
16c B23: # B72 B24 <- B22 Freq: 0.249651
16c movq R10, java/lang/Class:exact * # ptr
176 movl R11, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
17a movl RBP, [R11 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
17e MEMBAR-acquire ! (empty encoding)
17e movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
182 NullCheck RBP
182
182 B24: # B66 B25 <- B23 Freq: 0.24965
182 cmpl R10, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
189 jne,u B66 P=0.000000 C=-1.000000
189
18f B25: # B51 B26 <- B24 Freq: 0.24965
18f decode_heap_oop_not_null RSI,RBP
192 # checkcastPP of RSI
192 movq R10, #-9223372036854775808 # long
19c cmpq RCX, R10
19f je B51 P=0.000000 C=7240.000000
19f
1a5 B26: # B27 <- B25 Freq: 0.24965
1a5 movq RDX, RCX # spill
1a8 decq RDX # long
1ab
1ab B27: # B85 B28 <- B52 B26 Freq: 0.24965
1ab movq [rsp + #8], RCX # spill
1b0 movq [rsp + #0], R9 # spill
1b4 nop # 3 bytes pad for loops and calls
1b7 call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #8 L[2]=_
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{off=444}
1bc
1bc B28: # B73 B29 <- B27 Freq: 0.249645
# Block is sole successor of call
1bc movq [rsp + #16], RAX # spill
1c1 movq R10, java/lang/Class:exact * # ptr
1cb movl R10, [R10 + #124 (8-bit)] # compressed ptr ! Field user$fib.const__3
1cf movl RBP, [R10 + #28 (8-bit)] # compressed ptr ! Field Volatileclojure/lang/Var.root
1d3 MEMBAR-acquire ! (empty encoding)
1d3 movl R10, [RBP + #8 (8-bit)] # compressed klass ptr
1d7 NullCheck RBP
1d7
1d7 B29: # B67 B30 <- B28 Freq: 0.249645
1d7 cmpl R10, narrowoop: precise klass user$fib: 0x00007fd940329268:Constant:exact * # compressed ptr
1de jne,u B67 P=0.000000 C=-1.000000
1de
1e4 B30: # B56 B31 <- B29 Freq: 0.249645
1e4 decode_heap_oop_not_null RBP,RBP
1e4 # checkcastPP of RBP
1e4 movq RDX, [rsp + #8] # spill
1e9 addq RDX, #-2 # long
1ed movq R10, [rsp + #8] # spill
1f2 xorq R10, RDX # long
1f5 testq R10, R10
1f8 jl B56 P=0.000000 C=8268.000000
1f8
1fe B31: # B86 B32 <- B58 B56 B30 Freq: 0.249645
1fe movq RSI, RBP # spill
201 nop # 2 bytes pad for loops and calls
203 call,static user$fib::invokePrim
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #16 STK[1]=_
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{off=520}
208
208 B32: # B59 B33 <- B31 Freq: 0.24964
# Block is sole successor of call
208 movq R11, [rsp + #16] # spill
20d addq R11, RAX # long
210 movq R10, [rsp + #16] # spill
215 xorq R10, R11 # long
218 testq R10, R10
21b jl B59 P=0.000000 C=8899.000000
21b
221 B33: # B35 <- B61 B59 B32 Freq: 0.24964
221 movq R9, [rsp + #0] # spill
225 jmp,s B35
225
227 B34: # B35 <- B22 Freq: 0.249994
227 movl R11, #1 # long (unsigned 32-bit)
227
22d B35: # B40 B36 <- B33 B34 Freq: 0.499634
22d movq RAX, R9 # spill
230 addq RAX, R11 # long
233 xorq R9, RAX # long
236 testq R9, R9
239 jge,s B40 P=1.000000 C=8899.000000
239
23b B36: # B40 B37 <- B35 Freq: 2.38244e-07
23b xorq R11, RAX # long
23e testq R11, R11
241 jge,s B40 P=0.500000 C=-1.000000
241
243 B37: # B80 B38 <- B36 Freq: 1.19122e-07
243 call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::add @ bci:23 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:50 L[0]=_ L[1]=_ L[2]=_
# OopMap{off=584}
248
248 B38: # B40 <- B37 Freq: 1.1912e-07
# Block is sole successor of call
248 movslq RAX, RAX # i2l
24b jmp,s B40
24b
24d B39: # B40 <- B1 Freq: 0.500344
24d movl RAX, #1 # long (unsigned 32-bit)
24d
252 B40: # N805 <- B38 B36 B35 B39 Freq: 0.999978
252 addq rsp, 48 # Destroy frame
popq rbp
testl rax, [rip + #offset_to_poll_page] # Safepoint: poll for GC
25d ret
25d
25e B41: # B82 B42 <- B4 Freq: 2.38254e-07
25e movq [rsp + #0], RDX # spill
262 decode_heap_oop_not_null RBP,RBP
262 # checkcastPP of RBP
262 nop # 1 bytes pad for loops and calls
263 call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::dec @ bci:8 L[0]=_ L[1]=_
# user$fib::invokePrim @ bci:21 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=RBP
# OopMap{rbp=Oop off=616}
268
268 B42: # B6 <- B41 Freq: 2.3825e-07
# Block is sole successor of call
268 movslq R10, RAX # i2l
26b movq R8, [rsp + #0] # spill
26f jmp B6
26f
274 B43: # B22 B44 <- B21 Freq: 2.38249e-07
274 movq R11, RCX # spill
277 xorq R11, #-3 # long
27b testq R11, R11
27e jge B22 P=0.500000 C=-1.000000
27e
284 B44: # B81 B45 <- B43 Freq: 1.19125e-07
284 movq [rsp + #0], R9 # spill
288 decode_heap_oop_not_null RBP,R10
28b # checkcastPP of RBP
28b call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::minus @ bci:27 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:42 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_ STK[2]=RBP
# OopMap{rbp=Oop off=656}
290
290 B45: # B22 <- B44 Freq: 1.19122e-07
# Block is sole successor of call
290 movslq RCX, RAX # i2l
293 movq R9, [rsp + #0] # spill
297 jmp B22
297
29c B46: # B79 B47 <- B9 Freq: 1.19045e-07
29c movq [rsp + #8], RSI # spill
2a1 movq [rsp + #0], R10 # spill
2a5 movq RBP, R8 # spill
2a8 nop # 3 bytes pad for loops and calls
2ab call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::dec @ bci:8 L[0]=_ L[1]=_
# user$fib::invokePrim @ bci:21 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=rsp + #8
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=RBP L[2]=_
# OopMap{[8]=Oop off=688}
2b0
2b0 B47: # B11 <- B46 Freq: 1.19043e-07
# Block is sole successor of call
2b0 movslq RDX, RAX # i2l
2b3 movq R8, RBP # spill
2b6 movq R10, [rsp + #0] # spill
2ba movq RSI, [rsp + #8] # spill
2bf jmp B11
2bf
2c4 B48: # B15 B49 <- B14 Freq: 1.19043e-07
2c4 movq R10, RDX # spill
2c7 xorq R10, #-3 # long
2cb testq R10, R10
2ce jge B15 P=0.500000 C=-1.000000
2ce
2d4 B49: # B77 B50 <- B48 Freq: 5.95213e-08
2d4 movq RBP, RSI # spill
2d7 call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::minus @ bci:27 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:42 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=RBP
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{rbp=Oop off=732}
2dc
2dc B50: # B15 <- B49 Freq: 5.95202e-08
# Block is sole successor of call
2dc movslq RDX, RAX # i2l
2df movq RSI, RBP # spill
2e2 jmp B15
2e2
2e7 B51: # B78 B52 <- B25 Freq: 1.19043e-07
2e7 movq [rsp + #8], RSI # spill
2ec movq [rsp + #0], RCX # spill
2f0 movq RBP, R9 # spill
2f3 call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::dec @ bci:8 L[0]=_ L[1]=_
# user$fib::invokePrim @ bci:21 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=rsp + #8
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=RBP STK[1]=_
# OopMap{[8]=Oop off=760}
2f8
2f8 B52: # B27 <- B51 Freq: 1.1904e-07
# Block is sole successor of call
2f8 movslq RDX, RAX # i2l
2fb movq R9, RBP # spill
2fe movq RCX, [rsp + #0] # spill
302 movq RSI, [rsp + #8] # spill
307 jmp B27
307
30c B53: # B17 B54 <- B16 Freq: 1.1904e-07
30c xorq RAX, R9 # long
30f testq RAX, RAX
312 jge B17 P=0.500000 C=-1.000000
312
318 B54: # B76 B55 <- B53 Freq: 5.95202e-08
318 nop # 3 bytes pad for loops and calls
31b call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::add @ bci:23 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:50 L[0]=_ L[1]=_ L[2]=_
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{off=800}
320
320 B55: # B17 <- B54 Freq: 5.9519e-08
# Block is sole successor of call
320 movslq R9, RAX # i2l
323 jmp B17
323
328 B56: # B31 B57 <- B30 Freq: 1.1904e-07
328 movq R10, RDX # spill
32b xorq R10, #-3 # long
32f testq R10, R10
332 jge B31 P=0.500000 C=-1.000000
332
338 B57: # B75 B58 <- B56 Freq: 5.952e-08
338 nop # 3 bytes pad for loops and calls
33b call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::minus @ bci:27 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:42 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=RBP
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{rbp=Oop off=832}
340
340 B58: # B31 <- B57 Freq: 5.95188e-08
# Block is sole successor of call
340 movslq RDX, RAX # i2l
343 jmp B31
343
348 B59: # B33 B60 <- B32 Freq: 1.19038e-07
348 movq R8, RAX # spill
34b xorq R8, R11 # long
34e testq R8, R8
351 jge B33 P=0.500000 C=-1.000000
351
357 B60: # B74 B61 <- B59 Freq: 5.95188e-08
357 call,static clojure.lang.Numbers::throwIntOverflow
# clojure.lang.Numbers::add @ bci:23 L[0]=_ L[1]=_ L[2]=_ L[3]=_ L[4]=_ L[5]=_
# user$fib::invokePrim @ bci:50 L[0]=_ L[1]=_ L[2]=_
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{off=860}
35c
35c B61: # B33 <- B60 Freq: 5.95176e-08
# Block is sole successor of call
35c movslq R11, RAX # i2l
35f jmp B33
35f
364 B62: # N805 <- B3 Freq: 1e-35
364 movl RSI, #-34 # int
369 movq [rsp + #0], RDX # spill
36d nop # 2 bytes pad for loops and calls
36f call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=RBP
# OopMap{rbp=NarrowOop off=884}
374 int3 # ShouldNotReachHere
374
379 B63: # N805 <- B8 Freq: 1e-35
379 movl RSI, #-34 # int
37e movq [rsp + #0], R8 # spill
382 movq [rsp + #8], R10 # spill
387 call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=RBP
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{rbp=NarrowOop off=908}
38c int3 # ShouldNotReachHere
38c
391 B64: # N805 <- B13 Freq: 1e-35
391 movl RSI, #-34 # int
396 nop # 1 bytes pad for loops and calls
397 call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=RBP
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=rsp + #0 L[2]=_
# OopMap{rbp=NarrowOop off=924}
39c int3 # ShouldNotReachHere
39c
3a1 B65: # N805 <- B20 Freq: 1e-35
3a1 movl RSI, #-34 # int
3a6 movq RBP, R8 # spill
3a9 movq [rsp + #0], R9 # spill
3ad movl [rsp + #8], R10 # spill
3b2 nop # 1 bytes pad for loops and calls
3b3 call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=RBP L[2]=_ STK[0]=rsp + #0 STK[1]=_ STK[2]=rsp + #8
# OopMap{[8]=NarrowOop off=952}
3b8 int3 # ShouldNotReachHere
3b8
3bd B66: # N805 <- B24 Freq: 1e-35
3bd movl RSI, #-34 # int
3c2 movq [rsp + #0], R9 # spill
3c6 movq [rsp + #8], RCX # spill
3cb call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=RBP
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{rbp=NarrowOop off=976}
3d0 int3 # ShouldNotReachHere
3d0
3d5 B67: # N805 <- B29 Freq: 1e-35
3d5 movl RSI, #-34 # int
3da nop # 1 bytes pad for loops and calls
3db call,static wrapper for: uncommon_trap(reason='class_check' action='maybe_recompile')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=RBP
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{rbp=NarrowOop off=992}
3e0 int3 # ShouldNotReachHere
3e0
3e5 B68: # N805 <- B2 Freq: 5.06291e-07
3e5 movl RSI, #-12 # int
3ea movq RBP, RDX # spill
3ed nop # 2 bytes pad for loops and calls
3ef call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=RBP L[2]=_ STK[0]=#NULL
# OopMap{off=1012}
3f4 int3 # ShouldNotReachHere
3f4
3f9 B69: # N805 <- B19 Freq: 5.0628e-07
3f9 movl RSI, #-12 # int
3fe movq RBP, R8 # spill
401 movq [rsp + #0], R9 # spill
405 nop # 2 bytes pad for loops and calls
407 call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=RBP L[2]=_ STK[0]=rsp + #0 STK[1]=_ STK[2]=#NULL
# OopMap{off=1036}
40c int3 # ShouldNotReachHere
40c
411 B70: # N805 <- B7 Freq: 2.52971e-07
411 movl RSI, #-12 # int
416 movq RBP, R8 # spill
419 movq [rsp + #0], R10 # spill
41d nop # 2 bytes pad for loops and calls
41f call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=#NULL
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=RBP L[2]=_
# OopMap{off=1060}
424 int3 # ShouldNotReachHere
424
429 B71: # N805 <- B12 Freq: 2.52966e-07
429 movl RSI, #-12 # int
42e movq RBP, [rsp + #0] # spill
432 nop # 1 bytes pad for loops and calls
433 call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=#NULL
# user$fib::invokePrim @ bci:24 L[0]=_ L[1]=RBP L[2]=_
# OopMap{off=1080}
438 int3 # ShouldNotReachHere
438
43d B72: # N805 <- B23 Freq: 2.52966e-07
43d movl RSI, #-12 # int
442 movq RBP, R9 # spill
445 movq [rsp + #0], RCX # spill
449 nop # 2 bytes pad for loops and calls
44b call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:17 L[0]=_ L[1]=rsp + #0 L[2]=_ STK[0]=#NULL
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=RBP STK[1]=_
# OopMap{off=1104}
450 int3 # ShouldNotReachHere
450
455 B73: # N805 <- B28 Freq: 2.5296e-07
455 movl RSI, #-12 # int
45a nop # 1 bytes pad for loops and calls
45b call,static wrapper for: uncommon_trap(reason='null_check' action='make_not_entrant')
# user$fib::invokePrim @ bci:35 L[0]=_ L[1]=rsp + #8 L[2]=_ STK[0]=rsp + #16 STK[1]=_ STK[2]=#NULL
# user$fib::invokePrim @ bci:45 L[0]=_ L[1]=_ L[2]=_ STK[0]=rsp + #0 STK[1]=_
# OopMap{off=1120}
460 int3 # ShouldNotReachHere
460
465 B74: # B87 <- B60 Freq: 5.95188e-13
465 # exception oop is in rax; no code emitted
465 movq RSI, RAX # spill
468 jmp,s B87
468
46a B75: # B87 <- B57 Freq: 5.952e-13
46a # exception oop is in rax; no code emitted
46a movq RSI, RAX # spill
46d jmp,s B87
46d
46f B76: # B87 <- B54 Freq: 5.95202e-13
46f # exception oop is in rax; no code emitted
46f movq RSI, RAX # spill
472 jmp,s B87
472
474 B77: # B87 <- B49 Freq: 5.95213e-13
474 # exception oop is in rax; no code emitted
474 movq RSI, RAX # spill
477 jmp,s B87
477
479 B78: # B87 <- B51 Freq: 1.19043e-12
479 # exception oop is in rax; no code emitted
479 movq RSI, RAX # spill
47c jmp,s B87
47c
47e B79: # B87 <- B46 Freq: 1.19045e-12
47e # exception oop is in rax; no code emitted
47e movq RSI, RAX # spill
481 jmp,s B87
481
483 B80: # B87 <- B37 Freq: 1.19122e-12
483 # exception oop is in rax; no code emitted
483 movq RSI, RAX # spill
486 jmp,s B87
486
488 B81: # B87 <- B44 Freq: 1.19125e-12
488 # exception oop is in rax; no code emitted
488 movq RSI, RAX # spill
48b jmp,s B87
48b
48d B82: # B87 <- B41 Freq: 2.38254e-12
48d # exception oop is in rax; no code emitted
48d movq RSI, RAX # spill
490 jmp,s B87
490
492 B83: # B87 <- B11 Freq: 2.49656e-06
492 # exception oop is in rax; no code emitted
492 movq RSI, RAX # spill
495 jmp,s B87
495
497 B84: # B87 <- B15 Freq: 2.49651e-06
497 # exception oop is in rax; no code emitted
497 movq RSI, RAX # spill
49a jmp,s B87
49a
49c B85: # B87 <- B27 Freq: 2.4965e-06
49c # exception oop is in rax; no code emitted
49c movq RSI, RAX # spill
49f jmp,s B87
49f
4a1 B86: # B87 <- B31 Freq: 2.49645e-06
4a1 # exception oop is in rax; no code emitted
4a1 movq RSI, RAX # spill
4a1
4a4 B87: # N805 <- B82 B79 B83 B77 B84 B76 B81 B78 B85 B75 B86 B74 B80 Freq: 9.98603e-06
4a4 addq rsp, 48 # Destroy frame
popq rbp
4a9 jmp rethrow_stub
4a9
読み方がよく分かりませんが、明らかにコードサイズが大きくなってますね。
以上から fib
関数はインライン化されていることが分かりました。こんなに
インライン化されては、さすがの SBCL でも敵いません。
結論: Clojure コンパイラがすごいんじゃなくて HotSpot がすごい。 まあで もプリミティブ型をちゃんとコンパイルするようになったのは偉いか。
ところで SBCL が再帰関数のインライン化を行わないのは、何か理由があるの でしょうか。僕が知る限り、インライン化にこれといった障壁はないと思うの ですが。