Skip to content

Refactor bytecode compiler to use compileNode() helper#277

Merged
fglock merged 8 commits intomasterfrom
refactor-compileNode-helper
Mar 6, 2026
Merged

Refactor bytecode compiler to use compileNode() helper#277
fglock merged 8 commits intomasterfrom
refactor-compileNode-helper

Conversation

@fglock
Copy link
Owner

@fglock fglock commented Mar 6, 2026

Summary

  • Add compileNode(Node, int targetReg, int callContext) helper method to BytecodeCompiler that encapsulates the save/restore pattern for both targetOutputReg and currentCallContext
  • Add compileScalarOperand() helper in CompileOperator for the common scalar-context compilation pattern
  • Convert all save/restore sites in BytecodeCompiler.java, CompileOperator.java, CompileBinaryOperator.java, and CompileAssignment.java to use the new helpers
  • Eliminate the targetOutputRegStack (ArrayDeque-based) mechanism entirely
  • Fix interpreter issues: exists $hashref->{key}, double RHS compilation in hash/array assignments, local, __SUB__, and SUPER:: support
  • Fix $1 capture variable regression in eval blocks
  • Fix bytecode disassembler crashes with --disassemble --interpreter
  • Fix compound assignment in eval (e.g., ($x &= $y) .= 'x') by using compileNode() with explicit SCALAR context

Test plan

  • mvn compile passes
  • mvn test passes (all 156 tests)
  • op/bop.t maintains 490/522 (no regression from master)
  • re/pat.t maintains 1053/1296 (no regression from master)

Generated with Devin

fglock and others added 8 commits March 6, 2026 15:48
…ve/restore

Add compileNode(Node, int targetReg, int callContext) helper method that
encapsulates the save/restore pattern for targetOutputReg and currentCallContext.
Replace verbose manual save/restore boilerplate across BytecodeCompiler,
CompileOperator, and CompileBinaryOperator. Also add compileScalarOperand()
helper in CompileOperator for the common scalar-context compilation pattern.

This eliminates the targetOutputRegStack (ArrayDeque-based) mechanism and
reduces net code by ~280 lines while preserving identical behavior.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Replace all 51 .accept() calls with compileNode() using explicit context,
remove try/finally wrapper, eliminate 9 redundant intermediate context
restores, and rename savedContext to outerContext for the 4 read-only
comparison sites.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Set targetOutputReg before compiling subroutine/block bodies in compile()
so that allocateOutputRegister() can place results directly in the return
register. Convert remaining raw .accept() calls in CompileBinaryOperator
to compileNode() to properly scope targetOutputReg for intermediate
operands.

Reduces ALIAS opcodes from 31 to 4 in benchmark_lexical.pl disassembly.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
LOAD_GLOBAL_SCALAR for special variables like $1 returns a proxy object
that reads from RuntimeRegex.lastCaptureGroups. When RESTORE_REGEX_STATE
runs at the end of a block, it restores lastCaptureGroups to its previous
value, causing the proxy to return undef.

The fix is to use allocateRegister() instead of allocateOutputRegister()
for LOAD_GLOBAL_SCALAR, so that the ALIAS operation copies the value
before RESTORE_REGEX_STATE runs.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- Add local $hash{key} and local $array[index] support in interpreter
- Add __SUB__ field to RuntimeCode for self-reference in InterpretedCode
- Set __SUB__ when InterpretedCode subroutines are created
- Add __SUB__ operator handling in CompileOperator
- Fix local $Foo::x = value to use NameNormalizer for qualified names

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- Fix hash/array element assignment compiling RHS twice, causing
  side effects like shift() to execute twice
- Add support for exists $hashref->{key} and exists $arrayref->[idx]
  using fast HASH_EXISTS/ARRAY_EXISTS opcodes instead of slow path

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- Add bounds checking for stringPool access in RETRIEVE_BEGIN_HASH
- Add try-catch wrapper around disassembly loop to gracefully handle
  malformed bytecode instead of crashing
- Add SLOW_OP handler (deprecated but needed for completeness)

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
…ntext

The handleCompoundAssignment() method was using node.left.accept(this)
without setting SCALAR context, causing incorrect behavior when the
outer context was different (e.g., in eval blocks). This caused
expressions like `($x &= $y) .= "x"` to fail inside eval.

Also removes the incorrect LIST_TO_COUNT conversion which was destroying
lvalue references for non-simple left operands.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit 07964df into master Mar 6, 2026
2 checks passed
@fglock fglock deleted the refactor-compileNode-helper branch March 6, 2026 17:11
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