Saya seorang pemula dalam bahasa assembly dan telah memperhatikan bahwa kode x86 yang dipancarkan oleh kompiler biasanya menyimpan penunjuk bingkai bahkan dalam mode rilis / dioptimalkan ketika itu dapat menggunakan EBP
register untuk sesuatu yang lain.
Saya mengerti mengapa penunjuk bingkai dapat membuat kode lebih mudah untuk di-debug, dan mungkin diperlukan jika alloca()
dipanggil dalam suatu fungsi. Namun, x86 memiliki register yang sangat sedikit dan menggunakan dua di antaranya untuk menyimpan lokasi frame stack ketika satu sudah cukup tidak masuk akal bagi saya. Mengapa menghilangkan penunjuk bingkai dianggap sebagai ide yang buruk bahkan dalam versi yang dioptimalkan / rilis?
performance
assembly
x86
dsimcha
sumber
sumber
alloca
) 3. kemudahan implementasi runtime: penanganan pengecualian, kotak pasir, GCJawaban:
Frame pointer adalah pointer referensi yang memungkinkan debugger mengetahui di mana variabel lokal atau argumen berada dengan offset konstan tunggal. Meskipun nilai ESP berubah selama eksekusi, EBP tetap sama sehingga memungkinkan untuk mencapai variabel yang sama pada offset yang sama (seperti parameter pertama akan selalu pada EBP + 8 sementara offset ESP dapat berubah secara signifikan karena Anda akan mendorong / popping sesuatu)
Mengapa kompiler tidak membuang penunjuk bingkai? Karena dengan penunjuk bingkai, debugger dapat mengetahui di mana variabel dan argumen lokal menggunakan tabel simbol karena dijamin akan berada pada offset konstan ke EBP. Jika tidak, tidak ada cara mudah untuk mengetahui di mana variabel lokal berada pada titik mana pun dalam kode.
Seperti yang disebutkan Greg, ini juga membantu pelepasan tumpukan untuk debugger karena EBP menyediakan daftar bingkai tumpukan yang ditautkan terbalik sehingga memungkinkan debugger untuk mengetahui ukuran bingkai tumpukan (variabel lokal + argumen) dari fungsi tersebut.
Kebanyakan kompiler menyediakan opsi untuk menghilangkan penunjuk bingkai meskipun itu membuat debugging menjadi sangat sulit. Opsi itu tidak boleh digunakan secara global, bahkan dalam kode rilis. Anda tidak tahu kapan Anda perlu men-debug kerusakan pengguna.
sumber
-fomit-frame-pointer
. Pengaturan itu adalah default di gcc terbaru..eh_frame_hdr
bagian ini juga digunakan untuk pengecualian waktu proses. Anda akan menemukannya (denganobjdump -h
) di sebagian besar binari pada sistem Linux, Ini sekitar 16k untuk/bin/bash
, vs. 572B untuk GNU/bin/true
, 108k untukffmpeg
. Ada opsi gcc untuk menonaktifkan pembuatannya, tetapi ini adalah bagian data "normal", bukan bagian debug yangstrip
dihapus secara default. Jika tidak, Anda tidak dapat menelusuri kembali melalui fungsi pustaka yang tidak memiliki simbol debug. Bagian itu mungkin lebih besar daripush/mov/pop
instruksi yang digantikannya, tetapi memiliki biaya runtime yang mendekati nol (misalnya cache uop).Hanya menambahkan dua sen saya ke jawaban yang sudah bagus.
Ini adalah bagian dari arsitektur bahasa yang baik untuk memiliki rangkaian bingkai tumpukan. BP menunjuk ke frame saat ini, di mana variabel subrutin-lokal disimpan. (Penduduk setempat berada di offset negatif, dan argumen berada di offset positif.)
Gagasan bahwa ini mencegah register yang sangat baik digunakan dalam pengoptimalan menimbulkan pertanyaan: kapan dan di mana pengoptimalan sebenarnya bermanfaat?
Optimasi hanya bermanfaat dalam loop ketat yang 1) tidak memanggil fungsi, 2) di mana pencacah program menghabiskan sebagian besar waktunya, dan 3) dalam kode yang benar-benar akan dilihat kompilator (yaitu fungsi non-perpustakaan). Ini biasanya merupakan bagian yang sangat kecil dari keseluruhan kode, terutama dalam sistem yang besar.
Kode lain dapat dipelintir dan diperas untuk menghilangkan siklus, dan itu tidak masalah, karena penghitung program secara praktis tidak pernah ada.
Saya tahu Anda tidak menanyakan hal ini, tetapi menurut pengalaman saya, 99% masalah kinerja tidak ada hubungannya sama sekali dengan pengoptimalan compiler. Mereka ada hubungannya dengan desain berlebihan.
sumber
Tergantung pada kompilernya, tentunya. Saya telah melihat kode yang dioptimalkan yang dikeluarkan oleh kompiler x86 yang secara bebas menggunakan register EBP sebagai register tujuan umum. (Saya tidak ingat kompiler mana yang saya perhatikan dengannya.)
Penyusun juga dapat memilih untuk mempertahankan register EBP untuk membantu pelepasan tumpukan selama penanganan pengecualian, tetapi sekali lagi ini tergantung pada implementasi kompiler yang tepat.
sumber
-fomit-frame-pointer
ketika pengoptimalan diaktifkan. (bila ABI mengizinkannya). GCC, clang, ICC, dan MSVC semuanya melakukannya, IIRC, bahkan saat menargetkan Windows 32-bit. Yup, jawaban saya tentang Mengapa lebih baik menggunakan ebp daripada register esp untuk menemukan parameter di stack? menunjukkan bahwa bahkan Windows 32-bit dapat menghilangkan penunjuk bingkai. Linux 32-bit x86 pasti bisa dan bisa. Dan tentu saja 64-bit ABI telah memungkinkan penghilangan frame-pointer sejak awal.Ini benar hanya dalam arti bahwa opcode hanya dapat menangani 8 register. Prosesor itu sendiri sebenarnya akan memiliki lebih banyak register daripada itu dan menggunakan penggantian nama register, pipelining, eksekusi spekulatif, dan kata kunci prosesor lainnya untuk menyiasati batas itu. Wikipedia memiliki paragraf pengantar yang bagus tentang apa yang dapat dilakukan prosesor x86 untuk mengatasi batas register: http://en.wikipedia.org/wiki/X86#Current_implementations .
sumber
Menggunakan bingkai tumpukan menjadi sangat murah di perangkat keras apa pun bahkan yang modern. Jika Anda memiliki stack frame yang murah, menyimpan beberapa register tidaklah penting. Saya yakin bingkai tumpukan cepat vs. lebih banyak register adalah trade-off teknik, dan bingkai tumpukan cepat menang.
Berapa banyak yang Anda hemat dengan mendaftar murni? Apakah itu layak?
sumber