Asignación de Registros (register_alloc.rs)

Archivo: src/register_alloc.rs (482 líneas)

¿Por qué existe?

Implementa el algoritmo de Linear Scan Register Allocation (Poletto & Sarkar, 1999) para el JIT de Forja. Convierte los registros virtuales del IR register-based en registros físicos x86-64, con spill a stack cuando se agotan los registros disponibles.

Arquitectura / Estructuras clave

El módulo modela los registros físicos con sus encodings reales, las clases de registros, los intervalos de vida y el allocador propiamente dicho.

ComponenteDescripción
PhysRegRegistros físicos x86-64: RAX, RCX, RDX, RSI, RDI, R8-R11, callee-saved RBX/RBP/R12-R15 y XMM0-XMM7
PhysReg::rex_index()Índice real para REX encoding (0-15) de x86-64
is_callee_saved() / is_xmm()Clasificación de registros; modrm_byte y push_pop_byte ayudan al codegen
GP_REGS / CALLEE_SAVED / TEMP_REGS / EXTENDED_REGSConstantes: generales, callee-saved, temporales (caller-saved preferidos) y extendidos disponibles (R12, R13, R15; RBX/R14/RBP quedan reservados como punteros/frame)
VirtRegAlias usize de registro virtual
LiveIntervalRango de vida de un registro virtual: start, end (exclusive), reg_class y posiciones de uses para splitting
RegClassInteger (GP) o Float (XMM)
LocationUbicación asignada: Reg(PhysReg) o Stack(offset desde RBP)
RegisterAllocatorAllocador lineal: add_interval, allocate(), spill_count(), get_assignments()
split_intervalDivide intervalos con gaps de uso superiores a GAP_THRESHOLD (10) en sub-intervalos independientes para reducir spills

Cómo se integra

Recibe los LiveIntervals de register_ir.rs::compute_live_intervals, ejecuta el algoritmo y produce las asignaciones VirtReg → Location que codegen_reg.rs usa para emitir código nativo. El proceso: (1) aplicar interval splitting, (2) ordenar por start, (3) para cada intervalo expirar los viejos, buscar registro libre (temporales primero, luego extendidos; XMM para float) y si no hay, spillear el que termina más tarde con swap si conviene.

Tabla de registros x86-64: RAX (resultado), RCX/RDX/RSI/RDI (args), R8-R11 (args 5-8 y temporales), todos caller-saved; RBX, RBP, R12-R15 callee-saved. Cada slot de spill ocupa 8 bytes (64-bit).
Interval splitting: Si un intervalo tiene usos separados por más de GAP_THRESHOLD instrucciones, se parte en sub-intervalos, cada uno con su propia asignación (registro o stack). Esto evita mantener un registro ocupado durante regiones donde la variable no se usa.