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

marui.hatenablog.com

上記のJulia 0.6でやったことと同じですが、Julia 1.0がリリースされたので変化があったか見てみました。

前回と同じくadd1()を関数定義します。これはJulia 0.6でも1.0でも同じ。

julia> function add1(x)                                                             
         return x+1                                                                 
       end                                                                          
add1 (generic function with 1 method)

Julia 0.6では@code_llvm@code_nativeの出力結果は以下のようになっていました。

julia> @code_llvm add1(10)

define i64 @julia_add1_62318(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[1]
        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

Julia 1.0では以下のとおり。

julia> @code_llvm add1(10)

; Function add1
; Location: REPL[1]:2
define i64 @julia_add1_35956(i64) {
top:
; Function +; {
; Location: int.jl:53
  %1 = add i64 %0, 1
;}
  ret i64 %1
}

julia> @code_native add1(10)
        .section        __TEXT,__text,regular,pure_instructions
; Function add1 {
; Location: REPL[1]:2
; Function +; {
; Location: REPL[1]:2
        decl    %eax
        leal    1(%edi), %eax
;}
        retl
        nopw    %cs:(%eax,%eax)
;}

コメントが挟まるようになり、何をしているかが分かりやすくなりました。LLVMコードは同じままですが、ネイティブコードの見た目がだいぶ変わりました。最近のアセンブリはわからないのですが、なんとなく雰囲気からEAX(レジスタ名)、DEC(1だけ引き算)、LEA(当該アドレスを返す)、RET(プロシージャから帰る)、NOP(何もしない)あたりだと考えられます。末尾にLがついているのは64 bit(Long)、Wは任意長(Words)でしょうか? NOPを続けるのは一連の命令の合計長さを64 bitの倍数にしたいから?

ちゃんと理解しようと思うとIntelのマニュアルとか読むといいのかもしれませんね。

Intel® 64 and IA-32 Architectures Software Developer Manuals | Intel® Software