# UVM: a small virtual machine built to resist code rot

> UVM is a Rust virtual machine designed to run self-contained applications whose whole point is that they keep working, with a stack-based bytecode interpreter, variable-length instructions, a separate code and data address space, and a C compiler called uvclang that lowers C and C++ through LLVM IR. The author states plainly that it is a work in progress and that you will hit bugs and missing features.

**maximecb/uvm** — Fun, portable, minimalistic virtual machine.

- Repository: https://github.com/maximecb/uvm
- Stars: 625 · Forks: 23
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/maximecb-uvm

## The motivation is code rot, and it shapes every decision

The README opens with a warning that is worth taking seriously: this project is very much a work in progress, you will probably run into bugs and missing features, and the author would like collaborators who share the vision.

What follows is a statement of intent that is unusual in its specificity. UVM is intended as a platform to distribute programs that will not break and to combat code rot. It aims to be conceptually simple, easy to understand, easy to target, fun to work with and approachable to newcomers. It may also be valuable as a teaching tool or as a platform to experiment with. There is a four-minute overview video linked for a quick survey.

That framing explains choices that would otherwise look odd. An untyped design for simplicity, a stack-based bytecode interpreter, and an instruction set designed for straightforward JIT compilation to x86-64, arm64 and RV64 are all things a person teaching a virtual machine would choose, because each one is a concept you can hold in your head.

The code rot motivation is the more interesting half, and it is a technical problem rather than a nostalgic one. The promise is a distribution format for programs that keep working, and the mechanisms that deliver it are visible in the feature list: a Harvard architecture with separate flat, linear address spaces for code and data, little-endian byte ordering like x86, ARM and RISC-V, and 32-bit and 64-bit integer operations with 32-bit floating-point support.

The topics list gives the community context: bytecode, compiler, containerization, emulation, interpreter, jit-compiler, permacomputing, sandboxing, virtual-machine. Permacomputing is the term for making software that lasts decades, which is exactly the problem the project is aimed at.

The primary language is Rust, the licence is Apache-2.0, the default branch is main, and the last push was on 2026-10-09 with the project not archived. The repository publishes no GitHub releases.

## What the current feature set gives you

The current features list is compact and each entry is a design decision rather than a convenience.

The core is a stack-based bytecode interpreter with variable-length instructions for compactness and an untyped design for simplicity. Variable-length instruction encoding is the thing that makes a compact distribution format possible, and it is also what makes a JIT back end harder, which is a trade the README acknowledges implicitly by listing JIT compilation as a goal of the instruction set design rather than as an existing feature.

The instruction set is designed for straightforward JIT compilation to x86-64, arm64 and RV64, and the byte ordering is little-endian to match all three of those targets. If you are writing a compiler that targets UVM, that is the constraint set to design against.

The address space is Harvard-style: separate flat, linear address spaces for code and data. That is what makes self-contained application images practical, since code cannot be overwritten by a stray data write, and it is a natural fit for a system that wants to distribute a program as a file.

On the systems side, the project provides thread-based parallelism, an event-driven event execution model compatible with async operations, an easy to use frame buffer to draw RGB graphics with no boilerplate, an easy to use audio output API with no boilerplate, and a simple TCP networking API. The event model is the piece that decides whether an async application is pleasant or painful on this machine, and having it built in rather than emulated is a deliberate choice.

The built-in assembler with a simple syntax, at vm/src/asm.rs, and the examples directory of assembly programs mean the machine is usable without writing a compiler. The headline demonstration is that it can run Doom, in a separate repository, which is the kind of proof that a hobby virtual machine is real rather than a toy.

The planned features are where the direction points. A capability system to safely sandbox apps without granting access to the entire computer, the ability to compile without SDL and without graphics and audio for headless server-side use, the ability to encode metadata such as author name and app icon into app image files, and the ability to suspend running programs and save them to a new app image file. The last of those is the direct answer to code rot, since it lets a program carry its own state forward.

## Three subprojects, and the spec directory that ties them together

The repository is organised into subprojects, and the split is the clearest statement of the project's design.

