vas - x86-64 Assembler written in V
June 13, 2026 · View on GitHub
vas is a small x86-64 assembler written in V. It assembles the
same AT&T-syntax assembly as the GNU assembler (as), with instruction encodings
taken straight from NASM's table (insns.dat).
vas can assemble the unmodified gcc/g++ output of real-world programs, including SQLite and Lua, without any changes to the generated assembly. vas is also self-hosting: it assembles the assembly that V and gcc produce from its own source, and the rebuilt assembler reproduces itself byte-for-byte.
Installation
Docker setup
# Build the Docker image
docker build ./ -t vas
# Run the container
# Linux/MacOS:
docker run --rm -it -v "$(pwd)":/root/env vas
# Windows (CMD):
docker run --rm -it -v "%cd%":/root/env vas
# Windows (PowerShell):
docker run --rm -it -v "${pwd}:/root/env" vas
Build
Requires the V compiler to be installed.
v . -prod
Usage
Basic usage:
vas [options] <input_file>.s
Options:
-o <filename>: Set output file name (default: input_file.o)-f <format>: Output format:elf,macho, orpe(default: auto-detect from OS)--keep-locals: Keep local symbols (e.g., those starting with.L)
Examples
The examples/ directory has end-to-end showcases where vas stands in for the
GNU assembler: each script runs gcc -S to get AT&T assembly, vas to assemble
it, and gcc/ld to link. They need v, gcc, and ld on PATH (e.g. the
Docker container above), with vas already built (v -o vas .).
The scripts pin CC=gcc-12 so the assembly input is reproducible: the Docker
image above ships gcc-12, so running the examples inside it is guaranteed to
work. On the host, install gcc-12 (or override with CC=...).
SQLite — assemble the SQLite amalgamation with vas and run real SQL:
$ bash examples/sqlite/build-sqlite.sh
==> vas: assemble each .s (vas replaces GNU as here)
sqlite3.o 1252004 bytes, shell.o 507625 bytes
==> smoke test: run real SQL
--- vas-built SQLite ---
alice|9.5
carol|7.8
3|6.833|9.5
2870
SQLite 3.53.2 built by vas
Lua — assemble the whole Lua interpreter (every src/*.c) with vas and run
a script:
$ bash examples/lua/build-lua.sh
==> vas: assemble each .s (vas replaces GNU as here)
assembled 33 objects
==> smoke test: run real Lua
--- vas-built Lua ---
fib 1..12 : 1 1 2 3 5 8 13 21 34 55 89 144
primes <40 : 2 3 5 7 11 13 17 19 23 29 31 37
Lua 5.4 — assembled by vas
Self-hosting — vas assembles the assembly that V and gcc emit from its own source, until two generations produce a byte-identical object:
$ bash examples/selfhost/selfhost.sh
==> generation 1: vas assembles vas, then we link + test it
==> generation 2: vas1 assembles vas, then we link + test it
==> FIXPOINT at generation 2: gen2.o is byte-identical to gen1.o
==> vas now reproduces itself exactly — self-hosting verified.
The examples/{linux,macos,windows}/ directories also hold minimal hand-written
ELF / Mach-O / PE programs.
Testing
Regression tests live under tests/cases/, split by output format:
tests/cases/
elf/ — ELF test cases: <name>.s + <name>.expected.md5
macho/ — Mach-O test cases: <name>.s + <name>.expected.md5
pe/ — PE test cases: <name>.s + <name>.expected.md5
The runner is a V _test.v file:
v test tests/
This rebuilds vas, assembles every case in all three directories, and asserts
the output bytes match the recorded MD5. To add a case:
# ELF example
$EDITOR tests/cases/elf/my_feature.s
./vas -f elf tests/cases/elf/my_feature.s
md5 -q tests/cases/elf/my_feature.o > tests/cases/elf/my_feature.expected.md5
rm tests/cases/elf/my_feature.o
v test tests/
# Mach-O example
$EDITOR tests/cases/macho/my_feature.s
./vas -f macho tests/cases/macho/my_feature.s
md5 -q tests/cases/macho/my_feature.o > tests/cases/macho/my_feature.expected.md5
rm tests/cases/macho/my_feature.o
v test tests/
# PE example
$EDITOR tests/cases/pe/my_feature.s
./vas -f pe tests/cases/pe/my_feature.s
md5 -q tests/cases/pe/my_feature.o > tests/cases/pe/my_feature.expected.md5
rm tests/cases/pe/my_feature.o
v test tests/
Instruction table
The assembler's instruction set is data-driven: rows are generated from
NASM's third_party/insns.dat by tools/gen_insns.v and committed to
encoder/insns_table.gen.v. Regenerate after editing the parser:
v run tools/gen_insns.v
See LICENSE-NASM for the BSD-2-clause notice covering the bundled
insns.dat and the rows derived from it.
Star History
License
This project is licensed under the MIT License - see the LICENSE file for details.
It also bundles third_party/insns.dat from NASM, the
Netwide Assembler, which tools/gen_insns.v uses to generate
encoder/insns_table.gen.v. That bundled file and any code generated from it
remain under NASM's 2-clause BSD license — see LICENSE-NASM.