2012年6月28日 星期四

GCC RTL INSN 簡介

簡介


在 GCC Backend 中的 IR 主要使用 RTL , 當作一些 Target Dependency 的一些相關操作時常會需要直接的對 RTL 去做一些檢查或掃描, 而在這篇文章概略性的介紹了 RTL INSN 的種類及樣貌, 以供入門

RTL INSN



RTL INSN 所用的內部的結構為 Doubly-Linked Lists
由一個又一個的 INSN 所串列而成
其中 insn 分為下列幾種
  1. insn: 一般的指令, 非 branch/jump/call 的指令
  2. jump_insn: 跳躍指令, 會造成任何 control flow 轉移的指令類型, 包含 conditional/unconditional jump, 及 indirect jump.
  3. call_insn: 任何呼叫函數的指令
  4. debug_insn: 塞 debug info 的
  5. note: 就 note 阿
  6. barrier: 任何 control flow 都不應該穿越這傢伙, 隔絕各個 Basic Block 東東
  7. code_label: 在 c 裡面給 goto 用的 label 會變成這種東西
當然詳細的還是到 GCC Internal 會寫的比較清楚 http://gcc.gnu.org/onlinedocs/gccint/Insns.html
接著來看一下實際的 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
  1. i 代表該 INSN 的 uid , 也就是唯一的編號
  2. u 上一道 INSN 的 uid
  3. u 下一道 INSN 的 uid
  4. B Basic Block 的編號
  5. e 這道 INSN 的主要內容, 例如這上面那道指令所描述的是從記憶體讀取一個值到暫存器中
  6. i 此 INSN 相對應回原始碼的位置
  7. i 放 RTL pattern Name
  8. 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 也一定要仔細的看一遍, 避免發生悲劇...

1 則留言: