¿Por qué los compiladores producen código de ensamblaje?


19

El ensamblador convierte el lenguaje ensamblador en lenguaje máquina. ¿Por qué un compilador convertiría lenguaje de alto nivel a ensamblador? ¿No se puede convertir directamente del lenguaje de alto nivel al código de máquina?

Respuestas:


22

Otros motivos para que los compiladores produzcan ensamblajes en lugar del código de máquina adecuado son:

  • Las direcciones simbólicas utilizadas por los ensambladores en lugar de las direcciones de máquina de codificación rígida hacen que la reubicación del código sea ​​mucho más fácil.
  • El código de enlace puede implicar verificaciones de seguridad, como la verificación de tipo, y eso es más fácil de hacer con nombres simbólicos.
  • Los pequeños cambios en el código de la máquina son más fáciles de realizar cambiando el ensamblador en lugar del generador de código.

¿Por qué el lenguaje ensamblador es tan eficiente, aunque también está escrito en inglés y cómo lo entiende el procesador?
CODERSAM

3
@CODERSAM Assembly es un lenguaje formal, no un lenguaje natural. Está muy cerca del lenguaje de máquina. Por lo tanto, la traducción no introduce ineficiencias.
Martin Berger

cuando dices "muy cerca del lenguaje de máquina", ¿qué significa eso? Estoy realmente confundido con esto!
CODERSAM

2
@CODERSAM El significado preciso es complicado, pero algo así como el homomorfismo en álgebra. Cuando traduzca, diga "agregar eax, # 2", que es el ensamblado x86, puede traducirlo a d7f5 (o cualquier otra cosa que pueda ser el código de operación), inmediatamente, sin mirar el contexto, sin agregar más cosas. La asamblea no tiene abstracción.
Martin Berger

1
"El ensamblado no tiene abstracción": diría que los nombres de las etiquetas ya son una abstracción (de las compensaciones). Además, el contexto juega un papel importante: por ejemplo, add eax,2puede ser traducido a 83 c0 02o 66 83 c0 02, dependiendo de la última directiva producido como use16.
Ruslan

15

Un compilador generalmente convierte el código de alto nivel directamente al lenguaje de máquina, pero se puede construir de forma modular para que un back-end emita código de máquina y el otro código de ensamblaje (como GCC). La fase de generación de código produce "código", que es una representación interna del código de máquina, que luego debe convertirse a un formato utilizable como lenguaje de máquina o código de ensamblaje.


Además, si la fuente puede incluir algún código de ensamblaje, entonces debe estar disponible un mecanismo para traducir ese ensamblaje en línea de todos modos.
Paul A. Clayton

¿Por qué el lenguaje ensamblador es tan eficiente, aunque también está escrito en inglés y cómo lo entiende el procesador?
CODERSAM

1
El lenguaje ensamblador es una descripción "inglesa" del código de máquina.
Yuval Filmus

11

Históricamente, varios compiladores notables produjeron código de máquina directamente. Sin embargo, hay algunas dificultades para hacerlo. En general, a alguien que está tratando de confirmar que un compilador funciona correctamente le resultará más fácil examinar la salida del código de ensamblaje que el código de máquina. Además, es posible (y era históricamente común) usar un compilador C o Pascal de una pasada para producir un archivo en lenguaje ensamblador que luego puede procesarse usando un ensamblador de dos pasadas. Generar código directamente requeriría usar un compilador C o Pascal de dos pasos o bien usar un compilador de un solo paso seguido de algún medio de remendar direcciones de salto hacia adelante [si un entorno de tiempo de ejecución hace que el tamaño de un programa lanzado esté disponible en un lugar fijo, un compilador podría escribir una lista de parches al final del código y hacer que el código de inicio aplique esos parches en tiempo de ejecución; este enfoque aumentaría el tamaño del ejecutable en aproximadamente cuatro bytes por punto de parche, pero mejoraría la velocidad de generación del programa].

Si el objetivo es tener un compilador que se ejecute rápidamente, la generación directa de código puede funcionar bien. Sin embargo, para la mayoría de los proyectos, el costo de generar el código en lenguaje ensamblador y ensamblarlo realmente no es un problema importante hoy en día. Hacer que los compiladores produzcan código en una forma que pueda interactuar bien con el código producido por otros compiladores es generalmente un beneficio lo suficientemente grande como para justificar el aumento en los tiempos de compilación.


