Hallo zusammen,
ich habe es mal wieder geschafft, mich in eine totale Anfängerfrage zu
maneuvrieren:
Ich will ein Register auslesen, da dadurch das Statusflag zurückgesetzt
wird. Den ausgelesenen Wert kann ich allerdings für nichts gebrauchen.
Also mal schnell und naiv:
1
voiddummy(void){
2
volatiletemp;
3
4
// Auslesen, um Flag zu löschen
5
temp=I2Cx->SR2;
6
7
// temp wird nicht weiter gebraucht und nicht weiter verwendet
8
}
Wie kann ich sicherstellen, daß diese Leseoperation nicht wegoptimiert
wird. Immerhin kann der Compiler von ihrer Nebenwirkung ja nichts
wissen?
Viele Grüße
W.T.
I2Cx muß volatile sein.
Wenn da ein Statusflag dahintersteht, ist es vermutlich schon so
deklariert. Schau doch mal in den entsprechenden Systemheadern nach.
Damit man beim späteren Lesen des Codes nicht über die Stelle stolpert,
kann man es auch so schreiben:
1
(void)I2Cx->SR2;
Das verdeutlicht, dass der gelesene Wert nicht benötigt und deshalb
verworfern wird. Ein erklärender Kommentar schadet an der Stelle
natürlich auch nicht ...
Hm... ist die Frage wirklich beantwortet?
Was bedeutet 'volatile'? m.W. dass der Compiler damit rechnen muss, dass
sich der Wert einer Variable verändern kann, ohne dass er dies
nachvollziehen kann. Und dass er somit keine Optimierungen vornehmen
darf, die darauf beruhen, dass er den Wert einer Variable vermeintlich
schon kennt.
Aber hilft das bei obigem Beispiel? Selbst wenn der Compiler weiss, dass
er nicht weiss, was im SR2 Register steht, kann er doch, wenn obige
Erklärung hinreichend ist, die Zeile dennoch wegoptimieren, denn das
(wegen 'volatile' nicht vorhersehbara) Ergebnis wird nicht verwendet.
Ich hatte bisher nie das vom TO erwähnte Problem, darum habe ich mir das
nie überlegt. Aber ich finde die Frage interessant. Bedeutet 'volatile'
auch, dass ein Lesezugriff nicht nur ein für den Compiler nicht
vorhersehbares Ergebnis liefern kann, sondern dass dieser Lesezugriff
auch noch Randeffekte haben kann?
Oder anders gefragt: Was stimmt?
- Ein Lesezugriff auf eine Volatile-Variable darf nicht wegoptimiert
werden
oder
- Ein Lesezugriff auf eine Volatile-Variable darf nicht wegoptimert
werden, wenn das Ergebnis noch verwendet wird
Dann ist das zumindest auf Wikipedia falsch formuliert:
"In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich
der Wert der Variable jederzeit ändern kann, beispielsweise durch andere
Prozesse, Threads oder sogar externe Hardware[1]. Bei der Generierung
des Maschinen-Codes aus einem in C oder C++ geschriebenen Programm
verhindert die Kennzeichnung einer Variablen als volatile eine in diesem
Fall die Funktionalität beeinträchtigende Optimierung, so dass das
Programm immer auf den tatsächlich in der Hardware vorhandenen Wert
zugreift.[2]"
Im ersten Satz fehlt somit: ... sowie dass ein Lesezugriff für den
Compiler uneinsehbare Nebeneffekte haben kann... oder so.
Ich habe das aber nirgens je so gelesen... oder mich nicht geachtet...
Fred schrieb:> I2Cx muß volatile sein.
Genauer gesagt muß I2Cx ein Zeiger auf volatile sein. Der Zeiger selbst
muß es aber nicht sein. Auch temp im Beispiel des Ursprungsposters muß
nicht volatile sein. Wozu auch? Das stellt nur sicher, daß nach temp
geschrieben wird, aber nicht, daß von I2Cx->SR2 gelesen wird.
Simon Huwyler schrieb:> Hm... ist die Frage wirklich beantwortet?Simon Huwyler schrieb:> Was bedeutet 'volatile'? m.W. dass der Compiler damit rechnen muss, dass> sich der Wert einer Variable verändern kann, ohne dass er dies> nachvollziehen kann.
Oder auch umgekehrt, daß sie irgendeine Wirkung nach außen hat, die
zwingend stattfinden muß.
Allgemeiner: Alle Lese- und Schreibzugriffe müssen so ausgeführt werden,
wie sie im Code stehen.
Rolf Magnus schrieb:> Simon Huwyler schrieb:>> Was bedeutet 'volatile'? m.W. dass der Compiler damit rechnen muss, dass>> sich der Wert einer Variable verändern kann, ohne dass er dies>> nachvollziehen kann.>> Oder auch umgekehrt, daß sie irgendeine Wirkung nach außen hat, die> zwingend stattfinden muß.> Allgemeiner: Alle Lese- und Schreibzugriffe müssen so ausgeführt werden,> wie sie im Code stehen.
Sorry, dass ich hartnäckig bin, aber das finde ich jetzt wirklich
interessant.
Aber Deine Erklärung oben reicht nicht. Was meinst Du mit "sie"? Der
Wert der Variable? Ein Lesezugriff? Ist das so irgendwo definiert?
Einfach allgemein zu sagen: Jeglicher Zugriff auf eine Volatile Variable
darf nicht wegoptimiert werden, stimt m.E. nämlich auch nicht. Was, wenn
der Zugriff in einer Funktion stattfindet, die definitiv nie aufgerufen
wird? Ich denke, dann darf der Compiler das sehr wohl wegoptimeren.
Also z.B.
int x;
volatile int y;
if(0)
x=y;
Ich glaube eher, der Compiler darf sehr wohl optimieren, aber er darf
keine Schlüsse ziehen, die er glaubt ziehen zu können. Und da stellt
sich mir eben die Frage: Was muss er alles für möglich halten?
Bis jetzt habe ich immer nur gelesen, dass der Compilder damit rechnen
muss, dass sich der Wert der Variable verändert, und dass ein
SCHREIBzugriff (genauer: eine Veränderung des Wertes dieser Variable;
war das das 'sie' von Dir oben?) etwas bewirken kann, das er nicht
kennt.
Von Seiteneffekten eines LESEzugriffs habe ich nie irgendwas gesehen.
Weiss da jemand eine Quelle, wo das steht?
Simon Huwyler schrieb:> Sorry, dass ich hartnäckig bin, aber das finde ich jetzt wirklich> interessant.>> Aber Deine Erklärung oben reicht nicht. Was meinst Du mit "sie"? Ein> Lesezugriff? Ist das so irgendwo definiert?
In der Definition der Sprache C:
********
An object that has volatile-qualified type may be modified in ways
unknown to the implementation or have other unknown side effects.
Therefore any
expression referring to such an object shall be evaluated strictly
according to the rules of the abstract machine, as described in 5.1.2.3.
Furthermore, at every sequence point the value last stored in the
object shall agree with that prescribed by the abstract machine, except
as modified by the unknown factors mentioned previously.
********
Das Löschen des Flags durch lesen der Variable ist ein "other unknown
side effect", wie Walter ja selbst schon selbst sehr treffend formuliert
hat:
Walter Tarpan schrieb:> Immerhin kann der Compiler von ihrer Nebenwirkung ja nichts> wissen?> Einfach allgemein zu sagen:> Jeglicher Zugriff auf eine Volatile Variable darf nicht wegoptimiert> werden, stimt m.E. nämlich auch nicht. Was, wenn der Zugriff in einer> Funktion stattfindet, die definitiv nie aufgerufen wird? Ich denke, dann> darf der Compiler das sehr wohl wegoptimeren.
Ich meinte schon Zugriffe, die überhaupt stattfinden können. Vielleicht
war "so wie sie im Code steht" nicht ganz ideal. Vielleicht so: Alle
Zugriffe, die beim Ablauf des Programms theoretisch ausgeführt werden
müßten, dürfen von der Optimierung beliebig verändert werden, solange es
keine volatile-Zugriffe sind. Die müssen alle durchgeführt werden, und
sie müssen in der richtigen Reihenfolge durchgeführt werden.
Also (folgende Codefragmente sind jetzt nicht unbedingt sinnvoll, aber
ich will nur nochmal zeigen, was ich mine):
volatile int x = 0;
while(x!=0)
doSomething();
Das darf er nicht wegoptimieren, weil er nicht weiss, ob x vielleicht
doch plötzlich 1 werden könnte.
volatile int x = 0;
if(x==1)
{
x = 0;
}
dito.
volatile int x = 0;
x=1;
x=2;
Das darf er nicht wegoptimieren, weil er nicht weiss, ob jemand anders
davon beeinflusst wird, wenn der Wert auf 1 geht.
Aber im obigen Beispiel gilt nichts davon, WENN 'temp' nicht volatile
gesetzt wird. Also:
int x;
volatile int y = 0;
x=y;
Darf er das nun wegoptimieren? Nach allen Erklärungen, die ich bis jetzt
gesehen habe, schon. Denn:
Er darf NICHT einfach annehmen, dass y gleich 0 ist. ABER: Es ist ihm
scheissegal, denn x wird später nie mehr verwendet, und das ist eben
NICHT volatile.
Es sei denn, es ist explizit deklariert, dass der Compiler mit
Nebeneffekten von LESEzugriffen auf Variablen rechnen muss. Ist es das?
EDIT:
Ok, habe gerade obige Antwort gesehen. Ich finde, das ist zumindest
ziemlich vage formuliert. "other side effects"... aber ich lese die
Quelle nochmals genau durch.
Dann halte ich aber die obige Deklaration für wirklich sehr sehr vage.
"An object that has volatile-qualified type may be modified in ways
unknown to the implementation or have other unknown side effects."
Was folgt, ist eine reine Schlussfolgerung:
"Therefore ..."
Was vesteht man unter "unknown side effects" eines Types? Wenn ich das
so lese würde ich das verstehen als: Der INHALT kann Seiteneffekte
haben. Also Die VERÄNDERUNG des Inthalts kann Seiteneffekte haben. Aber
ein LESEZugriff finde ich, sollte da schon explizit erwähnt werden.
Mal ganz ganz ehrlich: Würdet Ihr aus obigem Satz (dem vor "therefore",
der wie gesagt eine reine Schlussfolgerung ist) so verstehen, dass das
LESEN, also das KOPIEREN eines Werts, auch einen Seiteneffekt haben
könnte?
Es gibt noch eine Fußnote:
********
A volatile declaration may be used to describe an object corresponding
to a memory-mapped input/output port or an object accessed by an
asynchronously interrupting function. Actions on objects so declared
shall not be ‘‘optimized out’’ by an implementation or reordered except
as permitted by the rules for evaluating expressions.
********
Fußnoten sind allerdings meines Wissens nur erklärend und nicht
normativ.
Simon Huwyler schrieb:> Ich finde, das ist zumindest ziemlich vage formuliert. "other side> effects"... aber ich lese die Quelle nochmals genau durch.
Naja, einen Satz hatte ich noch nicht mitzitiert:
********
What constitutes an access to an object that has volatile-qualified type
is implementation-defined.
********
Rolf Magnus schrieb:> ********> What constitutes an access to an object that has volatile-qualified type> is implementation-defined.> ********
Ok. Das macht es klar. :-)
Wieder was gelernt! Aber der Wikpedia-Artikel muss definitiv
überarbeitet werden. Denn die Erkärung da stimmt schlicht nicht.
"In C und C++ wird durch diesen Typqualifikator spezifiziert, dass sich
der Wert der Variable jederzeit ändern kann, beispielsweise durch andere
Prozesse, Threads oder sogar externe Hardware[1]"
Das ist, wie wenn ich schreiben würde: Ein Betriebssystem ist ein
System, das Speicherzugriffe von Anwendungsprogrammen überwacht."
Stimmt schon. Aaaaaber da sind noch 'ne Menge andere Sachen. :-)
Simon Huwyler schrieb:> Was vesteht man unter "unknown side effects" eines Types?
Nicht eines Typs, sondern eine Objekts.
> Wenn ich das so lese würde ich das verstehen als: Der INHALT kann> Seiteneffekte haben.
Ich lese es so, daß das Objekt selbst Seiteneffekte haben, also alles,
was man damit machen kann (oder selbst wenn man nichts damit macht),
kann Seiteneffekte produzieren. Genau deshalb darf kein einziger Zugriff
wegoptimiert werden, da diese Seiteneffekte dem Compiler nicht bekannt
sind und er deshalb nicht selbst sicherstellen kann, daß sie nicht
verlorengehen. Genau dafür ist volatile da.
> Mal ganz ganz ehrlich: Würdet Ihr aus obigem Satz (dem vor "therefore",> der wie gesagt eine reine Schlussfolgerung ist) so verstehen, dass das> LESEN, also das KOPIEREN eines Werts, auch einen Seiteneffekt haben> könnte?
Ich würde schlussfolgern, daß alles einen Seiteneffekt haben könnte.
"other unknown side effects" heißt für mich "irgendwas beliebiges, von
dem der Compiler nichts weiß". Darunter fällt der hier beschriebene
Fall.
Rolf Magnus schrieb:> Ich lese es so, daß das Objekt selbst Seiteneffekte haben, also alles,> was man damit machen kann (oder selbst wenn man nichts damit macht),> kann Seiteneffekte produzieren. Genau deshalb darf kein einziger Zugriff> wegoptimiert werden, da diese Seiteneffekte dem Compiler nicht bekannt> sind und er deshalb nicht selbst sicherstellen kann, daß sie nicht> verlorengehen. Genau dafür ist volatile da.
oooook..... hm.... klar. Nehmen wir mal etwas aus einer ganz anderen
Ecke. Ein Copy Constructor oder Assignment Operator kann natürlich auch
Seiteneffekte haben (wenn man gerne gefährlich lebt ;-) ). Insofern
mache ich definitiv etwas mit einem Objekt, wenn ich es "nur" kopiere.
Jup. Macht Sinn. :-)
Aus dem verlinkten Text:
"For every read from a volatile variable by the abstract machine, the
actual machine must load from the memory address corresponding to that
variable. Also, each read may return a different value"
Aus bisher jeder Erklärung von volatile (u.a. in Wikipedia) habe ich bis
jetzt entnommen, dass der erste Satz (bloss) eine Folge des zweiten sei.
Und das Wort "volatile" (flüchtig) deutet ja auch darauf hin.
Dieser Satz aber ist genial, denn er trifft es genau. Du MUSST jeden
Read durchführen, und zwar NICHT bloss, weil das Ergebnis anders sein
könnte, als Du meinst. Auch, wenn Dich das Ergebnis gar nicht
interessiert.
Guter Text! Merci!
Johann L. schrieb:> Hier noch etwas mehr zu lesen von John Regehr:>> "Nine ways to break your systems code using volatile">> http://blog.regehr.org/archives/28
Danke für die Antworten auf meine Frage. Und für den schönen Text.
Simon Huwyler schrieb:> Du MUSST jeden> Read durchführen, und zwar NICHT bloss, weil das Ergebnis anders sein> könnte, als Du meinst. Auch, wenn Dich das Ergebnis gar nicht> interessiert.
Genau das sagt der Standard
1
any expression referring to such an object shall be evaluated
2
strictly according to the rules of the abstract machine
Hinweis:
'shall' bedeutet in diesem Zusammenhang nicht: och mach das mal, wenn es
dir Spass macht.
'shall' bedeutet: du hast das so zu tun!
'any expression', also jeglicher Ausdruck (in dem das volatile Objekt
vorkommt), muss so ausgewertet werden, wie die Regeln der Sprache (und
zwar die der abstrakten Maschine, also vor Optimierung) das
vorschreiben.
1
i;
ist ein Ausdruck. Wird dieser Ausdruck ausgewertet, dann erfolgt ein
Lesezugriff auf i. In der abstrakten Maschine - vor der Optimierung.
Wenn i volatile ist, dann muss hier ein Lesezugriff erfolgen, wenn die
Programmausführung diesen Punkt erreicht.
In
1
i+i;
erfolgen 2 Lesezugrgiff. Der Compiler darf das auch nicht zu
1
2*i;
oder
1
i<<1;
oder gar
1
i;
abkürzen.
Optimierungen sind nicht Teil der Sprache (mit einer Ausnahme in C++).
Für Optimierungen gilt die 'as if' Regel als oberste Direktive, nach der
sich das sichtbare Verhalten eines Programmes nicht von dem
unterscheiden darf, was unoptimiert passieren würde. Unoptimiert muss
ein Lesezugriff erfolgen, also muss er in diesem Fall auch in der
optimierten Version erfolgen, weil ja de4r Compiler von 'unknown side
effects' ausgehen muss.
Karl Heinz schrieb:> Für Optimierungen gilt die 'as if' Regel als oberste Direktive, nach der> sich das sichtbare Verhalten eines Programmes nicht von dem> unterscheiden darf, was unoptimiert passieren würde.
Wobei das "sichtbare Verhalten" laut Definiton aus zwei Dingen besteht,
nämlich aus File-I/O und (man ahnt es schon) Zugriffen auf
Volatile-Variblen.