Forum: Compiler & IDEs schlechte Optimierung (GCC io.h) ATxmega


von Julius F. (xeno)


Lesenswert?

Hallo,

ich beschäftige mich gerade mit den ATxmegas dabei ist mir bei der neuen 
API der AVRLibC eine schlechte Optimierung aufgefallen.

Beispiel:
1
SPIE.DATA = 0xAA;

Wird zu:
1
LDI R24,0xAA    Load immediate 
2
LDI R30,0xC0    Load immediate 
3
LDI R31,0x0A    Load immediate 
4
STD Z+3,R24    Store indirect with displacement

Aber warum?
Viel Sinnvoller wäre doch:
1
LDI R24,0xAA    Load immediate 
2
STS 0x0AC3,R24    Store direct to data space

Die AVRLibC iox128a1.h sieht so aus:
1
#define SPIE    (*(SPI_t *) 0x0AC0)  /* Serial Peripheral Interface E */
2
3
typedef volatile uint8_t register8_t;
4
5
/* Serial Peripheral Interface */
6
typedef struct SPI_struct
7
{
8
    register8_t CTRL;  /* Control Register */
9
    register8_t INTCTRL;  /* Interrupt Control Register */
10
    register8_t STATUS;  /* Status Register */
11
    register8_t DATA;  /* Data Register */
12
} SPI_t;

Ich verstehe nicht warum der GCC nicht auf eine statische Adresse 
Optimiert. Denn die Adresse ändert sich ja nicht sondern nur der Inhalt.

z.B. bei:
1
SPIE.CTRL = 0xBB;

Macht der GCC es richtig und setzt dafür eine statische Adresse ein.
1
LDI R25,0xBB    Load immediate 
2
STS 0x0AC0,R25    Store direct to data space

Aber bei SPI.INTCTRL, SPI:STATUS ... nicht mehr.


Um das Problem zu lösen muss ich nun immer auf die "alte" API 
zurückgreifen. Sprich: SPIE_DATA = 0xAA;

Ich verwende:

Atmel AVR Studio 5
Atmel AVR Toolchain 3.2.3.314
Windows

AVR 8-bits GNU Binutils 2.20.1
AVR 8-bits GNU Compiler Collection (avr-gcc) 4.5.1
AVRLibC 1.7.1
AVR 8-bits GNU Debugger (avr-gdb) 6.7.1



Gibt es dazu einen Patch o.ä.?

von (prx) A. K. (prx)


Lesenswert?

Zeig mal die gesamte Funktion und den erzeugten Code davon.

von (prx) A. K. (prx)


Lesenswert?

Julius F. schrieb:

> Um das Problem zu lösen muss ich nun immer auf die "alte" API
> zurückgreifen. Sprich: SPIE_DATA = 0xAA;

Bist du beim Speicher tatsächlich so knapp dran, dass diese 2 Bytes zum 
echten "Problem" werden?

Ein Motiv für solche Adressierung ist, dass die Basisadresse ggf. 
mehrfach verwendet werden kann, wenn auf benachbarte Register ebenfalls 
zugegriffen wird. Das ist freilich bei AVRs aufgrund deren Knappheit an 
Adressregistern seltener möglich als bei anderen Architekturen.

von Julius F. (xeno)


Lesenswert?

Hier:
1
#include <avr/io.h>
2
3
int main(void)
4
{
5
  SPIE.DATA = 0xAA;
6
  
7
  while(!(SPIE.STATUS & SPI_IF_bm)){}
8
  
9
  SPIE.DATA = 0xBB;
10
  
11
   while(!(SPIE.STATUS & SPI_IF_bm)){}
12
  
13
  
14
    while(1)
15
    {
16
17
    }
18
}

wird zu:
1
  SPIE.DATA = 0xAA;
2
0000010C 8a ea                LDI R24,0xAA    Load immediate 
3
0000010D e0 ec                LDI R30,0xC0    Load immediate 
4
0000010E fa e0                LDI R31,0x0A    Load immediate 
5
0000010F 83 83                STD Z+3,R24    Store indirect with displacement 
6
  while(!(SPIE.STATUS & SPI_IF_bm)){}
7
00000110 80 91 c2 0a          LDS R24,0x0AC2    Load direct from data space 
8
00000112 87 ff                SBRS R24,7    Skip if bit in register set 
9
00000113 fc cf                RJMP PC-0x0003    Relative jump 
10
  SPIE.DATA = 0xBB;
