Kadang-kadang kode C ++ tidak akan berfungsi ketika dikompilasi dengan beberapa tingkat optimasi. Mungkin kompiler melakukan optimasi yang memecahkan kode atau mungkin kode yang berisi perilaku tidak terdefinisi yang memungkinkan kompiler melakukan apa pun yang dirasakannya.
Misalkan saya memiliki beberapa kode yang rusak ketika dikompilasi dengan level optimisasi yang lebih tinggi saja. Bagaimana saya tahu apakah itu kode atau kompiler dan apa yang harus saya lakukan jika kompiler?
optimization
compiler
sharptooth
sumber
sumber
Jawaban:
Saya akan mengatakan itu adalah taruhan yang aman bahwa, dalam sebagian besar kasus, itu adalah kode Anda, bukan kompiler, yang rusak. Dan bahkan dalam kasus luar biasa ketika itu adalah kompiler, Anda mungkin menggunakan beberapa fitur bahasa yang tidak jelas dengan cara yang tidak biasa, di mana kompiler spesifik tidak disiapkan; dengan kata lain, Anda kemungkinan besar dapat mengubah kode Anda menjadi lebih idiomatis dan menghindari titik lemah dari kompiler.
Bagaimanapun, jika Anda dapat membuktikan bahwa Anda menemukan bug kompiler (berdasarkan spesifikasi bahasa), laporkan itu kepada pengembang kompiler, sehingga mereka dapat memperbaikinya beberapa saat.
sumber
Seperti biasa, seperti bug lainnya: lakukan percobaan terkontrol. Persempit area yang mencurigakan, matikan optimisasi untuk yang lain dan mulailah memvariasikan optimasi yang diterapkan pada potongan kode itu. Setelah Anda mendapatkan reproduksibilitas 100%, mulailah memvariasikan kode Anda, perkenalkan hal-hal yang dapat mematahkan optimisasi tertentu (misalnya, perkenalkan kemungkinan penunjuk aliasing, masukkan panggilan eksternal dengan efek samping potensial, dll.). Melihat kode rakitan dalam debugger mungkin membantu juga.
sumber
Periksa kode rakitan yang dihasilkan dan lihat apakah itu sesuai dengan yang diminta sumber Anda. Ingatlah bahwa kemungkinannya sangat tinggi sehingga kode Anda salah dengan cara yang tidak jelas.
sumber
Dalam lebih dari 30 tahun pemrograman, jumlah bug kompiler (pembuatan kode) asli yang saya temukan masih hanya ~ 10. Jumlah bug saya sendiri (dan orang lain) yang saya temukan dan perbaiki pada periode yang sama mungkin > 10.000. "Aturan praktis" saya kemudian adalah bahwa probabilitas setiap bug yang diberikan karena kompiler adalah <0,001.
sumber
Saya mulai menulis komentar dan kemudian memutuskan terlalu lama dan terlalu banyak intinya.
Saya berpendapat bahwa itu adalah kode Anda yang rusak. Jika Anda menemukan bug di kompiler - Anda harus melaporkannya ke pengembang kompiler, tetapi di situlah perbedaannya berakhir.
Solusinya adalah mengidentifikasi konstruk yang menyinggung, dan refactor sehingga akan melakukan logika yang sama secara berbeda. Itu kemungkinan besar akan memecahkan masalah, apakah bug ada di pihak Anda atau di kompiler.
sumber
sumber
int + int
overflow persis seperti jika dikompilasi ke instruksi ADD perangkat keras. Ini berfungsi dengan baik ketika dikompilasi dengan versi GCC yang lebih lama, tetapi tidak ketika dikompilasi dengan kompiler yang lebih baru. Tampaknya orang-orang baik di GCC memutuskan bahwa karena hasil bilangan bulat bilangan bulat tidak ditentukan, pengoptimal mereka dapat beroperasi dengan asumsi bahwa hal itu tidak pernah terjadi. Itu dioptimalkan cabang penting keluar dari kode.Jika Anda ingin tahu apakah itu kode atau kompiler, Anda harus mengetahui spesifikasi C ++ dengan sempurna.
Jika keraguan tetap ada, Anda harus benar-benar mengetahui perakitan x86.
Jika Anda tidak ingin mempelajari keduanya dengan sempurna, maka hampir pasti merupakan perilaku yang tidak ditentukan yang diselesaikan oleh kompiler Anda berbeda-beda tergantung pada level optimisasi.
sumber
Mendapatkan kesalahan kompilasi pada kode standar atau kesalahan kompilasi internal lebih mungkin terjadi daripada pengoptimal yang salah. Tetapi saya telah mendengar tentang kompiler mengoptimalkan loop salah melupakan beberapa efek samping penyebab metode.
Saya tidak punya saran tentang cara mengetahui apakah itu Anda atau kompiler. Anda dapat mencoba kompiler lain.
Suatu hari saya bertanya-tanya apakah itu kode saya atau tidak dan seseorang menyarankan valgrind kepada saya. Saya menghabiskan 5 atau 10 menit untuk menjalankan program saya dengan itu (saya pikir
valgrind --leak-check=yes myprog arg1 arg2
melakukannya tetapi saya bermain dengan pilihan lain) dan segera menunjukkan kepada saya SATU baris yang dijalankan di bawah satu kasus tertentu yang merupakan masalah. Kemudian aplikasi saya berjalan dengan lancar sejak itu tanpa crash aneh, kesalahan atau perilaku aneh. valgrind atau alat lain seperti itu adalah cara yang baik untuk mengetahui apakah itu kode Anda.Catatan: Saya pernah bertanya-tanya mengapa kinerja aplikasi saya payah. Ternyata semua masalah kinerja saya dalam satu baris juga. Saya menulis
for(int i=0; i<strlen(sz); ++i) {
. Sz itu beberapa mb. Untuk beberapa alasan, kompiler menjalankan strlen setiap kali bahkan setelah optimasi. Satu baris bisa menjadi masalah besar. Dari pertunjukan hingga tabrakansumber
Situasi yang semakin umum adalah bahwa kompiler memecah kode yang ditulis untuk dialek C yang mendukung perilaku yang tidak diamanatkan oleh Standar, dan memungkinkan kode penargetan dialek itu menjadi lebih efisien daripada kode yang benar-benar sesuai. Dalam kasus seperti itu, akan tidak adil untuk menggambarkan kode "rusak" yang 100% dapat diandalkan pada kompiler yang mengimplementasikan dialek target, atau menggambarkan sebagai "rusak" kompiler yang memproses dialek yang tidak mendukung semantik yang diperlukan . Alih-alih, masalahnya hanya berasal dari fakta bahwa bahasa yang diproses oleh kompiler modern dengan optimisasi diaktifkan berbeda dari dialek yang dulu populer (dan masih diproses oleh banyak kompiler dengan optimisasi dinonaktifkan, atau oleh beberapa bahkan dengan optimasi diaktifkan).
Sebagai contoh, banyak kode ditulis untuk dialek yang mengakui sejumlah pola penunjuk yang sah yang tidak diamanatkan oleh interpretasi gcc tentang Standar, dan memanfaatkan pola tersebut untuk memungkinkan terjemahan langsung kode menjadi lebih mudah dibaca dan efisien daripada yang mungkin di bawah interpretasi gcc tentang Standar C. Kode seperti itu mungkin tidak kompatibel dengan gcc, tetapi itu tidak berarti bahwa itu rusak. Itu hanya bergantung pada ekstensi yang hanya didukung gcc dengan optimisasi dinonaktifkan.
sumber
Mengisolasi titik bermasalah dan membandingkan perilaku yang diamati dengan apa yang harus terjadi sesuai dengan spesifikasi bahasa. Jelas tidak mudah, tetapi itulah yang harus Anda lakukan untuk mengetahui (dan tidak hanya berasumsi ).
Saya mungkin tidak akan selincis itu. Sebaliknya, saya akan meminta forum / milis dukungan pembuat kompiler. Jika itu benar-benar bug di kompiler, maka mereka mungkin memperbaikinya. Mungkin itu akan menjadi kode saya. Sebagai contoh, spesifikasi bahasa mengenai visibilitas memori dalam threading bisa sangat berlawanan dengan intuisi, dan mereka bisa menjadi jelas hanya ketika menggunakan beberapa flag optimasi tertentu, pada beberapa perangkat keras tertentu (!). Beberapa perilaku dapat tidak ditentukan oleh spec, sehingga dapat bekerja dengan beberapa kompiler / beberapa flag, dan tidak bekerja dengan yang lain, dll.
sumber
Kemungkinan besar kode Anda memiliki beberapa perilaku yang tidak terdefinisi (seperti yang dijelaskan orang lain, Anda lebih cenderung memiliki bug dalam kode Anda daripada di kompiler, bahkan jika kompiler C ++ sangat kompleks sehingga mereka memiliki bug; bahkan spesifikasi C ++ memiliki bug desain) . Dan UB dapat berada di sini bahkan jika executable yang dikompilasi kebetulan bekerja (dengan nasib buruk).
Jadi, Anda harus membaca blog Lattner ' What Every Programer C harus tahu tentang perilaku yang tidak terdefinisi (sebagian besar berlaku untuk C ++ 11 juga).
Alat valgrind , dan
-fsanitize=
opsi instrumentasi terbaru untuk GCC (atau Dentang / LLVM ), juga harus membantu. Dan tentu saja, aktifkan semua peringatan:g++ -Wall -Wextra
sumber