Una representación intermedia ( IR ) es la estructura de datos o código utilizado internamente por un compilador o una máquina virtual para representar el código fuente . Una IR está diseñada para ser propicia para un procesamiento posterior, como la optimización y la traducción . [1] Una IR "buena" debe ser precisa (capaz de representar el código fuente sin pérdida de información [2] ) e independiente de cualquier lenguaje fuente o de destino en particular. [1] Una IR puede tomar una de varias formas: una estructura de datos en memoria o un código especial basado en tuplas o pilas legible por el programa. [3] En este último caso, también se le llama lenguaje intermedio .
Un ejemplo canónico se encuentra en la mayoría de los compiladores modernos. Por ejemplo, el intérprete CPython transforma el texto lineal legible por humanos que representa un programa en una estructura de gráfico intermedia que permite el análisis y reorganización del flujo antes de la ejecución. El uso de una representación intermedia como esta permite que los sistemas de compilación como GNU Compiler Collection y LLVM se utilicen en muchos lenguajes fuente diferentes para generar código para muchas arquitecturas de destino diferentes .
Un lenguaje intermedio es el lenguaje de una máquina abstracta diseñado para ayudar en el análisis de programas informáticos . El término proviene de su uso en compiladores , donde el código fuente de un programa se traduce a una forma más adecuada para las transformaciones que mejoran el código antes de usarse para generar código objeto o de máquina para una máquina de destino. El diseño de un lenguaje intermedio normalmente difiere del de un lenguaje de máquina práctico en tres formas fundamentales:
Un formato popular para idiomas intermedios es el código de tres direcciones .
El término también se utiliza para referirse a los lenguajes utilizados como intermediarios por algunos lenguajes de programación de alto nivel que no generan código objeto o de máquina por sí mismos, sino que generan únicamente el lenguaje intermedio. Este lenguaje intermedio se envía a un compilador para dicho lenguaje, que luego genera el código objeto o de máquina terminado. Esto generalmente se hace para facilitar el proceso de optimización o para aumentar la portabilidad mediante el uso de un lenguaje intermedio que tenga compiladores para muchos procesadores y sistemas operativos , como C. Los lenguajes utilizados para esto caen en complejidad entre los lenguajes de alto nivel y los lenguajes de bajo nivel , como los lenguajes ensambladores .
Aunque no está diseñado explícitamente como lenguaje intermedio, la naturaleza de C como una abstracción del ensamblador y su ubicuidad como lenguaje de sistema de facto en sistemas operativos tipo Unix y otros lo han convertido en un lenguaje intermedio popular: Eiffel , Sather , Esterel , algunos dialectos de Lisp (Lush, Gambit ), Slang , el subconjunto Smalltalk de Squeak, Nim , Cython , Seed7 , SystemTap , Vala , V y otros hacen uso de C como lenguaje intermedio. Se han diseñado variantes de C para proporcionar las características de C como lenguaje ensamblador portable , incluyendo C-- y el lenguaje intermedio C.
Cualquier lenguaje orientado a una máquina virtual o una máquina de código p puede considerarse un lenguaje intermedio:
La Colección de compiladores GNU (GCC) utiliza internamente varios lenguajes intermedios para simplificar la portabilidad y la compilación cruzada . Entre estos lenguajes se encuentran
El GCC apoya la generación de estos IR, como objetivo final:
El marco del compilador LLVM se basa en el lenguaje intermedio LLVM IR , cuya representación binaria serializada compacta también se conoce como "bitcode" y ha sido producida por Apple. [4] [5] Al igual que GIMPLE Bytecode, LLVM Bitcode es útil en la optimización del tiempo de enlace. Al igual que GCC, LLVM también apunta a algunos IR destinados a la distribución directa, incluidos PNaCl IR y SPIR de Google . Un desarrollo adicional dentro de LLVM es el uso de Multi-Level Intermediate Representation ( MLIR ) con el potencial de generar código para diferentes objetivos heterogéneos y combinar las salidas de diferentes compiladores. [6]
El lenguaje intermedio ILOC [7] se utiliza en clases sobre diseño de compiladores como un lenguaje de destino simple. [8]
Las herramientas de análisis estático suelen utilizar una representación intermedia. Por ejemplo, Radare2 es una caja de herramientas para el análisis de archivos binarios y la ingeniería inversa. Utiliza los lenguajes intermedios ESIL [9] y REIL [10] para analizar archivos binarios.