Hallo allerseits,
mein Vertrauen in den AVR-GCC stellt dieser gerade auf eine sehr harte
Probe: Bei folgendem C-Code
1
#include<avr/wdt.h>
2
3
intmain(void)
4
{
5
inti=0;
6
while(--i)
7
wdt_reset();
8
9
for(;;);
10
}
macht er mir aus der while-Verzögerungsschleife diesen Assemblercode
(Auszug aus der LSS-Datei):
1
00000024 <main>:
2
3
int main (void)
4
{
5
int i = 0;
6
while (--i)
7
wdt_reset();
8
24: a8 95 wdr
9
26: fe cf rjmp .-4 ; 0x24 <main>
10
11
00000028 <_exit>:
12
28: f8 94 cli
Das ist eine Endlosschleife zwischen Adresse 24 und 26 und hat nichts
mit dem C-Quelltext zu tun!
Das Verrückte ist: Wenn ich aus dem "int" ein "unsigned int" mache, dann
funktioniert alles:
1
#include<avr/wdt.h>
2
3
intmain(void)
4
{
5
unsignedinti=0;
6
while(--i)
7
wdt_reset();
8
9
for(;;);
10
}
LSS-Datei:
1
00000024 <main>:
2
#include <avr/wdt.h>
3
4
int main (void)
5
{
6
24: 80 e0 ldi r24, 0x00 ; 0
7
26: 90 e0 ldi r25, 0x00 ; 0
8
28: 01 c0 rjmp .+2 ; 0x2c <main+0x8>
9
unsigned int i = 0;
10
while (--i)
11
wdt_reset();
12
2a: a8 95 wdr
13
#include <avr/wdt.h>
14
15
int main (void)
16
{
17
unsigned int i = 0;
18
while (--i)
19
2c: 01 97 sbiw r24, 0x01 ; 1
20
2e: e9 f7 brne .-6 ; 0x2a <main+0x6>
21
30: ff cf rjmp .-2 ; 0x30 <main+0xc>
22
23
00000032 <_exit>:
24
32: f8 94 cli
Kompiliert wurde unter WinAVR 20100110 mit dem gcc 4.3.3 folgendermaßen
(von Makefile gesteuert):
Was passiert hier? Warum "optimiert" der GCC die Schleife mit der
"int"-Variablen zur Endlosschleife? Beim Kompilieren kommen keine
Warnungen.
Viele Grüße, Holger
Holger G. schrieb:> int i = 0;> while (--i)> wdt_reset();
Recht hat er. Du vertraust hier auf den (negativen) Überlauf einer
Variablen mit Vorzeichen. Das ist undefiniertes Verhalten.
Holger G. schrieb:> Das ist eine Endlosschleife zwischen Adresse 24 und 26 und hat nichts> mit dem C-Quelltext zu tun!
Eigentlich schon:
> for (;;);> }>
Wo ist das Problem?
Holger G. schrieb:> Das Verrückte ist: Wenn ich aus dem "int" ein "unsigned int" mache, dann> funktioniert alles:
Bei vorzeichenlosen Variablen ist das Überlaufverhalten klar definiert.
Walter Tarpan schrieb:> Wo ist das Problem?
Sein Problem liegt in der Schleife davor.
Ganz offen gesagt, lese ich sowas immer in Erwartung der Stelle, an der
auf die Seite im K&R verwiesen wird, an der steht, daß der inkriminierte
Code mit definiertes Verhalten beschrieben ist.
Das kommt allerdings wesentlich seltener vor, als erschüttertes
Vertrauen oder die Unterstellung eines Compilerfehlers.
Irgendwie auffällig.
A. K. schrieb:> Recht hat er. Du vertraust hier auf den (negativen) Überlauf einer> Variablen mit Vorzeichen. Das ist undefiniertes Verhalten.
Versteh' ich nicht. Was soll denn daran undefinierter sein als der
Rollover einer vorzeichenlosen 16-bit-Variablen beim "Dekrementieren"
von 0 auf 65535?
Abgesehen davon darf er von mir aus warnen, schimpfen, Exceptions werfen
- nur nicht stillschweigend so einen Blödsinn compilieren.
ich hätte jetzt darauf getippt das die decrementierung einer variablen
immer "true" zurückgibt - aber ich bin c-anfänger.
kommt irgendwie aus der gleichen ecke wie der beliebte java-fehler
'while (var = 5) {...}'.
Holger G. schrieb:> Versteh' ich nicht. Was soll denn daran undefinierter sein als der> Rollover einer vorzeichenlosen 16-bit-Variablen beim "Dekrementieren"> von 0 auf 65535?
Diesen Unterschied kannst du auch unter "par ordre du mufti"
zusammenfassen. Das steht schlicht so im C-Standard.
> Abgesehen davon darf er von mir aus warnen, schimpfen, Exceptions werfen> - nur nicht stillschweigend so einen Blödsinn compilieren.
Der Compiler darf alles optimieren, was im Rahmen der Sprachdefinition
zum richtigen Ergebnis führt. Und das ist hier der Fall.
Höflicher wärs natürlich, wenn er nett darauf hinweist, dass er hier
eine Schluderei des Programmierers ausnutzt. Wobei er nicht immer
erkennt, dass dies so ist.
Random ... schrieb:> Ich würde sagen: <0 ist beim gcc FALSE.
Bei "int"?
Holger G. schrieb:> Abgesehen davon darf er von mir aus warnen, schimpfen, Exceptions werfen> - nur nicht stillschweigend so einen Blödsinn compilieren.
Der Compiler erkennt, dass die Bedingung innerhalb der Sprachdefinition
nie falsch sein wird. Und er erkennt auch, dass "i" nirgends sonst
verwendet wird. Also ist es äquivalent zu
while (1) wdt_reset();
c.m. schrieb:> ich hätte jetzt darauf getippt das die decrementierung einer variablen> immer "true" zurückgibt - aber ich bin c-anfänger.> kommt irgendwie aus der gleichen ecke wie der beliebte java-fehler> 'while (var = 5) {...}'.
Das Decrementieren einer Variablen gibt natürlich das Ergebnis nach dem
Decrement wieder **kopfschüttel**
A. K. schrieb:> Random ... schrieb:>> Ich würde sagen: <0 ist beim gcc FALSE.>> Bei "int"?>> Holger G. schrieb:>> Abgesehen davon darf er von mir aus warnen, schimpfen, Exceptions werfen>> - nur nicht stillschweigend so einen Blödsinn compilieren.>> Der Compiler erkennt, dass die Bedingung innerhalb der Sprachdefinition> nie falsch sein wird. Und er erkennt auch, dass "i" nirgends sonst> verwendet wird. Also ist es äquivalent zu> while (1) wdt_reset();
Da hast du dich verlesen :-)
Der Compiler schmeisst die ganze while() Schleife raus und lässt nur die
for() dahinter drin.
Das deckt sich mit meiner Annahme, dass werte <0 FALSE entsprechen. Und
von daher kann er while(FALSE) rausnehmen.
> Der Compiler erkennt, dass die Bedingung innerhalb der Sprachdefinition> nie falsch sein wird.
a) Nehmen wir an, der zählt tatsächlich los, dann ist mind. (--i == 0)
FALSE :-)
b) dann würde nach deiner Annahme da ein while(TRUE) WDT(); stehen
bleiben.
Random ... schrieb:> Da hast du dich verlesen :-)> Der Compiler schmeisst die ganze while() Schleife raus und lässt nur die> for() dahinter drin.
und warum wird denn der Wachhund resettet?
> 24: a8 95 wdr
Random ... schrieb:> Der Compiler schmeisst die ganze while() Schleife raus und lässt nur die> for() dahinter drin.
Nein, denn es bleibt das übrig:
24: a8 95 wdr
26: fe cf rjmp .-4 ; 0x24 <main>
und das ist eben
while (1) wdt_reset();
Sorry für das Ungemach, aber ich kann lesen. Insbesondere den Output
von Compilern. ;-)
Random ... schrieb:> a) Nehmen wir an, der zählt tatsächlich los, dann ist mind. (--i == 0)> FALSE :-)
Aber das steht da nicht. Da steht
while (--i)
wdt_reset();
also
while (--i != 0)
wdt_reset();
und --i wird bei int und Anfangswert 0 niemals wieder definiert 0.
Was ist los? Schlechten Tag erwischt?
A. K. schrieb:> Der Compiler darf alles optimieren, was im Rahmen der Sprachdefinition> zum richtigen Ergebnis führt. Und das ist hier der Fall.
Wäre es nicht sinnvoller, wenn der Compiler alles optimieren darf, was
zum selben Verhalten wie der unoptimierte Code führt? Alles andere ist
doch kreuzgefährlich und nützt absolut niemandem.
> Höflicher wärs natürlich, wenn er nett darauf hinweist, dass er hier> eine Schluderei des Programmierers ausnutzt. Wobei er nicht immer> erkennt, dass dies so ist.
Mit Höflichkeit hat das nicht viel zu tun. Ein kommerzielles Produkt
würde Spießruten laufen, wenn es den Programmierer unter lapidarem
Verweis auf den C-Standard so im Regen stehen lässt. Aber Open Source
darf das wohl.
Vermutlich erkennt der Compiler, daß das ganze Programm mit
zigtausendfachem wdt-reset() sowieso völliger Blödsinn ist, und macht
daraus was sinnvolles ;)
Oliver
Den GCC-lern sitzt machnmal der Schalk im Nacken und dann nehmen sie die
Variante, die sich der Autor möglichst wenig vorgestellt hat.
Da signed Unterlauf nicht definiert ist, könnte es ja sein, daß der Wert
beim Minimalwert kleben bleibt.
Wird mit ner positiven Zahl initialisiert, funktioniert es.
A. K. schrieb:> Random ... schrieb:>> Der Compiler schmeisst die ganze while() Schleife raus und lässt nur die>> for() dahinter drin.>> Nein, denn es bleibt das übrig:> 24: a8 95 wdr> 26: fe cf rjmp .-4 ; 0x24 <main>> und das ist eben> while (1) wdt_reset();>> Sorry für das Ungemach, aber ich kann lesen. Insbesondere den Output> von Compilern. ;-)
Tatsächlich sehe ich hier nur das ergebnis der for() schleife :-)
Wo ist denn der call zum wdt?
Random ... schrieb:> Der Compiler schmeisst die ganze while() Schleife raus und lässt nur die> for() dahinter drin.
Nein.
Hier nochmal im Klartext, also generierter Assemblercode (in diesem
Falle vom GCC 4.7.2):
1
.L2:
2
/* #APP */
3
; 7 "foo.c" 1
4
wdr
5
; 0 "" 2
6
/* #NOAPP */
7
rjmp .L2
Das ist nicht nur irgendwie ein Ausschnitt, nein, das ist das
komplette main(), welches er generiert.
> Das deckt sich mit meiner Annahme, dass werte <0 FALSE entsprechen. Und> von daher kann er while(FALSE) rausnehmen.
Deine Annahme ist falsch. Der C-Standard definiert ganz eindeutig,
dass nur der Wert 0 als Wahrheitswert “false” entspricht, alle anderen
Werte entsprechen “true”.
Es ist schon so, wie A. K. es schreibt. Der Code verlässt sich darauf,
dass der Überlauf von -MAX_INT - 1 beim weiteren Herunterzählen
eine 0 ergibt. Ein solcher Überlauf ist aber in C nicht definiert.
Wenn man -Wstrict-overflow mit in die Optionen aufnimmt, bekommt man
auch die entsprechende Warnung:
1
foo.c: In function 'main':
2
foo.c:6:11: warning: assuming signed overflow does not occur when simplifying conditional to constant [-Wstrict-overflow]
Alternativ bekommt man mit -fno-strict-overflow das vom TE erhoffte
Ergebnis:
Holger G. schrieb:> Wäre es nicht sinnvoller, wenn der Compiler alles optimieren darf, was> zum selben Verhalten wie der unoptimierte Code führt?
Ist dir eigentlich klar, was das für Folgen hätte?
Probiers mal im Kleinen aus: Schreib in deinem Program vor alle
Variablen "volatile" davor, und schau an was dabei rauskommt. Ist ja ein
Standardproblem, wenn der Compiler Zugriffe wegoptimiert und damit die
Änderung im Interrupt übersieht.
Nochmal um klar zu stellen warum der Compiler das komplette
while-Statement entsorgt. Dort steht für ihn nachweisbar in diesem
Scope:
1
while(-1){
2
wdt_reset();
3
}
Das heisst die while kann nicht ein einziges Mal true werden und ist
somit sinnlos. Das gleiche gilt damit für i, welches gleich mitentsorgt
wird.
Am Ende bleibt das:
Random ... schrieb:> Tatsächlich sehe ich hier nur das ergebnis der for() schleife :-)> Wo ist denn der call zum wdt?
es gibt kein call, das erledigt ein asm befehlt.
> 24: a8 95 wdr
Matthias schrieb:> Nochmal um klar zu stellen warum der Compiler das komplette> while-Statement entsorgt. Dort steht für ihn nachweisbar in diesem> Scope:
mach er doch überhaupt nicht. Es wird nur eine endlosschleife.
A. K. schrieb:> Random ... schrieb:>> a) Nehmen wir an, der zählt tatsächlich los, dann ist mind. (--i == 0)>> FALSE :-)>> Aber das steht da nicht. Da steht> while (--i)> wdt_reset();> also> while (--i != 0)> wdt_reset();> und --i wird bei int und Anfangswert 0 niemals wieder definiert 0.>> Was ist los? Schlechten Tag erwischt?
Also, wenn <0 FALSE entspricht wird der while - Ausdruck genau einmal
mit while(-1) == while(FALSE) aufgerufen. Damit kann der Compiler den
ganzen Aufruf incl. wdt rauswerfen.
Ich meinte, falls <0 TRUE entspräche und der einmal in die Runde
laufen würde, wäre irgendwann das Ergebnis von --i NULL.
Peter II schrieb:> Matthias schrieb:>> Nochmal um klar zu stellen warum der Compiler das komplette>> while-Statement entsorgt. Dort steht für ihn nachweisbar in diesem>> Scope:>> mach er doch überhaupt nicht. Es wird nur eine endlosschleife.
Nope, der macht keine Endlosschleife. Die Endlosschleife ist das for() !
>Holger G. schrieb:> Wäre es nicht sinnvoller, wenn der Compiler alles optimieren darf, was> zum selben Verhalten wie der unoptimierte Code führt?
Genau das tut er ja.
Es ist ja nur eine von mehreren Möglichkeit, das im Standard
"undefiniertes" Verhalten, da es ohne Optimierung ja doch in irgendeiner
Weise entschieden werden muss, genau zu dem Verhalten führt, das Du für
sinnvoll hälst.
Genauso plausibel wäre etwa ein "stehenbleiben" bei INT_MIN oder auch
anderes.
Random ... schrieb:> Nope, der macht keine Endlosschleife. Die Endlosschleife ist das for() !
und warum wird dann der Watchdog resettet?
> 24: a8 95 wdr> 26: fe cf rjmp .-4 ; 0x24 <main>
>Also, wenn <0 FALSE entspricht wird der while - Ausdruck genau einmal>mit while(-1) == while(FALSE) aufgerufen. Damit kann der Compiler den>ganzen Aufruf incl. wdt rauswerfen.
An dem Argument ist etwas dran, denke ich. Der Punkt ist nur, das eben
die GCC-Autoren sich anders entschieden haben, und dabei die Tatsache,
dass das Verhalten für solche Fälle "implementierungsabhängig" ist zu
ihrer Rechtferigung benutzen können. Durchaus legitim, meine ich.
Faultier schrieb:>>Also, wenn <0 FALSE entspricht wird der while - Ausdruck genau einmal>>mit while(-1) == while(FALSE) aufgerufen. Damit kann der Compiler den>>ganzen Aufruf incl. wdt rauswerfen.>> An dem Argument ist etwas dran, denke ich. Der Punkt ist nur, das eben> die GCC-Autoren sich anders entschieden haben, und dabei die Tatsache,> dass das Verhalten für solche Fälle "implementierungsabhängig" ist zu> ihrer Rechtferigung benutzen können. Durchaus legitim, meine ich.
Wenn !=0 TRUE entspricht (also auch <0) sollte - rein technisch -
irgendwann der Überlauf und dann auch die (--i == NULL) kommen. (ich
schreibe --i, da ja das Ergebnis des Aufrufes, und nicht der aktuelle
Wert von i zählt).
Aber da schlägt wohl das Argument von Peter II (Gast) zu.
A. K. schrieb:> Holger G. schrieb:>> Wäre es nicht sinnvoller, wenn der Compiler alles optimieren darf, was>> zum selben Verhalten wie der unoptimierte Code führt?>> Ist dir eigentlich klar, was das für Folgen hätte?>> Probiers mal im Kleinen aus: Schreib in deinem Program vor /alle/> Variablen "volatile" davor, und schau an was dabei rauskommt. Ist ja ein> Standardproblem, wenn der Compiler Zugriffe wegoptimiert und damit die> Änderung im Interrupt übersieht.
"volatile" bezieht sich auf die Vermeidung von Seiteneffekten durch
Interrupts. Das ist aber etwas anderes, als einen deterministischen
Programmablauf umzuwürfeln. Ich hab grad noch mal ohne Optimierungen
kompiliert und folgenes erhalten:
1
int i = 0;
2
2e: 1a 82 std Y+2, r1 ; 0x02
3
30: 19 82 std Y+1, r1 ; 0x01
4
32: 01 c0 rjmp .+2 ; 0x36 <__CCP__+0x2>
5
while (--i)
6
wdt_reset();
7
34: a8 95 wdr
8
#include <avr/wdt.h>
9
10
int main (void)
11
{
12
int i = 0;
13
while (--i)
14
36: 89 81 ldd r24, Y+1 ; 0x01
15
38: 9a 81 ldd r25, Y+2 ; 0x02
16
3a: 01 97 sbiw r24, 0x01 ; 1
17
3c: 9a 83 std Y+2, r25 ; 0x02
18
3e: 89 83 std Y+1, r24 ; 0x01
19
40: 89 81 ldd r24, Y+1 ; 0x01
20
42: 9a 81 ldd r25, Y+2 ; 0x02
21
44: 00 97 sbiw r24, 0x00 ; 0
22
46: b1 f7 brne .-20 ; 0x34 <__CCP__>
23
48: ff cf rjmp .-2 ; 0x48 <__SREG__+0x9>
24
25
0000004a <_exit>:
26
4a: f8 94 cli
Hier macht er es also so, wie es dasteht, und pfeift auf den C-Standard.
Und ich hätte mindestens erwartet, daß er mir eine Warnung ausgibt, wenn
er diese eindeutig endende Schleife entfernt und einfach durch eine
Endlosschleife ersetzt.
Random ... schrieb:> Wenn !=0 TRUE entspricht (also auch <0) sollte - rein technisch -> irgendwann der Überlauf und dann auch die (--i == NULL) kommen.
Technisch geht auch mehr. Beispielsweise saturierende Arithmetik, dann
bleibt der Wert bei MIN_INT kleben. Wäre bei DSPs denkbar. Es könnte
auch einen Overflow-Trap geben.
Faultier schrieb:>>Holger G. schrieb:>> Wäre es nicht sinnvoller, wenn der Compiler alles optimieren darf, was>> zum selben Verhalten wie der unoptimierte Code führt?>> Genau das tut er ja.> Es ist ja nur eine von mehreren Möglichkeit, das im Standard> "undefiniertes" Verhalten, da es ohne Optimierung ja doch in irgendeiner> Weise entschieden werden muss, genau zu dem Verhalten führt, das Du für> sinnvoll hälst.> Genauso plausibel wäre etwa ein "stehenbleiben" bei INT_MIN oder auch> anderes.
Wenn er also ein Konstrukt erkennt, das wie meines laut Standard zu
"undefiniertem" Verhalten führt und er einen Ausweg finden muss - ist es
da zuviel verlangt, mich davon per Warnung in Kenntnis zu setzen? Genau
das ist ja der Punkt, daß ich nicht weiß, wieviele solche Eier noch im
eigenen (oder in fremdem) Code versteckt sind, und der einzige, der mich
darüber informieren könnte, eben der gcc, es nicht tut!
Faultier schrieb:> Der Punkt ist nur, das eben die GCC-Autoren sich anders entschieden> haben, und dabei die Tatsache, dass das Verhalten für solche Fälle> "implementierungsabhängig" ist zu ihrer Rechtferigung benutzen können.
Nein, es ist nicht "implementierungsabhängig", sondern es ist
undefiniert. Der Standard schreibt dazu einleitend:
1
3.4.3
2
1 undefined behavior
3
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
4
for which this International Standard imposes no requirements
5
2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable
6
results, to behaving during translation or program execution in a documented manner characteristic of the
7
environment (with or without the issuance of a diagnostic message), to terminating a translation or
8
execution (with the issuance of a diagnostic message).
9
3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
Holger G. schrieb:> Wenn er also ein Konstrukt erkennt, das wie meines laut Standard zu> "undefiniertem" Verhalten führt und er einen Ausweg finden muss - ist es> da zuviel verlangt, mich davon per Warnung in Kenntnis zu setzen?
Du musst die Warnung einschalten (wie alle anderen Warnungen auch).
Wie das geht, schrieb ich weiter oben.
Das Einzige, was man den GCC-Entwicklern hier vorwerfen könnte ist,
dass sie diese Warnung nicht bei -Wextra automatisch mit aktivieren.
p.s.:
Holger G. schrieb:> Und ich hätte mindestens erwartet, daß er mir eine Warnung ausgibt, wenn> er diese eindeutig endende Schleife entfernt und einfach durch eine> Endlosschleife ersetzt.
Dass er deine "eindeutig endende" Schleife entfernt, ist ja dann nur
eine Folge davon. An der Stelle, wo er das tut, weiß er vermutlich
gar nicht mehr, was die Ursache dafür war.
@ Random
> ... - rein technisch - ...
Das ist kein festumrissener Begriff, mit dessen Hilfe man in solchen
Diskussionen argumentieren kann.
Dein Argument setzt voraus, das die Konvention, das Integers in
begrenzten physischen Speichern repräsentiert wird, hier als allgemein
gültig und relevant angesehen wird. Aber: Das ist eben eine Konvention.
Es ergibt sich nicht zwingend.
Es ist nämlich auch plausibel hier "rein mathematisch" (und dieser
Begriff ist wesentlich besser definiert) zu argumentieren, dass eine
vorzeichenbehaftete Zahl, falls sie bgeginnend bei Null dekrementiert
wird, IMMER negativ sein wird. Daher also auch der Vergleich ((--i) !=
0) auch IMMER falsch sein wird.
Also: Beide Standpunkte sind plausibel.
Faultier schrieb:> Eine Unaufmerksamkeit bei der Formulierung von mir. Ich meinte auch> undefiniert.
Die ist aber durchaus folgenschwer.
"Implementierungsabhängig" (im Sinne von "implementation-defined")
ist nämlich ein sehr wohl definiertes Verhalten, bei dem sich ein
Compiler eine bestimmte Variante aus mehreren aussuchen kann, diese
aber dann dokumentieren muss. Unter gleichen Umständen muss er sich
dann auch jedesmal gleich entscheiden.
Bei undefiniertem Verhalten muss der Compiler rein gar nichts, er
muss es nichtmal bemerken. Er darf es auch bei jedem 13. Compilevorgang
völlig anders umsetzen als sonst.
Holger G. schrieb:> Wenn er also ein Konstrukt erkennt, das wie meines laut Standard zu> "undefiniertem" Verhalten führt und er einen Ausweg finden muss
Falsch gedacht. Es ist der Programmierer, der einen Ausweg finden muss.
Der Compiler muss es nicht, der hat nämlich kein Problem damit.
In diesem Fall freilich entdeckt er, dass der Programmierer Mist gebaut
hat. Weshalb er davor nicht stets warnt, sondern nur auf Anforderung,
weiss ich nicht. Möglicherweise gibt es zu viel funktionierenden Code,
der haufenweise solche Warnungen wirft. Aber mindestens in -Wextra hätte
das wohl schon hineingepasst.
Aber es kann auch vorkommen, dass Optimierungen aufgrund undefinierten
Verhaltens durch den Compiler nicht klar ersichtlich sind.
Jörg Wunsch schrieb:> Du musst die Warnung einschalten (wie alle anderen Warnungen auch).> Wie das geht, schrieb ich weiter oben.>> Das Einzige, was man den GCC-Entwicklern hier vorwerfen könnte ist,> dass sie diese Warnung nicht bei -Wextra automatisch mit aktivieren.
Danke für den Tip. Ich hatte mit -Wall kompiliert und war bisher der
Meinung, dass im Code nichts Schwerwiegendes falsch ist, wenn bei -Wall
keine Warnung kommt. Man lernt nie aus...
@ Holger G.
>Hier macht er es also so, wie es dasteht, und pfeift auf den C-Standard.
Nein! Das tut er nicht! Der Standard sagt, das das Verhalten
"undefiniert" ist.
Es ist wichtig, daß Du das akzeptierst.
A. K. schrieb:> Aber mindestens in -Wextra hätte das wohl schon hineingepasst.
Sehe ich allerdings auch so. Sollte man einen enhancement request
dafür einreichen?
@ Jörg Wunsch
> Die ist aber durchaus folgenschwer.
Da gebe ich Dir durchaus recht. Aber warum schreibst Du das mir?
Es war mir garnicht bewusst, das meine Antwort von wegen
"Unaufmerksamkeit" so zu verstehen war, als wenn ich, um Dir zu
gefallen, eine ohnehin gleichwertige Formulierung durch eine andere
ersetzt habe. Falls es keinen relevanten Unterschied zwischen
"undefiniert" und "implementierungsabhängig" geben würde, dann hätte ich
das schon geschrieben.
Holger G. schrieb:> "volatile" bezieht sich auf die Vermeidung von Seiteneffekten durch> Interrupts.
Falsch.
> Hier macht er es also so, wie es dasteht, und pfeift auf den C-Standard.
Falsch.
Dir wurde bereits erklärt, was der Standard sagt. Der gcc hält sich mit
wie ohne Optimierung daran. Er macht sogar das, was man tendentiell
erwarten würde.
> Und ich hätte mindestens erwartet, daß er mir eine Warnung ausgibt, wenn> er diese eindeutig endende Schleife entfernt und einfach durch eine> Endlosschleife ersetzt.
Ja, dann schalt die Warnung eben einfach ein. Wurde dir ebenfalls schon
mitgeteilt, wie das geht.
Mehr noch, es gibt sogar eine Option, das von dir gewünschte Verhalten
einzuschalten.
Was willst du eigentlich?
Random ... schrieb:> Ich hab das mal fixx mit dem armcc ausprobiert bei -O0 sowie -O3, und> der zählt brav runter und steigt bei --i == 0 aus :-)
Das ist ebenso zulässig. Undefiniertes Verhalten schliesst auch jenes
Verhalten ein, das der Programmierer erwartet. :-)
A. K. schrieb:> Ja.http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59301
Ich habe das mal vom AVR weg verallgemeinert. Letztlich ist das
wdt_reset() ja weiter nichts als ein asm("wdr"), und indem man da
ein asm("nop") schreibt, passt das am Ende für praktisch jedes
Target. Damit hat man auch die Leute noch erreicht, die bei AVR im
Subject gleich weiterlesen würden. ;-)
Faultier schrieb:> Aber warum schreibst Du das mir?
Nicht dir, sondern ich wollte nur allgemein nochmal den Unterschied
zwischen "undefined" und "implementation-defined" behaviour
hervorheben. Für den Hinterkopf gewissermaßen.
Fred schrieb:> Ja, dann schalt die Warnung eben einfach ein. Wurde dir ebenfalls schon> mitgeteilt, wie das geht.>> Mehr noch, es gibt sogar eine Option, das von dir gewünschte Verhalten> einzuschalten.>> Was willst du eigentlich?
Kam nur beim Schreiben nicht mit Lesen hinterher. Aber nun ist alles
klar. Vielen Dank an alle.
Jörg Wunsch schrieb:> A. K. schrieb:>> Ja.>> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59301>> Ich habe das mal vom AVR weg verallgemeinert. Letztlich ist das> wdt_reset() ja weiter nichts als ein asm("wdr"), und indem man da> ein asm("nop") schreibt, passt das am Ende für praktisch jedes> Target. Damit hat man auch die Leute noch erreicht, die bei AVR im> Subject gleich weiterlesen würden. ;-)
Danke!
Ein unerwartetes Optimierungsverhalten war auch bei diesem Code
festzustellen. Das durchlaufen der Schleife ist unsicher.
do{
if (GPIOA->IDR & 0x0001)
{
GPIO_ResetBits(GPIOD,GPIO_Pin_15);
}
else
{
GPIO_SetBits(GPIOD,GPIO_Pin_15);
}
} while (1);
lutz h. schrieb:> Ein unerwartetes Optimierungsverhalten war auch bei diesem Code> festzustellen. Das durchlaufen der Schleife ist unsicher.>> do{> if (GPIOA->IDR & 0x0001)> {> GPIO_ResetBits(GPIOD,GPIO_Pin_15);> }> else> {> GPIO_SetBits(GPIOD,GPIO_Pin_15);> }> } while (1);
also ich sehe nichts wo hier der optimiere zuschlagen könnte. (Unter der
Voraussetzung das GPIOA->IDR und GPIOD Register sind die volatile
sind.)
Oder auf was willst du hinaus?
Random ... schrieb:> Zählt auch brav runter. Bei -O0 und -O3.
Dann hast du einen anderen ARM-GCC als ich. Bei mir kam das raus,
was ich oben gepostet habe.
> -O0:
-O0 brauchst du nicht posten, dabei werden sämtliche relevanten
Betrachtungen nicht angeworfen. Es muss also mindestens -O1 sein.
Ich bin auf der Suche für das Verhalten dieser Schleife.
Diese fragt bei stm 32 Board eine Taste ab, und schaltet dann eine LED
an oder aus. Bei zwei unterschiedlichen Board arbeitet diese Abfrage
unzuverlässig.
Leider habe ich das fehlerhaft compilierte File, bei dem es nicht ging
nicht mehr.
Der Code wurde so übersetzt, das die Abfrage nur einmal erfolgte,
oder die Schleife läuft endlos.
Möglicherweise erkennt der Compiler Endlosschleifen. Ich hoffe das
möglicherweise noch andere Codebeispiele für optimierte Schleifen
gefunden werden.
@ Lutz
Erstens handelt es sich bei Deinem Fall um ein völlig anderes Thema.
Hier gint es um eine Schleife die unbeabsichtigt eine endlose wurde.
Deine aber ist absichtlich eine endlose.
Zweitens kann man schlecht ein nicht reproduzierbares Problem behandeln.
Wie soll denn das gehen? Was erwartest Du?
Jörg Wunsch schrieb:> Random ... schrieb:>> Zählt auch brav runter. Bei -O0 und -O3.>> Dann hast du einen anderen ARM-GCC als ich. Bei mir kam das raus,> was ich oben gepostet habe.
Ich schrieb armcc, ohne 'g' :-)
Darum habe ich das asm("nop"); auch gegen die CMSIS Variante __NOP();
getauscht.
Faultier schrieb:>>enabliere>> WAS machst Du?
Büschn Spaß.
One switch to rule them all, One switch to find them,
One switch to print the warnings and in the darkness bind them
Faultier schrieb:>>Büschn Spaß.>> Sehr spassig. Da lache ich am Wochende mal in Ruhe drüber.
hmmm. Gehst du dazu in den Keller?
Heute mit dem falschen Fuß zuerst aufgestanden?
nix für ungut...
Mark Brandis schrieb:> Wenn ich als Programmierer auf Nummer Sicher gehen will, dann enabliere> ich also:>> -Wall> -Wextra>> Und was noch alles? :-)
Minimum sind für mich grundsätzlich
Jörg Wunsch schrieb:> Wer auch immer einen armcc bastelt.
Damals(tm) wurde der ARMCC von Norcroft entwickelt und von ARM als ARM
SDT (Software Development Toolkit) vertrieben. Um 2000 gab es die
Umstellung auf ARM ADS, ebenfalls von Norcroft und ARM.
Mit dem Kauf von Keil durch ARM wurden jedoch die Compiler durch die
entsprechenden Produkte von Keil ersetzt, ohne dies allzu publik zu
machen.
Rolf Magnus schrieb:> Minimum sind für mich grundsätzlich> -std=c99 (oder gnu99) -pedantic -Wall -Wextra
Nutzt in dem aktuellen Fall aber auch nichts.
Und pedantic zusammen mit gnu99 ist ja nun irgendwie wiedersinning.
Oliver
Oliver schrieb:> Rolf Magnus schrieb:>> Minimum sind für mich grundsätzlich>> -std=c99 (oder gnu99) -pedantic -Wall -Wextra>> Nutzt in dem aktuellen Fall aber auch nichts.
Deshalb schrieb ich ja "Minimum". Außerdem hätte ich solchen Code auch
nicht geschrieben. ;-)
> Und pedantic zusammen mit gnu99 ist ja nun irgendwie wiedersinning.
Ich wäre ja auch mit C99 zufrieden, aber auf dem PC werden halt manche
POSIX-Funktionen oder z.B. so Sachen wie M_PI damit abgeschaltet, weil
sie eigentlich nicht ISO-C-Konform sind. In dem Fall weiche ich dann auf
GNU99 aus. Ob -pedantic dann noch sinnvoll ist, darüber hab ich ehrlich
gesagt bisher gar nicht nachgedacht.
Andreas Schweigstill schrieb:> Mit dem Kauf von Keil durch ARM wurden jedoch die Compiler durch die> entsprechenden Produkte von Keil ersetzt, ohne dies allzu publik zu> machen.
Nicht wirklich. Das MDK-ARM (also die µVision IDE) werkelt seit dem auch
mit dem armcc. Der ursprüngliche ARM Compiler von Keil wurde
eingestellt.