GCC Optimierer macht Code funktionsuntüchtig

Gast #1248276
Lesenswert?

Hi,
ich hab folgenden Code, AVR Studio 4, AVR GCC, keine Warnings, keine 
Errors:
1
unsigned char USART_Receive( void )
2
{
3
  TCCR0 =(1<<WGM01) |(1<<CS01);
4
     OCR0=200;
5
  TIMSK|=(1<<OCIE0);
6
  _delay_ms(1);
7
  timeout=0;
8

9
  /* Wait for data to be received */
10
  while ( (!(UCSR1A & (1<<RXC1))) && (timeout<50) )
11
  ;
12
  TCCR0 = 0;
13
  if(timeout > 50)
14
    return ~ack;
15
  /* Get and return received data from buffer */
16
  return UDR1;
17
}
18

19
void setRelais(unsigned char adress, unsigned char value)
20
{
21
  unsigned char temp;
22
  unsigned char i=0;
23

24
  USART_Transmit(ADRESSBIT|adress);
25
  _delay_ms(10);
26
  USART_Transmit(REL_CMD_SET);
27
  _delay_ms(10);
28
  USART_Transmit(value);
29
  _delay_ms(10);
30
  USART_Transmit(ack);
31
  
32
  while(i<5) {
33
  
34
    if(USART_Receive() == ack)
35
      i=6;
36
    else {
37
      i++;
38
      USART_Transmit(ADRESSBIT|adress);
39
      _delay_ms(1);
40
      USART_Transmit(REL_CMD_SET);
41
      _delay_ms(1);
42
      USART_Transmit(value);
43
      _delay_ms(1);
44
      USART_Transmit(ack);
45
    }
46
  }
47
  ack++;
48
}
timeout wird in einem Interrupt erhöht.

Mit keiner Optimierung läuft der Code.
Mit Optimierung -Os (Standard in AVR Studio),
läuft der Code nicht, er bleibt irgendwie/wo
an
1
while ( (!(UCSR1A & (1<<RXC1))) && (timeout<50) )
2
  ;
 hängen.
