Backend LLVM
Archivo: src/backend_llvm.rs (871 líneas)
¿Por qué existe?
Backend que genera código máquina nativo usando LLVM como infraestructura. Soporta tipos básicos, funciones, clases/structs, enums y closures. A diferencia del JIT de jit.rs (que emite bytes x86-64 a mano), este backend serializa el programa a LLVM IR textual y delega las optimizaciones y la generación de código de bajo nivel a LLVM.
AST → (type checker) → AST tipado → LlvmBackend → LLVM IR → ObjectCode (.o) Arquitectura / Estructuras clave
| Componente | Descripción |
|---|---|
LlvmType | Tipos LLVM: I8, I64, Double, I1 (bool), Ptr, Void, Struct(name, campos) y Array(tipo, len). to_llvm_ir() serializa a texto IR; is_pointer() detecta tipos por referencia |
LlvmValue | Resultado de una instrucción: nombre SSA (%t0, %r, …) + tipo |
LlvmInstruction | IR instrucción por instrucción: Add/Sub/Mul/Div, Icmp/Fcmp (con ops IcmpOp/FcmpOp), And/Or/Xor, Alloca/Store/Load, Call, Br/BrCond, Ret, Gep, Bitcast, Phi |
LlvmBlock | Bloque básico: nombre + lista de instrucciones |
LlvmFunction | Función: nombre, tipo de retorno, parámetros, bloques, y flag is_declaration (declarada externa vs. definida) |
LlvmModule | Módulo completo: funciones, tipos globales (global_types) y strings globales (global_strings). to_llvm_ir() emite el target datalayout, el target triple y todo el IR |
LlvmBackend | Generador principal: new_temp()/new_block_label() para nombres SSA únicos, register_class() (clase → struct), add_string_literal(), e inyección de GC intrinsics |
GC Intrinsics
inject_gc_intrinsics() analiza las funciones del módulo y declara + inyecta las intrínsecas LLVM GC:
| Intrínseca | Propósito |
|---|---|
llvm.experimental.gc.statepoint | Marca safe points para el recolector |
llvm.experimental.gc.relocate | Reubica punteros después de una colección |
__gc_write_barrier | Write barrier para el generational GC |
__gc_poll | Polling check para el GC thread (safe point en loops) |
__gc_alloc | Asignación de memoria con soporte GC |
inject_function_gc() detecta loops buscando BrCond cuyos targets apuntan a bloques anteriores y, al encontrarlos, inserta una llamada a __gc_poll al inicio del bloque junto con un StackMap y un SafePoint registrados en el GcIntrinsicsManager (de gc_intrinsics.rs). to_llvm_ir_with_gc() aplica toda la inyección y serializa.
Layouts para enums, genéricos, closures y async
| Layout | Representación LLVM |
|---|---|
EnumLayout | Tagged union: %Enum = { i8, [max(tamaños) x i8] } con tag_for(variante) y to_llvm_type() |
result_type(T, E) | Result[T,E] = { i8 tag, payload } (tag 0 = Ok, 1 = Error) |
option_type(T) | Option[T] = { i1 is_some, T value } |
ClosureLayout | Closure = { fn_ptr, env_ptr, env_size: i64 } |
AsyncLayout | State machine async: { state: i64, resume_fn: ptr, vars capturadas... } con add_captured_var() |
Optimización
| Componente | Descripción |
|---|---|
OptLevel | Niveles None (debug), Less (-O1), Default (-O2) y Aggressive (-O3) |
OptLevel::to_passes() | Passes por nivel: always-inline, promote-memory-to-register, gvn, licm, loop-unroll, loop-vectorize, slp-vectorize, simplifycfg, merge-functions |
LlvmOptPipeline | Ejecuta los passes y acumula OptStats (funciones optimizadas, constantes plegadas, dead code eliminado) |
Builtins del runtime
declare_builtins(module) declara las funciones del runtime Forja como externas: forja_escribir_entero, forja_escribir_decimal, forja_escribir_texto, forja_leer, malloc, free y memcpy.
Target triple
target_triple() detecta la plataforma en compilación: x86_64-pc-windows-msvc (Windows), x86_64-unknown-linux-gnu (Linux), arm64-apple-darwin (macOS) o x86_64-unknown-unknown como fallback.
#![allow(dead_code)]: varias piezas (pipeline de passes real, alocación de ObjectCode) están esbozadas y listas para conectarse con la infraestructura LLVM cuando se integre la dependencia externa.