stringtranslate.com

Ensamblador en línea

En programación informática , 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 habría 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 razones: [1]

Por otra parte, 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 portabilidad y el mantenimiento futuros de un programa. [1]

A menudo se ofrecen funciones alternativas como una forma de simplificar el trabajo tanto del compilador como del programador. La mayoría de los compiladores ofrecen funciones intrínsecas para instrucciones especiales y en todas las plataformas Unix hay disponibles contenedores de funciones C para llamadas arbitrarias al sistema .

Sintaxis

En los estándares del lenguaje

El estándar ISO C++ y los estándares ISO C (anexo J) especifican una sintaxis compatible condicionalmente para el ensamblador en línea:

Una declaración asm tiene la forma
  asm-declaration :
     asm ( string-literal ) ;
La declaración asm tiene soporte condicional; su significado está definido por la implementación. [3]

Sin embargo, esta definición rara vez se utiliza en el lenguaje 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 la práctica, 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 sustituirlas como una extensión.

En general, hay dos tipos de ensamblaje en línea admitidos por los compiladores C/C++:

Las dos familias de extensiones representan diferentes entendimientos de la división del trabajo en el procesamiento de ensamblaje en línea. La forma GCC preserva la sintaxis general del lenguaje y compartimenta lo que el compilador necesita saber: qué se necesita y qué se cambia. No requiere explícitamente que el compilador entienda los nombres de las instrucciones, ya que el compilador solo necesita sustituir sus asignaciones de registros, más algunas operaciones mov , para manejar los requisitos de entrada. Sin embargo, el usuario es propenso a especificar registros clobbered incorrectamente. La forma MSVC de un lenguaje específico de dominio integrado proporciona facilidad de escritura, pero requiere que el compilador mismo conozca los nombres de los códigos de operación y sus propiedades clobbering, lo que exige una atención adicional en el mantenimiento y la portabilidad. [7] Todavía es posible verificar el ensamblaje de estilo GCC para detectar errores clobber con el conocimiento del conjunto de instrucciones. [8]

GNAT (el frontend del lenguaje Ada de la suite GCC) y LLVM utilizan la sintaxis GCC. [9] [10] El lenguaje de programación D utiliza un DSL similar a la extensión MSVC oficialmente para x86_64, [11] pero el LDC basado en LLVM también proporciona la sintaxis 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 que la versión LLVM (estilo GCC). Proporciona suficiente información para permitir la transformación del bloque en una función ensamblada externamente si el backend no puede manejar el ensamblaje integrado. [7]

Ejemplos

Una llamada al sistema en GCC

En general, no es posible llamar directamente a un sistema operativo en un sistema que utiliza memoria protegida. El sistema operativo se ejecuta en 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. Esto rara vez es una característica de un lenguaje de nivel superior, por lo que las funciones de envoltura para llamadas al sistema se escriben utilizando ensamblador en línea.

El siguiente ejemplo de código C muestra un contenedor de llamadas al sistema x86 en sintaxis de ensamblador de AT&T , utilizando el ensamblador GNU . Estas llamadas normalmente se escriben con la ayuda de macros; se incluye el código completo 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]

Para resumir, GCC admite tanto el ensamblador básico como el extendido . El primero simplemente pasa el texto textualmente al ensamblador, mientras que el segundo realiza algunas sustituciones de ubicaciones de registros. [4]

externo interno error ;  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 una salida "+" porque la llamada al sistema puede cambiarla] */ "+c" ( arg2 ), /* pasa arg2 en ecx ("c") [ídem] */ "+d" ( arg3 ) /* pasa arg3 en edx ("d") [ídem] */ : "a" ( num ) /* pasa el número de llamada del sistema en eax ("a") */ : "memory" , "cc" , /* anuncia al compilador que los códigos de memoria y condición han sido modificados */ "esi" , "edi" , "ebp" ); /* estos registros también son modificados [cambiados por la llamada al sistema] */                                        /* El sistema operativo devolverá un valor negativo en caso de error;  * los wrappers 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 ensamblaje en línea del lenguaje de programación D muestra el código que calcula la tangente de x utilizando las instrucciones FPU ( x87 ) de x86 .

// Calcular la tangente de x real tan ( real x ) { asm { fld x [ EBP ] ; // cargar x fxam ; // probar valores impares fstsw AX ; sahf ; jc trigerr ; // C0 = 1: x es NAN, infinito o vacío // 387 puede manejar desnormalizaciones SC18 : fptan ; fstp ST ( 0 ) ; // volcar X, que siempre es 1 fstsw AX ; sahf ; // si (!(fp_status & 0x20)) goto Lret jnp Lret ; // C2 = 1: x está fuera de rango, hacer reducción de argumentos fldpi ; // cargar 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 : devolver real.nan ; Lret : // No es necesario devolver nada manualmente ya que el valor ya está en la pila FP ; }                                                                  

Para los lectores no familiarizados con la programación x87, se utiliza el comando fstsw-sahf seguido de un salto condicional para acceder a los bits C0 y C2 de la palabra de estado de la FPU x87. fstsw almacena el estado en un registro de propósito general; sahf establece el registro FLAGS en los 8 bits superiores del registro; y el salto se utiliza para evaluar el bit de bandera que corresponda al bit de estado de la FPU. [14]

Referencias

  1. ^ ab "DontUseInlineAsm". Wiki de GCC . Consultado el 21 de enero de 2020 .
  2. ^ Striegel, Ben (13 de enero de 2020). ""Para un compilador, un fragmento de código ensamblador en línea es como una bofetada en la cara."". Reddit . Consultado el 15 de enero de 2020 .
  3. ^ C++, [dcl.asm]
  4. ^ ab "Asm extendido - Instrucciones de ensamblador con operandos de expresión C". Uso del compilador C de GNU . Consultado el 15 de enero de 2020 .
  5. ^ ab "Ensamblador en línea". docs.microsoft.com .
  6. ^ "Guía de migración y compatibilidad: ensamblaje en línea con Arm Compiler 6".
  7. ^ ab d'Antras, Amanieu (13 de diciembre de 2019). "Rust RFC-2873: ensamblaje en línea estable" . Consultado el 15 de enero de 2020 . Sin embargo, es posible implementar compatibilidad con ensamblaje en línea sin compatibilidad con el backend del compilador mediante el uso de un ensamblador externo.Solicitud de extracción para seguimiento de estado
  8. ^ "⚙ D54891 [RFC] Comprobación de la validez del ensamblaje en línea". reviews.llvm.org .
  9. ^ "Referencia del lenguaje LLVM: expresiones de ensamblaje en línea". Documentación de LLVM . Consultado el 15 de enero de 2020 .
  10. ^ "Ensamblaje en línea". Documentación de Rust (1.0.0) . Consultado el 15 de enero de 2020 .
  11. ^ "Ensamblador en línea". Lenguaje de programación D. Consultado el 15 de enero de 2020 .
  12. ^ "Expresiones de ensamblaje en línea de LDC". Wiki D . Consultado el 15 de enero de 2020 .
  13. ^ syscall(2)  –  Manual del programador de Linux – Llamadas al sistema
  14. ^ "FSTSW/FNSTSW — Almacenar palabra de estado de FPU x87". La forma FNSTSW AX de la instrucción se utiliza principalmente en la bifurcación condicional...

Enlaces externos