2016-12-15 9 views
5

Używam zestawu x86 z biblioteką Irvine.Jaki jest najłatwiejszy sposób ustalenia, czy wartość rejestru jest równa zero, czy nie?

Jaki jest najłatwiejszy sposób sprawdzenia, czy wartość rejestru jest równa zero, czy nie?

Użyłem instrukcji cmp, ale szukam alternatywnego sposobu. To jest mój kodu za pomocą instrukcji CMP i rejestr ebx

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

ten "booleanizes" wartości, tworząc 0 lub 1 jak int ebx = !!ebx byłoby w C

+4

Używasz co najmniej instrukcji 80386 ('ebx' nie istnieje w 8086). Również odpowiedź zależy od instrukcji poprzedzających ten kod, obecność wartości zerowej może "przeciekać" według poprzednich instrukcji, oszczędzając jeszcze jednego na teście, w przeciwnym razie typowym idiomem jest 'test ebx, ebx', aby ustawić ZF. Następnie, aby zamienić ZF na wartość 0/1 w ebx, jest inna historia, w której "test" może ponownie nie być częścią optymalnego rozwiązania. Podejmij decyzję, czy chcesz ustalić, czy rejestr jest zerowy, czy też chcesz ustawić jakiś rejestr na 0/1 zgodnie z nim. # 'mov ebx, 0' =' xor ebx, ebx' kiedy możesz niszczyć flagi. – Ped7g

+0

Aby zilustrować mój punkt widzenia (ważność szczegółów), załóżmy, że ostatnią instrukcją modyfikującą flagę (flag AND ebx) była "neg ebx". Wtedy 'sbb ebx, ebx' ustawi' ebx' na -1, gdy 'ebx' będzie równe zero, i na 0 dla niezerowych wartości (dalsze' neg ebx' zamieni to w twój pierwotny 0/1 sposób pracy). – Ped7g

+0

nie działa ze mną. –

Odpowiedz

11

Prawdopodobnie „najłatwiejszy”, lub najprostszych, „nie-dbałość o szczegóły” odpowiedzieć jak określić jest:

; 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". 

Istnieje szczegółowe uzasadnienie dotyczące ustawiania flag itp. Zawiera również link do kolejnej podobnej odpowiedzi, ale uzasadnienie, dlaczego jest najlepszy sposób z punktu widzenia wydajności. :)

Jak ustawić inny rejestr (będę pick eax) na 1, gdy ebx wynosi zero, a na 0, gdy ebx jest niezerowe (nieniszczących sposobem ebx sama):

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) 

lub jak przerobić ebx się do 1/0 gdy ebx jest zero/niezerowe:

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 

lub jak konwertować sam ebx do 1/0 przy ebx jest zero/niezerowe, inny wariant (szybciej na "P6" na "Haswell" rdzeni):

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 

etc, etc ... To zależy od tego, co dzieje się przed testów, a także to, co naprawdę chcesz jako wynik testu, możliwe są różne sposoby (optymalne dla różnych sytuacji i optymalne dla różnych docelowych procesorów).


dodam jeszcze kilka bardzo typowe sytuacje ... licznik pętli w dół licząc od N do zera, do pętli n razy:

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

Jak przetwarzać 5 elementów word (16b) (dostęp do nich w tablicy [0], tablica [1], ...kolejności):

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 

Jeszcze jeden przykład, jakoś podobny do tego dużo:

Jak ustawić rejestr docelowy (eax w przykładzie) do ~ 0 (-1)/0 gdy ebx jest zero/niezerowe i masz już jakąś wartość 1 w rejestrze (ecx w przykładzie):

; 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 może wyglądać tak praktyczny jak 1 (dla celów indeksowania s co najmniej), ale -1 działa również jako pełna maska ​​bitowa dla dalszych operacji and/xor/or, więc czasami jest bardziej przydatny.

+0

Dzięki temu działa poprawnie :) Doceniam twoją pomoc. –

+2

