x86_64 JIT: 12-21x faster than CPython, 230x faster than interpreter

Real native machine code via mmap(PROT_EXEC). No exec(). No strings.
Raw x86_64 bytes: mov, add, sub, imul, cmp, je, jne, call, ret, jmp.

ack(3,4):    0.12ms JIT vs 1.5ms CPython vs 28ms interpreter
fib-rec(20): 0.16ms JIT vs 3.4ms CPython vs 40ms interpreter

Added cond support to JIT (cascaded comparisons → conditional jumps).
Fixed JIT cache: sentinel value prevents retry on unjittable functions.
System V AMD64 ABI: args in rdi/rsi/rdx, callee-saved r12-r15.
Tail calls use jmp (true TCO at machine code level).

691 lines of jit.c. 114 functional tests pass. All C tests pass.
This commit is contained in:
russell@unturf.com 2026-04-14 19:58:26 -04:00
parent 46812e1885
commit c80eabac47
7 changed files with 867 additions and 22 deletions

View file

@ -1,7 +1,8 @@
/*
* bench.c Benchmarks matching bench.py same Scheme programs
* bench.c Benchmarks: interpreted vs compiled vs JIT
*/
#include "uncommonlisp.h"
#include "jit.h"
static double bench_time(void) {
struct timespec ts;
@ -60,17 +61,42 @@ static Benchmark benchmarks[] = {
"(tak 18 12 6)",
10
},
{
"ack(3,4)",
"(define (ack m n)\n"
" (if (= m 0) (+ n 1)\n"
" (if (= n 0) (ack (- m 1) 1)\n"
" (ack (- m 1) (ack m (- n 1))))))\n",
"(ack 3 4)",
100
},
{
"ack(3,7)",
"(define (ack m n)\n"
" (if (= m 0) (+ n 1)\n"
" (if (= n 0) (ack (- m 1) 1)\n"
" (ack (- m 1) (ack m (- n 1))))))\n",
"(ack 3 7)",
10
},
{NULL, NULL, NULL, 0}
};
static void run_benchmark(Benchmark *b, bool compiled) {
static void run_benchmark(Benchmark *b, const char *mode) {
Env *g = make_global_env();
int count;
Value *prelude_exprs = read_all(PRELUDE, &count, false);
for (int i = 0; i < count; i++) leval(prelude_exprs[i], g);
ul_free(prelude_exprs);
if (compiled) g_auto_compile = true;
g_auto_compile = false;
g_jit_enabled = false;
if (strcmp(mode, "compiled") == 0) {
g_auto_compile = true;
} else if (strcmp(mode, "jit") == 0) {
g_jit_enabled = true;
}
/* Setup */
Value *setup_exprs = read_all(b->setup, &count, false);
@ -87,18 +113,23 @@ static void run_benchmark(Benchmark *b, bool compiled) {
/* Timed run */
double start = bench_time();
Value last = VAL_VOID;
for (int i = 0; i < b->iterations; i++) {
for (int j = 0; j < count; j++) leval(bench_exprs[j], g);
for (int j = 0; j < count; j++) last = leval(bench_exprs[j], g);
}
double elapsed = bench_time() - start;
printf(" %-25s %s %8d iters %.3f s (%.1f us/iter)\n",
b->name, compiled ? "compiled" : "interp ",
char *result_str = show(last, false);
printf(" %-25s %-10s %8d iters %.3f s (%.1f us/iter) => %s\n",
b->name, mode,
b->iterations, elapsed,
(elapsed / b->iterations) * 1e6);
(elapsed / b->iterations) * 1e6,
result_str);
ul_free(result_str);
ul_free(bench_exprs);
g_auto_compile = false;
g_jit_enabled = false;
}
int main(void) {
@ -115,21 +146,20 @@ int main(void) {
return 1;
}
printf("uncommonlisp C benchmarks\n");
printf("uncommonlisp C benchmarks — interpreted vs compiled vs JIT\n");
printf("═════════════════════════════════════════════════════════════════\n");
for (int i = 0; benchmarks[i].name; i++) {
TRY(ctx) {
run_benchmark(&benchmarks[i], false);
} CATCH {
printf(" %-25s interp ERROR: %s\n", benchmarks[i].name, ctx.message);
} ENDTRY;
const char *modes[] = { "interp", "compiled", "jit", NULL };
TRY(ctx) {
run_benchmark(&benchmarks[i], true);
} CATCH {
printf(" %-25s compiled ERROR: %s\n", benchmarks[i].name, ctx.message);
} ENDTRY;
for (int i = 0; benchmarks[i].name; i++) {
for (int m = 0; modes[m]; m++) {
TRY(ctx) {
run_benchmark(&benchmarks[i], modes[m]);
} CATCH {
printf(" %-25s %-10s ERROR: %s\n", benchmarks[i].name, modes[m], ctx.message);
} ENDTRY;
}
printf(" ─────────────────────────────────────────────────────────────\n");
}
printf("═════════════════════════════════════════════════════════════════\n");