RISCV超12位立即数字段的跳转问题/解决数据竞争问题的底层支持/数组索引访问与指针访问/常见优化/x86的复杂性缺陷

RISC-V

Posted by Bruce Lee on 2024-01-03

关于超出条件分支的12位立即数字段的跳转问题

在少有的可能下,条件跳转会跳到很远的地址空间(超过12位地址表示的半字范围),我们不选择在硬件方面而是在汇编程序方面作出解答:在分支目标中插入无条件跳转指令,并将条件取反,以便条件分支决定是否跳过该无条件跳转.

SB型条件分支指令的立即数字段是12位,UJ型无条件跳转指令的立即数字段是20位.支持正负2^19个半字范围的寻址.

关于逆向工程将机器码恢复到汇编语言

当发生"内存转储"(core dump)时,需要逆向.

RISC-V世界的同步原语的构建-解决数据竞争问题的底层支持

构建一个对内存执行原子性的读取和写入操作,RISC-V采用了指令对的实现方式:保留加载/条件存储机制
使用lr.d/sc.d指令对来实现最基本的同步支持

1
2
3
4
again:  lr.d x10, (x20) //load-reserved
sc.d x11, x23, (x20) //store-conditional
bne x11, x0, again //branch if store fails
addi x23, x10, 0 //put loaded value in x23

构建支持编程语言中的加锁/解锁机制,例如c++中的锁变量,对于锁变量的作用,使其对某一个代码块中的内存地址保持唯一的线程控制权,这是可以在代码层面实现的.
而对于锁变量本身,需要一个同步原语来支持:当其他处理器访问锁变量时,会由于其他处理器已经进行读取而导致失败.

1
2
3
4
5
    addi x12, x0, 1 //copy locked value
again: lr.d x10, (x20) //load-reserved to read lock
bne x10, x0, again //check if it is 0 yet
sc.d x11, x12, (x20) //attempt to store new value
bne x11, x0, again //branch if store fails

当需要释放锁变量时:

1
sd x0, 0(x20) /free lock by writing 0

计算机解决问题的一般方法

事实上,计算机科学中的每一个问题都可以通过增加一个间接的中间层来解决

迭代时,使用数组索引访问与指针访问在汇编上的区别

数组访问迭代中,需要频繁使用下标来计算地址,尽管数组的首地址也是存放在寄存器中用来访问,但是由于下标计算,会消耗性能.
使用指针,是直接使用存储了内存地址的寄存器进行访问,不需要计算下标来定位.

在c编程中,应该尽可能的使用指针来获取更高的性能,但是现在的优化编译器已经可以帮助我们做到这些,即使我们是用数组下标来访问.

RISC-V编译器的3种常见优化

过程内联

将过程的核心代码直接改写在调用过程的地方,可以少执行一些调用,保存,返回等指令.但是同样,当对该过程的调用来自于很多地方的时候,会导致代码量的迅速增加,这种代码扩展也可能导致性能降低.

分配寄存器

RISC-V编译器会尽可能的耗尽临时寄存器,进而去使用保护寄存器或者内存单元(或在堆栈中分配局部变量)

强度削弱

使用移位来代替乘法

循环变量消除

尽可能的消除循环内的数组地址计算,使用指针来代替.
技巧:编译器在编译的时候就会将一些浮点/整点运算进行计算,避免在程序运行时进行除法运算

对于面向对象的另一种术语

可以这么说:这是一种面向对象而非动作,面向数据而非逻辑的编程方式

RISC-V的一些特点

1.访问内存的唯一方式是通过加载和存储指令
2.没有将多个寄存器加载和存储的指令
3.比较两个寄存器的大小时,只有分支指令

复杂性的陷阱-x86指令系统体系结构

1.保持了二进制兼容性,这在商业上可能是成功的,但是在学术上并不是如此,导致了"冗余".
2.寄存器全部是专用寄存器,所以并不认为8086是通用寄存器(GPR)体系结构.即使到了现代,x86中的通用寄存器并不完全通用,有一些指令会进行限制,而RISC-V所有的32个寄存器都是通用寄存器,可以成为任何指令中的地址或者数据.
3.x86和AMD64都使用了同样的遗产模式
4.汇编指令中,涉及的所有操作数都是内嵌到二进制码里面的,RISC-V的指令长度是固定的32位,尽管在涉及到立即数的范围有一些立即数字段长度的限制,但这并没有什么影响.相反,x86指令长度是变化很大的,这对与构建硬件有很大的难度,虽然其指令功能更加强大,但是这并不意味着高效.x86有很多寻址方式,反而RISC-V有很少的寻址方式,但是在x86中一些复杂的寻址方式中,使用RISC-V指令不会超过3行就可以实现其寻址功能.由于x86中庞大的指令数量,其中很多部分实现了一些复杂的功能,但是这些功能并不高效,比如字符串指令,在大多数程序中通常不执行,这些指令比等效的软件程序要慢.
5.由于这些复杂,庞大的指令功能,所以x86指令的编码是极其的复杂.
6.由于早期的x86微处理器比其竞争对手的下一代体系结构早两年,所以在商业上获得了成功,大量的市场也为x86的工程师提供更多资源来克服额外的复杂性.
综上,x86在未来并不具备竞争力,即使现在x86已经占据了大部分的市场和相当多的体系结构和制造专业知识.
7.RISC-V的基本体系结构及其扩展共有184条指令以及13条系统指令

更强大的指令意味着更高的性能 (X)

强大的指令可能是将简单的指令进行组合来完成某一个功能,但是组合简单指令完成功能有很多不同的方法(或者交叉方法),并且技术和算法一直是在发展的,已经形成的强大指令可能并不高效(比如x86中的添加前缀的move指令)

用汇编语言编程以获得最大性能 (X)

我现在始终认为,编译器是最早成熟的功能强大的超过人的人工智能,不管是在分配变量以寄存器的选择还是在任何其他方面,做的比汇编专家还要好.
最重要的是,汇编需要更长时间的编码和调试,以及维护性差和可移植性查.
问题:什么是人工智能?仔细思考,想想市面上人工智能的应用是如何做出来的
现象:商用计算机二进制兼容的重要性意味着成功的指令系统无须改变 (X)
技巧:忘记在字节寻址的机器中,连续的字或者双字地址相差不为1
技巧:在变量的定义过程外,使用一个指针指向该变量
现象:RISC-V指令都与编程语言中出现的结构相关
知识:标准指令系统体系结构的多媒体扩展通常提供饱和计算


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !