用于OpenCL本地阵列访问的LLVM-IR GEP

时间:2014-01-09 00:32:36

标签: opencl llvm llvm-ir

我通过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循环我的代码(省略循环结构,以及“index”是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”&gt;时,我会在断言上获得SIGABRT。 0:

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

发出此代码(对于index == 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。感谢@Oak的帮助。

1 个答案:

答案 0 :(得分: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显示constant expression GEP而不是真实GEP指令的原因。你需要的是创建一个Value并将其用作第二个索引 - 就像你在第一个例子中所做的那样。