Mid-Level IR en SSA (ir.rs)

Archivo: src/ir.rs (462 líneas)

¿Por qué existe?

Define la representación intermedia (IR) en forma SSA (Static Single Assignment) que conecta el AST con el bytecode. En SSA, cada variable se asigna exactamente una vez, y los φ-nodes se encargan de fusionar valores en las uniones de control flow (join points). Esta forma simplifica el análisis y la optimización del código al eliminar la distinción entre asignaciones y usos.

Arquitectura / Estructuras clave

El IR se compone de identificadores numéricos (ValueId, BlockId, SymId), instrucciones Inst, bloques básicos BasicBlock, funciones SSA SsaFunction y un programa completo IrProgram con su tabla de símbolos.

ComponenteDescripción
ValueId / BlockId / SymIdAlias de usize que identifican valores, bloques básicos y símbolos respectivamente
IrTypeTipos del IR: Int, Float, Bool, Ptr, Void
InstEnumeración de instrucciones: constantes, Param, Phi, aritmética, comparación, lógica, Load/Store/Alloca, Jump/Branch/Return, Call, y operaciones de objetos y arrays
BasicBlockSecuencia de instrucciones sin branches internos; incluye id, instructions y un terminator
TerminatorInstrucción final que define el flujo de control: Jump, Branch, Return o Unreachable
SsaFunctionFunción SSA: nombre, tipo de retorno, bloques, bloque entry, número y tipos de parámetros
IrProgramPrograma completo: funciones + tabla de símbolos
SymbolTableInterning de nombres a SymId con intern() / name()
IrBuilderBuilder que emite instrucciones (emit_*), crea bloques (new_block) y terminadores (terminate_*), y construye SsaFunction con build_function
MemIdxÍndice de un slot de memoria de variable en el scope (para Load/Store)

Cómo se integra

Ocupa el centro del pipeline de compilación:

AST → (IrConstructor) → IR SSA → (IrOptimizer) → IR optimizado → (IrToBytecode) → Bytecode

El IrBuilder es la interfaz que ir_constructor.rs usa para bajar el AST a IR; ir_ssa.rs enriquece este IR con dominadores y φ-nodes; y ir_to_bytecode.rs lo consume para emitir opcodes.

SSA: El beneficio principal es que cada ValueId corresponde a una única definición estática, lo que hace los análisis de alcance (liveness, dominancia) triviales y habilita optimizaciones que son difíciles en una representación mutante.
Nota: Muchas variantes de Inst (objetos, arrays, saltos en terminadores) existen en la enumeración pero el constructor y el conversor a bytecode aún no las emiten completamente; están marcadas con #![allow(dead_code)].