为什么 RISC-V 没有用于计算进位的指令?

9
我需要在RISC-V上进行大数运算(加法和减法,但我将减法视为有符号加法),情况有些复杂。根据我在互联网上半小时的研究,我得到以下结论:
- RISC-V操作没有提供检查进位或溢出的手段。 - 这个决定是由于标志或其他处理方式会给乱序微体系结构增加很多复杂性的缘故。 - 相反,他们建议之后进行分支
- 对于无符号加法,可以使用单个bltu进行溢出处理。 - 如果已知一个操作数的符号,则有符号加法也可以相同处理 - 否则,需要执行两个检查(三个额外指令)
- 互联网上的人对此感到非常愤怒(我这里不会链接)。
据我所知,这些分支确实涵盖了大部分场景,除了一个:(有符号)大数加法。因为在那里,我们在热循环中命中最慢的检查路径。
我只知道一点关于ISA设计的知识,但为什么他们没有包括一个计算(a+b)>>32(实际上是进位)的指令呢?有点像乘法指令被分成mul和mulh一样。这将允许使用始终只有两个指令的所需计算。更强大的微体系结构甚至可以检测序列并只执行一次加法。
我是否错过了使这个指令过时(或等同于它)的一些技巧?它有任何主要缺点吗?我没有找到关于这个一般性话题的很多好文档。

就此而言,Waterman的博士论文甚至没有提到进位标志位,只是在虚拟化的背景下讨论了标志寄存器的困难,而不是针对乱序执行。英特尔在2005年已经解决了这个乱序执行的“问题”。为了捍卫RISC-V,大数可能不会出现在他们的基准测试中。 - Olsonist
1个回答

9

add / sltu指令可以得到和以及进位值:https://godbolt.org/z/Y7f5dzj1P演示了GCC如何使用它进行无符号数学运算:sum=a+b / carry = sum<a。或者使用__builtin_uadd_overflow

但是这样做的问题是缺乏ILP:直到add结果准备好之前,sltu不能开始。如果可以像您提出的那样直接从输入中获取进位值,那么这个问题就可以解决;非常好的观点。当然合并add/sltu也可以解决这个问题;也许这就是设计师们考虑的。

我认为创建一个根据两个输入的加法产生01输出的指令不存在任何CPU设计难题。那将非常容易;支持add指令的任何32位或64位加法器都可以轻松地从高位产生进位信号。事实上,这可能就是sltu读取的内容,因为整数算术逻辑单元通常使用单个二进制加减器,其中一个输入的NOT和一个进位值1用于执行减法。(低位是全加器而不是半加器,否则就是正常的二进制加器。)


对于超过2个寄存器宽度的bignum来说,另一个主要问题是带进位加法(有带进位标志和add-with-carry指令的ISA)。

更糟糕的是,从这3个输入的加法中得到进位值。(其中任何一部分都可能会溢出,所以我认为将其合并为一个加法并进行比较是不可能的。这是纯C实现adc时的常见陷阱;链接答案的评论有可以工作的C代码,但效率不高)。

除非有我不知道的诀窍,否则我认为这才是人们对RISC-V和MIPS等无-FLAGS设计在Bignum方面感到沮丧的真正原因。


谢谢。关于进位的部分是一个非常好的观点。我以为“现在我的寄存器里有零/一了,我完成了”,但事实并非如此。我看到其他架构有一个adc(带进位加法)指令。拥有一个会对RISC-V有所帮助吗?在这种情况下,进位检查会是什么样子? - piegames
@piegames:是的,这会有很大帮助,但它是一个三输入指令,所以它会破坏整个流水线设计。(除非还有其他三输入指令,其中所有三个寄存器操作数都是输入,并且第一个是输出。例如条件选择,如x86 cmov / AArch64 csel / MIPS movn: https://godbolt.org/z/xW5dj7Pdn) - Peter Cordes
1
我明白了。我知道RISV-V有一种三输入指令(称为R4类型),但它很不常见,以至于一些嵌入式CPU根本不支持它。但即使我们有这样的指令,进位检查会是什么样子呢?进位引入了一个新的边缘情况,需要进行额外的检查吗? - piegames
1
@piegames:哦,是的,没错,如果没有另一个指令从这三个输入中获取进位而不是总和,不确定它是否会有显著帮助。(如果您提供adc,您肯定可以提供这个指令) - Peter Cordes
2
这对试图进行椭圆曲线加密实现的密码学家来说也非常糟糕——在加法/乘法后分支会导致代码无法保持恒定时间,可能会通过侧信道泄露信息。 - Chris Beck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接