2016-12-10 89 views
1

我已經存儲在EDX:EAX寄存器對一個64位整數。 我怎樣才能正確否定多少?如何取消存儲在32位寄存器對中的64位整數?

例如:123456789123-123456789123

+5

你忘了告訴你嘗試過或考慮什麼。有很多方法。首先,'-x = 0-x',所以你可以從0中減去。然後你也可以執行'-x = -1 * x'。做翻轉所有的二進制補充公式也是一種選擇。 – Jester

回答

10

提出的想法編譯器:編譯在32位模式int64_t neg(int64_t a) { return -a; }。當然,要求編譯器不同的方式都會有,或已經在EDX在內存中的初始值,在編譯器的選擇寄存器:EAX。查看所有三種方式on the Godbolt compiler explorer,與海灣合作委員會,鐺輸出ASM和MSVC(又名CL)。

當然也有很多方法來實現這一目標,但任何可能的順序將需要某種形式攜帶的由低到高在某些時候,所以沒有有效的辦法來避免SBB或ADC。


如果該值開始在內存,或者你想保持在寄存器中的原始值,異或爲零的目標,並使用SUB/SBB。 SysV x86-32 ABI在棧上傳遞參數,並在EDX:EAX中返回64位整數。這是clang3.9.1 -m32 -O3 does,爲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相同的方式。請注意,xor-zeroing is cheap,而不是延遲關鍵路徑的一部分,所以這肯定比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 

Cla即使對於就地情況也這樣做,即使這需要花費額外的mov ecx,edx。這對於具有零延遲mov reg,reg(Intel IvB +和AMD Zen)的現代CPU的延遲而言是最佳的,但對於融合域uops(前端吞吐量)或代碼大小的數量而言並非如此。


海灣合作委員會的序列是有趣的,而不是完全明顯。它保存了就地情況下的指令與叮噹聲,但是否則會更糟。

; 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 

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


對於什麼樣的編譯器做一個更全面的瞭解,看看它們的輸出爲這些功能(on 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(); 
} 
+3

有趣的是隻有GCC這樣做,鐺和ICC使用減爲零。 – Jester

+0

爲什麼我們需要'adc edx,0'?如果*運算符*爲* 0,'neg'操作只設置進位標誌* –

+0

@NagyRobi:NEG的標誌設置向後。它正在執行'edx = - (edx + CF)'而不是'edx = 0 - edx - CF',這就是爲什麼它使用ADC而不是SBB。 –

相關問題