0

llvm を介して OpenCL カーネルにローカル配列を作成し、サイズ [256 x i32] のルックアップテーブルと呼びます。後で、llvm を介してコードを挿入し、配列に値を入力します。私の問題は、配列にアクセスするコードを生成しようとすると、必要な要素へのポインターを正しく分離できないように見えることです。flattened と呼ばれるあいまいなローカル変数を使用すると、要素に誤ってインデックスを付けることができます。

Value *xs_ys_mul   = builder.CreateMul(shifted_x_size, y_size, "xs_ys_mul");
Value *xs_ys_z_mul = builder.CreateMul(xs_ys_mul, z, "xs_ys_z_mul");
Value *xs_y_mul    = builder.CreateMul(shifted_x_size, y, "xs_y_mul");
Value *sum_1       = builder.CreateAdd(xs_ys_z_mul, xs_y_mul, "sum_1");
Value *flattened   = builder.CreateAdd(sum_1, shifted_x, "FLATTENED");

これは、次元的に平坦化されたローカル ワークグループ ID になります。それは関係ありませんが。

GEP は次のように作成されます (ビルダーは IRBuilder のインスタンスです)。

std::vector<llvm::Value *> tmp_args;
tmp_args.push_back(builder.getInt32(0));
tmp_args.push_back(flattened);
Value *table_addr = builder.CreateGEP(M.getNamedGlobal(tablename), tmp_args, tablename+"_IDX");

この場合の M は Module オブジェクトです。生成される table_addr は次のとおりです。

%i32_cllocal_TABLE_IDX = getelementptr [256 x i32] addrspace(3)* @i32_cllocal_TABLE, i32 0, i32 %FLATTENED

ただし、LLVM のインデックスを for ループでウォークスルーして正しく入力したい場合は、私のコード (ループ構造を省略し、「インデックス」は i32 ループ カウンター):

std::vector<llvm::Value *> tmp_args;
tmp_args.push_back(builder.getInt32(0));
tmp_args.push_back(builder.getInt32(index));
Value *table_addr = builder.CreateGEP(M.getNamedGlobal(tablename), tmp_args, tablename+"_IDX");

その場合の table_addr の dump() は (index==0 の場合):

i32 addrspace(3)* getelementptr inbounds ([256 x i32] addrspace(3)* @i32_cllocal_CRC32_TABLE, i32 0, i32 0)

これは、ストアを実行するときにさらに下にあることを意味します。

store_inst = builder.CreateStore(builder.getInt32(tablevalues[index]), table_addr);

私はこの出力を得る:

store volatile i32 0, i32 addrspace(3)* getelementptr inbounds ([256 x i32] addrspace(3)* @i32_cllocal_TABLE, i32 0, i32 0), align 4

これは正しく見えませんが、さらに重要なことは、「インデックス」> 0 の場合にアサートで SIGABRT を取得することです。

Casting.h:194: typename llvm::cast_retty<To, From>::ret_type llvm::cast(const Y&) [with X = llvm::CompositeType, Y = llvm::Type*]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.

私は少し立ち往生しています。配列にインデックスを付ける明示的な値を与えることと、実行時に計算されるあいまいな値を与えることの違いがわかりません。どんな洞察も大歓迎です。

更新:私がやったことはこれです(allocは1回だけ行われます。視覚的な目的でこのコードブロックに含めているだけです。実際にはforループの外にあります):

std::vector<llvm::Value *> tmp_args;
tmp_args.push_back(builder.getInt32(0));
idxInst = builder.CreateAlloca(builder.getInt32Ty(), 0, "idxvalue");
//----- Inside the loop below --------------------------------------
idxStore = builder.CreateStore(builder.getInt32(index), idxInst);
indexValue = builder.CreateLoad(idxInst, "INDEX_VAL");
tmp_args.push_back(indexValue);
table_addr = builder.CreateGEP(table_ptr, tmp_args, "_IDX_PUT");
tmp_args.pop_back();
store_inst = builder.CreateStore(builder.getInt32(tableValues[index]), table_addr, "_ELEM_STORE");
store_inst->setAlignment(4);

このコードを発行したもの (インデックス == 0 および 1 の場合):

%idxvalue = alloca i32
store i32 0, i32* %idxvalue
%INDEX_VAL = load i32* %idxvalue
%i32_cllocal_TABLE_IDX_PUT = getelementptr [256 x i32] addrspace(3)*  @i32_cllocal_TABLE, i32 0, i32 %INDEX_VAL
store volatile i32 0, i32 addrspace(3)* %i32_cllocal_TABLE_IDX_PUT, align 4
store i32 1, i32* %idxvalue
%INDEX_VAL1 = load i32* %idxvalue
%i32_cllocal_TABLE_IDX_PUT2 = getelementptr [256 x i32] addrspace(3)* @i32_cllocal_TABLE, i32 0, i32 %INDEX_VAL1
store volatile i32 1996959894, i32 addrspace(3)* %i32_cllocal_TABLE_IDX_PUT2, align 4

今は正しいように見えますが、保存してからすぐにロードするので、コードをエミする奇妙な方法のように思えますが、最適化されると思います。または、その mem2reg を使用してみます。助けてくれてありがとう@オーク。

4

1 に答える 1