`testl` eax chống lại eax?


118

Tôi đang cố gắng hiểu một số lắp ráp.

Việc lắp ráp như sau, tôi quan tâm đến testldòng:

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

Tôi đang cố gắng hiểu điểm testlgiữa %eax%eax? Tôi nghĩ rằng các chi tiết cụ thể của mã này không quan trọng, tôi chỉ đang cố gắng hiểu bài kiểm tra với chính nó - giá trị không phải lúc nào cũng đúng?

Câu trả lời:


91

Nó kiểm tra xem eaxlà 0, hoặc cao hơn hoặc thấp hơn. Trong trường hợp này, bước nhảy được thực hiện nếu eaxlà 0.


2
Tôi đã thực hiện một chỉnh sửa để biến câu trả lời phổ biến này thành một câu trả lời chính tắc tốt hơn cho "TEST này nói về điều gì và nó khác với CMP như thế nào", điều này được ngụ ý. Xem thêm câu trả lời của riêng tôi để biết nhận xét về ý nghĩa ngữ nghĩa của JE và JZ đồng nghĩa. Vui lòng xem lại bản chỉnh sửa của tôi vì nó khá lớn và nó vẫn là câu trả lời của bạn.
Peter Cordes

@PeterCordes Tôi đánh giá cao ý định này, nhưng tôi sẽ hoàn nguyên bản chỉnh sửa của bạn. 1. "Giọng nói" của bạn rất khác với của tôi, và ngay bây giờ nó đọc rất giống câu trả lời của bạn hơn là của tôi. 2. Vấn đề hơn là khẳng định táo bạo rằng các lá cờ đi ra chính xác theo cùng một cách giữa testcmp. Vâng, tôi hiểu đó là niềm tin của bạn dựa trên những nhận xét của bạn với Cody. Tuy nhiên, đưa nó vào bài viết của tôi là một vấn đề khác; đó không phải là một sự khẳng định mà tôi sẵn sàng chấp nhận, đơn giản vì tôi không biết liệu nó có giống nhau trong mọi trường hợp hay không.
Chris Jester-Young

1
@PeterCordes Nếu tôi thấy có chút thời gian rảnh rỗi, tôi muốn biến câu trả lời này trở nên chính thống hơn. Tuy nhiên, tôi sẽ viết nó như tôi viết nó, và tôi khá đặc biệt về cách tôi viết mọi thứ. :-) Ví dụ, tôi sẽ viết je,jz , cmp, và test, và không JE, JZ, CMP, hoặc TEST. Tôi kén chọn như vậy.
Chris Jester-Young

1
Tôi không cố gắng tăng câu trả lời của chính mình. Tôi thực sự quên rằng tôi đã tự trả lời câu hỏi này khi thực hiện chỉnh sửa đó và chỉ nhận thấy sau đó. Tôi chỉ nhìn vào điều này sau khi ai đó va vào nó, và những gì bắt đầu khi một bản chỉnh sửa nhỏ bị trượt tuyết thành quá nhiều. Không có hành vi xúc phạm nào mà bạn muốn quay trở lại; nó chỉ là một gợi ý và nó chắc chắn đọc giống như tác phẩm của tôi, không phải của bạn. Tôi sẽ lấy một số những gì tôi đã viết và đưa nó vào câu trả lời của riêng tôi.
Peter Cordes

2
Chà, sau khi chỉnh sửa câu trả lời của tôi cho câu hỏi này để bao gồm những gì tôi đã thêm vào của bạn, tôi nhận ra rằng tôi đã sao chép gần như chính xác hầu hết những gì tôi đã viết vào tháng Sáu. Giáo sư! Tôi đã cập nhật nó với nhiều lý do hơn để sao lưu tuyên bố của tôi test a,acmp $0,ađặt cờ giống hệt nhau; cảm ơn vì đã chỉ ra rằng đó là một tuyên bố không tầm thường. re: TEST vs test.: gần đây tôi đã bắt đầu sử dụng tất cả các chữ viết hoa như sách hướng dẫn của Intel. Nhưng khi tôi nói về kỹ năng ghi nhớ của AT&T so với kỹ thuật ghi nhớ của Intel, tôi sử dụng testbphong cách cho AT&T. IDK nếu điều đó giúp dễ đọc.
Peter Cordes

