www.mikrocontroller.net

Forum: Compiler & IDEs Problem inline Assembler


Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich muß 64Bit rechnen und das geht ja mit dem AVR-GCC nur äußerst 
bescheiden, es werden riesige und langsame Codemonster erzeugt.

Ich habe daher versucht, die Funktionen in Assembler zu schreiben, 
scheitere aber schon an der Addition.

Für 8Bit und 16Bit Zahlen ist alles o.k., aber bei 32Bit und 64Bit macht 
er nur noch Schrott.
Der 1. Operand wird weggeschmissen und der 2. mit sich selbst addiert.

Im Anhang das Listing.

unsigned short add16( unsigned short a, unsigned short b )
{
  __asm__ __volatile__ (
        "add %A0, %A1" "\n\t"
        "adc %B0, %B1"
        : "=r" (a)
        : "r" (b)
  );
  return a;
}


unsigned short add16a( unsigned short a, unsigned short b )
{
  return a + b;
}


unsigned long add32( unsigned long a, unsigned long b )
{
  __asm__ __volatile__ (
        "add %A0, %A1" "\n\t"
        "adc %B0, %B1" "\n\t"
        "adc %C0, %C1" "\n\t"
        "adc %D0, %D1"
        : "=r" (a)
        : "r" (b)
  );
  return a;
}


unsigned long add32a( unsigned long a, unsigned long b )
{
  return a + b;
}


unsigned long long add64( unsigned long long a, unsigned long long b )
{
  __asm__ __volatile__ (
        "add %A0, %A1" "\n\t"
        "adc %B0, %B1" "\n\t"
        "adc %C0, %C1" "\n\t"
        "adc %D0, %D1" "\n\t"
        "adc %E0, %E1" "\n\t"
        "adc %F0, %F1" "\n\t"
        "adc %G0, %G1" "\n\t"
        "adc %H0, %H1"
        : "=r" (a)
        : "r" (b)
  );
  return a;
}


Geht inline Assembler prinzipiell nur mit 16Bit Variablen?


Peter

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Peter Dannegger (peda)

>Ich muß 64Bit rechnen und das geht ja mit dem AVR-GCC nur äußerst
>bescheiden, es werden riesige und langsame Codemonster erzeugt.

Wohl leider wahr :-(

>Geht inline Assembler prinzipiell nur mit 16Bit Variablen?

AFAIK nein. Aber nach der Beschreibung in der Doku der libc ist das 
schon ne kleine Wissenschaft für sich.

MFG
Falk

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

Bewertung
0 lesenswert
nicht lesenswert
Nützlicher wäre es, du würdest die entsprechenden Routinen für die
libgcc.S beisteuern... wobei die Addition ja noch inline im Compiler
erfolgt und offenbar auch nicht schlechter als deine ist, davon
abgesehen, dass sie funktioniert. ;-)

Du hast einfach einen Denkfehler: beide Operanden sind input-Operanden,
und einer von beiden gleichzeitig output-Operand.  Du hast aber nur
einen als input und einen als output deklariert.  Damit steht es dem
Compiler frei, für beide Operanden die gleiche location zu wählen.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:
> Nützlicher wäre es, du würdest die entsprechenden Routinen für die
> libgcc.S beisteuern...

Kann ich gerne machen, weiß bloß nicht wo man das hinposten muß (ginge 
hier?).
Die Routinen sind ja einfach.


> wobei die Addition ja noch inline im Compiler
> erfolgt und offenbar auch nicht schlechter als deine ist, davon
> abgesehen, dass sie funktioniert. ;-)

Die für 64Bit ist richtig groß (168 Words), daher hatte ich sie nicht 
mit gepostet, um das Listing nicht unnötig aufzublähen.

Zu erwarten sind eigentlich max 9 Words (1*ADD, 7*ADC, 1* RET).

Was er in meinem Code unnnütz pusht und moved, entzieht sich völlig 
meinem Verständnis.


