LLVM语法语义指令特性
LLVM語法語義指令特性
High Level Structure
Module Structure
LLVM 程序由Module’s組成,每個 's 是輸入程序的一個翻譯單元。每個模塊由函數,全局變量和符號表條目組成。模塊可與 LLVM 鏈接器組合在一起,后者合并函數(全局變量)定義,解析前向聲明,合并符號表條目。這是“hello world”模塊的示例:
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8*…
%cast210 = getelementptr [13 x i8], [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!0 = !{i32 42, null, !“string”}
!foo = !{!0}
這個示例是由高達全局變量命名為“ .str”,在一個外部聲明“ puts”函數, 函數定義為“ main”, 命名為元數據“ foo”。
通常,模塊由全局值列表組成(其中函數和全局變量都是全局值)。全局值由指向內存位置的指針(在這種情況下,指向字符數組的指針和指向函數的指針)表示。
命名元數據
命名元數據是元數據的集合。元數據節點(但不是元數據字符串)是命名元數據的唯一有效操作數。
- 命名元數據表示為帶有元數據前綴的字符串。元數據名稱的規則與標識符相同,但不允許引用名稱。"\xx"類型轉義仍然有效,允許任何字符成為名稱的一部分。
句法:
; Some unnamed metadata nodes, which are referenced by the named metadata.
!0 = !{!“zero”}
!1 = !{!“one”}
!2 = !{!“two”}
; A named metadata.
!name = !{!0, !1, !2}
參數屬性
函數類型的返回類型和每個參數,可能有一組與之關聯的參數屬性。參數屬性用于傳達有關函數結果,或參數的附加信息。參數屬性被認為是函數的一部分,不是函數類型,具有不同參數屬性的函數,可具有相同的函數類型。
參數屬性是遵循指定類型的簡單關鍵字。如果需要多個參數屬性,空格分隔。例如:
declare i32 @printf(i8* noalias nocapture, …)
declare i32 @atoi(i8 zeroext)
declare signext i8 @returns_signed_char()
前綴數據
前綴數據是與函數關聯的數據,代碼生成器將在函數入口點之前立即發出數據。允許前端將特定于語言的運行時元數據與特定函數相關聯,通過函數指針可用,允許調用函數指針。
要訪問給定函數的數據,程序可將函數指針位轉換為指向常量類型的指針,取消引用索引 -1。這意味著 IR 符號剛好越過前綴數據的末尾。例如,一個用單i32個,注釋的函數為例
define void @f() prefix i32 123 { … }
前綴數據可引用
%0 = bitcast void* () @f to i32*
%a = getelementptr inbounds i32, i32* %0, i32 -1
%b = load i32, i32* %a
define void @f() prologue i8 144 { … }
序言數據
prologue屬性允許在函數體之前,插入任意代碼(編碼為字節)。用于啟用功能熱修補和檢測。
為了維護普通函數調用的語義,序言數據必須具有特定的格式。具體來說,必須以一個字節序列開始,這些字節序列解碼為一系列機器指令,對模塊的目標有效,這些指令將控制轉移到序言數據之后的點,不執行任何可見的操作。允許內聯和通道推理函數定義的語義,無需推理序言數據。顯然,這使得序言數據的格式高度依賴于目標。
x86 架構的有效序言數據的一個簡單示例,對指令進行編碼:i8 144nop
define void @f() prologue i8 144 { … }
通常可通過編碼,跳過元數據的相對分支指令,形成序言數據,如x86_64架構的有效序言數據示例,前兩個字節編碼:jmp .+10
%0 = type <{ i8, i8, i8* }>
define void @f() prologue %0 <{ i8 235, i8 8, i8* @md}> { … }
; Target-independent attributes:
attributes #0 = { alwaysinline alignstack=4 }
; Target-dependent attributes:
attributes #1 = { “no-sse” }
; Function @f has attributes: alwaysinline, alignstack=4, and “no-sse”.
define void @f() #0 #1 { … }
屬性組
屬性組是由 IR 內的對象引用的屬性組。對于保持.ll文件可讀很重要,因為許多函數將使用相同的屬性集。在.ll文件對應于單個.c文件的退化情況下 ,單個屬性組將捕獲用于構建該文件的重要命令行標志。
屬性組是模塊級對象。要使用屬性組,對象引用屬性組的 ID(例如#37)。一個對象可能引用多個屬性組。在這種情況下,來自不同組的屬性將合并。
下面是一個函數的屬性組示例,該函數應該始終內聯,堆棧對齊為 4,不應使用 SSE 指令:
define void @f() noinline { … }
define void @f() alwaysinline { … }
define void @f() alwaysinline optsize { … }
define void @f() optsize { … }
去優化操作數包
去優化操作數包的特點是"deopt" 操作數包標簽。這些操作數包代表了所連接的調用站點的替代“安全”延續,可由合適的運行時使用,取消優化指定調用站點的編譯幀。最多可有一個"deopt"操作數束,附加到調用站點。去優化的確切細節超出了語言參考的范圍,通常涉及將編譯幀重寫為一組解釋幀。
從編譯器的角度來看,去優化操作數包,所連接的調用點,至少連接到readonly。通讀了所有指針類型的操作數(即使沒有以其它方式轉義)和整個可見堆。去優化操作數包不捕獲操作數,除非在去優化期間,在這種情況下控制,不會返回到編譯幀。
內聯器知道如何通過具有去優化操作數包的調用進行內聯。就像通過普通調用站點內聯涉及組合正常和異常延續一樣,通過具有去優化操作數包的調用站點,內聯需要適當地組合“安全”去優化延續。內聯程序通過將父級的去優化延續,添加到內聯正文中的每個去優化延續之前,做到這一點。例如內聯@f到@g在下面的示例中
define void @f() {
call void @x() ;; no deopt state
call void @y() [ “deopt”(i32 10) ]
call void @y() [ “deopt”(i32 10), “unknown”(i8* null) ]
ret void
}
define void @g() {
call void @f() [ “deopt”(i32 20) ]
ret void
}
導致
define void @g() {
call void @x() ;; still no deopt state
call void @y() [ “deopt”(i32 20, i32 10) ]
call void @y() [ “deopt”(i32 20, i32 10), “unknown”(i8* null) ]
ret void
}
在每個需要 a 的規范上:,指定 對齊方式是可選的。如果省略,前面的應該省略,并且等于。
在為給定目標構建數據布局時,LLVM 從一組默認規范開始,然后(可能)被datalayout關鍵字中的規范覆蓋。此列表中給出了默認規格:
? E - big endian
? p:64:64:64 - 64-bit pointers with 64-bit alignment.
? p[n]:64:64:64 - Other address spaces are assumed to be the same as the default address space.
? S0 - natural stack alignment is unspecified
? i1:8:8 - i1 is 8-bit (byte) aligned
? i8:8:8 - i8 is 8-bit (byte) aligned
? i16:16:16 - i16 is 16-bit aligned
? i32:32:32 - i32 is 32-bit aligned
? i64:32:64 - i64 has ABI alignment of 32-bits but preferred alignment of 64-bits
? f16:16:16 - half is 16-bit aligned
? f32:32:32 - float is 32-bit aligned
? f64:64:64 - double is 64-bit aligned
? f128:128:128 - quad is 128-bit aligned
? v64:64:64 - 64-bit vector is 64-bit aligned
? v128:128:128 - 128-bit vector is 128-bit aligned
? a:0:64 - aggregates are 64-bit aligned
使用列表order指令
使用列表指令,對每個使用列表的內存順序進行編碼,允許重新創建順序。是分配給引用值的使用的索引的逗號分隔列表。引用值的使用列表,立即按這些索引排序。
使用列表指令,可能出現在函數作用域,或全局作用域。不是指令,對 IR 的語義沒有影響。當處于函數作用域時,必須出現在最終基本塊的終止符之后。
如果基本塊的地址,通過blockaddress()表達式獲取, uselistorder_bb則可用于從其函數范圍之外重新排序使用列表。
Syntax:
uselistorder , { }
uselistorder_bb @function, %block { }
Examples:
define void @foo(i32 %arg1, i32 %arg2) {
entry:
; … instructions …
bb:
; … instructions …
; At function scope.
uselistorder i32 %arg1, { 1, 0, 2 }
uselistorder label %bb, { 1, 0 }
}
; At global scope.
uselistorder i32* @global, { 1, 2, 0 }
uselistorder i32 7, { 1, 0 }
uselistorder i32 (i32) @bar, { 1, 0 }
uselistorder_bb @foo, %bb, { 5, 1, 3, 2, 0, 4 }
函數類型
概述:
函數類型可被認為是一個函數簽名。由一個返回類型和一個形參類型列表組成。函數類型的返回類型是 void 類型,或第一類類型——標簽和元數據類型除外。
句法:
()
…其中 ’ ’ 是逗號分隔的類型說明符列表。參數列表可包括一個 type ,表明該函數采用可變數量的參數。可變參數函數可使用可變參數處理內部函數訪問參數。’ ’ 是除標簽和元數據之外的任何類型。…
示例
i32 (i32) 函數取一個i32,返回一個i32
float (i16, i32 *) * 指針,以接受一個函數i16和一個指針 i32,返回float。
i32 (i8*, …) 可變參數函數,至少一個指針到i8(在C炭),返回一個整數。這是printfLLVM 中的簽名。
{i32, i32} (i32) 一個函數采用i32,返回一個包含兩個值的結構i32
整數將占用的位數由N 值指定。
例示例
i1 一位整數。
i32 一個 32 位整數。
i1942652 一個超過 100 萬位的非常大的整數。
浮點類型
類型 描述
half 16 位浮點值
float 32 位浮點值
double 64 位浮點值
fp128 128 位浮點值(112 位尾數)
x86_fp80 80 位浮點值 (X87)
ppc_fp128 128 位浮點值(兩個 64 位)
指針類型
概述:
指針類型用于指定內存位置。指針通常用于引用內存中的對象。
指針類型可能有一個可選的地址空間屬性,用于定義指向對象所在的編號地址空間。默認地址空間是數字零。非零地址空間的語義是特定于目標的。
請注意,LLVM 不允許指向 void ( void*) 的指針,不允許指向標簽 ( label*) 的指針。使用i8來代替。
句法:
<類型> *
示例
[4 x i32] 指針到陣列的4個i32值。
i32 (i32*) * 一個指向函數的指針,接受一個i32*,返回一個i32。
i32 addrspace(5)* 指向i32位于地址空間#5值的指針。
矢量類型?
概述:
向量類型是表示元素向量的簡單派生類型。當使用單個指令 (SIMD) ,并行操作多個原始數據時,將使用向量類型。向量類型需要大小(元素數量)和底層原始數據類型。向量類型被認為是一類的。
< <# elements> x >
元素個數是一個大于0的常量整數值;elementtype 可是任何整數,浮點數或指針類型。不允許大小為零的向量。
示例:
<4 x i32> 4 個 32 位整數值的向量。
<8 x float> 8 個 32 位浮點值的向量。
<2 x i64> 2 個 64 位整數值的向量。
<4 x i64*> 4 個指向 64 位整數值的指針的向量。
數數組類型
概述:
數組類型是一種非常簡單的派生類型,在內存中按順序排列元素。數組類型需要大小(元素數)和基礎數據類型。
句法:
[<# elements> x ]
元素的數量是一個常量整數值;elementtype可是具有大小的任何類型。
示例:
[40 x i32] 40 個 32 位整數值的數組。
[41 x i32] 41 個 32 位整數值的數組。
[4 x i8] 4 個 8 位整數值的數組。
多維數組的一些示例:
[3 x [4 x i32]] 32 位整數值的 3x4 數組。
[12 x [10 x float]] 12x10 單精度浮點值數組。
[2 x [3 x [4 x i16]]] 2x3x4 16 位整數值數組。
對超出靜態類型所隱含的數組末尾的索引,沒有限制(盡管在某些情況下對超出已分配對象范圍的索引有限制)。在具有零長度數組類型的 LLVM 中,實現一維“可變大小數組”尋址。例如,LLVM 中“pascal 樣式數組”的實現,可使用類型“ ”。{ i32, [0 x float]}
結構類型
概述:
結構類型用于表示內存中數據成員的集合。結構的元素可是具有大小的任何類型。
通過使用“ ”指令,獲取指向字段的指針,可使用“ load”和“ store”訪問內存中的結構getelementptr。使用“ extractvalue”和“ insertvalue”指令,訪問寄存器中的結構。
結構可選擇是“打包”結構,這表明結構的對齊是一個字節,元素之間沒有填充。在非壓縮結構中,字段類型之間的填充,按照模塊中 DataLayout 字符串的定義插入,這是匹配底層代碼生成器所期望的。
結構可是“文字”或“已知”。文字結構是與其它類型(例如)內聯定義的,標識的類型總是在頂層定義一個名稱。文字類型因內容而唯一,永遠不會是遞歸或不透明的,無法編寫。已知類型可是遞歸的,可是不透明的,永遠不會是唯一的。{i32, i32}*
句法:
% T1 = type { <類型 列表> } ; 已知的 正常 結構體 類型
% T2 = type < { <類型 列表> } > ; 已知的 壓縮 結構 類型
示例:
{ i32, i32, i32 } 三個i32值的三元組
{ float, i32 (i32) * } 第一個元素是 float,第二個元素是指向一個函數的指針,該函數接受一個i32,返回一個i32。
<{ i8, i32 }> 已知大小為 5 個字節的打包結構。
不透明結構類型
概述:
不透明結構類型,用于表示沒有指定主體的命名結構類型。對應于(例如)前向聲明結構的 C 概念。
句法:
% X = 類型 不透明
% 52 = 類型 不透明
示例:
opaque 不透明類型。
全局變量和函數地址
全局變量和 函數的地址總是隱式有效(鏈接時)常量。這些常量在使用全局標識符時顯式引用,始終具有 指針類型。例如,以下是一個合法的 LLVM 文件:
@X = global i32 17
@Y = global i32 42
@Z = global [2 x i32*] [ i32* @X, i32* @Y ]
未定義值
字符串 ’ undef’ 可用于任何需要常量的地方,指示該值的用戶,可能會收到未指定的位模式。未定義的值可是任何類型(除了“ label”或“ void”),可在任何允許常量的地方使用。
未定義值很有用,向編譯器表明無論使用什么值,程序都是定義良好的。這給了編譯器更多的優化自由。以下是一些有效(在偽 IR 中)轉換的示例:
%A = add %X, undef
%B = sub %X, undef
%C = xor %X, undef
Safe:
%A = undef
%B = undef
%C = undef
這是安全的,因為所有輸出位都受 undef 位影響。任何輸出位都可有0,,1,具體取決于輸入位。
%A = or %X, undef
%B = and %X, undef
Safe:
%A = -1
%B = 0
Safe:
%A = %X ;; By choosing undef as 0
%B = %X ;; By choosing undef as -1
Unsafe:
%A = undef
%B = undef
這些邏輯運算的位,并不總是受輸入影響。例如,如果%X有一個零位,那么and對于該位’ ’ 操作的輸出,將始終為零,無論 ’ undef’的相應位是什么。因此,優化或假設“ and”的結果 “ undef”是不安全的。但是,可安全地假設 ’ undef’ 的所有位,都可為 0,將 ’ and’優化為 0。同樣,可安全地假設 ’ undef’ 操作數的所有位or,都可設置,允許將“ or”設置為 -1。
%A = select undef, %X, %Y
%B = select undef, 42, %Y
%C = select %X, %Y, undef
Safe:
%A = %X (or %Y)
%B = 42 (or %Y)
%C = %Y
Unsafe:
%A = undef
%B = undef
%C = undef這組示例表明,未定義的“ select”(和條件分支)條件,可采用任何一種方式,必須來自兩個操作數之一。在%A示例中,如果%X和%Y是兩個已知具有明顯的低位,那么%A就必須有一個清除低位。但是,在%C示例中,優化器可假設“ undef”操作數,可與 %Y相同,從而select可消除整個“ ”。
%A = xor undef, undef
%B = undef
%C = xor %B, %B
%D = undef
%E = icmp slt %D, 4
%F = icmp gte %D, 4
Safe:
%A = undef
%B = undef
%C = undef
%D = undef
%E = undef
%F = undef
此示例指出兩個“ undef”操作數不一定相同。可能會讓人們感到驚訝(并且也匹配 C 語義),假設“ X^X”始終為零,即使 X是未定義的。出于多種原因,情況并非如此,但簡短的回答是undef“變量”,可在 “有效范圍”內,任意更改值。因為變量實際上沒有有效范圍。相反,該值是在需要時,從恰好在附近的任意寄存器邏輯讀取的,因此該值不一定隨時間保持一致。事實上,%A與 %C需要具有相同的語義,或核心LLVM概念,不會執行“替換所有用途”。
%A = fdiv undef, %X
%B = fdiv %X, undef
Safe:
%A = undef
b: unreachable
這些示例顯示了未定義值 和未定義行為之間的關鍵區別。undef允許未定義的值(如“ ”)具有任意位模式。這意味著%A 操作可常量折疊為“ undef”,因為“ undef”可能是 SNaN, fdiv(當前)未在 SNaN 上定義。在第二個示例中,可做出更激進的假設:因為undef允許是任意值,可假設可能為零。由于除以零具有未定義的行為,可假設該操作根本不執行。刪除除法和之后的所有代碼。因為未定義的操作“不可能發生”,優化器可假設發生在死代碼中。
a: store undef -> %X
b: store %X -> undef
Safe:
a:
b: unreachable
危險值
危險值類似于undef 值,代表了這樣一個事實,即不能引起副作用的指令,或常量表達式,仍然檢測到導致未定義行為的條件。
目前沒有辦法在 IR 中表示危險值;當通過操作,如產生只存在附加與nsw標記。
危險值行為是根據值依賴定義的:
? phi節點以外的值取決于操作數。
? Phi節點依賴于與動態前驅基本塊對應的操作數。
? 函數參數取決于函數的動態調用者中相應的實際參數值。
? 調用指令依賴于 動態地將控制返回給ret指令。
? Invoke指令依賴于 ret, resume,或異常拋出調用指令,動態地將控制權轉移回。
? 非易失性加載和存儲,依賴于所有引用內存地址的最新存儲,遵循 IR 中的順序(包括內部函數隱含的加載和存儲,例如 @llvm.memcpy。)
? 具有外部可見副作用的指令,取決于最近的具有外部可見副作用的指令,遵循 IR 中的順序。(包括易失性操作。)
? 如果終止指令有多個后繼指令,當控制轉移到其中一個后繼指令時,總是執行該指令,當控制轉移到另一個后繼指令時,可能不執行該指令,指令控制依賴于終止指令。
? 此外,如果終止符已將控制權轉移到不同的后繼指令,該指令還控制依賴于終止符指令,否則該指令所依賴的指令集將不同。
? 依賴是可傳遞的。
危險值與undef值具有相同的行為,附加效果是任何依賴 危險值的指令,都具有未定義的行為。
示例:
entry:
%poison = sub nuw i32 0, 1 ; Results in a poison value.
%still_poison = and i32 %poison, 0 ; 0, but also poison.
%poison_yet_again = getelementptr i32, i32* @h, i32 %still_poison
store i32 0, i32* %poison_yet_again ; memory at @h[0] is poisoned
store i32 %poison, i32* @g ; Poison value stored to memory.
%poison2 = load i32, i32* @g ; Poison value loaded back from memory.
store volatile i32 %poison, i32* @g ; External observation; undefined behavior.
%narrowaddr = bitcast i32* @g to i16*
%wideaddr = bitcast i32* @g to i64*
%poison3 = load i16, i16* %narrowaddr ; Returns a poison value.
%poison4 = load i64, i64* %wideaddr ; Returns a poison value.
%cmp = icmp slt i32 %poison, 0 ; Returns a poison value.
br i1 %cmp, label %true, label %end ; Branch to either destination.
true:
store volatile i32 0, i32* @g ; This is control-dependent on %cmp, so
; it has undefined behavior.
br label %end
end:
%p = phi i32 [ 0, %entry ], [ 1, %true ]
; Both edges into this PHI are
; control-dependent on %cmp, so this
; always results in a poison value.
store volatile i32 0, i32* @g ; This would depend on the store in %true
; if %cmp is true, or the store in %entry
; otherwise, so this is undefined behavior.
br i1 %cmp, label %second_true, label %second_end
; The same branch again, but this time the
; true block doesn’t have side effects.
second_true:
; No side effects!
ret void
second_end:
store volatile i32 0, i32* @g ; This time, the instruction always depends
; on the store in %end. Also, it is
; control-equivalent to %end, so this is
; well-defined (ignoring earlier undefined
; behavior in this example).
支持的約束代碼列表
通常,約束代碼的行為方式,與在 GCC 中的行為方式相同。LLVM 的支持通常是在“按需”基礎上實現的,支持 GCC 支持的 C 內聯匯編代碼。LLVM 和 GCC 之間的行為不匹配,表明 LLVM 中存在錯誤。
所有目標通常都支持一些約束代碼:
? r:目標通用寄存器類中的寄存器。
? m: 一個內存地址操作數。支持什么尋址模式是特定于目標的,典型的示例是寄存器,或寄存器 + 寄存器偏移量,或寄存器 + 立即偏移量(某些特定于目標的大小)。
? i: 整數常量(特定于目標的寬度)。允許簡單的立即數或可重定位的值。
? n: 一個整數常量——不包括可重定位的值。
? s: 一個整數常量,但只允許可重定位的值。
? X: 允許任何類型的操作數,沒有任何約束。通常用于為 asm 分支或調用傳遞標簽。
? {register-name}: 需要完全命名的物理寄存器。
其它約束是特定于目標的:
AArch64:
? z: 一個立即數 0。輸出WZR或XZR,視情況而定。
? I: 對ADDorSUB指令有效的立即數,即 0 到 4095,可選擇移位 12。
? J: 一個立即數,當取反時,對ADDor SUB指令有效,即 -1 到 -4095,可選左移 12。
? K:一個直接整數,它是有效的邏輯指令等的“位掩碼即時32” AND,EOR或ORR與32位寄存器。
? L:一個直接整數,它是有效的邏輯指令等的“位掩碼即時64” AND,EOR或ORR在64位寄存器。
? M: 與MOV32 位寄存器上的程序集別名一起使用的立即數。這是 的超集K:除了位掩碼immediate 之外,還允許可使用單個MOVZ或MOVL指令加載的立即整數 。
? N: 與MOV64 位寄存器上的程序集別名一起使用的立即數。這是 的超集L。
? Q:內存地址操作數必須在單個寄存器中(無偏移)。(但是,LLVM 目前也為m約束執行此操作。)
? r:一個 32 或 64 位整數寄存器(W* 或 X*)。
? w:一個 32,64 或 128 位浮點/SIMD 寄存器。
? x: 一個較低的 128 位浮點/SIMD 寄存器 ( V0to V15)。
AMDGPU:
? r: 一個 32 位或 64 位整數寄存器。
? [0-9]v:32 位 VGPR 寄存器,編號 0-9。
? [0-9]s: 32 位 SGPR 寄存器,編號 0-9。
所有 ARM 模式:
? Q, Um, Un, Uq, Us, Ut, Uv, Uy: 內存地址操作數。目前與操作數相同m。
ARM 和 ARM 的 Thumb2 模式:
? j: 0 到 65535 之間的立即數(對 有效MOVW)
? I: 一個對數據處理指令有效的立即數。
? J: -4095 到 4095 之間的立即數。
? K: 一個立即數,其按位反轉對數據處理指令有效。(可與模板修飾符“ B”一起使用以打印反轉值)。
? L: 一個立即數,其取反對數據處理指令有效。(可與模板修飾符“ n”一起使用以打印取反值)。
? M: 2 的冪或 0 到 32 之間的整數。
? N: 無效的立即約束。
? O: 無效的立即約束。
? r: 一個通用的 32 位整數寄存器 ( r0-r15)。
? l: 在 Thumb2 模式下,低 32 位 GPR 寄存器 ( r0-r7)。在 ARM 模式下,同r.
? h: 在 Thumb2 模式下,高 32 位 GPR 寄存器 ( r8-r15)。在 ARM 模式下,無效。
? w:一個 32,64 或 128 位浮點/SIMD 寄存器:s0-s31, d0-d31, 或q0-q15。
? x:一個 32,64 或 128 位浮點/SIMD 寄存器:s0-s15, d0-d7, 或q0-q3。
? t:浮點/SIMD 寄存器,僅支持 32 位值: s0-s31。
ARM 的 Thumb1 模式:
? I: 0 到 255 之間的立即數。
? J: -255 和 -1 之間的立即數。
? K: 0 到 255 之間的立即數,可選擇左移一定量。
? L: -7 到 7 之間的立即數。
? M: 0 到 1020 之間的 4 的倍數的立即數。
? N: 0 到 31 之間的立即數。
? O: 一個介于 -508 和 508 之間的 4 的倍數的立即數。
? r: 一個低 32 位 GPR 寄存器 ( r0-r7)。
? l: 一個低 32 位 GPR 寄存器 ( r0-r7)。
? h: 一個高 GPR 寄存器 ( r0-r7)。
? w:一個 32,64 或 128 位浮點/SIMD 寄存器:s0-s31, d0-d31, 或q0-q15。
? x:一個 32,64 或 128 位浮點/SIMD 寄存器:s0-s15, d0-d7, 或q0-q3。
? t:浮點/SIMD 寄存器,僅支持 32 位值: s0-s31。
Hexagon:
? o, v: 一個內存地址操作數,目前與約束相同m。
? r: 32 位或 64 位寄存器。
MSP430:
? r: 8 位或 16 位寄存器。
MIPS:
? I:立即有符號的 16 位整數。
? J:立即整數零。
? K: 立即數無符號 16 位整數。
? L:立即數 32 位整數,其中低 16 位為 0。
? N: -65535 和 -1 之間的立即數。
? O:立即有符號的 15 位整數。
? P: 1 到 65535 之間的一個立即數。
? m: 一個內存地址操作數。在 MIPS-SE 模式下,允許基地址寄存器加上 16 位立即偏移量。在 MIPS 模式下,只是一個基址寄存器。
? R: 一個內存地址操作數。在 MIPS-SE 模式下,允許基地址寄存器加上 9 位有符號偏移量。在 MIPS 模式下,與約束相同 m。
? ZC:一個存儲器地址操作數,適用于使用pref,ll或 sc在給定的子目標指令(細節有所不同)。
? r, d, y: 32 或 64 位 GPR 寄存器。
? f:32 或 64 位 FPU 寄存器 ( F0-F31),或 128 位 MSA 寄存器 ( W0-W31)。對于 MSA 寄存器,建議使用w 參數修飾符以與 GCC 兼容。
? c:適用于間接跳轉的 32 位或 64 位 GPR 寄存器(始終 25)。
? l:lo寄存器,32 位或 64 位。
? x: 無效的。
NVPTX:
? b: 1 位整數寄存器。
? c或h:一個 16 位整數寄存器。
? r: 一個 32 位整數寄存器。
? l或N:一個 64 位整數寄存器。
? f: 一個 32 位浮點寄存器。
? d: 一個 64 位浮點寄存器。
PowerPC:
? I:立即有符號的 16 位整數。
? J:立即數無符號 16 位整數,左移 16 位。
? K: 立即數無符號 16 位整數。
? L: 一個有符號的立即數 16 位整數,左移 16 位。
? M: 一個大于 31 的立即數。
? N: 是 2 的精確冪的立即數。
? O: 立即數整數常量 0。
? P: 一個立即整數常量,其否定是一個有符號的 16 位常量。
? es, o, Q, Z, Zy: 一個內存地址操作數,目前與m.
? r: 一個 32 位或 64 位整數寄存器。
? b: 一個 32 位或 64 位整數寄存器,不包括R0(即 :)R1-R31。
? f:一個 32 或 64 位浮點寄存器 ( F0-F31),或者當啟用 QPX 時,一個 128 或 256 位 QPX 寄存器 ( Q0-Q31; 別名F寄存器)。
? v: 對于or類型,當啟用 QPX 時,為 128 或 256 位 QPX 寄存器 ( ),否則為 128 位 altivec 矢量寄存器 ( )。4 x f324 x f64Q0-Q31V0-V31
? y: 條件寄存器 ( CR0-CR7)。
? wc: CR 寄存器中的單個 CR 位。
? wa, wd, wf: 任何 128 位 VSX 向量寄存器,來自完整的 VSX 寄存器集(重疊浮點和向量寄存器文件)。
? ws:來自完整 VSX 寄存器組的 32 位或 64 位浮點寄存器。
sparc:
? I:立即數 13 位有符號整數。
? r: 一個 32 位整數寄存器。
? f:SparcV8 上的任何浮點寄存器,或 SparcV9 上寄存器“低”半部分的浮點寄存器。
? e: 任何浮點寄存器。(與fSparcV8相同。)
SystemZ:
? I: 立即數無符號 8 位整數。
? J: 立即數無符號 12 位整數。
? K:立即有符號的 16 位整數。
? L:立即有符號的 20 位整數。
? M: 一個立即數 0x7ffffffff。
? Q:具有基地址和 12 位立即數無符號位移的內存地址操作數。
? R:具有基地址,12 位立即數無符號位移和索引寄存器的內存地址操作數。
? S:具有基地址和 20 位立即有符號位移的內存地址操作數。
? T:具有基地址,20 位立即符號位移和索引寄存器的內存地址操作數。
? r或d:32,64 或 128 位整數寄存器。
? a:一個 32,64 或 128 位整數地址寄存器(不包括 R0,它在地址上下文中計算為零)。
? h: 64 位數據寄存器高位部分的 32 位值(LLVM 特定)
? f:一個 32,64 或 128 位浮點寄存器。
X86:
? I: 0 到 31 之間的立即數。
? J: 0 到 64 之間的立即數。
? K: 一個有符號的立即數 8 位整數。
? L:立即數,0xff 或 0xffff 或(僅在 64 位模式下)0xffffffff。
? M: 0 到 3 之間的立即數。
? N: 立即數無符號 8 位整數。
? O: 0 到 127 之間的立即數。
? e:立即數 32 位有符號整數。
? Z:立即數 32 位無符號整數。
? o, v: 目前與m,一樣對待。
? q:一個 8,16,32 或 64 位寄存器,可作為 8 位 l整數寄存器訪問。在X86-32,這是a,b,c,和d 寄存器,以及X86-64,它是所有的整數寄存器。
? Q:一個 8,16,32 或 64 位寄存器,可作為 8 位 h整數寄存器訪問。這是a,b,c,和d寄存器。
? r或l:一個 8,16,32 或 64 位整數寄存器。
? R:一個 8,16,32 或 64 位“傳統”整數寄存器——自 i386 以來就已經存在,無需 REX 前綴即可訪問。
? f:一個 32,64 或 80 位的 '387 FPU 堆棧偽寄存器。
? y: 64 位 MMX 寄存器,如果啟用 MMX。
? x:如果啟用了 SSE:SSE 寄存器中的 32 或 64 位標量操作數,或 128 位向量操作數。如果也啟用了 AVX,可是 AVX 寄存器中的 256 位向量操作數。如果也啟用了 AVX-512,可是 AVX512 寄存器中的 512 位向量操作數,否則報錯。
? Y: 與 相同x,如果啟用了SSE2,否則會出錯。
? A: 特殊情況:首先為單個操作數分配 EAX,然后是 EDX(在 32 位模式下,64 位整數操作數將被拆分為兩個寄存器)。不建議使用此約束,因為在 64 位模式下,64 位操作數將僅分配給 RAX – 如果需要兩個 32 位操作數,最好自己拆分,然后再將其傳遞給asm 語句。
Core:
? r: 一個 32 位整數寄存器。
Asm 模板參數修飾符
在 asm 模板字符串中,可在操作數引用上使用修飾符,例如“ 0:n”。通常,修飾符的行為方式,與在GCC中的行為方式相同。LLVM的支持通常是在“按需”基礎上實現的,支持GCC支持的C內聯匯編代碼。LLVM和GCC之間的行為不匹配,可能表明LLVM中存在錯誤。目標獨立:?c:打印一個不加修飾的直接整數常量,沒有特定于目標的直接標點符號(例如沒有{0:n}”。 通常,修飾符的行為方式,與在 GCC 中的行為方式相同。LLVM 的支持通常是在“按需”基礎上實現的,支持 GCC 支持的 C 內聯匯編代碼。LLVM 和 GCC 之間的行為不匹配,可能表明 LLVM 中存在錯誤。 目標獨立: ? c: 打印一個不加修飾的直接整數常量,沒有特定于目標的直接標點符號(例如沒有0:n”。通常,修飾符的行為方式,與在GCC中的行為方式相同。LLVM的支持通常是在“按需”基礎上實現的,支持GCC支持的C內聯匯編代碼。LLVM和GCC之間的行為不匹配,可能表明LLVM中存在錯誤。目標獨立:?c:打印一個不加修飾的直接整數常量,沒有特定于目標的直接標點符號(例如沒有前綴)。
? n: 否定并打印未經修飾的立即整數常量,沒有特定于目標的立即標點符號(例如,沒有前綴)。?l:打印為未修飾的標簽,沒有特定于目標的標簽標點符號(例如,沒有前綴)。 ? l: 打印為未修飾的標簽,沒有特定于目標的標簽標點符號(例如,沒有前綴)。?l:打印為未修飾的標簽,沒有特定于目標的標簽標點符號(例如,沒有前綴)。
AArch64:
? w: 使用w名稱而不是x名稱打印 GPR 寄存器。例如,代替x30,打印w30。
? x:打印帶有x名稱的 GPR 寄存器。(無論如何,這是默認設置)。
? b, h, s, d, q: 打印帶有b, h*, s*, d*, 或qname的浮點/SIMD 寄存器 ,而不是默認的 v。
AMDGPU:
? r: 沒有效果。
ARM:
? a: 將操作數打印為地址(帶有[和]包圍寄存器)。
? P: 沒有效果。
? q: 沒有效果。
? y: 將 VFP 單精度寄存器打印為索引雙精度值(例如打印為d4[1]而不是s9)
? B: 按位反轉并打印一個沒有# 前綴的立即整數常量。
? L: 打印立即整數常量的低 16 位。
? M: 打印為適合 ldm/stm 的寄存器集。還會打印 指定一個 (!) 之后的所有寄存器操作數,因此請謹慎使用。
? Q: 打印一個寄存器對的低位寄存器,或兩個寄存器操作數的低位寄存器。
? R: 打印一個寄存器對的高位寄存器,或兩個寄存器操作數的高位寄存器。
? H: 打印寄存器對的第二個寄存器。(在大端系統上, H等價于Q,在小端系統上,H等價于R。)
? e: 打印 NEON quad 寄存器的低位雙字寄存器。
? f: 打印 NEON quad 寄存器的高位雙字寄存器。
? m: 打印不帶[and] 修飾的內存操作數的基址寄存器。
Hexagon:
? L: 打印雙寄存器操作數的第二個寄存器。要求它已經連續分配到第一個。
? I: 如果操作數是整數常量,打印字母 ‘i’,否則不打印。用于打印“addi”與“add”指令。
MSP430:
沒有額外的修飾符。
MIPS:
? X: 以十六進制打印一個立即數
? x: 將立即數的低 16 位打印為十六進制。
? d: 將立即整數打印為十進制。
? m: 減一并以十進制形式打印一個立即數。
? z: 如果立即數為零,打印 $0,否則正常打印。
? L: 打印二寄存器操作數的低位寄存器,或打印雙字內存操作數的低位字地址。
? M: 打印二寄存器操作數的高位寄存器,或打印雙字內存操作數的高位字地址。
? D: 打印雙寄存器操作數的第二個寄存器,或打印雙字內存操作數的第二個字。(在大端系統上,D等價于L,在小端系統上,D等價于 M。)
? w: 沒有效果。提供與 GCC 的兼容性,它需要此修飾符才能W0-W31使用f 約束打印 MSA 寄存器 ( ) 。
NVPTX:
? r: 沒有效果。
PowerPC:
? L: 打印雙寄存器操作數的第二個寄存器。要求它已經連續分配到第一個。
? I: 如果操作數是整數常量,打印字母 ‘i’,否則不打印。用于打印“addi”與“add”指令。
? y: 對于內存操作數,打印兩寄存器 X 格式指令的格式化程序。(目前總是打印r0,OPERAND)。
? U: 如果內存操作數是更新形式,打印 ‘u’,否則不打印。(注意:LLVM 不支持更新表單,因此當前將始終不打印任何內容)
? X: 如果內存操作數是索引形式,打印“x”。(注意:LLVM 不支持索引形式,因此目前將始終不打印任何內容)
Sparc:
? r: 沒有效果。
SystemZ:
SystemZ 僅實現n,不支持任何其它與目標無關的修飾符。
X86:
? c: 打印未修飾的整數或符號名稱。(后者是此通常與目標無關的修飾符的特定于目標的行為)。
? A: 打印*前面帶有“ ”的寄存器名稱。
? b: 打印一個 8 位寄存器名稱(例如al);在內存操作數上什么都不做。
? h: 打印高8位寄存器名(如ah);在內存操作數上什么都不做。
? w: 打印 16 位寄存器名稱(例如ax);在內存操作數上什么都不做。
? k: 打印 32 位寄存器名稱(例如eax);在內存操作數上什么都不做。
? q: 打印 64 位寄存器名稱(例如rax),如果 64 位寄存器可用,否則打印32 位寄存器名稱;在內存操作數上什么都不做。
? n: 取反并打印一個未修飾的整數,或者,對于非立即數的操作數(例如可重定位符號表達式),在操作數前打印一個“-”。(可重定位符號表達式的行為是此通常與目標無關的修飾符的特定于目標的行為)
? H:打印具有額外偏移量 +8 的內存引用。
? P:打印內存引用或操作數以用作調用指令的參數。(例如 omit (rip),即使它是 PC 相關的。)
參考鏈接:
https://releases.llvm.org/6.0.0/docs/LangRef.html#module-structure
總結
以上是生活随笔為你收集整理的LLVM语法语义指令特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI中pass架构设计优化
- 下一篇: LLVM与Clang局部架构与语法分析