mikrocontroller.net

Forum: Compiler & IDEs Ignoriert gcc 4.x Registerzuweisungen von Variablen?


Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich hänge schon lange an einem Problem, welches ich hier:
Beitrag "Fehler beim compilieren mit gcc 4.2.1 den ich nicht verstehe"
beschrieben hatte.
Nun habe ich mir mal die Mühe gemacht den erzeugten Assembler Code 
durchzusehen:

Meine Funktion beginnt so:
void sched(void) __attribute__ ((naked));
void sched(void) {
register unsigned char sreg asm("r0");
register unsigned char sreg2 asm("r3");
sreg = SREG;
asm volatile ("push r0");
asm volatile ("push r2");

Daraus wird bei dem gcc 3.4:
/* prologue: frame size=0 */
/* prologue: naked */
/* prologue end (size=0) */
  .stabn 68,0,107,.LM17-sched
.LM17:
  in r0,95-0x20
  .stabn 68,0,108,.LM18-sched
.LM18:
/* #APP */
  push r0
  .stabn 68,0,109,.LM19-sched
.LM19:

bei dem gcc 4.1 jedoch:
/* prologue: frame size=0 */
/* prologue: naked */
/* prologue end (size=0) */
  .stabn  68,0,107,.LM15-sched
.LM15:
  in r17,95-0x20
  .stabn  68,0,108,.LM16-sched
.LM16:
/* #APP */
  push r0
  .stabn  68,0,109,.LM17-sched
.LM17:

Somit ließe sich erklären warum sich mein Programm doch recht 
"merkwürdig" verhält. r17 Muss laut avr libc doku bei einem 
Funktionsaufruf gesichert werden, r0 jedoch nicht.

Bug beim GCC? oder ein Feature, oder habe ich die falsche Syntax 
verwendet?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
An feste Register gebundene Variablen werden nur auf globaler Ebene
unterstützt.  Für lokale Variablen kannst du dem Registerallokator
nicht vorschreiben, wohin er sie legen soll.

naked functions are evil.
naked functions are evil.
naked functions are...

Schreib die komplette Funktion in eine Assemblerdatei.  Das
naked-Attribut hat wirklich nur Sinn für Dinge wie die
nicht-wirklich-Funktionen für die .initN sections.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
R0 und R1 sind außerdem reserviert (LPM, MUL) und R0 ist auch noch das 
Zero-Register.

Für globale Registervariablen sind nur R2..R17 frei.


Peter

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
r0 ist _tmp_reg_, r1 ist _zero_reg_.

Die Register ab r8 sind auch nur dann für fest gebundene Variablen
benutzbar, wenn man keine Funktionen mit vielen/großen Parametern
in der Applikation hat, ansonsten werden sie für die Parameterübergabe
mit genutzt.  In den Mini-Applikationen, in denen man typischerweise
versuchen wird, viele Variablen an Register zu binden, dürfte das
aber wohl eher eine untergeordnete Rolle spielen.

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:
> An feste Register gebundene Variablen werden nur auf globaler Ebene
> unterstützt.  Für lokale Variablen kannst du dem Registerallokator
> nicht vorschreiben, wohin er sie legen soll.
Danke, das wusste ich nicht.

> naked functions are evil.
Ich sichere direkt alle Register, die die avr-libc als "Call-saved 
registers" nennt. Solange die Funktion nicht auch noch extra Speicher 
auf dem Stack benötigt sehe ich deswegen kein Problem. Ich könnte auch 
auf das Naked verzichten, aber dann wüsste ich nicht welche Register der 
Compiler für mich sichert und welche mir noch fehlen. Wenn ich dann 
allerdings weiterhin alle sicher würde, würde die Funktion unnötig 
Flash, Rechnenzeit und SRAM benötigen.

> Schreib die komplette Funktion in eine Assemblerdatei.
Dazu fehlen mir noch die nötigen Kentnisse, lassen sich aus 
Assemblerdateinen überhaupt in Header Dateien per #define definerten 
Werte und gewönliche Arrays (bzw dessen Startadresse) zugreifen? 
Außerdem wollte ich soweit wie möglich alles in C lösen.

Als simplen Workaround habe ich die Variablen Definitionen einfach 
global definiert und damit läuft mein Programm jetzt auch mit dem gcc 
4.1 :-)