> Du hast einfach einen Denkfehler: beide Operanden sind input-Operanden,
> und einer von beiden gleichzeitig output-Operand.  Du hast aber nur
> einen als input und einen als output deklariert.  Damit steht es dem
> Compiler frei, für beide Operanden die gleiche location zu wählen.

Könnte das dann so richtig sein?

unsigned long add32( unsigned long a, unsigned long b )
{
  __asm__ __volatile__ (
        "add %A0, %A1" "\n\t"
        "adc %B0, %B1" "\n\t"
        "adc %C0, %C1" "\n\t"
        "adc %D0, %D1"
        : "=r" (a), "=r" (b)
        : "r" (a)
  );
  return a;
}


Bei 32Bit funktionierts, ist sogar kürzer als der Bibliothekscode.

Bei 64Bit macht er allerdings unnützte pushs/moves, die den 1.Opernaden 
zerstören, d.h. es kommt wieder nur 2*Operand2 heraus.


Peter

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Wie man im Listing sieht, geht 64Bit prinzipiell nicht, die Parameter 
%E0..%H0, %E1..%H1 werden nicht als die oberen 4 Bytes expandiert.

Man könnte die Register direkt hinschreiben, aber dann gibts ne Warnung, 
daß kein Returnwert existiert.


Anbei mal die 4 Grundrechenarten in Assembler geschrieben.
Leider habe ich überhaupt keine Ahnung, wie man die nun in die libgcc.S 
einbindet.

Wenn mir jemand vielleicht helfen könnte?


Die Größenunterschiede (in Bytes) sind ja riesig:

    AVR-GCC    Assembler

Addition:  336    18

Subtraction:  364    18

Multiplication:  812    106

Division:  3632    130


Peter

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

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:

> Wie man im Listing sieht, geht 64Bit prinzipiell nicht, die Parameter
> %E0..%H0, %E1..%H1 werden nicht als die oberen 4 Bytes expandiert.

Ja, da wirst du Recht haben. :-(

Dieser Patch im GCC müsste das ändern, aber ich bin kein GCC-Experte:
Index: gcc/config/avr/avr.c
===================================================================
--- gcc/config/avr/avr.c    (revision 124056)
+++ gcc/config/avr/avr.c    (working copy)
@@ -1118,7 +1118,7 @@
 {
   int abcd = 0;
 
-  if (code >= 'A' && code <= 'D')
+  if (code >= 'A' && code <= 'H')
     abcd = code - 'A';
 
   if (code == '~')

> Anbei mal die 4 Grundrechenarten in Assembler geschrieben.  Leider
> habe ich überhaupt keine Ahnung, wie man die nun in die libgcc.S
> einbindet.

Das kannst du dir in der libgcc.S anlesen.  Allerdings denke ich, dass
zumindest Addition und Subtraktion besser im Compiler selbst
aufgehoben sind, statt dass man da Bibliotheksfunktionen aufruft.  Bei
der Multiplikation bin ich mir nicht so sicher (könnte davon abhängen,
ob ein Hardware-Multiplizierer da ist), die Division ist sicherlich
als Funktion besser aufgehoben.

Es müssten dafür neue Code-Templates in gcc/config/avr/avr.md
eingebaut werden, die die 64-bit-Operationen implementieren.  Diese
würden dann übrigens auch die Präfixbuchstaben E bis H mit benutzen,
d. h. obiger Patch wäre die Voraussetzung dafür.  (Wem der inline-
Assembler schon immer kryptisch vorkam, das ist der Grund: das ist
eigentlich eine komplett interne Compilersyntax und -semantik, die da
nach außen geführt wird.  Dafür kann er eben praktisch auch alles das,
was der Compiler intern selbst kann.)

Die derzeitigen 64-bit-Operationen werden meines Wissens vom
generischen Compilercode komplett synthetisiert, indem er die
vorhandenen low-level 32-bit-Operationen miteinander verkettet.  Das
erklärt auch, warum dabei solch umständlicher Code generiert wird.

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.