2016-12-15 62 views
5

我正在使用x86彙編和Irvine庫。確定寄存器值是否等於零的最簡單方法是什麼?

什麼是最簡單的方法來檢查寄存器值是否等於零?

我用cmp指令,但我正在尋找替代方法。 這是使用CMP指令我的代碼和寄存器是EBX

cmp ebx,0 
    je equ1 
    mov ebx,0 
    jmp cont 
equ1: 
    mov ebx,1 
    jmp cont 
cont: 
    exit 

這種 「booleanizes」 的值,產生一個0或1像int ebx = !!ebx將在C.

+4

您至少使用80386條指令(在8086上不存在'ebx')。此外,答案取決於此代碼前面的指令,零值的存在可能會按照前面的指令進行「泄漏」,從而爲您節省更多的測試時間,否則常用的習慣用法是'test ebx,ebx'來設置ZF。然後把ZF變成0/1的值是ebx的另一個故事,那裏的'測試'可能再次不是最佳解決方案的一部分。下定決心,無論你想確定寄存器是否爲零,或者你想根據它設置一些寄存器爲0/1。 #'mov ebx,0' ='xor ebx,ebx'當你可以摧毀旗幟時。 – Ped7g

+0

爲了說明我的觀點(細節的重要性),讓我們假設在這之前的最後一個(標誌和ebx)修改指令是'neg ebx'。然後'sbb ebx,ebx'會在'ebx'等於零時將'ebx'設置爲-1,對於非零值則設爲0(進一步'neg ebx'會將其轉換爲原來的0/1工作方式)。 – Ped7g

+0

沒有與我一起工作。 –

回答

11

大概是「最簡單」,或者簡單地說,「不關心細節有關」回答如何確定是:

; here ebx is some value, flags are set to anything 
    test ebx,ebx ; CF=0, ZF=0/1 according to ebx 
    jz  whereToJumpWhenZero 
    ; "non-zero ebx" will go here 

    ; Or you can use the inverted "jnz" jump to take 
    ; a branch when value was not zero instead of "jz". 

有一個詳細的answer from Peter Cordes to "testl eax against eax?" question關於設置標誌的推理等。還有一個鏈接到另一個類似的答案,但推斷爲什麼它是從性能角度來看最好的方式。 :)

如何設置一些其他的寄存器(我會選擇eax)到1時ebx是零,而設置爲0時ebx非零(非破壞性的方式爲ebx本身):

xor eax,eax ; eax = 0 (upper 24 bits needed to complete "al" later) 
    test ebx,ebx ; test ebx, if it is zero (ZF=0/1) 
    setz al  ; al = 1/0 when ZF=1/0 (eax = 1/0 too) 

或如何ebx轉換成自己當1/0爲ebx零/非零:

neg ebx  ; ZF=1/0 for zero/non-zero, CF=not(ZF) 
    sbb ebx,ebx ; ebx = 0/-1 for CF=0/1 
    inc ebx  ; 1 when ebx was 0 at start, 0 otherwise 

或如何ebx本身轉化爲1/0時,ebx是零/非零的,其他的變種(快上「P6」爲「Haswell的」芯):

test ebx,ebx ; ZF=1/0 for zero/non-zero ebx 
    setz bl  ; bl = 1/0 by ZF (SETcc can target only 8b r/m) 
    movzx ebx,bl ; ebx = bl extended to 32 bits by zeroes 

等等,等等...這取決於你在測試之前發生了什麼,還你真正想要的作爲測試的輸出,存在多種可能的方式(對於不同的情況是最佳的,對於不同的目標CPU是最佳的)。


我會加幾個非常常見的情況......一個反向環向下計數從N到零,循環N次:

mov ebx,5 ; loop 5 times 
exampleLoop: 
    ; ... doing something, preserving ebx 
    dec ebx 
    jnz exampleLoop ; loop 5 times till ebx is zero 

如何處理的word 5個元素( 16b)數組(在數組[0],數組[1]中訪問它們,...順序):

mov ebx,-5 
    lea esi,[array+5*2] 
exampleLoop: 
    mov ax,[esi+ebx*2] ; load value from array[i] 
    ; process it ... and preserve esi and ebx 
    inc ebx 
    jnz exampleLoop ; loop 5 times till ebx is zero 

再舉一個例子,我不知何故像這樣的很多:

如何設置目標寄存器(eax例子中)到〜0(-1)/ 0時ebx是零/非零並且已經有值1在某些寄存器(例如在ecx):

; ecx = 1, ebx = some value 
    cmp ebx,ecx ; cmp ebx,1 => CF=1/0 for ebx zero/non-zero 
    sbb eax,eax ; eax = -1 (~0)/0 for CF=1/0 ; ebx/ecx intact 