90

Ý nghĩa của testlà AND các đối số cùng nhau và kiểm tra kết quả là 0. Vì vậy, mã này kiểm tra nếu EAX bằng 0 hay không. jesẽ nhảy nếu bằng không.

BTW, điều này tạo ra một lệnh nhỏ hơn cmp eax, 0đó là lý do mà các trình biên dịch nói chung sẽ làm theo cách này.


34

Lệnh kiểm tra thực hiện phép toán AND logic giữa các toán hạng nhưng không ghi lại kết quả vào một thanh ghi. Chỉ những lá cờ được cập nhật.

Trong ví dụ của bạn về eax thử nghiệm, eax sẽ đặt cờ 0 nếu eax bằng 0, cờ dấu nếu đặt bit cao nhất và một số cờ khác.

Lệnh Nhảy nếu Bằng (je) nhảy nếu cờ 0 được đặt.

Bạn có thể dịch mã sang mã dễ đọc hơn như sau:

cmp eax, 0
je  somewhere

Điều đó có cùng chức năng nhưng yêu cầu thêm một số byte không gian mã. Đó là lý do tại sao trình biên dịch phát ra một bài kiểm tra thay vì so sánh.


3
Trên thực tế, cmp có thể không hoạt động ở đó. Có nghĩa là, nó hoạt động cho trường hợp cụ thể được trình bày, nhưng cmp ảnh hưởng đến cờ khác với thử nghiệm, do nó là một phụ bên trong thay vì và. Một cái gì đó để giữ trong tâm trí.
Cody Brocious, 29/09/08

4
đối với một bài kiểm tra bằng 0, nó hoàn toàn hợp lệ.
Nils Pipenbrinck 29/09/08

3
Nhưng bạn không biết những gì khác nhìn vào các lá cờ sau đó. Các hiệu ứng trên cờ rất khác nhau, vì vậy đây có thể là một vấn đề và rất thường xuyên xảy ra.
Cody Brocious, 29/09/08

2
Không, các cờ duy nhất được đặt bởi một / method / khác là mang và tràn, cả hai đều được đặt thành 0. Giá trị / / của các cờ khác sẽ khác nhau vì cmp sử dụng sub và test sử dụng và.
Cody Brocious, 29/09/08

2
@CodyBrocious: test eax, eaxcmp eax, 0cả hai đều đặt tất cả các cờ và đặt chúng thành các giá trị giống hệt nhau. Cả hai hướng dẫn đều đặt tất cả các cờ "theo kết quả". Phép trừ 0không bao giờ có thể tạo ra mang hoặc tràn. Lập luận của bạn là chính xác cho bất kỳ ngay lập tức khác hơn 0, nhưng không phải cho 0.
Peter Cordes

22

