回答
提出的想法編譯器:編譯在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();
}
有趣的是隻有GCC這樣做,鐺和ICC使用減爲零。 – Jester
爲什麼我們需要'adc edx,0'?如果*運算符*爲* 0,'neg'操作只設置進位標誌* –
@NagyRobi:NEG的標誌設置向後。它正在執行'edx = - (edx + CF)'而不是'edx = 0 - edx - CF',這就是爲什麼它使用ADC而不是SBB。 –
- 1. 32位寄存器將64位分成兩個32位寄存器
- 2. 如何將XMM 128位寄存器分成兩個64位整數寄存器?
- 3. 將兩個x86 32位寄存器存儲到128位xmm寄存器中
- 4. 在32位窗口下的64位寄存器
- 5. 64位的dll寄存器
- 6. 展位乘法器在64位寄存器的高32位中放置1
- 7. 負載32位寄存器
- 8. 零輸出64位寄存器的前32位
- 9. 大會:與32位寄存器的64位乘法
- 10. SSE 64位寄存器
- 11. 32位寄存器作爲8位寄存器
- 12. 如何將64位整數存儲爲二進制值,並且我如何讀取存儲的64位整數?
- 13. 64位十進制如何存儲在32位地址中?
- 14. 寄存器長度,內存大小和32/64位計算(MIPS)
- 15. x86中的額外寄存器64位
- 16. NASM - 如何將8位寄存器移動到完整的32位寄存器中?
- 17. 如何將兩個32位寄存器移動到一個64位?
- 18. 如何將兩個32位寄存器合併爲64位答案?
- 19. char數據類型如何在32位寄存器中表示?
- 20. 使用32位而不是64位來減少寄存器的數量
- 21. 到32位的值存儲到64位的存儲器上半部可變
- 22. 32位架構的DS段寄存器
- 23. 如何計算存儲N位所需的長整數(64位)?
- 24. 從DX:AX寄存器移動到單個32位寄存器
- 25. 如何在SQL Server中存儲未簽名的64位整數?
- 26. 如何64位整數分成兩個32位的整數
- 27. 彙編:使用兩個32位寄存器中的值劃分,就好像它們是一個64位整數
- 28. ASCII和64位寄存器彙編
- 29. 32位和64位值共享相同的寄存器空間嗎?
- 30. 如何在整數列中存儲64位字符串?
你忘了告訴你嘗試過或考慮什麼。有很多方法。首先,'-x = 0-x',所以你可以從0中減去。然後你也可以執行'-x = -1 * x'。做翻轉所有的二進制補充公式也是一種選擇。 – Jester