tipos significado partes interprete informatica ejemplos compilar compiladores compilador compiler-construction assembly

compiler construction - significado - ¿Los compiladores de lenguaje de programación primero se traducen al ensamblaje o directamente al código de máquina?



partes de un compilador (12)

Estoy interesado principalmente en compiladores populares y ampliamente utilizados, como gcc. Pero si las cosas se hacen de manera diferente con diferentes compiladores, me gustaría saber eso también.

Tomando gcc como ejemplo, ¿compila un programa corto escrito en C directamente a código de máquina , o primero lo traduce a un ensamblado legible por humanos, y solo luego usa un ensamblador (incorporado?) Para traducir el programa de ensamblado a binario, código de máquina : ¿una serie de instrucciones para la CPU?

¿Usar código ensamblador para crear un ejecutable binario es una operación significativamente costosa? ¿O es algo relativamente simple y rápido de hacer?

(Supongamos que estamos tratando solo con la familia de procesadores x86, y todos los programas están escritos para Linux).

Estaría muy agradecido por cualquier ayuda y pensamiento sobre el asunto. ¡Gracias!


Aunque todos los compiladores no convierten el código fuente en un código de nivel intermedio, existe un puente para llevar el código fuente al código de nivel de máquina en varios compiladores.


Casi todos los compiladores, incluido gcc, producen código ensamblador porque es más fácil, tanto producir como depurar el compilador. Las principales excepciones suelen ser compiladores just-in-time o compiladores interactivos, cuyos autores no desean la sobrecarga del rendimiento o la molestia de bifurcar todo un proceso para ejecutar el ensamblador. Algunos ejemplos interesantes incluyen

  • Estándar ML de Nueva Jersey , que se ejecuta de forma interactiva y compila todas las expresiones sobre la marcha.

  • El compilador de tinycc , que está diseñado para ser lo suficientemente rápido como para compilar, cargar y ejecutar un script en C en menos de 100 milisegundos, y por lo tanto no desea la sobrecarga de llamar al ensamblador y al enlazador.

Lo que estos casos tienen en común es un deseo de respuesta "instantánea". Los ensambladores y los enlazadores son bastante rápidos, pero no lo suficientemente buenos para una respuesta interactiva. Todavía.

También hay una gran familia de idiomas, como Smalltalk, Java y Lua , que compilan en código de bytes, no en código de ensamblado, pero cuyas implementaciones pueden traducir ese bytecode directamente al código de máquina sin el beneficio de un ensamblador.

(Nota al pie: a principios de la década de 1990, Mary Fernandez y yo escribimos el New Jersey Machine Code Toolkit , cuyo code está en línea, que genera bibliotecas C que los compiladores pueden usar para omitir el ensamblador y el enlazador estándar. Mary lo usó para doblarlo aproximadamente la velocidad de su enlazador de optimización al generar a.out . Si no escribe en el disco, las aceleraciones son aún mayores ...)


En la mayoría de los compiladores de paso múltiple, el lenguaje ensamblado se genera durante los pasos de generación de código. Esto le permite escribir el lexer, la sintaxis y las fases semánticas una vez y luego generar el código ejecutable usando un solo backend ensamblador. esto se usa mucho en compiladores cruzados, tales como compiladores de C, que se generan para una gama de diferentes CPU.

Casi todos los compiladores tienen alguna forma de este si es un paso implicito o explicito.


GCC compila al ensamblador. Algunos otros compiladores no. Por ejemplo, LLVM-GCC compila a LLVM-assembly o LLVM-bytecode, que luego se compila en código máquina. Casi todos los compiladores tienen algún tipo de representación interna, LLVM-GCC usa LLVM y, IIRC, GCC usa algo llamado GIMPLE.


Hay muchas fases de compilación. En abstracto, está el front-end que lee el código fuente, lo divide en tokens y finalmente en un árbol de análisis sintáctico.