Könntet ihr mir helfen? Irgendwo muss ich Mist gebaut haben - aber ich 
steh auf dem Schlauch :(

 Viele Grüße
 Daniel
(Firma: Fraunhofer IIS) #1248291
Lesenswert?

Hi

Meiner Meinung nach ist das

while ( (!(UCSR1A & (1<<RXC1))) && (timeout<50) );

so nicht richtig.

In C/C++ ist && ein lazy and, das bedeuted, dass die zweite Bedingung 
nur geprüft wird wenn die erste true ist.

Falls du kein Zeichen empfängst ist die erste Bedingung immer false, 
daher wird nie auf den Timeout geprüft.

Richtig sollte es heißen:

while ((timeout<50) && (!(UCSR1A & (1<<RXC1))));

Das ganze passiert nur bei aktivierter Optimierung, da der Compiler erst 
dann das lazy and nutzt.

mfg Manuel
#1248317
Lesenswert?

Manuel Andreu schrieb:

> Meiner Meinung nach ist das
>
> while ( (!(UCSR1A & (1<<RXC1))) && (timeout<50) );
>
> so nicht richtig.
>
> In C/C++ ist && ein lazy and, das bedeuted, dass die zweite Bedingung
> nur geprüft wird wenn die erste true ist.
>
> Falls du kein Zeichen empfängst ist die erste Bedingung immer false,
> daher wird nie auf den Timeout geprüft.

Nein, sie ist dann immer true, und timeout wird zum entscheidenden 
Faktor, so wie es auch gedacht ist.

> Richtig sollte es heißen:
>
> while ((timeout<50) && (!(UCSR1A & (1<<RXC1))));

Das Tauschen der beiden &&-Operanten hat hier keinerlei Auswirkungen. 
Beide Varianten sind richtig. Das Problem wird ein fehlendes volatile 
sein.
#1249722
Lesenswert?

tuppes schrieb:
> Manuel Andreu:
>> In C/C++ ist && ein lazy and, das bedeuted,
>> dass die zweite Bedingung nur geprüft wird
>> wenn die erste true ist.
>
> So weit stimmts.
>
>> Das ganze passiert nur bei aktivierter Optimierung,
>> da der Compiler erst dann das lazy and nutzt.
>
> Das stimmt nicht. && ist immer lazy, auch ohne Optimierung.

Noch ein kleiner Nachtrag:
... und das ist auch wichtig, dass das so ist. Letztendlich garantiert 
mir dieses geforderte Verhalten, dass eine Abfrage
1
   if( Pointer && Pointer->Member )

die NULL-Pointer Behandlung korrekt ausführt und keinen ungültigen 
Member-Zugriff nach sich zieht. In anderen Sprachen, zb Ur-Pascal, war 
das nicht so und hat unnötig tief verschachtelten Code nach sich 
gezogen.

PS2: Der Ausdruck 'lazy' ist dafür in C/C++ unüblich. Man spricht hier 
meist von 'shortcut evaluation'
Persönliche Seite #1250182
Lesenswert?

tuppes schrieb:

>> Das ganze passiert nur bei aktivierter Optimierung,
>> da der Compiler erst dann das lazy and nutzt.
>
> Das stimmt nicht. && ist immer lazy, auch ohne Optimierung.

Ich hab gcc schon Code erzeugen gesehen, wo er die zweite Bedinung 
auswertete, obwohl die erste falsch war. Erst dachte ich an nen Bug, 
aber es war nur in Fällen, wo kein Speicherzugriff erfolgte; es ging um 
reine Arithmetik und die Maschine verfügte über Befehle, die (A' & B') 
für bestimmt Ausdrücke A' und B' effizient auswerten konnte, und die die 
gleichen Effekte auf die Maschine hatten, als (A && B), wobei A und A' 
bzw. B und B' in bestimmten Zusammenhang standen.

Womöglich erkennte die Analyse, daß B unabhängig von A war.

Es war aber in allen Fällen so, daß der Code korrekt war und was auf der 
Maschine abging nicht der C-Semantik widersprach.

Johann
#1250198
Lesenswert?

Johann L. schrieb:

> Es war aber in allen Fällen so, daß der Code korrekt war und was auf der
> Maschine abging nicht der C-Semantik widersprach.

Tja. Das ist schwer zu beurteilen ohne den konkreten Code und die 
konkrete Maschine. Mgwl. hat der Optimizer rausgefunden, dass die 
Evaluierung billiger ist, als eine Abfrage samt conditional Branch samt 
eventuellem Pipeline Reloading.

Rein formal muss der Compiler aber bei einem && den 2-ten Ausdruck 
unausgewertet lassen, wenn der erste bereits falsch ist.

Und dann gibts natürlich noch die 'As-if' Regel. Der Optimizer darf den 
Code umstricken wie er lustig ist, solange sich das Ergebnis so verhält, 
'as-if' der Optimizer nie am Werke war. Dass der gcc bei dem von dir 
beobachteten Fällen einen Fehler gemacht hat, glaub ich nicht wirklich. 
Solche Shortcuts werden oft ausgenutzt, sodass man auf fehlerhaftes 
Verhalten schnell aufmerksam werden würde. Auf der anderen Seite machen 
Optimizer heutzutage Analysen, die sollte man nicht für möglich halten.
Gast #1250265
Lesenswert?

Ich mag da nicht mehr up to date sein, aber früher hat der Standard dem 
Compiler die Entscheidung überlassen, in welcher Reihenfolge die Zweige 
von logischen Verknüpfungen überprüft werden und ob alle Zweige 
überprüft werden oder nur die nötigen. Da war das geschilderte Verhalten 
lediglich das spezifische Verhalten von gcc, nicht unbedingt von allen 
Compilern.
Moderator Persönliche Seite #1250267
Lesenswert?

ein schrieb:
> Ich mag da nicht mehr up to date sein, aber früher hat der Standard dem
> Compiler die Entscheidung überlassen, in welcher Reihenfolge die Zweige
> von logischen Verknüpfungen überprüft werden und ob alle Zweige
> überprüft werden oder nur die nötigen.

Nein, in C war das schon immer definiert.
Moderator Persönliche Seite #1250404
Lesenswert?

ein schrieb:
> Operatoren haben in der Tat eine
> definierte Assoziativität.

Es geht hier nicht um die Assoziativität, sondern ausdrücklich
darum, dass die Argumente von && und || von links nach rechts
dergestalt bewertet werden müssen, dass die Evaluierung des
Ausdrucks abgebrochen wird, sowie sein logisches Ergebnis
eindeutig feststeht.

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