Hallo, besteht die Möglichkeit Funktionen in .S-Dateien so zu deklarieren, daß der Linker die Dinger als 'inline' betrachtet? Beispiel: Die Funktionen SPI_Init() und UART_Init() werden nur einmal aufgerufen. Um mir das 'rcall' und 'ret' des AVR-GCC zu sparen hätte ich die gerne als 'inline' deklariert, aber mit dem Vorteil eines noch lesbaren Codes (also nicht direkt als inline-asm): in .h void UART_Init(uint16_t UBRR); in .S .global UART_Init .func UART_Init UART_Init: out _SFR_IO_ADDR(UBRRH), 25 out _SFR_IO_ADDR(UBRRL), 24 ldi 18, (1<<RXEN)|(1<<TXEN) out _SFR_IO_ADDR(UCSRB), 18 // 8 Data, 2 Stop ldi 18, (1<<URSEL)|(1<<USBS)|(3<<UCSZ0) out _SFR_IO_ADDR(UCSRC), 18 ret .endfunc Grüße, Frekazoid
Wenn du den Krempel in C schreibst, macht's der Compiler für dich (eine "static" deklarierte Funktion, die nur einmal gerufen wird, wird inline eingebaut).
Das weiß ich. Ich will aber einige Funktionen nicht in C haben. Sei es aus Timinggründen oder anderen. Wollte halt wissen, ob es für den GAS sowas wie '.inline' gibt.
@Jörg: Oder meinst Du 'nur diese Funktion' in 'C'? Immerhin kommt dann auch nur ein .o raus und die Teile kommen schließlich alle in eine LIB. Wäre vielleicht eine Lösung (der Linker scheint Objekte als Einheit zu sehen und linkt diese komplett. Deswegen die Teilung der Module in einzelne Funktionsobjekte).
Nur der Compiler kann etwas inline einbauen. Der Assembler tut, was sein Name sagt: er reiht die von dir vorgegebenen Befehle aneinander. Eventuell wäre ein Makro ein Ausweg. Alles ziemlich krückig, wenn du mich fragst.
Ich wette ausserdem, daß Du es nicht mit vertretbarem Aufwand (Zeit/Codegewusel) schaffst, dem GCC was vorzumachen. Aus Fairnessgründen gebe ich Dir noch den Hinweis, daß ich diese Wette schon mal gewonnen habe :-)
g ich will dem GCC nix vormachen. Dachte das der Preprozessor sich schon Stücke nehmen kann und da einsetzt. Immerhin werden .S-Dateien von ihm 'begutachtet'. Hab die Funktion jetzt in C realisiert. Man muß halt kompromisse eingehen. Trotzdem danke an alle.
Vielleicht ja auch nicht. Welche Kompromisse gehst Du denn jetzt ein?
Der Preprozessor ist aber nicht der Compiler. Der Preprozessor ist nichts anderes als ein glorifzierter Text-Editor, der seine Kommandos aus dem Text erhaelt den er bearbeitet. Von Assembler oder C versteht der Preprozessor genausoviel wie Lieschen Mueller.
Kompromiss: Die Funktion ist jetzt 'sichtbar' für andere. Wenn der Linker die Sache erledigen könnte, hätte ich 'ne Lib gemacht und nur ein .h-File mit der Deklaration benötigt. Stört bei einer Funktion wie SPI_Init() nicht wirklich, aber für die Zukunft hätte ich gerne die Funktion in einer Lib um eventuell Code weiterzugeben - ohne das jemand den Quellcode kennt. Außerdem funktioniert das wohl nur mit Libs (und einzelnen Objekten pro Funktion), damit der Linker sich Teile rausnimmt welche er benötigt. Sonst wird Code mitgelinkt, welcher nicht benötigt wird.
Aehm
Wenn Du eine Library weitergibst, dann gibst
Du weiter
* die Library
* den/die zugehoerigen Header-Files
Derjenige, dem Du die Library gibst, kriegt Deinen
Source Code nie zu sehen.
> Sonst wird Code mitgelinkt, welcher nicht benötigt wird.
Das halte ich fuer ein Geruecht.
Wenn es in den letzten 20 Jahren einen Linker gegeben
hat, der eine Library ohne Analyse des Einsatzes von
Funktion komplett einbindet, dann ist der Programmierer
dieses Linkers mit einem nassen Fetzen erschlagen worden
und die Firma laengst bankrott.
Das kannst Du genau so wie Du es möchtest auch in C machen! http://www.mikrocontroller.net/articles/Libraries
@Karl: Ist aber so. Die Objekte einer Lib werden vom GNU-Linker als Einheit betrachtet (http://www.mikrocontroller.net/forum/read-2-193465.html). Ist von mir auch verifiziert worden ;-) NUR wenn jede Funktion ein Objekt ist, werden andere nicht beachtet. Zudem ist es so, wenn Dein Programm eine Funktion Test() beinhaltet, wird auch der Code gelinkt - ohne Rücksicht auf dessen Verwendung. Laß Dir mal ein .lss oder .map generieren.
@Patrick: Hatte ich auch erst so. Aber 'inline static' läßt sich bei mir nur komplett im Header implementieren (nicht deklarieren). Aber ich lern ja noch ;-)
Ähm. Nachtrag: @Karl: > Wenn Du eine Library weitergibst, dann gibst > Du weiter > * die Library > * den/die zugehoerigen Header-Files > Derjenige, dem Du die Library gibst, kriegt Deinen > Source Code nie zu sehen. Das ist richtig. allerdings hab ich es bisher nicht gebacken bekommen 'inline static' wie es sich gehört im .h zu deklarieren und in .c zu implementieren. Andere Funktionen gehen ;-)
Das ist das Wesen von 'inline'. Um eine Funktion inline ersetzen zu koennen, muss der Kompiler den Code sehen koennen. So einen Code kannst Du daher nicht in eine Library packen, sondern musst ihn so ausliefern, dass der Compiler ihn auch zu Gesicht bekommt, wenn er die Aufruf- stelle uebersetzt. Ist ja auch logisch, wenn man bedenkt was inline eigentlich macht.
@Karl: Das seh ich ein. Ich hatte nur Patrick so verstanden, daß es möglich ist eine 'inline-Funktion' in eine Lib zu packen. Ich glaub aber auch, daß das wohl nicht geht. Also muß ich mit meinem Kompromiß leben. Ist ja jetzt auch nicht sooo schlimm.
Soweit ich weiss kann der Compiler nur inlinen, was im gleichen Source File (bzw. darin eingebundenen Headerfile) steht. Jede Funktion die in einem anderen .C oder .o File steht kann er nicht inlinen, weil er die Datei beim compilieren ja nicht ansieht. Und der Linker ist sowieso doof, der kann nix (ausser .o files zusammenpackeln und Referenzen auflösen). Aber doch nie Code in ein Object einfügen. Just my 0.02
@Fritz Das ist aber kein grundsaetzliches Problem. Die letzten Microsoft-Linker machen zb. bereits inlining.
Glaubst bringt das was? Er muss dann im Inline ja auf Verdacht alle Register push/popen, weil er ja nicht weiss wie die vorher/nachher verwendet werden. Er wird wohl nicht eine Codeanalyse mit dem Disassembler machen.
Soweit ich weiss, machen die im Linker eine (kleine) Codeanalyse. Was ich nicht weiss ist, welche Restriktionen der Linker anwendet, sprich was er noch inlined und was nicht. Interessant ist das aber trotzdem. Dadurch koennte inlining auch sprachuebergreifend (Assembler - C - C++ - Fortran - was weiss ich noch was) angewendet werden. Auch waere damit inlining aus einer Library heraus moeglich.
Hab jetzt einige Stunden experimentiert. Momentan sieht es wohl so aus, daß diverse Vorteile von C gegenüber Assembler verschwimmen. Der Linker jedenfalls bietet mir momentan nur zwei Möglichkeiten: o ich pack diverse Funktionen in Einzelobjekte und diese in eine Library um ein 'cleveres' Linking zu erreichen. - Haufenweise Dateien für Module - Lib ist nur für einen AVR kompiliert (IO-Bereiche, RAM, Flash) und damit ziemlich eingeschränkt. + Linker wählt nur Funktionen die auch gebraucht werden. o Das komplette Projekt wird für den aktuellen AVR kompiliert/gelinkt. + Flexibel gegenüber AVR-Wechsel - Kompletter Code ist im Binary - Änderungen in 'globalen' Modulen (Log, UART) müssen in anderen Projekten eingepflegt werden. Ich hab mal mit den Optionen -ffunction-sections und --gc-sections experimientiert -> nix. Laut Google hat aber Jörg Wunsch die Linkerskripte so geändert, daß die Optionen funktionieren. Wenn man dem Linker das 'selektive Verhalten' ohne den Umweg über die Libs angewöhnen könnte, wäre ich ja schon zufrieden. Das Einpflegen der aktuellsten Moduldateien in die Projekte würde ich dann das CVS-System machen lassen ;-) Oder gibt es einen Tip eine Library, die z.B. auf IOs zugreift (die AVR-spezifisch sind) flexibel zu gestalten? Also Bitte an Jörg Wunsch (der ja auch relativ oft hier schreibt): Erhöre mich ;-) Du scheinst ja den gleichen Wunsch gehabt zu haben ... Grüße, Freakazoid
> Laut Google hat aber Jörg Wunsch die Linkerskripte so geändert, daß > die Optionen funktionieren. Dann weiß Google hier mehr als ich. ;-) Ich bin mir im Moment gerade nicht sicher, woran es liegt, dass -ffunction-sections + --gc-sections nicht funktioniert. Ich finde persönlich diese Methode eher unsauber, da mein Standpunkt ist, dass ein Entwickler wissen sollte, welche Funktionen voneinander unabhängig sind und daher in eigene Quelldateien (und damit in eigene Objektmodule) gehen können und welche nicht. Ich halte das für sauberer, als hinterher die Faulheit des Programmierers (die sich dann auch in aller Regel in nicht mehr wirklich überschautem Code äußert) durch irgendeine Heuristik des Linkers ausbügeln zu lassen. Ein wenig haben wir uns jetzt vom ursprünglichen Subject wegbewegt. Klar könnte der Linker wahrscheinlich auch noch derartige Funktionen inline auflösen, aber ich frage mich, ob die wenigen gesparten CALL und RET den Aufwand wirklich wert sind. Prinzipiell kannst du dir auch die .initN-sections mal angucken, wobei bei denen natürlich ganz besondere Vorsicht walten muss, wenn man --gc-sections benutzt -- sonst ist die komplette Initialisierung flöten gegangen. So 100%ig klar ist mir dein Anspruch allerdings noch nicht, das gebe ich zu. Ich habe bislang noch keinen Code gesehen, den man nicht für einen anderen Prozessor nicht ohnehin anpassen müsste, es sei denn, es handelt sich um reine Arithmetik (also keinerlei IO-Zugriffe). Daher muss man für den IO-Bereich ohnehin entweder den Endkunden compilieren lassen oder eine Bibliothek pro unterstütztem Prozessor mitgeben. Für reine Arithmetik kannst du ja so arbeiten, wie's die avr-libc selbst macht: pro grundlegegender AVR-Architektur (derzeit also avr2 ... avr5) baust du eine Bibliothek. Vielleicht beschreibst du ja mal bisschen detaillierter, was du wirklich genau vorhast.
Hallo, erstmal danke für Deine Antwort. Ich hab die Aussage von 2003 auch nur grob überflogen. Vielleicht hast Du ja auch einen 'Doppelgänger' ;-) Ich finde nicht, daß es als 'Faulheit' eines Programmierers zu werten ist, wenn er aus Modulsicht zusammengehörige Funktionen in eine Datei packt. Es wird schnell unübersichtlich, wenn man jede Funktion einzeln verpacken muß. Abgesehen davon versteh ich das Verhalten des Linkers nicht einzelne Objekte NUR aus Libraries auszuwählen, aber aus einem Gesamtprojekt nicht. Es muß also nicht nur jede Funktion einzeln in Dateien stehen, sonmdern diese auch noch in Bibliotheken. Das ist ein heiden Aufwand. Es sollte für einen Linker ein Leichtes sein 'unused code' oder Sektions zu entfernen. Bei manchen Architekturen kann er das ja auch. Generell will ich auch keinen Anspruch erheben ;-) Das mit den verschiedenen Prozessorarchitekturen und der Unmöglichkeit diese in Bibliotheken abzubilden ist verständlich. Allerdings wirft der Linker genau da mein Problem auf: Wird der Code generell zusammen übersetzt (also ohne Libs), packt der Linker ALLES zusammen. Auch unbenutzten Code. Da hilft es auch NICHT, wenn man für jede Funktion eine eigene Datei hat. Das schafft er nur bei Bibliotheken (siehe vorheriger Absatz). BTW: Es kann natürlich auch sein, daß ich mich 'vertestet' habe, aber Google deckt sich mit meinem Ergebnis. Ein einfaches 'void Test() { PIND = 1}' in einer Einzeldatei taucht immer im map und in der lss auf. Da hilft auch kein Strippen von Symbolen ;-)
Ja, auf der Kommandozeile angegebene Objekte werden immer gelinkt. Der Programmierer wird sich ja was dabei gedacht haben, dass er sie angegeben hat. So groß ist der Aufwand für Bibliotheken auch wieder nicht, zumal Makefiles traditionell Möglichkeiten bieten, Bibliotheksmodule einzeln zu aktualisieren. Braucht vermutlich heute kaum noch jemand, mit derzeitigen Rechnern ist eine Bibliothek auch from scratch ganz fix neu zusammengezimmert. Ansonsten: warum nicht zur Sortierung alles was zusammengehört in ein Verzeichnis, und in diesem dann eine Datei pro Funktion? Ich finde das durchaus auch aufgeräumt. Vielleicht bin ich ja einfach nur altmodisch. :-)
Hm. 'Altmodisch' ist meistens besser ;-) Vielleicht liegt es auch am AVR-Studio und meiner Makefile-Unkenntnis und dem darauf resultierendem blinden Vertrauen ;-) Kleine Frage: Deine Methode würde beim Linken (für eine neue Prozessorarchitektur) eines Projektes erst die Bibliotheken aktualisieren (eventuell sogar mit ArchitekturSuffix) und dann den Rest kompilieren und gegen diese Libs linken? Da braucht man wohl tiefgehendes Know-How, oder? Denke mal mit AVRStudio steh ich da auf dem Schlauch.
Naja, mit dem AVR Studio allein wird das nicht gehen, auch Mfile ist nicht mehr dafür gedacht. Beide bieten ja eher eine vereinfachte Sichtweise, die für kleinere Projekte die Makefile-Gebilde vor dem Benutzer verstecken sollen. Ein derartiges Build-System müsste man schon irgendwie designen. Vielleicht mehrere build-Verzeichnisse für die einzelnen Unterstützten Prozessoren, in die hinein dann mit unterschiedlichen -mmcu-Optionen die gleichen Quellen hinein compiliert werden (ggf. noch mit verschiedenen Namen der Zielbibliotheken). Kann man mit der Hand aufsetzen oder mit irgendwelchen Tools wie autoconf/automake, Cmake, scons etc. pp. Ehrlich gesagt, ich kann mir nicht vorstellen, wie man mit einem derart primitiven Editor, wie er in AVR Studio drin ist, ein größeres Projekt auch nur ansatzweise bewältigen will. Der kann ja nichtmal richtige reguläre Ausdrücke (alles mit ^$ etc. endet darin, dass er den Suchbegriff nicht findet). Ist sicher Geschmackssache, Windowsnutzer scheinen mir ja eher zu Masochismus zu neigen, was vernünftige Prozeduren zum Suchen und Ersetzen angeht. ;-)
Hehe. Bitte nicht unter die Gürtellinie ;-) Werde mal Beizeiten tiefer in die Makefiles einsteigen. Momentan werde ich dann doch erstmal den Linker so hinnehmen. Dauert wohl auch noch ein Weilchen bis ich an die Grenzen stoße. Bis dahin gibt es bestimmt auch andere Werkzeuge.
Den Masochismus gibt es nicht - findet man vor lauter Installierten Tools oder heruntergeladenen Dateien nicht mehr auf Anhieb das, was man sucht, installiert man einfach Windows neu ;-) SCNR
So als Idee: Probier mal die Compileroption -funit-at-a-time ob die was bringt.
-funit-at-a-time hat (zumindest, wenn ich die Doku richtig lese) nur Sinn, wenn man alle C-Quelldateien gleichzeitig auf der Kommandozeile angibt.
> Es sollte für einen Linker ein Leichtes sein 'unused code' oder > Sektions zu entfernen. In C++ ist das zum Teil ganz schoen knifflig. Stichwort: virtuelle Funktionen Da gibt es (normalerweise) keinen einzigen direkten Aufruf der Funktion und trotzdem wird sie zur Laufzeit (indirekt) aufgerufen. Wenn man nun nur einen gemeinsamen Linker für mehrere Compiler benutzen will (und das will man normalerweise), dann sitzt man in der Bredulie.
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.