Standar C11 tampaknya menyiratkan bahwa pernyataan iterasi dengan ekspresi kontrol konstan tidak boleh dioptimalkan. Saya menerima saran saya dari jawaban ini , yang secara khusus mengutip bagian 6.8.5 dari konsep standar:
Pernyataan iterasi yang ekspresi pengontrolnya bukan ekspresi konstan ... dapat diasumsikan oleh implementasi untuk diakhiri.
Dalam jawaban itu disebutkan bahwa perulangan seperti while(1) ;
tidak boleh dikenai optimasi.
Jadi ... mengapa Dentang / LLVM mengoptimalkan loop di bawah ini (dikompilasi dengan cc -O2 -std=c11 test.c -o test
)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
Di mesin saya, ini dicetak begin
, kemudian crash pada instruksi ilegal ( ud2
jebakan ditempatkan setelah die()
). Pada godbolt , kita dapat melihat bahwa tidak ada yang dihasilkan setelah panggilan ke puts
.
Sudah menjadi tugas yang sangat sulit untuk mendapatkan Dentang untuk menghasilkan loop tak terbatas di bawah -O2
- sementara saya bisa berulang kali menguji volatile
variabel, yang melibatkan memori baca yang tidak saya inginkan. Dan jika saya melakukan sesuatu seperti ini:
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x = 1;
if(x)
die();
printf("unreachable\n");
}
... Dentang cetakan begin
diikuti unreachable
seolah-olah infinite loop tidak pernah ada.
Bagaimana Anda membuat Dentang untuk menghasilkan loop tak terbatas tanpa-memori-akses yang tepat dengan optimisasi dihidupkan?
sumber
exit()
, dan karena kode mungkin telah menemukan situasi di mana ia tidak dapat menjamin bahwa efek dari eksekusi lanjutan tidak akan lebih buruk daripada tidak berguna . Lompat-ke-diri adalah cara yang cukup buruk untuk menangani situasi seperti itu, tetapi mungkin ini cara terbaik untuk menangani situasi yang buruk.Jawaban:
Standar C11 mengatakan ini, 6.8.5 / 6:
Catatan dua kaki tidak normatif tetapi memberikan informasi yang berguna:
Dalam kasus Anda,
while(1)
adalah ekspresi konstan sebening kristal, sehingga mungkin tidak diasumsikan oleh implementasi untuk mengakhiri. Implementasi seperti itu akan putus harapan, karena loop "selamanya" adalah konstruksi pemrograman yang umum.Apa yang terjadi pada "kode yang tidak terjangkau" setelah loop adalah, sejauh yang saya tahu, tidak didefinisikan dengan baik. Namun, dentang memang berperilaku sangat aneh. Membandingkan kode mesin dengan gcc (x86):
gcc 9.2
-O3 -std=c11 -pedantic-errors
dentang 9.0.0
-O3 -std=c11 -pedantic-errors
gcc menghasilkan loop, dentang hanya berjalan ke hutan dan keluar dengan kesalahan 255.
Saya condong ke arah ini karena tingkah laku tidak patuh. Karena saya mencoba untuk memperluas contoh Anda lebih lanjut seperti ini:
Saya menambahkan C11
_Noreturn
dalam upaya untuk membantu kompilator lebih jauh. Harus jelas bahwa fungsi ini akan menutup telepon, dari kata kunci itu saja.setjmp
akan mengembalikan 0 pada eksekusi pertama, jadi program ini seharusnya hanya menabrakwhile(1)
dan berhenti di sana, hanya mencetak "mulai" (dengan asumsi \ n flushes stdout). Ini terjadi dengan gcc.Jika pengulangan hanya dihapus, itu harus mencetak "mulai" 2 kali kemudian mencetak "tidak terjangkau". Namun pada dentang ( godbolt ), ia mencetak "mulai" 1 kali dan kemudian "tidak dapat dijangkau" sebelum mengembalikan kode keluar 0. Itu benar-benar salah, tidak peduli bagaimana Anda memasukkannya.
Saya tidak dapat menemukan kasus untuk mengklaim perilaku tidak terdefinisi di sini, jadi pendapat saya adalah bahwa ini adalah bug di dentang. Bagaimanapun, perilaku ini membuat dentang 100% tidak berguna untuk program seperti embedded system, di mana Anda harus bisa mengandalkan loop kekal yang menggantung program (sambil menunggu anjing penjaga dll).
sumber
6.8.5/6
dalam bentuk if (ini) maka Anda dapat menganggap (ini) . Itu tidak berarti jika tidak (ini) Anda mungkin tidak menganggap (ini) . Ini spesifikasi hanya untuk saat kondisi terpenuhi, bukan ketika mereka tidak terpenuhi di mana Anda dapat melakukan apa pun yang Anda inginkan dengan standar. Dan jika tidak ada yang bisa diamati ...int z=3; int y=2; int x=1; printf("%d %d\n", x, z);
sana tidak ada2
di majelis, jadi dalam arti kosong tidak bergunax
setelahy
tetapi setelahz
karena optimasi. Jadi, dari kalimat terakhir Anda, kami mengikuti aturan reguler, menganggap sementara itu dihentikan (karena kami tidak dibatasi lebih baik), dan meninggalkan dalam cetakan akhir, "tidak terjangkau". Sekarang, kami mengoptimalkan pernyataan tidak berguna itu (karena kami tidak tahu yang lebih baik).while(1);
sama denganint y = 2;
pernyataan dalam hal apa semantik yang kita boleh optimalkan, bahkan jika logika mereka tetap di sumbernya. Dari n1528 saya mendapat kesan bahwa mereka mungkin sama, tetapi karena orang yang lebih berpengalaman daripada saya berdebat dengan cara lain, dan itu adalah bug resmi rupanya, kemudian melampaui debat filosofis tentang apakah kata-kata dalam standar itu eksplisit. , argumen tersebut diterjemahkan menjadi moot.Anda perlu memasukkan ekspresi yang dapat menyebabkan efek samping.
Solusi paling sederhana:
Tautan Godbolt
sumber
asm("")
secara implisitasm volatile("");
dan dengan demikian pernyataan asm harus berjalan sebanyak yang dilakukannya di mesin abstrak gcc.gnu.org/onlinedocs/gcc/Basic-Asm.html . (Perhatikan bahwa efek sampingnya tidak aman untuk menyertakan memori atau register; Anda perlu Memperpanjang asm dengan"memory"
clobber jika Anda ingin membaca atau menulis memori yang pernah Anda akses dari C. Asm dasar hanya aman untuk hal-hal sepertiasm("mfence")
ataucli
.)Jawaban lain sudah membahas cara-cara untuk membuat Dentang memancarkan loop tak terbatas, dengan bahasa rakitan inline atau efek samping lainnya. Saya hanya ingin mengkonfirmasi bahwa ini memang bug kompiler. Secara khusus, ini adalah bug LLVM lama - ini menerapkan konsep C ++ dari "semua loop tanpa efek samping harus diakhiri" ke bahasa yang tidak seharusnya, seperti C.
Sebagai contoh, bahasa pemrograman Rust juga memungkinkan loop tak terbatas dan menggunakan LLVM sebagai backend, dan memiliki masalah yang sama.
Dalam jangka pendek, tampaknya LLVM akan terus menganggap bahwa "semua loop tanpa efek samping harus diakhiri". Untuk bahasa apa pun yang memungkinkan loop tak terbatas, LLVM mengharapkan front-end untuk memasukkan
llvm.sideeffect
opcode ke dalam loop tersebut. Ini adalah apa yang direncanakan Rust untuk dilakukan, jadi Dentang (ketika mengkompilasi kode C) mungkin harus melakukannya juga.sumber
sideeffect
op (pada 2017) dan mengharapkan front-end untuk memasukkan op itu ke dalam loop atas kebijakan mereka. LLVM harus memilih beberapa default untuk loop, dan kebetulan memilih salah satu yang sejalan dengan perilaku C ++, sengaja atau tidak. Tentu saja, masih ada beberapa pekerjaan optimasi yang harus dilakukan, seperti menggabungkansideeffect
ops berturut-turut menjadi satu. (Inilah yang menghalangi front-end Rust untuk menggunakannya.) Jadi atas dasar itu, bug ada di front-end (dentang) yang tidak memasukkan op dalam loop.sideeffect
ops ke awal setiap fungsi dan tidak melihat adanya regresi kinerja runtime. Satu-satunya masalah adalah regresi waktu kompilasi , tampaknya karena kurangnya penggabungan ops berturut-turut seperti yang saya sebutkan dalam komentar saya sebelumnya.Ini adalah bug Dentang
... saat menggarisbawahi suatu fungsi yang mengandung infinite loop. Perilaku ini berbeda ketika
while(1);
muncul langsung di utama, yang baunya sangat buggy bagi saya.Lihat jawaban @ Arnavion untuk ringkasan dan tautan. Sisa jawaban ini ditulis sebelum saya mendapat konfirmasi bahwa itu adalah bug, apalagi bug yang dikenal.
Untuk menjawab pertanyaan judul: Bagaimana cara membuat loop kosong tanpa batas yang tidak akan dioptimalkan? ? -
buat
die()
makro, bukan fungsi , untuk mengatasi bug ini di Dentang 3.9 dan yang lebih baru. (Sebelumnya versi dentang baik membuat loop atau memancarkancall
ke versi non-inline fungsi dengan loop tak terbatas.) Itu muncul untuk menjadi aman bahkan jikaprint;while(1);print;
inlines fungsi menjadi nya pemanggil ( Godbolt ).-std=gnu11
vs-std=gnu99
tidak mengubah apa pun.Jika Anda hanya peduli dengan GNU C, P__J___ di
__asm__("");
dalam loop juga berfungsi, dan seharusnya tidak merusak optimasi kode di sekitarnya untuk setiap kompiler yang memahaminya. GNU C Pernyataan asm dasar secara implisitvolatile
, jadi ini dianggap sebagai efek samping yang terlihat yang harus "mengeksekusi" sebanyak yang akan terjadi pada mesin abstrak C. (Dan ya, Clang mengimplementasikan dialek C GNU, seperti yang didokumentasikan oleh manual GCC.)Beberapa orang berpendapat bahwa mungkin saja mengoptimalkan loop kosong tanpa batas yang sah. Saya tidak setuju 1 , tetapi bahkan jika kami menerimanya, tidak bisa juga legal bagi Dentang untuk mengambil pernyataan setelah perulangan tidak dapat dijangkau, dan membiarkan eksekusi jatuh dari ujung fungsi ke fungsi berikutnya, atau menjadi sampah yang menerjemahkan sebagai instruksi acak.
(Itu akan memenuhi standar untuk Clang ++ (tapi masih tidak terlalu berguna); loop tak terbatas tanpa efek samping adalah UB dalam C ++, tetapi tidak C.
Apakah sementara (1); perilaku tidak terdefinisi dalam C? UB memungkinkan kompiler memancarkan apa pun pada dasarnya untuk kode di jalur eksekusi yang pasti akan menemui UB.
asm
Pernyataan dalam loop akan menghindari UB ini untuk C ++. Namun dalam praktiknya, Clang mengkompilasi sebagai C ++ tidak menghapus ekspresi konstan loop kosong tak terbatas kecuali saat inlining, sama seperti ketika kompilasi sebagai C.)Inlining secara manual
while(1);
mengubah cara Clang mengkompilasinya: infinite loop hadir dalam asm. Ini adalah apa yang kami harapkan dari POV aturan-pengacara.Di explorer compiler Godbolt, Dentang 9.0 -O3 dikompilasi sebagai C (
-xc
) untuk x86-64:Kompiler yang sama dengan opsi yang sama mengkompilasi a
main
yang memanggilinfloop() { while(1); }
ke yang sama pertamaputs
, tetapi kemudian hanya berhenti memancarkan instruksi untukmain
setelah titik itu. Jadi seperti yang saya katakan, eksekusi hanya jatuh dari ujung fungsi, ke fungsi apa pun yang berikutnya (tetapi dengan tumpukan tidak selaras untuk entri fungsi sehingga bahkan tidak ada panggilan masuk yang valid).Opsi yang valid adalah untuk
label: jmp label
loop tak terbatasreturn 0
darimain
.Menerjang atau melanjutkan tanpa mencetak "tidak dapat dijangkau" jelas tidak ok untuk implementasi C11, kecuali ada UB yang belum saya perhatikan.
Catatan kaki 1:
Sebagai catatan, saya setuju dengan jawaban @ Lundin yang mengutip standar untuk bukti bahwa C11 tidak mengizinkan asumsi penghentian untuk loop tak terbatas ekspresi konstan, bahkan ketika mereka kosong (tidak ada I / O, volatile, sinkronisasi, atau lainnya efek samping yang terlihat).
Ini adalah serangkaian kondisi yang memungkinkan loop dikompilasi ke loop asm kosong untuk CPU normal. (Bahkan jika tubuh tidak kosong di sumber, tugas ke variabel tidak dapat dilihat oleh utas lain atau penangan sinyal tanpa data-ras UB saat loop sedang berjalan. Jadi implementasi yang sesuai dapat menghapus tubuh loop seperti itu jika diinginkan Kemudian yang meninggalkan pertanyaan apakah loop itu sendiri dapat dihapus. ISO C11 secara eksplisit mengatakan tidak.)
Mengingat bahwa C11 memilih kasus itu sebagai kasus di mana implementasi tidak dapat mengasumsikan loop berakhir (dan bahwa itu bukan UB), tampaknya jelas mereka berniat loop hadir pada saat run-time. Implementasi yang menargetkan CPU dengan model eksekusi yang tidak dapat melakukan jumlah pekerjaan tak terbatas dalam waktu terbatas tidak memiliki alasan untuk menghapus loop infinite konstan kosong. Atau bahkan secara umum, kata-kata yang tepat adalah tentang apakah mereka dapat "diasumsikan berakhir" atau tidak. Jika sebuah loop tidak dapat diakhiri, itu berarti kode yang lebih baru tidak dapat dijangkau, tidak peduli argumen apa yang Anda buat tentang matematika dan ketidakterbatasan dan berapa lama waktu yang diperlukan untuk melakukan pekerjaan tak terbatas pada beberapa mesin hipotetis.
Lebih jauh dari itu, Clang bukan hanya DeathStation 9000 yang sesuai dengan ISO C, itu dimaksudkan untuk berguna untuk pemrograman sistem tingkat rendah dunia nyata, termasuk kernel dan hal-hal yang tertanam. Jadi apakah Anda menerima argumen tentang C11 yang memungkinkan penghapusan
while(1);
, tidak masuk akal bahwa Clang ingin benar-benar melakukan itu. Jika Anda menuliswhile(1);
, itu mungkin bukan kecelakaan. Penghapusan loop yang berakhir tanpa batas oleh kecelakaan (dengan ekspresi kontrol variabel runtime) dapat berguna, dan masuk akal bagi kompiler untuk melakukan itu.Jarang Anda ingin hanya berputar sampai interupsi berikutnya, tetapi jika Anda menuliskannya dalam C, itulah yang Anda harapkan akan terjadi. (Dan apa yang terjadi di GCC dan dentang, kecuali untuk dentang ketika loop tak terbatas di dalam fungsi pembungkus).
Misalnya, dalam kernel OS primitif, ketika scheduler tidak memiliki tugas untuk menjalankannya mungkin menjalankan tugas idle. Implementasi pertama mungkin
while(1);
.Atau untuk perangkat keras tanpa fitur siaga hemat daya, itu mungkin satu-satunya implementasi. (Sampai awal 2000-an, itu saya pikir tidak jarang pada x86. Meskipun
hlt
instruksi itu ada, IDK jika itu menghemat jumlah daya yang berarti sampai CPU mulai memiliki status siaga daya rendah.)sumber
-ffreestanding -fno-strict-aliasing
. Ini berfungsi baik dengan ARM dan mungkin dengan AVR lama.Hanya sebagai catatan, Clang juga bertingkah dengan
goto
:Ini menghasilkan output yang sama seperti pada pertanyaan, yaitu:
Saya melihat tidak melihat cara untuk membaca ini sebagaimana diizinkan dalam C11, yang hanya mengatakan:
Karena
goto
ini bukan "pernyataan iterasi" (6.8.5 daftarwhile
,do
danfor
) tidak ada apa-apa tentang indulgensi khusus "diasumsikan pemutusan" berlaku, namun Anda ingin membacanya.Per kompiler tautan Godbolt pertanyaan asli adalah x86-64 Dentang 9.0.0 dan flag-flagnya adalah
-g -o output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-9.2.0 -fcolor-diagnostics -fno-crash-diagnostics -O2 -std=c11 example.c
Dengan yang lain seperti x86-64 GCC 9.2 Anda mendapatkan yang cukup sempurna:
Bendera:
-g -o output.s -masm=intel -S -fdiagnostics-color=always -O2 -std=c11 example.c
sumber
nasty: goto nasty
bisa menyesuaikan dan tidak memutar CPU (s) sampai pengguna atau sumber daya mengalami gangguan.bar()
dalamfoo()
diproses sebagai panggilan dari__1foo
ke__2bar
, dari__2foo
ke__3bar
, dll dan dari__16foo
ke__launch_nasal_demons
, yang kemudian akan memungkinkan semua objek otomatis dialokasikan secara statis, dan akan membuat apa yang biasanya menjadi batas "waktu berjalan" menjadi batas terjemahan.Saya akan berperan sebagai pendukung iblis dan berpendapat bahwa standar tersebut tidak secara eksplisit melarang kompiler untuk mengoptimalkan loop tanpa batas.
Mari kita uraikan ini. Pernyataan iterasi yang memenuhi kriteria tertentu dapat diasumsikan berakhir:
Ini tidak mengatakan apa-apa tentang apa yang terjadi jika kriteria tidak terpenuhi dan dengan asumsi bahwa loop dapat berakhir bahkan tidak dilarang secara eksplisit selama aturan standar lainnya diamati.
do { } while(0)
atauwhile(0){}
setelah semua pernyataan iterasi (loop) yang tidak memenuhi kriteria yang memungkinkan kompiler untuk hanya mengasumsikan pada kemauan bahwa mereka mengakhiri dan namun mereka jelas-jelas mengakhiri.Tetapi dapatkah kompiler hanya mengoptimalkan
while(1){}
?5.1.2.3p4 mengatakan:
Ini menyebutkan ekspresi, bukan pernyataan, jadi tidak 100% meyakinkan, tetapi tentu saja memungkinkan panggilan seperti:
untuk dilewati. Menariknya, dentang memang melewatkannya, dan gcc tidak .
sumber
while(1){}
apakah urutan1
evaluasi yang tak terbatas terkait dengan{}
evaluasi, tetapi di mana dalam standarnya dikatakan evaluasi itu perlu waktu yang tidak nol ? Perilaku gcc lebih berguna, saya kira, karena Anda tidak perlu trik yang melibatkan akses memori atau trik di luar bahasa. Tapi saya tidak yakin bahwa standar melarang optimasi ini di dentang. Jika membuatwhile(1){}
tidak dioptimalkan adalah niat, standar harus eksplisit tentang hal itu dan perulangan tak terbatas harus terdaftar sebagai efek samping yang dapat diamati dalam 5.1.2.3p2.1
kondisi sebagai perhitungan nilai. Waktu eksekusi tidak masalah - yang penting adalah apa yangwhile(A){} B;
mungkin tidak dioptimalkan sepenuhnya, tidak dioptimalkan untukB;
dan tidak diurutkan kembaliB; while(A){}
. Mengutip mesin abstrak C11, penekanan pada tambang: "Kehadiran titik sekuens antara evaluasi ekspresi A dan B menyiratkan bahwa setiap perhitungan nilai dan efek samping yang terkait dengan A diurutkan sebelum setiap perhitungan nilai dan efek samping yang terkait dengan B. " NilaiA
ini jelas digunakan (oleh loop).Saya yakin ini hanyalah bug lama. Saya meninggalkan tes saya di bawah ini dan khususnya referensi untuk diskusi di komite standar untuk beberapa alasan yang saya miliki sebelumnya.
Saya pikir ini adalah perilaku yang tidak terdefinisi (lihat akhir), dan Dentang hanya memiliki satu implementasi. GCC memang berfungsi seperti yang Anda harapkan, hanya mengoptimalkan
unreachable
pernyataan cetak tetapi meninggalkan loop. Entah bagaimana Clang secara aneh membuat keputusan saat menggabungkan in-lining dan menentukan apa yang bisa dilakukan dengan loop.Perilaku ini sangat aneh - menghapus cetakan akhir, jadi "melihat" loop tak terbatas, tetapi kemudian menyingkirkan loop juga.
Itu lebih buruk sejauh yang saya tahu. Menghapus inline yang kita dapatkan:
jadi fungsinya dibuat, dan panggilan dioptimalkan keluar. Ini bahkan lebih tangguh dari yang diharapkan:
menghasilkan perakitan yang sangat tidak optimal untuk fungsi tersebut, tetapi pemanggilan fungsi kembali dioptimalkan! Lebih buruk lagi:
Saya membuat banyak tes lain dengan menambahkan variabel lokal dan meningkatkannya, melewati pointer, menggunakan
goto
dll ... Pada titik ini saya akan menyerah. Jika Anda harus menggunakan dentangmelakukan pekerjaan. Ini payah dalam mengoptimalkan (jelas), dan pergi ke final yang berlebihan
printf
. Setidaknya programnya tidak berhenti. Mungkin GCC?Tambahan
Setelah berdiskusi dengan David, saya berpendapat bahwa standar tidak mengatakan "jika kondisinya konstan, Anda mungkin tidak menganggap loop berakhir". Dengan demikian, dan diberikan berdasarkan standar tidak ada perilaku yang dapat diamati (seperti yang didefinisikan dalam standar), saya hanya akan berdebat tentang konsistensi - jika kompiler mengoptimalkan loop karena menganggap itu berhenti, seharusnya tidak mengoptimalkan pernyataan berikut.
Heck n1528 memiliki ini sebagai perilaku yang tidak terdefinisi jika saya membacanya dengan benar. Secara khusus
Dari sini saya pikir itu hanya bisa beralih ke diskusi tentang apa yang kita inginkan (diharapkan?) Daripada apa yang diizinkan.
sumber
Tampaknya ini adalah bug dalam kompiler Dentang. Jika tidak ada paksaan pada
die()
fungsi menjadi fungsi statis, hapusstatic
dan buatinline
:Ini berfungsi seperti yang diharapkan ketika dikompilasi dengan kompiler Dentang dan portabel juga.
Explorer Compiler (godbolt.org) - dentang 9.0.0
-O3 -std=c11 -pedantic-errors
sumber
static inline
?Tampaknya ini berfungsi untuk saya:
di godbolt
Secara eksplisit mengatakan Dentang untuk tidak mengoptimalkan bahwa satu fungsi menyebabkan loop tak terbatas akan dipancarkan seperti yang diharapkan. Semoga ada cara untuk menonaktifkan optimasi tertentu secara selektif daripada hanya mematikannya seperti itu. Dentang masih menolak untuk mengeluarkan kode untuk yang kedua
printf
, meskipun. Untuk memaksanya melakukan itu, saya harus lebih lanjut memodifikasi kode di dalamnyamain
menjadi:Sepertinya Anda harus menonaktifkan pengoptimalan untuk fungsi loop tak terbatas Anda, lalu memastikan bahwa loop tak terbatas Anda disebut kondisional. Di dunia nyata, yang terakhir hampir selalu terjadi.
sumber
printf
dihasilkan jika loop benar-benar pergi selamanya, karena dalam kasus yang keduaprintf
benar-benar tidak dapat dijangkau dan karena itu dapat dihapus. (Kesalahan dentang adalah dalam mendeteksi unreachability dan kemudian menghapus loop sehingga kode yang tidak terjangkau tercapai).__attribute__ ((optimize(1)))
, tetapi dentang mengabaikannya sebagai tidak didukung: godbolt.org/z/4ba2HM . gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.htmlImplementasi yang sesuai dapat, dan banyak yang praktis, memberlakukan batasan sewenang-wenang pada berapa lama suatu program dapat dijalankan atau berapa banyak instruksi yang akan dijalankannya, dan berperilaku secara sewenang-wenang jika batasan tersebut dilanggar atau - di bawah aturan "seolah-olah" --Jika itu menentukan bahwa mereka pasti akan dilanggar. Asalkan suatu implementasi dapat berhasil memproses setidaknya satu program yang secara nominal menjalankan semua batas yang tercantum dalam N1570 5.2.4.1 tanpa mencapai batas terjemahan, keberadaan batas, sejauh mana dokumen tersebut didokumentasikan, dan dampak dari melampauinya, adalah semua masalah Kualitas Penerapan di luar yurisdiksi Standar.
Saya pikir maksud Standar cukup jelas bahwa penyusun tidak boleh berasumsi bahwa
while(1) {}
perulangan tanpa efek samping ataubreak
pernyataan akan berakhir. Bertentangan dengan apa yang mungkin dipikirkan oleh beberapa orang, penulis Standar tidak mengundang penulis kompiler untuk menjadi bodoh atau bodoh. Implementasi yang sesuai mungkin berguna untuk memutuskan untuk menghentikan program apa pun yang, jika tidak terganggu, akan menjalankan lebih banyak instruksi efek samping daripada ada atom di alam semesta, tetapi implementasi kualitas tidak boleh melakukan tindakan seperti itu berdasarkan asumsi apa pun tentang pemberhentian tetapi lebih atas dasar bahwa hal itu bisa berguna, dan tidak akan (tidak seperti perilaku dentang) lebih buruk daripada tidak berguna.sumber
Loop tidak memiliki efek samping, sehingga dapat dioptimalkan. Pengulangan secara efektif merupakan jumlah iterasi tanpa batas dari nol unit kerja. Ini tidak terdefinisi dalam matematika dan logika dan standar tidak mengatakan apakah suatu implementasi diizinkan untuk menyelesaikan banyak hal jika setiap hal dapat dilakukan dalam waktu nol. Interpretasi dentang sangat masuk akal dalam memperlakukan infinity kali nol sebagai nol daripada infinity. Standar tidak mengatakan apakah loop infinite dapat berakhir atau tidak jika semua pekerjaan dalam loop sudah selesai.
Kompiler diizinkan untuk mengoptimalkan segala sesuatu yang bukan perilaku yang dapat diamati sebagaimana didefinisikan dalam standar. Itu termasuk waktu eksekusi. Tidak perlu menjaga fakta bahwa perulangan, jika tidak dioptimalkan, akan membutuhkan waktu yang tidak terbatas. Hal ini diizinkan untuk mengubah itu ke waktu lari yang jauh lebih pendek - pada kenyataannya, itulah poin dari kebanyakan optimasi. Loop Anda dioptimalkan.
Bahkan jika dentang menerjemahkan kode secara naif, Anda dapat membayangkan sebuah CPU yang mengoptimalkan yang dapat menyelesaikan setiap iterasi dalam setengah waktu iterasi sebelumnya. Itu benar-benar akan menyelesaikan loop tak terbatas dalam jumlah waktu yang terbatas. Apakah mengoptimalkan CPU seperti itu melanggar standar? Tampaknya tidak masuk akal untuk mengatakan bahwa mengoptimalkan CPU akan melanggar standar jika terlalu baik dalam mengoptimalkan. Hal yang sama berlaku untuk kompiler.
sumber
Saya minta maaf jika ini bukan kepalang masalahnya, saya menemukan posting ini dan saya tahu karena tahun-tahun saya menggunakan distro Gentoo Linux bahwa jika Anda ingin kompiler tidak mengoptimalkan kode Anda, Anda harus menggunakan -O0 (Nol). Saya ingin tahu tentang hal itu, dan mengkompilasi dan menjalankan kode di atas, dan loop tidak berjalan tanpa batas. Dikompilasi menggunakan clang-9:
sumber
while
Loop kosong tidak memiliki efek samping pada sistem.Oleh karena itu Dentang menghapusnya. Ada beberapa cara "lebih baik" untuk mencapai perilaku yang diinginkan yang memaksa Anda untuk lebih jelas tentang niat Anda.
while(1);
adalah baaadd.sumber
abort()
atauexit()
. Jika situasi muncul di mana fungsi menentukan bahwa (mungkin sebagai akibat dari kerusakan memori) eksekusi lanjutan akan lebih buruk daripada berbahaya, perilaku standar umum untuk pustaka tertanam adalah untuk memanggil fungsi yang melakukan awhile(1);
. Mungkin bermanfaat bagi kompiler untuk memiliki opsi untuk menggantikan perilaku yang lebih bermanfaat , tetapi penulis kompiler yang tidak dapat menemukan cara memperlakukan konstruksi sederhana seperti itu sebagai penghalang untuk melanjutkan program tidak kompeten untuk dipercaya dengan optimisasi yang kompleks.