Skip to content

Merge train 205: perex 0.1.7 and a lent regex scratch (v0.5.1583) - #10396

Merged
proggeramlug merged 6 commits into
mainfrom
train205r
Sep 16, 2026
Merged

proggeramlug merged 6 commits into
mainfrom
train205r

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

This train lands #10372 as v0.5.1583. It sits on 872ce07e24 and cherry-picks clean.

Five source commits, each verified to preserve its patch-id and authorship.

No train repairs

All five source commits are exact patch-id cherry-picks with authorship preserved byte-for-byte (name, email and author date all unchanged). The train adds only the version bump.

Worth naming the fifth one, because it is the gate this change had to satisfy: the lent scratch is a runtime-side cache of a heap pointer, which is a GC root the static checker cannot see. It has to be declared with perry_thread_local! and carry a verdict in scripts/gc_runtime_root_holders.json, or the holders gate fails. The author's own 6b9d09a198 does exactly that, and it is in this train.

Validation

Validated head c091bb1807. Five-package release build pinned and hash-verified, and re-verified after the gap run (artifacts_match_pin_after_gap) so nothing rebuilt underneath the fixtures.

  • Crate suites: codegen 1563, runtime 3970, stdlib 139, hir 431, transform 137, cli 1139 — all green except main's one known runtime failure.
  • All nine preflight gates pass, including both raw-handle ratchet invocations (bare and --no-raise-vs), the holders gate, and the unrooted-local-shape check against main.
  • Gap: filters regex, regexp, unicode — 42 fixture selections, 100% parity on each, snapshot clean. That is the right exposure for this change: it swaps the regex engine version and changes scratch lifetime, so every fixture that compiles a pattern is in scope.

Remaining red on the source PR is entirely inherited from main: gap-suite shards 3, 4 and 5 (the three long-standing regressions — test_gap_iterator_prototype_next_patch, test_gap_2899_2779_2777_static_helpers, test_gap_disposablestack_2875), benchmark-evidence freshness in lint, the pre-existing rustls RUSTSEC-2026-0285 advisory, and the pr-gate fan-in over those. #10387 fixes all three gap shards and is the next train.

Before merging, the pushed head and unchanged main are checked again. After merging, the rewritten commits are checked for preserved authorship and the main tree must match the validated train exactly.

Summary by CodeRabbit

  • Performance

    • Improved regular expression search performance, including faster .test() and exec operations.
    • Reduced temporary memory allocation during typical searches while preserving fallback behavior for larger patterns.
  • New Features

    • Expanded v-flag support for string properties, string members, and Unicode set operators.
    • Start-anchored regular expressions now avoid unnecessary alternate starting positions.
  • Bug Fixes

    • Updated runtime behavior so valid Unicode set expressions compile and match successfully.

Ralph Küpper added 6 commits September 16, 2026 23:37
Three unadopted versions over the pinned 0.1.4:

* 0.1.5 — a start-anchored program tries only its first start. Worth a flat
  ~870-960 instructions per call on the `.test()` probes of #10166, measured
  against 0.1.4 on the same host.
* 0.1.6 — `Search::restart_at`, for a host loop that walks one subject, and
  `impl ScratchOwner for &mut O`, which lets a host lend scratch instead of
  giving it up. The next commit takes the second; restart_at is separate work.
* 0.1.7 — the `v` flag is complete: string members `[\q{abc|de}]` and the
  seven properties of strings. Perex reports 48,718 Test262 cases compared
  with no differences and nothing unsupported.

0.1.7 bumps the program format (HEADER 10 -> 11 words, VERSION 12 -> 13, a new
SEQUENCE instruction and a filter section), and older programs are rejected
outright. Perry has nothing to migrate: programs exist only as `GcProgram` /
`ProgramCell` on the GC heap, and no cache keys on program words — not
`.perry-cache`, not the auto-optimize cache, which key on source and objects.
`State`, `Frame` and `Phase` are unchanged; `Shape`s header copy grows one
…g one per call

#10166's attribution put a third of a short `.test()` in scratch the call never
needed to build: `find_near` constructed a `MatchBuffers` per call, a
32-register inline array zeroed and then moved by value into `Search`, which
disassembled to `mov $0x150,%edx; call memcpy` — 336 bytes at every call.
Nothing in that scratch depends on the subject, and a search initializes its
own live state, so perex 0.1.6's `impl ScratchOwner for &mut O` lets one
per-thread cell serve every search.

