stringtranslate.com

convención de llamada

En informática , una convención de llamada es un esquema de nivel de implementación (de bajo nivel) sobre cómo las subrutinas o funciones reciben parámetros de su llamador y cómo devuelven un resultado. Cuando algún código llama a una función, se han tomado decisiones de diseño sobre dónde y cómo se pasan los parámetros a esa función, y dónde y cómo se devuelven los resultados de esa función, y estas transferencias generalmente se realizan a través de ciertos registros o dentro de un marco de pila en la llamada. pila . Hay opciones de diseño sobre cómo se dividen entre la persona que llama y el destinatario las tareas de preparación para una llamada de función y restauración del entorno una vez completada la función. Alguna convención de llamadas especifica la forma en que se debe llamar a cada función. Se debe utilizar la convención de llamada correcta para cada llamada de función, para permitir la ejecución correcta y confiable de todo el programa utilizando estas funciones.

Introducción

Las convenciones de llamada generalmente se consideran parte de la interfaz binaria de la aplicación (ABI).

Conceptos relacionados

Los nombres o significados de los parámetros y valores de retorno se definen en la interfaz de programación de aplicaciones (API, a diferencia de ABI), que es un concepto separado aunque relacionado con ABI y la convención de llamada. Los nombres de los miembros dentro de las estructuras y objetos pasados ​​también se considerarían parte de la API y no de la ABI. A veces, las API incluyen palabras clave para especificar la convención de llamada de funciones.

Las convenciones de llamada generalmente no incluyen información sobre el manejo de la vida útil de estructuras y objetos asignados dinámicamente. {{{1}}} otra documentación indica dónde recae la responsabilidad de liberar la memoria asignada.

Es poco probable que las convenciones de llamada especifiquen el diseño de elementos dentro de estructuras y objetos, como el orden de bytes o el empaquetado de estructuras.

Para algunos lenguajes, la convención de llamada incluye detalles de manejo de errores o excepciones (por ejemplo, Go , Java ) y para otros, no (por ejemplo, C++ ).

Para las llamadas a procedimientos remotos , existe un concepto análogo llamado Marshalling .

Las convenciones de llamada pueden estar relacionadas con la estrategia de evaluación de un lenguaje de programación en particular , pero la mayoría de las veces no se consideran parte de él (o viceversa), ya que la estrategia de evaluación generalmente se define en un nivel de abstracción más alto y se ve como parte del lenguaje en lugar de como un detalle de implementación de bajo nivel del compilador de un lenguaje en particular .

Diferentes convenciones de llamadas

Las convenciones de llamada pueden diferir en:

Llamar a convenciones dentro de una plataforma

A veces aparecen varias convenciones de llamadas en una única plataforma; una plataforma y una implementación de lenguaje determinadas pueden ofrecer una opción de convenciones de llamada. Las razones de esto incluyen el rendimiento, la adaptación de convenciones de otros lenguajes populares y las restricciones o convenciones impuestas por varias " plataformas informáticas ".

Muchas arquitecturas sólo tienen una convención de llamada ampliamente utilizada, a menudo sugerida por el arquitecto. Para RISC, incluidos SPARC, MIPS y RISC-V , a menudo se utilizan nombres de registros basados ​​en esta convención de llamada. Por ejemplo, los registros MIPS tienen "nombres ABI" $4hasta , lo que refleja su uso para el paso de parámetros en la convención de llamada estándar. (Las CPU RISC tienen muchos registros equivalentes de uso general, por lo que normalmente no hay ninguna razón de hardware para darles nombres distintos a los números).$7$a0$a3

