答えは簡単です。汎用アドレス空間にはハードウェア表現がありません。
汎用アドレス空間 (AS) は、他の各 AS が組み合わされた論理 AS として見ることができます。例: 次のカーネル呼び出しと、ポインターを受け入れるデバイス関数。
__device__ void bar(int* x){
*x = *x + 1;
}
__global__ void foo(int* x){
__shared__ int y[1];
bar(x);
bar(y);
}
関数には任意のポインターを渡すことができます。言語の観点からは、ポインターが AS 1 (グローバル) または AS 3 (共有) のどちらにあるかは関係ありません。C++ (および CUDA C/C++) では、AS を明示的に指定する必要はありません。たとえば、OpenCL < 2.0 では、各ポインターに修飾子を明示的に追加しbar
、特定の AS ポインターを受け取る関数を提供する必要があります。
LLVM IR で何が起こるかというと、ポインター witch が関数に渡されaddresspacecast
、汎用 AS への命令を介してキャストされます。PTXでは、次の命令addresspacecast
で表されます。cvta
// convert const, global, local, or shared address to generic address
cvta.space.size p, a; // source address in register a
cvta.space.size p, var; // get generic address of var
cvta.space.size p, var+imm; // generic address of var+offset
// convert generic address to const, global, local, or shared address
cvta.to.space.size p, a;
.space = { .const, .global, .local, .shared };
.size = { .u32, .u64 };
ジェネリック ポインターは、他の AS 用に予約されたアドレス領域内にない限り、グローバル メモリにマップされます。ハードウェアは、汎用ポインタから AS の開始アドレスを差し引いて、正しいメモリ領域を決定します。
アトミックは良い例です:
atom{.space}.op.type d, [a], b;
atom{.space}.op.type d, [a], b, c;
アドレス空間を指定するか、ハードウェアに選択させることができます。ポインター減算オーバーヘッドなしで正しいアトミック命令を生成したい場合、バックエンドはポインターを正しいアドレス空間にキャストする責任があります。