¿Por qué la representación intermedia de LLVM (LLVM IR) es similar a un ensamblado en lugar de un árbol?
Alternativamente, ¿por qué las implementaciones de lenguaje se dirigen a LLVM IR en lugar de AST de clang?
No estoy tratando de hacer dos preguntas diferentes a la vez si parece así. Para mí, simplemente parece que tanto los programadores de cliente como de biblioteca han llegado al consenso de que la API de LLVM, nada más y nada menos, es obviamente un buen diseño de software y mi pregunta es "¿por qué?".
La razón por la que pregunto es que parece que LLVM podría proporcionar más funcionalidad a las interfaces si su IR era similar a AST porque entonces las herramientas basadas en AST de clang podrían usarse para cualquier interfaz. Alternativamente, los idiomas que se dirigen a LLVM IR podrían obtener más funcionalidad si se dirigen a AST de clang.
Clang tiene clases y funciones para crear y trabajar con AST y es el único proyecto frontend que está fuertemente vinculado al proyecto LLVM, entonces, ¿por qué la funcionalidad AST de clang es externa a LLVM?
Fuera de mi cabeza, sé que Rust (rustc), D (ldc) y Haskell (GHC) pueden usar LLVM como back-end, pero no usan Clang AST (que yo sepa, podría estar equivocado). No conozco todos los detalles internos de estos compiladores, pero al menos Rust y D ciertamente parecen que podrían compilarse para AST de clang. Quizás Haskell también podría, pero estoy mucho menos seguro de eso.
¿Esto se debe a razones históricas (LLVM originalmente era una "máquina virtual de bajo nivel" y el sonido metálico surgió más tarde)? ¿Es esto porque otras interfaces quieren tener el mayor control posible sobre lo que alimentan a LLVM? ¿Existen razones fundamentales por las que el AST de clang no es apropiado para lenguajes "que no sean C"
No pretendo que esta pregunta sea un ejercicio de lectura mental. Solo quiero que sea útil para aquellos de nosotros que tenemos curiosidad sobre el diseño del compilador, pero que aún no lo somos. Dado que los proyectos LLVM y clang se desarrollan en público, espero que alguien familiarizado con el desarrollo de estos proyectos pueda responder o que la respuesta sea lo suficientemente obvia para algunos nerds de compilación que se sientan lo suficientemente seguros como para responder.
Para evitar algunas respuestas obvias pero insatisfactorias:
Sí, tener un IR similar a un ensamblaje le da más control a quien crea el IR (tal vez X lang tenga una mejor base de código y formato AST que clang), pero si esa es la única respuesta, la pregunta es "¿por qué LLVM solo tiene un ensamblado? como IR en lugar de un IR de árbol de alto nivel y un IR de montaje de bajo nivel ".
Sí, no es tan difícil analizar un lenguaje de programación en un AST (al menos en comparación con los otros pasos de compilación). Aun así, ¿por qué usar AST separados? Por lo menos, usar el mismo AST le permite usar herramientas que funcionan en AST (incluso cosas simples como impresoras AST).
Sí, estoy totalmente de acuerdo en que ser más modular es algo bueno, pero si esa es la única razón, ¿por qué las implementaciones de otros idiomas tienden a apuntar a LLVM IR en lugar de AST de clang?
Estos anticipos pueden ser erróneos o pasar por alto detalles, así que siéntase libre de dar estas respuestas si tiene más detalles o mis suposiciones están equivocadas.
Para cualquiera que quiera responder una pregunta más definitiva: ¿cuáles son las ventajas y desventajas de un IR tipo ensamblaje frente a un IR tipo árbol?
fuente
Respuestas:
Aquí hay una serie de preguntas relacionadas entre sí, intentaré separarlas lo mejor que pueda.
¿Por qué otros lenguajes se basan en LLVM IR y no suenan AST?
Esto se debe simplemente a que clang es una interfaz C / C ++ y el AST que produce está estrechamente acoplado a C / C ++. Otro lenguaje podría usarlo, pero necesitaría una semántica casi idéntica a algún subconjunto de C / C ++, lo cual es muy limitante. Como señala, el análisis de un AST es bastante sencillo, por lo que es poco probable que valga la pena ahorrar un poco en restringir sus elecciones semánticas.
Sin embargo, si está escribiendo herramientas para C / C ++, por ejemplo, analizadores estáticos, reutilizar el AST tiene mucho sentido, ya que es mucho más fácil trabajar con el AST que el texto sin formato si está trabajando con C / C ++ .
¿Por qué LLVM IR es la forma que es?
Se eligió LLVM IR como una forma apropiada para escribir optimizaciones del compilador. Como tal, su característica principal es que está en forma SSA . Es un IR de nivel bastante bajo, por lo que es aplicable a una amplia gama de idiomas, por ejemplo, no escribe memoria, ya que esto varía mucho entre idiomas.
Ahora, resulta que escribir optimizaciones de compiladores es una tarea bastante especializada y a menudo es ortogonal al diseño de características del lenguaje. Sin embargo, tener un lenguaje compilado que se ejecute rápidamente es un requisito bastante general. Además, la conversión de LLVM IR a ASM es bastante mecánica y, en general, tampoco es interesante para los diseñadores de idiomas.
Por lo tanto, reducir un lenguaje a LLVM IR le da al diseñador de lenguaje muchas "cosas gratis" que son muy útiles en la práctica, dejándolo concentrado en el lenguaje mismo.
¿Sería útil un IR diferente (OK, no preguntado pero implícito)?
¡Absolutamente! Los AST son bastante buenos para ciertas transformaciones en la estructura del programa, pero son muy difíciles de usar si desea transformar el flujo del programa. Un formulario SSA es generalmente mejor. Sin embargo, LLVM IR tiene un nivel muy bajo, por lo que se pierde gran parte de la estructura de alto nivel (a propósito, por lo que es de aplicación más general). Tener un IR entre el AST y el IR de bajo nivel puede ser beneficioso aquí. Tanto Rust como Swift adoptan este enfoque y tienen un IR de alto nivel entre los dos.
fuente