LLVM平臺,短短幾年間,改變了眾多編程語言的走向,也催生了一大批具有特色的編程語言的出現,不愧為編譯器架構的王者,也榮獲2012年ACM軟件系統獎 —— 題記
版權聲明:本文為 西風逍遙游 原創文章,轉載請注明出處 西風世界 [http://blog.csdn.net/xfxyy_sxfancy](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](http://llvm.org/docs/GetElementPtr.html)
最近研究的LLVM技術,大部分應用于在進行的ELite編譯器開發,歡迎朋友們關注和參與。
[https://github.com/elite-lang/Elite](https://github.com/elite-lang/Elite)