Forum: Compiler & IDEs fehlerhafte Registerzuordnung bei Inline-Assembler


von Schawwi (Gast)


Lesenswert?

Hallo zusammen!

Ich habe ein echtes Problem bei der Registerzuordnung von einem 
Inline-Assembler-Schnipsel. Und zwar muss ich aus Zeitgründen die 
Befehlszeile
1
osc_x0 = ( ( (signed long)osc_coeff * (signed long)osc_x1 ) >> 14) - ((signed long)osc_x2);
in Assembler optimieren. Hierfür verwende ich grundlegend das 
Application Note 201 "Using the AVR Hardware Multiplier" von Atmel, was 
ich noch ein wenig für mein Problem optimiert habe.

Der Inline-Assembler-Schnipsel sieht im C-Code wie folgt aus:
1
    char asm_temp1;
2
    char asm_temp2;
3
    asm volatile (
4
      "clr   %A[temp1]                    \n\t" // [osc_x0 + temp] = osc_coeff * osc_x1
5
      "muls  %B[osc_x1],    %B[osc_coeff] \n\t"
6
      "movw  %A[osc_x0],    r0            \n\t"
7
      "mul   %A[osc_x1],    %A[osc_coeff] \n\t"
8
      "mov   %A[temp2],     r1            \n\t"
9
      "mulsu %B[osc_x1],    %A[osc_coeff] \n\t"
10
      "sbc   %B[osc_x0],    %A[temp1]     \n\t"
11
      "add   %A[temp2],     r0            \n\t"
12
      "adc   %A[osc_x0],    r1            \n\t"
13
      "adc   %B[osc_x0],    %A[temp1]     \n\t"
14
      "mulsu %B[osc_coeff], %A[osc_x1]    \n\t"
15
      "sbc   %B[osc_x0],    %A[temp1]     \n\t"
16
      "add   %A[temp2],     r0            \n\t"
17
      "adc   %A[osc_x0],    r1            \n\t"
18
      "adc   %B[osc_x0],    %A[temp1]     \n\t"
19
      "rol   %A[temp2]                    \n\t" // [osc_x0 + temp] = [osc_x0 + temp] << 1
20
      "rol   %A[osc_x0]                   \n\t"
21
      "rol   %B[osc_x0]                   \n\t"
22
      "rol   %A[temp2]                    \n\t" // [osc_x0 + temp] = [osc_x0 + temp] << 1
23
      "rol   %A[osc_x0]                   \n\t"
24
      "rol   %B[osc_x0]                   \n\t"
25
      "sub   %A[osc_x0],    %A[osc_x2]    \n\t" // osc_x0 = osc_x0 - osc_x2
26
      "sbc   %B[osc_x0],    %B[osc_x2]    \n\t"
27
      "clr   __zero_reg__                     "
28
        : [osc_x0] "=r" (osc_x0), [temp1] "=r" (asm_temp1), [temp2] "=r" (asm_temp2)
29
        : [osc_coeff] "a" (osc_coeff), [osc_x1] "a" (osc_x1), [osc_x2] "r" (osc_x2)
30
        : "r0", "r1"
31
    );

Wenn ich das ergebnis jedoch Disassembliere, fällt deutlich auf, dass 
manche Register zwei Variablen zugeordnet werden.
1
LDS     R20,0x01A5
2
LDS     R21,0x01A6
3
LDS     R18,0x01A7
4
LDS     R19,0x01A8
5
LDS     R24,0x01A9
6
LDS     R25,0x01AA
7
CLR     R25
8
MULS    R19,R21
9
MOVW    R20,R0
10
MUL     R18,R20
11
MOV     R24,R1
12
MULSU   R19,R20
13
SBC     R21,R25
14
ADD     R24,R0
15
ADC     R20,R1
16
ADC     R21,R25
17
MULSU   R21,R18
18
SBC     R21,R25
19
ADD     R24,R0
20
ADC     R20,R1
21
ADC     R21,R25
22
ROL     R24
23
ROL     R20
24
ROL     R21
25
ROL     R24
26
ROL     R20
27
ROL     R21
28
SUB     R20,R24
29
SBC     R21,R25
30
CLR     R1
31
STD     Y+3,R21
32
STD     Y+2,R20

Und zwar sieht die Zuordnung ja so aus:

osc_x0 = R20:R21
temp1 = R25
temp2 = R24
osc_coeff = R20:R21
osc_x1 = R18:R19
osc_x2 = R24:R25

Wie man sieht, werden die Ausgabe-Variablen mit gleichen Registern 
verknüpft, die schon für Eingabe-Variablen verwendet werden.

Mache ich hier etwas falsch? Muss ich noch irgendwelche Befehle 
hinzufügen? Oder ist das ein Fehler im GCC? Ich verwende das AVR Studio 
4.13, Build 528.
Wenn die Variablen eindeutige Register erhalten, funktioniert übrigens 
auch der Code. Also der stimmt.

Danke für Antworten im Voraus!

Christian

von Jörg X. (Gast)


Lesenswert?

Schau mal da rein: 
http://www.roboternetz.de/wissen/index.php/Inline-Assembler_in_avr-gcc#Operanden_und_Constraints
 Tipp: du suchst wahrsceinlich "&"