Wahrscheinlich werde ich noch die globalen Variablen weglassen und das 
Einlesen statt durch
sreg = SREG;
mit
asm volatile ("in r0, %0" : : "I" (_SFR_IO_ADDR(SREG)): "r0");
erledigen.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> naked functions are evil.
> Ich sichere direkt alle Register, die die avr-libc als "Call-saved
> registers" nennt. Solange die Funktion nicht auch noch extra Speicher
>auf dem Stack benötigt sehe ich deswegen kein Problem. Ich könnte auch
> auf das Naked verzichten, aber dann wüsste ich nicht welche Register
> der Compiler für mich sichert und welche mir noch fehlen.

Warum mußt du denn überhaupt irgendwelche Register manuell sichern?

> Dazu fehlen mir noch die nötigen Kentnisse, lassen sich aus
> Assemblerdateinen überhaupt in Header Dateien per #define definerten
> Werte und gewönliche Arrays (bzw dessen Startadresse) zugreifen?

Kommt auf den Header an, aber im Prinzip ja. gcc benutzt für Assembler 
denselben Präprozessor wie für C, also kann er auch #defines auflösen, 
sofern sie nicht durch was C-spezifisches ersetzt werden. Dort, wo es 
für C und Assembler unterschiedlich ist, könnte man im Header auch eine 
Fallunterscheidung einbauen. Und externe Symbole können im Assembler 
natürlich auch genutzt werden. Schließlich macht auch der Compiler aus 
dem C-Code erstmal Assembler-Code.

> Außerdem wollte ich soweit wie möglich alles in C lösen.

Dann drängt sich umso mehr die Frage auf, warum du dann überhaupt 
irgendwelche Register sichern willst.

> Wahrscheinlich werde ich noch die globalen Variablen weglassen und das
> Einlesen statt durch
> sreg = SREG;
> mit
> asm volatile ("in r0, %0" : : "I" (_SFR_IO_ADDR(SREG)): "r0");
> erledigen.

Warum muß es unbedingt r0 sein? Gerade von r0 sollte man die Finger 
lassen, denn es ist ja wie schon erwähnt im Compiler bereits mehrfach 
belegt. Ich würde dem Compiler die Registerverwaltung überlassen, etwa 
so:

uint8_t sreg;
asm volatile ("in %0, %1" : "=r" (sreg): "I" (_SFR_IO_ADDR(SREG)));

Autor: Malte __ (malte) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus wrote:
> Warum mußt du denn überhaupt irgendwelche Register manuell sichern?
Bei dem Programm Teil handelt es sich um einen kooperativen Scheduler. 
Es werden also alle Register bei der sich die libc auf ein Beibehalten 
des Inhalts bei einem Funktionsaufruf verlässt gesichert, der SP auf den 
Stack eines anderen Task gesetzt und für diesen alle Register wieder 
hergestellt. Das Ganze funktioniert auch inzwischen wunderbar.

> Kommt auf den Header an, aber im Prinzip ja. gcc benutzt für Assembler
> denselben Präprozessor wie für C, also kann er auch #defines auflösen,
> sofern sie nicht durch was C-spezifisches ersetzt werden. Dort, wo es
> für C und Assembler unterschiedlich ist, könnte man im Header auch eine
> Fallunterscheidung einbauen. Und externe Symbole können im Assembler
> natürlich auch genutzt werden. Schließlich macht auch der Compiler aus
> dem C-Code erstmal Assembler-Code.
Gut zu wissen.

> Warum muß es unbedingt r0 sein? Gerade von r0 sollte man die Finger
> lassen, denn es ist ja wie schon erwähnt im Compiler bereits mehrfach
> belegt.
Es muss nicht r0 sein, sondern einfach eins welches
http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...
nicht als "Call-saved registers" aufführt. r0 habe ich genommen weil 
dort steht "may be used to remember something for a while within one 
piece of assembler code" und ich denke einen Wert einlesen und im 
nächsten Schritt sichern fällt darunter. Danach brauche ich den Inhalt 
nicht mehr.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Malte __ wrote:

>> Warum mußt du denn überhaupt irgendwelche Register manuell sichern?

> Bei dem Programm Teil handelt es sich um einen kooperativen Scheduler.

Das ist aber nun wirklich ein typischer Kandidad dafür, dass man das
Stück komplett in eine Assemblerdatei schreibt.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder eine naked-Funktion in C, die dann aber den ganzen Code als 
Inline-Assembler enthält, was aber letztendlich dann auch keinen Vorteil 
mehr gegenüber einer reinen Assembler-Datei hat. Aber den Compiler 
seinen Teil erledigen lassen und dann irgendwie in Inline-Assembler den 
Rest dranflanschen ist eher keine gute Idee.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
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.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.