Hab grad den relativ neuen avr-gcc 7.1.0 auf einem Arch-System
installiert.
Wollte ein gut abgehangenes ATmega-Projekt kompilieren.
Das Projekt hab ich damals zu gcc 4.X-Zeiten gestartet, aber
zwischenzeitlich auch mit gcc 5.X und 6.X kompiliert.
Machte bisher nie Probleme.
Jetzt bekomme ich Linkerfehler (undefined reference).
Tritt immer bei inline-Funktionen in Headern auf.
Hab die Sourcen abgespeckt.
Hier ein Minimalbeispiel was unter gcc 7.1.X nicht baut:
main.c
(ja, ich bau hier mit dem nativen gcc, der verhält sich aber genauso wie
der avr-gcc)
Das inline war damals nötig damit ich die Funktionen in den Header
packen konnte. Sind hauptsächlich kleine Setter und Getter und konnten
dadurch weitestgehend wegoptimiert werden.
Hat sich hier etwas geändert wie gcc mit inline-Funktionen umgeht?
Inliner schrieb:> Hat sich hier etwas geändert wie gcc mit inline-Funktionen umgeht?
"GCC implements three different semantics of declaring a function
inline. One is available with -std=gnu89 or -fgnu89-inline or when
gnu_inline attribute is present on all inline declarations, another when
-std=c99, -std=c11, -std=gnu99 or -std=gnu11 (without -fgnu89-inline),
and the third is used when compiling C++."
https://gcc.gnu.org/onlinedocs/gcc-7.2.0/gcc/Inline.html> header.h> inline void doSomething(void)
Schon seit langer Zeit gilt, dass das "static inline" sein sollte.
Ab GCC 5 ist GNU-C11 der voreingestellte Standard, davor war's GNU-C89.
https://gcc.gnu.org/gcc-5/changes.html
Bevor du eine neue Version hernimmst sollten auf jeden Fall aller
zwischenzeitlichen Release-Notes begutachtet werden, insbesondere deren
Caveats sowie Änderungen an der jeweiligen Sprache und Target.
Bevor "inline" ab C99 in den C-Standard übernommen wurde, wurde es
bereits in GNU-C unterstützt — allerding mit anderer Semantik wie das
C99 inline. Das alte inline gibt's weiterhin als __gnu_inline__, siege
GCC-Doku:
http://gcc.gnu.org/onlinedocs/gcc-7.1.0/gcc/Common-Function-Attributes.html#index-gnu_005finline-function-attribute
Merke: Wenn allen andere versagt, lies die Doku !-)
Falls eine Funktion nicht geinlinet wird, wird eine externe Referenz
erzeugt, und wenn keine entsprechende Funktion implementiert ist, gibt's
natürlich einen Linkerfehler.
Wenn du auch bei -O0 auf jeden Fall inlinen willst, dann per
Johann L. schrieb:> Wenn du auch bei -O0 auf jeden Fall inlinen willst, dann per> static inline __attribute__((_always_inline_)) func ...> Falls das nicht geinlinet werden kann (z.B. weil die Adresse der> Funktion genommen wird), kann man mit -Winline eine Warnung erzeugen> lassen.
Wird da nicht (unabhängig von den Warnungseinstellungen) immer mit
Fehler abgebrochen, wenn inlining nicht möglich ist?
Inliner schrieb:> Das inline war damals nötig damit ich die Funktionen in den Header> packen konnte. Sind hauptsächlich kleine Setter und Getter und konnten> dadurch weitestgehend wegoptimiert werden.
Mit -O0 wird gar nichts optimiert, also auch nicht geinlined.
Nur mit der Kneifzange __attribute__((_always_inline_)) kannst Du dann
Inlining erzwingen.
Ins H-File sollten nur solche Sachen, die auch von anderen C-Files
benötigt werden.
Ein Inlinefunktion, die nur lokal benötigt wird, schreibt man besser ins
C-File.
Merkwürdig ist allergings, daß der GCC bei der Definition erst auf
Inlining plädiert und sich dann später beim Aufruf für den Call
umentscheidet.
"inline" ist ja nur eine Empfehlung an den Compiler, der er nicht folgen
muß. Aber egal, wie er sich entscheidet, bei mir gab es bisher nie
Probleme. Ich benutze allerdings ausnahmslos den Optimierungslevel -Os.
Peter D. schrieb:> Merkwürdig ist allergings, daß der GCC bei der Definition erst auf> Inlining plädiert und sich dann später beim Aufruf für den Call> umentscheidet.
"Inline" heißt, dass er Inlining machen darf, aber nicht muss.
Kein "static" heißt, dass es irgendwo eine Nicht-Inline-Version gibt,
die er stattdessen aufrufen darf.
("Static" heißt, dass er die Nicht-Inline-Version selber erzeugen muss,
oder dann doch gleich inline macht, weil er weiß, dass es keine anderen
Aufrufe geben kann, und es dann doch Platz spart.)
Clemens L. schrieb:> Peter D. schrieb:>> Merkwürdig ist allergings, daß der GCC bei der Definition erst auf>> Inlining plädiert und sich dann später beim Aufruf für den Call>> umentscheidet.>> "Inline" heißt, dass er Inlining machen darf, aber nicht muss.
Auch ohne "inline" darf er inlinen.
Rolf M. schrieb:> Johann L. schrieb:>> Wenn du auch bei -O0 auf jeden Fall inlinen willst, dann per>> static inline __attribute__((_always_inline_)) func ...>> Falls das nicht geinlinet werden kann (z.B. weil die Adresse der>> Funktion genommen wird), kann man mit -Winline eine Warnung erzeugen>> lassen.>> Wird da nicht (unabhängig von den Warnungseinstellungen) immer mit> Fehler abgebrochen, wenn inlining nicht möglich ist?
Soweit ich weiß nein. Siehe z.B. "extern inline", wo ein Abbruch
kontraproduktiv wäre. AUßerdem ist "inline" nur eine höfliche Bitte an
den Compiler.
Ich hatte damals als ich den Code erstellt hab viel rumprobiert und
quergelesen.
Mein Fazit war:
Da der Compiler auch inlined wenn die Funktion nicht per "inline"
gekennzeichnet wurde und umgekehrt nicht inlinen muss, selbst wenn die
Funktion als "inline" deklariert ist ist inline grundsätzlich erst mal
nur Deko.
Ein nacktes "inline" ohne weitere Qualifier ist für den gcc erstmal
irrelevant bei Entscheidung ob er die Funktion inlined.
Allerdings: durch das "inline" konnte ich die Funktion in den Header
packen ohne dass mir der Linker später die "multiple definitions" um die
Ohren knallt.
Und die Funktion in den Header zu packen ist Grundvorrausetzug um den
Compiler möglichst gute Optimierung zu ermöglichen.
(von LTO mal abgesehen).
Deswegen hab ich das so umgesetzt.
Ich hab damals glaub ich auch mit anderen Qualifiern probiert.
Ergebnis waren Linkerfehler. Nämlich dann wenn gcc entschied nicht zu
inlinen, es aber keine non-inline Definition gab.
Inliner schrieb:> Ich hatte damals als ich den Code erstellt hab viel rumprobiert und> quergelesen.>> Mein Fazit war:> Da der Compiler auch inlined wenn die Funktion nicht per "inline"> gekennzeichnet wurde und umgekehrt nicht inlinen muss, selbst wenn die> Funktion als "inline" deklariert ist ist inline grundsätzlich erst mal> nur Deko.> Ein nacktes "inline" ohne weitere Qualifier ist für den gcc erstmal> irrelevant bei Entscheidung ob er die Funktion inlined.
Nein, es wird schon berücksichtigt. Allerdings gehen auch Kosten ein,
Werte von --param und anderen Optionen, insbesondere Optimierungsgrad.
> Allerdings: durch das "inline" konnte ich die Funktion in den Header> packen ohne dass mir der Linker später die "multiple definitions" um die> Ohren knallt.
Das Problem war dann nicht das "inline", sondern die Linkage der
Funktionen: Wenn das inline fehlt und in jedem Modul eine Funktion
gleichen Namens implementiert wird, dann meckert natürlich auch der
Linker. Lösung wäre "static" gewesen, d.h. nicht external Linkage zu
verwenden. Die Semantik von inline ist zudem etwas vertrackt, vor allem
auch vor dem Hintergrund von GNU inline.
inline sollte aber für Optimierungszwecke dienen, nicht um irgendwelche
Linkerfehler zu Umschiffen. Entspechend ergibt sich die Linkage aus dem
Design des Projekts, wobei dort auch Optimierungsaspekte eine Rolle
spielen können.
> Ich hab damals glaub ich auch mit anderen Qualifiern probiert.> Ergebnis waren Linkerfehler. Nämlich dann wenn gcc entschied nicht zu> inlinen, es aber keine non-inline Definition gab.
Das ist die Semantik von extern inline: Falls nicht geinlint wird (z.B.
aufgrund der Kosten bei O0), geht der Compiler davon aus, dass eine
Implementierung irgendwo existiert, so dass gegen diese
Fallback-Implementation gelinkt werden kann.