testgiống như and, ngoại trừ nó chỉ viết CỜ, để lại cả hai đầu vào của nó không được sửa đổi. Với hai đầu vào khác nhau , nó hữu ích để kiểm tra nếu một số bit đều bằng 0 hoặc nếu ít nhất một bit được đặt. (ví dụ test al, 3đặt ZF nếu EAX là bội số của 4 (và do đó có cả 2 bit thấp của nó được làm 0).


test eax,eaxđặt tất cả các cờ theo cùng một cách cmp eax, 0sẽ :

  • CF và OF được xóa (AND / TEST luôn làm điều đó; trừ đi số 0 không bao giờ tạo ra lỗ)
  • ZF, SF và PF theo giá trị trong EAX. ( a = a&a = a-0).
    (PF như bình thường chỉ được đặt theo 8 bit thấp )

Ngoại trừ AF lỗi thời (cờ mang phụ trợ, được sử dụng bởi lệnh ASCII / BCD). TEST không xác định nó , nhưng CMP đặt nó "theo kết quả" . Vì trừ đi số 0 không thể tạo ra chuyển động từ bit thứ 4 đến thứ 5, nên CMP phải luôn xóa AF.


TEST nhỏ hơn (không có ngay lập tức) và đôi khi nhanh hơn (có thể kết hợp macro thành uop so sánh và phân nhánh trên nhiều CPU hơn trong nhiều trường hợp hơn CMP). Điều đó làm cho testthành ngữ được ưa thích để so sánh một thanh ghi với số không . Đó là một tối ưu hóa lỗ nhìn trộm chocmp reg,0 mà bạn có thể sử dụng bất kể ý nghĩa ngữ nghĩa.

Lý do phổ biến duy nhất để sử dụng CMP với số 0 ngay lập tức là khi bạn muốn so sánh với toán hạng bộ nhớ. Ví dụ: cmpb $0, (%esi)để kiểm tra một byte 0 kết thúc ở cuối chuỗi kiểu C có độ dài ngầm định.


AVX512F thêmkortestw k1, k2thêm AVX512DQ / BW (Skylake-X nhưng không phải KNL) ktestb/w/d/q k1, k2, hoạt động trên thanh ghi mặt nạ AVX512 (k0..k7) nhưng vẫn đặt CỜ thông thường như testhiện tại, giống như cách mà số nguyên ORhoặc ANDhướng dẫn làm. (Sắp xếp như SSE4 ptesthoặc SSE ucomiss: đầu vào trong miền SIMD và kết quả là CỜ số nguyên.)

kortestw k1,k1là cách thành ngữ để rẽ nhánh / cmovcc / setcc dựa trên kết quả so sánh AVX512, thay thế SSE / AVX2 (v)pmovmskb/ps/pd+ testhoặc cmp.


Việc sử dụng jzso với jecó thể gây nhầm lẫn.

jzjetheo nghĩa đen là cùng một chỉ dẫn , tức là cùng một opcode trong mã máy. Chúng làm điều tương tự, nhưng có ý nghĩa ngữ nghĩa khác nhau đối với con người . Các trình giải mã (và thường là đầu ra asm từ trình biên dịch) sẽ chỉ sử dụng một, do đó, sự phân biệt ngữ nghĩa bị mất.

cmpsubđặt ZF khi hai đầu vào của chúng bằng nhau (tức là kết quả phép trừ là 0). je(nhảy nếu bằng nhau) là từ đồng nghĩa có liên quan về mặt ngữ nghĩa.

test %eax,%eax/ and %eax,%eaxlại đặt ZF khi kết quả bằng 0, nhưng không có thử nghiệm "bình đẳng". ZF sau khi kiểm tra không cho bạn biết liệu hai toán hạng có bằng nhau hay không. Vì vậy, jz(nhảy nếu không) là từ đồng nghĩa có liên quan về mặt ngữ nghĩa.


Tôi sẽ xem xét thêm thông tin cơ bản về hoạt động testbitwise and, có thể không rõ ràng đối với những người mới học lắp ráp (và lười biếng / không biết kiểm tra hướng dẫn tham khảo hướng dẫn sau mỗi 60 giây;) :)).
Ped7g

1
@ Ped7g: công bằng mà nói, tôi đoán không hại gì khi đặt mọi thứ vào câu trả lời này, thay vì để phần đó cho các câu trả lời khác. Đã thêm AVX512 kortest*ktest*trong khi tôi ở đó.
Peter Cordes

