¿Cómo puedo decirle a gcc que no incorpore una función?

126

Digamos que tengo esta pequeña función en un archivo fuente

static void foo() {}

y construyo una versión optimizada de mi binario, pero no quiero que esta función esté incorporada (para fines de optimización). ¿Hay una macro que pueda agregar en un código fuente para evitar la inserción?

vehomzzz
fuente
Gracias por esta pregunta! Estaba perfilando con oprofile cuando una función no aparecía, las respuestas aquí arreglaron esto.
Simon A. Eugster
c ++: stackoverflow.com/questions/3329214/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

149

Desea el atributo gccespecífico noinline.

Este atributo de función evita que una función se considere para la alineación. Si la función no tiene efectos secundarios, existen otras optimizaciones que no sean en línea que hacen que las llamadas a funciones se optimicen, aunque la llamada a funciones está activa. Para evitar que esas llamadas se optimicen, coloque asm ("");

Úselo así:

void __attribute__ ((noinline)) foo() 
{
  ...
}
alex tingle
fuente
32
Al usar gcc 4.4.3 en Arch Linux, obtengo un error de sintaxis con el atributo colocado como se indica arriba. Funciona correctamente cuando precede a la función (por ejemplo, atributo ((noinline)) void foo () {})
mrkj
2
Arduino también quería colocarlo antes de la función.
Peter N Lewis
2
Editado para corregir la sintaxis del atributo.
Quuxplusone
1
La construcción asm ("") es en realidad bastante multiplataforma y realizó el trabajo. Lo hice para x86 Linux y no causó un problema de compilación en PowerPC AIX. Gracias por esta útil sugerencia!
Marty
1
El enfoque que requiere cambios de código en todas partes no puede considerarse razonablemente una respuesta aceptable.
ajeh
31

GCC tiene un interruptor llamado

-fno-inline-small-functions

Así que utilízalo cuando invoques gcc. Pero el efecto secundario es que todas las demás funciones pequeñas tampoco están alineadas.

lukmac
fuente
No funcionó a nivel de compilador. Estaba usando gcc 5.2.1 20150902 (Red Hat 5.2.1-2)
John Greene
O el GCC 6.4 actual está roto, o esto y más simple -fno-inlineno funciona en absoluto. gdbaún ingresa métodos en el paso a paso. Algo está roto y dudo que lo esté gdb.
ajeh
Desactivará la optimización en línea para todos, no solo para una función específica.
where23
@ajeh No incluir funciones en línea significa que se llaman normalmente, ¿no?
Melebius
21

Una forma portátil de hacer esto es llamar a la función a través de un puntero:

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

Aunque esto produce diferentes instrucciones para ramificar, lo que puede no ser su objetivo. Lo que trae un buen punto: ¿cuál es su objetivo aquí?

Andy Ross
fuente
2
Si el puntero está definido en el alcance del archivo, y no estático, debería funcionar ya que el compilador no puede asumir que tiene su valor inicial en el momento del uso. Si es un local (como se muestra), es casi seguro que se trate de la misma manera que foo (). ("En esta década", agregó, mirando las fechas)
greggo
16

Sé que la pregunta es sobre GCC, pero pensé que podría ser útil tener información sobre compiladores y otros compiladores también.

El noinline atributo de función de GCC también es bastante popular entre otros compiladores. Es apoyado por al menos:

  • Clang (consultar con __has_attribute(noinline))
  • Compilador Intel C / C ++ (su documentación es terrible, pero estoy seguro de que funciona en 16.0+)
  • Oracle Solaris Studio vuelve al menos a 12.2
  • Compilador ARM C / C ++ de nuevo al menos a 4.1
  • IBM XL C / C ++ vuelve al menos a 10.1
  • TI 8.0+ (o 7.3+ con --gcc, que definirá __TI_GNU_ATTRIBUTE_SUPPORT__)

Además, MSVC admite __declspec(noinline) volver a Visual Studio 7.1. Intel probablemente también lo admite (intentan ser compatibles tanto con GCC como con MSVC), pero no me he molestado en verificarlo. La sintaxis es básicamente la misma:

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

PGI 10.2+ (y probablemente más antiguo) admite un noinlinepragma que se aplica a la siguiente función:

#pragma noinline
static void foo(void) { }

TI 6.0+ admite un FUNC_CANNOT_INLINE pragma que (molestamente) funciona de manera diferente en C y C ++. En C ++, es similar a los IGP:

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

En C, sin embargo, se requiere el nombre de la función:

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

Cray 6.4+ (y posiblemente antes) adopta un enfoque similar, que requiere el nombre de la función:

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

Oracle Developer Studio también admite un pragma que toma el nombre de la función, volviendo al menos a Forte Developer 6 , pero tenga en cuenta que debe venir después de la declaración, incluso en versiones recientes:

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

Dependiendo de lo dedicado que esté, podría crear una macro que funcionaría en todas partes, pero necesitaría tener el nombre de la función y la declaración como argumentos.

Si, OTOH, estás de acuerdo con algo que simplemente funciona para la mayoría de las personas, puedes salirte con la tuya con algo que es un poco más estéticamente agradable y que no requiere repetirse. Ese es el enfoque que he tomado para Hedley , donde la versión actual de HEDLEY_NEVER_INLINE se ve así:

#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

Si no desea utilizar Hedley (es un solo dominio público / encabezado CC0), puede convertir la versión que comprueba las macros sin demasiado esfuerzo, pero más de lo que estoy dispuesto a poner ☺.

nemequ
fuente
Gracias por el enlace a su proyecto @nemequ. Les he pedido a nuestros otros desarrolladores que lo evalúen para nuestro uso. Tenemos diversas arquitecturas.
Daisuke Aramaki
Me interesaría mucho saber lo que dicen, especialmente si no están interesados. Y, por supuesto, estoy cerca para responder preguntas (rastreador de problemas de GitHub, correo electrónico, lo que sea ...).
nemequ
14

En caso de que obtenga un error del compilador __attribute__((noinline)), simplemente puede intentar:

noinline int func(int arg)
{
    ....
}
Sam Liao
fuente
10
static __attribute__ ((noinline))  void foo()
{

}

Esto es lo que funcionó para mí.

KenBee
fuente
8

Usa el noinline atributo :

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

Probablemente deberías usarlo tanto cuando declaras la función para uso externo como cuando escribes la función.

Chris Lutz
fuente
2

Yo trabajo con gcc 7.2. Específicamente, necesitaba una función que no estuviera en línea, porque tenía que ser instanciada en una biblioteca. Intenté la __attribute__((noinline))respuesta, así como la asm("")respuesta. Ninguno de los dos resolvió el problema.

Finalmente, pensé que definir una variable estática dentro de la función forzaría al compilador a asignarle espacio en el bloque de variables estáticas y a emitir una inicialización cuando se llama por primera vez a la función.

Este es un truco sucio, pero funciona.

Ofri Sadowsky
fuente
Puede definir su función inline void foo(void) { ... }en un encabezado y declararla extern inline void foo(void);en un archivo fuente de la biblioteca. Siguiendo la semántica de C99, el compilador podría alinear la función cuando le plazca Y emitir código objeto en su biblioteca. Ver ¿Es útil "en línea" sin "estático" o "externo" en C99? .
diapir