¿Cuáles son las diferentes convenciones de llamadas en C / C ++ y qué significa cada una?

Existen diferentes convenciones de llamadas disponibles en C / C ++: stdcall , extern , pascal , etc. ¿Cuántas convenciones de llamadas están disponibles y qué significa cada una? ¿Hay algún enlace que los describa?

Ni el estándar C ni el estándar C ++ tienen tal concepto: estas son características de comstackdores, enlazadores y / o sistemas operativos específicos, por lo que realmente debería indicar qué tecnologías específicas le interesan.

Respuesta simple: uso cdecl, stdcall y fastcall. Rara vez uso la llamada rápida. stdcall se usa para llamar a las funciones de la API de Windows.

Respuesta detallada (robado de Wikipedia ):

cdecl : en cdecl, los argumentos de la subrutina se pasan a la stack. Los valores enteros y las direcciones de memoria se devuelven en el registro EAX, valores de coma flotante en el registro ST0 x87. Los registros EAX, ECX y EDX se guardan de la persona que llama, y ​​el rest se guardan en el dispositivo. Los registros de punto flotante x87 ST0 a ST7 deben estar vacíos (reventados o liberados) cuando se llama a una nueva función, y ST1 a ST7 debe estar vacío al salir de una función. ST0 también debe estar vacío cuando no se usa para devolver un valor.

syscall – Esto es similar a cdecl en que los argumentos son empujados de derecha a izquierda. EAX, ECX y EDX no se conservan. El tamaño de la lista de parámetros en doublewords se pasa en AL.

pascal : los parámetros se insertan en la stack en orden de izquierda a derecha (al contrario de cdecl), y el destinatario es responsable de equilibrar la stack antes del retorno.

stdcall – La convención de llamadas stdcall [4] es una variación de la convención de llamadas Pascal en la que el destinatario es responsable de limpiar la stack, pero los parámetros se envían a la stack en orden de derecha a izquierda, como en la llamada _cdecl convención. Los registros EAX, ECX y EDX están designados para su uso dentro de la función. Los valores devueltos se almacenan en el registro EAX.

fastcall – __fastcall convención (también conocido como __msfastcall) pasa los primeros dos argumentos (evaluados de izquierda a derecha) que se ajustan a ECX y EDX. Los argumentos restantes se insertan en la stack de derecha a izquierda.

vectorcall : en Visual Studio 2013, Microsoft introdujo la convención de llamadas __vectorcall en respuesta a los problemas de eficiencia de los desarrolladores de juegos, gráficos, video / audio y códec. [7] Para el código IA-32 y x64, __vectorcall es similar a __fastcall y a las convenciones de llamada x64 originales, respectivamente, pero las amplía para admitir el paso de argumentos vectoriales usando registros SIMD. Para x64, cuando cualquiera de los primeros seis argumentos son tipos de vectores (flotante, doble, __m128, __m256, etc.), se pasan a través de los registros XMM / YMM correspondientes. De manera similar para IA-32, hasta seis registros XMM / YMM se asignan secuencialmente para argumentos de tipo vector de izquierda a derecha, independientemente de la posición. Además, __vectorcall agrega soporte para pasar valores de agregado vectorial homogéneo (HVA), que son tipos compuestos que consisten únicamente en hasta cuatro tipos de vectores idénticos, utilizando los mismos seis registros. Una vez que los registros se han asignado para los argumentos de tipo vector, los registros no utilizados se asignan a los argumentos HVA de izquierda a derecha, independientemente de la posición. El tipo de vector resultante y los valores de HVA se devuelven utilizando los primeros cuatro registros XMM / YMM.

safecall – n Delphi y Free Pascal en Microsoft Windows, la convención de llamadas safecall encapsula el manejo de errores COM (Modelo de Objetos Componentes), por lo que las excepciones no se filtran a la persona que llama, pero se informan en el valor de retorno HRESULT, como lo requiere COM / VIEJO. Al llamar a una función safecall desde el código Delphi, Delphi también verifica automáticamente el HRESULT devuelto y genera una excepción si es necesario.

La convención de llamada segura es la misma que la convención de llamada stdall, excepto que las excepciones se transmiten a la persona que llama en EAX como un resultado HR (en lugar de en FS: [0]), mientras que el resultado de la función se pasa por referencia en la stack aunque fue un parámetro final de “salida”. Al llamar a una función Delphi desde Delphi, esta convención de llamadas aparecerá igual que cualquier otra convención de llamadas, ya que aunque las excepciones se transmiten en EAX, la persona que llama automáticamente las vuelve a convertir en excepciones adecuadas. Al utilizar objetos COM creados en otros idiomas, los resultados de HR se generarán automáticamente como excepciones, y el resultado para las funciones de obtención se encuentra en el resultado en lugar de en un parámetro. Al crear objetos COM en Delphi con safecall, no hay necesidad de preocuparse por los resultados de HR, ya que las excepciones se pueden plantear como normales, pero se verán como resultados de RR.HH. en otros idiomas.