Dla tego samego rejestru rejestracyjnego bez użycia dodatkowych regów: 'test ebx, ebx' /' setz bl'/'movzx ebx, bl' powinien być bardziej efektywny na P6 do Haswell (SBB to 2c opóźnienia/2 uop). 'neg/sbb/inc' jest bardziej efektywny na Pentium4 (slow setcc), i myślę, że są równi na Broadwell i później, i na AMD (wszystkie opóźnienia 1c), i ma mniejszy rozmiar kodu (szczególnie w 32-bitowym). –

+1

test/setz/movzx jest tym, co robi gcc6.2 '-mtune = haswell', jeśli nadasz mu sytuację, w której chce to zrobić na miejscu: https://godbolt.org/g/076BSI. Clang zeruje oddzielny rejestr, a następnie używa mov. –

-3

Można użyć:

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

Downvoting jak iść prosto do 'lub ebx, 0', nawet gdy już napisałem w komentarzu wspólnego idiom jest' test ebx, ebx'. Mógłbyś przynajmniej użyć 'lub ebx, ebx', aby natychmiast uniknąć' 0', ale 'test' jest jeszcze lepszy, ponieważ ten jest powszechnie używany przez kompilatory C/C++, więc procesor może mieć do tego dodatkowe optymalizacje, podczas gdy "lub" może być traktowane jako zwykła operacja arytmetyczna. – Ped7g

+1

Tak, widziałem twoją odpowiedź po tym, jak napisałem moją, nauczyłem się czegoś! Dzięki! – LeCalore

+3

@ Ped7g: Więcej informacji o tym, dlaczego 'lub' jest złym wyborem: http://stackoverflow.com/a/33724806/224132. Podsumowanie: Poza oczywistym rozmiarem kodu, 'test ebx, ebx' może makro-fuse z JCC i nie zapisuje EBX, wprowadzając dodatkowy cykl opóźnienia w łańcuchu zależności dla następnej rzeczy do odczytu EBX. Więc tak, procesory mają "dodatkowe optymalizacje" dla TEST/JCC vs. OR/JCC, a OR jest po prostu traktowane tak samo jak każdy inny OR. –

2

Użyj flagi, Luke
przetestować, czy rejestr jest zeru poprzez sprawdzenie flaga zero.
Jeśli rejestr uzyskał swoją wartość przez jakąś operację, która miała wpływ na flagi (a dokładniej na flagę zerową), to nie musisz nic robić, ponieważ flaga zero będzie już odzwierciedlać wartość zapisaną w tym rejestrze.

testu tylko w razie potrzeby
Jeśli nie możemy zagwarantować, że flagi zostały ustawione, będziesz musiał użyć tryb testowy.
Operacje te mają dwa smaki: niszczący i nieniszczący.

Można wyświetlić listę instrukcji i flagi to zmienia w: http://ref.x86asm.net -Więcej konkretnie na: http://ref.x86asm.net/coder32-abc.html

W mov i lea instrukcje nigdy zmieniać flagi, a więc potrzebują pomocy. Większość innych instrukcji ustawia co najmniej jedną flagę.

Nie tworzyć fałszywe zależności
Jeśli chcesz sprawdzić rejestr do zera, ale nie chcą zmieniać swoją wartość, należy użyć instrukcji test.
Nie powinieneś używać instrukcji or lub and do sprawdzania rejestru, ponieważ procesor może nie wiedzieć, że or/and może być używany niezniszczalnie i może nie być w stanie zastosować pewnych optymalizacji. Technicznym terminem jest "fałszywa zależność". Rejestr potrzebuje ebx i "myśli", że został ostatnio zmieniony, więc czeka na zakończenie.

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. 

Jeśli chcesz stan zerowy, aby odzwierciedlić w innym rejestrze, można po prostu mov ebx do innego rejestru.

Zmniejszenie wartości do logicznej
Jeśli chcesz zmniejszyć Zarejestrować się do wartości logicznej, należy użyć jednej z następujących sekwencji (True jeśli niezerowe, False inaczej):

; 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. 

Należy pamiętać, że używanie rejestrów bajtów może być problematyczne z powodu straganów związanych z "zapisami rejestru częściowego".

+0

Jak wskazuje odpowiedź Ped7g, 'neg ebx' ustawia flagi z' 0 - ebx' w jednym instrukcja (ale także zmienia oryginalną wartość). –

Powiązane problemy