La convención de llamada del lenguaje de un programa determinado puede diferir de la convención de llamada de la plataforma subyacente, el sistema operativo o de alguna biblioteca a la que se está vinculando. Por ejemplo, en Windows de 32 bits , las llamadas al sistema operativo tienen la convención de llamada stdcall , mientras que muchos programas C que se ejecutan allí usan la convención de llamada cdecl . Para dar cabida a estas diferencias en la convención de llamada, los compiladores a menudo permiten palabras clave que especifican la convención de llamada para una función determinada. Las declaraciones de funciones incluirán palabras clave adicionales específicas de la plataforma que indican la convención de llamada que se utilizará. Cuando se maneja correctamente, el compilador generará código para llamar funciones de la manera adecuada.

Algunos lenguajes permiten que la convención de llamada para una función se especifique con esa función; otros tendrán alguna convención de llamada, pero estará oculta para los usuarios de ese lenguaje y, por lo tanto, normalmente no será una consideración para el programador.

Arquitecturas

x86 (32 bits)

La versión de 32 bits de la arquitectura x86 se utiliza con muchas convenciones de llamadas diferentes. Debido a la pequeña cantidad de registros arquitectónicos y al enfoque histórico en la simplicidad y el tamaño pequeño del código, muchas convenciones de llamadas x86 pasan argumentos en la pila. El valor de retorno (o un puntero a él) se devuelve en un registro. Algunas convenciones utilizan registros para los primeros parámetros que pueden mejorar el rendimiento, especialmente para rutinas hoja cortas y simples que se invocan con mucha frecuencia (es decir, rutinas que no llaman a otras rutinas).

Llamada de ejemplo:

 empujar EAX ; pasar algún resultado de registro push dword [ EBP + 20 ] ; pasar alguna variable de memoria (sintaxis FASM/TASM) push 3 ; pasar alguna llamada constante calc ; el resultado devuelto ahora está en EAX            

Estructura típica del destinatario: (algunas o todas (excepto ret) de las instrucciones siguientes se pueden optimizar mediante procedimientos simples). Algunas convenciones dejan asignado el espacio de parámetros y utilizan simple reten lugar de ret imm16. En ese caso, la persona que llama podría, add esp,12en este ejemplo, o de otra manera, ocuparse del cambio a ESP.

calc: empujar EBP ; guardar puntero de marco antiguo mov EBP , ESP ; obtener nuevo puntero de marco subESP , tamaño local ; Reserve espacio en la pila para los locales . . ; realizar cálculos, dejar el resultado en EAX . movESP , EBP ; _ espacio libre para los locales pop EBP ; restaurar el puntero del marco antiguo ret paramsize ; espacio libre de parámetros y retorno.                      

x86-64

La versión de 64 bits de la arquitectura x86, conocida como x86-64 , AMD64 e Intel 64, tiene dos secuencias de llamadas de uso común. En Windows se utiliza una secuencia de llamada, definida por Microsoft; la otra secuencia de llamada, especificada en AMD64 System V ABI, es utilizada por sistemas tipo Unix y, con algunos cambios, por OpenVMS . Como x86-64 tiene más registros de uso general que x86 de 16 bits, ambas convenciones pasan algunos argumentos en los registros.

BRAZO (A32)

La convención de llamadas ARM estándar de 32 bits asigna los 16 registros de propósito general como:

Si el tipo de valor devuelto es demasiado grande para caber en r0 a r3, o cuyo tamaño no se puede determinar estáticamente en tiempo de compilación, entonces la persona que llama debe asignar espacio para ese valor en tiempo de ejecución y pasar un puntero a ese espacio en r0.

Las subrutinas deben preservar el contenido de r4 a r11 y el puntero de la pila (quizás guardándolos en la pila en la función prólogo , luego usándolos como espacio temporal y luego restaurándolos desde la pila en la función epílogo ). En particular, las subrutinas que llaman a otras subrutinas deben guardar la dirección de retorno en el registro de enlace r14 en la pila antes de llamar a esas otras subrutinas. Sin embargo, dichas subrutinas no necesitan devolver ese valor a r14; simplemente necesitan cargar ese valor en r15, el contador del programa, para regresar.

La convención de llamadas ARM exige el uso de una pila descendente completa. Además, el puntero de la pila siempre debe estar alineado con 4 bytes y siempre debe estar alineado con 8 bytes en una llamada de función con una interfaz pública. [1]

