2010-03-25 9 views

Respuesta

7

La convención de llamadas tiene que ser aquella en la que el que llama borre los argumentos de la pila (porque el destinatario no sabe qué se pasará).

Eso no se corresponde necesariamente con lo que Microsoft llama "__cdecl". Solo por ejemplo, en un SPARC, normalmente pasará los argumentos en los registros, porque así es como el SPARC está diseñado para funcionar: sus registros básicamente actúan como una pila de llamadas que se propaga a la memoria principal si las llamadas son lo suficientemente profundas como para que ya no encajarán en el registro.

Aunque estoy menos seguro al respecto, esperaría más o menos lo mismo en IA64 (Itanium); también tiene un gran conjunto de registros (un par de cientos si la memoria sirve). Si no me equivoco, es un poco más permisivo sobre cómo usar los registros, pero espero que se use de manera similar al menos una gran parte del tiempo.

¿Por qué es esto importante para usted? El objetivo de usar stdarg.h y sus macros es ocultar las diferencias en la convención de llamadas de su código, por lo que puede funcionar con argumentos de variables de forma portátil.

Editar, basado en comentarios: Bien, ahora entiendo lo que estás haciendo (al menos lo suficiente para mejorar la respuesta). Dado que ya (aparentemente) tiene un código para manejar las variaciones en el ABI predeterminado, las cosas son más simples. Eso solo deja la cuestión de si las funciones variadic siempre usan el "ABI predeterminado", sea lo que sea que sea para la plataforma en cuestión. Con "stdcall" y "default" como las únicas opciones, creo que la respuesta es sí. Solo por ejemplo, en Windows, wsprintf y wprintf rompa la regla de oro, y usa la convención de llamadas cdecl en lugar de stdcall.

+1

Su comentario sobre SPARC se aplica a muchas, si no a todas, las arquitecturas RISC. Dado que los registros son tan abundantes, el compilador solo los usa para pasar argumentos. Sin mencionar, son un poco más rápidos que la memoria, especialmente. ya que probablemente esté pasando datos que ya están en registros (aunque supongo que tendrá que insertarlos en la pila para restaurarlos si los necesita más adelante, pero ...). De todos modos, sé que así es como funciona en MIPS y estoy bastante seguro de que PPC funciona de la misma manera. –

+2

* "¿Por qué te importa esto?" * Ben está trabajando en js-ctypes para Mozilla. Puede ver el contexto exacto en https://bugzilla.mozilla.org/show_bug.cgi?id=554790 (busque "idea loca") y puede encontrar algunos documentos que explican js-ctypes en https: //developer.mozilla. org/es/JavaScript_code_modules/ctypes.jsm –

+0

No hace falta decir que js-ctypes solo está disponible para el código de aplicación y complementos, no para las páginas web. –

0

Se refiere a "plataformas compatibles con MSVC" o, como regla general, incluso si se limita a las plataformas admitidas por MSVC, todavía tiene situaciones como IA64 y AMD64 donde solo hay "una" convención de llamadas, y esa convención de llamadas se llama __stdcall, pero ciertamente no es la misma __stdcall que obtiene en x86.

0

AFAIK, la diversidad de convenciones de llamadas es exclusiva de DOS/Windows en x86. La mayoría de las otras plataformas tenían compiladores que vienen con OS y estandarizan la convención.

2

La manera más definitiva que puede determinar esto es analizar la llamada g convenciones. Para las funciones de variadic funcionan, el convenio de llamada necesita un par de atributos:

  • El destinatario de la llamada debe ser capaz de acceder a los parámetros que no son parte de la lista de argumentos variable a partir de un desplazamiento fijo de la parte superior de la pila . Esto requiere que el compilador inserte los parámetros en la pila de derecha a izquierda. (Esto incluye cosas tales como el primer parámetro a printf, la especificación de formato. Además, la dirección de la lista de argumentos de variables también debe derivarse de una ubicación conocida.)
  • El llamante debe ser responsable de eliminar los parámetros del apilar una vez que la función ha regresado, porque solo el compilador, al generar el código para la persona que llama, sabe cuántos parámetros se insertaron en la pila en primer lugar. La función variadic en sí no tiene esta información.

stdcall no funcionará porque el destinatario es responsable de sacar los parámetros de la pila. En los viejos días de Windows de 16 bits, pascal no funcionaba porque empujaba los parámetros a la pila de izquierda a derecha.

Por supuesto, como han aludido las otras respuestas, muchas plataformas no le dan ninguna opción en términos de convención de llamadas, haciendo que esta pregunta sea irrelevante para esas.

1

considera la siguiente función en un sistema x86:

vacío algo __stdcall (char *, ...);

La función se declara como __stdcall, que es una convención de limpieza de llamada. Pero una función variadica no puede ser llamada-limpia, ya que el destinatario no sabe cuántos parámetros se pasaron, por lo que no sabe cuántos debe limpiar.

El compilador de Microsoft Visual Studio C/C++ resuelve este conflicto convirtiendo silenciosamente la convención de llamadas a __cdecl, que es la única convención de llamadas variadic admitida para funciones que no tienen un parámetro oculto.

¿Por qué esta conversión se lleva a cabo de forma silenciosa en lugar de generar una advertencia o error?

Supongo que es para hacer que las opciones del compilador/Gr (establezca la convención de llamadas predeterminada para __fastcall) y/Gz (establezca la convención de llamadas predeterminada para __stdcall) sean menos molestas.

La conversión automática de funciones variadic a __cdecl significa que puede simplemente agregar el modificador de línea/Gr o/Gz a sus opciones de compilación, y todo se compilará y ejecutará (solo con la nueva convención de llamadas).

Otra forma de ver esto no es por el pensamiento del compilador como convertir __stdcall variadic a __cdecl sino simplemente diciendo “para las funciones variadic, __stdcall es la persona que llama-limpio.”

click here

Cuestiones relacionadas