Juliaコードのディスアセンブル(Julia 0.6)

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