`testl` eax проти eax?


118

Я намагаюся зрозуміти якусь збірку.

Збірка наступна, мене цікавить testlрядок:

000319df  8b4508        movl   0x08(%ebp), %eax  
000319e2  8b4004        movl   0x04(%eax), %eax  
000319e5  85c0          testl  %eax, %eax  
000319e7  7407          je     0x000319f0  

Я намагаюся зрозуміти цю точку testlміж %eaxі %eax? Я думаю, що специфіка цього коду не важлива, я просто намагаюся зрозуміти тест сам - чи не завжди це значення буде правдивим?

Відповіді:


91

Він перевіряє, чи eaxдорівнює 0, або вище, або нижче. У цьому випадку стрибок робиться, якщо eaxдорівнює 0.


2
Я вніс редагування, щоб перетворити цю популярну відповідь на кращу канонічну відповідь на те, "що це за TEST і про те, чим він відрізняється від CMP"? Дивіться мою власну відповідь далі для коментарів щодо семантичного значення синонімічних JE та JZ. Перегляньте мою редакцію, оскільки вона є досить головною, і це все ще ваша відповідь.
Пітер Кордес

@PeterCordes Я ціную намір, але я збираюся відновити вашу редакцію. 1. Ваш "голос" сильно відрізняється від мого, і зараз він читає набагато більше, як ваша відповідь, ніж мій. 2. Більш проблематичним є сміливе твердження, що прапори виходять точно так само між testі cmp. Так, я розумію, що це ваше переконання, засноване на ваших коментарях Коді. Однак викласти це на своїй посаді - справа інша; це не твердження, яке я готовий стояти осторонь, просто тому, що не знаю, чи воно однакове у всіх випадках.
Кріс Єстер-Янг

1
@PeterCordes Якщо я знайду вільний час, я хочу, щоб ця відповідь була більш канонічною. Я б написав це так, як пишу, але я дуже конкретний у тому, як я пишу речі. :-) Наприклад, я пишу je, jz, cmp, і test, а не JE, JZ, CMP або TEST. Я такий вибагливий.
Кріс Єстер-Янг

1
Я не намагався посилити власну відповідь. Я фактично забув, що сам відповів на це запитання, коли вніс цю редакцію, і лише після цього помітив. Я просто подивився на це після того, як хтось наткнувся на це, і те, що почалося, як невелика редакція, засніжило занадто багато. Без правопорушення, якщо ви хотіли відкотити його; це була лише пропозиція, і вона, безумовно, звучить як моя робота, а не ваша. Я візьму трохи того, що написав, і викладу це у власній відповіді.
Пітер Кордес

2
Нічого собі, після редагування моєї відповіді на це питання, щоб включити те, що я додав до вашого, я зрозумів, що майже точно дублював більшість того, що написав у червні. На жаль! Я оновив його, маючи більше міркувань, щоб створити резервну копію своєї заяви test a,aта cmp $0,aвстановити прапори однаково; дякую, що вказали, що це нетривіальна претензія. re: TEST vs test.: останнім часом я почав використовувати всі шапки, як-от посібники від Intel. Але коли я говорю про мнемоніку AT&T проти мнемоніки Intel, я використовую testbстиль для AT&T. IDK, якщо це допомагає читати.
Пітер Кордес

90

Сенс testаргументів аргументується AND і перевіряє результат на нуль. Тож цей код перевіряє, чи EAX дорівнює нулю чи ні. jeпідскочить, якщо нуль.

До речі, це генерує меншу інструкцію, ніж cmp eax, 0це є причиною того, що компілятори, як правило, роблять це так.


34

Інструкція тесту виконує логічну операцію AND між операндами, але не записує результат назад в регістр. Оновлено лише прапори.

У вашому прикладі тестовий eax, eax встановить нульовий прапор, якщо eax дорівнює нулю, знак-прапор, якщо встановлено найвищий біт, а також деякі інші прапори.

Інструкція Jump, якщо рівна (je), стрибає, якщо встановлено нульовий прапор.

