8.3. 汇编源文件中的汇编指令

汇编源文件中的汇编指令包括体系架构汇编手册定义的指令,以及编译器提供的汇编宏指令。下面分别说明。

8.3.1. 汇编指令

汇编源文件中的汇编指令通常与架构汇编手册中的指令一一对应,只是书写形式略有差异。以加法指令为例,汇编手册中的写法是add.w r4, r5, r6,而汇编源文件中的写法是add.w $r4, $r5, $r6,即寄存器名前需要添加$符号。同时,汇编源文件也支持寄存器别名,例如add.w $a0, $a1, $a2

8.3.2. 汇编宏指令

成熟的体系架构生态通常会提供丰富的宏指令,用于屏蔽目标文件中不够直观的汇编指令写法,或隐藏符号重定位等细节,从而帮助开发者更快编写汇编程序。在汇编阶段,汇编器会根据当时已知的信息,把宏指令展开为一条或多条真实机器指令。下面列举一些LoongArch ABI支持的汇编宏指令。

  1. 空指令

空指令没有实际功能语义,常用于地址对齐填充。LoongArch编译器支持的空指令为:

nop

该宏指令最终会被汇编器翻译为如下真实汇编指令。

andi 	$r0, $r0, 0x0
  1. 立即数加载宏指令

立即数加载宏指令用于把32位或64位立即数加载到通用寄存器中,加载过程不需要借助其他寄存器。LoongArch编译器支持的立即数加载宏指令有两种:

li.w 	rd, imm31
li.d 	rd, imm64

根据立即数长度不同,宏指令li.w需要1~2条LoongArch汇编指令实现,li.d需要3~4条LoongArch汇编指令实现。例如,将立即数0加载到寄存器a0的宏指令li.w $a0, 0,最终会被汇编器实现为下面的LoongArch指令。

addi.w 	$r4, $r0, 0

第03章已经详细介绍不同长度立即数加载的汇编写法,这里不再重复。

  1. 地址加载宏指令

地址加载宏指令用于把符号地址加载到通用寄存器中,同样不需要借助其他寄存器中转。LoongArch编译器支持的地址加载宏指令如下:

la.local 	rd, label
la.global 	rd, label

其中,la.local用于加载同一汇编源文件内的符号地址,la.global用于加载其他汇编源文件中的符号地址。指令中的label是目标文件中的标签,通常表示某个符号的起始地址。例如,在汇编源文件中使用宏指令la.local $r4, .LC0时,在可重定位目标文件中会被汇编器转换为如下真实汇编指令。

pcaddu12i 	$r4, 0
addi.d 		$r4, $r4, 0
  1. 跳转宏指令

跳转宏指令用于有条件或无条件跳转到符号地址,且不需要借助其他寄存器中转。LoongArch编译器支持的几种典型跳转宏指令如下:

jr 		rd
bl 		symbol
bgt 	rj, rd, label
ble 	rj, rd, label
bgtz 	rj, rd, label
blez 	rj, rd, label

其中,宏指令jr用于无条件跳转到寄存器rd中保存的目标地址。通常使用寄存器r1,写作jr r1,用于函数返回。该宏指令最终实现为如下LoongArch指令:

jirl 	$r0, $r1, 0

宏指令bl symbol用于函数调用。调用同一文件内的函数时,例如函数test,可直接写bl test。调用外部文件中的函数时,例如libc库函数printf,可使用bl %plt(printf)。使用bl symbol时,开发者无需过多处理函数地址重定位细节,可明显提高汇编程序编写效率。

宏指令bgtble等用于条件跳转。例如,bgt表示当rj大于rd时跳转到label。以下指令为例:

label_1:
	addi.d 	$r5, $r5, 2
	bgt 	$r4, $r5, label_1
	nop

这里bgt $r4, $r5, label_1用于判断r4是否大于r5。条件成立时,程序跳转到label_1重新执行addi.d $r5, $r5, 2;否则继续执行nop

LoongArch ABI还定义了更多宏指令,例如move rd, rs用于把寄存器rs中的数据复制到rd,la.got rd, label用于通过GOT(Global Offset Table,全局偏移表)把地址加载到寄存器rd。后续还可能增加更多宏指令,读者需要时可查阅龙芯架构参考手册ABI部分。