この章では、32 ビットまたは 64 ビットのコンパイル環境用のコードを作成するために必要なことについて説明します。
32 ビット、64 ビット両方のコンパイル環境で動作するコードを作成または変更する場合、次の 2 つの基本的な問題に直面します。
異なるデータ型モデル間でのデータ型の統一
異なるデータ型モデルを使用するアプリケーション間の相互動作
通常、複数のソースツリーを保守するより、#ifdef をできるだけ少なくした 1 つのソースコードを保守するほうが便利です。このため、この章では、32 ビットと 64 ビット両方のコンパイラ環境で正しく機能するコードを作成する際のガイドラインを示します。場合によっては、現在のコードを再コンパイルして、64 ビットライブラリに再リンクすればよいだけのこともあります。しかし、コードの修正が必要になる場合もあり得るため、この章では、こうした変換をより簡単に行うためのツールと参考情報について説明します。
32 ビットと 64 ビットコンパイル環境の最大の違いは、データ型モデルにあります。
32 ビットアプリケーション用の C のデータ型モデルは ILP32 モデルです。この名前は、integer、long、pointer が 32 ビットデータ型であることから名付けられています。long と pointer が 64 ビットの大きさになったことから名付けられた LP64 データ型モデルは、業界の関連企業から構成されるコンソーシアムが作成したものです。残りの C のデータ型の int、long long、short、char はどちらのデータ型モデルでも同じです。
C の整数型間の標準の関係は、次に示すようにデータ型モデルに関係なく有効です。
sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long)
ILP32 と LP64 データ型モデルの基本的な C のデータ型と対応するサイズ (単位: ビット) は、次の表に示すとおりです。
表 7–1 ILP32 と LP64 のデータ型のサイズ
C データ型 |
LP32 |
LP64 |
---|---|---|
char |
8 |
8 |
short |
16 |
16 |
int |
32 |
32 |
long |
32 |
64 |
long long |
64 |
64 |
pointer |
32 |
64 |
enum |
32 |
32 |
float |
32 |
32 |
double |
64 |
64 |
long double |
128 |
128 |
現在の 32 ビットアプリケーションでは integer、pointer、long が同じサイズであるとみなされることが多くあります。LP64 データ型モデルでは、long と pointer のサイズが変更されているため、この変更だけでも、ILP32 から LP64 への変換で多くの問題が発生する可能性があります。
また、宣言と型変換を調べることも非常に重要です。データ型が変わると、式の評価方法が影響を受ける可能性があります。データ型のサイズが変わると、標準 C 変換規則の処理が影響を受けます。意図したことを正しく示すには、定数の型を明示的に宣言してください。式で型変換を使用して、意図したとおりに式が評価されるようにすることもできます。このことは、意図したことを指示する上で明示的な型変換が欠かせない符号拡張部で特に必要になります。
この節では、32 ビットと 64 ビットの両方でコンパイル可能な単一ソースコードの作成に使用できる資源をいくつか紹介します。
32 ビットと 64 ビットのどちらのコンパイル環境でも安全なコードにするには、システム派生型を使用します。一般的に、変更の可能性がある場合には派生型を使用することをお勧めします。派生データ型を使用すると、データ型モデルの変更あるいは移植に際して、システム派生型を変更すればよいだけになります。
システムインクルードファイルの <sys/types.h> および <inttypes.h> には、32 ビットと 64 ビットのどちらにも安全なアプリケーションの作成に役立つ定数、マクロ、派生型が含まれています。
アプリケーションのソースファイルに <sys/types.h> をインクルードして、_LP64 および _ILP32 の定義を使用できるようにしてください。このヘッダーには、必要に応じて使用される基本派生型もいくつか含まれています。特に次は大切です。
clock_t クロックの刻み数でシステム時間を表します。
dev_t デバイス番号に使用されます。
off_t ファイルのサイズとオフセットに使用されます。
ptrdiff_t 2 つのポインタの減算結果用の符号付き整数型です。
size_t メモリー上のオブジェクトのサイズをバイト数で表します。
ssize_t バイト数あるいはエラー発生通知を返す関数によって使用されます。
time_t 秒数で時間をカウントします。
これらの派生型はすべて、ILP32 コンパイル環境では 32 ビット量のままですが、LP64 コンパイル環境では、64 ビット量になります。
<inttypes.h> インクルードファイルには、コンパイル環境に関係なく、明示的にサイズ指定されたデータ項目との互換性を持たせるのに役立つ定数、マクロ、派生型が含まれています。このファイルには、8、16、32、64 ビットオブジェクトを操作するための仕組みも含まれています。inttypes.h は、新しい 1999 ISO/IEC C 規格の一部であり、このファイルが 1999 ISO/IEC C 規格に含まれるように、ファイルの内容は従っています。近い将来、このファイルは更新され、1999 ISO/IEC C 規格に完全に適合する予定です。<inttypes.h> に含まれることが議論されている基本機能としては、次のものがあります。
固定幅の整数型
uintptr_t などの便利な型
定数マクロ
制限
書式文字列マクロ
次に <inttypes.h> のこれらの基本機能について詳しく説明します。
<inttypes.h> が提供する固定幅の整数型には、int8_t、int16_t、int32_t、int64_t などの符号付整数型と、uint8_t、uint16_t、uint32_t、uint64_t などの符号なし整数型があります。
指定数のビットを保持できる最小サイズの整数型として定義されている派生型としては、int_least8_t、…、int_least64_t、uint_least8_t、…、uint_least64_t などがあります。
ループカウンタやファイル記述子などの演算に int または unsigned int を使用することは問題ありません。配列インデックスに long を使用することも問題ありません。しかし、これらの固定幅型はむやみに使用しないでください。固定幅の型は、次の明示的なバイナリ表現に使用してください。
ディスク上のデータ
データ回線上のデータ
ハードウェアレジスタ
バイナリのインタフェース仕様
バイナリのデータ構造体
<inttypes.h> ファイルには、ポインタを保持するのに十分な大きさの符号付き整数型と符号なし整数型 intptr_t と uintptr_t が含まれます。また、<inttypes.h> は符号付きと符号なし整数型の中で最長 (ビット) の整数型である intmax_t と uintmax_t も提供します。
uintptr_t 型は、符号なし long などの基本型ではなく、ポインタ用の整数型として使用してください。ILP32 と LP64 コンパイル環境で符号なし long とポインタが同じサイズであるとしても、uintptr_t を使用するということは、データ型モデルが変わった場合に、その影響を受けるのは uintptr_t の定義だけになることを意味します。このため、ほかの多くのシステムにコードを移植できるようになります。また、これは、C で自分の意図していることをより明確に表現する手段になります。
intptr_t および uintptr_t 型は、アドレス演算でポインタの型変換を行うときに大変役立ちます。この目的には、long や符号なし long ではなく、intptr_t と uintptr_t 型を使用してください。
定数のサイズと符号の指定には、INT8_C(c) … INT64_C(c)、UINT8_C(c) … UINT64_C(c) マクロを使用してください。基本的に、これらのマクロは、必要に応じて定数の末尾に l、ul、ll、ull という文字列を追加します。たとえば、INT64_C(1) は、ILP32 では、定数 1 に ll、LP 64 では l を付加します。
定数を最大型にするときは、INTMAX_C(c) と UINTMAX_C(c) を使用してください。これらのマクロは、「7.3 LP64 データ型モデルへの変換」で説明している定数型を指定する際に大変役立ちます。
<inttypes.h> で定義されている上下制限は、いろいろな整数型の最小値と最大値を指示する定数です。これには、INT8_MIN … INT64_MIN、INT8_MAX … INT64_MAX などの固定幅型をそれぞれの符合なし型に対する最小値と最大値が含まれます。
<inttypes.h> ファイルには、最小サイズのそれぞれの型に対する最小値と最大値も含まれます。これには、INT_LEAST8_MIN … INT_LEAST64_MIN、INT_LEAST8_MAX … INT_LEAST64_MAX 型やこれらに対応する符号なし型があります。
また、<inttypes.h> には、サポートされる最大整数型の最小値と最大値も定義されています。これには、INTMAX_MIN、INTMAX_MAX、これらに対応する符号なし型があります。
<inttypes.h> ファイルには printf(3S) および scanf(3S) の書式指示子を指定するマクロも含まれています。基本的にこれらのマクロは、引数のビット数がマクロ名に組み込まれていることを条件に、書式指示子の前に l または ll を付加して、引数が long または long long のどちらであるかを示します。
次の例に示すように、最小および最大整数型を 10 進、8 進、符号なし、16 進の形式で表示する、printf(3S) 用のマクロがあります。
int64_t i; printf("i =%" PRIx64 "\n", i); |
同様に、最小および最大整数型を 10 進、8 進、符号なし、16 進の形式で読み取る、scanf(3S) 用のマクロがあります。
uint64_t u; scanf("%" SCNu64 "\n", &u); |
これらのマクロはむやみに使用しないでください。「固定幅の整数型」で説明しているように、固定幅型に対して使用するのがもっとも適しています。
lint プログラムの -errchk オプションは、64 ビットへの移植でエラーになる可能性のある問題を検出します。cc -v を指定して、-v を付けずにコンパイルするよりも、より厳密な意味検査を行うようコンパイラに指示することもできます。-v オプションは、指定されたファイルに対して lint に似た検査もいくつか行います。
64 ビット環境で安全なコードにするには、Solaris オペレーティングシステムに含まれているヘッダーファイルを使用してください。このヘッダーファイルには、64 ビットコンパイル環境用の派生型とデータ構造体の正しい定義が含まれています。
32 ビットおよび 64 ビットの両方のコンパイル環境用に作成したコードの検査には、lint を使用してください。LP64 の警告を生成するには、-errchk=longptr64 オプションを使用します。また、ロング整数とポインタのサイズが 64 ビットで普通の整数のサイズが 32 ビットの環境への移植性を検査する場合も -errchk=longptr64 フラグを使用してください。-errchk=longptr64 フラグは、明示的な型変換が使用されているときにも、ポインタ式とロング整数式の普通の整数への代入を検査します。
符号なし整数型の式における符号付き整数値の符号拡張を ISO C の通常の値保持規則が認めるコードを検索するには、-errchk=longptr64,signext オプションを使用してください。
Solaris の 64 ビットコンパイル環境でだけ実行するコードを検査する場合は、lint の -Xarch=v9 オプションを使用してください。x86 64 ビット環境でだけ実行するコードを検査する場合は、-Xarch=amd64 を使用します。
警告する場合、lint は問題のコードの行番号とその問題の内容や、ポインタが関連するかどうかを示すメッセージを表示します。また、関係するデータ型のサイズも示します。ポインタが関係していること、データ型のサイズがわかれば、64 ビットの問題を特定し、32 ビットとそれより小さい型の間に以前から存在している問題を避けることできます。
ただし、64 ビット環境でエラーになる可能性のある問題について警告を出すといっても、lint によってすべての問題が検出できるわけではありません。多くの場合、意図したとおりであり、正しいコードであっても、警告は出されます。
行の前に “NOTE(LINTED(“<optional message”>))” の形式のコメントを挿入すると、特定の行に対する警告を抑止できます。この機能は、lint に型変換や代入などの行を無視させる場合に役立ちます。ただし、現実には存在する問題が隠される可能性があるため、“NOTE(LINTED(“<optional message”>))” コメントを使用するときは、細心の注意を払ってください。NOTE を使用する場合、#include<note.h> のようにして note.h をインクルードしてください。詳細は、lint のマニュアルページを参照してください。
この節では実際の例を使用して、コードを変換したときに発生する可能性のある一般的な問題をいくつか紹介します。対応する lint の警告がある場合は、その警告も示します。
ILP32 コンパイル環境では整数とポインタは同じサイズであるため、コードには、この前提に立って作成されているものがあります。アドレス演算では、ポインタはしばしば int または unsigned int に型変換されます。LP64 コンパイル環境への変換では、ポインタは long に型変換してください。これは、ILP32 と LP64 データ型モデルで、long とポインタが同じサイズであるためです。明示的に unsigned long を使用するのではなく、uintptr_t を使用してください。uintptr_t の方が目的の用途により近く、コードの移植性を高めるため、将来的に変更しなくてもよいようにします。次の例を考えてみましょう。
char *p; p = (char *) ((int)p & PAGEOFFSET); % warning: conversion of pointer loses bits |
修正版は次のようになります。
char *p; p = (char *) ((uintptr_t)p & PAGEOFFSET); |
ILP32 データ型モデルでは実際には整数とロング整数が区別されないため、ほとんどの場合、既存のコードでは区別なしに整数とロング整数が使用されています。整数とロング整数が区別なしに使用されているコードは、修正して ILP32 と LP64 両方のデータ型モデルの条件に準拠するようにしてください。ILP32 データ型モデルでは整数とロング整数はともに 32 ビットですが、LP64 データ型モデルではロング整数は 64 ビットです。
次の例を考えてみましょう。
int waiting; long w_io; long w_swap; ... waiting = w_io + w_swap; % warning: assignment of 64-bit integer to 32-bit integer |
LP64 データ型モデルでは、int や unsigned int 型配列に比べて、long や unsigned long 型などのの大きな配列が原因で、重大なパフォーマンスの低下を招くことがあります。long や unsigned long 型の大きな配列はまた、キャッシュミスの大幅な増加や使用メモリーの増加の原因になることもあります。
このため、アプリケーションの用途上 long 型と同程度に int 型で問題がないならば、long 型ではなく int 型を使用する方が安全です。
ポインタ型配列の代わりに int 型配列を使用すべきという意見もあります。一部 C 言語で開発されたアプリケーションは、LP64 データ型モデルに変換すると、深刻なパフォーマンスの低下を招きます。これは、そうした C 言語で開発されたアプリケーションが多数の大きなポインタ型配列に依存しているためです。
型の変換と拡張規則はいくぶん曖昧ですから、64 ビットコンパイル環境への移行で、符号拡張はよく問題になります。符号拡張の問題を避けるには、明示的な型変換を使用して、意図した結果を得られるようにしてください。
符号拡張が発生する理由を理解するには、ISO C の変換規則の知識が役立ちます。32 ビットと 64 ビットコンパイル環境間で最大の符号拡張問題を引き起こすと思われる変換規則は、次の処理で適用されます。
整数の拡張
整数を必要とする式では、符号の有無に関係なく、char、short、enumerated type、ビットフィールドを使用することができます。
整数が元の型が取り得る値をすべて保持できる場合、値は整数に変換され、それ以外の場合は、符号なし整数に変換されます。
符号付きと符号なし整数間の変換
負符号付きの整数を同じまたは大きい型の符号なし整数に拡張する場合は、最初に大きな型符号付き整数に拡張され、次に符号なし値に変換されます。
次のコードを 64 ビットプログラムとしてコンパイルすると、addr と a.base の両方が符号なしの型であっても、addr 変数は符号拡張されます。
%cat test.c struct foo { unsigned int base:19, rehash:13; }; main(int argc, char *argv[]) { struct foo a; unsigned long addr; a.base = 0x40000; addr = a.base << 13; /* Sign extension here! */ printf("addr 0x%lx\n", addr); addr = (unsigned int)(a.base << 13); /* No sign extension here! */ printf("addr 0x%lx\n", addr); } |
ここで符号拡張が起きるのは、次のように変換規則が適用されるためです。
a.base は、整数拡張規則により符号なし int から int に変換されます。つまり、式の a.base << 13 は int 型ですが、符号拡張はまだ発生していません。
式の a.base << 13 は int 型ですが、符号付きと符号なし整数拡張規則により、addr に代入する前に long、次に符号なし long へと変換されます。符号拡張は、int から long に変換したときに発生します。
% cc -o test64 -xarch=v9 test.c % ./test64 addr 0xffffffff80000000 addr 0x80000000 % |
同じ例を 32 ビットプログラムとしてコンパイルすると、符号拡張はまったく表示されません。
cc -o test test.c %test addr 0x80000000 addr 0x80000000 |
変換規則の詳細については、ANSI/ISO C 規格の仕様書を参照してください。この規格には通常の演算変換や整数定数に関する有用な規則も規定されています。
ポインタ演算が常にデータ型モデルから独立しているのに対し、整数は独立していないことがあるため、一般的には整数を使用するより、ポインタ演算を使用する方がよいでしょう。また、通常、ポインタ演算を使用することによって、コードを簡単にすることもできます。次の例を考えてみましょう。
int *end; int *p; p = malloc(4 * NUM_ELEMENTS); end = (int *)((unsigned int)p + 4 * NUM_ELEMENTS); % warning: conversion of pointer loses bits |
修正版は次のようになります。
int *end; int *p; p = malloc(sizeof (*p) * NUM_ELEMENTS); end = p + NUM_ELEMENTS; |
アプリケーションの内部データ構造体に穴がないか検査してください。境界整列条件を満たすには、構造体のフィールドとフィールドの間にパディングをします。このパディングは、ロング整数またはポインタフィールドが LP64 データ型モデル用に 64 ビットになったときに適用します。SPARC プラットフォームの 64 ビットコンパイル環境では、あらゆる種類の構造体が、その中の最大量のサイズに合わせて整列されます。構造体を整列し直すときは、ロング整数およびポインタフィールドを構造体の先頭に移動するという簡単な規則に従ってください。次の例を考えてみましょう。
struct bar { int i; long j; int k; char *p; }; /* sizeof (struct bar) = 32 */ |
次は、同じ構造体の例です。ロング整数およびポインタデータ型を構造体の先頭で定義しています。
struct bar { char *p; long j; int i; int k; }; /* sizeof (struct bar) = 24 */ |
ILP32 と LP64 データ型モデルの間では、共用体のフィールドのサイズが変わる可能性があるため、共用体は必ず検査してください。
typedef union { double _d; long _l[2]; } llx_t; |
修正版は次のようになります。
typedef union { double _d; int _l[2]; } llx_t; |
精度が足りないと、一部の定数式でデータが失われることがあります。定数式でデータ型を指定するときは明示的に行なってください。u、U、l、L のいくつかを組み合わせて、すべての整定数の型を指定してください。型変換を使用して、定数式の型を指定することもできます。次の例を考えてみましょう。
int i = 32; long j = 1 << i; /* j will get 0 because RHS is integer */ /* expression */ |
修正版は次のようになります。
int i = 32; long j = 1L << i; |
-xc99=none を使用する場合、C コンパイラは、モジュールで使用されていて、外部定義または宣言されてない関数や変数をすべて整数とみなします。このようにして使用されるロング整数やポインタは、コンパイラの暗黙の整数宣言によって切り捨てられます。この問題を避けるには、C モジュールではなく、ヘッダーに関数または変数に対する適切な extern 宣言を挿入してください。そして、その関数または変数を使用する C モジュールにヘッダーをインクルードしてください。システムヘッダーによって定義されている関数あるいは変数であっても、コードに正しいヘッダーをインクルードする必要があります。次の例を考えてみましょう。
int main(int argc, char *argv[]) { char *name = getlogin(); printf("login = %s\n", name); return (0); } % warning: improper pointer/integer combination: op "=" warning: cast to pointer from 32-bit integer implicitly declared to return int getlogin printf |
次の修正版には正しいヘッダーが含まれています。
#include <unistd.h> #include <stdio.h> int main(int argc, char *argv[]) { char *name = getlogin(); (void) printf("login = %s\n", name); return (0); } |
LP64 データ型モデルでは、sizeof() の有効な型は unsigned long です。ときには sizeof() は、int 型の引数を待つ関数に渡されたり、整数に代入あるいは型変換されたりします。そうした場合は、切り捨てによってデータが失われることがあります。
long a[50]; unsigned char size = sizeof (a); % warning: 64-bit constant truncated to 8 bits by assignment warning: initializer does not fit or is out of range: 0x190 |
変換規則により、関係式は扱いにくいことがあります。必要に応じて型変換を追加することによって、式の評価方法を明示するようにしてください。
printf(3S)、sprintf(3S)、scanf(3S)、sscanf(3S) に対する書式文字列が long あるいは pointer 引数を受け付けられるようになっていることを確認してください。pointer 引数については、書式文字列中の変換操作を %p で指定して、32 ビットおよび 64 ビット両方のコンパイル環境で機能するようにします。
char *buf; struct dev_info *devi; ... (void) sprintf(buf, "di%x", (void *)devi); % warning: function argument (number) type inconsistent with format sprintf (arg 3) void *: (format) int |
修正版は次のようになります。
char *buf; struct dev_info *devi; ... (void) sprintf(buf, ”di%p", (void *)devi); |
long 引数については、書式文字列中の変換操作文字の前に long サイズ指定の l を付加します。また、buf の指し示す記憶場所が 16 桁を保持できる大きさであるか確認してください。
size_t nbytes; u_long align, addr, raddr, alloc; printf("kalloca:%d%%%d from heap got%x.%x returns%x\n", nbytes, align, (int)raddr, (int)(raddr + alloc), (int)addr); % warning: cast of 64-bit integer to 32-bit integer warning: cast of 64-bit integer to 32-bit integer warning: cast of 64-bit integer to 32-bit integer |
修正版は次のようになります。
size_t nbytes; u_long align, addr, raddr, alloc; printf("kalloca:%lu%%%lu from heap got%lx.%lx returns%lx\n", nbytes, align, raddr, raddr + alloc, addr); |
この節では、アプリケーションを完全な 64 ビットプログラムに変換するときに発生する問題を取り上げます。
いくつかの派生型が変更されており、64 ビットコンパイル環境で 64 ビット量を表すようになっています。32 ビットアプリケーションがこの変更の影響を受けることはありませんが、これらの型で表されるデータを消費またはエクスポートする 64 ビットアプリケーションは、評価し直す必要があります。たとえば utmp(4) あるいは utmpx(4) ファイルを直接操作するアプリケーションがこれにあたります。64 ビットアプリケーション環境で正しく動作させるには、utmp または utmpx ファイルに直接にアクセスしないようにしてください。代わりに、getutxent(3C) および関連する系列の関数を使用します。
ある場所で型を変更したために、別のコード部分で予想外の 64 ビット変換が発生することがあります。たとえば、それまで int を返していて、現在は ssize_t を返す ようになった関数のすべての呼び出し元を検査してください。
long と定義された変数は、ILP32 データ型モデルでは 32 ビット、LP64 データ型モデルでは 64 ビットです。可能な場合は、こうした変数を定義し直し、移植性に優れた派生型を使用することによって問題の発生を回避してください。
これに関連して、LP64 データ型モデルでは、いくつかの派生型が変更されています。たとえば、pid_t は 32 ビット環境では long のままですが、64 ビット環境では int になります。
場合によっては、32 ビットや 64 ビット専用のインタフェースを使用しなければならないことがあります。そうしたインタフェースには、ヘッダー中で _LP64 または _ILP32 の機能テストマクロを指定して区別できます。同様に、32 ビットまた 64 ビット環境で動作するコードでは、コンパイルモードに従って適切な #ifdefs を使用する必要があります。
構造体を値によって渡し、64 ビット環境用にコードをコンパイルした場合、その構造体は、コピーへのポインタとしてではなく、レジスタ中で渡されます (構造体がそのようにできるほどの大きさの場合)。その場合、C コードと手書きのアセンブリコード間で構造体を渡そうとすると、問題が起きることがあります。
浮動小数点パラメータも同様に機能します。値で渡される浮動小数点値は浮動小数点レジスタ中で渡されます。
64 ビット環境で安全なコードを作成したら、コードを見直して、アルゴリズムとデータ構造体が正しく機能することを確認してください。データ構造体のデータ型が大きいほど、使用する空間が増えることがあります。コードのパフォーマンスも影響を受けるかもしれません。こうしたことに注意し、必要に応じてコードを修正してください。
コードを 64 ビットに変換するにあたっては次の事項を確認してください。
すべてのデータ構造体とインタフェースを見直して、64 ビット環境でも問題がないことを確認します。
コードに <inttypes.h> をインクルードして、多数の基本派生方とともに _ILP32 または _LP64 の定義を取り込みます。システムプログラムは _ILP32 または _LP64 の定義を取得するために <sys/types.h> (または少なくとも <sys/isa_defs.h>) をインクルードする可能性があります。
スコープが局所ではない関数プロトタイプと外部宣言はヘッダーに移動し、コード中にヘッダーをインクルードします。
lint は、-errchk=longptr64 および signext オプションを付けて実行します。また、SPARC アーキテクチャー向けのアプリケーションには -D__sparcv9、x86 アーキテクチャー向けのアプリケーションには -Xarch=amd64 も指定します。1 つ 1 つすべての警告に目を通してください。必ずしもすべての警告について、コードの変更が必要になるわけではありません。変更によっては、32 ビットと 64 ビットモードの両方で lint を再度実行してください。
アプリケーションの 64 ビット版だけ提供するのでないかぎり、32 ビットと 64 ビットの両方でコードをコンパイルしてください。
アプリケーションのテストは、32 ビット版は 32 ビットオペレーティングシステム上で、64 ビット版は 64 ビットオペレーティングシステム上で行なってください。32 ビット版は、64 ビットオペレーティングシステム上でテストすることもできます。