The /vm directory is the virtual machine implementation itself, with /vm/examples holding example assembly programs that UVM can run. The /uvclang directory is a C and C++ compiler that outputs UVM assembly, with its own README and its own examples directory of C source files. The /spec directory is a system to document and automatically export bindings for UVM system calls and constants, and /docs holds the Markdown documentation, including the list of system calls and constants accessible to UVM programs.

The uvclang approach is the interesting engineering decision. The compiler drives clang to lower C and C++ to LLVM IR and then compiles that IR to UVM assembly. Because clang handles the parsing, it supports the full C and C++ language. The work in progress is in the LLVM IR to UVM back end, which does not yet cover every construct clang can emit.

Reusing clang buys a complete front end for the price of depending on a large C++ codebase, and it means the back end only has to deal with LLVM IR, which is a much smaller and more regular target than arbitrary C. The cost is stated honestly in the README: the back end is incomplete, and it is already usable to write real programs, shipping with a set of UVM bindings and standard-library headers.

The /spec directory is the piece that makes the other two coherent. It contains JSON files representing a declarative list of system calls, constants and the permission system UVM exposes to programs. The directory also contains code that automatically generates the Markdown documentation at docs/syscalls.md, Rust constants, and C definitions in uvclang/include/uvm/syscalls.h.

So the system call surface is defined once, in JSON, and everything else is generated from it. For a third-party compiler author, that is the contract to target: the specification is the API, and the VM, the documentation and the C headers are all derived from it.

## Building it, which is mostly about SDL2

The build dependencies are the Rust toolchain and the SDL2 libraries, and the per-platform instructions are given separately because the SDL2 step is the fiddly one.

On macOS, install the SDL2 package with Homebrew:

```sh
```sh
brew install sdl2
```
```

and then add the library path to your profile, because Homebrew does not put SDL2 where the linker looks by default:

```sh
```sh
export LIBRARY_PATH="$LIBRARY_PATH:$(brew --prefix)/lib"
```
```

On Debian and Ubuntu the package is named differently:

```sh
```sh
sudo apt-get install libsdl2-dev
```
```

On Windows the README points at the Rust toolchain instructions and then tells you to get SDL2.dll from the SDL2 releases and copy it, unzipped, into the vm/ folder. That last detail is the sort of thing that only appears in a project that has been built on Windows by a small number of people.

The Rust toolchain installation is the same everywhere:

```sh
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
```

Compiling the project is a cd into the vm directory and a cargo build:

```sh
```sh
cd vm
cargo build
```
```

Running an assembly example goes through cargo run with the example path:

```sh
```sh
cargo run examples/fizzbuzz.asm
```
```

And the C compiler has its own build and run script, driven from the uvclang directory:

```sh
```sh
cd uvclang
./build_and_run.sh examples/snake.c
```
```

uvclang uses clang as a front end, so it needs a working clang and LLVM installation, which the README notes and defers to the uvclang README for detail.

Tests run with cargo test from the vm and uvclang directories, and uvclang additionally has shell-based end-to-end test harnesses. The examples under vm/examples and uvclang/examples are shared under the Creative Commons CC0 licence, while the code for UVM, uvclang and the associated tools is under Apache-2.0.

## What the project is not, and how to read the warning

It is worth being clear about the shape of this project, because the README's warning and the feature list could easily be read as contradictory.

This is not a production virtual machine and not a drop-in replacement for the JVM, the CLR or wasmtime. It is a virtual machine designed to be conceptually simple and approachable, with a work in progress note at the top of the page and an explicit request for collaborators. The one-line description, fun, portable, minimalistic virtual machine, and the design goals of being easy to understand and easy to target say the same thing from the other direction.

The realistic uses line up with the stated goals. As a teaching tool, a machine whose entire system call surface is defined in a JSON file and whose documentation is generated from that file is a good thing to learn from, because a reader can go from the spec to the implementation without wading through a large codebase. As an experimentation platform, the spec directory is the interesting part, because writing a compiler that targets UVM means writing against a small, declarative, documented interface rather than against a proprietary runtime.

The portability claim also has a specific reading. The instruction set is designed for JIT compilation to x86-64, arm64 and RV64, and the byte order matches all three, so a program built for one is not tied to a host architecture in the way a native binary is. The Linux build depends on SDL2, and the headless compile without SDL and without graphics and audio is on the planned list rather than done, which means the server-side use case is the one still ahead.

