7.9. 代码模型(Code Model)

LoongArch作为一个精简指令集,它的内存范围地址受限于指令中的编码位宽。

为了在不同情况下实现内存访问,我们定义了多种代码模型,这些模型包含一系列指令,并具有必要的寻址能力和性能开销。

一般来说,更宽的寻址范围需要更多的指令,并带来更高的开销。
如果代码模型能够合理的访问的内存空间,则可以提升应用程序的性能并减小其体积。

下面我们主要介绍LoongArch上目前支持的三种代码模型。

我们还是以下面的代码为例,解释所示支持的代码情况。假设有代码

#include <stdio.h>

long global_val = 0;

int main(int argc, char const *argv[])
{
	global_val = 0x100;

	printf("GLobal Value: %lx,\n", global_val);
	return 0;
}

代码模型最核心的问题,就是如何加载符号的地址,比如上述代码中,

数据global_val和函数printf都是全局的符号。

  1. 正常代码模型(Normal code model)

此模型下:

  • 数据的访问地址跨度范围为:[(PC & ~0xfff)-2GiB-0x800, (PC & ~0xfff)+2GiB-0x800)
    可以跨越4GB。

指令模式如下所示:

00:  pcalau12i $t0, %pc_hi20(data) # 汇编指令代码
     0: R_LARCH_PCALA_HI20 data    # 可重定位类型

04:  ld.w $a0, $t0, %pc_lo12(data) # 汇编指令代码
     4: R_LARCH_PCALA_LO12 data    # 可重定位类型

我们反汇编上面的C代码,结果如下:

bash> loongarch64-linux-gnu-objdump -ald hello
  1c:	1a00000c 	pcalau12i   	$t0, 0
  20:	02c0018c 	addi.d      	$t0, $t0, 0
  24:	0284000d 	addi.w      	$t1, $zero, 256
  28:	2700018d 	stptr.d     	$t1, $t0, 0
  • 函数的跳转地址跨度范围为:[PC-128MiB, PC+128MiB-4],相对PC跨越256MB内存空间。

指令模式如下所示:

00:  bl %plt(printf)
     0: R_LARCH_B26 printf

我们反汇编上面的C代码,结果如下:

  38:	00150185 	or          	$a1, $t0, $zero
  3c:	1a000004 	pcalau12i   	$a0, 0
  40:	02c00084 	addi.d      	$a0, $a0, 0
  44:	54000000 	bl          	0	# 44 <L0^A+0x18> # 跳转到printf函数
  1. 中等代码模型(Medium code model)

此模型下:

  • 数据的访问地址跨度范围为:[(PC & ~0xfff)-2GiB-0x800, (PC & ~0xfff)+2GiB-0x800)
    可以跨越4GB。

指令模式如下所示:

00:  pcalau12i $t0, %pc_hi20(data) # 汇编指令代码
     0: R_LARCH_PCALA_HI20 data    # 可重定位类型

04:  ld.w $a0, $t0, %pc_lo12(data) # 汇编指令代码
     4: R_LARCH_PCALA_LO12 data    # 可重定位类型

我们反汇编上面的C代码,结果如下:

bash> loongarch64-linux-gnu-objdump -ald hello
  1c:	1a00000c 	pcalau12i   	$t0, 0
  20:	02c0018c 	addi.d      	$t0, $t0, 0
  24:	0284000d 	addi.w      	$t1, $zero, 256
  28:	2700018d 	stptr.d     	$t1, $t0, 0

注意: 数据访问和上述的Normal模式一样。

  • 函数的跳转地址跨度范围为:[PC-128GiB-0x20000, PC+128GiB-0x20000-4],相对PC跨越256GB内存空间。

指令模式如下所示:

00:  pcaddu18i $ra, %call36(printf) # 汇编指令代码
     0: R_LARCH_CALL36 printf       # 可重定位类型

04:  jirl $ra, $ra, 0

我们反汇编上面的C代码,结果如下:

   120000740:	1a000004 	pcalau12i   	$a0, 0
   120000744:	02dda084 	addi.d      	$a0, $a0, 1896
   120000748:	1e000001 	pcaddu18i   	$ra, 0
   12000074c:	4ffdb821 	jirl        	$ra, $ra, -584    # 跳转到printf函数
  1. 极限代码模型(Extreme code model)

此模型下:

  • 数据的访问地址跨度范围为:整个64位地址空间。

指令模式如下所示(加载):

00: pcalau12i $t1, %pc_hi20(data)       # 汇编指令代码
    0: R_LARCH_PCALA_HI20 data
04: addi.d $t0, $zero, %pc_lo12(data)   # 汇编指令代码
    4: R_LARCH_PCALA_LO12 data
08: lu32i.d $t0, %pc64_lo20(data)       # 汇编指令代码
    8: R_LARCH_PCALA64_LO20 data
0c: lu52i.d $t0, $t0, %pc64_hi12(data)  # 汇编指令代码
    c: R_LARCH_PCALA64_HI12 data
10: ldx.w $a0, $t1, $t0                 # 汇编指令代码

我们反汇编上面的C代码,结果如下:

bash> loongarch64-linux-gnu-objdump -ald hello
  1c:	1a00000d 	pcalau12i   	$t1, 0
  20:	02c0000c 	addi.d      	$t0, $zero, 0
  24:	1600000c 	lu32i.d     	$t0, 0
  28:	0300018c 	lu52i.d     	$t0, $t0, 0
  2c:	0284000e 	addi.w      	$t2, $zero, 256
  30:	381c31ae 	stx.d       	$t2, $t1, $t0
  • 函数的跳转地址跨度范围为:整个64位地址空间。

指令模式如下所示:

00: pcalau12i $t1, %pc_hi20(foo)      # 汇编指令代码
    0: R_LARCH_PCALA_HI20 foo
04: addi.d $t0, $zero, %pc_lo12(foo)  # 汇编指令代码
    4: R_LARCH_PCALA_LO12 foo
08: lu32i.d $t0, %pc64_lo20(foo)      # 汇编指令代码
    8: R_LARCH_PCALA64_LO20 foo
0c: lu52i.d $t0, $t0, %pc64_hi12(foo) # 汇编指令代码
    c: R_LARCH_PCALA64_HI12 foo
10: add.d $t0, $t0, $t1               # 汇编指令代码
14: jirl $ra, $t0, 0                  # 汇编指令代码

我们反汇编上面的C代码,结果如下:

  60:	1a00000d 	pcalau12i   	$t1, 0
  64:	02c0000c 	addi.d      	$t0, $zero, 0
  68:	1600000c 	lu32i.d     	$t0, 0
  6c:	0300018c 	lu52i.d     	$t0, $t0, 0
  70:	380c31ac 	ldx.d       	$t0, $t1, $t0
  74:	4c000181 	jirl        	$ra, $t0, 0# 跳转到printf函数

Tip

  1. 对于GCC,默认的-mcmodel=normal,使用参数-mcmodel=medium和-mcmodel=extreme分别采用medium和extreme模式。

  2. 对于应用于链接器时,ld有可能会进行优化,也就是我们所说的relax。想要查看原始的没有优化的,则可以参数–no-relax(链接器参数)

loongarch64-linux-musl-gcc code_model.c -o code_model.extreme.elf -O0 -g -mcmodel=extreme -Wl,--no-relax
loongarch64-linux-musl-objdump -alD -M no-aliases code_model.extreme.elf > code_model.extreme.elf.s
  1. 其他GCC支持的-mcmodel的参数,比如large, tiny, tiny-static目前龙架构暂时不支持。