avr-gcc: den Zugriff auf register variablen optimieren?

Gast #2163784
Lesenswert?

Hallo,

ich versuche mit dem avr-gcc effizient auf register variablen 
zuzugreifen.
Leider macht er aus
1
register i asm("r17")
2
...
3
--i

folgendes
1
    mov r24,r17
2
    subi r24,lo8(-(-1))
3
    mov r17,r24

Der Compiler scheint register variablen wie Ram zu behandeln:
laden, dekrementieren, speichern. Es ist egal welches Register ich 
benutze (high oder low).
Wie kann ich den Compiler dazu bringen ein 'subi r17' oder 'dec' zu 
generieren?
(avr-gcc version ist 4.3.4)

Vielen Dank im Voraus,

Chris
Persönliche Seite #2163794
Lesenswert?

Chris schrieb:
> Wie kann ich den Compiler dazu bringen ein 'subi r17' oder 'dec' zu
> generieren?

Gar nicht. Wenn du wirklich was optimieren möchtest, schreib Funktionen 
direkt in ASM und linke sie mit dem übrigen Programm zusammen. Dazu 
gibt's ein Kapitel in der avr-libc Doku inkl. Beispiel.

Welchen Code generiert der Compiler für i-- ?
Wie wirken sich die Optimierungen aus (-O2 oder -Os)?
Moderator #2163910
Lesenswert?

Chris schrieb:
> Der Compiler scheint register variablen wie Ram zu behandeln:
> laden, dekrementieren, speichern.

In welchem Kontext passiert das? Hast du ein kleines, kompilierbares
Testprogramm, wo man das Problem sehen kann?

Unabhängig davon ist es aber immer etwas gefährlich, C-Variablen feste
Register zuzuweisen und bringt oft mehr Nachteile als Vorteile.
Gast #2164069
Lesenswert?

@tom:
Also der GCC kann mir doch ganz toll ganze Codeblöcke wegoptimieren
(wenn man mal das volentile vergisst ;-) und dann kann er diese
einfache Registeroptimierung nicht ???

@Yalu
Ich probiere mal das runterzubrechen in ein kleines Programm. Mir ist es 
bei der Statevariabe aufgefallen, die in der Interruptrutine verändert 
wird.

Eine Registervariable bringt mir  schon ca 100 Bytes weniger, weil state 
naürlich als volatile deklariert ist und daher immer aus dem Ram neu 
geladen und gespeichert wird wird.
Und das Programm macht nicht so viel, auf ein Register kann ich (bzw. 
der Compiler) gut verzichten. Und die Avr-Lib wird auch nicht benutzt, 
also auch von hier keine Gefahr.

Ich versuche mal ein kleines Beispiel zu machen, vielleicht bring das 
neue Erkenntnisse.

Viele Grüsse,

Chris
Gast #2164145
Lesenswert?

"register" ist ein Relikt aus dem Ur-C-PDP-11-Pleistozän. Aktuelle gcc's 
nehmen das höchstens noch als unverbindlichen Wunsch des Programmieres 
zur Kenntnis, und ignorieren es, wenn immer erforderlich. "volatile 
register" kann der gcc schon gar nicht, was u.a. daran liegt, daß das 
gar keinen Sinn ergibt.

Wenn du deine volatile state-variable am Anfang der ISR in eine lokale 
Variable umkopierst, und am Ende wieder zurück, dann hat der Compiler 
dazwischen alle Optimierungsmöglichkeiten. Den würde ich da einfach 
machen lassen, und ihm nicht ins Handwerk pfuschen.

Wenn es tatsächlich auf jeden einzelnen Zyklus und jedes einzelne Byte 
ankommt, nimm, wie schon gesagt wurde, Assembler.

Oliver
Gast #2164208
Lesenswert?

Ja dass hört sich gut an, das den Compiler einfach machen zu lassen. 
Leider ist er doch nicht so schlau. Wie gesagt, mit dem Holzhammer an 
eine Registervariable gebunden spart mir schon 100 Byte und ich bin halt 
knapp über den 2K vom ATtiny2313. und wenn er den Zugriff auch noch 
besser machen würde hätte ich noch mehr davon. Im Hauptprogramm wird die 
Statevariable auch sehr häufig abgefragt.
Vielleicht wenn man das Programm mit --complete (oder wie heisst die 
option?) compiliert kann der Compiler das besser optimieren. (hat da 
jemand Erfahrung?)

Zu Assembler kann ich mich noch nicht durchringen, solange ich es in C 
hinbekomme.

Gruss

