Forum: Compiler & IDEs Probleme mit inline bei neuem gcc


von Inliner (Gast)


Lesenswert?

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
1
#include "header.h"
2
3
int main(void)
4
{
5
  doSomething();
6
}

header.h
1
inline void doSomething(void)
2
{
3
4
}

build:
1
gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"main.d" -MT"main.o" -o "main.o" "../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?

von Clemens L. (c_l)


Lesenswert?

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.

von Adib (Gast)


Lesenswert?

... Probiers mal mit
 static inline

Nur so n verdacht.

Grüße, Adib.

von Inliner (Gast)


Lesenswert?

Das wars, baut wieder, Danke.

Gibts da auch eine Erklärung dafür?
Hintergrund?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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.

von Clemens L. (c_l)


Lesenswert?

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

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

inline ja, aber nicht __attribute__((always_inline)).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Selbst mit always_inline gibt es nicht immer Warnungen:
1
static inline __attribute((always_inline))
2
void fun (void) {}
3
4
void (*f)(void) = fun;

von Inliner (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

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.