Codegen Register-Based (codegen_reg.rs)

Archivo: src/codegen_reg.rs (1060 líneas)

¿Por qué existe?

Genera código máquina x86-64 a partir del IR register-based después de la asignación de registros. Reemplaza al codegen stack-based para las funciones donde la asignación de registros produce código más eficiente: emite los bytes directamente a un buffer, sin dependencia de ensambladores externos ni librerías.

Arquitectura / Estructuras clave

La estructura CodegenReg mantiene el estado de emisión: asignaciones, buffer de bytes, labels, fixups de saltos, puntero a variables y frame de spill.

ComponenteDescripción
OperandOperando en código máquina: Reg(PhysReg) o Stack(offset) accedido como [RBP - (off+8)]
CodegenRegEstado: assignments, bytes, label_offsets, fixups, vars_ptr (RBX), spill_frame, call_addresses
resolve(vreg)Convierte un VirtReg en Operand según la asignación (RAX como fallback para dead code)
emit_program(prog, vars_ptr, spill_frame)Emite prólogo (push rbp; push rbx; push r14; mov rbp, rsp; cargar vars_ptr desde RCX y output desde RDX; sub rsp si hay spills), todos los bloques y resuelve los fixups de saltos
emit_instruction(&RegInstruction)Traduce cada instrucción a bytes (mov/imm64, aritmética, idiv con cqo, cmp+setCC+movzx, load/store a variables, jumps, return, call)
stub_nop_addr()Dirección de un stub extern "C" que retorna 0; se usa cuando un Call no encuentra la función en la tabla, evitando saltos a direcciones inválidas
emit_program(...) / emit_program_with_calls(...)Funciones públicas que compilan un RegProgram completo a Vec<u8> con o sin tabla de direcciones de funciones

Cómo se integra

Cierra el backend register-based:

Bytecode → (stack_to_reg) → IR register-based → (register_alloc) → asignaciones → (codegen_reg) → x86-64

Los Calls usan la convención del JIT (fn(vars_ptr: RCX, output: RDX) -> RAX): guarda los registros caller-saved en un frame temporal de 64 bytes alineado, escribe los argumentos en vars[0..nargs] (el callee los lee con LoadVar), carga la dirección desde call_addresses y emite call rax. El epílogo restaura rsp/r14/rbx/rbp y ejecuta ret.

Layout del frame: [rbp+16]=ret_addr, [rbp+8]=saved_r14, [rbp]=saved_rbx, saved_rbp, y desde [rbp-8] hacia abajo los slots de spill. Sin spills no se emite sub rsp.
Alcance actual: Genera x86-64 exclusivamente (el módulo usa REX, ModR/M y encodings específicos). El comentario del header menciona ARM64 como objetivo futuro, pero la implementación concreta es x86-64.
División: RegDiv sigue la convención rdx:rax / b → rax: mueve el dividendo a RAX, emite cqo (0x48 0x99) para extender signo y luego idiv.