jonah merriam ·

Cortex-VM

A fast, extensible 64-bit virtual machine and assembler built as a compiler backend target.

C · VM · Assembler · ISA Design

word-addressed ISA
64-bit
registers
64
instructions/sec
~400M
passing tests
201

Overview

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.

Design Principles

The Register File

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.

Instruction Set

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

Extensions

Two optional extensions, auto-detected by the assembler and flagged in the binary header:

Assembler & Disassembler

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.

Library API

// 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.