簡介
在 GCC Backend 中的 IR 主要使用 RTL , 當作一些 Target Dependency 的一些相關操作時常會需要直接的對 RTL 去做一些檢查或掃描, 而在這篇文章概略性的介紹了 RTL INSN 的種類及樣貌, 以供入門
RTL INSN
RTL INSN 所用的內部的結構為 Doubly-Linked Lists
由一個又一個的 INSN 所串列而成
其中 insn 分為下列幾種
- insn: 一般的指令, 非 branch/jump/call 的指令
- jump_insn: 跳躍指令, 會造成任何 control flow 轉移的指令類型, 包含 conditional/unconditional jump, 及 indirect jump.
- call_insn: 任何呼叫函數的指令
- debug_insn: 塞 debug info 的
- note: 就 note 阿
- barrier: 任何 control flow 都不應該穿越這傢伙, 隔絕各個 Basic Block 東東
- code_label: 在 c 裡面給 goto 用的 label 會變成這種東西
接著來看一下實際的 insn 長怎樣
(insn 21 20 22 6 (set (reg:SI 59 [ D.1238 ]) (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars) (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32])) /home/kito/sum.c:7 -1 (nil))
大致上就是 Lisp-style 的樣子
在 gcc/rtl.def 檔案裡可以找到 insn 的定義 DEF_RTL_EXPR(INSN, "insn", "iuuBeiie", RTX_INSN)
由這串我們來解讀整個是在幹嘛的
攤開成一個一行來看, 並且參照上面定義 : "iuuBeiie", 一道 insn 總共由八個東西所組成
(insn 21 20 22 6 (set (reg:SI 59 [ D.1238 ]) (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars) (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32])) /home/kito/sum.c:7 -1 (nil))
後面 # 後面為註解
(insn 21 # 1. i 20 # 2. u 22 # 3. u 6 # 4. B (set (reg:SI 59 [ D.1238 ]) (mem/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars) (const_int -8 [0xfffffffffffffff8])) [0 sum+0 S4 A32])) # 5. e /home/kito/sum.c:7 # i -1 # 6. i (nil)) # 7. e
- i 代表該 INSN 的 uid , 也就是唯一的編號
- u 上一道 INSN 的 uid
- u 下一道 INSN 的 uid
- B Basic Block 的編號
- e 這道 INSN 的主要內容, 例如這上面那道指令所描述的是從記憶體讀取一個值到暫存器中
- i 此 INSN 相對應回原始碼的位置
- i 放 RTL pattern Name
- e 塞 REG_NOTES 資訊, 主要是暫存器相關資訊如 live or kill 等等
相關存取函數可以從 rtl.h 中找到
/* ACCESS MACROS for particular fields of insns. */ /* Holds a unique number for each insn. These are not necessarily sequentially increasing. */ #define INSN_UID(INSN) XINT (INSN, 0) /* Chain insns together in sequence. */ #define PREV_INSN(INSN) XEXP (INSN, 1) #define NEXT_INSN(INSN) XEXP (INSN, 2) #define BLOCK_FOR_INSN(INSN) XBBDEF (INSN, 3) /* The body of an insn. */ #define PATTERN(INSN) XEXP (INSN, 4) #define INSN_LOCATOR(INSN) XINT (INSN, 5)
相關判斷函數也可以從 rtl.h 中找到
/* Predicate yielding nonzero iff X is a label insn. */ #define LABEL_P(X) (GET_CODE (X) == CODE_LABEL) /* Predicate yielding nonzero iff X is a jump insn. */ #define JUMP_P(X) (GET_CODE (X) == JUMP_INSN) /* Predicate yielding nonzero iff X is a call insn. */ #define CALL_P(X) (GET_CODE (X) == CALL_INSN) /* Predicate yielding nonzero iff X is an insn that cannot jump. */ #define NONJUMP_INSN_P(X) (GET_CODE (X) == INSN) /* Predicate yielding nonzero iff X is a debug note/insn. */ #define DEBUG_INSN_P(X) (GET_CODE (X) == DEBUG_INSN) /* Predicate yielding nonzero iff X is an insn that is not a debug insn. */ #define NONDEBUG_INSN_P(X) (INSN_P (X) && !DEBUG_INSN_P (X)) /* Nonzero if DEBUG_INSN_P may possibly hold. */ #define MAY_HAVE_DEBUG_INSNS MAY_HAVE_DEBUG_STMTS /* Predicate yielding nonzero iff X is a real insn. */ #define INSN_P(X) \ (NONJUMP_INSN_P (X) || DEBUG_INSN_P (X) || JUMP_P (X) || CALL_P (X)) /* Predicate yielding nonzero iff X is a note insn. */ #define NOTE_P(X) (GET_CODE (X) == NOTE) /* Predicate yielding nonzero iff X is a barrier insn. */ #define BARRIER_P(X) (GET_CODE (X) == BARRIER)其中要注意的部份是 INSN_P 所濾出來的不是 INSN 而是 INSN, DEBUG_INSN, JUMP_INSN 及 CALL_INSN
要單純的 INSN 要用 NONJUMP_INSN_P 來判斷
小結
對於 GCC Backend 的 Porting 來說 RTL 相關部份是相當重要的基礎知識之一,
如果真的不幸的踏入 GCC 屠龍之道則相關文件一定要先熟讀幾次
以及 rtl.h 也一定要仔細的看一遍, 避免發生悲劇...
要看中文的 GCC 教學,果然還得是 Kito
回覆刪除