Forum: Compiler & IDEs Problem inline Assembler


von Peter D. (peda)


Angehängte Dateien:

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.
1
unsigned short add16( unsigned short a, unsigned short b )
2
{
3
  __asm__ __volatile__ (
4
        "add %A0, %A1" "\n\t"
5
        "adc %B0, %B1"
6
        : "=r" (a)
7
        : "r" (b)
8
  );
9
  return a;
10
}
11
12
13
unsigned short add16a( unsigned short a, unsigned short b )
14
{
15
  return a + b;
16
}
17
18
19
unsigned long add32( unsigned long a, unsigned long b )
20
{
21
  __asm__ __volatile__ (
22
        "add %A0, %A1" "\n\t"
23
        "adc %B0, %B1" "\n\t"
24
        "adc %C0, %C1" "\n\t"
25
        "adc %D0, %D1"
26
        : "=r" (a)
27
        : "r" (b)
28
  );
29
  return a;
30
}
31
32
33
unsigned long add32a( unsigned long a, unsigned long b )
34
{
35
  return a + b;
36
}
37
38
39
unsigned long long add64( unsigned long long a, unsigned long long b )
40
{
41
  __asm__ __volatile__ (
42
        "add %A0, %A1" "\n\t"
43
        "adc %B0, %B1" "\n\t"
44
        "adc %C0, %C1" "\n\t"
45
        "adc %D0, %D1" "\n\t"
46
        "adc %E0, %E1" "\n\t"
47
        "adc %F0, %F1" "\n\t"
48
        "adc %G0, %G1" "\n\t"
49
        "adc %H0, %H1"
50
        : "=r" (a)
51
        : "r" (b)
52
  );
53
  return a;
54
}
Geht inline Assembler prinzipiell nur mit 16Bit Variablen?


Peter

von Falk B. (falk)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Peter D. (peda)


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?
1
unsigned long add32( unsigned long a, unsigned long b )
2
{
3
  __asm__ __volatile__ (
4
        "add %A0, %A1" "\n\t"
5
        "adc %B0, %B1" "\n\t"
6
        "adc %C0, %C1" "\n\t"
7
        "adc %D0, %D1"
8
        : "=r" (a), "=r" (b)
9
        : "r" (a)
10
  );
11
  return a;
12
}


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

von Peter D. (peda)


Angehängte Dateien:

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:
1
    AVR-GCC    Assembler
2
3
Addition:  336    18
4
5
Subtraction:  364    18
6
7
Multiplication:  812    106
8
9
Division:  3632    130

Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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:
1
Index: gcc/config/avr/avr.c
2
===================================================================
3
--- gcc/config/avr/avr.c    (revision 124056)
4
+++ gcc/config/avr/avr.c    (working copy)
5
@@ -1118,7 +1118,7 @@
6
 {
7
   int abcd = 0;
8
 
9
-  if (code >= 'A' && code <= 'D')
10
+  if (code >= 'A' && code <= 'H')
11
     abcd = code - 'A';
12
 
13
   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.

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.