Grüß euch
Ich darf mich seit längerem mal wieder mit Assembler (Cortex M4)
befassen und kämpfe etwas mit der Syntax und dem ein oder anderen
Stolperstein.
Eins vorweg, folgenden Guide nutze ich bereits:
http://www.ethernut.de/en/documents/arm-inline-asm.html
Soweit so gut. Mein Problem ist folgendes. Ich habe einige Zeilen Code
die DSP Instruktionen nutzen und nach ein wenig herumrechnen 4x 32bit
Werte (p0-p4) ausspucken.
Das funktioniert soweit wunderbar und sieht im Disassembly quasi 1:1 so
aus
1
08002790:ldr.wr9,[pc,#252]
2
08002794:ldr.wr8,[r9]
3
08002798:mov.wlr,#256
4
0800279c:mov.wr10,#2155905152
5
080027a0:ssub8r8,r8,r10
6
080027a4:sxtb16r0,r8
7
080027a8:sxtb16r2,r8,ror#8
8
080027ac:smulbbr1,r0,lr
9
080027b0:smultbr5,r0,lr
10
080027b4:smulbbr3,r2,lr
11
080027b8:smultbr7,r2,lr
12
080027bc:pkhbtr0,r1,r12,lsl#16
13
080027c0:pkhbtr1,r3,r1,lsl#16
14
080027c4:pkhbtr2,r5,r3,lsl#16
15
080027c8:pkhbtr3,r7,r5,lsl#16
16
080027cc:shadd16r4,r0,r1
17
080027d0:shadd16r5,r2,r3
18
080027d4:pkhtbr10,r1,r4,asr#16
19
080027d8:pkhbtr11,r4,r2
20
080027dc:pkhtbr12,r3,r5,asr#16
21
080027e0:pkhbtlr,r5,r3,lsl#16
22
080027e4:movr1,r11
23
080027e6:str.wr8,[r9]
Das Problem ist jetzt, dass man den C-Code aktuell nicht mehr erweitern
kann, da die Register auf denen gerechnet wird hard-coded sind. Sobald
man versucht weitere Assembler Befehle einzufügen spuckt der Compiler
eine Fehlermeldung aus, dass sich der Spaß von den Register-Constraints
her nicht ausgeht... sprich es is kein Platz mehr da zum Herumrechnen.
:)
Jetzt wollt ich die ganzen Register Angaben schlichtweg durch temporäre
Variablen ersetzen, der Compiler soll sich das ganze doch bitte selbst
optimieren.
Leider funktioniert der Code dann nur noch wenn die temporären Variablen
volatile deklariert werden, was den unschönen Nebeneffekt hat dass
erstmal zig loads durchgeführt werden bevor irgendwas interessantes
passiert...
1
08002790:ldrhr1,[r4,#30]// :(
2
08002792:ldrr0,[sp,#28]// :(
3
08002794:ldrr2,[sp,#24]// :(
4
08002796:ldrr3,[sp,#20]// :(
5
08002798:ldrr7,[sp,#16]// :(
6
0800279a:ldr.wlr,[sp,#12]// :(
7
0800279e:ldr.wr12,[sp,#8]// :(
8
080027a2:ldr.wr8,[sp,#4]// :(
9
080027a6:ldrr6,[pc,#232]// :(
10
080027a8:ldrr5,[r6,#0]// :(
11
080027aa:mov.wr9,#2155905152
12
080027ae:mov.wr10,#256
13
080027b2:ssub8r5,r5,r9
14
080027b6:sxtb16r0,r5
15
080027ba:sxtb16r3,r5,ror#8
16
080027be:smulbbr2,r0,r10
17
080027c2:smultbr12,r0,r10
18
080027c6:smulbbr7,r3,r10
19
080027ca:smultbr8,r3,r10
20
080027ce:pkhbtr0,r2,r1,lsl#16
21
080027d2:pkhbtr2,r7,r2,lsl#16
22
080027d6:pkhbtr3,r12,r7,lsl#16
23
080027da:pkhbtr7,r8,r12,lsl#16
24
080027de:shadd16lr,r0,r2
25
080027e2:shadd16r12,r3,r7
26
080027e6:pkhtbr2,r2,lr,asr#16
27
080027ea:pkhbtr3,lr,r3
28
080027ee:pkhtbr12,r7,r12,asr#16
29
080027f2:pkhbtr8,r12,r7,lsl#16
30
080027f6:strr5,[r6,#0]
Meine Recherche hat ergeben, dass man an den sogenannten Constraints
(z.b. "r" vor der Variablen) wohl noch jede Menge drehen kann. Leider
scheinen die DSP Instruktionen aber alle ausschließlich direkt mit
Registern bzw. als "Register" deklarierte Variablen arbeiten zu
wollen...
Was tut man in so einem Fall? Diese ganzen Loads sind komplett
umsonst...
/edit
aux Variablen sind in & output, falsch kopiert...
"volatile" ist oft ein Zeichen, dass du die passenden Modifier nicht
gesetzt hast: https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html
Wenn dir die Register ausgehen, solltest du nur die unwichtigsten Werte
in temporäre Variablen auslagern. (Und dann kannst du auch "m" benutzen
und das "ld" explizit hinschreiben.)
Den passenden Modifier scheint es in meinem Fall nicht zu geben.
Deklariere ich die "aux" Variablen lediglich als Inputs, so passiert
folgendes...
1
080027c8:sxtb16r3,r3
2
080027cc:sxtb16r3,r3,ror#8
3
080027d0:smulbbr3,r3,r1
4
080027d4:smultbr3,r3,r1
5
080027d8:smulbbr3,r3,r1
6
080027dc:smultbr3,r3,r1
7
080027e0:pkhbtr3,r3,r0,lsl#16
8
080027e4:pkhbtr3,r3,r3,lsl#16
9
080027e8:pkhbtr3,r3,r3,lsl#16
10
080027ec:pkhbtr3,r3,r3,lsl#16
11
080027f0:shadd16r3,r3,r3
12
080027f4:shadd16r3,r3,r3
13
080027f8:pkhtbr5,r3,r3,asr#16
14
080027fc:pkhbtr0,r3,r3
15
08002800:pkhtbr1,r3,r3,asr#16
16
08002804:pkhbtr3,r3,r3,lsl#16
Dass da nichts Vernünftiges rauskommt muss ich wohl nicht erwähnen. :D
"volatile" oder auch der "+r" Modifier verhindern das, sorgen aber
wieder für lästige Loads weil der Compiler offensichtlich nicht klug
genug ist um zu erkennen, dass sämtliche "aux" vorher eigentlich nicht
geladen werden müssen...
"m" ist für DSP Instruktionen nicht zulässig und der Compiler schreit
sofort nach Registern.
Deklariere ich sämtliche "aux" als "Output" only (=r), dann kommt auch
nur Blödsinn raus... :/
/edit
Ok...
volatile + (=r) sorgt dafür, dass die Loads am Anfang verschwinden.
Dafür hab ich nun unnötige Stores der ganzen "aux" Variablen. :)
Wenn die DSP-Befehle Register haben wollen, musst du ihnen Register
geben.
Wenn du temporäre Variablen brauchst, solltest du ihre Adresse
übergeben, und st/ld von Hand machen.
Vincent H. schrieb:> Dass da nichts Vernünftiges rauskommt muss ich wohl nicht erwähnen.
Ist üblicherweise ein Symptom vergessener earyl-clobbers "&".
Wenn das asm eh gast alle Register braucht, kann man auch dem Allokator
"helfen" und Register explizit vergeben.
Solche mega-asm sind aber oft einfach nur aua und ein Indiz, so langsam
Assembler einzusetzen — spätestens wenns mehr Register (gleichzeitig)
braucht als die Maschine überhaupt hat...
> Was tut man in so einem Fall?
Du musst alle benutzten Register retten (z. Bsp. auf den Stack), evlt.
dafür sorgen dass kein Interrupt reinspukt, dann die Berechnung mit den
ASM Teil ausführen und abschliessend die Register wieder herstellen.
Versteht sich von selbst, daß ein solcher Asm Teil nicht rekursiv sein
muss. C Compiler
sind i.d.R. aber darauf angewiesen rekursiven Code zu erzeugen weshalb
sie
praktisch nie was großes in Registern rechnen sondern immer auf dem
Stack arbeiten obgleich das viel langsamer sein kann.
>Diese ganzen Loads sind komplett umsonst...
es wäre auch falsch, wenn ein Compiler den in ASM codierten Code
optimieren möchte. Viel mehr als das Einsetzen der passenden
Sprungweiten bzw. das aneinanderreihen der Literale sollte man den
Compiler bzw. Linker nicht machen lassen und im ASM Code hat ein
Compiler gar nix zu suchen.
Warum schreibst du Inline und machst nicht ein vernünftiges Asm File mit
definierter Schnittstelle und Funktionsaufrufen in deinem Projekt ?
Inline ist eher für einzelne ASM Anweisung wie das Abschalten von
Interrupts oder so gedacht wo aber die CMSIS schon was über "Intrinsics"
Funktionen bereit stellt. Es gibt wohl noch viel was die CMSIS nicht
abdeckt und auch die ST-Libs geben nur typische aber keine speziellen
Einstellungen an so daß man durchaus sinnvolles erreichen kann indem man
selbst an den Registern rumfummelt oder gar einen Teil in ASM schreibt.
Johann L. schrieb:> Vincent H. schrieb:>> Dass da nichts Vernünftiges rauskommt muss ich wohl nicht erwähnen.>> Ist üblicherweise ein Symptom vergessener earyl-clobbers "&".>> Wenn das asm eh gast alle Register braucht, kann man auch dem Allokator> "helfen" und Register explizit vergeben.>> Solche mega-asm sind aber oft einfach nur aua und ein Indiz, so langsam> Assembler einzusetzen — spätestens wenns mehr Register (gleichzeitig)> braucht als die Maschine überhaupt hat...
Danke!
Die "extended asm" Seite hat zu den "early-clobbers" hierzu auch ein
gutes Beispiel:
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
Exakt was in dem von mir geposteten Fall passiert ist. Ich hab die "&"
nun drin und kein einziger unnötiger str/load ist mehr vorgekommen.
@"gleich in asm"
Ich hab bereits überlegt das ganze direkt als asm-file abzulegen, jedoch
finde ich sind 16-asm Befehle jetzt nicht unbedingt mega viel. Es
war/ist irgendwie grenzwertig... Da das ganze aber innerhalb einer
Funktion werkeln soll, die blockweise einen Buffer abarbeitet, bietet
sich inline assembler für einen einzelnen Block dann doch irgendwie an.
Das komplett in Assembler zu schreiben wollt ich mir dann doch nicht
unbedingt antun...