Ви можете перевести код у більш читабельний код на зразок цього:

cmp eax, 0
je  somewhere

Він має той же функціонал, але вимагає трохи байтів більше кодового простору. Саме тому компілятор випробовував тест замість порівняння.


3
Насправді, cmp може не працювати там. Тобто, він працює для конкретного представленого випадку, але cmp впливає на прапори інакше, ніж у тесті, через те, що він є внутрішнім підрозділом замість і. Щось мати на увазі.
Cody Brocious

4
для тесту на нуль це абсолютно справедливо.
Нілс Піпенбрінк

3
Але ви не знаєте, що ще дивиться на прапори пізніше. Вплив на прапори дуже різний, тому це може бути проблемою і дуже часто.
Коді Броші

2
Ні, єдиними прапорами, які встановлюються іншим / методом /, є перенесення та переповнення, обидва встановлені на 0. Значення / значення / інших прапорів будуть відрізнятися, оскільки cmp використовує додаткові та тестові використання та.
Коді Брості

2
@CodyBrocious: test eax, eaxі cmp eax, 0обидва встановлюють усі прапори та встановлюють їх однакові значення. Обидві інструкції встановлюють усі прапори "відповідно до результату". Віднімання 0ніколи не може призвести до перенесення або переповнення. Ваш аргумент правильний для будь-якого іншого, крім 0, але не для 0.
Пітер Кордес,

22

testце як and, за винятком того, що він пише лише FLAGS, залишаючи обидва входи незміненими. З двома різними входами корисно перевірити, чи всі біти всі нульові, або якщо принаймні один встановлений. (наприклад, test al, 3встановлює ZF, якщо EAX кратне 4 (і при цьому обидва його низьких 2 біта нульові).


test eax,eaxвстановлює всі прапори точно так само, як cmp eax, 0і :

За винятком застарілого автофокусу (прапор допоміжного переносу, який використовується інструкціями ASCII / BCD). TEST залишає його невизначеним , але CMP встановлює "відповідно до результату" . Оскільки віднімання нуля не може спричинити перенесення з 4-го по 5-й біт, CMP повинен завжди очищати AF.


TEST менший (не негайний), а іноді і швидший (може макросплавлення перетворитись на порівняння та розгалуження на загальних процесорах у більшості випадків, ніж на CMP). Це робить testпереважну ідіому для порівняння регістра з нулем . Це оптимізація маточок, cmp reg,0яку ви можете використовувати незалежно від смислового значення.

Єдина поширена причина використання CMP з негайним 0 - це коли ви хочете порівняти з операндом пам'яті. Наприклад, cmpb $0, (%esi)для перевірки наявності кінцевого нульового байта в кінці рядка C стилю неявної довжини.


AVX512F додаєkortestw k1, k2 і AVX512DQ / BW (Skylake-X, але не KNL), додають ktestb/w/d/q k1, k2, які працюють на масках AVX512 (k0..k7), але все ще встановлюють звичайні FLAGS, як testце робить, так само, як це роблять цілі ORчи ANDінструкції. (На зразок SSE4 ptestабо SSE ucomiss: введення в домен SIMD і результат у цілому FLAGS.)

kortestw k1,k1є ідіоматичним способом розгалуження / cmovcc / setcc на основі результату порівняння AVX512, замінюючи SSE / AVX2 (v)pmovmskb/ps/pd+ testабо cmp.


Використання jzпорівняно jeможе бути заплутаним.

jzі jeє буквально однаковою інструкцією , тобто тим самим кодом в машинному коді. Вони роблять те саме, але мають різне смислове значення для людини . Розбиральники (і, як правило, вихід ASM від компіляторів) коли-небудь використовуватимуть лише один, тому семантична відмінність втрачається.

cmpі subвстановити ZF, коли їх два входи рівні (тобто результат віднімання дорівнює 0). je(стрибок, якщо рівний) - семантично релевантний синонім.

test %eax,%eax/ and %eax,%eaxзнову встановлює ZF, коли результат дорівнює нулю, але тесту на «рівність» немає. ZF після тесту не говорить про те, чи були два операнди рівними. Отже jz(стрибок, якщо нуль) - семантично релевантний синонім.


Я б розглядав можливість додавання основної інформації про testпобітну andоперацію, може бути не очевидним для людей, які тільки навчаються складанням (і лінуватися / не знати, щоб перевіряти посібник з інструкціями кожні 60 секунд;) :)).
Ped7g