11
00000114 8b eb                LDI R24,0xBB    Load immediate 
12
00000115 e0 ec                LDI R30,0xC0    Load immediate 
13
00000116 fa e0                LDI R31,0x0A    Load immediate 
14
00000117 83 83                STD Z+3,R24    Store indirect with displacement 
15
   while(!(SPIE.STATUS & SPI_IF_bm)){}
16
00000118 80 91 c2 0a          LDS R24,0x0AC2    Load direct from data space 
17
0000011A 87 ff                SBRS R24,7    Skip if bit in register set 
18
0000011B fc cf                RJMP PC-0x0003    Relative jump 
19
0000011C ff cf                RJMP PC-0x0000    Relative jump


Und dagegen die "alte" API:

1
int main(void)
2
{
3
  SPIE_DATA = 0xAA;
4
  
5
  while(!(SPIE_STATUS & SPI_IF_bm)){}
6
    
7
  SPIE_DATA = 0xBB;
8
  
9
  while(!(SPIE_STATUS & SPI_IF_bm)){}
10
  
11
    while(1)
12
    {
13
14
    }
15
}
1
SPIE_DATA = 0xAA;
2
0000010C 8a ea                LDI R24,0xAA    Load immediate 
3
0000010D 80 93 c3 0a          STS 0x0AC3,R24    Store direct to data space 
4
  while(!(SPIE_STATUS & SPI_IF_bm)){}
5
0000010F 80 91 c2 0a          LDS R24,0x0AC2    Load direct from data space 
6
00000111 87 ff                SBRS R24,7    Skip if bit in register set 
7
00000112 fc cf                RJMP PC-0x0003    Relative jump 
8
  SPIE_DATA = 0xBB;
9
00000113 8b eb                LDI R24,0xBB    Load immediate 
10
00000114 80 93 c3 0a          STS 0x0AC3,R24    Store direct to data space 
11
  while(!(SPIE_STATUS & SPI_IF_bm)){}
12
00000116 80 91 c2 0a          LDS R24,0x0AC2    Load direct from data space 
13
00000118 87 ff                SBRS R24,7    Skip if bit in register set 
14
00000119 fc cf                RJMP PC-0x0003    Relative jump 
15
0000011A ff cf                RJMP PC-0x0000    Relative jump

von Julius F. (xeno)


Lesenswert?

>Bist du beim Speicher tatsächlich so knapp dran, dass diese 2 Bytes zum
>echten "Problem" werden?

Es geht mir nicht um die 2Byte Codegröße sondern um die Geschwindigkeit.
Immerhin sind es 2Takte mehr pro aufruf. Und das ganze beschränkt sich 
ja nicht nur auf die SPI Struct sondern auf alle Bereiche.

Es macht einen unterschied ob ich einen Pin in 2Takten oder in 4Takten 
Toggeln kann.

von (prx) A. K. (prx)


Lesenswert?

Yep, unschön, insbesondere weil die relative Adressierung an Stelle des 
STS hier tatsächlich sinnvoll wäre, wenn Z für alle 4 
Lade/Speicher-Operationen nur einmal geladen würde.

Rechne aber lieber nicht damit, dass sich das in absehbarer Zeit ändert, 
denn fehlende Optimierung auf AVR in einem minder schweren Fall hat 
vmtl. nicht grad hohe Priorität bei den Entwicklern.

von Stefan E. (sternst)


Lesenswert?

Und wo hast du den Assembler-Code her? Vielleicht irgendwo aus dem 
AVR-Studio-5-Debugger? Ist dir klar, dass das Studio5 den Debug-Build 
mit -O0 übersetzt?

von (prx) A. K. (prx)


Lesenswert?

-O0 sieht anders aus. Nö, das ist reproduzierbar.

Was ich mit der Z-Optimierung meinte zeigt sich bei:
1
#define SPIE    (*(SPI_t *) 0x0AC0)  /* Serial Peripheral Interface E */
2
3
typedef volatile unsigned char register8_t;
4
5
/* Serial Peripheral Interface */
6
typedef struct SPI_struct
7
{
8
    register8_t CTRL;  /* Control Register */
9
    register8_t INTCTRL;  /* Interrupt Control Register */
10
    register8_t STATUS;  /* Status Register */
11
    register8_t DATA;  /* Data Register */
12
} SPI_t;
13
14
SPI_t *p = &SPIE;
15
16
int main(void)
17
{
18
  p->DATA = 0xAA;
19
  
20
  while(!(p->STATUS & 1)){}
21
  
22
  p->DATA = 0xBB;
23
  
24
   while(!(p->STATUS & 1)){}
25
  
26
  
27
    while(1)
28
    {
29
30
    }
31
}
denn das wird zu:
1
        lds r30,p
