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.
| Componente | Descripció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í: Jump → Opcode::Jump(offset); Branch → LoadIdx(cond) + JumpSiFalso(else) + Jump(then); Return → carga el valor y Opcode::Return; Unreachable → Halt.
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.