Esta convención de llamada hace que una subrutina ARM "típica" haga lo siguiente:

BRAZO (A64)

La convención de llamada ARM de 64 bits ( AArch64 ) asigna los 31 registros de propósito general como: [2]

Todos los registros que comienzan con x tienen un registro correspondiente de 32 bits con el prefijo w . Por tanto, un x0 de 32 bits se denomina w0.

De manera similar, los 32 registros de punto flotante se asignan como: [3]

RISC-V ISA

RISC-V tiene una convención de llamada definida con dos tipos, con o sin punto flotante. [4] Pasa argumentos en registros siempre que sea posible.

ENERGÍA, PowerPC y Energía ISA

Las arquitecturas POWER , PowerPC y Power ISA tienen una gran cantidad de registros, por lo que la mayoría de las funciones pueden pasar todos los argumentos en los registros para llamadas de un solo nivel . Se pasan argumentos adicionales a la pila, y siempre se asigna espacio para argumentos basados ​​en registros en la pila para comodidad de la función llamada en caso de que se utilicen llamadas multinivel (recursivas o no) y los registros deban guardarse. Esto también es útil en funciones variadas , como printf(), donde es necesario acceder a los argumentos de la función como una matriz. Se utiliza una única convención de llamada para todos los lenguajes de procedimiento.

Las instrucciones de rama y enlace almacenan la dirección del remitente en un registro de enlace especial separado de los registros de propósito general; una rutina regresa a su llamador con una instrucción de bifurcación que utiliza el registro de enlace como dirección de destino. Las rutinas hoja no necesitan guardar ni restaurar el registro de enlace; Las rutinas que no son hoja deben guardar la dirección de retorno antes de realizar una llamada a otra rutina y restaurarla antes de que regrese, guardándola usando la instrucción Move From Special Purpose Register para mover el registro de enlace a un registro de propósito general y, si es necesario, luego guardarlo en la pila y restaurarlo, si se guardó en la pila, cargando el valor del registro de enlace guardado en un registro de propósito general y luego usando la instrucción Mover a registro de propósito especial para mover el registro que contiene el valor guardado. valor del registro de enlace al registro de enlace.

MIPS

El ABI O32 [5] es el ABI más utilizado, debido a su condición de ABI System V original para MIPS. [6] Está estrictamente basado en pilas, con sólo cuatro registros disponibles para pasar argumentos. Esta lentitud percibida, junto con un modelo antiguo de punto flotante con sólo 16 registros, ha fomentado la proliferación de muchas otras convenciones de llamadas. La ABI tomó forma en 1990 y nunca se actualizó desde 1994. Sólo está definida para MIPS de 32 bits, pero GCC ha creado una variación de 64 bits llamada O64. [7]$a0-$a3

Para 64 bits, el N64 ABI (no relacionado con Nintendo 64 ) de Silicon Graphics es el más utilizado. La mejora más importante es que ahora hay ocho registros disponibles para pasar argumentos; También aumenta el número de registros de punto flotante a 32. También hay una versión ILP32 llamada N32, que utiliza punteros de 32 bits para código más pequeño, análogo al ABI x32 . Ambos se ejecutan en el modo de 64 bits de la CPU. [7]

Se han realizado algunos intentos de reemplazar O32 con un ABI de 32 bits que se parece más a N32. En una conferencia de 1995 se propuso MIPS EABI, cuya versión de 32 bits era bastante similar. [8] EABI inspiró a MIPS Technologies a proponer una ABI "NUBI" más radical que además reutiliza registros de argumentos para el valor de retorno. [9] MIPS EABI es compatible con GCC pero no con LLVM; ninguno apoya a NUBI.

Para todos los O32 y N32/N64, la dirección del remitente se almacena en un $raregistro. Esto se configura automáticamente con el uso de las instrucciones JAL(saltar y vincular) o JALR(saltar y vincular registrar). La pila crece hacia abajo.

