`testl` eax melawan eax?


118

Saya mencoba memahami beberapa pertemuan.

Perakitan sebagai berikut, saya tertarik pada testlbaris:

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

Saya mencoba memahami titik testlantara %eaxdan %eax? Saya pikir secara spesifik apa kode ini tidak penting, saya hanya mencoba memahami tes dengan dirinya sendiri - bukankah nilainya selalu benar?

Jawaban:


91

Ini menguji apakah eax0, atau lebih, atau di bawah. Dalam hal ini, lompatan dilakukan jika eax0.


2
Saya membuat pengeditan untuk mengubah jawaban populer ini menjadi jawaban kanonik yang lebih baik untuk "tentang apa TEST ini, dan apa bedanya dengan CMP", yang agak tersirat. Lihat jawaban saya sendiri lebih jauh untuk komentar tentang arti semantik dari sinonim JE dan JZ. Silakan tinjau suntingan saya karena ini cukup besar, dan itu tetap jawaban Anda.
Peter Cordes

@PeterCordes Saya menghargai niatnya, tapi saya akan mengembalikan hasil edit Anda. 1. "Suara" Anda sangat berbeda dari saya, dan saat ini lebih mirip dengan jawaban Anda daripada jawaban saya. 2. Yang lebih bermasalah adalah pernyataan yang berani bahwa flag keluar dengan cara yang persis sama antara testdan cmp. Ya, saya mengerti bahwa keyakinan Anda berdasarkan komentar Anda kepada Cody. Namun, memasukkannya ke dalam posting saya adalah masalah yang berbeda; ini bukan pernyataan yang ingin saya pertahankan, hanya karena saya tidak tahu apakah itu identik dalam semua kasus.
Chris Jester-Young

1
@PeterCordes Jika saya menemukan waktu luang, saya ingin menyempurnakan jawaban ini agar lebih kanonik. Saya akan menulisnya seperti saya menulisnya, dan saya sangat khusus tentang bagaimana saya menulis sesuatu. :-) Sebagai contoh, saya akan menulis je, jz, cmp, dan test, dan tidak JE, JZ, CMP, atau UJI. Saya pilih-pilih seperti itu.
Chris Jester-Young

1
Saya tidak mencoba untuk meningkatkan jawaban saya sendiri. Saya benar-benar lupa bahwa saya telah menjawab pertanyaan ini sendiri ketika saya melakukan pengeditan itu, dan baru menyadarinya setelah itu. Saya hanya melihat ini setelah seseorang menabraknya, dan apa yang dimulai sebagai pengeditan kecil menjadi terlalu banyak. Jangan tersinggung karena Anda ingin memutarnya kembali; itu hanya saran dan pasti terbaca seperti pekerjaan saya, bukan milik Anda. Saya akan mengambil beberapa dari apa yang saya tulis dan memasukkannya ke dalam jawaban saya sendiri.
Peter Cordes

2
Wow, setelah mengedit jawaban saya atas pertanyaan ini untuk memasukkan apa yang saya tambahkan ke jawaban Anda, saya menyadari bahwa saya hampir menggandakan sebagian besar dari apa yang saya tulis di bulan Juni. Ups! Saya memperbaruinya dengan lebih banyak alasan untuk mendukung klaim saya itu test a,adan cmp $0,amenetapkan bendera secara identik; terima kasih telah menunjukkan bahwa itu adalah klaim yang tidak sepele. re: TEST vs test.: baru-baru ini saya mulai menggunakan huruf besar semua seperti manual Intel. Tetapi ketika saya berbicara tentang mnemonik AT&T vs mnemonik Intel, saya menggunakan testbgaya untuk AT&T. IDK jika itu membantu keterbacaan.
Peter Cordes

90

Arti dari testadalah untuk DAN argumen bersama-sama, dan periksa hasilnya untuk nol. Jadi kode ini menguji apakah EAX adalah nol atau tidak. jeakan melompat jika nol.

BTW, ini menghasilkan instruksi yang lebih kecil daripada cmp eax, 0yang merupakan alasan kompiler umumnya akan melakukannya dengan cara ini.


34

Instruksi tes melakukan operasi AND yang logis antara operan tetapi tidak menulis hasilnya kembali ke register. Hanya bendera yang diperbarui.

Dalam contoh Anda, test eax, eax akan menyetel flag nol jika eax adalah nol, tanda-flag jika bit tertinggi disetel dan beberapa flag lain juga.

Instruksi Jump if Equal (je) melompat jika flag nol disetel.

Anda dapat menerjemahkan kode ke kode yang lebih mudah dibaca seperti ini:

