Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions lrem_spec_compliance_report.md
Original file line number Diff line number Diff line change
@@ -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`.
Loading