SPARC

La arquitectura SPARC , a diferencia de la mayoría de las arquitecturas RISC , se basa en ventanas de registro . Hay 24 registros accesibles en cada ventana de registro: 8 son los registros "de entrada" (%i0-%i7), 8 son los registros "locales" (%l0-%l7) y 8 son los registros "de salida" (% o0-%o7). Los registros "de entrada" se utilizan para pasar argumentos a la función que se llama, y ​​cualquier argumento adicional debe insertarse en la pila . Sin embargo, la función llamada siempre asigna espacio para manejar un posible desbordamiento de la ventana de registro, variables locales y (en SPARC de 32 bits) devolver una estructura por valor. Para llamar a una función, se colocan los argumentos de la función que se llamará en los registros de "salida"; cuando se llama a la función, los registros "de salida" se convierten en registros "de entrada" y la función llamada accede a los argumentos en sus registros "de entrada". Cuando se completa la función llamada, coloca el valor de retorno en el primer registro "de entrada", que se convierte en el primer registro "de salida" cuando regresa la función llamada.

El System V ABI , [10] que siguen la mayoría de los sistemas modernos tipo Unix , pasa los primeros seis argumentos en los registros "en" %i0 a %i5, reservando %i6 para el puntero de marco y %i7 para la dirección de retorno.

IBM System/360 y sucesores

IBM System/360 es otra arquitectura sin pila de hardware. Los ejemplos siguientes ilustran la convención de llamada utilizada por OS/360 y sus sucesores antes de la introducción de z/Architecture de 64 bits ; Otros sistemas operativos para System/360 pueden tener convenciones de llamada diferentes.

Programa de llamadas:

 LA 1,ARGS Cargar dirección de lista de argumentos L 15,=A(SUB) Cargar dirección de subrutina BALR 14,15 Rama a la rutina llamada 1 ...ARGS DC A(FIRST) Dirección del primer argumento CC A (SEGUNDO) ... DC A(TERCERO)+X'80000000' Último argumento 2

Programa llamado:

SUB EQU * Este es el punto de entrada del subprograma

Secuencia de entrada estándar:

 USING *,15 3 STM 14,12,12(13) Guardar registros 4 ST 13, GUARDAR+4 Guardar dirección del área de guardado de la persona que llama LA 12,SAVE Áreas de guardado en cadena ST 12,8(13) LR 13,12 ...

Secuencia de retorno estándar:

 L 13,GUARDAR+4 5 LM 14,12,12(13) L 15,REVALICIÓN 6 BR 14 Volver a la persona que llamaGUARDAR DS 18F Área de guardado 7

Notas:

  1. La BALRinstrucción almacena la dirección de la siguiente instrucción (dirección de retorno) en el registro especificado por el primer argumento (registro 14) y se bifurca a la dirección del segundo argumento en el registro 15.
  2. La persona que llama pasa la dirección de una lista de direcciones de argumentos en el registro 1. La última dirección tiene el bit de orden superior configurado para indicar el final de la lista. Esto limita los programas que utilizan esta convención a direccionamiento de 31 bits .
  3. La dirección de la rutina llamada está en el registro 15. Normalmente, esto se carga en otro registro y el registro 15 no se utiliza como registro base.
  4. La STMinstrucción guarda los registros 14, 15 y 0 a 12 en un área de 72 bytes proporcionada por el llamante llamada área de guardado señalada por el registro 13. La rutina llamada proporciona su propia área de guardado para que la utilicen las subrutinas que llama; la dirección de esta área normalmente se mantiene en el registro 13 durante toda la rutina. Las instrucciones siguientes STMactualizan las cadenas de avance y retroceso que vinculan esta área de guardado con el área de guardado de la persona que llama.
  5. La secuencia de retorno restaura los registros de la persona que llama.
  6. El registro 15 se suele utilizar para pasar un valor de retorno.
  7. Declarar un saveareaestáticamente en la rutina llamada la convierte en no reentrante y no recursiva ; un programa reentrante utiliza una dinámica savearea, adquirida del sistema operativo y liberada al regresar, o en el almacenamiento pasado por el programa que llama.

