簡體   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