Skip to content

Interpreter improvements: Close test gap with compiler to 0 tests#215

Merged
fglock merged 3 commits into
masterfrom
fix/test-regressions
Feb 20, 2026
Merged

Interpreter improvements: Close test gap with compiler to 0 tests#215
fglock merged 3 commits into
masterfrom
fix/test-regressions

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Feb 20, 2026

Summary

This PR contains a series of improvements to the bytecode interpreter that closes the test gap between interpreter and compiler execution modes. The interpreter now achieves parity with the compiler on re/regexp.t, with both passing 1786/2210 tests (80.8%).

Key Achievements

Interpreter vs Compiler Gap: 0 tests (was 48+ tests)
perf/benchmarks.t: 100% pass rate with interpreter (1960/1960)
re/regexp.t: 80.8% pass rate (1786/2210) - matches compiler exactly
op/decl-refs.t: 66.7% pass rate (272/408) - 2 tests better than compiler (270/408)
BytecodeInterpreter size: 8,228 bytes (under 8KB JIT limit)

Changes

1. BytecodeInterpreter Optimization (#1136003)

  • Extracted 84 opcodes to OpcodeHandlerExtended and OpcodeHandlerFileTest
  • Reduced execute() method size from 12,066 bytes to 8,228 bytes
  • Achieved 9% performance improvement (0.68s vs 0.75s on benchmarks)
  • All 1960 perf/benchmarks.t tests passing at 100% with interpreter

2. Interpreter Feature Additions

  • sprintf operator (#88f4f60): Added SPRINTF opcode support
  • keys/values scalar context (#38c095f): Return count in scalar context
  • chop operator (#ee49768): Added CHOP opcode support
  • matchRegex fix (#c7920c1): Fixed =~ binding to perform actual match

3. Register Management Fixes

  • Foreach iterator protection (#3dadb30): Prevent recycling of iterator registers
  • Hash to scalar conversion (#391b4e0): Fixed scalar context handling
  • Assignment return values (#f8f25dd): Return scalar context values correctly

4. Error Message Improvements (#ad5807d)

  • Removed "Interpreter error" prefix from eval'd code exceptions
  • Clean error messages in $@ that match compiler behavior
  • Fixed 47 tests in re/regexp.t (1738 → 1785 passing)

5. Scalar Reference Dereference (#ebc92fa + #7f14e66)

  • Implemented proper DEREF opcode behavior for scalar references
  • Fixed LOAD_SYMBOLIC_SCALAR to handle reference dereferencing
  • Enabled ${\...} syntax in interpreter (e.g., ${\(0)})
  • Fixed final test gap in re/regexp.t: 1785 → 1786 passing
  • Regression fix (#7f14e66): Handle RuntimeList in DEREF to prevent ClassCastException
    • Fixed crash with declared reference lists like my (\$f, $g)
    • Now checks value type before attempting dereference
    • op/decl-refs.t went from 157/408 (crash) → 272/408 (66.7%)

Testing

  • ✅ All unit tests pass (make test)
  • re/regexp.t: 1786/2210 (80.8%) with both compiler and interpreter
  • op/decl-refs.t: 272/408 (66.7%) with interpreter vs 270/408 (66.2%) with compiler
  • perf/benchmarks.t: 1960/1960 (100%) with interpreter
  • ✅ No regressions in existing tests

Impact

re/regexp.t - Before This PR

re/regexp.t (compiler):    1786/2210 (80.8%)
re/regexp.t (interpreter): 1738/2210 (78.6%)
Gap: -48 tests

re/regexp.t - After This PR

re/regexp.t (compiler):    1786/2210 (80.8%)
re/regexp.t (interpreter): 1786/2210 (80.8%)
Gap: 0 tests ✓

op/decl-refs.t - After Regression Fix

op/decl-refs.t (compiler):    270/408 (66.2%)
op/decl-refs.t (interpreter): 272/408 (66.7%)
Interpreter is +2 tests better! ✓

The interpreter now provides identical or better functionality compared to the compiler while staying under the JVM JIT compilation threshold for optimal performance.

🤖 Generated with Claude Code

fglock and others added 3 commits February 20, 2026 10:26
When interpreter executes code from eval STRING (sourceName starts with
"(eval N)"), exceptions are now passed through without wrapping them in
formatInterpreterError(). This preserves clean error messages in $@ that
match the compiler's behavior.

Impact on re/regexp.t with JPERL_EVAL_USE_INTERPRETER=1:
- Before: 1738/2210 passing (78.6%)
- After:  1785/2210 passing (80.8%)
- Improvement: +47 tests (+2.1 percentage points)

This closes the gap between interpreter and compiler to just 1 test
(1786 compiler vs 1785 interpreter).

Example error message improvement:
Before: Interpreter error in (eval 6) line 1 (pc=7): Invalid [] range...
After:  Invalid [] range "b-a" in regex; marked by <-- HERE...

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed two issues with scalar reference dereference in the interpreter:

1. DEREF opcode was just copying the reference instead of dereferencing it.
   Now properly calls scalarDeref() on the reference to get the target value.

2. LOAD_SYMBOLIC_SCALAR was treating all block results as variable names for
   symbolic references (like ${"varname"}). Now checks if the block returns a
   REFERENCE type and dereferences it (like ${\(0)}), otherwise treats it as
   a symbolic variable name.

This fixes the inline scalar reference dereference syntax ${\...} which is used
in Perl for patterns like:
  - ${\(0)} - create anonymous scalar ref and dereference it
  - ${\(defined($1)?1:0)} - conditional expression in scalar ref

Impact on re/regexp.t with JPERL_EVAL_USE_INTERPRETER=1:
- Before: 1785/2210 passing (80.8%)
- After:  1786/2210 passing (80.8%)
- Gap closed: Interpreter now matches compiler exactly (0 test difference)

Test case that now works:
  eval q{my $x = ${\(0)}; say $x}  # Now prints "0" instead of ""

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The DEREF opcode was unconditionally casting to RuntimeScalar, which caused
ClassCastException when processing declared reference lists like `my (\$f, $g)`.

Now checks the value type before attempting dereference:
- If RuntimeScalar with REFERENCE type: calls scalarDeref()
- If RuntimeScalar without REFERENCE type: passes through unchanged
- If RuntimeList or other type: passes through unchanged

This maintains the fix for ${\(0)} while handling list contexts correctly.

Impact on perl5_t/t/op/decl-refs.t with JPERL_EVAL_USE_INTERPRETER=1:
- Before regression: 270/408 passing (66.2%)
- After regression: 157/408 passing (38.5%) - ClassCastException crash
- After this fix: 272/408 passing (66.7%) - actually 2 tests better!

Impact on perl5_t/t/re/regexp.t with JPERL_EVAL_USE_INTERPRETER=1:
- Still passing: 1786/2210 (80.8%) - no regression

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fglock fglock merged commit e6ad88f into master Feb 20, 2026
2 checks passed
@fglock fglock deleted the fix/test-regressions branch February 20, 2026 10:27
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.

1 participant