繁体   English   中英

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

[英]How do I negate a 64-bit integer stored in a 32-bit register pair?

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

例如: 123456789123-123456789123

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

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


如果该值在 memory 中开始,或者您想将原始值保留在寄存器中,请对目标进行异或零处理并使用 SUB/SBB。 SysV x86-32 ABI 在堆栈上传递参数并在 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]

如果您在寄存器中有值并且不需要就地结果,您可以使用NEG将寄存器设置为 0 - 本身,如果输入不为零,则设置 CF。 即与 SUB 相同的方式。 请注意,异或归零很便宜,并且不是延迟关键路径的一部分,因此这绝对比 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 的序列很有趣,但不是很明显。 它为就地情况保存了一条指令 vs. clang,但否则情况会更糟。

    ; 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 会更好。


要更全面地了解编译器的作用,请查看这些函数的输出( 在 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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM