32bitコードの64bitへの移植
D は 32bit と 64bit の間のコード移植性を容易に保てるように設計されていますが、 システムプログラミング言語であるという性質上、 どうしても、依存性が忍び込んでしまうことがあります。 ここでは、32bit と 64bit での違いについて説明します。
version
全てのコードが32bit/64bit互換で書けるとは限らず、 versionによる切り分けが必要です。以下のように記述できます:
version (X86)
... 32 bit code ...
else version (X86_64)
... 64 bit code ...
else
static assert("unsupported target")
未知のターゲット環境に対しては、 バージョン切り分けの結果はコンパイルエラーとするべきです。 例えば、64bit ARM 環境がどちらで動くかなどを前もって推測して設定するようなことは避けた方が良いでしょう。 経験として、未知のプラットフォームの挙動を予測のみで記述するのは、 常に誤動作の元です。 そのような決定は、実際に64bit ARMで動かす機会ができるまで取っておきましょう。
サイズの変化
ポインタと参照型のサイズは4バイトから8バイトに増えます。 size_t は uint への alias から ulong に変わり、ptrdiff_t は int への alias から long に変わります。
複合型のサイズも、ベースとなる型のサイズ増加に従って変化します。 これは、 動的配列、連想配列、delegate、クラス参照が当てはまります。
構造体
構造体のサイズとフィールドの alignment は、 Cの構造体のABIに合わせて変化します。
クラス
クラスのサイズとフィールドの alignment は、 64bitモードでもっとも最適になるように、 また、 ポインタサイズ等の増加を反映して変化します。
printf
printf は C の関数であるため、C の型規則に従います。 これを D から使うには、いくつか考慮すべき事があります。
D の型 | printf 書式 | |
---|---|---|
32 bit | 64 bit | |
T* | %p | %p |
long | %lld | %d |
ulong | %llu | %u |
ptrdiff_t | %td | %td |
size_t | %zu | %zu |
32bitコードでは、%.*s という書式でDの文字列を出力するコードがよく使われていました。これは 32bit の C ABI が、 動的配列を長さとポインタ引数の列として扱うことに依存しており、 64bit の引数渡しはこれとは異なります。 したがって、長さとポインタは明示的に引き渡す必要があります:
string s;
...
printf("s = '%.*s'\n", s); // 32 bit 限定
printf("s = '%.*s'\n", s.length, s.ptr); // 32/64 bit 共通
インラインアセンブリ
当然ながら、32bit用のインラインアセンブリは64bitでは動作しません。 バージョン切り分けは以下のように記述します:
version (D_InlineAsm_X86)
... 32 bit assembler ...
version (D_InlineAsm_X86_64)
... 64 bit assembler ...
else
static assert("unsupported target");
64bit のインラインアセンブラの構文は、 Intel と AMD の命令セット仕様書のものに従います。
可変長引数
64bit環境での可変長引数の動作は、32bitのそれとは大幅に異なります。 64bitでは単純なスタック上の配列とはなりません。 アクセスには、必ず std.c.stdarg の関数とテンプレートを使う必要があります。