码迷,mamicode.com
首页 > 其他好文 > 详细

编译器架构的王者LLVM——(11)深入理解GetElementPtr

时间:2015-12-28 09:11:35      阅读:209      评论:0      收藏:0      [点我收藏+]

标签:

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

编译器架构的王者LLVM——(11)深入理解GetElementPtr

标签:

原文地址:http://blog.csdn.net/xfxyy_sxfancy/article/details/50413808

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!