Apakah kompiler diperbolehkan melipat-lipat volatile lokal secara konstan?

25

Pertimbangkan kode sederhana ini:

void g();

void foo()
{
    volatile bool x = false;
    if (x)
        g();
}

https://godbolt.org/z/I2kBY7

Anda dapat melihat bahwa tidak gccjuga clangmengoptimalkan potensi panggilan ke g. Ini benar dalam pemahaman saya: Mesin abstrak mengasumsikan bahwa volatilevariabel dapat berubah setiap saat (karena misalnya perangkat keras-dipetakan), jadi melipat- falseinisialisasi inisialisasi menjadi ifcek akan salah.

Tetapi MSVC menghilangkan panggilan untuk gsepenuhnya (menjaga membaca dan menulis volatilemeskipun!). Apakah perilaku sesuai standar ini?


Latar belakang: Saya kadang-kadang menggunakan konstruksi semacam ini untuk dapat mengaktifkan / menonaktifkan output debugging on-the-fly: Kompiler harus selalu membaca nilai dari memori, jadi mengubah variabel / memori selama debugging harus memodifikasi aliran kontrol sesuai . Output MSVC membaca ulang nilainya tetapi mengabaikannya (mungkin karena lipat dan / atau penghapusan kode mati), yang tentu saja mengalahkan niat saya di sini.


Suntingan:

  • Penghapusan membaca dan menulis volatiledibahas di sini: Apakah diizinkan untuk kompiler untuk mengoptimalkan variabel volatile lokal? (terima kasih Nathan!). Saya pikir standarnya sangat jelas bahwa membaca dan menulis itu harus terjadi. Tetapi diskusi itu tidak mencakup apakah legal bagi kompiler untuk mengambil hasil dari bacaan-bacaan itu dengan benar dan mengoptimalkan berdasarkan itu. Saya kira ini adalah standar yang tidak ditentukan , tetapi saya akan senang jika seseorang membuktikan saya salah.

  • Saya tentu saja dapat membuat xvariabel non-lokal untuk mengesampingkan masalah ini. Pertanyaan ini lebih karena penasaran.

Max Langhof
sumber
3
Ini terlihat seperti bug kompiler yang jelas bagi saya.
Sam Varshavchik
1
Sejauh yang saya tahu ini legal menurut aturan seolah-olah. Compiler dapat membuktikan bahwa meskipun objek tidak stabil, tidak ada cara untuk mengubah keadaan sehingga dapat dilipat keluar. Saya tidak cukup percaya diri untuk menjawabnya, tetapi saya merasa itu benar.
NathanOliver
3
Tapi saya pikir argumen OP bahwa variabel dapat dimodifikasi oleh debugger adalah masuk akal. Mungkin seseorang harus mengajukan laporan bug dengan MSVC.
Brian
2
@curiousguy Bahkan jika Anda membuang hasilnya dan / atau mengasumsikan nilai yang tepat, Anda masih membacanya.
Deduplicator
2
Menariknya hanya untuk x64. Versi x86 masih memanggil g () godbolt.org/z/nc3Y-f
Jerry Jeremiah

Jawaban:

2

Saya pikir [intro.execution] (nomor paragraf bervariasi) dapat digunakan untuk menjelaskan perilaku MSVC:

Sebuah instance dari setiap objek dengan durasi penyimpanan otomatis dikaitkan dengan setiap entri ke dalam bloknya. Objek semacam itu ada dan mempertahankan nilainya yang terakhir disimpan selama eksekusi blok dan ketika blok ditangguhkan ...

Standar tidak mengizinkan penghapusan membaca melalui glvalue yang mudah menguap, tetapi paragraf di atas dapat ditafsirkan sebagai memungkinkan untuk memprediksi nilai false.


BTW, Standar C (N1570 6.2.4 / 2) mengatakan itu

Objek ada, memiliki alamat konstan, dan mempertahankan nilai yang terakhir disimpan sepanjang masa pakainya. 34


34) Dalam kasus objek volatil, toko terakhir tidak perlu eksplisit dalam program ini.

Tidak jelas apakah mungkin ada toko non-eksplisit ke dalam objek dengan durasi penyimpanan otomatis dalam memori C / model objek.