1

Incluso las plataformas que usan el mismo conjunto de instrucciones pueden tener diferentes formatos de archivo de objetos reubicables. Puedo pensar en "a.out" (UNIX temprano), OMF, MZ (MS-DOS EXE), NE (Windows de 16 bits), COFF (UNIX System V), Mach-O (OS X e iOS), y ELF (Linux y otros), así como variantes de ellos, como XCOFF (AIX), ECOFF (SGI) y ejecutable portátil (PE) basado en COFF en Windows de 32 bits. Un compilador que produce lenguaje ensamblador no necesita saber mucho sobre formatos de archivos de objetos, lo que permite al ensamblador y al enlazador encapsular ese conocimiento en un proceso separado.

Consulte también Diferencia entre OMF y COFF en Desbordamiento de pila.


1

Por lo general, los compiladores trabajan internamente con secuencias de instrucciones. Cada instrucción estará representada por una estructura de datos que representa su nombre de operación, operandos y demás. Cuando los operandos son direcciones, esas direcciones generalmente serán referencias simbólicas, no valores concretos.

El ensamblador de salida es relativamente simple. Se trata básicamente de tomar la estructura de datos internos del compilador y volcarla en un archivo de texto en un formato específico. La salida del ensamblador también es relativamente fácil de leer, lo cual es útil cuando necesita verificar lo que está haciendo el compilador.

La salida de archivos de objetos binarios es mucho más trabajo. El escritor del compilador necesita saber cómo están codificadas todas las instrucciones (que pueden estar lejos de ser triviales en algunos CPUS), necesitan convertir algunas referencias simbólicas en direcciones relativas de contador de programa y otras en alguna forma de metadatos en el archivo de objetos binarios. . Necesitan escribir todo en un formato que sea altamente específico del sistema.

Sí, puede crear un compilador que pueda generar objetos binarios directamente sin escribir el ensamblador como un paso intermedio. La pregunta como muchas otras cosas en el desarrollo de software es si la reducción en el tiempo de compilación vale el trabajo adicional de desarrollo y mantenimiento.

El compilador con el que estoy más familiarizado (freepascal) puede generar ensamblador en todas las plataformas, pero solo puede generar objetos binarios directamente en un subconjunto de plataformas.


1

Un compilador debe poder producir una salida de ensamblador además del código reubicable normal para beneficio del programador.

Una vez simplemente no encuentro el error en un programa C que se ejecuta en Unix System V en una máquina LSI-11. Nada parecía funcionar. Finalmente, desesperado, hice que el compilador de C protable excretara una versión ensambladora de su traducción. ¡Finalmente encontré el error! ¡El compilador estaba asignando más registros de los que existían en la máquina! (El compilador asignó registros R0 a R8 en una máquina con solo registros R0 a R7). Logré solucionar el error en el compilador y mi programa funcionó.

Otro beneficio de tener una salida de ensamblador es tratar de usar bibliotecas "estándar" que usan protocolos de paso de parámetros diferentes. Los compiladores de C posteriores me permiten establecer el protocolo con un parámetro ("pascal" haría que el compilador agregue los parámetros en el orden dado en oposición al estándar de C de invertir el orden).

Otro beneficio más es permitir que el programador vea qué trabajo terrible está haciendo su compilador. Una simple declaración en C toma alrededor de 44 instrucciones de máquina. Los valores se cargan de la memoria y luego se descartan rápidamente. etc, etc, etc ...

Personalmente, creo que tener un compilador en lugar de un módulo de objeto reubicable es realmente estúpido. Mientras compila su programa, el compilador recopila una gran cantidad de información sobre su programa. Por lo general, almacena toda esta información en algo llamado Tabla de símbolos. Después de excretar el código del ensamblador, arroja toda esta tabla de información. Luego, el ensamblador examina el código excretado y vuelve a recopilar parte de la información que el compilador ya tenía. Sin embargo, el ensamblador no sabe nada acerca de las declaraciones If de las declaraciones For o las declaraciones While. Entonces toda esta información falta. Luego, el ensamblador produce el módulo de objeto reubicable que el compilador no produjo.

¿¿¿Por qué???

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.