D の ABI

#D で面白いことを教えてもらいました。

import std.stdio;

void func(int a, int b, int c, int d) {
    writefln(&a); writefln(&b);
    writefln(&c); writefln(&d);
}

int main() {
    func(1,2,3,4);
    return 0;
}

DMD で上記のコードをコンパイルして実行すると、以下のような出力が得られます。

bff9cdc4
bff9cdc0
bff9cdbc
bff9cdb0

これを見ると、a-b, b-c のメモリの間隔は 4byte なのですが、 c と d のメモリ間隔は 12byte になっています。これは D の ABI によるものだということです。 D の ABI では、最後の引数はレジスタ渡しになるとのことで、そのため &d の計算をするために関数のコンテキストで 8byte スタック消費した後の空間に d の位置を確保するためらしいです。

というわけで確認。

import std.stdio;

void func(int a) {
    int i;
    asm { mov i, EAX; }
    writefln(i);
}

int main() {
    func(1);
    return 0;
}

結果、ちゃんと 1 が出ました。この挙動は extern(C) したらもちろん消えます。これって C から目デマングルした D の関数呼んだら悲しいことになるということですね気をつけましょう誰もそんなことしません。あと gdc はこの挙動をしない模様。そりゃそうか。

あと、以下のようなものは、

void func(int a, int b) {
    writefln(a);
    writefln(b);
}

より、

void func(int a, int b) {
    writefln(b);
    writefln(a);
}

の方が速いみたいです。逆アセして確認しました。前者の逆アセ。

0804bb1c <_D1a4funcFiiZv>:
 804bb1c:       55                      push   %ebp
 804bb1d:       8b ec                   mov    %esp,%ebp
 804bb1f:       83 ec 04                sub    $0x4,%esp
 804bb22:       89 45 fc                mov    %eax,0xfffffffc(%ebp)
 804bb25:       ff 75 08                pushl  0x8(%ebp)
 804bb28:       ff 35 1c b9 05 08       pushl  0x805b91c
 804bb2e:       ff 35 18 b9 05 08       pushl  0x805b918
 804bb34:       e8 0b 55 00 00          call   8051044 <_D3std5stdio8writeflnFYv>
 804bb39:       ff 75 fc                pushl  0xfffffffc(%ebp)
 804bb3c:       ff 35 1c b9 05 08       pushl  0x805b91c
 804bb42:       ff 35 18 b9 05 08       pushl  0x805b918
 804bb48:       e8 f7 54 00 00          call   8051044 <_D3std5stdio8writeflnFYv>
 804bb4d:       83 c4 18                add    $0x18,%esp
 804bb50:       c9                      leave
 804bb51:       c2 04 00                ret    $0x4

後者の逆アセ。

0804bb1c <_D1a4funcFiiZv>:
 804bb1c:       55                      push   %ebp
 804bb1d:       8b ec                   mov    %esp,%ebp
 804bb1f:       50                      push   %eax
 804bb20:       ff 35 14 b9 05 08       pushl  0x805b914
 804bb26:       ff 35 10 b9 05 08       pushl  0x805b910
 804bb2c:       e8 0b 55 00 00          call   805103c <_D3std5stdio8writeflnFYv>
 804bb31:       ff 75 08                pushl  0x8(%ebp)
 804bb34:       ff 35 14 b9 05 08       pushl  0x805b914
 804bb3a:       ff 35 10 b9 05 08       pushl  0x805b910
 804bb40:       e8 f7 54 00 00          call   805103c <_D3std5stdio8writeflnFYv>
 804bb45:       83 c4 18                add    $0x18,%esp
 804bb48:       5d                      pop    %ebp
 804bb49:       c2 04 00                ret    $0x4

ていうか私だけが知らんかったなら悲しいな。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h