Variable an Register binden - ich kanns nicht oder compilerbug :)

Gast #1249373
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
Moderator Persönliche Seite #1249446
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.
Persönliche Seite #1249481
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
Angehängte Dateien:
Moderator Persönliche Seite #1249500
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.
Persönliche Seite #1249523
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
#1249538
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.
Moderator Persönliche Seite #1249547
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.
Gast #1249598
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
Gast #1249608
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" :-(
Persönliche Seite #1249609
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
Persönliche Seite #1249622
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.
#1249709
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.
Persönliche Seite #1250169
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

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