-1可能看起來儘可能的1(用於索引目的至少),但-1還可以作爲完整位掩碼用於進一步的操作,所以有時它更方便。

+0

感謝它正常工作:) 我感謝你的幫助。 –

+2

對於沒有使用任何額外regs的同一個寄存器的情況:'test ebx,ebx' /'setz bl' /'movzx ebx,bl'應該在P6和Haswell上更高效(SBB是2c latency/2 uop)。 'neg/sbb/inc'在Pentium4(slow setcc)上效率更高,我認爲它們在Broadwell及其後的版本和AMD(所有1c延遲)上都是相同的,並且代碼量較小(特別是在32位)。 –

+1

test/setz/movzx是gcc6.2'-mtune = haswell'所做的,如果你給它一個它想在原地執行的情況:https://godbolt.org/g/076BSI。鐺零一個單獨的寄存器,然後使用一個mov。 –

-3

你可以使用:

or ebx, 0 ;This does nothing, just triggers the zero flag if ebx is zero 
jnz notZero 
or ebx, 1 ;ebx was zero, then ebx is 1 
notZero: 
+3

即使我已經在評論中發佈了'or ebx,0',直接進行downvoting,常見的習慣用法是'test ebx,ebx'。您可以至少使用'或ebx,ebx'來避免立即執行'0',但'test'仍然更好,因爲C/C++編譯器廣泛使用它,所以CPU可以進行額外的優化。 '或'可以被視爲普通的算術運算。 – Ped7g

+1

是的,我發佈我的東西后,我看到了你的答案,學到了一些東西!謝謝! – LeCalore

+3

@ Ped7g:更多關於爲什麼'或'是這個不好的選擇:http://stackoverflow.com/a/33724806/224132。總結:除了明顯的代碼大小之外,'test ebx,ebx'可以與JCC宏觀融合,並且它不會寫入EBX,從而在依賴鏈中引入一個額外的延遲週期,以便下一次讀取EBX。所以是的,CPU對TEST/JCC與OR/JCC有「額外的優化」,並且OR與其他OR相同。 –

2

使用標誌盧克
您通過檢查零標誌來測試寄存器是否爲零。
如果寄存器通過一些影響標誌的操作(或者更確切地說是零標誌)獲得了它的值,那麼你不必做任何事情,因爲零標誌已經反映了存儲在該寄存器中的值。

測試僅在需要
如果你不能保證標誌已經設置,你必須使用一個測試操作。
這些操作有兩種類型:破壞性和非破壞性。

你可以看到的指令,並改變在標誌列表:http://ref.x86asm.net - 更具體地說,在:http://ref.x86asm.net/coder32-abc.html

movlea指令從未改變的標誌,因此需要援助。大多數其他指令至少設置一個標誌。

如果你需要測試寄存器零不要創建虛假的依賴
,但不想改變它的價值,您使用test指令。
您不應該使用orand指令來檢查寄存器,因爲CPU可能不知道or/and可以非破壞性方式使用,並且可能無法應用某些優化。 這是一個'錯誤的依賴'的技術術語。該寄存器需要ebx,並且'認爲'它最近被更改了,因此它等待結果完成。

test ebx, ebx ; <-- CPU knows ebx was not altered, no stalls on subsequent reads. 
or ebx, ebx ; <-- CPU 'thinks' ebx was changed, stall on subsequent read. 

如果你想零狀態到另一個寄存器反映,你可以簡單地mov EBX到另一個寄存器。

如果你想減少寄存器爲布爾(真,如果不爲零,否則返回False),您使用以下序列中的一個減少的值到布爾

; ebx holds the value to reduce to a boolean. 
; eax is an unused register. 
xor eax, eax ; eax = 0 
sub eax, ebx ; eax = 0 - ebx; CF (carry flag) = 1 if ebx <> 0 
sbb ebx, ebx ; ebx = ebx - ebx - CF 
       ; <<-- ebx = -1 if non zero, 0 if zero 
xor eax, eax ; eax = 0 
sub eax, ebx ; eax = - ebx; CF = 1 if ebx <> 0 
adc ebx, eax ; ebx = (ebx + -ebx) aka 0 + CF 
       ; <<== ebx = 1 if non zero, 0 if zero 
test ebx, ebx ; force ZF to be correct 
setnz al  ; Store 1 if non-zero, 0 otherwise in byte register AL. 

請注意,由於與「部分寄存器寫入」相關的停頓,字節寄存器的使用可能會產生問題。

+0

Ped7g的回答指出,'neg ebx'將'0 - ebx'中的標誌設置爲1指令(但也會修改原始值)。 –

相關問題