Summary
The Level1A x86 JIT compiler aborts with java.lang.Error: Register(s) in use (thrown by X86RegisterPool.reset, core/src/core/org/jnode/vm/x86/compiler/l1a/X86RegisterPool.java:236) while compiling a method invoked reflectively at boot time. Reproducible: the boot crashes right at the installer Main-Class invocation, every run.
Repro (QEMU)
- Build a boot initjar whose Main-Class is a normal Java class (e.g. the installer,
org.jnode.install.Main) — see all/conf/install-plugin-list.xml.
- Boot it in QEMU (GRUB "JNode Install" entry / index 5 of the CD menu).
- Kernel, plugins and filesystems start fine; then the process dies at
org.jnode.boot.Main.vmMain:
Error in compilation: java.lang.RuntimeException: Error in compilation
...
Caused by: java.lang.Error: Register(s) in use
at org.jnode.vm.x86.compiler.l1a.X86RegisterPool.reset(X86RegisterPool.java:236)
Invocation point: core/src/core/org/jnode/boot/Main.java:95 — mainMethod.invoke(null, new Object[]{proc.getMainClassArguments()}).
Reproduced on two consecutive boots (same install initjar). The boot-time reflection Method.invoke triggers JIT compilation of the target method; the register pool is left non-empty when the compiled method returns, so the pooled register leak is detected as a fatal error.
Analysis
X86RegisterPool.reset(X86Assembler os) (X86RegisterPool.java:225-238) walks all registers and throws Error("Register(s) in use") if any is still allocated at reset time.
reset is called from EmitterContext.reset(os) (EmitterContext.java:96-97) at the end of a compilation (X86BytecodeVisitor.java:305-308), i.e. after each method body is emitted.
- A thrown
Error here aborts the whole boot (not just the offending method), which makes any boot-time Main-Class flow fragile: any single method that leaks a register kills the system.
- No "Warning: register in use" was visible for the specific register before the throw, and the crash is deterministic for this workload, so it is a genuine leak (or a mid-method
reset) rather than a rare race.
Impact
Any plugin with a Main-Class that does real work via the reflection invoke path (Main.java:91-95) is at risk. This currently blocks the JNode installer (see linked feature request) and would affect any future boot-time Main-Class plugin.
Suggested next steps
- Identify the compiled method that leaks the register (add a debug log of the current method name in
X86RegisterPool.reset before the throw).
- Fix the leak (a missing
release in some bytecode path, or an early reset mid-method).
- Consider making
reset log-and-continue instead of throwing Error when the pool is dirty — a leaked register should be a warning + auto-release, not a fatal boot abort — while keeping the assertion during testing.
Reference
- core/src/core/org/jnode/vm/x86/compiler/l1a/X86RegisterPool.java:225-238 (
reset, throw at :236)
- core/src/core/org/jnode/vm/x86/compiler/l1a/EmitterContext.java:96-97 (caller)
- core/src/core/org/jnode/vm/x86/compiler/l1a/X86BytecodeVisitor.java:305-308 (reset sequence)
- core/src/core/org/jnode/boot/Main.java:91-95 (reflection invoke that triggers the compile)
Summary
The Level1A x86 JIT compiler aborts with
java.lang.Error: Register(s) in use(thrown byX86RegisterPool.reset, core/src/core/org/jnode/vm/x86/compiler/l1a/X86RegisterPool.java:236) while compiling a method invoked reflectively at boot time. Reproducible: the boot crashes right at the installer Main-Class invocation, every run.Repro (QEMU)
org.jnode.install.Main) — seeall/conf/install-plugin-list.xml.org.jnode.boot.Main.vmMain:Invocation point: core/src/core/org/jnode/boot/Main.java:95 —
mainMethod.invoke(null, new Object[]{proc.getMainClassArguments()}).Reproduced on two consecutive boots (same install initjar). The boot-time reflection
Method.invoketriggers JIT compilation of the target method; the register pool is left non-empty when the compiled method returns, so the pooled register leak is detected as a fatal error.Analysis
X86RegisterPool.reset(X86Assembler os)(X86RegisterPool.java:225-238) walks all registers and throwsError("Register(s) in use")if any is still allocated at reset time.resetis called fromEmitterContext.reset(os)(EmitterContext.java:96-97) at the end of a compilation (X86BytecodeVisitor.java:305-308), i.e. after each method body is emitted.Errorhere aborts the whole boot (not just the offending method), which makes any boot-time Main-Class flow fragile: any single method that leaks a register kills the system.reset) rather than a rare race.Impact
Any plugin with a Main-Class that does real work via the reflection invoke path (Main.java:91-95) is at risk. This currently blocks the JNode installer (see linked feature request) and would affect any future boot-time Main-Class plugin.
Suggested next steps
X86RegisterPool.resetbefore the throw).releasein some bytecode path, or an earlyresetmid-method).resetlog-and-continue instead of throwingErrorwhen the pool is dirty — a leaked register should be a warning + auto-release, not a fatal boot abort — while keeping the assertion during testing.Reference
reset, throw at :236)