Für ne Berechnung auf nem ATtiny26 benötige ich ne große Dynamik, aber float paßt ja nicht rein. Da dachte ich, nimmste einfach long long (64Bit), dürfte ja nur doppelt soviel Code sein, wie long (32Bit). Aber Pustekuchen, das ist total verschwenderisch programmiert (noch viel teurer als float). Ne Division kostet etwa 5kB !!! Also Finger weg von long long, wenns unter nem ATmega16 ist ! Nun wollte ich mir ne Divisionsfunktion in Assembler schreiben (35 Words = 70Byte), also 2 Parameter übergeben (r25..r18, r17..10 -> r25..18). Aber wieder Pustekuchen, die Parameter werden gepusht, dann mein Assembler ausgeführt und dann die Parameter gepopt, also meine Berechnung weggeschmissen, schöne Scheiße. Obendrein wird noch gemeckert, daß kein Returnwert da ist. Ich bin mit meinem Latein am Ende :-( Hat überhaupt schonmal jemand erfolgreich Assembler mit Parameter und Returnwert in ne Funktion integrieren können ? Peter
Hallo, Peter, bei einem anderen Prozessor (Toshiba TLCS-900-Umgebung) habe ich es so gemacht: ich habe einen Funktionsrumpf in C geschrieben (gleiche Parameter und Rückgabe wie bei meiner späteren Assembler-Funktion, minimale Funktionalität in der Funktion), habe mir dann den Assembler-Code angeschaut, den der Compiler daraus macht (quasi abkopiert), und dann meine Funktionalität in die Assembler-Funktion eingebaut. Bei den AVR's habe ich keine Erfahrung mit Assembler, aber es könnte hier ja genauso gehen? Günter
Peter Dannegger wrote: > Ne Division kostet etwa 5kB !!! Das ist mehr oder weniger ein known issue. Da werden nur die generischen (in C geschriebenen) Funktionen der libgcc.a benutzt, für 64 bit gibt's keine handoptimierten Varianten. Du kannst ja gern mithelfen, in Assembler kennst du dich aus, warum schlägst du nicht mal einen Patch für die libgcc.a vor? Einziger Wermutstropfen: damit er von GNU akzeptiert wird, musst du deren Lizenzabkommen unterschreiben (Abtretung der Rechte aus dem Copyright an die FSF), ohne das akzeptieren sie keine Patches für den GCC, die mehr als `trivial patches' sind. (Bitte keine weitere Baustelle wie bei floating point schaffen, bei der die avr-libc die Funktionen aus der libgcc.a ersetzt. Das ist Sch***e und führt dazu, dass entsprechende Bugreports bei GCC nicht ernst genommen werden, auch dann, wenn sie eigentlich echte Bugs sind.) > Aber wieder Pustekuchen, die Parameter werden gepusht, dann mein > Assembler ausgeführt und dann die Parameter gepopt, also meine > Berechnung weggeschmissen, schöne Scheiße. > Obendrein wird noch gemeckert, daß kein Returnwert da ist. Dann hast du wohl irgendwas flasch deklariert, die Warnung passt ja wohl gut zum Verhalten des Compilers, den Rückkehrwert dann auch zu ignorieren. > Hat überhaupt schonmal jemand erfolgreich Assembler mit Parameter > und Returnwert in ne Funktion integrieren können ? Na klar, macht die avr-libc haufenweise: % find ~/src/avr-libc/lib* -name \*.c | wc -l 61 % find ~/src/avr-libc/lib* -name \*.S | wc -l 126 Doppelt so viele Assemblerquellen wie in C geschriebene. Ich mag jetzt nicht zählen, wie viele davon Werte zurückgeben, ist mir zu müßig.
1 | #include <stdint.h> |
2 | |
3 | extern uint64_t myfunc(uint64_t, uint64_t); |
4 | |
5 | uint64_t x, y, z; |
6 | |
7 | void
|
8 | dosomething(void) |
9 | {
|
10 | z = myfunc(x, y); |
11 | }
|
Daraus wird generiert:
1 | .global dosomething |
2 | .type dosomething, @function |
3 | dosomething: |
4 | /* prologue: frame size=0 */
|
5 | push r2 |
6 | push r3 |
7 | push r4 |
8 | push r5 |
9 | push r6 |
10 | push r7 |
11 | push r8 |
12 | push r9 |
13 | push r10 |
14 | push r11 |
15 | push r12 |
16 | push r13 |
17 | push r14 |
18 | push r15 |
19 | push r16 |
20 | push r17 |
21 | /* prologue end (size=16) */
|
22 | lds r18,y |
23 | lds r19,y+1 |
24 | lds r20,y+2 |
25 | lds r21,y+3 |
26 | lds r22,y+4 |
27 | lds r23,y+5 |
28 | lds r24,y+6 |
29 | lds r25,y+7 |
30 | lds r2,x |
31 | lds r3,x+1 |
32 | lds r4,x+2 |
33 | lds r5,x+3 |
34 | lds r6,x+4 |
35 | lds r7,x+5 |
36 | lds r8,x+6 |
37 | lds r9,x+7 |
38 | mov r10,r18 |
39 | mov r11,r19 |
40 | mov r12,r20 |
41 | mov r13,r21 |
42 | mov r14,r22 |
43 | mov r15,r23 |
44 | mov r16,r24 |
45 | mov r17,r25 |
46 | mov r18,r2 |
47 | mov r19,r3 |
48 | mov r20,r4 |
49 | mov r21,r5 |
50 | mov r22,r6 |
51 | mov r23,r7 |
52 | mov r24,r8 |
53 | mov r25,r9 |
54 | rcall myfunc |
55 | sts z,r18 |
56 | sts z+1,r19 |
57 | sts z+2,r20 |
58 | sts z+3,r21 |
59 | sts z+4,r22 |
60 | sts z+5,r23 |
61 | sts z+6,r24 |
62 | sts z+7,r25 |
63 | /* epilogue: frame size=0 */
|
64 | pop r17 |
65 | pop r16 |
66 | pop r15 |
67 | pop r14 |
68 | pop r13 |
69 | pop r12 |
70 | pop r11 |
71 | pop r10 |
72 | pop r9 |
73 | pop r8 |
74 | pop r7 |
75 | pop r6 |
76 | pop r5 |
77 | pop r4 |
78 | pop r3 |
79 | pop r2 |
80 | ret
|
81 | /* epilogue end (size=17) */
|
82 | /* function dosomething size 98 (65) */
|
Zugegebenermaßen ist das Umspeichern von x über r2...r9 nach r18...r25 alles andere als optimal, aber das Ergebnis wird ordentlich nach z abgespeichert. Bitte nicht mehr als 2 uint64_t übergeben: da gibt's einen offenen GCC-Bug dafür, für den noch keiner eine richtige Lösung parat hat. Der Fehler scheint im generischen GCC-Teil zu liegen, tritt aber nur bei nicht-mainstream-CPUs wie dem AVR zu Tage, da die anderen ihre eigenen Parameterübergaberoutinen haben. Das führt leider dazu, dass bei GCC daraus eine recht geringe Priorität resultiert. :-(
Mit dem Qualifier naked kann man ja beim GCC für ARM und AVR den Prolog und Epilog von Funktionen unterdrücken. http://www.delorie.com/gnu/docs/gcc/gcc_55.html Hilft dir das vielleicht bei deinem Push/Pop Problem weiter?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.