链接器松弛优化原理介绍

1、前言

(1)要理解链接器松弛优化,需要先理解:RISC-V架构的寻址方式、编译和链接过程;

(2)链接器松弛优化在《RISC-V 体系结构编程与实践》6.4节有详细介绍;

2、链接器松弛优化介绍

(1)作用:通常RISC架构处理器访问符号地址需要两条指令,一条指令处理地址高位,一条指令处理地址低位,但是在链接阶段,通过链接松弛优化,使用一条指令实现对符号地址的访问;

(2)链接松弛优化主要涉及两方面:函数跳转优化、符号地址访问优化;

3、汇编实现程序跳转的过程

(1)当程序需要跳转时,符号地址与当前PC值相差有多有少,可能几百字节,也可能几百兆,这里就涉及到长跳转和短跳转;

(2)RISC-V有短跳转指令,比如jal、jalr、beq等等,但是这些指令都是短跳转,指令跳转到当前PC值前后几KB或者1MB的范围,当要跳转的地址与当前PC值相差几百MB的时候,这些短跳转指令就没法实现跳转;

(3)RISC-V也可以实现长跳转,使用auipc+jalr能够跳转到当前PC值前后2GB范围的范围,能实现所有地址的跳转;

(4)短跳转和长跳转的区别:长跳转可以实现所有地址的跳转,但是需要两条指令才能实现;短跳转只能在当前PC值地址前后小范围跳转,但是只需要一条指令就能跳转;

总结:链接器松弛优化就是把能够使用短跳转的地方都使用短跳转,这样可以减少汇编指令;

3、函数跳转优化

3.1、main .c

void foo(void)
{
	
}

int main(void)
{
	foo();
}

3.2、不使用链接器松弛优化

3.2.1、编译指令

# -mno-relax是关闭链接器松弛优化,默认是打开的
riscv64-linux-gnu-gcc -mno-relax main.c -o test.elf
riscv64-linux-gnu-objdump -D test.elf > test.dis

3.2.2、反汇编文件test.dis

······
00000000000005fc <foo>:
 5fc:	1141                	addi	sp,sp,-16
 5fe:	e422                	sd	s0,8(sp)
 600:	0800                	addi	s0,sp,16
 602:	0001                	nop
 604:	6422                	ld	s0,8(sp)
 606:	0141                	addi	sp,sp,16
 608:	8082                	ret

000000000000060a <main>:
 60a:	1141                	addi	sp,sp,-16
 60c:	e406                	sd	ra,8(sp)
 60e:	e022                	sd	s0,0(sp)
 610:	0800                	addi	s0,sp,16
 612:	00000097          	auipc	ra,0x0
 616:	fea080e7          	jalr	-22(ra) # 5fc <foo>
 ······

从反汇编文件可知,main函数跳转到foo函数,使用两条指令实现:auipc ra,0x0;jalr -22(ra) # 5fc ;

3.3、使用链接器松弛优化

3.3.1、编译指令

# 打开链接器松弛优化
riscv64-linux-gnu-gcc  main.c -o test.elf
riscv64-linux-gnu-objdump -D test.elf > test.dis

3.3.2、反汇编文件test.dis

 ······
00000000000005ea <foo>:
 5ea:	1141                	addi	sp,sp,-16
 5ec:	e422                	sd	s0,8(sp)
 5ee:	0800                	addi	s0,sp,16
 5f0:	0001                	nop
 5f2:	6422                	ld	s0,8(sp)
 5f4:	0141                	addi	sp,sp,16
 5f6:	8082                	ret

00000000000005f8 <main>:
 5f8:	1141                	addi	sp,sp,-16
 5fa:	e406                	sd	ra,8(sp)
 5fc:	e022                	sd	s0,0(sp)
 5fe:	0800                	addi	s0,sp,16
 600:	febff0ef          		jal	ra,5ea <foo>
 604:	4781                	li	a5,0
 606:	853e                	mv	a0,a5
 608:	60a2                	ld	ra,8(sp)
 60a:	6402                	ld	s0,0(sp)
 60c:	0141                	addi	sp,sp,16
 60e:	8082                	ret
  ······

只使用一条短跳转指令就跳转到foo函数处执行:jal ra,5ea ;因为foo所在地址在jal短跳转指令的寻址范围内,这样就减少一条汇编指令;

4、符号地址访问优化

4.1、main.c

int a =5;

int foo(void)
{
	return a;
}

int main(void)
{
	foo();
}

4.2、不使用编译器松弛优化

4.2.1、编译指令

# 打开链接器松弛优化
riscv64-linux-gnu-gcc  main.c -o test.elf
riscv64-linux-gnu-objdump -D test.elf > test.dis

4.2.2、反汇编文件test.dis

...
00000000000005ea <foo>:
 5ea:	1141                	addi	sp,sp,-16
 5ec:	e422                	sd	s0,8(sp)
 5ee:	0800                	addi	s0,sp,16
 
 # 实现对全局变量a访问
 5f0:	00002797          		auipc	a5,0x2
 5f4:	a1878793          		addi	a5,a5,-1512 # 2008 <a>
 5f8:	439c                	lw	a5,0(a5)
 
 5fa:	853e                	mv	a0,a5
 5fc:	6422                	ld	s0,8(sp)
 5fe:	0141                	addi	sp,sp,16
 600:	8082                	ret

0000000000000602 <main>:
 602:	1141                	addi	sp,sp,-16
 604:	e406                	sd	ra,8(sp)
 606:	e022                	sd	s0,0(sp)
 608:	0800                	addi	s0,sp,16
 60a:	fe1ff0ef          		jal	ra,5ea <foo>
 60e:	4781                	li	a5,0
 610:	853e                	mv	a0,a5
 612:	60a2                	ld	ra,8(sp)
 614:	6402                	ld	s0,0(sp)
 616:	0141                	addi	sp,sp,16
 618:	8082                	ret
...
0000000000002008 <a>:
    2008:	0005                	c.nop	1
	...

4.2、使用编译器松弛优化

4.2.1、修改链接脚本

# 新增.sdata段和设置_global_pointer$符号
.sdata : {
		_global_pointer$ = . + 0x800;
		*(.sdata) 
		*(.sbss)
		}

4.2.2、在启动代码里初始化GP寄存器

···
# 新增以下代码
.option push
.option norelax
la gp,_global_pointer$
.option pop
······

4.3.3、反汇编代码

addi a5,gp,xxx

最后对全局变量的寻址会被替换成一条指令,相对于gp寄存器的值为基地址进行寻址;

4.3.4、原理分析

(1)在链接时把全局变量等符号放到.sdata段,然后把.sdata段的基地址赋值给gp寄存器;
(2)访问符号时,可以基于gp寄存器的值为基地址进行寻址,进行短跳转;
(3)这里addi的寻址范围只能在gp寄存器的值为基地址的前后2KB;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值