The cell keeps whatever frames and undo length an earlier call needed, which
removes the other half of the cost: `PERRY_REGEX_DIAG` counted a scratch growth
on nearly every search, so each call was rebuffering as well as constructing.
In steady state a loop grows nothing and constructs nothing. A search that asks
for more than the cell holds grows it for the next call and lets this one run
the owned path from the budget it entered on, so a single call charges exactly
the work it charges today.

Reentrancy is a runtime borrow rather than the compile-time one perex gives a
single frame: a nested regex — a replacer callback that matches, or a poll that
re-enters — finds the cell borrowed and takes the owned path, so two searches
never share slots. The operation's memory limit still sees the slots: the lent
path takes a `Charge` for what it lends, exactly as the owner it replaces did.
No GC pointer is stored in the cell.

INLINE_REGISTERS drops 32 -> 8 for the owned path, which is now only a
fallback; the lent cell keeps 32 in LENT_REGISTERS, since it is allocated once
per thread rather than moved per call.
`perex_host_failures_release_scratch_and_preserve_consumed_work` used
`[a--b]` under `v` as its Unsupported witness, on the note that the union
grammar compiled but the set *operators* did not. 0.1.7 implements them, so
that arm now compiles successfully and the test failed on its own assertion
that the compile errored.

The cleanup coverage it was there for is unchanged: compile failures still
release scratch and keep the work they charged, exercised by the remaining
syntax-error and exhausted-work arms. `[a--b]` becomes a positive case
instead — a real difference, matching the `a` at index 1 of "ba" and not the
`b` at 0 — so the newly supported grammar is asserted rather than dropped.
check_thread_locals rejects a raw thread_local! in perry-runtime (#7469): the
address belongs in this thread's hot cache instead of costing a _tlv_get_addr
call on every access. That applies with force here — every search on the thread
reads this cell, which is the opposite of what a cold declaration looks like —
so it is declared hot, with a const initializer.
@coderabbitai

coderabbitai Bot commented Sep 16, 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: 3dd5a04d-d63f-4c2d-9ea5-004381b0bcb4

📥 Commits

Reviewing files that changed from the base of the PR and between 872ce07 and c091bb1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • CLAUDE.md
  • Cargo.toml
  • changelog.d/10372-perex-017-lent-scratch.md
  • crates/perry-runtime/src/gc/tests/runtime_roots/perex_execution.rs
  • crates/perry-runtime/src/regex/perex_runtime.rs
  • scripts/gc_runtime_root_holders.json

📝 Walkthrough

Walkthrough

The workspace updates to version 0.5.1583 and perex 0.1.7. The regex runtime adds thread-local lent scratch reuse with owned-buffer fallback. Runtime tests now validate v-flag set operators.

Changes

Perex release and runtime scratch

Layer / File(s) Summary
Release and perex version updates
CLAUDE.md, Cargo.toml, changelog.d/10372-perex-017-lent-scratch.md
The workspace and documented versions change to 0.5.1583. The perex dependency changes to 0.1.7. The changelog records the perex and regex changes.
Thread-local lent search path
crates/perry-runtime/src/regex/perex_runtime.rs, scripts/gc_runtime_root_holders.json
Regex searches can use thread-local scratch storage for searches with up to 32 registers. Capacity errors and active borrows fall back to owned buffers. The scratch storage is classified as non-GC data.
Runtime validation for v-flag support
crates/perry-runtime/src/gc/tests/runtime_roots/perex_execution.rs
The test now compiles and matches [a--b] with the v flag. Failure checks retain syntax and work-limit cases instead of expecting unsupported Unicode sets.

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

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant find_near
  participant ScratchCell
  participant Search
  participant MatchBuffers
  find_near->>ScratchCell: attempt lent search
  ScratchCell->>Search: run search with shared scratch
  Search-->>ScratchCell: return match or capacity error
  ScratchCell-->>find_near: return Lent::Done or Lent::Fallback
  find_near->>MatchBuffers: use owned buffers after fallback
Loading
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train205r

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.

@proggeramlug
proggeramlug merged commit f9c3e51 into main Sep 16, 2026
33 of 36 checks passed
@proggeramlug
proggeramlug deleted the train205r branch September 16, 2026 22:21
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