mikrocontroller.net

Forum: Compiler & IDEs XC32 Inline Funktionen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Danish B. (danishbelal)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
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.

Ich empfinde das als höchst unsauber und habe zum Vergleich mal in den 
Abschnitt der gcc Dokumentation geguckt 
(https://gcc.gnu.org/onlinedocs/gcc/Inline.html).
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.

Autor: Uli N. (uln)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
void msTimeTick(void);

In der .c-Datei dann:
#include "msTime.h"

static volatile int16_t tickCnt16 = 0 ;

inline void msTimeTick(void) { 
  tickCnt16++;
} 

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???
!void __attribute__ ((__interrupt__, no_auto_psv)) _T3Interrupt(void)
!{
0x36A: PUSH RCOUNT
0x36C: MOV.D W0, [W15++]
0x36E: MOV.D W2, [W15++]
0x370: MOV.D W4, [W15++]
0x372: MOV.D W6, [W15++]
0x374: LNK #0x0
!    msTimeTick();
0x376: RCALL msTimeTick
!    IFS0bits.T3IF = 0;        // Clear Timer3 Interrupt Flag
0x378: BCLR 0x85, #0
!}
0x37A: ULNK
0x37C: MOV.D [--W15], W6
0x37E: MOV.D [--W15], W4
0x380: MOV.D [--W15], W2
0x382: MOV.D [--W15], W0
0x384: POP RCOUNT
0x386: RETFIE


!inline void msTimeTick(void) {
0xF0EA: LNK #0x0
!  tickCnt16++;
0xF0EC: MOV tickCnt16, W0
0xF0EE: INC W0, W0
0xF0F0: MOV W0, tickCnt16
!}
0xF0F2: ULNK
0xF0F4: RETURN

: Bearbeitet durch User
Autor: Danish B. (danishbelal)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Danish B. schrieb:
> 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.
>
> 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:
static inline int fun (void)
{
    static int count;
    return ++count;
}

// count_a und count_b verwenden das gleiche count.
// Die ist nicht der Fall, wenn count_a und count_b zu
// unterschiedlichen Compilation Units gehoeren.

int count_a (void)
{
    return fun();
}

int count_b (void)
{
    return fun();
}

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

: Bearbeitet durch User
Autor: Danish B. (danishbelal)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die ausführlich Antwort -- man lernt ja nie aus :)

Habe noch etwas gefunden, dass "für" Inline-Definitionen im Header ist: 
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-inline

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.