Apakah penulis kompiler benar-benar perlu 'memahami' kode mesin? [Tutup]

10

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.

Aviv Cohn
sumber
1
Tidak. Anda bahkan tidak perlu mengetahuinya, Anda hanya dapat secara membabi buta, tanpa sadar
SK-logic
1
Coffescript mengkompilasi ke javascript.
Kartik
@Kartik Apakah kompiler CoffeeScript yang mengkompilasi ke Javascript, juga termasuk kompiler Javascript yang mengkompilasi ke Javascript apa pun yang dikompilasi? Atau apakah itu hanya dikompilasi ke kode sumber Javascript dan tidak lebih?
Aviv Cohn
Kompiler coffeescript hanya mengubah cofeescript menjadi javascript. Javascript tidak dikompilasi, ini ditangani oleh browser. Saya ingin mengatakan bahwa Anda dapat menulis kompiler yang mengkompilasi satu bahasa ke bahasa lain, Anda tidak perlu tahu bahasa mesin untuk itu. Contoh lain adalah kompiler 'SPL' yang mengkompilasi drama Shakespeare ke C ++. shakespearelang.sourceforge.net
Kartik

Jawaban:

15

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.

amon
sumber
Dan properti bagus lain dari LLVM adalah bahwa seseorang tidak perlu memahami secara mendalam target ISA untuk mengimplementasikan backend yang efisien. Ini cukup deklaratif, sehingga orang hampir dapat menyalin dan menempelkan spesifikasi ISA ke dalam file .td bahkan tanpa mencoba memahaminya.
SK-logic
Terimakasih telah menjawab. Pertanyaan: Saya mengerti dari jawaban Anda dan jawaban orang lain bahwa kompiler-penulis tidak harus memahami kode mesin, ia dapat menggunakan alat lain yang melakukan konversi-ke-mesin-kode yang sebenarnya untuknya. Namun, pria yang menulis alat itu memang harus mengerti bahasa mesin, kan? Orang yang menulis perangkat lunak yang melakukan konversi sebenarnya dari beberapa bahasa ke kode mesin harus benar-benar mengerti bahasa mesin, kan?
Aviv Cohn
5
@Prog ya. Jika Anda membuat lapisan abstraksi, Anda hanya perlu memahami lapisan di bawah Anda. Meskipun berguna untuk memiliki pemahaman dasar tentang semua lapisan, ini sebenarnya tidak perlu. Anda tidak perlu memahami fisika kuantum untuk menggunakan transistor. Anda tidak perlu memahami desain chip untuk menggunakan CPU. Anda tidak perlu tahu kode mesin untuk menulis rakitan. Anda tidak perlu tahu set instruksi platform ketika menggunakan VM, dll. Tetapi seseorang harus membangun level abstraksi di bawah Anda.
amon
1
@ SK-logic Tidak benar (setidaknya jika Anda menginginkan kode yang baik) dari apa yang saya dengar. Orang-orang yang menerapkan backend Aarch64 untuk llvm memiliki beberapa tantangan (relokasi untuk satu, pola penyimpanan yang mengerikan, ..). Dan itu mengabaikan para gajah terbesar di dalam ruangan: model memori ISA dan model memori bahasa Anda sedang tertarik Anda dapat bekerja pada kompilator, tetapi Anda tidak bisa bekerja pada backend tanpa memahami arsitektur ...
Voo
1
Saya akan menambahkan bahwa sebenarnya tidak ada perbedaan substansial antara perakitan dan kode mesin, secara konsep. Anda tidak benar-benar akan mendapat banyak manfaat, begitu tahu bagaimana menggunakan instruksi tertentu, apa opcode instruksi itu. Jika Anda tahu cara menggunakan MOV, tidak masalah jika Anda tidak tahu bahwa itu adalah instruksi 27, yang saya pikir mirip dengan apa yang dijelaskan oleh @ SK-logic.
whatsisname
9

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.

Euforia
sumber
Dan orang yang menulis alat atau pustaka yang melakukan konversi sebenarnya ke bahasa mesin, harus mengerti bahasa mesin sepenuhnya, kan?
Aviv Cohn
3
@Prog Apakah Anda perlu memahami bahasa pemrograman sepenuhnya untuk memprogram di dalamnya? Tidak, tetapi Anda mungkin akan menulis kode kurang optimal, dan Anda tidak dapat melakukan hal-hal tertentu yang mungkin dapat dilakukan orang lain. Apakah Anda perlu memahami bahasa mesin sepenuhnya jika Anda menulis kompiler yang menerjemahkannya. Tidak, tetapi kompiler Anda akan menjadi kurang optimal, dan tidak mampu melakukan hal-hal tertentu.
Sumurai8
@ Sumurai8: meskipun sampai batas tertentu Anda dapat "memahami" bahasa mesin secara berurutan untuk menulis emitor kode mesin yang memahami semuanya lebih baik daripada Anda. Misalnya, jika Anda menulis kerangka kerja yang baik, Anda mungkin mengonfigurasi definisi setiap opcode bersama dengan pertimbangan biaya dan pipeliningnya, dan kemudian kerangka kerja Anda dapat menulis kode mesin yang dioptimalkan meskipun Anda tidak memiliki keahlian apa pun dalam mengoptimalkan mesin tertentu itu. Mampu memprogram kode mesin itu sendiri secara kompeten mungkin tidak ada salahnya.
Steve Jessop
@SteveJessop Jika Anda memahami setiap opcode ke suatu titik di mana Anda dapat mempelajari mesin bagaimana cara rantai opcode itu bersama-sama dengan opcodes lain untuk mengekspresikan konsep tingkat yang lebih tinggi, Anda benar-benar mengerti bahasa mesin. Anda kemudian terlalu malas untuk menemukan solusi optimal untuk setiap masalah di luar sana ;-)
Sumurai8
@ Sumurai8: hmm, tapi setidaknya pada prinsipnya saya mungkin "memahami" setiap opcode secara singkat selama lima menit yang saya perlukan untuk mengkonfigurasinya, dan kemudian melupakannya pada saat saya "memahami" opcode setelah berikutnya. Itu mungkin bukan apa yang dimaksud oleh penanya dengan "dapat membaca / menulis bahasa mesin mentah". Tentu saja saya mengasumsikan kerangka kerja yang sangat bagus di sini, itu cukup dapat dikonfigurasi untuk mendefinisikan dan menggunakan semua informasi yang berguna tentang setiap opcode dari set instruksi. LLVM agak bertujuan untuk ini tetapi menurut "Voo" (dalam komentar di bawah) belum mencapai itu.
Steve Jessop
3

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.

Charles E. Grant
sumber
2

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….

Ian
sumber
0

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.)

  1. 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)?

  2. 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?

  3. 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".)

  4. 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.

Logika Pengembaraan
sumber