En System/390 ABI [11] y z/Architecture ABI, [12] utilizados en Linux:

Se pasan argumentos adicionales a la pila.

superh

Nota: "preservado" se reserva para guardar el destinatario de la llamada; Lo mismo ocurre con "garantizado".

68k

La convención de llamadas más común para la serie Motorola 68000 es: [13] [14] [15] [16]

IBM 1130

La IBM 1130 era una pequeña máquina direccionable por palabras de 16 bits. Tenía sólo seis registros más indicadores de condición y no tenía pila. Los registros son Registro de dirección de instrucción (IAR) , Acumulador (ACC) , Extensión del acumulador (EXT) y tres registros de índice X1–X3. El programa de llamada es responsable de guardar ACC, EXT, X1 y X2. [17] Hay dos pseudooperaciones para llamar a subrutinas, CALLpara codificar subrutinas no reubicables directamente vinculadas con el programa principal y LIBFpara llamar a subrutinas de biblioteca reubicables a través de un vector de transferencia . [18] Ambas pseudooperaciones se resuelven en una instrucción de máquina Branch y Store IAR ( BSI) que almacena la dirección de la siguiente instrucción en su dirección efectiva (EA) y se bifurca a EA+1.

Los argumentos siguen a BSI‍—‌generalmente son direcciones de argumentos de una palabra‍—‌la rutina llamada debe saber cuántos argumentos esperar para poder omitirlos al regresar. Alternativamente, los argumentos se pueden pasar en registros. Las rutinas de funciones devolvían el resultado en ACC para argumentos reales, o en una ubicación de memoria denominada pseudoacumulador de números reales (FAC). Los argumentos y la dirección del remitente se abordaron utilizando un desplazamiento del valor IAR almacenado en la primera ubicación de la subrutina.

 * Ejemplo de subrutina 1130 ENT SUB Declarar "SUB" como punto de entrada externo SUB DC 0 Palabra reservada en el punto de entrada, codificada convencionalmente "DC *-*" * El código de subrutina comienza aquí * Si hubiera argumentos las direcciones se pueden cargar indirectamente desde la dirección del remitente. LDX I 1 SUB Cargar X1 con la dirección del primer argumento (por ejemplo) ... * Secuencia de retorno LD RES Cargar resultado entero en ACC * Si no se proporcionaron argumentos, rama indirecta a la dirección de retorno almacenada BI SUB Si no se proporcionaron argumentos FIN SUB

Las subrutinas en IBM 1130, CDC 6600 y PDP-8 (las tres computadoras se introdujeron en 1965) almacenan la dirección del remitente en la primera ubicación de una subrutina. [19]

Llamar a convenciones fuera de las arquitecturas de las máquinas

código roscado

El código subproceso asigna toda la responsabilidad de configurar y limpiar después de una llamada de función al código llamado. El código de llamada no hace más que enumerar las subrutinas que se llamarán. Esto coloca todo el código de configuración y limpieza de funciones en un solo lugar (el prólogo y el epílogo de la función) en lugar de en los muchos lugares donde se llama a esa función. Esto hace que el código subproceso sea la convención de llamadas más compacta.

El código enhebrado pasa todos los argumentos de la pila. Todos los valores devueltos se devuelven en la pila. Esto hace que las implementaciones ingenuas sean más lentas que llamar a convenciones que mantienen más valores en los registros. Sin embargo, las implementaciones de código de subprocesos que almacenan en caché varios de los valores de la pila superior en los registros (en particular, la dirección de retorno) suelen ser más rápidas que las convenciones de llamada de subrutinas que siempre envían y sacan la dirección de retorno a la pila. [20] [21] [22]

PL/I

