Rozważ ten prosty kod:
void g();
void foo()
{
volatile bool x = false;
if (x)
g();
}
Widać, że ani gcc
nie clang
optymalizuje potencjalnego połączenia z g
. W moim rozumieniu jest to poprawne: maszyna abstrakcyjna ma zakładać, że volatile
zmienne mogą się zmieniać w dowolnym momencie (z powodu np. Mapowania sprzętowego), więc ciągłe składanie false
inicjalizacji do if
sprawdzenia byłoby błędne.
Ale MSVC g
całkowicie eliminuje wezwanie do (zachowując odczyty i zapisy do volatile
tego!). Czy to zachowanie jest zgodne ze standardami?
Tło: czasami używam tego rodzaju konstrukcji, aby móc włączać / wyłączać wyjście debugowania w locie: kompilator musi zawsze odczytywać wartość z pamięci, więc zmiana tej zmiennej / pamięci podczas debugowania powinna odpowiednio zmodyfikować przepływ sterowania . Wyjście MSVC ponownie odczytuje wartość, ale ignoruje ją (prawdopodobnie ze względu na ciągłe składanie i / lub eliminację martwego kodu), co oczywiście nie zgadza się z moimi intencjami.
Edycje:
Eliminacja odczytów i zapisów
volatile
jest omawiana tutaj: Czy kompilator może zoptymalizować lokalną zmienną zmienną? (dzięki Nathan!). Myślę, że standard jest całkowicie jasny, że te odczyty i zapisy muszą się zdarzyć. Ale ta dyskusja nie obejmuje tego, czy kompilator może przyjmować wyniki tych odczytów za pewnik i optymalizować na ich podstawie. Przypuszczam, że jest to niedokreślone / nieokreślone w standardzie, ale byłbym szczęśliwy, gdyby ktoś udowodnił, że się mylę.Mogę oczywiście utworzyć
x
zmienną nielokalną, aby rozwiązać problem. To pytanie jest bardziej z ciekawości.
źródło
Odpowiedzi:
Myślę, że [intro. Wykonanie] (numer paragrafu może się różnić) może być użyte do wyjaśnienia zachowania MSVC:
Norma nie pozwala na wyeliminowanie odczytu przez lotną wartość glvalue, ale powyższy akapit można interpretować jako pozwalający przewidzieć wartość
false
.BTW, mówi C Standard (N1570 6.2.4 / 2)
Nie jest jasne, czy mógłby istnieć niepisany zapis w obiekcie o automatycznym czasie przechowywania w pamięci C / modelu obiektowym.
źródło
volatile
kupowanie (oprócz zbędnych odczytów / zapisów), jeśli jest ignorowane w celu optymalizacji?TL; DR Kompilator może robić, co chce, przy każdym dostępie lotnym. Ale dokumentacja musi ci powiedzieć. - „Semantyka dostępu przez niestabilną glvalue jest zdefiniowana w implementacji”.
Norma określa dla programu dozwolone sekwencje „lotnych dostępów” i innych „obserwowalnych zachowań” (osiąganych poprzez „skutki uboczne”), które wdrożenie musi przestrzegać zgodnie z „zasadą„ jak gdyby ”.
Ale standard mówi (moje pogrubienie):
Podobnie w przypadku urządzeń interaktywnych (moje pogrubienie):
(W każdym razie, jaki konkretny kod jest generowany dla programu, nie jest określony przez standard).
Więc chociaż standard mówi, że niestabilnych dostępów nie można uciec od abstrakcyjnych sekwencji abstrakcyjnych efektów ubocznych maszyny i konsekwencji możliwych do zaobserwowania zachowań, które definiuje jakiś kod (być może), nie można oczekiwać, że cokolwiek zostanie odzwierciedlone w kodzie obiektowym lub w świecie rzeczywistym zachowanie, chyba że dokumentacja kompilatora mówi ci, co stanowi niestabilny dostęp . To samo dotyczy urządzeń interaktywnych.
Jeśli jesteś zainteresowany lotny vis a vis abstrakcyjnych sekwencji abstrakcyjnych efektów ubocznych maszyna i / lub wynikają obserwowalnych zachowań, które jakiś kod (może) definiuje wtedy tak powiedzieć . Ale jeśli jesteś zainteresowany tym, jaki generowany jest odpowiedni kod obiektowy , musisz interpretować to w kontekście twojego kompilatora i kompilacji .
Chronicznie ludzie błędnie uważają, że w przypadku niestabilnego dostępu ocena abstrakcyjna maszyny / odczyt powoduje zaimplementowany odczyt, a abstrakcyjne przypisanie / zapis maszyny powoduje zaimplementowany zapis. Nie ma podstaw, by twierdzić, że nie ma takiej dokumentacji dotyczącej wdrożenia. Kiedy / iff implementacja mówi, że faktycznie robi coś po „niestabilnym dostępie”, ludzie mają uzasadnione oczekiwania, że coś - być może, wygenerowanie określonego kodu obiektowego.
źródło
Uważam, że pomijanie czeku jest legalne.
Akapit, który każdy lubi cytować
nie oznacza, że implementacja musi zakładać, że takie sklepy są możliwe w dowolnym momencie lub dla dowolnej zmiennej lotnej. Wdrożenie wie, które sklepy są możliwe. Na przykład całkowicie uzasadnione jest założenie, że takie niejawne zapisy zdarzają się tylko w przypadku zmiennych lotnych, które są mapowane do rejestrów urządzeń, i że takie mapowanie jest możliwe tylko w przypadku zmiennych z zewnętrznym sprzężeniem. Lub implementacja może zakładać, że takie zapisy zdarzają się tylko w lokalizacjach pamięci o dopasowanych słowach.
Mimo to myślę, że zachowanie MSVC jest błędem. Nie ma żadnego rzeczywistego powodu, aby zoptymalizować połączenie. Taka optymalizacja może być zgodna, ale jest niepotrzebnie zła.
źródło
volatile
ma to być wskazówka dla implementacji, że wartość może się zmienić w sposób nieznany implementacji.volatile
na tej platformie, a poza specyfikacją zawsze będzie to strzelanie do bzdur.volatile
robi i trzymać się tego. Chodzi mi o to, że sam kod (zgodnie ze standardową / abstrakcyjną maszyną C ++) nie pozwala ci ustalić, czyg
można go wywołać.