Forum: Compiler & IDEs Variable an Register binden - ich kanns nicht oder compilerbug :)


von Roland Praml (Gast)


Lesenswert?

Hallo ich hab grad ein Problem mit meinem GCC

ich hab folgendes Progrämmchen (Attiny24)
1
#include <AVR/signal.h>
2
3
volatile unsigned int register currData asm("r2");
4
5
int main(void) {
6
  MCUCR = 1 << ISC00 | 1 << ISC01;        // int0 - rising edge
7
  GIMSK = 1 << INT0;                // enable int0
8
  DDRB  = 1 << PB1;
9
  PORTB = 0 << PB1;
10
  sei();
11
  for (;;) {
12
    while( PINB & (1 << PB0) );   // Wait until chip is selected
13
    currData = 0x2123;
14
    while(! (PINB & (1 << PB0)) );   // Wait until chip is deselected
15
  }
16
}
17
18
ISR(INT0_vect) {
19
  if (currData & 0x4000) {
20
    PORTB |= 1 << PB1;
21
  } else {
22
    PORTB &= ~(1 << PB1);
23
  }
24
  currData = currData << 1;
25
}

hier hab ich mir gedacht, ich binde die Variable currData fest an das 
Register R2 (aus Performancegründen, damit die ISR direkt drauf 
zugreifen kann), denkste, dies wird in der Main gleich mal wegoptimiert 
auf:
1
  for (;;) {
2
    while( PINB & (1 << PB0) );   // Wait until chip is selected
3
  40:  b0 99         sbic  0x16, 0  ; 22
4
  42:  fe cf         rjmp  .-4        ; 0x40 <__SREG__+0x1>
5
    currData = 0x2123; <-- HIER SOLLTE R2/R3 GESETZT WERDEN!
6
    while(! (PINB & (1 << PB0)) );   // Wait until chip is deselected
7
  44:  b0 9b         sbis  0x16, 0  ; 22
8
  46:  fe cf         rjmp  .-4        ; 0x44 <__SREG__+0x5>
9
  48:  fb cf         rjmp  .-10       ; 0x40 <__SREG__+0x1>

Hab ich hier wirklich einen Bug gefunden, oder kann man das nicht so 
machen wie ich es vorhabe.
Die ISR sieht im Listing i.O. aus, hier greift er immer brav auf r2/r3 
zu.
Mit der Deklaration:
  volatile unsigned int currData ;
(also ohne Register) funktioniert mein Programm dann auch, allerdings 
ist die ISR dann anstatt 17 Instruktionen 27 lang und das möchte ich 
nicht :-P

Gruß
Roland

von Roland Praml (Gast)


Lesenswert?

übrigens, GCC hab ich mir grad die neueste Version runter geladen. 
Optimierung ist -Os (tritt aber auch bei -O1 -O2 und -O3 auf)

von Benedikt K. (benedikt)


Lesenswert?

Ist ein 8bit Register nicht etwas klein für eine 16bit Variable?

Ich habe ehrlich gesagt keine Ahnung was der Compiler in diesem Fall 
macht, bzw. ob das überhaupt geht. Die FAQ zeigt als Beispiele nur 8bit 
Variablen.

von Roland Praml (Gast)


Lesenswert?

bei geraden Variablen wird die nächste ungerade dazugebunden, also R2/R3

von Benedikt K. (benedikt)


Lesenswert?

Füg mal in die Schleife irgendeinen Funktionsaufruf ein, dann setzt er 
r2/r3. Es scheint also so zu sein, als wenn er das volatile ignoriert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Benedikt K. schrieb:
> Es scheint also so zu sein, als wenn er das volatile ignoriert.

Ja, volatile & register kann GCC nicht behandeln.  Früher[tm] gab
es dafür eine Warnung, die wurde aber irgendwann mal rausgeworfen,
da sie an manchen Stellen nicht sinnvoll war.  Auf meinen Protest
hin:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34351

ist sie nun wieder drin, aber erst mit GCC 4.4.

Summa summarum: don't do it.

von Simon K. (simon) Benutzerseite


Lesenswert?

Ohne volatile ist ja das Hauptanwendunsgebiet vom dem Registerbinden 
weg.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon K. schrieb:
> Ohne volatile ist ja das Hauptanwendunsgebiet vom dem Registerbinden
> weg.

Ja, leider.

Beim AVR könnte man noch tricksen (außer Xmega), indem man zusätzlich
einen Zeiger auf das Register anlegt und den als auf ein volatile-
Objekt markiert, da der AVR ja die CPU-Register in die ersten 32
RAM-Adressen abbildet.

von Benedikt K. (benedikt)


Lesenswert?

Oder man könnte die Register in inline asm Beschreiben. Ist nicht schön, 
aber sollte gehen.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Das volatile an register sollte überflüssig sein, wundert mich, daß gcc 
da nicht meckert...

Als Optionen solltest du zusätzlich -ffixed-2 -ffixed-3 angeben bei 
allen Modulen, die zur Applikation gelinkt werden.

Davon ab sieht's nach nem Bug aus.

Testfall anbei.

Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen? Die 
Insn wird in Pass 141r.cse1 von avr-gcc 4.3.2 als tot erkannt und 
gelöscht.

Johann

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:

> Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen?

Schlecht, ich habe immer nur gepatchte davon...  Gerade bei der
4.3er Serie sind ja einige Patches notwendig (hab mir noch nicht
angesehen, was davon bei 4.3.3 alles bereits integriert ist).

Die Warnung scheint ja erst bei 4.4 wieder dabei zu sein.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Jörg, kannst du das mit nem ungepatchten avr-gcc nachvollziehen?
>
> Schlecht, ich habe immer nur gepatchte davon...  Gerade bei der
> 4.3er Serie sind ja einige Patches notwendig (hab mir noch nicht
> angesehen, was davon bei 4.3.3 alles bereits integriert ist).

Ok, dann müsste ich die Tage mal nen ungepatchten 4.3.2 bauen und 
nachschauen, ob's da auch hakt.

Folgendes wird jedoch korrekt übersetzt:
1
register unsigned int currData asm ("r2");
2
void foo (void) 
3
{
4
    currData = 0x2123;
5
}

Der bekannten Bug von WinAVR ist es mal nicht, weil der hier resistent 
gegen -fno-split-wide-types ist...

Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser 
natürlich der Code ist tot. Oder lassen wir und von "register" in die 
Irre führen?

Johann

von (prx) A. K. (prx)


Lesenswert?

Zuweisungen an globale Variablen können, wenn sie unabhändig vom 
Schleifeninhalt sind und der Schleifencode vollständig bekannt ist, ggf. 
verschoben oder wie hier wegoptimiert werden. Ob "register" oder nicht 
ändert daran nichts. Daran würde nur "volatile" etwas ändern, und das 
kann GCC eben nicht zusammen mit "register".

Insofern ist das auch kein Bug, denn das ist sowieso kein Standard-C, 
also ist das Verhalten nirgendwo definiert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:

> Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser
> natürlich der Code ist tot.

Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
Register, was in einer ISR geändert wird, pollst:
1
#include <stdint.h>
2
#include <avr/interrupt.h>
3
4
register uint8_t flag asm("r2");
5
6
ISR(INT0_vect)
7
{
8
  flag = 42;
9
}
10
11
int
12
main(void)
13
{
14
  flag = 0;
15
16
  while (flag == 0)
17
    /* wait */;
18
19
  return 42;
20
}

Das übersetzen alle mir vorliegenden Versionen von GCC falsch, indem
sie die while-Schleife zur Endlosschleife machen.  (OK, ist sie auch,
aber nur, weil ich keine Interruptquelle für den Externinterrupt
aktiviere. ;-)

So würde es funktionieren:
1
int
2
main(void)
3
{
4
  volatile uint8_t *flagptr = (uint8_t *)0x02;
5
6
  flag = 0;
7
8
  while (*flagptr == 0)
9
    /* wait */;
10
11
  return 42;
12
}

Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

von (prx) A. K. (prx)


Lesenswert?

Jörg Wunsch schrieb:

> Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
> benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

Und funktioniert bei den Xmegas nicht, denn die haben diese 
Speicheradressen nicht mehr.

von Roland Praml (Gast)


Lesenswert?

Also denkt ihr ich soll das mit den Registern bleiben lassen und auf die 
paar Befele & Bytes die ich mir spare, verzichten, weil das Verhalten 
des GCC an dieser Stelle eben undefiniert ist.

Irgendwie juckts mich jetzt das Ganze dann komplett in ASM zu machen :)

