How to detect an overflow on assembler risc-v?

问题描述 投票:0回答:1

我正在尝试用 RISC-V 汇编语言实现一个递归阶乘函数,如果出现溢出,该函数会引发错误。但是,我正在努力检测它。有解决办法吗?

`

assembly overflow riscv integer-overflow riscv32
1个回答
1
投票

看看 GCC 如何实现

__builtin_smul_overflow
umul
(https://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;
}

clang 16 -O2 for rv32gc on Godbolt

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
跨阶乘迭代将高半部分放在一起以检查最后是否溢出。

(如果您希望调用者经常传递溢出的输入,那么提前输出可以节省时间,并且使最坏情况下的性能只有大约 13 次乘法,因为

13!
溢出 32 位整数。与最坏的 -大约 2 ^ 32-1 次迭代的案例时间和早出。但否则只是一个
or
可以比每次分支更便宜。当然,如果你关心性能,你就不会进行递归实现;这增加了很多开销与循环。)


正如 Jester 所建议的,

mulhu
获得完整乘法的高半部分将使您检查高半部分是否非零。这与检查完整结果是否与输入操作数的宽度相同。

(

mulh
是一个带符号的乘法,所以在一般情况下,您需要检查它是否是低半部分的符号扩展,如果将结果截断到输入的宽度,则检查是否有符号溢出。对于具有非负输入的阶乘,您可以检查它是否为零,或者最好只使用无符号来增加您可以支持的值范围。)

在具有高效乘法器的 CPU 上,尤其是将

mulhu
/
mul
融合到单个操作中以扩大乘法 ALU 的 CPU 上,额外的
mulhu
比您可能做的任何其他事情都要便宜,例如计算前导零输入。特别是没有扩展 B,因为基线 RISC-V 遗漏了许多其他主流 ISA 中常见的指令,例如位扫描。

© www.soinside.com 2019 - 2024. All rights reserved.