A fast, extensible 64-bit virtual machine and assembler built as a compiler backend target.
Cortex-VM is a 64-bit virtual machine and assembler I built from scratch in C as a dedicated backend target for Marrow-Lang. Rather than targeting LLVM or x86 directly, I designed the entire ISA myself — a decision that kept the full system understandable, made the calling convention compiler-friendly, and gave me complete control over every design tradeoff.
The VM reaches around 400 million instructions per second at -O3 -march=native on modern hardware — roughly comparable to a Lua interpreter, and more than fast enough for a language runtime.
libcortex-vm.a) with a three-function API from day one.64 registers, each 64 bits wide. r0 (zero) is hardwired. r1–r3 are pc, sp, and ra. Fourteen callee-saved registers (s0–s13), fourteen argument/return registers (a0–a13), and thirty-two temporaries (t0–t31). Having 32 temporaries means register pressure is rarely the bottleneck for most function bodies.
All instructions are 64 bits wide. Seven base formats: R-type (register ALU), I-type (immediate ALU / jump), S-type (store), L-type (load), B-type (branch), and SYS (halt, syscall, nop, break). Calls and returns use a single jmp instruction that writes the return address into a register — no implicit link register behavior.
jmp ra, zero, target ; call — save return address in ra, jump to target
jmp zero, ra, 0 ; return — jump to address in ra
Two optional extensions, auto-detected by the assembler and flagged in the binary header:
mul, mulh, div, divu, rem, remu and their immediate variants. mulh produces the upper 64 bits of a 128-bit product via __int128_t.The assembler is single-pass with two-pass label resolution for forward references. It supports decimal, hex (0xFF), binary (0b1010), octal (0o17), and character literals. The .data section accepts strings, integers, and 64-bit float literals.
The disassembler is a full round-trip tool: assemble a program, disassemble the binary, re-assemble, and get a functionally equivalent binary. 42 pytest tests verify this fidelity.
// Assemble source to binary, optionally write to file
uint64_t *cortexAssemble(const char *source, const char *outputPath);
// Assemble and execute immediately
int cortexExecSource(const char *source);
// Execute a pre-assembled binary
int cortexExecBinary(const uint64_t *binary, size_t wordCount);
The compile-once-run-many workflow via cortexAssemble was designed specifically for the Marrow-Lang compiler pipeline.