2008-09-29 3 views
101

일부 어셈블리를 이해하려고합니다. 다음과 같이`testl` eax against eax?

조립, 나는 testl 라인에 관심이 :

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

나는 %eax%eax 사이 testl의 점을 이해하기 위해 노력하고 있어요? 이 코드가 중요하지 않은 것에 대한 세부 사항은, 나는 그 자체로 테스트를 이해하려고 노력하는 것입니다. 가치가 항상 사실이 아닐까요?

답변

73

eax이 0 이상인지 여부를 테스트합니다. 이 경우 eax이 0 일 경우 점프가 수행됩니다.

+0

나는이 일반적인 대답을 "이 테스트의 목적은 무엇이며, CMP와 다른 점은 무엇인가"에 대한 더 나은 정식 답변으로 바꾸기 위해 편집했습니다. 동의어 JE와 JZ의 의미 론적 의미에 대한 의견을 보려면 내 대답을 참조하십시오. 꽤 편집 적이기 때문에 편집을 검토하십시오. 여전히 답변입니다. –

+0

@PeterCordes 의도를 고맙게 생각하지만 편집을 취소하겠습니다. 1. 당신의 "목소리"는 내 것과 매우 다르며, 지금은 내 대답보다 훨씬 더 많이 읽습니다. 2. 문제는 플래그가'test'와'cmp' 사이에서 똑같은 방식으로 나오게된다는 대담한 주장입니다. 예, 코디에 대한 귀하의 의견에 근거한 귀하의 신념입니다. 그러나 그것을 내 게시물에 넣는 것은 다른 문제입니다. 그것은 모든 경우에 동일하다는 것을 알지 못하기 때문에 내가 서서 기꺼이 주장하는 주장이 아닙니다. –

+0

