Saat membaca tentang assembler, saya sering menjumpai orang yang menulis bahwa mereka mendorong register prosesor tertentu dan memunculkannya lagi nanti untuk memulihkan keadaan sebelumnya.
- Bagaimana Anda bisa mendaftar? Di mana itu didorong? Mengapa ini dibutuhkan?
- Apakah ini bermuara pada instruksi prosesor tunggal atau lebih kompleks?
assembly
x86
stack
terminology
Ars emble
sumber
sumber
b
,w
,l
, atauq
untuk menunjukkan ukuran memori dimanipulasi. Contoh:pushl %eax
danpopl %eax
%eax
yang selalu berukuran 32 bit.Jawaban:
mendorong nilai (tidak harus disimpan dalam register) berarti menuliskannya ke stack.
popping berarti memulihkan apa pun yang ada di atas tumpukan ke dalam register. Itu adalah instruksi dasar:
sumber
r/m
, bukan hanya mendaftar, jadi Anda bisapush dword [esi]
. Atau bahkanpop dword [esp]
untuk memuat dan kemudian menyimpan nilai yang sama kembali ke alamat yang sama. ( github.com/HJLebbink/asm-dude/wiki/POP ). Saya hanya menyebutkan ini karena Anda mengatakan "belum tentu mendaftar".pop
masuk ke area memori:pop [0xdeadbeef]
Inilah cara Anda mendorong register. Saya berasumsi kita berbicara tentang x86.
Itu didorong di tumpukan. Nilai dari
ESP
register diturunkan ke ukuran nilai yang didorong saat tumpukan tumbuh ke bawah dalam sistem x86.Itu diperlukan untuk melestarikan nilai-nilai. Penggunaan umum adalah
A
push
adalah instruksi tunggal di x86, yang melakukan dua hal secara internal.ESP
register dengan ukuran nilai yang didorong.ESP
register saat ini.sumber
Di mana itu didorong?
esp - 4
. Lebih tepatnya:esp
akan dikurangi 4esp
pop
membalikkan ini.System V ABI memberi tahu Linux untuk
rsp
menunjukkan lokasi stack yang masuk akal ketika program mulai berjalan: Apa status register default ketika program diluncurkan (asm, linux)? yang biasanya Anda gunakan.Bagaimana Anda bisa mendaftar?
Contoh GNU GAS Minimal:
Di atas di GitHub dengan pernyataan yang dapat dijalankan .
Mengapa ini dibutuhkan?
Memang benar bahwa instruksi tersebut dapat dengan mudah diimplementasikan melalui
mov
,add
dansub
.Alasan mereka ada, adalah karena kombinasi instruksi tersebut sangat sering, sehingga Intel memutuskan untuk menyediakannya untuk kami.
Alasan mengapa kombinasi tersebut sangat sering, adalah karena kombinasi tersebut memudahkan untuk menyimpan dan memulihkan nilai register ke memori sementara sehingga tidak ditimpa.
Untuk memahami masalahnya, coba kompilasi beberapa kode C secara manual.
Kesulitan utama, adalah memutuskan di mana setiap variabel akan disimpan.
Idealnya, semua variabel akan masuk ke dalam register, yang merupakan memori tercepat untuk diakses (saat ini sekitar 100x lebih cepat dari RAM).
Tetapi tentu saja, kita dapat dengan mudah memiliki lebih banyak variabel daripada register, khususnya untuk argumen fungsi bersarang, jadi satu-satunya solusi adalah menulis ke memori.
Kita bisa menulis ke alamat memori apa pun, tetapi karena variabel lokal dan argumen pemanggilan dan pengembalian fungsi cocok dengan pola tumpukan yang bagus, yang mencegah fragmentasi memori , itulah cara terbaik untuk mengatasinya. Bandingkan dengan kegilaan menulis pengalokasi heap.
Kemudian kita biarkan kompiler mengoptimalkan alokasi register untuk kita, karena itu adalah NP lengkap, dan salah satu bagian tersulit dalam menulis kompilator. Masalah ini disebut alokasi register , dan itu isomorfik untuk pewarnaan graf .
Ketika pengalokasi kompiler dipaksa untuk menyimpan sesuatu dalam memori dan bukan hanya register, itu dikenal sebagai spill .
Apakah ini bermuara pada instruksi prosesor tunggal atau lebih kompleks?
Yang kita tahu pasti adalah bahwa Intel mendokumentasikan a
push
danpop
instruksi, jadi mereka adalah satu instruksi dalam pengertian itu.Secara internal, ini dapat diperluas ke beberapa mikrokode, satu untuk memodifikasi
esp
dan satu untuk melakukan IO memori, dan mengambil banyak siklus.Tapi mungkin juga itu single
push
lebih cepat daripada kombinasi setara dari instruksi lain, karena lebih spesifik.Ini sebagian besar tidak (der) didokumentasikan:
push
danpop
mengambil satu operasi mikro tunggal.sumber
push
/pop
mendekode menjadi uops. Berkat penghitung kinerja, pengujian eksperimental dimungkinkan, dan Agner Fog telah melakukannya dan menerbitkan tabel instruksi . Pentium-M dan CPU yang lebih baru memiliki single-uoppush
/pop
terima kasih kepada mesin stack (Lihat pdf microarch Agner). Ini termasuk CPU AMD terbaru, berkat kesepakatan pembagian paten Intel / AMD.mov
muatan terpisah ). Untuk variabel non-const yang tumpah, perjalanan bolak-balik penerusan toko memiliki latensi ekstra (~ 5c ekstra vs. meneruskan secara langsung, dan instruksi toko tidak murah).ocperf.py
skrip pembungkus untuk mendapatkan nama simbolis yang mudah untuk penghitung.Register pendorong dan popping berada di belakang layar yang setara dengan ini:
Perhatikan bahwa ini adalah sintaks x86-64 At & t.
Digunakan sebagai pasangan, ini memungkinkan Anda menyimpan register di stack dan memulihkannya nanti. Ada kegunaan lain juga.
sumber
lea rsp, [rsp±8]
daripadaadd
/sub
untuk meniru efekpush
/pop
pada flag dengan lebih baik.Hampir semua CPU menggunakan stack. Tumpukan program adalah teknik LIFO dengan pengelolaan yang didukung perangkat keras.
Stack adalah jumlah memori program (RAM) yang biasanya dialokasikan di bagian atas heap memori CPU dan bertambah (pada instruksi PUSH, penunjuk stack berkurang) ke arah yang berlawanan. Istilah standar untuk memasukkan ke dalam tumpukan adalah PUSH dan untuk dihapus dari tumpukan adalah POP .
Stack dikelola melalui register CPU yang dimaksudkan oleh stack, juga disebut stack pointer, jadi ketika CPU melakukan POP atau PUSH , penunjuk tumpukan akan memuat / menyimpan register atau konstan ke dalam memori tumpukan dan penunjuk tumpukan akan otomatis berkurang xor bertambah sesuai jumlah kata yang didorong atau dimunculkan ke (dari) tumpukan.
Melalui instruksi assembler yang dapat kami simpan untuk ditumpuk:
sumber