Add HASH_DEREF_FETCH and ARRAY_DEREF_FETCH superoperators#304
Merged
Add HASH_DEREF_FETCH and ARRAY_DEREF_FETCH superoperators#304
Conversation
These superoperators combine multiple bytecode instructions into single
operations for better interpreter performance:
- HASH_DEREF_FETCH (opcode 381): Combines DEREF_HASH + LOAD_STRING + HASH_GET
for patterns like $hashref->{key} with bareword or string literal keys
- ARRAY_DEREF_FETCH (opcode 382): Combines DEREF_ARRAY + LOAD_INT + ARRAY_GET
for patterns like $arrayref->[n] with integer literal indices
Based on bytecode analysis of ExifTool tests, HASH_DEREF_FETCH alone
eliminates ~2,498 instruction sequences (~7.3% of hash operations).
Design document: dev/design/superoperators.md
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
- Add emitHashDerefGet() and emitArrayDerefGet() helpers in BytecodeCompiler
- Refactor handleGeneralHashAccess() and handleGeneralArrayAccess() to use helpers
- Refactor CompileBinaryOperator -> operator handling to use helpers
- Enables superoperators for both $h->{a}{b} (implicit arrows) and $h->{a}->{b} (explicit arrows)
- Reduces code duplication across 3 call sites
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
…ashAccess
The superoperators (ARRAY_DEREF_FETCH, HASH_DEREF_FETCH) expect a scalar
containing a reference, but handleGeneralArrayAccess and handleGeneralHashAccess
can receive a RuntimeList (e.g., from `(caller)[0]`).
This caused `(caller)[0]` and similar expressions to fail with:
Can't use string ("...") as an ARRAY ref while "strict refs" in use
Fix: Keep superoperators only in the -> operator handler (CompileBinaryOperator)
where the left side is always a scalar reference. For handleGeneralArrayAccess
and handleGeneralHashAccess, use the original DEREF_ARRAY/HASH + ARRAY/HASH_GET
instruction sequence which correctly handles all input types.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
- Add HASH_DEREF_FETCH_NONSTRICT (383) and ARRAY_DEREF_FETCH_NONSTRICT (384) - Update BytecodeInterpreter with handlers for new opcodes - Update BytecodeCompiler emitHashDerefGet/emitArrayDerefGet to use NONSTRICT variants when strict refs is not enabled - Add disassembler support for NONSTRICT superoperators - Fix (expr)[index] handling in CompileBinaryOperator (ListNode transform) Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
…lHashAccess
Now that ListNode cases like (caller)[0] are transformed to array literal
+ arrow deref before reaching these handlers, it is safe to use superoperators.
- handleGeneralArrayAccess: uses emitArrayDerefGet() for chained array access
- handleGeneralHashAccess: uses emitHashDerefGet() for chained hash access
- Removed redundant code that was duplicating the helper logic
- Changed handleGeneralArrayAccess to use SCALAR context (not LIST)
Example improvement for $v[1]{a}{b}{c}->[2]:
- Before: 50 shorts (DEREF_HASH + LOAD_STRING + HASH_GET sequences)
- After: 32 shorts (HASH_DEREF_FETCH_NONSTRICT superoperators)
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <noreply@cognition.ai>
Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
Matches JVM backend behavior (EmitLiteral.java line 55-56): 'Perl semantics: array literal elements are always evaluated in LIST context' This fixes regressions in op/bop.t tests 36-38 where (keys %h)[0] was incorrectly returning the count instead of the first key, because keys was evaluated in SCALAR context. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <noreply@cognition.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds two superoperators to the bytecode interpreter that combine multiple instruction sequences into single operations:
HASH_DEREF_FETCH (opcode 381)
DEREF_HASH + LOAD_STRING + HASH_GETHASH_DEREF_FETCH rd hashref_reg key_string_idx$hashref->{key}with bareword or string literal keysARRAY_DEREF_FETCH (opcode 382)
DEREF_ARRAY + LOAD_INT + ARRAY_GETARRAY_DEREF_FETCH rd arrayref_reg index_immediate$arrayref->[n]with integer literal indicesFiles Changed
Opcodes.java: Added opcode constants 381-384 (strict and non-strict variants)CompileBinaryOperator.java: Pattern detection for->operatorBytecodeCompiler.java: Helper methods and pattern detectionBytecodeInterpreter.java: Execution handlers for all superoperatorsDisassemble.java: Disassembly supportExample
Before (3 instructions):
After (1 instruction):
Bug Fix: RuntimeList Handling
Fixed a regression where
(caller)[0]and similar expressions failed with:Root cause: Superoperators were incorrectly used in
handleGeneralArrayAccess()where the input could be a RuntimeList (not a scalar reference).
Fix: Superoperators are now only used in the
->operator handler where theleft side is always compiled in SCALAR context.
Test plan
./gradlew build./gradlew test$hashref->{key}returns correct value$arrayref->[n]returns correct value$data->{users}->[0]->{name}(caller)[0]works correctlyGenerated with Devin