Ir al contenido

VM y Bytecode

La VM de Achronyme es un intérprete de bytecode basado en registros. Ejecuta archivos .achb compilados usando una pila de tamaño fijo de 65,536 slots de Value.

Fuente (.ach)
│
▼
Compilador de Bytecode → Prototipos de función + bytecode
│
▼
Serializador → archivo binario .achb
│
▼
Cargador → heap + pila + frames de la VM
│
▼
Intérprete → Ejecución basada en registros
VM {
heap: Heap, // arenas tipadas + GC
stack: [Value; 65536], // pila de registros de tamaño fijo
frames: Vec<CallFrame>, // pila de llamadas
globals: Vec<GlobalEntry>, // variables globales
natives: Vec<NativeObj>, // 23 funciones integradas
open_upvalues: lista enlazada, // variables de pila capturadas
stress_mode: bool, // forzar GC en cada ciclo
}

Cada llamada a función apila un CallFrame:

CallFrame {
closure: u32, // handle a Closure en el heap
ip: usize, // puntero de instrucción
base: usize, // offset base en la pila
dest_reg: usize, // dónde almacenar valor de retorno
}

El registro R[i] en el frame actual mapea a stack[frame.base + i].

Los valores son enteros de 64 bits etiquetados — sin boxing para tipos comunes:

Bits 63..60 = etiqueta de 4 bits
Bits 59..0 = payload de 60 bits
EtiquetaNombrePayload
0INTentero con signo i60 (en línea)
1NIL—
2FALSE—
3TRUE—
4STRINGhandle u32 → arena de strings
5LISThandle u32 → arena de listas
6MAPhandle u32 → arena de mapas
7FUNCTIONhandle u32 → arena de funciones
8FIELDhandle u32 → arena de campos (BN254)
9PROOFhandle u32 → arena de pruebas
10NATIVEhandle u32 → tabla de nativos
11CLOSUREhandle u32 → arena de closures
12ITERhandle u32 → arena de iteradores

Los enteros (etiqueta 0) son el tipo de valor más común — usar la etiqueta 0 significa que no se necesita enmascaramiento para el caso común.

Rango de enteros: -2^59 a 2^59 - 1 (576,460,752,303,423,487). El desbordamiento genera IntegerOverflow.

Cada instrucción es un u32 en uno de dos formatos:

[opcode:8][A:8][B:8][C:8]

Usado para instrucciones de 3 operandos como Add R[A] = R[B] + R[C].

[opcode:8][A:8][Bx:16]

Usado para instrucciones con un operando de 16 bits, como LoadConst R[A] = K[Bx] o Jump IP = Bx.

OpcodeCódigoFormatoDescripción
LoadConst0ABxR[A] = K[Bx] — cargar del pool de constantes
LoadTrue1AR[A] = true
LoadFalse2AR[A] = false
LoadNil3AR[A] = nil
Move5ABR[A] = R[B]
OpcodeCódigoFormatoDescripción
Add10ABCR[A] = R[B] + R[C]
Sub11ABCR[A] = R[B] - R[C]
Mul12ABCR[A] = R[B] * R[C]
Div13ABCR[A] = R[B] / R[C]
Mod14ABCR[A] = R[B] % R[C]
Pow15ABCR[A] = R[B] ^ R[C]
Neg16ABR[A] = -R[B]
OpcodeCódigoFormatoDescripción
Eq20ABCR[A] = R[B] == R[C]
Lt21ABCR[A] = R[B] < R[C]
Gt22ABCR[A] = R[B] > R[C]
NotEq23ABCR[A] = R[B] != R[C]
Le24ABCR[A] = R[B] <= R[C]
Ge25ABCR[A] = R[B] >= R[C]
LogNot26ABR[A] = !R[B]
OpcodeCódigoFormatoDescripción
GetUpvalue34ABR[A] = Upvalue[B]
SetUpvalue35ABUpvalue[B] = R[A]
CloseUpvalue36ACerrar upvalue en slot de pila A
OpcodeCódigoFormatoDescripción
Return54ARetornar R[A] del frame actual
Call55ABCR[A] = Call(R[B], R[B+1]..R[B+C-1])
Closure56ABxR[A] = Closure(K[Bx])
OpcodeCódigoFormatoDescripción
Jump60BxIP = Bx
JumpIfFalse61ABxSi !R[A] entonces IP = Bx
GetIter65ABR[A] = Iterator(R[B])
ForIter66ABxSiguiente elemento o saltar a Bx
OpcodeCódigoFormatoDescripción
DefGlobalVar98ABxDefinir global mutable
DefGlobalLet99ABxDefinir global inmutable
GetGlobal100ABxR[A] = Global[K[Bx]]
SetGlobal101ABxGlobal[K[Bx]] = R[A]
Print102AImprimir R[A]
OpcodeCódigoFormatoDescripción
BuildList150ABCR[A] = [R[B]..R[B+C-1]]
BuildMap151ABCR[A] = {R[B]:R[B+1], ...}
GetIndex152ABCR[A] = R[B][R[C]]
SetIndex153ABCR[A][R[B]] = R[C]
OpcodeCódigoFormatoDescripción
Prove160—Compilar + verificar circuito ZK
OpcodeCódigoDescripción
Nop255Sin operación

Cada función compilada produce un struct Function en el heap:

Function {
name: String, // para depuración
arity: u8, // conteo de parámetros
max_slots: u16, // uso pico de registros
chunk: Vec<u32>, // instrucciones de bytecode
constants: Vec<Value>, // pool de constantes
upvalue_info: Vec<u8>, // pares [is_local, index]
line_info: Vec<u32>, // número de línea por instrucción
}

El vector line_info es paralelo a chunk — cada instrucción tiene un número de línea fuente para reporte de errores.

El formato de archivo .achb (versión 9):

Magic: b"ACH\x09" (4 bytes)
Metadata: max_slots (u16 LE)
Global Strings:
count (u32 LE)
por cada uno: length (u32 LE) + bytes UTF-8
Global Constants:
count (u32 LE)
por cada uno: tag (u8) + payload
INT (0): i64 LE
STRING (1): handle u32 LE
FIELD (8): 4 × u64 LE (limbs Montgomery)
NIL (255): (sin payload)
Prototypes:
count (u32 LE)
por cada uno:
name_len (u32 LE) + bytes del nombre
arity (u8)
max_slots (u16 LE)
const_count (u32 LE) + constantes
upvalue_count (u32 LE) + info de upvalue
bytecode_len (u32 LE) + instrucciones (u32 LE cada una)
Main Bytecode:
instruction_count (u32 LE)
instrucciones (u32 LE cada una)

El formato es compatible con el módulo loader de la VM, que deserializa en objetos del heap.

Índice: 0..22 23..
Nativos Globales de usuario

Los primeros 23 slots (0–22) están reservados para funciones nativas. Las globales definidas por el usuario comienzan en el índice 23 (USER_GLOBAL_START).

ComponenteArchivo
Opcodesvm/src/opcode.rs
Intérprete VMvm/src/machine/vm.rs
Frames de llamadavm/src/machine/frame.rs
Despacho de nativosvm/src/specs.rs
Codificación de valoresmemory/src/value.rs
Struct Functionmemory/src/heap.rs
Compilador de bytecodecompiler/src/codegen.rs
Compilador de funcionescompiler/src/function_compiler.rs
Serializador binariocli/src/commands/compile.rs
Cargador binariovm/src/loader.rs