Pengacara Bahasa
sumber
Setuju bahwa kompiler mungkin tahu kapan toko non-eksplisit dimungkinkan pada platform target
MM
1
Jadi jika ini benar, maka objek volatil lokal (setidaknya pada MSVC) sepenuhnya tidak berguna? Apakah ada sesuatu yang menambahkan volatilemembeli Anda (selain membaca / menulis berlebihan) jika diabaikan untuk tujuan optimasi?
Max Langhof
1
@ MaxLanghof Ada perbedaan antara sepenuhnya tidak berguna dan tidak memiliki efek yang Anda inginkan / harapkan.
Deduplicator
1
@MaxLanghof Hasil antara perhitungan floating point terkadang dipromosikan ke register 80-bit yang menyebabkan masalah presisi. Saya percaya ini adalah gcc-isme dan dihindari dengan menyatakan semua ganda seperti volatile. Lihat: gcc.gnu.org/bugzilla/show_bug.cgi?id=323
ForeverLearning
1
@ philipxy PS Lihat jawaban saya Saya tahu bahwa akses ditentukan oleh implementasi. Pertanyaannya bukan tentang akses (objek diakses), tetapi prediksi nilai.
Pengacara Bahasa
2

TL; DR Kompiler dapat melakukan apa pun yang diinginkan pada setiap akses volatil. Tetapi dokumentasi tersebut harus memberi tahu Anda .-- "Semantik dari akses melalui nilai yang mudah menguap ditentukan oleh implementasi."


Standar ini mendefinisikan untuk program yang diizinkan urutan "akses mudah menguap" & "perilaku yang dapat diamati" lainnya (dicapai melalui "efek samping") bahwa suatu implementasi harus menghormati per "aturan 'seolah-olah'.

Tetapi standar mengatakan (penekanan tebal saya):

Working Draft, Standar untuk Bahasa Pemrograman C ++
Nomor Dokumen: N4659
Tanggal: 2017-03-21

§ 10.1.7.1 Kualifikasi cv

5 Semantik dari akses melalui glvalue yang volatile ditentukan oleh implementasi. [...]

Demikian pula untuk perangkat interaktif (penekanan tebal saya):

§ 4.6 Eksekusi program

5 Implementasi yang sesuai mengeksekusi program yang terbentuk dengan baik harus menghasilkan perilaku yang dapat diamati sama seperti salah satu eksekusi yang mungkin dari mesin abstrak yang sesuai dengan program yang sama dan input yang sama. [...]

7 Persyaratan paling tidak pada implementasi yang sesuai adalah:

(7.1) - Akses melalui nilai yang mudah menguap dievaluasi secara ketat sesuai dengan aturan mesin abstrak.
(7.2) - Pada penghentian program, semua data yang ditulis ke dalam file harus identik dengan salah satu hasil yang mungkin dihasilkan oleh eksekusi program sesuai dengan semantik abstrak.
(7.3) - Dinamika input dan output dari perangkat interaktif harus berlangsung sedemikian rupa sehingga mendorong output benar-benar dikirimkan sebelum suatu program menunggu input. Apa yang merupakan perangkat interaktif ditentukan oleh implementasi.

Ini secara kolektif disebut sebagai perilaku yang dapat diamati dari program. [...]

(Pokoknya kode spesifik apa yang dihasilkan untuk suatu program tidak ditentukan oleh standar.)

Jadi, meskipun standar mengatakan bahwa akses volatil tidak dapat dielakkan dari urutan abstrak efek samping mesin abstrak & perilaku yang dapat diamati yang didefinisikan oleh beberapa kode, Anda tidak dapat mengharapkan sesuatu tercermin dalam kode objek atau dunia nyata perilaku kecuali dokumentasi kompiler Anda memberi tahu Anda apa yang merupakan akses volatil . Ditto untuk perangkat interaktif.

Jika Anda tertarik pada volatile vis a vis urutan abstrak dari efek samping mesin abstrak dan / atau perilaku yang dapat diamati yang didefinisikan oleh beberapa kode (mungkin) maka katakanlah demikian . Tetapi jika Anda tertarik pada kode objek apa yang dihasilkan, maka Anda harus menafsirkannya dalam konteks kompiler & kompilasi Anda .

Kronis orang secara keliru percaya bahwa untuk akses yang mudah menguap evaluasi mesin / dibaca abstrak menyebabkan membaca diimplementasikan & tugas mesin abstrak / write menyebabkan write dilaksanakan. Tidak ada dasar untuk keyakinan ini tidak ada dokumentasi implementasi mengatakan demikian. Ketika / jika implementasi mengatakan bahwa itu benar - benar melakukan sesuatu pada "akses volatile", orang dibenarkan dalam mengharapkan sesuatu - mungkin, generasi kode objek tertentu.

