Skip to content

Merge train 184: #10212 - #10214

Merged
proggeramlug merged 3 commits into
mainfrom
train184
Sep 13, 2026
Merged

proggeramlug merged 3 commits into
mainfrom
train184

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Merge train 184: lands #10212 (skip the unobservable exec lookup, keep small match scratch inline, copy ASCII captures in one pass; #10166) at head b96c44d2fa, plus the workspace version bump to 0.5.1558.

Both PR commits were cherry-picked onto 64f5249ac without conflicts. main has not moved since the PR's base, so the tree before the bump is byte-identical to the PR head.

Review: execute skips Get(R, "exec") only when is_valid_regex_ptr && regexp_view_uses_builtin, the same non-observable admission split's forward path uses. Inline match slots are charged to and released from the operation's MemoryBudget exactly as a heap buffer is. The ASCII capture copy reads the subject's ascii_bytes() under one view that neither allocates nor collects.

Instruction counts are on #10212 (perf stat instructions:u, per call, vs main): hoisted test −49.4 %, literal test −46.5 %, exec with captures −30.4 %, /g test −44.2 %, non-ASCII exec −2.9 %, non-ASCII exec loop −3.2 % per match. There are no regressions, and outputs match Node.

Local validation of this branch (perrymaster, Linux x86_64, --locked):

  • cargo metadata --locked, cargo fmt --all -- --check
  • cargo check -p perry-runtime --no-default-features --features full --lib (regex feature off): 0 warnings
  • cargo check -p perry-runtime --lib --tests: one warning outside the known global_this_webassembly.rs dead code. It is object/field_get_set/ic_miss/ic_slow.rs:544 ("value assigned to cache is never read"), which comes from main's own perf(codegen): collapse the generic property-get tower to two exits #10196 / generic property-get commits; this PR does not touch that file.
  • cargo test -p perry-runtime --lib -- --test-threads=1: 3739 passed, 1 failed. The failure is native_stack::tests::stack_top_respects_custom_thread_stack_sizes, red on main.
  • cargo clippy -p perry-runtime --lib --tests: the same 12 approx_constant errors. Compared with train 181's warning diff, the only addition is that same ic_slow.rs warning from main.
  • scripts/run_lint_gates.sh script tier: 76 of 77 pass. The failure is public benchmark evidence freshness, identical on main. gc_runtime_root_holders.py, check_file_size.sh and check_changeset_fragment.sh PerryTS/perry 10212 pass.

https://claude.ai/code/session_01Da12JXeG5XuVBma5yWp5C9

Summary by CodeRabbit

  • Performance

    • Improved RegExp.prototype.test() and exec() performance for standard, unmodified regular expressions.
    • Reduced overhead for smaller match results by avoiding unnecessary allocations.
    • Improved handling of ASCII capture groups for faster match-result creation.
    • Preserved expected behavior when regular-expression methods or prototypes are customized.
  • Release

    • Updated the documented and workspace version to 0.5.1558.

