volatile cast: GCC Bug oder gültige Optimierung

Gast #3298316
Lesenswert?
• ▲
▼
Ich habe hier ein kleines Problem, für welches ich zwar einen Workaround 
habe, aber nicht sicher bin, ob das ein GCC Bug ist.

Und zwar sollen im Code Zeichen asynchron über den UART gelesen werden 
bis ein gültiges Längenbyte (!=0) kommt. Über den Code Stil kann man 
streiten, aber er sollte so funktionieren, und tat das auch bis wir Link 
Time Optimization verwendeten und dabei ein Call durch Inlinen ersetzt 
wurde.

Ich hab als Beispiel was gebastelt, das diese Auffälligkeiten zeigt, 
dabei sind 3 Beispiele zu sehen, im ersten wird bei der if Überprüfung 
in der Schleife der zweite Teil weggelassen, obwohl auf (volatile ...) 
gecastet. Darf der Compiler das, obwohl ich ihm sage die Variable kann 
sich (durch die UART ISR) ändern?
Bei den beiden anderen Varianten wird alles wie gewünscht ausgeführt. Am 
besten man kompiliert das immer nur mit einer Schleife, dann sieht man 
im Listing was ich meine. Optimierung ist -Os mit GCC 4.7.2 von 
Launchpad

Plattform ist ein STM32
1
#include <stdint.h>
2
 
3
static char m_cRecBuffer[23];
4

5
void __attribute__((noinline)) uart_read_async(uint8_t* pa_pcBuffer, int pa_nLen)
6
{
7
   volatile uint8_t * pUartBusy = (volatile uint8_t*)0x42; 
8
   *pUartBusy = 1;
9
}
10
 
11
 uint8_t __attribute__((noinline)) uart_is_busy()
12
 {
13
    volatile uint8_t * pUartBusy = (volatile uint8_t*)0x42; 
14
   return *pUartBusy; 
15
 }
16
 
17
 void _exit(int pa_Exit)
18
 {
19
 }
20
 
21
int main(void)
22
{
23

24
   m_cRecBuffer[0] = 0;
25
   uart_read_async(&m_cRecBuffer[0], 1);
26
   
27
   // some pointer to uart driver status, changed in uart ISR
28
   volatile uint8_t * p_UartBusy = (volatile uint8_t*)0x42; 
29
   
30
   // not working
31
   /*
32
   * second condition in if is ignored (0 == (volatile uint8_t)m_cRecBuffer[0])
33
   */
34
   while ((0 == (volatile uint8_t)m_cRecBuffer[0])) {
35
    if (*p_UartBusy != 0 && (0 == (volatile uint8_t)m_cRecBuffer[0])) {
36
      uart_read_async(&m_cRecBuffer[0], 1);
37
    }
38
   }
39
   
40
   // working
41
   while ((0 == (volatile uint8_t)m_cRecBuffer[0])) {
42
    if (uart_is_busy() != 0 && (0 == (volatile uint8_t)m_cRecBuffer[0])) {
43
      uart_read_async(&m_cRecBuffer[0], 1);
44
    }
45
   }
46
   
47
   // working
48
   volatile uint8_t * pcLen = (volatile uint8_t*) &m_cRecBuffer[0];
49
   while (0 == *pcLen) {
50
    if (*p_UartBusy != 0 && 0 == *pcLen) {
51
      uart_read_async(&m_cRecBuffer[0], 1);
52
    }
53
   }
54
}
Gast #3298342
Lesenswert?
• ▲
▼
Das der
>> (0 == (volatile uint8_t)m_cRecBuffer[0])
Teil wegoptimiert wird, könnte vielleicht daran liegen, dass du
m_cRecBuffer nicht als volatile deklariert ist. Das könntest mal 
ausprobieren.
Aber ich weiß auch nich, was ein volatile cast letztlich für 
Auswirkungen hat.
#3298370
Lesenswert?
• ▲
▼
Wenn man auf eine Variable, die nicht als "volatile" deklariert ist, 
erst einmal zugegriffen hat, dann ist es ziemlich egal, ob man den so 
erhaltenen Wert nachträglich zu "volatile" castet. Das ist so, als ob 
man den Stall zusperrt, nachdem das Pferd schon ausgebüxt ist.
Gast #3298381
Lesenswert?
• ▲
▼
Peter Dannegger schrieb:
> Also beim AVR-GCC läßt sich volatile nicht direkt casten.
Genau das ist das Problem. Du liest die *nicht*-volatile Variable 
m_recBuffer aus (kann also wegoptimiert werden) und castest das 
Ergebnis nach "volatile uint8_t".
In C wäre es so richtig:
1
while (0 == ((volatile uint8_t*)m_cRecBuffer)[0]) {
In C++:
1
while (0 == const_cast<volatile uint8_t*>(&m_cRecBuffer)[0]) {
(const_cast ist auch für volatile zuständig...)
Natürlich wäre es geschickter, m_cRecBuffer gleich komplett als 
"volatile" zu definieren.

Peter Dannegger schrieb:
> #define IVAR(x)         (*(volatile typeof(x)*)&(x))
Und für die 2 anderen C++-Programmierer hier um Forum:
1
template <typename T>
2
inline volatile T& IVAR (T& var) {
3
  return const_cast<volatile T&>(var);
4
}

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.