Running ABCL (Armed Bear Common Lisp) on OpenLDK
February 23, 2026 ยท View on GitHub
Overview
ABCL is a Common Lisp implementation that runs on the JVM. Getting it to run on OpenLDK is a significant milestone: a Lisp running on a Lisp-based JVM.
See also: https://github.com/atgreen/openldk/issues/4
Setup
Prerequisites
- OpenLDK binary (built from this repo)
- Java 8 JDK (for rt.jar)
- ABCL 1.9.2 jar (already Java 8 / class version 52.0)
On this system:
JAVA_HOME=/home/linuxbrew/.linuxbrew/Cellar/openjdk@8/1.8.0-482/libexec/jre
JAVA8_JAVAC=/home/linuxbrew/.linuxbrew/Cellar/openjdk@8/1.8.0-482/libexec/bin/javac
Getting the ABCL jar
Download the binary release:
curl -sL "https://abcl.org/releases/1.9.2/abcl-bin-1.9.2.tar.gz" | tar xz -C /tmp/
cp /tmp/abcl-bin-1.9.2/abcl.jar lib/abcl/
The release jar is already compiled for Java 8 (class version 52.0).
The Thread Problem
ABCL's stock org.armedbear.lisp.Main spawns a new thread with a 4MB stack
to run the interpreter:
new Thread(null, r, "interpreter", 4194304L).start();
OpenLDK needs the interpreter to run on the main thread. Rather than
patching ABCL itself, we wrote a custom entry point: lib/abcl/ABCLMain.java
in the org.armedbear.lisp package. This calls
Interpreter.createDefaultInstance(args) and interpreter.run() directly.
Compiling the custom entry point
cd lib/abcl
$JAVA8_JAVAC -source 1.8 -target 1.8 -cp abcl.jar -d . ABCLMain.java
This creates org/armedbear/lisp/ABCLMain.class under lib/abcl/.
Running
export JAVA_HOME=/home/linuxbrew/.linuxbrew/Cellar/openjdk@8/1.8.0-482/libexec/jre
export LDK_CLASSPATH="lib/abcl/abcl.jar:lib/abcl"
./openldk org.armedbear.lisp.ABCLMain
Current Status (2026-02-22)
What Works
- Full ABCL bootstrap completes successfully
- Custom ABCLMain entry point runs the interpreter on the main thread
- ABCL loads 3700+ classes successfully (JDK + ABCL core + CLOS + compiled Lisp)
Interpreter.createDefaultInstance()andinitializeLisp()run to completion- ABCL's bootstrap Lisp file (
boot.lisp) is fully loaded - All FASL-compiled Lisp files (
.cls) load viaFaslClassLoader:macros,read-circle,early-defuns,defstruct,defpackage, etc.- Full CLOS implementation (
clos_1throughclos_1642) print_object,format,ldb, etc.
- ABCL banner prints with both init phases completing:
Armed Bear Common Lisp 1.9.2 Java 8.0 OpenLDK OpenLDK Low-level initialization completed in -0.001 seconds. Startup completed in 42.173 seconds.
Current Blocker
REPL stdin interaction
After full startup, interpreter.run() enters the REPL but hangs waiting
for stdin. When stdin is /dev/null, the REPL eventually throws an
org.armedbear.lisp.Go exception (ABCL's tagbody/go control transfer)
which escapes to the top level. With a pipe or FIFO, the REPL simply hangs
without producing a prompt or processing input.
The --batch and --eval flags are passed through to ABCL's
Interpreter.createDefaultInstance(args) but don't appear to take effect,
suggesting the argument processing or stream setup during interpreter.run()
has an issue (possibly related to OpenLDK's stdin stream implementation).
ABCL .cls Extension
ABCL stores compiled Lisp code as .cls files inside its jar (not .class).
For example: org/armedbear/lisp/destructuring_bind_10.cls. The classpath
loader tries both .class and .cls extensions when looking up entries in
jar files.
Changes Made for ABCL Support
src/classfile.lisp
- Warning when
classloadreturns NIL (instead of silently proceeding)
src/classpath.lisp
- Try
.clsextension in addition to.classwhen loading from jars
src/codegen.lisp
- Null class check in
ir-newcodegen: generateNoClassDefFoundError
src/openldk.lisp
sb-c:compiler-errorhandler in%eval: logs error, dumps generated code to/tmp/abcl-codegen-error.lispfor debugging- Recursive compilation detection: track compiling thread identity (not
just boolean
t) to detect same-thread recursion vs different-thread compilation handler-casearound%evalin%compile-method: catch compiler errors and skip the method (log which method failed) instead of crashing
lib/abcl/ABCLMain.java
- Custom entry point that runs ABCL on the main thread
Debugging Tips
SIGQUIT thread dump
kill -3 $(pgrep -f ABCLMain)
This dumps all thread stacks to stderr. Useful to see where compilation is
stuck (often in SB-WALKER::WALK-METHOD-LAMBDA for large methods).
Verbose output
./openldk -verbose:class org.armedbear.lisp.ABCLMain # class loading
LDK_DEBUG=L ./openldk org.armedbear.lisp.ABCLMain # loading + compile timing
ABCL Bootstrap Sequence
ABCLMain.main()->Interpreter.createDefaultInstance(args)Interpreter.initializeLisp()- initializes the Lisp runtimeLoad.loadSystemFile("boot.lisp")- loads the core Lisp systemboot.lisploads dozens more Lisp files vialoadSystemFile:setf,fdefinition,macros,read-circle,early-defuns, etc.
- Many of these are FASL files (
.abclformat) containing compiled.clsclass files that get loaded viaFaslClassLoader - Full CLOS bootstrap: 1600+
clos_*compiled classes interpreter.run()starts the REPL
Each loaded Lisp file triggers class loading and JIT compilation for the Java classes implementing ABCL's built-in functions.
Performance Notes
- Full startup takes ~42 seconds (3700+ classes JIT-compiled)
- Java single-dispatch optimization (
java-generic-functionmetaclass) was critical for reaching this point โ the previous PCL discrimination-net approach caused exponential slowdowns with thousands of classes kill -3is essential for diagnosing apparent hangs during compilation
Known Issues / TODO
- REPL stdin interaction - REPL hangs after startup;
--eval/--batchflags don't appear to work;Goexception on EOF - Thread support - ABCL may try to create threads during later use
- Performance - 42s startup is acceptable but could improve with lazy/deferred compilation