Una representación intermedia ( IR ) es la estructura de datos o código utilizado internamente por un compilador o máquina virtual para representar el código fuente . Un IR está diseñado para favorecer un procesamiento posterior, como la optimización y la traducción . [1] Un "buen" IR debe ser preciso – capaz de representar el código fuente sin pérdida de información [2] – e independiente de cualquier idioma fuente o de destino en particular. [1] Un IR puede tomar una de varias formas: una estructura de datos en memoria o un código especial basado en tupla o pila legible por el programa. [3] En este último caso también se le llama lengua intermedia .
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 gráfica intermedia que permite el análisis del flujo y la reorganización antes de la ejecución. El uso de una representación intermedia como esta permite que sistemas compiladores como GNU Compiler Collection y LLVM sean utilizados por 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ñada 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 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 aspectos fundamentales:
Un formato popular para lenguajes intermedios es el código de tres direcciones .
El término también se utiliza para referirse a los lenguajes utilizados como intermedios por algunos lenguajes de programación de alto nivel que no generan código objeto o de máquina, sino que generan únicamente el lenguaje intermedio. Este lenguaje intermedio se envía a un compilador para dicho lenguaje, que luego genera código objeto o 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 un lenguaje intermedio, la naturaleza de C como una abstracción de 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 Los dialectos de Lisp (Lush, Gambit ), Squeak 's Smalltalk-subset Slang, Nim , Cython , Seed7 , SystemTap , Vala , V y otros utilizan C como lenguaje intermedio. Se han diseñado variantes de C para proporcionar las características de C como lenguaje ensamblador portátil , incluido C-- y el lenguaje intermedio C.
Cualquier lenguaje dirigido a una máquina virtual o una máquina de código p puede considerarse un lenguaje intermedio:
La GNU Compiler Collection (GCC) utiliza varios lenguajes intermedios internamente para simplificar la portabilidad y la compilación cruzada . Entre estos idiomas se encuentran
GCC apoya la generación de estos RI, como objetivo final:
El marco del compilador LLVM se basa en el lenguaje intermedio LLVM IR , cuya representación serializada binaria compacta también se conoce como "código de bits" 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 representación intermedia de niveles múltiples ( 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 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 análisis de archivos binarios e ingeniería inversa. Utiliza los lenguajes intermedios ESIL [9] y REIL [10] para analizar archivos binarios.