Skip to content

All 3 compilers PARTIAL: missing static/<clinit> checks; L2 also breaks LONG/DOUBLE returns#571

Open
opencode-agent[bot] wants to merge 1 commit into
masterfrom
opencode/issue408-20260624013036
Open

All 3 compilers PARTIAL: missing static/<clinit> checks; L2 also breaks LONG/DOUBLE returns#571
opencode-agent[bot] wants to merge 1 commit into
masterfrom
opencode/issue408-20260624013036

Conversation

@opencode-agent

Copy link
Copy Markdown

Spec compliance report

Compiler Status Issues
l1a ⚠️ PARTIAL Missing validation for resolved method being static (not instance) and not
l1b ⚠️ PARTIAL Missing validation for resolved method being static (not instance) and not
l2 ⚠️ PARTIAL Missing validation for resolved method being static (not instance) and not ; LONG/DOUBLE return value handling incomplete in StaticCallAssignQuad codegen

Details

l1a Implementation

  • Status: ⚠️ PARTIAL
  • Files: core/src/core/org/jnode/vm/x86/compiler/l1a/X86BytecodeVisitor.java:2786, plus X86CompilerHelper.java:276, X86StackFrame.java:221
  • Issues:
    1. Missing static method validation (line 2788-2789): Direct cast to VmStaticMethod without checking if resolved method is actually static. Spec §invokestatic Linking Exceptions: "if the resolved method is an instance method, the invokestatic instruction throws an IncompatibleClassChangeError". Current code would throw ClassCastException instead.
    2. Missing check: Spec says "The method must not be the class or interface initialization method". No validation for method.isInitializer().
    3. Operand stack effect: Correct - vstack.push() flushes vstack, dropParameters(method, false) pops nargs arguments using category-aware pops, helper.invokeJavaMethod() calls pushReturnValue() which correctly handles VOID (no push), category-1 (EAX), and category-2 (EAX:EDX for LONG/DOUBLE).
    4. Class initialization: Correctly handled at callee entry via X86StackFrame.emitTrailer()helper.writeClassInitialize(method) at line 221.
    5. Synchronized static methods: Handled at callee entry via emitSynchronizationCode() which pushes declaring class and calls monitorEnter.

l1b Implementation

  • Status: ⚠️ PARTIAL
  • Files: core/src/core/org/jnode/vm/x86/compiler/l1b/X86BytecodeVisitor.java:3462, plus shared helper/stackframe
  • Issues:
    1. Missing static method validation (line 3467-3469): Same as l1a - direct cast to VmStaticMethod without IncompatibleClassChangeError check.
    2. Missing check: Same as l1a.
    3. Operand stack effect: Correct - identical to l1a implementation.
    4. Class initialization: Correct - same callee-side handling.
    5. Synchronized static methods: Correct - same callee-side handling.

l2 Implementation

  • Status: ⚠️ PARTIAL
  • Files:
    • core/src/core/org/jnode/vm/compiler/ir/IRGenerator.java:1140 (IR generation)
    • core/src/core/org/jnode/vm/x86/compiler/l2/GenericX86CodeGenerator.java:5640 (StaticCallAssignQuad), :5666 (StaticCallQuad)
    • core/src/core/org/jnode/vm/x86/compiler/l2/X86StackFrame.java:221 (callee init)
  • Issues:
    1. Missing static method validation (IRGenerator line 1142, GenericX86CodeGenerator lines 5642, 5668): Both IR generation and codegen resolve and cast to VmStaticMethod without checking for instance method → should throw IncompatibleClassChangeError.
    2. Missing check: No validation that resolved method is not an initializer.
    3. LONG/DOUBLE return value handling incomplete (GenericX86CodeGenerator line 5640-5662): generateCodeFor(StaticCallAssignQuad) only moves EAX to destination. For LONG/DOUBLE returns (category 2), spec requires both EAX and EDX (32-bit) or RAX (64-bit) to be preserved. Compare with VarReturnQuad handler (lines 376-383) which correctly handles LONG/DOUBLE by moving both registers.
    4. L2ByteCodeSupportChecker.java: Does not override visit_invokestatic (correctly inherits no-op from BytecodeVisitorSupport), so invokestatic is considered supported.
    5. Operand stack effect in IR: Correct - IRGenerator pops arguments by category (line 1156: stackOffset -= stackChange where stackChange = getCategory(jvmType)), creates appropriate quad, and adjusts stackOffset for return type (line 1165: stackOffset += typeSizeInfo.getStackSlots(returnType)).
    6. Class initialization: Correct - callee-side via X86StackFrame.emitTrailer() line 221.
    7. Argument passing: writeParameters() (line 5728) correctly handles LONG/DOUBLE by pushing two stack slots (lines 5742-5744).
    8. Synchronized static methods: Handled at callee entry same as l1a/l1b.

JVM Spec References (Java SE 6)

  • Format: invokestatic indexbyte1 indexbyte2 (opcode 0xb8)
  • Operand Stack: ..., [arg1, [arg2 ...]] → ... (void) or ..., result (non-void)
  • Linking Exceptions: Method resolution per §5.4.3.3; IncompatibleClassChangeError if resolved method is instance method
  • Runtime Exceptions: Error if class initialization triggered; UnsatisfiedLinkError if native method unbound
  • Notes: nargs not 1:1 with locals (long/double take 2 slots); class initialization on first active use

Summary of Spec Deviations

All three compilers lack the required linking-time check that the resolved method is static (not instance) and not . L2 additionally has incomplete codegen for LONG/DOUBLE return values from static calls.

Closes #408

New%20session%20-%202026-06-24T01%3A30%3A35.129Z
opencode session  |  github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JVM instruction spec compliance: invokestatic

0 participants