@PeterCordes "정식"답변을 원한다는 것을 이해합니다. 그러나 대답은 허용 된 답변에 피기 백하기보다는 자신의 장점으로 수영하거나 가라 앉혀 야한다고 생각합니다. 나는 [부동 소수점 입문서] (http://stackoverflow.com/a/27030789/13)와 같이 아주 대중적인 질문에 대한 나의 대답을 위해 그렇게해야만했다. 응답은 이미 500+ upvotes에 다른 응답이 있었다. –

82

test의 의미는 인수를 AND로 AND 한 결과가 0인지 확인하는 것입니다. 따라서이 코드는 EAX가 0인지 아닌지를 테스트합니다. je은 0이면 점프합니다.

현재로서는 컴파일러가 일반적으로 이런 식으로 수행하는 이유는 cmp eax, 0보다 작은 명령어를 생성합니다.

3

EAX가 조건부 점프를 수행 제로인 경우, 그렇지 않으면 319e9

31

시험 명령어 피연산자 사이의 논리 AND 동작을 수행하지만, 다시 레지스터에 그 결과를 기록하지 않는에서 실행을 계속한다. 플래그 만 갱신됩니다.

예에서 test eax, eax는 eax가 0이면 0 플래그를 설정하고 최상위 비트가 설정된 경우 sign-flag와 다른 플래그도 설정합니다.

제로 플래그가 설정되면 점프 경우 같음 (je) 명령이 점프합니다.

이 같은 더 읽을 수있는 코드에 코드를 번역 할 수 있습니다 : 동일한 기능을 가지고 있지만, 일부 바이트 더 많은 코드 공간이 필요합니다

cmp eax, 0 
je somewhere 

합니다. 이것이 컴파일러가 비교 대신 테스트를 내 보낸 이유입니다.

+3

실제로 cmp가 작동하지 않을 수 있습니다. 즉, 제시된 특정 사례에 대해 작동하지만 cmp는 test와는 다른 방식으로 플래그에 영향을줍니다. 명심해야 할 것이 있습니다. –

+4

0에 대한 테스트는 완벽하게 유효합니다. –

+3

하지만 나중에 플래그가 어떤 것인지 알 수 없습니다. 플래그에 미치는 영향은 매우 다르기 때문에 문제가 될 수 있으며 매우 자주 발생합니다. –

5

이 스 니펫은 무언가 (아마도 struct 또는 object)에 대한 포인터가 지정된 서브 루틴에서 가져온 것입니다. 두 번째 라인은 포인터를 역 참조하고, 그 것으로부터 값을 가져옵니다. 아마도 그것 자체는 포인터 또는 어쩌면 단지 int인데, 그것의 두 번째 멤버 (offset +4)로 저장됩니다. 세 번째와 네 번째 줄에서는이 값을 0으로 테스트하고 (포인터 인 경우 NULL) 다음의 몇 가지 작업 (표시되지 않음)이 0이면 건너 뜁니다.

0의 테스트는 때로는 리터럴 0 값과의 비교로 코딩되지만, 이것을 작성한 컴파일러 (또는 인간)는 testl op가 더 빠르게 실행될 것이라고 생각했을 수 있습니다. 파이프 라이닝 및 레지스터 이름 변경과 같은 기능을합니다. 이것은 XOR EAX, EAX (콜로라도에있는 다른 사람의 번호판에서 본!)를 사용하여 레지스터를 지우는 아이디어를 담고있는 똑같은 트릭 백에서 나온 것입니다. 분명하지만 다소 느린 MOV EAX, # 0 (이전 표기법을 사용합니다.).

asm에서 perl과 마찬가지로 TMTOWTDI.

15

test은 모두 과 비슷하지만 FLAGS 만 쓰고 나머지 입력은 모두 수정되지 않습니다. 두 개의 입력이 다른 인 경우 일부 비트가 모두 0이거나 하나 이상 설정되어 있는지 테스트하는 데 유용합니다. (예 :test al, 3은 EAX가 4의 배수이면 (따라서 낮은 2 비트가 모두 0 인 경우) ZF를 설정합니다.


test eax,eax 세트의 모든 플래그 정확히 cmp eax, 0하는 것과 같은 방법 :

  • CF 및 클리어는 (AND/TEST는 항상 그 수행하고, 영을 빼는 것은 결코 캐리 생산 없음)
  • 을 ZF, SF 및 PF를 EAX의 값에 따라 계산합니다. (a = a&a = a-0)
  • (ASCII/BCD 명령에 의해 사용되지 않는 AF (보조 캐리 플래그). TEST leaves it undefined 제외

하지만 CMP sets it "according to the result". 5 비트 4에서 캐리를 생성 할 수 제로를 감산하기 때문에, CMP는 항상 AF를 제거해야합니다).


TEST가 작은 (즉각적인) 때로는 빠르게 (CMP보다 더 많은 경우에 이상의 CPU에 대한 비교 및 ​​분기의 UOP에 할 수있는 매크로 퓨즈). That makes test the preferred idiom for testing a register for zero or not.

즉시 0으로 CMP를 사용하는 가장 일반적인 이유는 메모리 피연산자와 비교할 때입니다 (예 : cmpb $0, (%esi)은 암시 적 길이의 C 스타일 문자열 끝에 0 바이트가 종료되는지 확인) .


AVX512F는 test이하는 것처럼 AVX512 마스크 레지스터 (k0..k7)하지만 여전히 설정 일반 FLAGS에서 작동 ktestb/w/d/q k1, k2, 같은 방법으로 추가 kortestw k1, k2을 추가하고 AVX512DQ/BW (스카이 레이크하지만 KNL) 그 정수 OR 또는 AND 지침을 참조하십시오.

kortestw k1,k1는 SSE/AVX2에게 (v)pmovmskb/ps/pd + testcmp 또는 교체, 비교 결과에 기초 AVX512/cmovcc/setcc를 분기하는 관용적 인 방법이다.


이용 jzje는 혼란 스러울 수 있습니다.

jz and je are literally the same instruction, 즉 기계 코드의 opcode입니다. 그들은 똑같은 일을하지만 인간에 대해서는 다른 의미 론적 의미를 가지고있다.. 디스어셈블러 (일반적으로 컴파일러의 asm 출력)는 오직 하나만 사용하므로 의미를 구분하지 못합니다.

cmpsub은 두 입력이 동일 할 때 (즉, 감산 결과가 0 일 때) ZF로 설정됩니다. je (동일하면 점프)은 의미 적으로 관련된 동의어입니다.

test %eax,%eax/and %eax,%eax은 결과가 0 일 때 ZF를 다시 설정하지만 "동등성"테스트는 없습니다. 테스트 후 ZF는 두 피연산자가 같은지 여부를 알려주지 않습니다. 따라서 jz (0으로 점프)은 의미 적으로 관련된 동의어입니다.

+0

'test '에 대한 기본적인 정보를 비트와'및'연산으로 추가하는 것을 고려할 것입니다. 단지 어셈블리를 배우는 사람들에게는 명백하지 않을 수 있습니다 (그리고 게으름/알지 못함 60 초마다 지침 참조 안내서 확인) :). – Ped7g

+1

@ Ped7g : 충분히 공정한, 나는 다른 부분에 그 부분을 남겨 두는 대신에이 답안에 모든 것을 두는 것을 상처주지 않는다고 생각합니다. 내가 AVX512'kortest *'와'ktest *'를 추가했습니다. –

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 <[email protected]> 
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 <[email protected]> 
0x08048425 <main+49>: jmp 0x8048433 <main+63> 
0x08048427 <main+51>: mov DWORD PTR [esp],0x8048529 
0x0804842e <main+58>: call 0x804832c <[email protected]> 
0x08048433 <main+63>: leave 
0x08048434 <main+64>: ret 
+0

레지스터가 0이 아닌지 확인하는 특정 사례가이 Q & A에 추가되는 것을 나는 알지 못합니다. 특히'cmp DWORD PTR [esp + 0x5c], 0' /'jz 0x8048427 '이 별도의 MOV-로드보다 효율적이었을 것입니다. 이것은 0을 검사하는 일반적인 경우는 거의 없습니다. –

-2

을 표시 "다시 시도"것 우리가 쉽게 JLE을 찾을 수 testl %edx,%edx. jle .L3 경우 JG, JLE는 %의 EDX는, ZF = 1 0 인 경우, 소송 (SF^OF)|ZF이지만 %의 EDX는 0이 아닌 그리고, testl 후, -1 인 경우 =의 0, SF = 1이므로 flag = true가 점프를 구현합니다. .sorry, 내 영어가 약하다