Chris
Persönliche Seite #2164311
Lesenswert?

Wenn du Code sparen willst, dann:

Fass Variablen, die zueinander gehören, zu Strukturen zusammen und 
greife indirekt darauf zu. Das spart dann pro Zugriff 2 Bytes. Um das 
sinnvoll nutzen zu können, braucht das Programm aber eine gewisse 
Grundstruktur, weil der Code ansonsten nur Pointervariablen am 
Rumschaufeln ist (wegen der mageren Ausstattung der AVRs mit 
Adressregistern).
Gast #2164586
Lesenswert?

Hallo,

hier habe ich ein kleines Testprogramm geschrieben:
1
#include <stdint.h>
2

3
volatile register uint8_t state __asm__("r24");
4

5
// damit die Schleife nicht komplett wegoptimiert wird:
6
static volatile uint8_t tmp; 
7

8
void main() {
9
//register uint8_t state ;
10

11
   state=100;
12
   tmp=state;
13

14
   do {
15
        tmp=state;      
16
   }while (--state);   
17
}

Mit -O0 sieht man schön dass r24 als 'Speicherzelle' betrachtet wird die 
kein register ist und nicht rechnen kann:
1
        ldi r24,lo8(100)         ;  state,
2
.LM2:
3
        mov r25,r24      ;  state.0, state
4
        sts tmp,r25      ;  tmp, state.0
5
.L2:
6
.LM3:
7
        mov r25,r24      ;  state.1, state
8
        sts tmp,r25      ;  tmp, state.1
9
.LM4:
10
        mov r25,r24      ;  state.2, state
11
        subi r25,lo8(-(-1))      ;  state.3,
12
        mov r24,r25      ;  state, state.3
13
        mov r25,r24      ;  state.4, state
14
        tst r25          ;  state.4
15
        brne .L2         ; ,