cmp eax, 0
je  somewhere

Itu memiliki fungsi yang sama tetapi membutuhkan beberapa byte lebih banyak ruang kode. Itulah alasan mengapa kompilator mengeluarkan tes, bukan perbandingan.


3
Sebenarnya, cmp mungkin tidak berfungsi di sana. Artinya, ini berfungsi untuk kasus tertentu yang disajikan, tetapi cmp memengaruhi flag secara berbeda dari yang dilakukan pengujian, karena itu adalah sub internal, bukan dan. Sesuatu yang perlu diingat.
Cody Brocious

4
untuk pengujian terhadap nol itu sangat valid.
Nils Pipenbrinck

3
Tapi Anda tidak tahu apa lagi yang melihat bendera nanti. Efek pada flag sangat berbeda, jadi ini bisa menjadi masalah dan sering kali begitu.
Cody Brocious

2
Tidak, satu-satunya tanda yang ditetapkan oleh / metode / yang berbeda adalah carry dan overflow, keduanya ditetapkan ke 0. Tanda / nilai / dari tanda lainnya akan berbeda karena cmp menggunakan sub dan uji penggunaan dan.
Cody Brocious

2
@CodyBrocious: test eax, eaxdan cmp eax, 0keduanya menyetel semua tanda, dan menyetelnya ke nilai yang identik. Kedua instruksi mengatur semua tanda "sesuai dengan hasil". Pengurangan 0tidak akan pernah menghasilkan carry atau overflow. Argumen Anda benar untuk jawaban langsung selain 0, tetapi tidak untuk angka 0.
Peter Cordes

22

testseperti and, kecuali hanya menulis BENDERA, membiarkan kedua inputnya tidak diubah. Dengan dua input yang berbeda , ini berguna untuk menguji apakah beberapa bit semuanya nol, atau jika setidaknya ada satu bit yang disetel. (misalnya test al, 3set ZF jika EAX adalah kelipatan 4 (dan dengan demikian keduanya memiliki 2 bit rendah yang nol).


test eax,eaxmenyetel semua flag dengan cara yang persis sama seperti yang cmp eax, 0akan :

  • CF dan OF dihapus (AND / TEST selalu melakukan itu; mengurangi nol tidak pernah menghasilkan carry)
  • ZF, SF dan PF sesuai dengan nilai di EAX. ( a = a&a = a-0).
    (PF seperti biasa hanya diset sesuai dengan low 8 bits )

Kecuali untuk AF yang sudah usang (auxiliary-carry flag, digunakan oleh instruksi ASCII / BCD). TEST membiarkannya tidak terdefinisi , tetapi CMP menetapkannya "sesuai dengan hasil" . Karena mengurangi nol tidak dapat menghasilkan carry dari bit ke-4 hingga ke-5, CMP harus selalu menghapus AF.


TEST lebih kecil (tidak langsung) dan terkadang lebih cepat (dapat melakukan fusi makro menjadi uop pembanding dan cabang pada lebih banyak CPU dalam lebih banyak kasus daripada CMP). Itu membuat testidiom yang disukai untuk membandingkan register dengan nol . Ini adalah pengoptimalan lubang intip cmp reg,0yang dapat Anda gunakan terlepas dari arti semantiknya.

Satu-satunya alasan umum untuk menggunakan CMP dengan 0 langsung adalah saat Anda ingin membandingkan dengan operan memori. Misalnya, cmpb $0, (%esi)untuk memeriksa penghentian byte nol di akhir string gaya-C dengan panjang implisit.


AVX512F menambahkankortestw k1, k2 dan AVX512DQ / BW (Skylake-X tetapi bukan KNL) menambahkan ktestb/w/d/q k1, k2, yang beroperasi pada register topeng AVX512 (k0..k7) tetapi tetap mengatur BENDERA biasa seperti testhalnya, seperti halnya integer ORatau ANDinstruksi. (Semacam seperti SSE4 ptestatau SSE ucomiss: masukan dalam domain SIMD dan menghasilkan BENDERA integer.)

kortestw k1,k1adalah cara idiomatis untuk mencabangkan / cmovcc / setcc berdasarkan hasil perbandingan AVX512, menggantikan SSE / AVX2 (v)pmovmskb/ps/pd+ testatau cmp.


Penggunaan jzvs. jebisa membingungkan.

jzdan jesecara harfiah merupakan instruksi yang sama , yaitu opcode yang sama dalam kode mesin. Mereka melakukan hal yang sama, tetapi memiliki arti semantik yang berbeda bagi manusia . Disassembler (dan biasanya output asm dari compiler) hanya akan menggunakan satu, sehingga perbedaan semantik hilang.

cmpdan subatur ZF ketika kedua inputnya sama (yaitu hasil pengurangannya adalah 0). je(lompat jika sama) adalah sinonim yang relevan secara semantik.

test %eax,%eax/ and %eax,%eaxlagi menyetel ZF saat hasilnya nol, tetapi tidak ada uji "kesetaraan". ZF setelah pengujian tidak memberi tahu Anda apakah kedua operan itu sama. Jadi jz(lompat jika nol) adalah sinonim yang relevan secara semantik.


Saya akan mempertimbangkan untuk menambahkan informasi dasar tentang operasi testbitwise and, mungkin tidak jelas bagi orang yang baru belajar perakitan (dan malas / tidak sadar untuk memeriksa panduan referensi instruksi setiap 60 detik;) :)).
Ped7g