Gruß
Roland

von Roland Praml (Gast)


Lesenswert?

> Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
> Register, was in einer ISR geändert wird, pollst:

Die (schnelle) Kommunikation zwischen Main() und ISR() wär ja meiner 
Meinung ein WESENTLICHER Vorteil. Wenn ich eine normale Variable als 
"volatile" deklariere, dann bastelt der Compiler da noch jede Menge 
"Zeug" dazu. z.B.:
1
  currData = currData << 1;
2
 174:  80 91 60 00   lds  r24, 0x0060
3
 178:  90 91 61 00   lds  r25, 0x0061
4
 17c:  88 0f         add  r24, r24
5
 17e:  99 1f         adc  r25, r25
6
 180:  90 93 61 00   sts  0x0061, r25
7
 184:  80 93 60 00   sts  0x0060, r24
vs:
1
  currData = currData << 1;
2
 14a:  22 0c         add  r2, r2
3
 14c:  33 1c         adc  r3, r3

Leider optimiert er die Registerzugriffe in der Main weg. Und die 
Register in der Main direkt beschreiben ist ja auch "quatsch" :-(

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Wenn ein Register fixed ist, sollte da nix wegoptimiert werden, ausser
>> natürlich der Code ist tot.
>
> Ich glaube, das Problem entsteht vor allem dann, wenn du auf so ein
> Register, was in einer ISR geändert wird, pollst:
>
> Das übersetzen alle mir vorliegenden Versionen von GCC falsch, indem

Es ist anders als ich erwarten würde. Aber um zu beurteilen, ob's falsch 
ist, weiss ich offen gestanden zu wenig über die Semantik von 
"register".

Jedenfalls zeit ein Blick in rtl.h, daß sowas wie "volatile register" 
garnicht ausgedrückt werden kann in RTL-Flags:
1
  /* 1 in a MEM or ASM_OPERANDS expression if the memory reference is volatile.
2
     1 in an INSN, CALL_INSN, JUMP_INSN, CODE_LABEL, BARRIER, or NOTE
3
     if it has been deleted.
4
     1 in a REG expression if corresponds to a variable declared by the user,
5
     0 for an internally generated temporary.
6
     1 in a SUBREG with a negative value.
7
     1 in a LABEL_REF, REG_LABEL_TARGET or REG_LABEL_OPERAND note for a
8
     non-local label.
9
     In a SYMBOL_REF, this flag is used for machine-specific purposes.  */
10
  unsigned int volatil : 1;

> So würde es funktionieren:
>
1
>   volatile uint8_t *flagptr = (uint8_t *)0x02;
2
>
> Allerdings natürlich recht umständlich, da wirklich LDS-Befehle
> benutzt werden, um R2 von der Speicheradresse 2 zu lesen.

UAAA seh ich jetzt erst. Hack as Hack can!
Wenn das raschelt, dann zurecht ;-)