BTW, điều này về cơ bản giống với câu trả lời của tôi cho một phiên bản khác của cùng một câu hỏi , nhưng tôi đã nói nhiều thứ hơn về hiệu suất ở đó, ví dụ như có thể tránh các lỗi đăng ký-đọc trên các CPU dòng P6 cũ như Nehalem bằng cách viết lại thanh ghi với cùng giá trị.
Peter Cordes

@PeterCordes Đây phải là câu trả lời được chấp nhận: đầy đủ và kỹ thuật. Không giống như bài đăng được chấp nhận, điều này làm dịu sự tò mò và khát khao kiến ​​thức của một người. Giữ nó lên thưa ngài.
lập trình viên vào

Cần lưu ý rằng PF được đặt thành tính chẵn lẻ của 8 bit thấp, trong trường hợp này là AL.
ecm

5

Đoạn mã này là từ một chương trình con được cung cấp một con trỏ đến một cái gì đó, có thể là một số cấu trúc hoặc đối tượng. Dòng thứ 2 tham chiếu đến con trỏ đó, tìm nạp một giá trị từ thứ đó - bản thân nó có thể là một con trỏ hoặc có thể chỉ là một int, được lưu trữ dưới dạng thành viên thứ 2 của nó (offset +4). Dòng thứ 3 và thứ 4 kiểm tra giá trị này bằng 0 (NULL nếu đó là một con trỏ) và bỏ qua một vài thao tác sau (không hiển thị) nếu nó bằng 0.

Bài kiểm tra cho giá trị 0 đôi khi được mã hóa để so sánh với giá trị 0 theo nghĩa đen ngay lập tức, nhưng trình biên dịch (hoặc con người?) Người đã viết điều này có thể đã nghĩ rằng một op testl sẽ chạy nhanh hơn - có tính đến tất cả các thứ CPU hiện đại như pipelining và đăng ký đổi tên. Đó là từ cùng một túi thủ thuật có ý tưởng xóa sổ đăng ký với XOR EAX, EAX (tôi đã thấy trên biển số xe của một ai đó ở Colorado!) Chứ không phải là MOV EAX rõ ràng nhưng có thể chậm hơn, # 0 (tôi sử dụng ký hiệu cũ hơn ).

Trong asm, như perl, TMTOWTDI.


3

Nếu eax bằng 0, nó sẽ thực hiện bước nhảy có điều kiện, nếu không nó sẽ tiếp tục thực hiện ở 319e9


0

Trong một số chương trình, chúng có thể được sử dụng để kiểm tra lỗi tràn bộ đệm. Ở trên cùng của không gian được cấp phát, một số 0 được đặt. Sau khi nhập dữ liệu vào ngăn xếp, nó sẽ tìm số 0 ở đầu không gian được cấp phát để đảm bảo không gian được cấp phát không bị tràn.

Nó được sử dụng trong bài tập khai thác-bài tập stack0 để kiểm tra xem nó có bị tràn hay không và nếu có và có số 0 ở đó, nó sẽ hiển thị "Thử lại"

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

Tôi không thấy trường hợp cụ thể này của việc kiểm tra sổ đăng ký có giá trị khác 0 sẽ bổ sung gì vào phần Hỏi và Đáp này hay không. Đặc biệt là khi cmp DWORD PTR [esp+0x5c], 0/ jz 0x8048427 <main+51>sẽ hiệu quả hơn so với tải MOV riêng biệt và sau đó TEST. Đây hầu như không phải là trường hợp sử dụng phổ biến để kiểm tra số 0.
Peter Cordes

-4

chúng ta có thể thấy jgjle Nếu testl %edx,%edx. jle .L3chúng ta có thể dễ dàng tìm thấy jle là phù hợp (SF^OF)|ZF, nếu% edx là 0, ZF = 1, nhưng nếu% edx không phải là 0 và là -1, sau testl, OF = 0 và SF = 1, do đó, flag = true, đó là bước nhảy thực hiện. Xin lỗi, tiếng Anh của tôi kém

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.