`testl` eax против eax? - PullRequest
       72

`testl` eax против eax?

112 голосов
/ 29 сентября 2008

Я пытаюсь понять некоторые сборки.

Сборка следующая, меня интересует строка 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? Я думаю, что специфика того, что этот код не важен, я просто пытаюсь понять тест с самим собой - не всегда ли значение будет истинным?

Ответы [ 8 ]

86 голосов
/ 29 сентября 2008

Значение test состоит в том, чтобы И аргументы вместе, и проверить результат на ноль. Так что этот код проверяет, равен ли EAX нулю или нет. je будет прыгать, если ноль.

Кстати, это генерирует команду меньшего размера, чем cmp eax, 0, поэтому компиляторы обычно делают это таким образом.

84 голосов
/ 29 сентября 2008

Проверяет, равен ли eax 0 или выше, или ниже. В этом случае переход выполняется, если eax равно 0.

32 голосов
/ 29 сентября 2008

Тестовая команда выполняет логическую операцию И между операндами, но не записывает результат обратно в регистр. Обновлены только флаги.

В вашем примере тестовый eax eax установит флаг нуля, если eax равен нулю, флаг знака, если установлен старший бит, а также некоторые другие флаги.

Команда Jump if Equal (je) переходит, если установлен нулевой флаг.

Вы можете перевести код в более читаемый код, например:

cmp eax, 0
je  somewhere

Имеет ту же функциональность, но требует больше байтов кода. Вот почему компилятор отправил тест вместо сравнения.

19 голосов
/ 25 июня 2016

test похож на and, за исключением того, что он записывает только флаги, оставляя оба своих ввода неизмененными. С двумя различными входами полезно проверять, все ли биты равны нулю, или установлен хотя бы один из них. (например, test al, 3 устанавливает ZF, если EAX кратно 4 (и поэтому оба его младших 2 бита обнуляются).


test eax,eax устанавливает все флаги точно так же, как cmp eax, 0 будет :

  • CF и OF очищены (AND / TEST всегда так делает; вычитание нуля никогда не приводит к переносу)
  • ZF, SF и PF в соответствии со значением в EAX. (a = a&a = a-0)

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


TEST меньше (не немедленный), а иногда и быстрее (может слиться макросом в моп со сравнением и ветвлением на большем количестве процессоров в большем количестве случаев, чем CMP). Это делает test предпочтительной идиомой для проверки регистра на ноль или нет .

Единственная распространенная причина использования CMP с немедленным 0 - это когда вы хотите сравнить с операндом памяти (например, cmpb $0, (%esi), чтобы проверить завершающий нулевой байт в конце строки C-стиля неявной длины ).


AVX512F добавляет kortestw k1, k2 и AVX512DQ / BW (Skylake, но не KNL) add ktestb/w/d/q k1, k2, которые работают с регистрами маски AVX512 (k0. .k7), но все равно устанавливают обычные флаги, как test, так же, как это делают целочисленные OR или AND инструкции.

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 (переход на ноль) является семантически релевантным синонимом.

5 голосов
/ 29 сентября 2008

Этот фрагмент кода взят из подпрограммы, которой был дан указатель на что-то, возможно, некоторую структуру или объект. Вторая строка разыменовывает этот указатель, извлекая значение из этой вещи - возможно, самого указателя или, может быть, просто int, сохраненного как его 2-й член (смещение +4). 3-я и 4-я строки проверяют это значение на ноль (NULL, если это указатель) и пропускают следующие несколько операций (не показаны), если оно равно нулю.

Тест на ноль иногда кодируется как сравнение с непосредственным литеральным нулевым значением, но компилятор (или человек?), Написавший это, мог подумать, что тестовая операция будет выполняться быстрее - с учетом всех современных вещей ЦП, таких как конвейерная обработка и переименование регистров. Это из того же пакета трюков, который содержит идею очистки регистра с помощью XOR EAX, EAX (которую я видел на чьем-то номерном знаке в Колорадо!), А не очевидного, но, возможно, более медленного MOV EAX, # 0 (я использую более старую запись ).

В asm, как perl, TMTOWTDI.

3 голосов
/ 29 сентября 2008

Если eax равен нулю, он выполнит условный переход, в противном случае он продолжит выполнение с 319e9

0 голосов
/ 14 марта 2017

мы могли видеть jg jle Если бы testl %edx,%edx. jle .L3 мы могли легко найти jle подходит (SF^OF)|ZF, если% edx равно нулю, ZF = 1, но если% edx не равно нулю и равно -1, то после testl OF = 0, и SF = 1, поэтому флаг = true, который реализует прыжок извините, мой английский плохой

0 голосов
/ 07 декабря 2016

В некоторых программах их можно использовать для проверки переполнения буфера. В самой верхней части выделенного пространства помещается 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
...