Hilfe - AVR-GCC "optimiert" Schleife zur Endlosschleife

OP #3419827
Lesenswert?

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
int main (void)
4
{
5
    int i = 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
int main (void)
4
{
5
    unsigned int i = 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):
1
avr-gcc  -mmcu=at90s2313 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT main.o -MF dep/main.o.d  -c  main.c
2
avr-gcc -mmcu=at90s2313  main.o     -o test.elf
3
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  test.elf test.hex
4
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex test.elf test.eep || exit 0
5
avr-objdump -h -S test.elf > test.lss
6

7
AVR Memory Usage
8
----------------
9
Device: at90s2313
10

11
Program:      54 bytes (2.6% Full)
12
(.text + .data + .bootloader)
13

14
Data:          0 bytes (0.0% Full)
15
(.data + .bss + .noinit)

Was passiert hier? Warum "optimiert" der GCC die Schleife mit der 
"int"-Variablen zur Endlosschleife? Beim Kompilieren kommen keine 
Warnungen.

Viele Grüße, Holger
Gast #3419841
Lesenswert?

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.
OP #3419842
Lesenswert?

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.
Gast #3419845
Lesenswert?

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) {...}'.
#3419849
Lesenswert?

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.
#3419855
Lesenswert?

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();
Gast #3419863
Lesenswert?

1
  int i = 0;
2
    while (--i)
3
        wdt_reset();

Wer so einen Sch... schreibt muss sich auch nicht wundern wenn ihm die 
selbige um die Ohren fliegt.

Vermutlich war das gemeint:
1
 unsigned int i = UINT_MAX;
2
 while (i--) {
3
  wdt_reset();
4
 }

Was hat man davon nicht genau das zu schreiben was man auch meint? Der 
Compiler ist offenbar nicht weiblich.
Persönliche Seite #3419866
Lesenswert?

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.
OP #3419884
Lesenswert?

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.
#3419898
Lesenswert?

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.
Persönliche Seite #3419899
Lesenswert?

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?
Moderator Persönliche Seite #3419901
Lesenswert?

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:
1
        ldi r24,0
2
        ldi r25,0
3
        rjmp .L2
4
.L3:
5
/* #APP */
6
 ;  7 "foo.c" 1
7
        wdr
8
 ;  0 "" 2
9
/* #NOAPP */
10
.L2:
11
        sbiw r24,1
12
        brne .L3
13
.L4:
14
        rjmp .L4
#3419903
Lesenswert?

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.
Gast #3419904
Lesenswert?

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:
1
for(;;);
Persönliche Seite #3419910
Lesenswert?

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.
Gast #3419913
Lesenswert?

>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.
Gast #3419926
Lesenswert?

>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.
Persönliche Seite #3419936
Lesenswert?

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.
OP #3419939
Lesenswert?

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.
OP #3419951
Lesenswert?

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!
Moderator Persönliche Seite #3419953
Lesenswert?

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.
Moderator Persönliche Seite #3419960
Lesenswert?

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.
Gast #3419963
Lesenswert?

@  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.
Moderator Persönliche Seite #3419973
Lesenswert?

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.
#3419974
Lesenswert?

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.
OP #3419977
Lesenswert?

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...
Gast #3419980
Lesenswert?

@ 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.
Gast #3419998
Lesenswert?

@ 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.
Gast #3420005
Lesenswert?

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?
Moderator Persönliche Seite #3420011
Lesenswert?

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. ;-)
OP #3420028
Lesenswert?

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.
Gast #3420034
Lesenswert?

Jörg Wunsch schrieb:
>
1
> int main(void)
2
> {
3
>   int i = 0;
4
>   while (--i)
5
>     asm("nop");
6
>   for (;;) ;
7
> }
8
>
>
> Was bekommst du?
>
>
1
> .L2:
2
>       nop
3
>       b .L2
4
>
>
> Also letztlich dasselbe wie oben.

Was allerdings in dem Fall dem vom Pogrammierer gewünschten Verhalten 
ausreichend nahe kommt ;)

Oliver
OP #3420037
Lesenswert?

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!
Persönliche Seite #3420044
Lesenswert?

Zählt auch brav runter. Bei -O0 und -O3.

-O0:
1
    64: int main(void) 
2
    65: { 
3
0x08000610 4770      BX       lr
4
    66:   int i = 0; 
5
0x08000612 2000      MOVS     r0,#0x00
6
    67:   while (--i) 
7
0x08000614 E000      B        0x08000618
8
    68:     __NOP(); 
9
0x08000616 BF00      NOP      
10
0x08000618 1E41      SUBS     r1,r0,#1
11
0x0800061A 0008      MOVS     r0,r1
12
0x0800061C D1FB      BNE      0x08000616
13
    69:   for (;;) ; 
14
0x0800061E BF00      NOP      
15
0x08000620 E7FE      B        0x08000620
Gast #3420057
Lesenswert?

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?
#3420086
Lesenswert?

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.
Gast #3420094
Lesenswert?

@ 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?
(Firma: Schweigstill IT) Persönliche Seite #3420600
Lesenswert?

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.
Gast #3420914
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren