Come posso dire a gcc di non incorporare una funzione?


126

Supponiamo di avere questa piccola funzione in un file sorgente

static void foo() {}

e costruisco una versione ottimizzata del mio binario, ma non voglio che questa funzione sia incorporata (per scopi di ottimizzazione). c'è una macro che posso aggiungere in un codice sorgente per impedire l'allineamento?


Grazie per questa domanda! Stavo profilando con oprofile quando una funzione non si presentava, le risposte qui risolto questo.
Simon A. Eugster,

Risposte:


149

Si desidera l' attributo gcc-specifico noinline.

Questo attributo di funzione impedisce che una funzione venga considerata per l'inline. Se la funzione non ha effetti collaterali, ci sono ottimizzazioni diverse dall'inline che causano l'ottimizzazione delle chiamate di funzione, sebbene la chiamata di funzione sia attiva. Per evitare che tali chiamate vengano ottimizzate, metti asm ("");

Usalo in questo modo:

void __attribute__ ((noinline)) foo() 
{
  ...
}

32
Usando gcc 4.4.3 su Arch Linux, ottengo un errore di sintassi con l'attributo posizionato come sopra. Funziona correttamente quando precede la funzione (ad es. Attributo ((noinline)) void foo () {})
mrkj

2
Arduino voleva anche che fosse posto prima della funzione.
Peter N Lewis,

2
Modificato per correggere la sintassi dell'attributo.
Quuxplusone,

1
Il costrutto asm ("") è in realtà abbastanza multipiattaforma e ha fatto il lavoro. L'ho fatto per Linux x86 e non ha causato problemi di compilazione su PowerPC AIX. Grazie per questo suggerimento utile!
Marty,

1
L'approccio che richiede cambiamenti di codice ovunque non può essere ragionevolmente considerato una risposta accettabile.
ajeh

31

GCC ha un interruttore chiamato

-fno-inline-small-functions

Quindi usalo quando invochi gcc. Ma l'effetto collaterale è che tutte le altre piccole funzioni sono anche non incorporate.


Non ha funzionato a livello di compilatore. Stava usando gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene il

L'attuale GCC 6.4 è rotto, o questo e più semplice -fno-inlinenon funzionano affatto. gdbimmette ancora metodi al passaggio. Qualcosa è rotto, e dubito che lo sia gdb.
ajeh

Disattiverà l'ottimizzazione in linea per tutti, non solo per una funzione specificata.
dove23

@ajeh Le funzioni non incorporate indicano che vengono chiamate normalmente, no?
Melebio

21

Un modo portatile per farlo è chiamare la funzione tramite un puntatore:

void (*foo_ptr)() = foo;
foo_ptr();

Anche se questo produce diverse istruzioni per ramificare, che potrebbe non essere il tuo obiettivo. Il che fa emergere un buon punto: qual è il tuo obiettivo qui?


2
Se il puntatore è definito nell'ambito del file e non statico, dovrebbe funzionare poiché il compilatore non può assumere che abbia il suo valore iniziale al momento dell'uso. Se è un locale (come mostrato) è quasi certamente trattato come foo (). ("In questo decennio", ha aggiunto, guardando le date)
greggo

16

So che la domanda riguarda GCC, ma ho pensato che potesse essere utile avere alcune informazioni su compilatori e altri compilatori.

L' noinline attributo di funzione di GCC è abbastanza popolare anche con altri compilatori. È supportato da almeno:

  • Clang (verifica con __has_attribute(noinline))
  • Compilatore Intel C / C ++ (la loro documentazione è terribile, ma sono certo che funziona su 16.0+)
  • Oracle Solaris Studio torna almeno a 12.2
  • Compilatore ARM C / C ++ di nuovo almeno 4.1
  • IBM XL C / C ++ indietro almeno alla 10.1
  • TI 8.0+ (o 7.3+ con --gcc, che definirà __TI_GNU_ATTRIBUTE_SUPPORT__)

Inoltre, MSVC supporta __declspec(noinline) nuovamente Visual Studio 7.1. Probabilmente anche Intel lo supporta (cercano di essere compatibili con GCC e MSVC), ma non mi sono preoccupato di verificarlo. La sintassi è sostanzialmente la stessa:

__declspec(noinline)
static void foo(void) { }

IGP 10.2+ (e probabilmente precedente) supporta un noinlinepragma che si applica alla funzione successiva:

#pragma noinline
static void foo(void) { }

TI 6.0+ supporta un FUNC_CANNOT_INLINE pragma che (fastidiosamente) funziona diversamente in C e C ++. In C ++, è simile a IGP:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

In C, tuttavia, è richiesto il nome della funzione:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+ (e possibilmente prima) adotta un approccio simile, richiedendo il nome della funzione:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio supporta anche un pragma che prende il nome della funzione, risalendo almeno a Forte Developer 6 , ma nota che deve venire dopo la dichiarazione, anche nelle versioni recenti:

static void foo(void);
#pragma no_inline(foo)

A seconda di quanto ti dedichi, potresti creare una macro che funzionerebbe ovunque, ma dovrai avere il nome della funzione e la dichiarazione come argomenti.

Se, OTOH, stai bene con qualcosa che funziona solo per la maggior parte delle persone, puoi cavartela con qualcosa che è un po 'più esteticamente piacevole e non richiede di ripeterti. Questo è l'approccio che ho adottato per Hedley , dove la versione attuale di HEDLEY_NEVER_INLINE è simile a:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

Se non vuoi usare Hedley (è un singolo dominio pubblico / intestazione CC0) puoi convertire la versione controllando le macro senza troppi sforzi, ma più di quanto io sia disposto a inserire ☺.


Grazie per il link al tuo progetto @nemequ. Ho chiesto ai nostri altri sviluppatori di valutarlo per il nostro uso. Abbiamo diverse architetture.
Daisuke Aramaki,

Sarei molto interessato a sapere cosa dicono, soprattutto se non sono interessati. E, naturalmente, sono in giro per rispondere alle domande (tracker di problemi GitHub, e-mail, qualunque cosa ...).
nemequ

14

Nel caso in cui ricevi un errore del compilatore __attribute__((noinline)), puoi semplicemente provare:

noinline int func(int arg)
{
    ....
}

10
static __attribute__ ((noinline))  void foo()
{

}

Questo è ciò che ha funzionato per me.


8

Usa l' noinline attributo :

int func(int arg) __attribute__((noinline))
{
}

Probabilmente dovresti usarlo sia quando dichiari la funzione per uso esterno sia quando scrivi la funzione.


2

Lavoro con gcc 7.2. In particolare avevo bisogno di una funzione per non essere incorporata, perché doveva essere istanziata in una libreria. Ho provato la __attribute__((noinline))risposta, così come la asm("")risposta. Nessuno dei due ha risolto il problema.

Infine, ho immaginato che la definizione di una variabile statica all'interno della funzione costringerà il compilatore ad allocare spazio per essa nel blocco di variabili statiche e ad emettere un'inizializzazione al momento della prima chiamata della funzione.

Questo è una specie di trucco sporco, ma funziona.


È possibile definire la funzione inline void foo(void) { ... }in un'intestazione e dichiararla extern inline void foo(void);in un file di origine della libreria. Dopo la semantica C99, il compilatore potrebbe incorporare la funzione quando lo desidera ed emettere il codice oggetto nella libreria. Vedi "inline" senza "statico" o "esterno" è mai utile in C99? .
diapir,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.