如何否定存储在32位寄存器对中的64位整数?

Nag*_*obi 1 x86 assembly

我在寄存器对中存储了一个64位整数.我怎样才能正确否定这个数字?EDX:EAX

例如:123456789123-123456789123.

Pet*_*des 10

向编译器询问想法:int64_t neg(int64_t a) { return -a; }以32位模式编译.当然,询问编译器的不同方式将在内存中,编译器选择寄存器或已在EDX:EAX中具有起始值.在Godbolt编译器资源管理器中查看所有三种方式,使用gcc,clang和MSVC(又名CL)的asm输出.

当然有很多方法可以实现这一点,但任何可能的序列在某些时候都需要某种从低到高的进位,因此没有有效的方法来避免SBB或ADC.


如果值在内存中开始,或者您希望将原始值保留在寄存器中,则将目标xor-zero并使用SUB/SBB.SysV x86-32 ABI在堆栈上传递args并在EDX:EAX中返回64位整数.这就是clang3.9.1的-m32 -O3作用,用于neg_value_from_mem:

    ; optimal for data coming from memory: just subtract from zero
    xor     eax, eax
    xor     edx, edx
    sub     eax, dword ptr [esp + 4]
    sbb     edx, dword ptr [esp + 8]
Run Code Online (Sandbox Code Playgroud)

如果在寄存器中有值并且不需要就地结果,则可以使用NEG将寄存器设置为0 - 本身,如果输入为非零则设置CF. 即SUB会采用相同的方式.请注意,xor-zeroing是便宜的,而不是延迟关键路径的一部分,所以这肯定比gcc的3指令序列(下面)更好.

    ;; partially in-place: input in ecx:eax
    xor     edx, edx
    neg     eax         ; eax = 0-eax, setting flags appropriately
    sbb     edx, ecx    ;; result in edx:eax
Run Code Online (Sandbox Code Playgroud)

Clang甚至为就地案件做到这一点,即使这需要额外费用mov ecx,edx.这对于具有零延迟mov reg,reg(Intel IvB +和AMD Zen)的现代CPU的延迟是最佳的,但不适用于融合域uops(前端吞吐量)或代码大小的数量.


gcc的序列很有趣,并不是很明显.它为就地案件保存了一个指令与铿锵声,但情况更糟.

    ; gcc's in-place sequence, only good for in-place use
    neg     eax
    adc     edx, 0
    neg     edx
       ; disadvantage: higher latency for the upper half than subtract-from-zero
       ; advantage: result in edx:eax with no extra registers used
Run Code Online (Sandbox Code Playgroud)

不幸的是,gcc和MSVC都使用它,即使xor-zero + sub/sbb会更好.


有关编译器的更完整图片,请查看这些函数的输出(在godbolt上)

#include <stdint.h>

int64_t neg_value_from_mem(int64_t a) {
     return -a;
}

int64_t neg_value_in_regs(int64_t a) {
    // The OR makes the compiler load+OR first
    // but it can choose regs to set up for the negate
    int64_t reg = a | 0x1111111111LL;
    // clang chooses mov reg,mem   / or reg,imm8 when possible,
    // otherwise     mov reg,imm32 / or reg,mem.  Nice :)
    return -reg;
}

int64_t foo();
int64_t neg_value_in_place(int64_t a) {
    // foo's return value will be in edx:eax
    return -foo();
}
Run Code Online (Sandbox Code Playgroud)

  • 有趣的是,只有gcc这样做,clang和icc使用从零减去. (3认同)