1
@ Ped7g: cukup adil, saya rasa tidak ada salahnya untuk meletakkan semuanya dalam jawaban ini, daripada menyerahkan bagian itu pada jawaban yang lain. Menambahkan AVX512 kortest*dan ktest*saat saya melakukannya.
Peter Cordes

BTW, ini pada dasarnya sama dengan jawaban saya untuk versi lain dari pertanyaan yang sama , tetapi saya mengatakan lebih banyak hal tentang kinerja di sana, misalnya mungkin menghindari warung register-baca pada CPU keluarga P6 lama seperti Nehalem dengan menulis ulang register dengan nilai yang sama.
Peter Cordes

@PeterCordes Ini harus menjadi jawaban yang diterima: lengkap dan teknis. Berbeda dengan pos yang diterima, ini memuaskan rasa ingin tahu dan haus seseorang akan pengetahuan. Teruskan Pak.
programmer

Perlu dicatat bahwa PF diatur ke paritas dari 8 bit rendah, yang dalam hal ini adalah AL.
ecm

5

Potongan kode ini berasal dari subrutin yang diberikan penunjuk ke sesuatu, mungkin beberapa struct atau objek. Baris ke-2 membedakan penunjuk itu, mengambil nilai dari benda itu - mungkin itu sendiri penunjuk atau mungkin hanya int, disimpan sebagai anggota ke-2 (offset +4). Baris ke-3 dan ke-4 menguji nilai ini untuk nol (NULL jika itu pointer) dan lewati beberapa operasi berikut (tidak ditampilkan) jika nilainya nol.

Tes untuk nol terkadang dikodekan sebagai perbandingan dengan nilai nol literal langsung, tetapi kompiler (atau manusia?) Yang menulis ini mungkin mengira operasi test akan berjalan lebih cepat - dengan mempertimbangkan semua hal CPU modern seperti pipelining dan register mengganti nama. Ini dari sekumpulan trik yang sama yang memegang gagasan untuk membersihkan register dengan XOR EAX, EAX (yang saya lihat di plat nomor seseorang di Colorado!) Daripada MOV EAX yang jelas tapi mungkin lebih lambat, # 0 (saya menggunakan notasi yang lebih lama ).

Dalam asm, seperti perl, TMTOWTDI.


3

Jika eax nol maka akan melakukan lompatan bersyarat, jika tidak maka akan melanjutkan eksekusi pada 319e9


0

Dalam beberapa program, mereka dapat digunakan untuk memeriksa buffer overflow. Di bagian paling atas dari ruang yang dialokasikan, 0 ditempatkan. Setelah memasukkan data ke dalam tumpukan, ia mencari 0 di awal ruang yang dialokasikan untuk memastikan ruang yang dialokasikan tidak meluap.

Itu digunakan dalam latihan stack0 dari exploits-exercise untuk memeriksa apakah itu meluap dan jika tidak ada dan ada nol di sana, itu akan menampilkan "Coba lagi"

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

Saya tidak melihat apa kasus khusus memeriksa register untuk bukan nol yang menambah Q&A ini. Terutama ketika cmp DWORD PTR [esp+0x5c], 0/ jz 0x8048427 <main+51>akan lebih efisien daripada MOV-load terpisah dan kemudian TEST. Ini bukan kasus penggunaan umum untuk memeriksa nol.
Peter Cordes

-4

kita bisa melihat jgjle Jika testl %edx,%edx. jle .L3kita dapat dengan mudah menemukan jle cocok (SF^OF)|ZF, jika% edx adalah nol, ZF = 1, tetapi jika% edx bukan nol dan -1, setelah testl, OF = 0, dan SF = 1, jadi benderanya = true, yang menerapkan jump. Maaf, bahasa Inggris saya buruk

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.