LLVM中数组索引的不同方式

3
在LLVM IR中,当我想从数组中获取一个值时,似乎有三种方法可以做到这一点:使用extractvalue、使用extractelement和使用getelementptr后跟一个load。
然而,从语言参考中并不清楚应该在哪种情况下使用哪种方法。除了差异(extractvalue还可以访问结构体的成员,而extractelement无法访问,语法略有不同,GEP仅进行地址计算,而extractvalue和extractelement似乎也进行内存解引用),在每种情况下应该使用哪些指令?
例如,在以下C代码中:
int arr[2];
// do some stuff with arr
int i = arr[0];

第三行可以用IR写成:
%0 = extractvalue [2 x i32] @arr, i32 0
%0 = extractelement [2 x i32] @arr, i32 0
%0 = load i32* getelementptr inbounds ([2 x i32]* @arr, i32 0, i32 0)

如果我没记错的话,这三个IR行代码实际上是做同样的事情。 我编译成IR的另一个C程序中包含了这一行代码。
printf(" %d", a[i]);

当我使用clang编译时,相应的IR如下所示:
%25 = load i32, i32* %i14, align 4
%26 = load i32*, i32** %a, align 4
%arrayidx18 = getelementptr inbounds i32, i32* %26, i32 %25
%27 = load i32, i32* %arrayidx18, align 4
%call20 = invoke i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %27)

那么为什么在这里使用getelementptr而不是例如extractelement?其他指令在什么情况下使用?

LLVM 的版本是什么? - Frank C.
我现在正在使用3.7版本。 - Boris Mulder
2个回答

4
在LLVM 4.0文档中: extractelement 是针对向量的,返回提供索引的标量值。(如果索引越界则未定义) extractvalue 则适用于结构体和数组(聚合类型),返回按索引寻址的结构字段的值。它支持多个索引,并可用于类似于getelementptr中使用的索引访问嵌套元素的方式。 getelementptr(正如其名称所示)返回指向位置的指针,而上述其他操作返回一个值。因此需要load来获取值(写入时需要store)。
我正在编写一个发射LLVM的编译器,并使用了getelementptr来简化代码中的大部分向量和聚合访问(读取和写入)。然而,其他编译器可能会进行更深入的分析并生成特定类型的LLVM操作。

那么,使用extractvalue/insertvalue与getelementptr + load/store的原因是什么?这只是个人口味问题吗? - Boris Mulder
1
内存访问严格限制为加载和存储指令。extractvalue和extractelement只能触及虚拟寄存器,而不是内存中的内容。GEP通常跟随执行内存访问的加载/存储操作。 - esam

3

使用 LLVM IR,与 C 不同的是,数组是存储在虚拟寄存器中的值类型,因此您不能 GEP 到值数组的元素。如果您将该数组存储在内存中并具有指向该数组的指针,则可以使用 GEP,然后进行加载或存储。


好的,那么您的意思是ExtractValue实际上不执行内存解引用操作? - Boris Mulder
1
正确的ExtractElement、ExtractValue和GetElementPtr不会访问内存,只是加载和存储。 - Colin LeMahieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接