我如何移動:MOV EAX, EBX不使用MOV指令?
uj5u.com熱心網友回復:
您可以使用lea簡單的暫存器尋址模式作為較慢的mov(沒有移動消除并且在 Ice Lake 之前在英特爾上的更少埠上運行),盡管它仍然是一條指令。
# nasm -f elf64 foo.asm && objdump -drwC -Mintel foo.o
0000000000000000 <.text>:
0: 89 c8 mov eax,ecx
2: 8d 01 lea eax,[rcx] # in 64-bit code,
4: 67 8d 01 lea eax,[ecx] # don't use 32-bit address size
除了速度較慢之外,某些暫存器(RSP/R12 和 RBP/R13)還需要額外的代碼大小。(我使用了 64 位運算元大小,因此它們都需要 REX 前綴,例如 RSP 和 R12 機器代碼排列在一起,因為它們都需要使用 SIB 位元組。
10: 48 89 fe mov rsi,rdi
13: 48 8d 37 lea rsi,[rdi]
16: 48 8d 34 24 lea rsi,[rsp]
1a: 49 8d 34 24 lea rsi,[r12]
1e: 48 8d 75 00 lea rsi,[rbp 0x0] # source had just [rbp]
22: 49 8d 75 00 lea rsi,[r13 0x0]
在其他模式下,同樣的事情當然是可能的,lea eax, [ecx]用作 2 位元組指令。(或者 32 位暫存器在 16 位模式下的 4 個位元組。即使你只想要 16 位暫存器,16 位模式需要 32 位地址大小用于源暫存器而不是 [bx|bp] [si|di ] 因為 16 位尋址模式編碼限制。)
push/pop也是一個選項,可以只用 2 個位元組的機器代碼復制 64 位暫存器,而不是通常的 3 個位元組。
或者如果你不關心源暫存器(實際上是移動而不是復制),你可以xchg,當 EAX 是兩個暫存器之一時,它的編碼更短
30: 52 push rdx
31: 58 pop rax
32: 48 89 d0 mov rax,rdx
35: 48 8d 02 lea rax,[rdx]
38: 87 f1 xchg ecx,esi # opcode ModRM form
3a: 91 xchg ecx,eax # EAX special case
這對 code-golf 很有用。
其他愚蠢的計算機技巧包括imul和來自評論的雙班建議
立即數
imul1。對于 16 位暫存器,計數 = 16 的SHLD或 SHRD 是可能的。(x86 標量整數移位屏蔽了
& 31除 64 位以外的任何運算元大小的計數,因此這不能移位 32 位或 64 位完整暫存器的所有位,只能移位 16 位部分暫存器。和 shld /shrd 在 386 中是新的,所以 16 位暫存器總是“部分的”。)
40: 6b f1 01 imul esi,ecx,0x1
43: 66 0f a4 ce 10 shld si,cx,0x10 # SI = CX. CX unchanged.
48: 66 0f ac ce 10 shrd si,cx,0x10
4d: 0f a4 ce 20 shld esi,ecx,0x20 # nope, equivalent to a shift by 0
或者,如果您想考慮多個指令,您可以像 Yuri 建議的那樣將目標和add、or、 或異或歸零xor。零是 ,|和的單位元素^。
效率更低的是將 AND 轉化為全 1,即 的標識元素&。(對 EAX 舊值的錯誤依賴,并且它不是異或歸零,因此不能像 Sandybridge 系列 CPU 那樣消除(沒有執行單元)。還有更大的代碼大小)
or eax, -1
and eax, ecx
(Godbolt 編譯器資源管理器 NASM 源代碼和反匯編,用于本答案中的反匯編)
uj5u.com熱心網友回復:
首先,清理一個暫存器上的資料XOR EAX, EAX,然后執行OR EAX, EBX
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/326209.html
