$ cargo build $ cargo test
Running simple programs is straight forward.
$ echo "(define (twice x) (* x 2)) (twice 21)" | cargo run -q 42
How does this work?
The previous step generates x86 assembly that gets compiled to a very tiny (~13KB) native executable binary along with some runtime written in Rust and some glue code in C.
$ ./inc 42 $ file inc inc: Mach-O 64-bit executable x86_64 $ stat -f "%z" inc 13556
The generated assembly is usually easy to read, and if you squint hard enough
kinda looks like the source code
$ echo "(define (twice x) (* x 2)) (twice 21)" | cargo run -q -- -S
.section __TEXT,__text .intel_syntax noprefix .globl "_init" "_init": push rbp mov rbp, rsp mov r12, rdi # Store heap index to R12 mov rax, 168 mov qword ptr [rbp - 24], rax call "twice" pop rbp ret .globl "twice" "twice": push rbp mov rbp, rsp mov rax, [rbp - 8] mov qword ptr [rbp - 16], rax mov rax, 16 sar rax, 3 mul qword ptr [rbp - 16] pop rbp ret
Under the hood, inc compiles scheme to x86 assembly and uses Clang (or GCC on Linux) to generate machine executable binaries.
Generate the asm
$ echo "(define (twice x) (* x 2)) (twice 21)" | cargo run -q -- -S > inc.s
Compile the runtime as well the generated assembly into shared object files
$ clang -c inc.s # Generates inc.o $ clang -c runtime.c # Generates runtime.o $ cargo build # Generates ./target/debug/libinc.dylib
Link it all together with a linker
$ ld -L./target/debug runtime.o inc.o -linc -ldl -lpthread -o inc
The same binary is generated again
$ ./inc 42
Conveniently clang can do it all in one step if you prefer it that way.
$ clang -L./target/debug inc.s runtime.c -linc -ldl -lpthread -o inc
Inc is reasonably well documented and is preferably read with Cargo docs. Build
docs locally or read online (
$ cargo doc --document-private-items --open
Where can I learn more?
- Read the paper for an overview
- Watch a talk about this project if that works better.
- Ask HN: What's the best resource for learning modern x64 assembly?