c - C 和 asm 中的 imulq 和 unsigned long long 溢出检测

标签 c assembly gcc x86-64 integer-overflow

作为汇编新手,我使用 gcc 进行逆向工程。但是现在我遇到了一个有点有趣的问题:我尝试将两个 64 位整数乘以 x86-64。 C 代码如下所示:

unsigned long long 
val(unsigned long long a, unsigned long long b){
    return a*b;
}

并用 gcc 编译:

val:
    movq    %rdi, %rax
    imulq   %rsi, %rax
    ret

对无符号整数使用有符号乘法可能违反直觉,但它适用于 C。

但是,我想检查乘法是否溢出。现在,如果结果大于 2^63-1,就会设置溢出标志(我猜是因为它毕竟是有符号乘法)。但对于无符号 64 位,只要结果不大于 2^64-1,这仍然可以。

在这种情况下(在汇编中)进行乘法的正确方法是什么?

最佳答案

看起来你不能使用imul没有一堆额外的代码,因为 CF 和 OF 都以相同的方式设置。作为the "operation" section of the manual描述,如果完整的 128b 结果与 sign_extend(low_half_result) 不匹配,则设置它们.所以你是对的,即使是 imul 的多操作数形式也是如此仍然有一些签名行为。如果他们像 add 就好了/sub并独立设置 OF 和 CF,因此您可以查看未签名数据的 CF 或已签名数据的 OF。

为某物找到好的 asm 序列的最佳方法之一是询问编译器。 C 没有方便的整数溢出检测,but Rust does .

我编译此函数以返回值和无符号环绕检测 bool 值。显然 Rust 的 ABI 将它们作为隐藏的第一个 arg 传递指针返回,而不是在 rdx:rax 中,就像我认为 C ABI 会为这么小的结构一样。 :(

pub fn overflowing_mul(a: u64, b: u64) -> (u64, bool) {
  a.overflowing_mul(b)
}
    # frame-pointer boilerplate elided
    mov     rax, rsi
    mul     rdx
    mov     qword ptr [rdi], rax
    seto    byte ptr [rdi + 8]

    mov     rax, rdi                # return the pointer to the return-value
    ret

Godbolt compiler explorer (Rust 1.7.0) 的 Asm 输出.这或多或少证实了 mov指令和单操作数完全乘法的额外 uop 比我们在双操作数 imul 之后进行额外检查所能做的任何事情都更有效。 .

documentation for mul

"The OF and CF flags are set to 0 if the upper half of the result is 0; otherwise, they are set to 1."

总而言之,使用mul并检查 OFCF查看高半部分是否非零。


mulimul琐事:

imul 之间只有全乘法 (N x N => 2N) 结果的上半部分不同和 mul .我认为英特尔选择了 imul作为具有多个显式操作数的那个
imul r32, r32, sign-extended-imm8会更有意义,因为符号扩展可能比零扩展更有用。

我才刚刚意识到标志来自 imul但是,仅签名。有趣的一点。


why does gcc not use mul for unsigned multiplication?

因为单操作数 mul/imul较慢(根据 Agner Fog's insn tables,在 Intel CPU 上是 2 微指令而不是 1 微指令。另请参见 标签 wiki)。他们还使用更多的寄存器:他们需要在 rax 中输入一个,并在 rdx:rax 中产生它们的输出,所以额外mov通常需要指令将数据移入/移出这些寄存器。

因此,imul r64, r64是比 mul r64 更好的选择,如果您不关心标志结果。

在 Intel CPU 上 imul r64,r64实际上比 mul r32 快.在其他一些 CPU 上情况并非如此,包括 AMD Bulldozer 系列,其中 64 位乘法运算速度稍慢。但是因为 mul r32将其结果放入 edx:eax而不是只有一个目标寄存器,它们在大多数情况下都不是彼此的直接替代品。

关于c - C 和 asm 中的 imulq 和 unsigned long long 溢出检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38253541/

相关文章:

c - 在 C 中存储和打印出结构的元素

c - 使用标准输入识别方向键

c++ - 在 OSX 上 Hook C++ 方法?

opencv - 优化SIMD直方图计算

assembly - BGE指令ARM

c - 仅将表达式的一部分标记为可能()/不太可能()是否合理

android - 是否可以在 SO 库中公开 C API 以访问 Android JAVA 方法?

gcc - GCC 生成的 ARM 和 x86 汇编代码的区别

gcc - 在哪里可以找到完整的 gcc 警告和错误列表?

c - 如何在 Xcode 中使用带有 -ansi -pedantic 的 gcc 进行编译?