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指令集,也就能够编译通过了。

5 最后问一句为什么

为什么要搞这么一套,而不直接使用bx指令呢?u-boot里面的用法不太容易看出来原因,不妨将目光转向kernel:

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

kernel既要支持比较老的ARM芯片,也要支持比较新的。而老的芯片可能不支持THUMB指令,函数返回使用mov pc, lr,而新的芯片则使用bx lr。在源码里面,对每个汇编宏进行条件编译实在是不合理,而使用.irp.macro可以不重复的使用非常少的代码完成这一任务,汇编源中只需要统一使用ret宏,岂不美哉!

参考文献

[1] GNU官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值