IR → Bytecode (ir_to_bytecode.rs)

Archivo: src/ir_to_bytecode.rs (238 líneas)

¿Por qué existe?

Cierra la fase de frontend del pipeline: convierte el IR SSA optimizado al bytecode stack-based de Forja (los Opcodes que interpreta la VM). Recorre los bloques del IR y emite la secuencia de opcodes correspondiente, incluyendo la resolución de saltos entre bloques.

Arquitectura / Estructuras clave

La estructura IrToBytecode mantiene el mapeo entre valores SSA y variables del bytecode, y entre bloques y offsets para los jumps.

ComponenteDescripción
value_to_var: HashMap<ValueId, usize>Mapea cada valor SSA a su índice de variable en flat_vars
block_offsets: HashMap<BlockId, usize>Offset de cada bloque dentro del bytecode, usado para emitir Jump y JumpSiFalso
convert_function(&SsaFunction) -> Vec<Opcode>Convierte una función completa en dos pasadas: estimar offsets y emitir instrucciones
emit_inst(...)Traduce cada Inst a opcodes (constantes → DeclareIdxGlobal + Push* + StoreIdx; aritmética → LoadIdx + opcode; llamadas → LoadIdx en orden inverso + Call)
alloc_var()Asigna el siguiente índice de variable

Cómo se integra

Es el último eslabón de la cadena AST → IrConstructor → IR SSA → IrOptimizer → IrToBytecode → Bytecode. Los terminadores se emiten así: JumpOpcode::Jump(offset); BranchLoadIdx(cond) + JumpSiFalso(else) + Jump(then); Return → carga el valor y Opcode::Return; UnreachableHalt.

Dos pasadas: La primera pasada estima los offsets de bloques (~3 opcodes por instrucción + 2 por terminador); la segunda emite el bytecode definitivo y usa block_offsets para resolver los saltos.
Estado parcial: Los φ-nodes se omiten en la emisión ("se resuelven durante la construcción SSA") y muchas instrucciones (objetos, arrays, Mod, Neg, comparaciones no-Eq/Lt) caen en el caso _ => {} y aún no generan bytecode.