La convención de llamada predeterminada para programas escritos en lenguaje PL/I pasa todos los argumentos por referencia , aunque opcionalmente se pueden especificar otras convenciones. Los argumentos se manejan de manera diferente para diferentes compiladores y plataformas, pero normalmente las direcciones de los argumentos se pasan a través de una lista de argumentos en la memoria. Se puede pasar una dirección final oculta que apunte a un área que contenga el valor de retorno. Debido a la amplia variedad de tipos de datos admitidos por PL/I, también se puede pasar un descriptor de datos para definir, por ejemplo, las longitudes de cadenas de caracteres o bits, la dimensión y los límites de las matrices ( vectores de datos ), o el diseño y el contenido. de una estructura de datos . Los argumentos ficticios se crean para argumentos que son constantes o que no concuerdan con el tipo de argumento que espera el procedimiento llamado.

Ver también

Referencias

  1. ^ "Estándar de llamada de procedimiento para la arquitectura ARM". 2021.
  2. ^ "Parámetros en registros de uso general". Guía del programador de la serie ARM Cortex-A para ARMv8-A . Consultado el 12 de noviembre de 2020 .
  3. ^ "Parámetros en NEON y registros de coma flotante". desarrollador.arm.com . Consultado el 13 de noviembre de 2020 .
  4. ^ "Convención de llamadas RISC-V" (PDF) .
  5. ^ "Referencia rápida del conjunto de instrucciones MIPS32".
  6. ^ Dulce, Dominic. Consulte Ejecución de MIPS (2 ed.). Editores Morgan Kaufmann . ISBN 0-12088-421-6.
  7. ^ ab "Historia de MIPS ABI".
  8. ^ Christopher, Eric (11 de junio de 2003). "documentación mips eabi". [email protected] (lista de correo) . Consultado el 19 de junio de 2020 .
  9. ^ "NUBI".
  10. ^ Suplemento del procesador SPARC de interfaz binaria de aplicación System V (3 ed.).
  11. ^ "Suplemento de interfaz binaria de aplicación S/390 ELF".
  12. ^ "Suplemento de interfaz binaria de aplicación zSeries ELF".
  13. ^ Smith, Dr. Mike. "Comparación de registros SHARC (21k) y 68k".
  14. ^ XGCC: el sistema de lenguaje Gnu C/C++ para desarrollo integrado (PDF) . Corporación de herramientas de soporte integradas. 2000. pág. 59.
  15. ^ "COLDFIRE/68K: ThreadX para la familia Freescale ColdFire". Archivado desde el original el 2 de octubre de 2015.
  16. ^ Moshovos, Andreas. "Continuación de subrutinas: pasar argumentos, devolver valores y asignar variables locales". todos los registros excepto d0, d1, a0, a1 y a7 deben conservarse durante una llamada.
  17. ^ Corporación IBM (1967). IBM 1130 Disk Monitor System, versión 2 Introducción al sistema (C26-3709-0) (PDF) . pag. 67 . Consultado el 21 de diciembre de 2014 .
  18. ^ Corporación IBM (1968). Lenguaje ensamblador IBM 1130 (C26-5927-4) (PDF) . págs. 24 y 25.
  19. ^ Smotherman, Mark (2004). "Soporte de llamadas a procedimientos y subrutinas: historia temprana".
  20. ^ Rodríguez, Brad. "Avanzando, parte 1: decisiones de diseño en el cuarto núcleo". En el 6809 o Zilog Super8, el DTC es más rápido que el STC.
  21. ^ Ertl, Antón. "Velocidad de diversas técnicas de despacho de intérpretes".
  22. ^ Zaleski, Mateo (2008). "Capítulo 4: Diseño e implementación de una interpretación eficiente". YETI: un intérprete de trazas gradualmente extensible . Aunque se sabe que los intérpretes de subprocesos directos tienen malas propiedades de predicción de bifurcaciones... la latencia de una llamada y un retorno puede ser mayor que la de un salto indirecto.

enlaces externos