From 76678e8fb1753a45f31fdf70c7a06e3512d28f54 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Tue, 23 Jun 2026 05:59:13 +0000 Subject: [PATCH] l1a/l1b: PASS; L2: FAIL (LREM unimplemented) Co-authored-by: LSantha --- lrem_spec_compliance_report.md | 92 ++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 lrem_spec_compliance_report.md diff --git a/lrem_spec_compliance_report.md b/lrem_spec_compliance_report.md new file mode 100644 index 000000000..4b334960f --- /dev/null +++ b/lrem_spec_compliance_report.md @@ -0,0 +1,92 @@ +## Spec compliance report + +| Compiler | Status | Issues | +|----------|--------|--------| +| l1a | ✅ PASS | None | +| l1b | ✅ PASS | None | +| l2 | ❌ FAIL | LREM (and all long arithmetic ops) not implemented in GenericX86CodeGenerator | + +--- + +## Details + +### l1a Implementation +- **Status:** ✅ PASS +- **Files:** + - `core/src/core/org/jnode/vm/x86/compiler/l1a/X86BytecodeVisitor.java:3371` +- **Issues:** None + +**32-bit mode (lines 3372-3380):** Delegates to `context.getLremMethod()` which resolves to `MathSupport.lrem()`. + +`MathSupport.lrem()` (core/src/core/org/jnode/vm/MathSupport.java:98-149) correctly implements the JVM spec: +- Handles `Long.MIN_VALUE` edge cases per spec identity `(a/b)*b + (a%b) == a` +- Explicitly checks `den == 0` and throws `ArithmeticException("Divide by zero")` at line 126-127 +- Result sign matches dividend (value1) per spec + +**64-bit mode (lines 3381-3412):** Uses x86 `IDIV` instruction directly: +- Loads dividend into `RAX`, sign-extends to `RDX:RAX` via `CDQ` +- Executes `IDIV` with divisor +- Remainder ends up in `RDX` (pre-claimed as result register) +- Hardware raises `#DE` (divide error) on division by zero +- VM exception handler (`VmThread.java:1391-1393`) catches `EX_DIV0` and throws `ArithmeticException("Division by zero at address ...")` + +**Operand stack effect:** Correctly pops two `LongItem` (category-2), pushes one `LongItem` (category-2). Uses `DoubleWordItem` for 32-bit (two GPRs) and single `GPR64` for 64-bit. + +--- + +### l1b Implementation +- **Status:** ✅ PASS +- **Files:** + - `core/src/core/org/jnode/vm/x86/compiler/l1b/X86BytecodeVisitor.java:4212` +- **Issues:** None + +**Implementation is identical to l1a** with same 32-bit/64-bit split and same correctness properties. Only difference is additional counter instrumentation. + +--- + +### l2 Implementation +- **Status:** ❌ FAIL +- **Files:** + - `core/src/core/org/jnode/vm/x86/compiler/l2/L2ByteCodeSupportChecker.java` — does NOT reject `lrem` + - `core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java` — all 16 `LREM` case labels (lines 920, 1083, 1266, 1481, 1666, 1859, 2045, 2207, 2384, 2541, 2688, 2862, 3016, 3210, 3399, 3634) fall through to: + ```java + case LADD: + case LAND: + case LDIV: + case LMUL: + case LOR: + case LREM: + case LSHL: + case LSHR: + case LSUB: + case LUSHR: + case LXOR: + default: + throw new IllegalArgumentException("Unknown operation: " + operation); + ``` +- **Issues:** + 1. **Complete lack of LREM support** — any method containing `lrem` will fail L2 compilation with `IllegalArgumentException` + 2. **L2ByteCodeSupportChecker doesn't filter it out** — `canCompile()` returns `true` for methods with `lrem`, but code generation fails + 3. **All long arithmetic ops missing** — same issue affects `ldiv`, `lmul`, `ladd`, `lsub`, `land`, `lor`, `lxor`, `lshl`, `lshr`, `lushr`, `lneg`, `lcmp` + +--- + +## JVM Spec References + +- **JVMS SE6 §lrem**: https://docs.oracle.com/javase/specs/jvms/se6/html/Instructions2.doc8.html +- **Operation**: `result = value1 - (value1 / value2) * value2` +- **Operand Stack**: `..., value1, value2 → ..., result` (both `long`, category-2) +- **Runtime Exception**: `ArithmeticException` if `value2 == 0` +- **Identity**: `(a/b)*b + (a%b) == a` (holds for `MIN_VALUE / -1` where remainder is 0) + +--- + +## Verification Notes + +1. **Divide-by-zero handling**: Both L1 compilers correctly produce `ArithmeticException` — 32-bit via explicit check in `MathSupport.lrem()`, 64-bit via x86 `#DE` exception translated by VM. + +2. **Edge case `Long.MIN_VALUE % -1`**: `MathSupport.lrem()` handles this at lines 99-108, returning `0` per spec identity. + +3. **Category-2 stack handling**: Both L1 compilers use `DoubleWordItem` / `LongItem` correctly for 64-bit values on virtual stack. + +4. **L2 gap is systemic**: The entire `long`/`double` arithmetic category is unimplemented in L2 code generator (`GenericX86CodeGenerator`), not just `lrem`. \ No newline at end of file