Forum: Compiler & IDEs Assemblerfunktionen in .S 'inlinen'


von André K. (freakazoid)


Lesenswert?

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

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


Lesenswert?

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).

von André K. (freakazoid)


Lesenswert?

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.

von André K. (freakazoid)


Lesenswert?

@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).

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


Lesenswert?

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.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

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 :-)

von André K. (freakazoid)


Lesenswert?

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.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Vielleicht ja auch nicht.
Welche Kompromisse gehst Du denn jetzt ein?

von Karl H. (kbuchegg)


Lesenswert?

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.

von André K. (freakazoid)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Das kannst Du genau so wie Du es möchtest auch in C machen!

http://www.mikrocontroller.net/articles/Libraries

von André K. (freakazoid)


Lesenswert?

@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.

von André K. (freakazoid)


Lesenswert?

@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 ;-)

von André K. (freakazoid)


Lesenswert?

Ä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 ;-)

von Karl H. (kbuchegg)


Lesenswert?

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.

von André K. (freakazoid)


Lesenswert?

@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.

von Fritz G. (fritzg)


Lesenswert?

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 €

von Karl H. (kbuchegg)


Lesenswert?

@Fritz

Das ist aber kein grundsaetzliches Problem.
Die letzten Microsoft-Linker machen zb. bereits
inlining.

von Fritz G. (fritzg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von André K. (freakazoid)


Lesenswert?

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

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


Lesenswert?

> 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.

von André K. (freakazoid)


Lesenswert?

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 ;-)

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


Lesenswert?

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. :-)

von André K. (freakazoid)


Lesenswert?

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.

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


Lesenswert?

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. ;-)

von André K. (freakazoid)


Lesenswert?

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.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

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

von Fritz G. (fritzg)


Lesenswert?

So als Idee:
Probier mal die Compileroption -funit-at-a-time ob die was bringt.

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


Lesenswert?

-funit-at-a-time hat (zumindest, wenn ich die Doku richtig
lese) nur Sinn, wenn man alle C-Quelldateien gleichzeitig
auf der Kommandozeile angibt.

von Karl H. (kbuchegg)


Lesenswert?

> 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
Noch kein Account? Hier anmelden.