1
@ Ped7g: Досить чесно, я думаю, що не завадить ставити все на цю відповідь, замість того, щоб залишити цю частину на інші відповіді. Додано AVX512 kortest*і ktest*поки я був на ньому.
Пітер Кордес

До речі, це в основному те саме, що я відповів на іншу версію того ж запитання , але я сказав більше матеріалів про продуктивність, наприклад, можливо, уникнути зривів, прочитаних у реєстрі, на старих процесорах сімейства P6, таких як Nehalem, переписавши реєстр з тим же значенням.
Пітер Кордес

@PeterCordes Це має бути прийнята відповідь: вичерпна та технічна. На відміну від прийнятої посади, це вгамовує цікавість та спрагу знань. Тримай це, сер.
програмісти

Слід зазначити, що PF встановлюється на паритет низьких 8 біт, що в даному випадку є AL.
ЄСМ

5

Цей фрагмент коду - з підпрограми, на яку було вказано вказівник на щось, можливо, якусь структуру чи об'єкт. Відміни 2-го рядка, що вказують, отримуючи значення з цієї речі - можливо, сам вказівник або, можливо, просто int, що зберігається як його другий член (зміщення +4). 3-й та 4-й рядки перевіряють це значення на нуль (NULL, якщо це вказівник) і пропускають наступні кілька операцій (не показано), якщо вони дорівнюють нулю.

Тест на нуль іноді кодується як порівняння з негайним буквальним нульовим значенням, але компілятор (чи людина?), Який це написав, міг би подумати, що тест-оп буде працювати швидше - з урахуванням усіх сучасних процесорів, таких як конвеєрний процес та реєстрація перейменування. Це з тієї ж сумки хитрощів, яка дотримується ідеї очищення реєстру з XOR EAX, EAX (яку я бачив на чиємусь номерному знаку в Колорадо!), А не з очевидного, але, можливо, повільніше MOV EAX, № 0 (я використовую старі позначення ).

У asm, як perl, TMTOWTDI.


3

Якщо eax дорівнює нулю, він виконає умовний стрибок, інакше він продовжить виконання на рівні 319e9


0

У деяких програмах їх можна використовувати для перевірки переповнення буфера. У самому верху виділеного простору розміщується 0. Після введення даних у стек, він шукає 0 на самому початку виділеного простору, щоб переконатися, що виділений простір не переповнений.

Він був використаний у виконанні stack0 вправ для експлуатації-вправ, щоб перевірити, чи він переповнений, і якщо там не було, а там нуль, він відобразив би "Спробуйте ще раз"

0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60                     
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c] 
0x08048415 <main+33>:   test   eax,eax                  ; checks if its zero
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500 
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret

Я не бачу, що цей конкретний випадок перевірки регістра на ненульовий додає до цього запитання. Особливо, коли cmp DWORD PTR [esp+0x5c], 0/ jz 0x8048427 <main+51>було б ефективніше, ніж окремий навантаження MOV, а потім тест. Це навряд чи поширений випадок використання для перевірки нуля.
Пітер Кордес

-4

ми могли б побачити JG , JLE Якщо testl %edx,%edx. jle .L3ми могли б легко знайти JLE костюм (SF^OF)|ZF, якщо% EDX дорівнює нулю, ZF = 1, але якщо% EDX не дорівнює нулю і -1, після testl, то OF = 0 і SF = 1, значить прапор = true, що реалізує стрибок .sorry, моя англійська мова погана

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.