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 的创建方式(builder 是 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

但是,如果我想通过使用 for 循环遍历 LLVM 中的索引来正确填充它(省略循环结构,其中“索引”是 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

这看起来不正确,但更重要的是,当“index”> 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 只完成一次,我只是为了视觉目的将它包含在这个代码块中,它实际上是在 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

现在看起来是正确的,对我来说这似乎是一种奇怪的 emi 代码方式,因为我正在存储然后立即加载,但我想这会得到优化,或者我会尝试使用那个 mem2reg。感谢@Oak 的帮助。

4

1 回答 1

2

这个片段是有问题的:

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");

IRBuilder::getInt32创建一个常量int。因此 GEP 将始终访问数组中的第一项,这不是您想要的。这也是 IR 显示常量表达式 GEP而不是真正的 GEP 指令的原因。您需要创建一个Value并将其用作第二个索引 - 就像您在第一个示例中所做的那样。

于 2014-01-09T06:23:25.983 回答