Mungkin semacam pertanyaan aneh.
Seorang pria yang menulis kompiler C ++ (atau bahasa non-VM apa pun): Apakah dia harus bisa membaca / menulis bahasa mesin mentah? Bagaimana cara kerjanya?
EDIT: Saya secara khusus merujuk pada kompiler yang mengkompilasi ke kode mesin, bukan ke beberapa bahasa pemrograman lain.
compiler
machine-code
Aviv Cohn
sumber
sumber
Jawaban:
Tidak, tidak sama sekali. Sangat mungkin (dan bahkan sering preferrable) bagi kompiler Anda untuk mengeluarkan kode assembly. Assembler kemudian menangani pembuatan kode mesin yang sebenarnya.
By the way, membedakan Anda dari implementasi non-VM vs implementasi VM tidak berguna.
Sebagai permulaan, menggunakan VM atau prekompilasi ke kode mesin hanyalah beberapa cara berbeda untuk mengimplementasikan suatu bahasa; dalam kebanyakan kasus bahasa dapat diimplementasikan menggunakan salah satu strategi. Saya benar-benar harus menggunakan juru bahasa C ++ sekali.
Juga, banyak VM seperti JVM memiliki kode mesin biner dan assembler, seperti arsitektur biasa.
LLVM (yang digunakan oleh kompiler Dentang) layak disebutkan secara khusus di sini: Ini mendefinisikan VM yang instruksi dapat direpresentasikan sebagai kode byte, rakitan teks, atau struktur data yang membuatnya sangat mudah dipancarkan dari kompiler. Jadi meskipun itu akan berguna untuk debugging (dan untuk memahami apa yang Anda lakukan), Anda bahkan tidak perlu tahu tentang bahasa assembly, hanya tentang API LLVM.
Hal yang menyenangkan tentang LLVM adalah bahwa VM-nya hanyalah sebuah abstraksi, dan bahwa kode byte biasanya tidak ditafsirkan, tetapi sebaliknya JITted. Jadi sepenuhnya mungkin untuk menulis bahasa yang dikompilasi secara efektif, tanpa harus tahu tentang set instruksi CPU Anda.
sumber
Tidak. Poin utama dari pertanyaan Anda adalah kompilasi adalah istilah yang sangat luas. Kompilasi dapat terjadi dari bahasa apa pun ke bahasa apa pun. Dan kode assembly / mesin hanyalah salah satu dari banyak bahasa untuk target kompilasi. Misalnya bahasa Java dan .NET seperti C #, F # dan VB.NET semuanya mengkompilasi ke beberapa jenis kode perantara alih-alih kode khusus mesin. Tidak masalah jika kemudian dijalankan pada VM, bahasa masih dikompilasi. Ada juga opsi untuk mengkompilasi ke beberapa bahasa lain, seperti C. C sebenarnya target kompilasi yang cukup populer dan banyak alat melakukannya. Dan akhirnya, Anda bisa menggunakan beberapa alat atau pustaka untuk melakukan kerja keras menghasilkan kode mesin untuk Anda. ada misalnya LLVM yang dapat mengurangi upaya yang diperlukan untuk membuat kompilator mandiri.
Juga, hasil edit Anda tidak masuk akal. Itu seperti bertanya, "Apakah setiap insinyur perlu memahami cara kerja mesin? Dan saya bertanya tentang para insinyur yang mengerjakan mesin." Jika Anda mengerjakan program atau pustaka yang memancarkan kode mesin, maka Anda harus memahaminya. Intinya adalah, Anda tidak perlu melakukan hal seperti itu saat menulis compiler. Banyak orang melakukannya sebelum Anda, jadi Anda harus memiliki alasan serius untuk melakukannya lagi.
sumber
Secara klasik kompiler memiliki tiga bagian: analisis leksikal, parsing, dan pembuatan kode. Analisis leksikal memecah teks program menjadi kata kunci, nama, dan nilai bahasa. Parsing menggambarkan bagaimana token yang berasal dari analisis leksikal digabungkan dalam pernyataan sintaksis yang benar untuk bahasa. Pembuatan kode mengambil struktur data yang dihasilkan oleh parser, dan menerjemahkannya ke dalam kode mesin atau representasi lainnya. Saat ini analisis leksikal dan penguraian dapat digabungkan menjadi satu langkah.
Jelas orang yang menulis generator kode harus memahami kode mesin target pada level yang sangat dalam, termasuk set instruksi, jalur pipa prosesor, dan perilaku cache. Kalau tidak, program yang dihasilkan oleh kompiler akan lambat dan tidak efisien. Mereka sangat mungkin dapat membaca dan menulis kode mesin seperti diwakili oleh angka oktal atau heksadesimal, tetapi mereka umumnya akan menulis fungsi untuk menghasilkan kode mesin, merujuk secara internal ke tabel instruksi mesin. Secara teoritis orang-orang yang menulis lexer dan parser mungkin tidak tahu apa-apa tentang pembuatan kode mesin. Bahkan, beberapa kompiler modern memungkinkan Anda memasang rutinitas pembuatan kode sendiri yang mungkin memancarkan kode mesin untuk beberapa CPU yang belum pernah didengar oleh penulis lexer dan parser.
Namun, dalam praktiknya penulis kompiler pada setiap langkah tahu banyak tentang arsitektur prosesor yang berbeda, dan yang membantu mereka merancang struktur data langkah pembuatan kode akan diperlukan.
sumber
Beberapa waktu yang lalu saya menulis kompiler yang dikonversi antara dua skrip shell yang berbeda. Itu tidak mendekati kode mesin.
Tulis kompiler harus memahami outputnya , tetapi itu sering bukan kode mesin.
Sebagian besar programmer tidak akan pernah menulis kompiler yang mengeluarkan kode mesin atau kode perakitan, tetapi kompiler khusus dapat sangat berguna pada banyak proyek untuk menghasilkan output lain.
YACC adalah salah satu kompiler yang tidak mengeluarkan kode mesin….
sumber
Anda tidak perlu memulai dengan pengetahuan terperinci tentang semantik dari bahasa input dan output Anda, tetapi Anda lebih baik menyelesaikannya dengan pengetahuan yang sangat terperinci tentang keduanya, jika tidak, kompiler Anda akan menjadi buggy yang luar biasa. Jadi, jika input Anda adalah C ++ dan output Anda adalah beberapa bahasa mesin yang spesifik Anda pada akhirnya perlu mengetahui semantik keduanya.
Berikut adalah beberapa seluk-beluk dalam mengkompilasi C ++ ke kode mesin: (tepat di atas kepala saya, saya yakin ada lebih banyak lagi yang saya lupa.)
Berapa ukurannya
int
? Pilihan "benar" di sini adalah seni, berdasarkan pada ukuran penunjuk alami mesin, kinerja ALU untuk berbagai ukuran operasi aritmatika, dan pilihan yang dibuat oleh kompiler yang ada untuk mesin. Apakah mesin itu bahkan memiliki aritmatika 64-bit? Jika tidak maka penambahan bilangan bulat 32-bit harus diterjemahkan ke instruksi sementara penambahan bilangan bulat 64-bit harus diterjemahkan ke panggilan fungsi untuk melakukan penambahan 64-bit. Apakah mesin memiliki operasi penambahan 8-bit dan 16-bit atau apakah Anda harus mensimulasikan operasi dengan operasi dan masking 32-bit (mis. DEC Alpha 21064)?Apa konvensi pemanggilan yang digunakan oleh kompiler lain, pustaka dan bahasa pada mesin? Apakah parameter didorong pada tumpukan kanan-ke-kiri atau kiri-ke-kanan? Apakah beberapa parameter masuk register sementara yang lain masuk stack? Apakah int dan mengapung di ruang register yang berbeda? Apakah parameter yang dialokasikan register perlu diperlakukan secara khusus pada panggilan varargs? Register mana yang disimpan oleh penelepon dan mana yang diselamatkan? Bisakah Anda melakukan optimasi panggilan-daun?
Apa yang dilakukan masing-masing instruksi shift mesin? Jika Anda meminta untuk menggeser integer 64 bit dengan 65 bit apa hasilnya? (Pada banyak mesin hasilnya sama dengan menggeser 1 bit, pada yang lain hasilnya "0".)
Apa semantik konsistensi memori mesin? C ++ 11 memiliki semantik memori yang terdefinisi dengan sangat baik yang menempatkan pembatasan pada beberapa optimisasi dalam beberapa kasus, tetapi memungkinkan optimisasi dalam kasus lain. Jika Anda mengkompilasi bahasa yang tidak memiliki semantik memori yang terdefinisi dengan baik (seperti setiap versi C / C ++ sebelum C ++ 11, dan banyak bahasa imperatif lainnya) maka Anda harus menciptakan semantik memori saat Anda melanjutkan, dan biasanya Anda akan ingin menciptakan semantik memori yang paling cocok dengan semantik mesin Anda.
sumber