Ralph Küpper added 3 commits September 13, 2026 15:46
…tch inline, copy ASCII captures in one pass (#10166)

Instruction attribution on the #10166 probes put a hoisted short-string
`RegExp.prototype.test` at 23.7k instructions per call and `exec` with captures
at 40.2k, with the engine itself about 12% and 7% of those.

- `perex_dispatch::execute` performed `Get(R, "exec")` through the generic
  property path on every call, about half of each `test`. When the receiver is
  a RegExp and `regexp_view_uses_builtin` proves its own properties, prototype
  and `exec` are the untouched builtins, that Get reaches the builtin without
  running anything, so it is skipped. Any other receiver takes the Get.
- `find_near` heap-allocated match registers per call and noted them to the
  collector inside a try frame, about a tenth of each `test`. `Slots` holds up
  to 32 registers and 16 capture spans inline; frames and undo start empty and
  still grow through `rebuffer` onto heap buffers. Inline slots are charged to
  the operation's memory limit exactly as a buffer of the same count is, so
  the limit and peak accounting are unchanged.
- `copy_span_near` decoded each capture unit by unit through `BoundSpan` and
  re-encoded it, twice. On an ASCII subject UTF-16 offsets are byte offsets and
  the bytes are already the output encoding, so the span is copied as one byte
  range. Other subjects keep the existing path. No Perex change is needed.

Tests: `perex_dispatch_skips_the_exec_lookup_only_when_nothing_can_observe_it`
counts lookups — none for an untouched RegExp after its first call, and a
lookup that runs the override for an own `exec`, a reparented RegExp and a
replaced `RegExp.prototype.exec`. `perex_public_exec_captures_agree_across_
inline_and_heap_slots_and_storage` checks every group for inline and heap slot
counts, a backtracking alternation that grows frames, unset and empty groups,
behind an ASCII and a non-ASCII prefix, under forced evacuation. Three injected
faults are caught: dropping the builtin-view check (four dispatch and search
tests), truncating large programs into inline slots, and an off-by-one ASCII
copy (three capture tests). `perex_` and `regex::` suites: 168 passed.

Claude-Session: https://claude.ai/code/session_01RJkA4Fhqz9J5F5fzDk5HWv
@proggeramlug
proggeramlug merged commit 44e78de into main Sep 13, 2026
21 of 23 checks passed
@proggeramlug
proggeramlug deleted the train184 branch September 13, 2026 15:56
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 2eed7c7b-51ff-4aab-9ccf-e0aa0896b071

📥 Commits

Reviewing files that changed from the base of the PR and between 64f5249 and 8ff9373.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • CLAUDE.md
  • Cargo.toml
  • changelog.d/10212-regex-per-call.md
  • crates/perry-runtime/src/gc/tests/runtime_roots/perex_dispatch.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs
  • crates/perry-runtime/src/regex/perex_dispatch.rs
  • crates/perry-runtime/src/regex/perex_memory.rs
  • crates/perry-runtime/src/regex/perex_runtime.rs
  • crates/perry-runtime/src/regex/perex_strings.rs
  • scripts/gc_runtime_root_holders.json

📝 Walkthrough

Walkthrough

The regex runtime now skips unobservable builtin exec lookups, stores small match data inline, and copies ASCII captures directly. Tests cover lookup observability, inline and heap capture storage, and ASCII and non-ASCII inputs. Project metadata records version 0.5.1558.

Changes

RegExp execution optimizations

Layer / File(s) Summary
RegExp exec lookup dispatch
crates/perry-runtime/src/regex/perex_dispatch.rs, crates/perry-runtime/src/gc/tests/runtime_roots/perex_dispatch.rs, scripts/gc_runtime_root_holders.json
Known builtin RegExp receivers bypass Get(receiver, "exec"). Other receivers retain override handling. Tests verify lookup behavior for builtin, overridden, reparented, and restored prototypes.
Inline match storage and capture validation
crates/perry-runtime/src/regex/perex_memory.rs, crates/perry-runtime/src/regex/perex_runtime.rs, crates/perry-runtime/src/gc/tests/runtime_roots/perex_public.rs
Inline slots use memory-budget accounting. Larger buffers use heap storage. Capture tests cover inline slots, heap slots, unset groups, and ASCII or non-ASCII prefixes.
ASCII capture copying
crates/perry-runtime/src/regex/perex_strings.rs
ASCII spans are copied directly as bytes after span and size checks. Non-ASCII spans use the existing unit-copy path.
Release metadata
Cargo.toml, CLAUDE.md, changelog.d/10212-regex-per-call.md
The project version changes from 0.5.1557 to 0.5.1558. The changelog records the runtime optimizations.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant RegExpReceiver
  participant execute
  participant execOverride
  participant execute_with_resources
  RegExpReceiver->>execute: execute(receiver, input)
  alt known builtin receiver
    execute->>execute_with_resources: run builtin matcher
  else lookup required
    execute->>RegExpReceiver: Get(receiver, "exec")
    execute->>execOverride: handle override
    execOverride-->>execute: override result or builtin fallback
    execute->>execute_with_resources: run builtin matcher when no override applies
  end
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train184

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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