En la programación de computadoras , un ensamblador en línea es una característica de algunos compiladores que permite que el código de bajo nivel escrito en lenguaje ensamblador se incruste dentro de un programa, entre el código que de otro modo se ha compilado a partir de un lenguaje de nivel superior como C o Ada .
Motivación y alternativas
La incrustación de código en lenguaje ensamblador generalmente se realiza por una de estas tres razones: [1]
- Optimización : los programadores pueden usar código en lenguaje ensamblador para implementar las partes más sensibles al rendimiento de los algoritmos de su programa , código que puede ser más eficiente que el que podría generar el compilador.
- Acceso a instrucciones específicas del procesador : la mayoría de los procesadores ofrecen instrucciones especiales, como comparar e intercambiar y probar y configurar , que pueden usarse para construir semáforos u otras primitivas de sincronización y bloqueo. Casi todos los procesadores modernos tienen estas instrucciones u otras similares, ya que son necesarias para implementar la multitarea . Ejemplos de instrucciones especializadas se encuentran en el SPARC VIS , Intel MMX y SSE , y Motorola Altivec conjuntos de instrucciones .
- Acceso a convenciones de llamadas especiales que el compilador aún no admite.
- Llamadas e interrupciones al sistema: los lenguajes de alto nivel rara vez tienen una facilidad directa para realizar llamadas arbitrarias al sistema, por lo que se utiliza código ensamblador. Las interrupciones directas se suministran incluso con menos frecuencia.
- Para emitir directivas especiales para el enlazador o ensamblador, por ejemplo, para cambiar secciones, macros o crear alias de símbolos.
Por otro lado, el ensamblador en línea plantea un problema directo para el propio compilador, ya que complica el análisis de lo que se hace con cada variable, una parte clave de la asignación de registros. [2] Esto significa que el rendimiento podría disminuir. El ensamblador en línea también complica la adaptación y el mantenimiento futuros de un programa. [1]
A menudo se proporcionan recursos alternativos como una forma de simplificar el trabajo tanto para el compilador como para el programador. La mayoría de los compiladores proporcionan funciones intrínsecas para instrucciones especiales y las envolturas de funciones C para llamadas arbitrarias al sistema están disponibles en todas las plataformas Unix .
Sintaxis
En estándares de lenguaje
El estándar ISO C ++ y los estándares ISO C (anexo J) especifican una sintaxis admitida condicionalmente para ensamblador en línea:
Una declaración asm tiene la forma
declaración-
asm : asm ( literal-cadena );
La declaración asm se admite condicionalmente; su significado está definido por la implementación. [3]
Esta definición, sin embargo, rara vez se usa en C real, ya que es simultáneamente demasiado liberal (en la interpretación) y demasiado restringida (en el uso de un solo literal de cadena).
En compiladores reales
En el uso práctico, el ensamblaje en línea que opera con valores rara vez es independiente como código flotante. Dado que el programador no puede predecir a qué registro se asigna una variable, los compiladores suelen proporcionar una forma de sustituirlos como una extensión.
En general, hay dos tipos de ensamblados en línea compatibles con compiladores C / C ++:
- asm (o __asm__ ) en GCC . GCC utiliza una extensión directa de las reglas ISO: la plantilla de código de ensamblaje se escribe en cadenas, con entradas, salidas y registros agrupados especificados después de las cadenas en dos puntos. Las variables C se utilizan directamente, mientras que los nombres de los registros se citan como cadenas literales. [4]
- __asm en Microsoft Visual C ++ (MSVC), compilador Borland / Embarcadero C y descendientes. Esta sintaxis no se basa en absoluto en las reglas ISO; los programadores simplemente escriben ASM dentro de un bloque sin necesidad de ajustarse a la sintaxis C. Las variables están disponibles como si fueran registros y se permiten algunas expresiones C. [5] El compilador ARM solía tener una instalación similar. [6]
Las dos familias de extensiones representan diferentes entendimientos de la división del trabajo en el procesamiento del ensamblaje en línea. El formulario GCC conserva la sintaxis general del lenguaje y compartimenta lo que el compilador necesita saber: lo que se necesita y lo que se cambia. No requiere explícitamente que el compilador comprenda los nombres de las instrucciones, ya que el compilador solo es necesario para sustituir sus asignaciones de registro, más algunas operaciones mov , para manejar los requisitos de entrada. Sin embargo, el usuario es propenso a especificar registros maltratados incorrectamente. La forma MSVC de un lenguaje específico de dominio integrado proporciona facilidad de escritura, pero requiere que el propio compilador conozca los nombres de los códigos de operación y sus propiedades de bloqueo, lo que exige una atención adicional en el mantenimiento y la migración. [7] Aún es posible verificar el ensamblaje de estilo GCC en busca de errores de golpe con el conocimiento del conjunto de instrucciones. [8]
GNAT (interfaz de lenguaje Ada de la suite GCC) y LLVM usa la sintaxis GCC. [9] [10] El lenguaje de programación D usa un DSL similar a la extensión MSVC oficialmente para x86_64, [11] pero el LDC basado en LLVM también proporciona la sintaxis de estilo GCC en cada arquitectura. [12] MSVC solo admite ensamblador en línea en x86 de 32 bits. [5]
Desde entonces, el lenguaje Rust ha migrado a una sintaxis que abstrae las opciones de ensamblaje en línea más allá de la versión LLVM (estilo GCC). Proporciona suficiente información para permitir transformar el bloque en una función ensamblada externamente si el backend no puede manejar el ensamblaje integrado. [7]
Ejemplos de
Una llamada al sistema en GCC
Generalmente, no es posible llamar directamente a un sistema operativo en un sistema que utiliza memoria protegida. El sistema operativo se ejecuta a un nivel más privilegiado (modo kernel) que el usuario (modo usuario); Se utiliza una interrupción (de software) para realizar solicitudes al sistema operativo. Esta rara vez es una característica en un lenguaje de nivel superior, por lo que las funciones contenedoras para las llamadas al sistema se escriben usando ensamblador en línea.
El siguiente ejemplo de código C muestra un contenedor de llamadas del sistema x86 en la sintaxis del ensamblador de AT&T , utilizando GNU Assembler . Estas llamadas se escriben normalmente con la ayuda de macros; el código completo está incluido para mayor claridad. En este caso particular, el contenedor realiza una llamada al sistema de un número dado por el llamador con tres operandos, devolviendo el resultado. [13]
En resumen, GCC admite el ensamblaje básico y extendido . El primero simplemente pasa el texto literalmente al ensamblador, mientras que el segundo realiza algunas sustituciones de las ubicaciones de los registros. [4]
extern int errno ;int syscall3 ( int num , int arg1 , int arg2 , int arg3 ) { int res ; __asm__ ( "int $ 0x80" / * realiza la solicitud al SO * / : "= a" ( res ), / * devuelve el resultado en eax ("a") * / "+ b" ( arg1 ), / * pasa arg1 en ebx ("b") [como salida "+" porque la llamada al sistema puede cambiarlo] * / "+ c" ( arg2 ), / * pasar arg2 en ecx ("c") [ídem] * / "+ d " ( arg3 ) / * pasar arg3 en edx (" d ") [ídem] * / : " a " ( num ) / * pasar el número de llamada del sistema en eax (" a ") * / : " memoria " , " cc " , / * anuncia al compilador que la memoria y los códigos de condición han sido modificados * / " esi " , " edi " , " ebp " ); / * estos también están aplastados * / / * El sistema operativo devolverá un valor negativo en caso de error; * los contenedores devuelven -1 en caso de error y establecen la variable global errno * / if ( -125 <= res && res < 0 ) { errno = - res ; res = -1 ; } return res ; }
Instrucción específica del procesador en D
Este ejemplo de línea de montaje del lenguaje de programación D de código muestra que calcula la tangente de x utilizando la X 86 's FPU ( x87 instrucciones).
// Calcula la tangente de x real tan ( real x ) { asm { fld x [ EBP ] ; // cargar x fxam ; // prueba para valores extraños fstsw AX ; sahf ; jc trigerr ; // C0 = 1: x es NAN, infinito o vacío // Los 387 pueden manejar desnormales SC18 : fptan ; fstp ST ( 0 ) ; // volcar X, que siempre es 1 fstsw AX ; sahf ; // if (! (fp_status & 0x20)) goto Lret jnp Lret ; // C2 = 1: x está fuera de rango, hacer reducción de argumento fldpi ; // carga pi fxch ; SC17 : fprem1 ; // recordatorio (parcial) fstsw AX ; sahf ; jp SC17 ; // C2 = 1: recordatorio parcial, es necesario realizar un bucle fstp ST ( 1 ) ; // eliminar pi de la pila jmp SC18 ; } trigerr : devuelve real . nan ; LRet : ; }
Para los lectores que no estén familiarizados con la programación x87, fstsw-sahf seguido del modismo de salto condicional se utiliza para acceder a los bits C0 y C2 de la palabra de estado x87 FPU. fstsw almacena el estado en un registro de propósito general; sahf establece el registro FLAGS en los 8 bits más altos del registro; y el salto se usa para juzgar cualquier bit de bandera que corresponda al bit de estado de la FPU. [14]
Referencias
- ^ a b "DontUseInlineAsm" . Wiki de GCC . Consultado el 21 de enero de 2020 .
- ^ Striegel, Ben. " " Para un compilador, una gota de ensamblaje en línea es como una bofetada en la cara. " " . Reddit . Consultado el 15 de enero de 2020 .
- ^ C ++, [dcl.asm]
- ^ a b "Extended Asm - Instrucciones de ensamblador con operandos de expresión C" . Usando el compilador de C de GNU . Consultado el 15 de enero de 2020 .
- ^ a b "Ensamblador en línea" . docs.microsoft.com .
- ^ https://www.keil.com/support/man/docs/armclang_mig/armclang_mig_ioz1485879652178.htm
- ^ a b d'Antras, Amanieu (13 de diciembre de 2019). "Rust RFC-2873: conjunto en línea estable" . Consultado el 15 de enero de 2020 .
Sin embargo, es posible implementar soporte para ensamblado en línea sin soporte del backend del compilador usando un ensamblador externo en su lugar.
Solicitud de extracción para seguimiento de estado - ^ "⚙ D54891 [RFC] Comprobando la validez del ensamblaje en línea" . reviews.llvm.org .
- ^ "Referencia del lenguaje LLVM: expresiones de ensamblaje en línea" . Documentación LLVM . Consultado el 15 de enero de 2020 .
- ^ "Montaje en línea" . Documentación de óxido (1.0.0) . Consultado el 15 de enero de 2020 .
- ^ "Ensamblador en línea" . Lenguaje de programación D . Consultado el 15 de enero de 2020 .
- ^ "Expresiones de ensamblaje en línea de LDC" . D Wiki . Consultado el 15 de enero de 2020 .
- ^ - Manual del programador de Linux - Llamadas al sistema
- ^ "FSTSW / FNSTSW - Almacenar palabra de estado FPU x87" .
La forma FNSTSW AX de la instrucción se utiliza principalmente en la ramificación condicional ...
enlaces externos
- GCC-Ensamblaje-en-línea-CÓMO
- Ensamblado en línea de Clang
- Ensamblador en línea GNAT
- Referencia del ensamblador en línea de GCC
- Explorador del compilador