LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 —— 题记
版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 http://blog.csdn.net/xfxyy_sxfancy
深入理解GetElementPtr
LLVM平台,和C语言极为类似,强类型,需要复杂的指针操作,基于系统的符号调用等。而LLVM的指针操作指令,GetElementPtr,几乎是所有指针计算的关键,而理解它个运作原理,正确的使用,非常的重要。
强类型的LLVM
编写LLVM需要时刻记住,LLVM是强类型的,每一条语句,都有确定的类型,GetElementPtr也正是这样,不同的参数,会有不同类型的返回类型。
我们先来看一段LLVM官网上的示例:
struct munger_struct {
int f1;
int f2;
};
void munge(struct munger_struct *P) {
P[0].f1 = P[1].f1 + P[2].f2;
}
...
munger_struct Array[3];
...
munge(Array);
我们用Clang以C格式编译这段代码,munge
函数会编译成如下IR:
void %munge(%struct.munger_struct* %P) {
entry:
%tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0
%tmp = load i32* %tmp
%tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1
%tmp7 = load i32* %tmp6
%tmp8 = add i32 %tmp7, %tmp
%tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0
store i32 %tmp8, i32* %tmp9
ret void
}
我们仔细来观察一下,每一条指令,都有明确的指明 P
指针的类型为 %struct.munger_struct*
,
而下面的load语句,也间接说明了返回类型为 i32*
我们在正确理解GetElementPtr的工作方式时,必须时刻了解对应的类型,这样才不会偏差。
GetElementPtr的指令规则
GetElementPtr指令其实是一条指针计算语句,本身并不进行任何数据的访问或修改,进行是计算指针,修改计算后指针的类型。
GetElementPtr至少有两个参数,第一个参数为要进行计算的原始指针,往往是一个结构体指针,或数组首地址指针。
第二个参数及以后的参数,都称为indices
,表示要进行计算的参数,如结构体的第几个元素,数组的第几个元素。
下面我们结合示例,来对应看一下是如何工作的:
P[0].f1
这是示例代码中的被赋值指针,我们C语言的经验告诉我们,首先P[0]
的地址就是数组的首地址,而f1
又是结构体的第一个参数,那么P的地址就是我们最终要放置数据的结构地址。
这条地址计算对应如下语句:
%tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0
我们发现参数是两个0,这两个0含义不大一样,第一个0是数组计算符,并不会改变返回的类型,因为,我们任何一个指针都可以作为一个数组来使用,进行对应的指针计算,所以这个0并不会省略。
第二个0是结构体的计算地址,表示的是结构体的第0个元素的地址,这时,会根据结构体指针的类型,选取其中的元素长度,进行计算,最后返回的则是结构体成员的指针。
同理,我们可以对照参考这两条语句:
P[1].f1
P[2].f2
对应的计算翻译后为:
%tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0
%tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1
注意事项
首先,不是全部的indices
都必须是i32,也可以是i64,但结构体的计算地址,也就是上面例子中的第二个数字,必须是i32
GEP x,1,0,0 和 GEP x,1 计算后的地址是一样的,但类型不一样,所以千万注意不要在语句后添加多余的0。
其他情况
仅有数组计算
如果仅有数组指针计算,那么就简单了许多,数组指针的移动只需要一个参数即可。
但如果是仅有结构体指针,那么还是必须两个参数才行
多维数组
个人觉得LLVM的数组定义很难写,推荐自己用一维数组代替,比较计算也不复杂。这样高维数组统一化成一维后,都成了基本的指针计算,就非常简单了。
连续选取
GetElementPtr基本上可以认为是不限参数长度的,可以连续选取,于是我们可以实现:
A->B->C
这类连续指向的计算。
但个人不推荐这样做,尤其是语法驱动的编译时,也很难做到这点,建议分开一条一条的语句进行执行,分别选取。
参考
http://llvm.org/docs/GetElementPtr.html
最近研究的LLVM技术,大部分应用于在进行的ELite编译器开发,欢迎朋友们关注和参与。
https://github.com/elite-lang/Elite