assembly 如何检测汇编程序risc-v上的溢出?

lsmd5eda  于 2023-03-30  发布在  其他
关注(0)|答案(1)|浏览(133)

我试图在RISC-V汇编语言中实现一个递归阶乘函数,如果有溢出就会引发错误。但是,我很难检测到它。有解决方案吗?

dpiehjr4

dpiehjr41#

看看GCC如何实现__builtin_smul_overflowumulhttps://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html

unsigned checked_umul(unsigned a, unsigned b, bool *overflow_happened)
{
    unsigned res;
    bool ovf = __builtin_umul_overflow(a,b, &res);
    if (ovf){
        *overflow_happened = 1;   // set or leave unmodified
                                  // check once at the end.
    }
    return res;
}

为Godbolt上的rv 32 gc发出16 -O2叮当声

checked_umul(unsigned int, unsigned int, bool*):
        mulhu   a3, a0, a1            # high half of a * b
        mul     a0, a0, a1            # result = a * b
        beqz    a3, .LBB1_2
        li      a1, 1
        sb      a1, 0(a2)             # just an example of something to branch on
.LBB1_2:
        ret

当然你会在asm中内联这个,而不是实际调用一个函数;在C语言中将其作为函数只是为了让我们可以单独查看asm,就像How to remove "noise" from GCC/clang assembly output?中那样
你可以在非零的高半部分分支,做任何你喜欢的事情,或者你可以在阶乘迭代中一起OR高半部分,只是检查最后的溢出。
(If你期望调用者经常传递溢出的输入,在它上面的early-out可以保存时间,并且使最坏情况下的性能只有大约13次乘法,因为13!溢出32位整数。与early-out的最坏情况下大约2^32-1次迭代的时间相比。但是否则,仅仅一个or可以比每次分支更便宜。当然,如果你关心性能,你就不会做递归实现了与循环相比,这增加了大量开销。)
正如Jester所建议的,mulhu获取完整乘法的高半部分将允许您检查高半部分是否为非零。这与检查完整结果是否与输入操作数的宽度相同。
mulh是一个有符号乘法,所以在一般情况下,你会想检查它是下半部分的符号扩展,如果将结果截断到输入的宽度,检查是否有符号溢出。对于非负输入的阶乘,你可能会检查它是否为零,或者更好地使用unsigned来增加你可以支持的值范围。)
在具有高效乘法器的CPU上,特别是将mulhu/mul融合到扩展乘法ALU的单个操作中的CPU,额外的mulhu比您可能做的任何事情都便宜,例如计算输入的前导零。特别是没有扩展B,因为基线RISC-V省略了许多在其他主流ISA中常见的指令,例如位扫描。

相关问题