GNU彙編器——.irp和.macro

1 從一個彙編錯誤開始說起

最近從u-boot中摘了一段代碼出來(源於kernel),這段代碼作用是用軟件(彙編)實現除法和取模運算,因爲有些老的CPU是沒有相關的硬件指令的。在編譯的時候,彙編器報了一些錯誤,這些錯誤都是同一類型,這裏僅列出其中的一個:Error: bad instruction 'reteq lr'

錯誤本身還是比較好理解的,arm32是沒有ret指令的。但讓人不理解的是爲什麼這段代碼在u-boot中可以編譯通過。直到我看到了這麼一段彙編代碼:

/*
 * We only support cores that support at least Thumb-1 and thus we use
 * 'bx lr'
 */
.irp	c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
.macro	ret\c, reg
.ifeqs	"\reg", "lr"
bx\c	\reg
.else
mov\c	pc, \reg
.endif
.endm
.endr

初看這段代碼可能覺得有些奇怪,且看下文一一分解。

2 GNU彙編器的.irp

首先是.irp的用法:.irp symbol,values . . .

symbol是符號,values 是一串值,這一串值會被逐一賦給symbol,在引用symbol需要加上斜槓,即\symbol。舉例來說,彙編下面這段代碼:

.irp param,1,2,3
move d\param,sp@-
.endr

等價於彙編:

move d1,sp@-
move d2,sp@-
move d3,sp@-

3 GNU彙編器的.macro

彙編宏可能更爲常見,其用法如下(一個帶參,一個不帶參):

.macro macname
.macro macname macargs ...

macname爲宏名,如果宏帶參的話,參數之間以空格或逗號隔開。對於宏參數,可以通過在其後添加:req來表明引用宏時,該參數必須賦一個非空值;也可以添加:vararg來表明引用宏時,該參數接收所有剩下的引用時傳參;還可以添加=deflt來給參數指定一個值爲deflt的默認默認值。引用宏時,按照定義時的參數順序傳參(如果有的話)即可。當然,也可以按照macname=value的形式傳參,這時就不必按照定義時的參數順序了。

看一個具體的例子,宏定義如下:

.macro sum from=0, to=5
.long \from
.if \to-\from
sum "(\from+1)",\to
.endif
.endm

使用sum 0, 3或等價的形式sum to=3, from=0來引用宏,那麼可以得到如下的結果:

.long 0
.long 1
.long 2
.long 3

4 當.irp遇到.macro

此時,再回到第1節中的那段代碼:

.irp	c,,eq,ne,cs,cc,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,hs,lo
.macro	ret\c, reg
.ifeqs	"\reg", "lr"
bx\c	\reg
.else
mov\c	pc, \reg
.endif
.endm
.endr

通過.irp定義的符號c(條件碼)擁有一系列的取值,如eqne等,還要注意最開始的那個空值。在.irq.endr之間的那一段是一個宏定義,當我們將那一系列的值賦給c時,就可以得到一系列的彙編宏:

.macro	ret, reg
.ifeqs	"\reg", "lr"
bx	\reg
.else
mov	pc, \reg
.endif
.endm

.macro	reteq, reg
.ifeqs	"\reg", "lr"
bxeq	\reg
.else
moveq	pc, \reg
.endif
.endm

......(不再列出)

至此一切真相大白,reteq lr中的reteq是一個宏,由於參數是lr,因此該宏引用會被替換爲bxeq lr,而bx指令當然屬於arm32指令集,也就能夠編譯通過了。

參考文獻

[1] GNU官方文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章