Was mir als Hack einfallen würde ist
1
currData = 0x2123;
2
asm volatile ("" : "+r" (currData));

Damit macht zumindest der Testfall das, was er soll...

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Roland Praml schrieb:
> Also denkt ihr ich soll das mit den Registern bleiben lassen und auf die
> paar Befele & Bytes die ich mir spare, verzichten, weil das Verhalten
> des GCC an dieser Stelle eben undefiniert ist.

Ich verwende in einer Anwendung auch Registervariablen für Flags und für 
Daten. Ohne Register wäre das ganze empfindlich langsamer, und pro 
Sekunde hätte ich über 300000 Speicherzugriffe mehr...

Allerdings hatte ich bemerkt, daß avr-gcc 4.x damit Zicken macht. Bei 
der Echtzeitanwendung seh ich jede kleine Zeitabweichung und hab es 
darauf zurückgefügrt, das der Code langsamer (und zudem auch größer) ist 
als beim 3.4.6, den ich immer noch einsetze.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:

> Es ist anders als ich erwarten würde. Aber um zu beurteilen, ob's falsch
> ist, weiss ich offen gestanden zu wenig über die Semantik von
> "register".

Stammt aus der Zeit, als man einem Compiler noch explizit sagen musste, 
welche Variable er in Registern vorhalten sollte. Wenn er genug davon 
hatte. Immerhin musste so ein Compiler in einer PDP-11 mit 64KB 
Adressraum arbeiten, da war für grössere Statement-übergreifende 
Optimizer kein Platz.

Das ist also ein Synonym zu "auto" mit einem freundlichen Hinweis 
obendrauf. Und interessiert heutzutage keinen halbwegs ernstzunehmenen 
Compiler mehr. Bei globalen Variablen ist das aus dieser historischen 
Sicht heraus folglich irgendwas zwischen undefiniert und Blödsinn.

von (prx) A. K. (prx)


Lesenswert?

Was ggf. helfen kann: Einige AVRs haben ein paar freie Bytes im 
bitadressierbaren I/O-Raum. GPIOR0-2 oder so. Ideal für Statusbits.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:
> Was ggf. helfen kann: Einige AVRs haben ein paar freie Bytes im
> bitadressierbaren I/O-Raum. GPIOR0-2 oder so. Ideal für Statusbits.

Stimmt, die Stati hab ich tatsächlich in GPIOR0 (schon was her seit ich 
das gemacht hab), weil das SFR bitadressierbar ist und man Bits 
setzen/löschen kann; im Gegensatz zu "l"-Regs. Allerdings sind GPRIO1/2 
blöderweise nicht bitadressierbar (ATmega168).

Aber für Pixel-Koordinaten nehm ich GPRs, was auch prima geht im 3.4.6. 
Hier würde GPRIO1/2 nix sparen.

Johann

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.