Hallo, wenn ich in einer Funktion den direkten Zugriff auf ein Array durch eine static inline Funktion ersetze, so erhöht sich aus welchen Gründen auch immer der benötigte Speicherplatz. #define pixel_set_always_safe 0 static _inline_ void pixel_set(u08 posx, u08 posy, u08 color) { #if (pixel_set_always_safe == 1) if ((posx < screenx) && (posy < screeny)) { gdata[posy][posx] = color; } #else gdata[posy][posx] = color; #endif } void move_line_up (u08 x) { //Zeile um 1 Pixel nach oben u08 y; for (y = 0;y < (screeny-1);y++) { pixel_set(x,y,gdata[y+1][x]); //gdata[y][x] = gdata[y+1][x]; if (y == (screeny-2)) { pixel_set(x,y+1,linebuff[x]); //gdata[y+1][x] = linebuff[x]; } } } Direkt dach Eintritt in die for-Schleife ist es für die Code Größe egal, ob ich pixel_set oder direkt gdata verwende. Innerhalb der if-Verzeweigung kostet das Verwenden von pixel_set Anstelle von gdata jedoch 12 Byte Code. Den generierten Assembler Code von beiden Varianten habe ich mal als Datei angehängt. Was mus ich verändern, sodass der generierte Code bei Verwendung der Inline-Funktion genauso klein bleibt? Schon mal vielen Dank im Voraus. Vielleicht noch nützliche Informationen: typedef uint8_t u08; #define screenx 16 #define screeny 16 u08 volatile gdata[screeny][screenx]; avr-gcc Version: 3.4.1 Compiler Optimierungen: OPT = -O2 -ffast-math -fweb -frename-registers -Winline -mtiny-stack
In meinen Augen heißt inline, dass der Compiler die so deklarierte Funktion überall wo sie aufgerufen wird, komplett einfügt. Es wird kein Funktionsblock draus, der bei jedem Aufruf angesprungen wird. Somit wird jeder Aufruf dieser Funktion deinen Code weiter vergrößern, was ja logisch ist. Alex
Alex: Du hast zwar Recht, aber das meint Malte nicht. Die inline-Funktion besteht nur aus einer einzigen Zeile. Ob man die nun manuell per Copy&Paste in den Code schreibt oder das dem Compiler (über inline) erledigen lässt, sollte keine Rolle spielen. Malte: Es wäre u.U. hilfreich, ein Listing zu posten, bei dem abwechselnd C-Code/Assembler-Code enthalten ist, damit man die Zeilen leichter zuordnen kann. Sollte eigentlich nichts ausmachen, aber lass mal das static vor der Funktion weg. Nimm lieber einen unnamed namespace, wenn du Namenskollisionen vermeiden willst/musst. Ich möchte hier keine Mutmaßungen anstellen, aber evtl. ist das ein Bug des gcc. Der 3.4.x hat nämlich mit dem AVR-target noch ein paar Probleme. Siehe z.B. auch: http://lists.gnu.org/archive/html/avr-libc-dev/2004-11/msg00086.html Dort generiert der 3.4.3er gigantischen Code für eine simple if-Abfrage. Allerdings würde ich wirklich sicher gehen, dass es ein Bug ist, bevor du in die gcc-Bugdatenbank postet. Z.B. Gegenüberstellung des generierten Codes von gcc 3.4.x <-> 3.3.x, wie in dem Link oben.
Bezüglich der Codegröße und möglichen Bugs: Mir ist aufgefallen, dass ein und das selbe Projekt mit unerschiedlichen ausgewählten MCUs erhebliche Code-Größenschwankungen unterliegt. in Byte ohne -mint8; mit -mint8 ATMEGA16/32 : 7326 7332 ATMEGA8 : 6834 6830 AT90S8535 : 7182 7138 Eigentlich müsste der Code mit mint8 jedesmal kleiner oder gleichgroß sein. Dass der Code für den MEGA8 kleiner ist als für den 90S8535 lässt sich wohl mit dem erweitertem Befehlssatz (insbesondere movw) erklären. Aber warum der Code für den MEGA32 so groß ist verstehe ich nicht (die unterschiedlich großen Interrupt Tabelle können nich so viel ausmachen). Die Assembler Ausgabe kann ich bei Gelegenheit mal mit dem C-Code versehen (aber nicht mehr heute). Wie ich mit verschiedenen Versionen von avr-gcc gleichzeitig arbeite weiß ich leider nicht genau, da ich avr-gcc einfach per apt-get (Debian testing) installiert habe. Vielleicht hilf ja, dass avr-gcc -dumpversion 3.4.1 ausgibt und gcc -dumpversion 3.3.4
PS: wenn ich das static weg nehme, will avr-gcc nicht Compilieren. graphicfunctions.h:22: multiple definition of `pixel_set' lautet die Fehlermeldung. Die kann aber auch daran liegen, dass die Header Datei in mehreren .c Dateien meines Projekts verwendet wird.
> PS: wenn ich das static weg nehme, will avr-gcc nicht Compilieren. > graphicfunctions.h:22: multiple definition of `pixel_set' > lautet die Fehlermeldung. Ah ja. Ich sehe gerade, du hast inline geschrieben. Probier mal bitte nur inline, ohne die Unterstriche. Eigentlich sollte inline genau diese Fehlermeldung beheben (das inline-Schlüsselwort macht nämlich mehr als nur die Funktion zu inlinen).
Coedgrößenschwankungen: AT90S8515 ist noch der alte CPU-Kern, der konnte eine ganze Menge an Befehlen nicht. Daraus folgt, daß der Code größer wird. ATmega8 hat einen neuen Kern und kommt komplett mit RJMP/RCALL aus, da diese exakt 8 KB ROM adressieren können. Das ergibt den kleinsten Code in Deinem Vergleich. ATmega16+ hat zwar auch einen neuen Kern, aber durch den ROM > 8 KB lassen müssen absolute JMP/CALL Befehle genommen werden, um alles zu erreichen, daher wird der Code größer. (Eine Schachtelung, um möglichst weitgehend mit RJMP/RCALL arbeiten zu können, unterstützt der GCC bislang nicht.)
@Jörg Wunsch: Danke für die Erklärumg. Vielleicht wäre das ja noch eine Ergänzung für die FAQ der avr-libc Doku. :-) @Chris: Von der Fehlermeldung her mach es keinen Unterschied ob ich _inline_ oder nur inline schreibe. static inline void pixel_set(...: geht statich _inline_ void pixel_set(...: geht inline void pixel_set(...: Fehler wie oben beschrieben _inline_ void pixel_set(...: Fehler wie oben beschrieben void pixel_set( : Fehler wie oben beschrieben Wie schon gesagt, ich vermute das liegt daran, dass die Funktion in einer Header Datei steht, die von mehreren .c Dateien eingebunden wird. Ich habe die komplette Compiler Meldung mal als Anhang hinzugefügt, falls Interesse besteht.
Ja, die Definition einer Funktion darf nicht in einer Headerdatei stehen, daran ändert auch _inline_ nichts. Der Compiler generiert nämlich dennoch eine Kopie der Funktion außerhalb allen inline-Codes, da in C _inline_ ja nur ein Hinweis und keine zwingende Forderung ist, so daß andere Teile eben diese global sichtbare Funktion gebrauchen könnten. Wenn Du nun die Funktion derart in mehreren Quelldateien generiert hast, bekommst Du genau diesen Fehler. static _inline_ ist die korrekte Verfahrensweise: der Compiler weiß damit, daß die Funktion nur innerhalb dieses Übersetzungsmoduls benötigt wird, so daß er entweder eine lokale nicht inline aufgelöste Kopie der Funktion generieren kann oder aber tatsächlich allen Code inline auflösen konnte und auf die globale Funktion gänzlich verzichten darf.
So, ich habe den Code jetzt mal mit und ohne Verwendung von Inline in einer html Tabelle gegenübergestellt (siehe Anhang). Bezüglich der Gegenüberstellung des mit verschiedenen Compilerversionen generierten Codes: Leider habe ich keine Ahnung wie ich eine andere avr-gcc Version installiere (Debian-Linux). Ich finde außerdem nur die Möglichkeiten zum Download von gcc, nich aber avr-gcc. Wenn ich versuche mit gcc zu compilieren kennt dieser den -mmcu Parameter nicht.
@Jörg: > Ja, die Definition einer Funktion darf nicht in einer > Headerdatei stehen, daran ändert auch _inline_ nichts. Ich widerspreche dir nur ungern, aber in C++ ist inline mehr als ein Vorschlag an den Compiler, die Funktion zu inlinen. Aus dem C++-Standard (3.2.3, Auszug): "An inline function shall be defined in every translation unit in which it is used." (7.1.2 sagt das gleiche, fügt aber noch die Bedinung hinzu, dass alle Definitionen exakt gleich sein müssen). Das sagt ziemlich eindeutig, dass eine inline-Funktion in einer .h-Datei definiert (und nicht nur deklariert) werden sollte. Ich kenne mich mit C nicht so gut aus, womöglich ist es dort doch anders als in C++. @Malte: Lad dir den normalen gcc-Quellcode runter, falls du es nicht schon hast. Bei configure kannst du dann als target avr angeben, siehe: http://www.nongnu.org/avr-libc/user-manual/install_tools.html#install_avr_gcc
> Ich widerspreche dir nur ungern, aber in C++ ist inline mehr als ein > Vorschlag an den Compiler, die Funktion zu inlinen. In C schon. Einer der Unterschiede zwischen C und C++. In der Tat überlegt sich der GCC in C mehr als einmal, ob er Deiner Idee von inline wirklich folgen wird. (Es gibt noch irgendein GCC _attribute_, mit dem man das Inline erzwingen kann, aber den Namen habe ich vergessen.)
> Einer der Unterschiede zwischen C und C++.
Dann hast du natürlich Recht.
Diese kleinen aber feinen Unterschiede zwischen C und C++ sind mir
leider noch nicht alle bekannt.
@Chris: Die Anleitung war genau das was ich suchte, danke. Allerdings bricht make nach einer ganzen Weile des Compilierens mit einigen Fehlern ab (siehe unten). Davor gabs auch schon eine Menge Warnungen. Versucht hab ich es mit gcc 3.3.4. Auf meinem System war bereits avr-libc (1.0.4) und binutils (2.15) installiert. So langsam wird mir das alles etwas zu kompliziert. :-( ../../gcc/config/avr/libgcc.S: Assembler messages: ../../gcc/config/avr/libgcc.S:72: Error: suffix or operands invalid for `clr' ../../gcc/config/avr/libgcc.S:72: Error: no such instruction: `clear result' ../../gcc/config/avr/libgcc.S:74: Error: no such instruction: `sbrc r24,0' ../../gcc/config/avr/libgcc.S:75: Error: too many memory references for `add' ../../gcc/config/avr/libgcc.S:76: Error: too many memory references for `add' ../../gcc/config/avr/libgcc.S:76: Error: no such instruction: `shift multiplicand' ../../gcc/config/avr/libgcc.S:77: Error: no such instruction: `breq __mulqi3_exit' ../../gcc/config/avr/libgcc.S:77: Error: no such instruction: `while multiplicand!=0' ../../gcc/config/avr/libgcc.S:78: Error: no such instruction: `lsr r24' ../../gcc/config/avr/libgcc.S:79: Error: no such instruction: `brne __mulqi3_loop' ../../gcc/config/avr/libgcc.S:79: Error: no such instruction: `exit if multiplier=0' ../../gcc/config/avr/libgcc.S:81: Error: too many memory references for `mov' ../../gcc/config/avr/libgcc.S:81: Error: no such instruction: `result to return register' make[2]: *** [libgcc/./_mulqi3.o] Fehler 1 make[2]: Leaving directory `/home/malte/unzip/gcc/gcc-3.3.4/obj-avr/gcc' make: *** [all-gcc] Fehler 2
Hast du die binutils genau so installiert, wie in der Anleitung beschrieben? Bei mir kamen diese Fehler mal, als ich einfach "emerge binutils" (gentoo) gemacht hab.
Yep, sieht sehr danach aus, als würde sich der avr-gcc die binutils der Hostmaschine nehmen, also `as' statt `avr-as' usw.
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.