www.youtube.com
ヨーロッパLISPコンファレンスで発表された上記のプレゼンの中に、Juliaコードをネイティブコードに変換して中身を確認するマクロが紹介されていました。
公式ドキュメントではEssentials · The Julia Languageに書かれていますが、面白そうだったので、ちょっとだけ試してみました。
まずは簡単な関数を定義します。
julia> function add1(x)
return x+1
end
add1 (generic function with 1 method)
@code_llvm
や@code_native
マクロを使ってあげると、LLVMバイトコードや、ネイティブコードのアセンブリが見られます。
julia> @code_llvm add1(10)
define i64 @julia_add1_62362(i64) #0 !dbg !5 {
top:
%1 = add i64 %0, 1
ret i64 %1
}
julia> @code_native add1(10)
.section __TEXT,__text,regular,pure_instructions
Filename: REPL[5]
pushl %ebp
decl %eax
movl %esp, %ebp
Source line: 2
decl %eax
leal 1(%edi), %eax
popl %ebp
retl
nop
nop
nop
nop
nop
nop
@code_native add1()
のようにしてもエラーが出てしまうのは
多重ディスパッチの影響があるので、add1(10)
と具体的な値をつけてあげることで関数が適用する型を定めてあげるのだと思います。たとえばadd1(10.0)
と浮動小数点数を入れたり、add1(BigInt(10))
と多倍長整数にすると、生成されるコードも異なるものになります。
julia> @code_llvm add1(10.0)
define double @julia_add1_62439(double) #0 !dbg !5 {
top:
%1 = fadd double %0, 1.000000e+00
ret double %1
}
julia> @code_native add1(10.0)
.section __TEXT,__text,regular,pure_instructions
Filename: REPL[5]
pushl %ebp
decl %eax
movl %esp, %ebp
decl %eax
movl $200081256, %eax ## imm = 0xBECFF68
addl %eax, (%eax)
addb %al, (%eax)
Source line: 2
addsd (%eax), %xmm0
popl %ebp
retl
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
ちなみにCommon Lispにも言語仕様に似たものがありますね。(以下はSBCLの場合)
* (defun add1 (x)
(+ x 1))
ADD1
* (disassemble #'add1)
; disassembly for ADD1
; Size: 39 bytes. Origin: #x1001AC6434
; 34: 498B4C2458 MOV RCX, [R12+88] ; no-arg-parsing entry point
; thread.binding-stack-pointer
; 39: 48894DF8 MOV [RBP-8], RCX
; 3D: BF02000000 MOV EDI, 2
; 42: 488BD3 MOV RDX, RBX
; 45: 41BB8004B021 MOV R11D, #x21B00480 ; GENERIC-+
; 4B: 41FFD3 CALL R11
; 4E: 488B5DF0 MOV RBX, [RBP-16]
; 52: 488BE5 MOV RSP, RBP
; 55: F8 CLC
; 56: 5D POP RBP
; 57: C3 RET
; 58: 0F0B10 BREAK 16 ; Invalid argument count trap
NIL