The contribution guidance reflects the state. There is a lot of work to be done and contributions are welcome, with the suggested first steps being to read the open issues and the documentation, or to write new example programs that show what can be done, or to report bugs with as much context and the smallest reproduction as you can manage. There is even a search hint for finding work:

```sh
```sh
grep -IRi "todo" .
```
```

Smaller pull requests are easier to review and have a much higher chance of being merged. For a project at this stage that is the right thing to optimise for, and for a contributor it is a reminder that a small addition to the examples or the spec is worth more than an attempt at a large feature.

## Who should spend time on it, and who should not

The honest evaluation has three audiences.

For someone learning how virtual machines work, UVM is a good fit on the strength of its own stated aims. The spec directory, the generated documentation and the small example programs make it possible to read the machine from the top down, and the four-minute overview video is a reasonable way to decide whether to spend an hour with it.

For someone who wants to run a real program on it today, the answer depends on the program. The README says it can run Doom, and that uvclang is already usable to write real programs. So the capability is real, but the back end is incomplete, and the author says so. Expect to hit the missing constructs yourself.

For someone evaluating it as a platform for distributing long-lived software, the architecture looks right and the deliverable is not there yet. The capability system for sandboxing without granting access to the whole computer, the metadata in app images, and the suspend-and-save feature are exactly the features that make the code rot promise real, and all three are on the planned list. A platform you want to distribute applications on in 2026 is a different evaluation from a platform you want to contribute a small example program to.

The thing to check first is the two-way contract between the spec and the implementation. Because the system call list is declarative and everything else is generated, the spec is the API you target and the one place where behaviour is pinned down. If you are writing a compiler or a binding, start there rather than in the VM source, and expect to find the gaps by reading the spec rather than by reading the interpreter.

## Conclusion

Adopt UVM if you want to learn how a virtual machine fits together, if you want to write a compiler against a small declarative system call specification, or if you want to contribute examples and small fixes to a project that is explicitly looking for collaborators. Do not adopt it as a production runtime, because the author states that it is very much a work in progress with bugs and missing features, and the capability system, the headless build and the suspend-and-save feature that would make the code rot promise real are all still on the planned list. Before you commit, read docs/vision.md and docs/design.md, start from the spec directory rather than the VM source, and pick the uvclang back end only if your program stays inside the LLVM IR constructs it already handles.

## FAQ

### What is UVM and what problem is it meant to solve?

UVM is a minimalistic virtual machine written in Rust, intended as a platform to distribute programs that will not break and to combat code rot. It aims to be conceptually simple, easy to understand, easy to target, fun to work with and approachable to newcomers, and may also serve as a teaching tool or experimentation platform.

### What are UVM's main design choices?

A stack-based bytecode interpreter with variable-length instructions for compactness, an untyped design for simplicity, an instruction set designed for straightforward JIT compilation to x86-64, arm64 and RV64, little-endian byte ordering, 32-bit and 64-bit integer operations with 32-bit floating-point support, and separate flat linear address spaces for code and data in a Harvard architecture.

### How does the uvclang compiler work?

uvclang drives clang to lower C and C++ to LLVM IR and then compiles that IR to UVM assembly, so it supports the full C and C++ language. The work in progress is the LLVM IR to UVM back end, which does not yet cover every construct clang can emit, and it needs a working clang and LLVM installation.

### What is the spec directory in the UVM repository for?

It holds JSON files with a declarative list of the system calls, constants and permission system UVM exposes to programs, and code that automatically generates the Markdown documentation, Rust constants and C definitions from them. It is the contract to target if you are writing a compiler for UVM.

### How do I build UVM and run a test program?

You need the Rust toolchain and the SDL2 libraries, then cd into the vm directory and run cargo build. To run an assembly example, use cargo run with the example path, and the C compiler has a build_and_run.sh script in the uvclang directory. Tests run with cargo test from the vm and uvclang directories.

## Sources

- [Issues](https://github.com/maximecb/uvm/issues)
- [License: Apache-2.0](https://github.com/maximecb/uvm/blob/main/LICENSE)
- [maximecb/uvm on GitHub](https://github.com/maximecb/uvm)
- [README](https://github.com/maximecb/uvm/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/maximecb-uvm
