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

时间:2016-12-10 20:47:26

标签: assembly x86

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

例如:123456789123-123456789123

1 个答案:

答案 0 :(得分:10)

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

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

如果值在内存中开始,或者您想将原始值保留在寄存器中,请将目标xor-zero并使用SUB / SBB。 SysV x86-32 ABI在堆栈上传递args并在EDX:EAX中返回64位整数。这是neg_value_from_mem的{​​{1}}:

    ; 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]

如果您在寄存器中有值并且不需要就地结果,则可以使用clang3.9.1 -m32 -O3 does将寄存器设置为0 - 本身,设置CF iff输入非零。即与SUB相同的方式。请注意NEG,而不是延迟关键路径的一部分,所以这肯定比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

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

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

要更全面地了解编译器的作用,请查看这些函数的输出(xor-zeroing is cheap

#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();
}