Convención de llamadas de Microsoft X64 : se sigue la convención de llamadas de Microsoft x64 [12] [13] en Windows y UEFI previo al arranque (para modo largo en x86-64). Utiliza los registros RCX, RDX, R8, R9 para los primeros cuatro argumentos enteros o de puntero (en ese orden), y XMM0, XMM1, XMM2, XMM3 se utilizan para argumentos de coma flotante. Se insertan argumentos adicionales en la stack (de derecha a izquierda). Los valores de retorno enteros (similar a x86) se devuelven en RAX si son de 64 bits o menos. Los valores de retorno de punto flotante se devuelven en XMM0. Los parámetros de menos de 64 bits de longitud no se extienden por cero; los bits altos no están en cero.

Al comstackr para la architecture x64 en un contexto de Windows (ya sea que use herramientas Microsoft o no de Microsoft), solo hay una convención de llamadas, la que se describe aquí, de modo que stdcall, thiscall, cdecl, fastcall, etc., ahora son todos y el mismo.

En la convención de llamadas Microsoft x64, es responsabilidad del usuario asignar 32 bytes de “espacio sombreado” en la stack justo antes de llamar a la función (independientemente de la cantidad real de parámetros utilizados), y abrir la stack después de la llamada. El espacio sombreado se usa para dertwigr RCX, RDX, R8 y R9, [14] pero debe estar disponible para todas las funciones, incluso aquellas con menos de cuatro parámetros.

Los registros RAX, RCX, RDX, R8, R9, R10, R11 se consideran volátiles (se guardan los llamantes). [15]

Los registros RBX, RBP, RDI, RSI, RSP, R12, R13, R14 y R15 se consideran no volátiles (guardado en línea). [15]

Por ejemplo, una función que toma 5 argumentos enteros tomará del primero al cuarto en los registros, y el quinto se colocará en la parte superior del espacio sombreado. Entonces, cuando se ingresa la función llamada, la stack se compondrá de (en orden ascendente) la dirección de retorno, seguida por el espacio sombreado (32 bytes) seguido del quinto parámetro.

En x86-64, Visual Studio 2008 almacena números en coma flotante en XMM6 y XMM7 (así como en XMM8 a XMM15); consecuentemente, para x86-64, las rutinas del lenguaje ensamblador escritas por el usuario deben preservar XMM6 y XMM7 (en comparación con x86 donde las rutinas del lenguaje ensamblador escritas por el usuario no necesitan preservar XMM6 y XMM7). En otras palabras, las rutinas del lenguaje ensamblador escritas por el usuario se deben actualizar para guardar / restaurar XMM6 y XMM7 antes / después de la función cuando se transporta desde x86 a x86-64.

El estándar C ++ básicamente tiene dos: extern "C" y extern "C++" . Este último es el predeterminado; este antiguo se usa cuando necesita vincularse al código C. Los comstackdores pueden definir otras cadenas además de “C” y “C ++”. Por ejemplo, un comstackdor que sea compatible con su hermano Pascal puede definir extern "Pascal".

Desafortunadamente, algunos comstackdores han inventado palabras clave en su lugar. En estos casos, consulte la documentación del comstackdor.

Se refieren a qué orden colocar parámetros en la stack de llamadas, y cuándo usar call por valor y / o semántica de llamada por referencia. Son extensiones específicas del comstackdor destinadas a simplificar la progtwigción multilingüe.

Son extensiones específicas de plataforma necesarias para llamar funciones en ciertas bibliotecas, particularmente la API de Win32. No son estándares y son específicos de cada comstackdor, aunque las opciones de MSVC son el estándar de facto para Windows en x86. Normalmente, una biblioteca que los necesita los declarará en los archivos de encabezado y funcionarán de forma transparente. La principal diferencia entre ellos es que C usó históricamente una convención menos eficiente que permitía una cantidad variable de argumentos de cualquier tipo, mientras que Windows y la mayoría de los otros idiomas lo hacían de manera diferente. Sin embargo, muchas de las diferencias, como empujar a zurdos o diestros y hacer que la persona que llama o la función llamada se limpiaban, eran bastante arbitrarias.

Son en gran medida irrelevantes para el código de 64 bits: las guerras santas sobre las convenciones de llamadas nunca ocurrieron en esas plataformas.

Hay algunos casos comunes en los que es posible que deba agregar uno de estos a una función. Un módulo de C ++ que necesita vincularse con módulos escritos en otros lenguajes (y algunas veces incluso en otros comstackdores de C ++) tendrá que usar la convención de nombres extern "C" para la compatibilidad. Una función de callback necesita usar la misma convención de llamadas que la persona que llama, que con la API de Windows es CALLBACK , no la predeterminada. Una biblioteca compartida podría necesitar exportar sus funciones con una convención de llamadas diferente a la que usa internamente, o podría querer hacer que su uso de __cdecl explícito en caso de que los cambios predeterminados. Puede o no obtener un mejor rendimiento de __fastcall en algunas plataformas: acelera principalmente las funciones de hoja corta con uno o dos parámetros, y puede hacer que algunos progtwigs sean más lentos.

fastcall es el optimizado pero nadie lo usa