Leider kann der Optimierer -O3 das nicht mehr richten :-((
1
        ldi r24,lo8(100)         ;  state,
2
.LM2:
3
        ldi r25,lo8(100)         ;  state.0,
4
        sts tmp,r25      ;  tmp, state.0
5
.L2:
6
.LM3:
7
        mov r18,r24      ;  state.1, state
8
        sts tmp,r24      ;  tmp, state.1
9
.LM4:
10
        subi r18,lo8(-(-1))      ;  state.3,
11
        mov r24,r18      ;  state, state.3
12
        brne .L2         ; ,

So, hat jemand eine Idee wie man dem Compiler helfen kann ?

Chris
Gast #2164708
Lesenswert?

@Oliver:
>> "volatile register" kann der gcc schon gar nicht,
>Je nun, was wollte ich dir mit meinen Worten wohl sagen?

Das weiss ich leider nicht was Du mir sagen willst.
Also der Compiler beschwert sich nicht.
Aber lassen wir doch das volatile vorm Register mal weg dann macht der 
Compiler dieses hier:
1
.LM1:
2
        ldi r25,lo8(100)         ;  tmp43,
3
        sts tmp,r25      ;  tmp, tmp43
4
.L2:
5
        .stabn  68,0,14,.LM2-.LFBB1
6
.LM2:
7
        sts tmp,r25      ;  tmp, ivtmp.16
8
        subi r25,lo8(-(-1))      ;  ivtmp.16,
9
        .stabn  68,0,15,.LM3-.LFBB1
10
.LM3:
11
        brne .L2         ; ,
12
        ldi r24,lo8(0)   ;  state,

Das Register r24 wird ganz zum Schluss mit 0 geladen. Ist ja auch 
korrekt so. Aber das will ich nun schon gar nicht.

Der GCC macht auf jeden Fall was ziemlich anderes. Vielleicht ja auch 
was anderes als im Manual steht. Macht aber Sinn wenn GCC das Register 
als Speicherstelle ansieht und halt jedes mal vor der Verwendung sich 
den Wert daraus holt. Und nicht nur einmal zum Schluss.

Gruss

chris
Persönliche Seite #2164764
Lesenswert?

Chris schrieb:
> @Oliver:
>>> "volatile register" kann der gcc schon gar nicht,
>>Je nun, was wollte ich dir mit meinen Worten wohl sagen?
> Das weiss ich leider nicht was Du mir sagen willst.

volatile bezieht sich auf Objekte in der Speicherklasse static storage, 
und da liegt eine explizite Registervariable nun mal nicht. Die liegt 
eben in der Speicherklasse register, wie angeordnet.

Von daher dient ein "volatile register ...asm" bestenfalls der eigenen 
Verwirrung; stätestens wenn das Register in einer ISR und im 
Normalo-Code verwendet wird, ist eine Bauchlandung vorprogrammiert (etwa 
Poll-Schleife auf so eine Variable).
#2164787
Lesenswert?

Chris schrieb:
> Vielleicht wenn man das Programm mit --complete (oder wie heisst die
> option?) compiliert kann der Compiler das besser optimieren. (hat da
> jemand Erfahrung?)

--combine

Ja, ich benutze es gerne.
Es macht eine sehr effiziente Optimierung und findet fast jedes 
vergessene "volatile".
D.h. wenns danach nicht mehr geht, hat er Dir was wegoptimiert, was 
volatile sein muß.

Es müssen alle C-Files in einem Rutsch compiliert werden. Das geht am 
besten auf der Kommandozeile, *.c expandiert zu allen C-Files im 
aktuellen Verzeichnis.

Hier ein Beispiel (a.bat):

Beitrag "mehrere MC seriell über Datenbus verbinden (1Draht)"


Peter
Persönliche Seite #2164790
Lesenswert?

Hier mal ein kleines Beispiel:
1
register char state asm ("r2");
2

3
void bar (void);
4

5
void foo (void) 
6
{
7
   state = 100;
8

9
   while (1)
10
   {
11
        do 
12
        {
13
            bar();
14
        } while (state);
15
   }
16
}

avr-gcc 4.5 (ditto avr-gcc 3.4) macht daraus:
1
foo:
2
  ldi r24,lo8(100)   ;  20  *reload_inqi  [length = 2]
3
  mov r2,r24
4
.L2:
5
  call bar   ;  8  call_insn/3  [length = 2]
6
  rjmp .L2   ;  21  jump  [length = 1]

Und der Code ist so auch ok.
Gast #2164804
Lesenswert?

Johann L. schrieb:

> Aber bekanntlich ignoriert man Warnungen am besten... sind ja keine
> Fehler ;-)

Welche Warnungen ??

Compiliere bitte mit  GCC 4.3.4 und natürlich mit -ffixed-r24

Zu den Warnungen bei volatile register gibts einen Thread auf der 
AVR-GCC-Bug-Liste, das wurde anscheinend oft in den verschiedenen 
Versionen geändert.

Hilft mir aber nicht viel. Hast Du einen konstruktiven Vorschlag wie man 
effektiv eine Registervariable in C verwenden kann die in einer ISR 
verändert wird?
Persönliche Seite #2164854
Lesenswert?

Chris schrieb:

> Formaljuristisch ist er ok. Aber es soll natürlich die Abfrage auf
> state==0 nicht wegoptimiert werden. Bei einer statischen Variable macht
> man das mit volatile. Und was macht man bei einer Registervariablen ???

Selbst der Klassiker
1
asm volatile ("":"+r"(state));

bleibt wirkungslos... (avr-gcc 3.4, 4.3, 4.5), ebenso ein Verändern von 
state in der Schleife. Was willst da machen?

Verwende andere, robuste Techniken, um die Codegröße zu optimieren. Da 
ist zudem mehr zu holen. Auf den L-Regs sind eh kaum Operationen 
möglich. Wahrscheinlich wird dein Code mit der Registervariablen nur 
deshalb so klein, weil die hälfte in der Tonne landet...
Persönliche Seite #2164861
Lesenswert?

Chris schrieb:

> Compiliere bitte mit  GCC 4.3.4 und natürlich mit -ffixed-r24

Ich hab hier net jede jemals veröffentliche avr-gcc Version ;-)

-ffixed-n änder nix am ABI. Es nimmt die Variable dem Registerallokator, 
wenn er Pseudoregister zuordnen will. Aber -ffixed verhindert nicht das 
Erzeugen von r24 per se, zB durch Funktionsaufrufe wie a = b / c zur 
libgcc.

Bestenfalls Register, die nicht vom ABI belegt werden (wie oben schon 
geschrieben r2-r7) sind für sowas zu gebrauchen.

In einer Anwendung mit avr-gcc 3.4 verwende ich globale 
Registervariablen, weil das 200000 Speicherzugriffe pro Sekunde spart. 
Im Endeffekt bringt's nicht sooo viel, auch nicht für die Codegröße (die 
übrigens kleiner ist als mit jeder 4-er Version, die ich je angetestet 
habe). Aber nun ist's in Code eben mal drinne... An Codegröße macht das 
einen Unterschied von -0.5%, also lächerlich. Aber da ich in der ISR 
jeden Tick brauche, bleibt's eben drin.

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