`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
ฉันทำการแก้ไขเพื่อเปลี่ยนคำตอบที่เป็นที่นิยมนี้ให้เป็นคำตอบที่เป็นมาตรฐานที่ดีขึ้นสำหรับ "การทดสอบนี้เกี่ยวกับอะไรและแตกต่างจาก CMP อย่างไร" ซึ่งมีความหมายโดยนัย ดูคำตอบของฉันเองเพิ่มเติมสำหรับความคิดเห็นเกี่ยวกับความหมายเชิงความหมายของ JE และ JZ ที่ตรงกัน โปรดตรวจสอบการแก้ไขของฉันเนื่องจากมันค่อนข้างสำคัญและยังคงเป็นคำตอบของคุณ
Peter Cordes

@PeterCordes ฉันขอขอบคุณในความตั้งใจ แต่ฉันจะยกเลิกการแก้ไขของคุณ 1. "เสียง" ของคุณแตกต่างจากของฉันมากและตอนนี้มันอ่านคล้ายกับคำตอบของคุณมากกว่าของฉัน 2. ปัญหาที่มากกว่าคือการยืนยันอย่างชัดเจนว่าแฟล็กออกมาเหมือนกันทุกประการระหว่างtestและ cmpใช่ฉันเข้าใจว่านั่นเป็นความเชื่อของคุณตามความคิดเห็นของคุณ Cody อย่างไรก็ตามการใส่ไว้ในโพสต์ของฉันเป็นเรื่องที่แตกต่าง ไม่ใช่การยืนยันที่ฉันเต็มใจที่จะยืนหยัดเพียงเพราะฉันไม่รู้ว่ามันเหมือนกันในทุกกรณีหรือไม่
Chris Jester-Young

1
@PeterCordes ถ้าฉันหาเวลาว่างได้ฉันต้องการที่จะอธิบายคำตอบนี้ให้เป็นที่ยอมรับมากขึ้น ฉันจะเขียนมันเหมือนที่ฉันเขียนและฉันค่อนข้างเฉพาะเจาะจงเกี่ยวกับวิธีการเขียนสิ่งต่างๆ :-) ตัวอย่างเช่นผมต้องการเขียนje, jz, cmpและtestและไม่ JE, JZ, ซีเอ็มพีหรือทดสอบ ฉันจู้จี้จุกจิกแบบนั้น
Chris Jester-Young

1
ฉันไม่ได้พยายามเพิ่มคำตอบของตัวเอง ที่จริงฉันลืมไปว่าฉันได้ตอบคำถามนี้ด้วยตัวเองเมื่อฉันทำการแก้ไขและสังเกตเห็นหลังจากนั้นเท่านั้น ฉันเพิ่งดูสิ่งนี้หลังจากมีคนชนมันและสิ่งที่เริ่มต้นจากการแก้ไขเล็ก ๆ น้อย ๆ ที่มีหิมะปกคลุมมากเกินไป ไม่มีความผิดที่คุณต้องการที่จะย้อนกลับ; มันเป็นเพียงคำแนะนำและแน่นอนว่ามันอ่านเหมือนงานของฉันไม่ใช่ของคุณ ฉันจะนำสิ่งที่ฉันเขียนและใส่ไว้ในคำตอบของฉันเอง
Peter Cordes

2
ว้าวหลังจากแก้ไขคำตอบของฉันสำหรับคำถามนี้เพื่อรวมสิ่งที่ฉันเพิ่มให้กับคุณฉันก็รู้ว่าฉันทำซ้ำสิ่งที่ฉันเขียนเกือบทั้งหมดในเดือนมิถุนายน อ๊ะ! ฉันอัปเดตด้วยเหตุผลมากขึ้นในการสำรองข้อมูลการอ้างสิทธิ์ของฉันtest a,aและcmp $0,aตั้งค่าสถานะเหมือนกัน ขอบคุณที่ชี้ให้เห็นว่านั่นเป็นการอ้างสิทธิ์ที่ไม่สำคัญ เรื่องการทดสอบกับtest: เมื่อเร็ว ๆ นี้ฉันได้เริ่มใช้ตัวพิมพ์ใหญ่ทั้งหมดเช่นคู่มือของ Intel แต่เมื่อฉันพูดถึงการจำ AT&T กับการจำของ Intel ฉันใช้testbสไตล์สำหรับ AT&T IDK หากช่วยให้อ่านง่าย
Peter Cordes

90

ความหมายของtestis to AND อาร์กิวเมนต์เข้าด้วยกันและตรวจสอบผลลัพธ์ว่าเป็นศูนย์ ดังนั้นรหัสนี้จะทดสอบว่า EAX เป็นศูนย์หรือไม่jeจะกระโดดถ้าเป็นศูนย์

BTW สิ่งนี้จะสร้างคำสั่งที่เล็กกว่าcmp eax, 0ซึ่งเป็นเหตุผลที่คอมไพเลอร์มักจะทำเช่นนี้


34

คำสั่งทดสอบทำการดำเนินการ AND ทางตรรกะระหว่างตัวถูกดำเนินการ แต่ไม่ได้เขียนผลลัพธ์กลับลงในรีจิสเตอร์ อัพเดตเฉพาะแฟล็ก

ในตัวอย่าง eax ทดสอบของคุณ eax จะตั้งค่าแฟล็กศูนย์หาก eax เป็นศูนย์เครื่องหมาย - แฟล็กหากตั้งค่าบิตสูงสุดและแฟล็กอื่น ๆ ด้วย

คำสั่ง Jump if Equal (je) จะกระโดดหากตั้งค่าสถานะเป็นศูนย์

คุณสามารถแปลรหัสเป็นรหัสที่อ่านได้มากขึ้นดังนี้:

cmp eax, 0
je  somewhere

ที่มีฟังก์ชันการทำงานเหมือนกัน แต่ต้องการพื้นที่โค้ดเพิ่มเติมบางไบต์ นั่นเป็นเหตุผลว่าทำไมคอมไพเลอร์จึงปล่อยการทดสอบแทนที่จะเป็นการเปรียบเทียบ


3
จริงๆแล้ว cmp อาจไม่ทำงานที่นั่น นั่นคือใช้งานได้กับกรณีเฉพาะที่นำเสนอ แต่ cmp มีผลต่อแฟล็กต่างจากการทดสอบเนื่องจากเป็นหน่วยย่อยภายในแทนที่จะเป็นและ สิ่งที่ควรทราบ
Cody Brocious

4
สำหรับการทดสอบกับศูนย์นั้นใช้ได้อย่างสมบูรณ์แบบ
Nils Pipenbrinck

3
แต่คุณไม่รู้ว่ามีอะไรอีกบ้างที่ดูธงในภายหลัง ผลกระทบของแฟล็กนั้นแตกต่างกันมากดังนั้นนี่อาจเป็นปัญหาและเกิดขึ้นบ่อยมาก
Cody Brocious

2
ไม่แฟล็กเดียวที่กำหนดโดย / method / ต่างกันคือ carry และ overflow ซึ่งทั้งสองแฟล็กถูกตั้งค่าเป็น 0 ค่า / ค่า / ของแฟล็กอื่นจะแตกต่างกันเนื่องจาก cmp ใช้ sub และ test ใช้และ
Cody Brocious

2
@CodyBrocious: test eax, eaxและcmp eax, 0ทั้งสองตั้งค่าสถานะทั้งหมดและตั้งค่าเป็นค่าที่เหมือนกัน คำสั่งทั้งสองตั้งค่าสถานะทั้งหมด "ตามผลลัพธ์" การลบ0ไม่สามารถทำให้เกิดการพกพาหรือล้น อาร์กิวเมนต์ของคุณถูกต้องสำหรับสิ่งอื่นที่ไม่ใช่ 0 แต่ไม่ใช่สำหรับ 0
Peter Cordes

22

testก็เหมือนกับandยกเว้นว่ามันจะเขียนเฉพาะ FLAGS เท่านั้นโดยปล่อยให้อินพุตทั้งสองไม่ถูกแก้ไข ด้วยอินพุตที่แตกต่างกันสองอินพุตจะมีประโยชน์สำหรับการทดสอบว่าบิตบางส่วนเป็นศูนย์ทั้งหมดหรือตั้งค่าอย่างน้อยหนึ่งรายการ (เช่นtest al, 3ตั้งค่า ZF ถ้า EAX เป็นผลคูณของ 4 (และทำให้ทั้ง 2 บิตต่ำเป็นศูนย์)


test eax,eaxตั้งค่าสถานะทั้งหมดในลักษณะเดียวกับที่cmp eax, 0จะ :

ยกเว้น AF ที่ล้าสมัย (แฟล็กพกพาเสริมที่ใช้โดยคำแนะนำ ASCII / BCD) ใบทดสอบมันไม่ได้กำหนดแต่CMP ชุดมัน "ตามผล" เนื่องจากการลบศูนย์ไม่สามารถทำให้เกิดการพกพาจากบิตที่ 4 ถึง 5 ได้ CMP จึงควรล้าง AF เสมอ


TEST มีขนาดเล็กกว่า (ไม่มีทันที) และบางครั้งก็เร็วกว่า (สามารถนำมาโครมาหลอมรวมเป็น uop เปรียบเทียบและแยกสาขาบน CPU มากกว่า CMP ได้ในหลายกรณี) ที่ทำให้testสำนวนที่แนะนำสำหรับการเปรียบเทียบการลงทะเบียนกับศูนย์ เป็นการเพิ่มประสิทธิภาพช่องมองภาพcmp reg,0ที่คุณสามารถใช้ได้โดยไม่คำนึงถึงความหมาย

เหตุผลทั่วไปเพียงอย่างเดียวในการใช้ CMP กับ 0 ทันทีคือเมื่อคุณต้องการเปรียบเทียบกับตัวถูกดำเนินการหน่วยความจำ ตัวอย่างเช่นcmpb $0, (%esi)เพื่อตรวจสอบการสิ้นสุดศูนย์ไบต์ที่ส่วนท้ายของสตริงสไตล์ C ที่มีความยาวโดยนัย


เพิ่ม AVX512Fkortestw k1, k2และเพิ่ม AVX512DQ / BW (Skylake-X แต่ไม่ใช่ KNL) ktestb/w/d/q k1, k2ซึ่งทำงานบน AVX512 mask register (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เป็นคำสั่งเดียวกันอย่างแท้จริงนั่นคือ opcode เดียวกันในรหัสเครื่อง พวกมันทำสิ่งเดียวกัน แต่มีความหมายที่แตกต่างกันสำหรับมนุษย์แต่มีความหมายที่แตกต่างกันสำหรับมนุษย์ ตัวแยกชิ้นส่วน (และโดยทั่วไปคือเอาต์พุต asm จากคอมไพเลอร์) จะใช้เพียงตัวเดียวดังนั้นความแตกต่างทางความหมายจึงหายไป

cmp และ subตั้งค่า ZF เมื่ออินพุตทั้งสองเท่ากัน (เช่นผลการลบคือ 0) je(กระโดดถ้าเท่ากัน) เป็นคำพ้องความหมายที่เกี่ยวข้อง

test %eax,%eax/ and %eax,%eaxตั้งค่า ZF อีกครั้งเมื่อผลลัพธ์เป็นศูนย์ แต่ไม่มีการทดสอบ "ความเท่าเทียมกัน" ZF หลังการทดสอบไม่ได้บอกคุณว่าทั้งสองตัวถูกดำเนินการเท่ากันหรือไม่ ดังนั้นjz(กระโดดถ้าเป็นศูนย์) คือคำพ้องความหมายที่เกี่ยวข้อง


ฉันจะพิจารณาเพิ่มข้อมูลพื้นฐานเกี่ยวกับtestการandใช้งานแบบบิตอาจไม่ชัดเจนสำหรับผู้ที่เพิ่งเรียนรู้แอสเซมบลี (และขี้เกียจ / ไม่ทราบที่จะตรวจสอบคู่มืออ้างอิงคำแนะนำทุกๆ 60 วินาที;) :))
Ped7g

1
@ Ped7g: ยุติธรรมพอแล้วฉันเดาว่ามันไม่เจ็บที่จะใส่ทุกอย่างในคำตอบนี้แทนที่จะปล่อยให้ส่วนนั้นไปหาคำตอบอื่น เพิ่ม AVX512 kortest*และktest*ในขณะที่ฉันอยู่ที่นั่น
Peter Cordes

BTW โดยพื้นฐานแล้วสิ่งนี้เหมือนกับคำตอบของฉันสำหรับคำถามเดียวกันในเวอร์ชันอื่นแต่ฉันได้พูดถึงสิ่งอื่น ๆ เกี่ยวกับประสิทธิภาพที่นั่นเช่นอาจหลีกเลี่ยงการลงทะเบียนอ่านแผงลอยบนซีพียูตระกูล P6 รุ่นเก่าเช่น Nehalem โดยเขียนรีจิสเตอร์ใหม่ด้วยค่าเดียวกัน
Peter Cordes

@PeterCordes นี่ควรเป็นคำตอบที่ยอมรับ: ละเอียดถี่ถ้วนและมีเทคนิค ไม่เหมือนโพสต์ที่ได้รับการยอมรับสิ่งนี้ช่วยดับความอยากรู้อยากเห็นและกระหายความรู้ ให้มันขึ้นเซอร์
โปรแกรมเมอร์

ควรสังเกตว่า PF ถูกตั้งค่าเป็นความเท่าเทียมกันของ 8 บิตต่ำซึ่งในกรณีนี้คือ AL
ecm

5

ข้อมูลโค้ดนี้มาจากรูทีนย่อยที่กำหนดตัวชี้ไปยังบางสิ่งซึ่งอาจเป็นโครงสร้างหรืออ็อบเจ็กต์บางอย่าง บรรทัดที่ 2 dereferences ตัวชี้ดึงค่าจากสิ่งนั้น - อาจเป็นตัวชี้หรืออาจเป็นแค่ int ที่เก็บไว้เป็นสมาชิกตัวที่ 2 (offset +4) บรรทัดที่ 3 และ 4 จะทดสอบค่านี้เป็นศูนย์ (NULL หากเป็นตัวชี้) และข้ามการดำเนินการสองสามอย่างต่อไปนี้ (ไม่แสดง) หากเป็นศูนย์

การทดสอบศูนย์บางครั้งมีการเข้ารหัสเพื่อเปรียบเทียบกับค่าศูนย์ตามตัวอักษรในทันที แต่คอมไพเลอร์ (หรือมนุษย์?) ที่เขียนสิ่งนี้อาจคิดว่า testl op จะทำงานได้เร็วขึ้นโดยคำนึงถึงสิ่งที่ CPU สมัยใหม่ทั้งหมดเช่น pipelining และ register การตั้งชื่อใหม่ มันมาจากกลเม็ดกระเป๋าเดียวกันที่มีแนวคิดในการล้างทะเบียนกับ XOR EAX, EAX (ซึ่งฉันเห็นบนป้ายทะเบียนของใครบางคนในโคโลราโด!) มากกว่าที่เห็นได้ชัด แต่อาจจะช้ากว่า MOV EAX, # 0 (ฉันใช้สัญกรณ์ที่เก่ากว่า )

ใน asm เช่น perl, TMTOWTDI



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 ที่แยกจากกันแล้วทดสอบ นี่ไม่ใช่กรณีการใช้งานทั่วไปสำหรับการตรวจสอบศูนย์
Peter Cordes

-4

เราสามารถเห็นไฟล์ jgjle ถ้าtestl %edx,%edx. jle .L3เราสามารถหาjleได้ง่ายก็เหมาะสม(SF^OF)|ZFถ้า% edx เป็นศูนย์ ZF = 1 แต่ถ้า% edx ไม่ใช่ศูนย์และเป็น -1 หลังจาก testl ค่า OF = 0 และ SF = 1 ดังนั้น flag = true ที่ใช้ jump ขออภัยภาษาอังกฤษของฉันไม่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.