philipxy
sumber
1
Maksud saya, ini bermuara pada "jika saya datang dengan mesin di mana semua efek samping yang disebutkan adalah no-ops maka saya memiliki implementasi C ++ legal dengan mengkompilasi setiap program ke no-op" . Tentu saja kami tertarik pada efek yang dapat diamati secara praktis, karena efek samping mesin abstrak adalah abstrak secara tautologis. Ini memang memerlukan beberapa konsep dasar efek samping, dan saya berpendapat bahwa "akses volatile mengarah ke instruksi akses memori eksplisit" adalah bagian dari itu (bahkan jika standar tidak peduli), jadi saya tidak benar-benar membeli "katakan jika Anda menginginkan kode alih-alih semantik abstrak ". Tetap, +1.
Max Langhof
Ya, kualitas implementasi relevan. Namun demikian, selain menulis ke file, "diamati" [sic] adalah implementasi-didefinisikan. Jika Anda ingin dapat mengatur breakpoints, mengakses memori aktual tertentu, membiarkan 'volatile' diabaikan, dll pada abstrak volatile membaca & menulis maka Anda harus membuat penulis kompiler Anda untuk menghasilkan kode yang sesuai . PS Dalam C ++ "efek samping" tidak digunakan untuk mengamati perilaku saja, itu digunakan untuk menggambarkan evaluasi urutan sebagian dari subekspresi.
philipxy
Mau jelaskan [sic]? Sumber mana yang Anda kutip dan apa kesalahannya?
Max Langhof
Saya hanya bermaksud bahwa mesin abstrak yang dapat diamati hanya bisa diamati di dunia nyata jika & bagaimana implementasinya mengatakannya.
philipxy
1
Apakah Anda mengatakan bahwa suatu implementasi dapat mengklaim bahwa tidak ada yang namanya perangkat interaktif, sehingga program apa pun dapat melakukan apa saja, dan itu masih benar? (Catatan: Saya tidak mengerti penekanan Anda pada perangkat interaktif.)
curiousguy
-1

Saya percaya adalah sah untuk melewatkan cek.

Paragraf yang suka dikutip semua orang

34) Dalam kasus objek volatil, toko terakhir tidak perlu eksplisit dalam program ini

tidak menyiratkan bahwa suatu implementasi harus mengasumsikan toko-toko seperti itu dimungkinkan kapan saja, atau untuk variabel volatil apa pun. Implementasi tahu toko mana yang mungkin. Misalnya, sepenuhnya masuk akal untuk mengasumsikan bahwa penulisan implisit tersebut hanya terjadi untuk variabel volatil yang dipetakan ke register perangkat, dan pemetaan seperti itu hanya mungkin untuk variabel dengan tautan eksternal. Atau suatu implementasi dapat berasumsi bahwa penulisan semacam itu hanya terjadi pada lokasi memori yang sejajar kata-kata.

Karena itu, saya pikir perilaku MSVC adalah bug. Tidak ada alasan dunia nyata untuk mengoptimalkan panggilan. Optimalisasi seperti itu mungkin sesuai, tetapi sia-sia jahat.

n. 'kata ganti' m.
sumber
Bisakah Anda jelaskan mengapa itu jahat? Dalam kode yang ditampilkan, fungsi tersebut secara harfiah tidak pernah dapat dipanggil.
David Schwartz
@ Davidvidchchartz Anda hanya dapat menyimpulkan bahwa setelah Anda menentukan semantik variabel volatile lokal (lihat paragraf yang dikutip di atas). The standar itu sendiri catatan yang volatileseharusnya menjadi petunjuk untuk pelaksanaan yang nilai dapat mengubah dengan cara yang tidak diketahui pelaksanaan.
Max Langhof
@ MaxLanghof Tidak mungkin implementasi dapat menangani sesuatu yang tidak diketahui dengan benar. Apa sebenarnya platform yang berguna lakukan adalah menentukan apa yang Anda bisa dan tidak bisa gunakan volatileuntuk pada platform itu dan di luar spesifikasi itu, itu selalu akan menjadi omong kosong.
David Schwartz
@ DavidSchwartz Tentu saja bisa - dengan mengikuti semantik (khususnya, membaca dan menulis) dari mesin abstrak. Mungkin tidak dapat mengoptimalkan dengan benar - itulah inti dari standar. Sekarang, ini adalah catatan dan karenanya tidak normatif, dan seperti yang kami berdua katakan, implementasinya dapat menentukan apa yang volatiledilakukan dan tetap pada itu. Maksud saya adalah bahwa kode itu sendiri (sesuai dengan C ++ standar / mesin abstrak) tidak memungkinkan Anda untuk menentukan apakah gdapat dipanggil.
Max Langhof
@ DavidSchwartz Kode tidak menunjukkan hal seperti itu, karena tidak adanya penulisan implisit tidak mengikuti dari kode.
n. 'kata ganti' m.