Guten Abend,
ich habe eben komischen Linkerfehler behoben und bin dadurch auf etwas
gestoßen.
Der XC32 Compiler (für die PIC32) verlangt, dass bei Inline-Funktionen
die Definition im Header steht, damit er sie inlinen kann.
Siehe Abschnitt 13.10
http://ww1.microchip.com/downloads/en/DeviceDoc/50001686J.pdf
1
Function inlining will only take place when the function’s definition is visible (not just the prototype). In order to have a function inlined into more than one source file, the function definition may be placed into a header file that is included by each of the source files.
By declaring a function inline, you can direct GCC to make calls to that function faster. One way GCC can achieve this is to integrate that function’s code into the code for its callers. This makes execution faster by eliminating the function-call overhead; in addition, if any of the actual argument values are constant, their known values may permit simplifications at compile time so that not all of the inline function’s code needs to be included. The effect on code size is less predictable; object code may be larger or smaller with function inlining, depending on the particular case. You can also direct GCC to try to integrate all “simple enough” functions into their callers with the option -finline-functions.
Dort steht nichts davon, dass der GCC die Definition der Funktion im
Header benötigt. Daraus schließe ich, dass der GCC sich die Definition
aus der Objektdatei holt, in die Aufrufe einfügt und die inline-Funktion
dann nicht linkt (sofern sie an anderer Stelle nicht doch non-inline
aufgerufen wird).
Ich habe das Gefühl, dass der XC32 aus "bequemlichkeit" die Definition
im Header verlangt. Wenn ich allerdings die Definition nur im Header
stehen habe, wird mich das Fehlen in der Source-Datei bestimmt mal
verwirren.
Ich kenne mich in der Compilerwelt nicht sonderlich aus -- ist das
Verhalten so üblich?
Euer Meinungen würden mich sehr interessieren.
Komme gerade hierher, weil bei mir das inline-ing auch nicht
funktioniert (MPLAB X IDE v.5.15, dsPIC33FJ128MC706A). Allerdings
erhalte ich keinen Linkerfehler - das inline-ing wird einfach nicht
gemacht, auch nicht in der Release-Version. Wollte deshalb hier
nachfragen, ob jemand weiß, wie man das erzwingen kann. Das Verschieben
der Funktionsdefinition in das Headerfile funktioniert bei mir nicht, da
die Funktion eine statische Variable inkrementieren soll - und die
würde, falls mit ins Headerfile verschoben, dann ja in jedem Modul, das
dieses Headerfile includiert, neu angelegt werden.
In der .h-Datei habe ich einfach:
1
voidmsTimeTick(void);
In der .c-Datei dann:
1
#include"msTime.h"
2
3
staticvolatileint16_ttickCnt16=0;
4
5
inlinevoidmsTimeTick(void){
6
tickCnt16++;
7
}
Bliebe, um ein Verschieben möglich zu machen, tickCnt16 global/extern zu
deklarieren - dann kann ich mir den Versuch "sauber" zu programmieren
aber gleich sparen und die golbal/externe Variable in meiner
_T3_Interrupt() Routine (die alle 125us gerufen wird) inkrementiern.
Da werd' ich wohl nicht drum rumkommen, wenn ich vermeiden will, dass
die _T3_Interrupt Routine fünf mal so lang wird???
U. L. schrieb:> Bliebe, um ein Verschieben möglich zu machen, tickCnt16 global/extern zu> deklarieren
Das sehe ich auch so. Mir fällt auch keine andere Möglichkeit ein.
In meinem Fall konnte ich die Funktion problemlos ohne inlining nutzen
(ist bei mir nicht kritisch). Das wäre noch der einzige Tipp der mir
einfällt, aber wenn das bei dir auch so problemlos möglich wäre, hättest
du vermutlich nicht gefragt...
> Function inlining will only take place when the function’s definition is
2
> visible (not just the prototype). In order to have a function inlined
3
> into more than one source file, the function definition may be placed
4
> into a header file that is included by each of the source files.
>> Ich empfinde das als höchst unsauber [...]
Das ist übliches Vorgehen. Wie korrekt angemerkt, muss der Compiler die
Definition der Funktion kennen, um sie inlinen zu können. Soll die
Inline-Funktion in mehr als einem Modul (Cspeek: Compilation Unit)
verwendet werden, definiert man sie im Header, und zwar als static
inline. Dadurch hat man formal mehrere Inline-Funktionen: Alle
Modul-static und mit gleicher Implementierung und gleichem Name.
Vorteil von static ist auch, dass beim Übersetzen einer Unit bekannt
ist, dass die Funktion außerhalb der Moduls nicht verwendet wird. Dies
mecht Kostenabschätzungen präziser, da von der Funktion keine Instanz
erstellt werden muss (es sei denn, es wird die Adresse der Funktion
genommen o.ä.).
Allerdings ist zu beachten, dass statische Variablen nicht über
Modulgrenzen geteilt werden, da sie ja zu unterschiedlichen Funktionen
gehören. Beispiel:
1
staticinlineintfun(void)
2
{
3
staticintcount;
4
return++count;
5
}
6
7
// count_a und count_b verwenden das gleiche count.
8
// Die ist nicht der Fall, wenn count_a und count_b zu
9
// unterschiedlichen Compilation Units gehoeren.
10
11
intcount_a(void)
12
{
13
returnfun();
14
}
15
16
intcount_b(void)
17
{
18
returnfun();
19
}
> [...] und habe zum Vergleich mal in den Abschnitt der gcc> Dokumentation geguckt> (https://gcc.gnu.org/onlinedocs/gcc/Inline.html).
Im Endeffekt das gleiche in Grün: Um Inlinen zu können, muss gcc die
Definition kennen: Entweder klassisch, indem er die Implementierung
direkt in der Quelle der Compilation Unit vorfindet, oder per
Cross-Module-Inlining, was modulübergreifende Optimierungen wie LTO
erfordert. Mit LTO erhält der Compiler die Implementierung als lto
Bytecode; lto wird dabei wie eine eigene Sprache analog zu C/C++
behandelt.
Falls Inlining nicht wie gewünscht funktioniert, gibt es i.d.R. weitere,
compilerabhängige Mittel dies zu beeinflussen:
Attribute (GCC: always_inline, noinline, flatten, gnu_inline).
Optionen (GCC: -f[no-]inline[-functions[-called-once]],
-f[no-]inline-small-functions, ... sowie Einflussnahme auf Kosten via
--param).
Anderes Inlining-Verhalten, eher selten verwendet, ist extern inline.
Hier wied die Funktion vom Compiler implementiert für den Fall, dass ein
anderes Modul die Funktion braucht.
Weiters gibt es noch gnu_inline (GCC), was inline mit Semantik wie vor
C99 umsetzt, als es noch eine GCC-Erweiterung war: Für die Funktion wird
kein Code erzeugt. Für den Fall, dass Calls nicht geninlinet werden
können, muss man eine eigene (extern) Implementierung verfügbar machen,
damit der Linker das entsprechende Symbol auflösen kann.