El back-end es responsable de generar primero un código secuencial como el código de tres direcciones, por ejemplo:

código:

x = y + z + w

dentro:

reg1 = y + z x = reg1 + w

Luego lo optimizamos, traduciéndolo en ensamblaje y finalmente en lenguaje de máquina. Todos los pasos se colocan en capas cuidadosamente para que, cuando sea necesario, uno de ellos pueda ser reemplazado


Los compiladores de Java compilan el código de bytes de Java (formato binario) y luego lo ejecutan usando una máquina virtual (jvm).

Si bien esto puede parecer lento, puede ser más rápido porque la JVM puede aprovechar las últimas instrucciones de la CPU y las nuevas optimizaciones. Un compilador de C ++ no hará esto: tiene que apuntar al conjunto de instrucciones en tiempo de compilación.


Los compiladores, en general, analizan el código fuente en un Árbol de sintaxis abstracta (un AST), y luego en un lenguaje intermedio. Solo entonces, generalmente después de algunas optimizaciones, emiten el idioma de destino.

Acerca de gcc, puede compilar a una amplia variedad de objetivos. No sé si para x86 se compila para ensamblar primero, pero le di una idea de los compiladores, y usted también lo pidió.


Ninguna de las respuestas aclara el hecho de que un ENSAMBLADOR es la primera capa de abstracción entre el CÓDIGO BINARIO y el CÓDIGO SÍMBOLO DEPENDIENTE DE LA MÁQUINA. Un compilador es la segunda capa de abstracción entre el CÓDIGO SÍMBOLO DEPENDIENTE DE LA MÁQUINA y el CÓDIGO SÍMBOLO INDEPENDIENTE DE LA MÁQUINA.

Si un compilador convierte directamente el código en código binario, por definición, se llamará ensamblador y no un compilador.

Es más apropiado decir que un compilador usa un CÓDIGO INTERMEDIO que puede o no ser un lenguaje ensamblador, por ejemplo, Java usa código de bytes como código intermedio y el código de bytes es ensamblador para la máquina virtual Java (JVM).

EDITAR: Puede preguntarse por qué un ensamblador siempre produce código dependiente de la máquina y por qué un compilador es capaz de producir código independiente de la máquina. La respuesta es muy simple. Un ensamblador es un mapeo directo del código de máquina y, por lo tanto, el lenguaje de ensamblaje que produce depende siempre de la máquina. Por el contrario, podemos escribir más de una versión de un compilador para diferentes máquinas. Entonces, para ejecutar nuestro código independientemente de la máquina, debemos compilar el mismo código pero en la versión del compilador escrita para esa máquina.



Según el capítulo 2 de Introducción al software de ingeniería inversa (por Mike Perry y Nasko Oskov), tanto gcc como cl.exe (el compilador de back-end para MSVC ++) tienen el modificador -S que puede usar para generar el ensamblado que produce cada compilador.

También puede ejecutar gcc en modo detallado ( gcc -v ) para obtener una lista de los comandos que ejecuta para ver lo que está haciendo detrás de las escenas.


Visual C ++ tiene un switch al código ensamblador de salida, así que creo que genera un código ensamblador antes de generar el código máquina.


gcc realmente produce ensamblador y lo ensambla usando el ensamblador. No todos los compiladores hacen esto: los compiladores de MS producen código de objeto directamente, aunque puede hacer que generen salida de ensamblador. Traducir el ensamblador al código objeto es un proceso bastante simple, al menos en comparación con la compilación.

Algunos compiladores producen otro código de lenguaje de alto nivel como resultado, por ejemplo, cfront , el primer compilador de C ++ produjo C como su salida, que luego compiló un compilador de C.

Tenga en cuenta que ni la compilación directa ni el ensamblaje realmente producen un ejecutable. Esto lo hace el enlazador , que toma los diversos archivos de código objeto producidos por compilación / ensamblaje, resuelve todos los nombres que contienen y produce el binario ejecutable final.