hth. Jörg

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


Lesenswert?

Schawwi wrote:

> Wie man sieht, werden die Ausgabe-Variablen mit gleichen Registern
> verknüpft, die schon für Eingabe-Variablen verwendet werden.

Ja, du hast dem Compiler ja auch nicht gesagt, dass er das nicht
darf.  Das ist also `by design' so.

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


Lesenswert?

p.s.: Falls du das in eine separate Funktion auslagern kannst, wäre
es wahrscheinlich in einer reinen Assemblerdatei besser aufgehoben.
Inline-Assembler-Code hat eine Tendenz, "write-only" zu sein.

von Christoph (Gast)


Lesenswert?

Hallo Jörg,
wie würde denn ein solches Gerüst aussehen, d.h eine im C Code genutzte 
Funktion in einer seperaten Datei in assembler definieren. Bin auf dem 
Gebiet recht frisch und freue mich über Hilfe.
Gruss,
Christoph

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


Lesenswert?

Du brauchst kaum ein ,,Gerüst''.  Alles, was du brauchst, ist das
ABI, das findest du in der avr-libc-FAQ:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

Wenn du mir mal normalen Assemblercode dafür aufschreibst (möglichst
mit symbolischen Namen), schreib ich dir den Prolog und Epilog dafür.

von Christoph (Gast)


Lesenswert?

Danke für dein Angebot. Momentan schaut der Teil meines Programmes wie 
folgt aus:

void set(base *cp)
{
   unsigned int *ptr_1 = cp->start_ptr;
   unsigned int *ptr_2 = cp->end_ptr;

    _asm_ __volatile__("mov   r2,  %0           \n\t"
                         ....
                         ......


                                   :
                                   :"r"(ptr_1),"r"( ptr_2)
                                   :"r2","r3","r4","r5","r6","r7"

                                  );
}

Hierbei handelt es sich um eine Speichermanipulation Routine. Anfangs- 
und Endadresse liegen in einer struct namens 'base' vor. Da dieser Teil 
relative zeitkritisch ist, habe ich mich für die Implemtierung in inline 
assembler entschieden. Was das ganze natürlich beschleunigen würde wäre 
der Verzicht auf die Nutzung einer Funktion und den Code direkt 
einzubetten. Jedoch kommt diese Routine an recht vielen Programmstellen 
vor.
Habe ich Dich richtig verstanden, dass es möglich ist die komplette 
Routine in assembler zu schreien und diese via headerdatei einzubinden ?
Die Zielplatform ist übrigens kein Atmel sondern ein NiosII. 
Beispeilcode kann sich jedoch auch gerne auf einen AVR beziehen.
Vielen Dank, Christoph

von Falk B. (falk)


Lesenswert?

@ Christoph (Gast)

>Die Zielplatform ist übrigens kein Atmel sondern ein NiosII.

BOOAAHHH! Auf nem FPGA so einen Krampf veranstalten? Mann, da hätte ich 
dreimal nen kleinen Coprozessor in Hardware drangehängt und gut ist. 
Wenn der dann immer noch zu langsam ist gehts sowieso nicht.

>Beispeilcode kann sich jedoch auch gerne auf einen AVR beziehen.

Nützt wenig bis gar nix. Denn diese Tricks sind sehr Prozessor- und 
Compiler spezifisch.

MFG
Falk

von Christoph (Gast)


Lesenswert?

@Falk

Krampf ist dies keinewegs. Bei diesem Projekt handelt es sich um ein 
komplettes SOPC mit einem schnellen Hardware Frontend und einem NiosII 
der als PC Schnittstelle agiert. Er empfängt die prozessierten Daten des 
Frontends, bereitet sie auf und gibt sie, wenn der PC fragt an diesen 
weiter. Mit zeitkritisch habe ich vielleicht etwas übertrieben. Aber 
umso mehr Luft, umso besser. Jedoch habe ich die Erfahrung gemacht das 
NiosII gcc Compiler nicht wirklich immer netten Code ausspuckt, was 
nicht verwunderlich ist in Anbetracht der konfigurierbaren Zielplatform. 
Daher kam ich auf den Weg gewisse Codefragmente in assembler zu 
implementieren, was der Nios nun auch spielend schafft. Eine Coprozessor 
Lösung wäre in meinem konkreten Fall sicherlich übertriebene Härte. Was 
die Portierbarkeit von AVR Lösungen angeht kann ich nur sagen, dass ich 
schon vielerlei 'Kniffs' (die ich unter anderem auch hier gefunden 
habe), wenn auch sicherlich in leicht abgewandelter Form auf dem Nios 
zum Laufen bekommen habe. Was mich nun eigentlich interessiert ist wie 
schon oben aufgeführt das 'Auslagern und Definieren' einer im C 
Quellcode benutzten assembler Funktion.
Gruss,
Christoph

von Schawwi (Gast)


Lesenswert?

Hallo und danke für die Antworten!

Also amn muss erst mal durch diese Kürzel für die Registernutzung 
durchsteigen! :-)

Aber jetzt funktioniert es wunderbar.

Christian

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.