2
        lds r31,(p)+1
3
        ldi r24,lo8(-86)
4
        std Z+3,r24
5
.L2:
6
        ldd r24,Z+2
7
        sbrs r24,0
8
        rjmp .L2
9
        ldi r24,lo8(-69)
10
        std Z+3,r24
11
.L3:
12
        ldd r24,Z+2
13
        sbrs r24,0
14
        rjmp .L3
15
.L8:
16
        rjmp .L8

von Julius F. (xeno)


Lesenswert?

> Und wo hast du den Assembler-Code her? Vielleicht irgendwo aus dem
> AVR-Studio-5-Debugger? Ist dir klar, dass das Studio5 den Debug-Build
> mit -O0 übersetzt?

Ja aus dem AVR-Studio 5 Disassembler bzw. der *.lss Datei aber ich glaub 
nicht der er mir -O0 Code anzeigt denn wenn ich den Code mit -Os oder 
-O3 übersetzte kommt auch ein anderer ASM Code dabei raus.

habe nun meine #define geändert:
1
#define SPI_SEND(modul, data)  while(!(modul.STATUS & SPI_IF_bm)){}; modul.DATA = data
in
1
#define SPI_SEND(modul, data)  while(!(modul##_STATUS & SPI_IF_bm)){}; modul##_DATA = data

Aufruf
1
int main(void)
2
{
3
    SPI_SEND(SPIE, 0xAA);
4
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Diese 4.5.1 ist eine von Atmel gepatchte Version des Compilers, d.h. der 
entsprechende Bug-Report gehört nach atmel.com.

Auf einem "suberen" avr-gcc lässt sich dies nicht übersetzten und damit 
auch nicht nachvollziehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mit einer angepassten Quelle sehe ich das auch mit 4.3, 4.5 und 4.6.
1
typedef struct
2
{
3
    volatile unsigned char a,b,c,d;
4
} SPI_t;
5
6
#define SPIE (*(SPI_t*) 0x0AC0)
7
8
void foo (void)
9
{
10
    SPIE.d = 0xAA;
11
    while (!(SPIE.c & 0x80));
12
13
    SPIE.d = 0xBB;
14
    while (!(SPIE.c & 0x80));
15
}

Wenn du magst, kannst du einen Bug-Report bei GCC machen. Da es nur ein 
Optimierungsproblem ist, wird das frühestens in 4.7 behoben — zumindest 
im GCC und falls es in 4.7 noch besteht. Oder du musst eben selber 
Patches machen, besorgen oder zurückportieren, sobald es in 4.7/4.8 
behoben ist.

Mit 4.7 habe ich es noch nicht getestet, und ich kann auch (noch) nicht 
sagen, ob es am AVR-Teil hängt oder nicht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Eigentlich gehört das ins GCC-Forum.

Das Problem entsteht beim tree→RTL Lowering.

von Julius F. (xeno)


Lesenswert?

> Diese 4.5.1 ist eine von Atmel gepatchte Version des Compilers, d.h. der
> entsprechende Bug-Report gehört nach atmel.com.

> Auf einem "suberen" avr-gcc lässt sich dies nicht übersetzten und damit
> auch nicht nachvollziehen.

Mit WinAVR 20100110 besteht das Problem auch.
Werde gleich mal aufm Linux System einen "sauberen" avr-gcc compilieren 
mal gucken wie es dort aussieht.

> Wenn du magst, kannst du einen Bug-Report bei GCC machen.

Mein Schrift-Englisch ist jetzt nicht so gut das ich es machen würde.
Ich würde mich freuen wenn einer von euch es dort Posten könnte.

> Eigentlich gehört das ins GCC-Forum.

Ja, richtig. Hab es leider aus Reflex / Gewohnheit hier gepostet. Ein 
Mod könnte es ja verschieben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Julius F. schrieb:

>> Wenn du magst, kannst du einen Bug-Report bei GCC machen.
>
> Mein Schrift-Englisch ist jetzt nicht so gut das ich es machen würde.
> Ich würde mich freuen wenn einer von euch es dort Posten könnte.

Ist PR50448.

von Julius F. (xeno)


Lesenswert?

Danke dir.

Ich werde es weiterverfolgen.

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.