Skip to content

fix(native-backend): decline SEH funclets, refresh stale corpora, drop setjmp-era attrs (#7302) - #7310

Merged
proggeramlug merged 1 commit into
mainfrom
fix/7310-native-seh-decline
Aug 3, 2026
Merged

fix(native-backend): decline SEH funclets, refresh stale corpora, drop setjmp-era attrs (#7302)#7310
proggeramlug merged 1 commit into
mainfrom
fix/7310-native-seh-decline

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Three follow-ups for the in-process LLVM backend, all found by auditing what it actually covers after the EH migration.

1. --target windows + try/catch was a hard compile error

Perry lowers exceptions to SEH funclets on windows-msvc (catchswitch/catchpad/catchret). The reader can build invoke/landingpad (added in #7307) but not the funclet forms — and it can't, cheaply: inkwell 0.9 exposes no build_catch_switch/build_catch_pad/build_catch_ret, only an opcode enum for reading them. Real support needs raw llvm-sys FFI.

Reproduced before the fix:

native IR construction failed in @perry_fn_..._basic:
in line: %r7 = catchswitch within none [label %eh.pad.5] unwind to caller

Those modules now decline to the textual path — costing only the in-process speedup — instead of failing the build. The decline is deliberately narrow (windows triple and a personality present), so #7307's macOS EH support is untouched; this is not the blanket personality bail that #7307 removed.

2. The tracked corpora were stale

spike_text.ll and batch_kernel.ll were captured before #7305 and still contained setjmp calls and the #0/#1 attribute groups. So the reader's primary gate was validating IR Perry no longer emits, while not covering the forms it does — the corpus tests passed the whole time, because they're fixed files.

Regenerated from current codegen. The spike corpus now carries 38 invoke edges and zero setjmp, and both corpora round-trip through native construction and the LLVM verifier.

3. The setjmp-era attribute handling is deleted

With the corpora refreshed, nothing emits or contains #0 (returns_twice) or #1 (noinline), so the reader's branches for them are gone — the losing mode stops compiling rather than lingering as an untested branch (CLAUDE.md kill policy). A corpus still carrying one now fails loudly as stale input, which is the point.

Verification (local, LLVM 22.1.4)

Every assertion the native-backend job makes, plus the EH arm:

subject verdict
spike.ts liveness + parity + byte-identical objects (21,393 B)
batch.ts, 3 units byte-identical merged objects (56,296 B)
try/catch program liveness + parity + byte-identical objects (46,745 B)
--target windows + try/catch declines cleanly (only the unrelated missing-perry_runtime.lib link error remains)

cargo test -p perry-codegen --features llvm-inprocess --lib: 569 passed, 0 failed.

Known limitation left open

SEH construction itself. It needs llvm-sys funclet FFI and can't be run-tested for Windows from a macOS host, so it stays a documented decline rather than a half-implementation. Worth doing when the backend moves toward becoming the default; today it's opt-in and the fallback is correct.

Summary by CodeRabbit

  • New Features

    • Windows exception-handling modules now fall back to a compatible compilation path instead of failing during native compilation.
    • Exception handling now uses the updated personality-based mechanism.
  • Bug Fixes

    • Refreshed LLVM test corpora to reflect current code generation.
    • Obsolete exception-handling attributes are no longer accepted, with stale inputs failing explicitly.
  • Documentation

    • Added a changelog entry covering the exception-handling migration follow-ups and verification results.

…p setjmp-era attrs (#7302)

1. --target windows + try/catch was a hard compile error: Perry lowers EH
   to catchswitch/catchpad/catchret there, which the reader cannot build
   (inkwell 0.9 has no funclet builders — real support needs llvm-sys
   FFI). Such modules now decline to the textual path. The decline is
   narrow (windows triple AND personality present), so #7307's macOS EH
   support is untouched.

2. spike_text.ll and batch_kernel.ll predated #7305 and still contained
   setjmp + the #0/#1 groups — the backend's primary gate was validating
   IR Perry no longer emits. Regenerated: the spike corpus now carries 38
   invoke edges and zero setjmp.

3. With the corpora refreshed, the reader's #0/#1 handling is dead and is
   deleted; stale input now fails loudly instead of being quietly
   accepted.

Byte-identical object verdicts on spike (21393 B), 3-unit batch (56296 B)
and the try/catch program (46745 B); 569 tests green.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c5dc788-a73a-4eff-9d44-b4a5bc8e2fb5

📥 Commits

Reviewing files that changed from the base of the PR and between 4bf29f1 and d2c0be5.

📒 Files selected for processing (6)
  • changelog.d/7310-native-seh-decline-and-corpus-refresh.md
  • crates/perry-codegen/src/codegen/mod.rs
  • crates/perry-codegen/src/dialect/mod.rs
  • crates/perry-codegen/src/module.rs
  • experiments/llvm-inprocess-spike/batch_kernel.ll
  • experiments/llvm-inprocess-spike/spike_text.ll

📝 Walkthrough

Walkthrough

The change adds Windows SEH-funclet fallback detection, removes obsolete LLVM attribute handling, and refreshes LLVM spike corpora for personality-based exception handling and updated Fibonacci lowering.

Changes

SEH and LLVM exception-handling migration

Layer / File(s) Summary
SEH-funclet detection and native fallback
crates/perry-codegen/src/module.rs, crates/perry-codegen/src/codegen/mod.rs
LlModule::needs_eh_funclets detects Windows modules with personality functions. Native unit and construction paths fall back to textual LLVM processing.
Legacy LLVM attribute removal
crates/perry-codegen/src/dialect/mod.rs, experiments/llvm-inprocess-spike/batch_kernel.ll, experiments/llvm-inprocess-spike/spike_text.ll
Legacy #0 and #1 attribute-group handling and definitions were removed. Unknown stale references now fail through the existing error path.
Personality-based corpus updates
experiments/llvm-inprocess-spike/batch_kernel.ll, experiments/llvm-inprocess-spike/spike_text.ll, changelog.d/7310-native-seh-decline-and-corpus-refresh.md
The corpora replace setjmp-era declarations with EH declarations, add invoke and landingpad unwind paths, update Fibonacci lowering, and document validation results.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • PerryTS/perry#7301: Extends the same in-process LLVM paths and updates related LLVM dialect and corpus behavior.
  • PerryTS/perry#7305: Follows the same invoke/landingpad exception-handling migration.
  • PerryTS/perry#7307: Changes the native LLVM paths that now receive SEH-funclet fallback handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main backend fallback, corpus refresh, and obsolete attribute removal changes.
Description check ✅ Passed The description covers the changes, rationale, verification results, and known limitation, but omits some template headings and checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 fix/7310-native-seh-decline

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 b50e857 into main Aug 3, 2026
28 of 41 checks passed
@proggeramlug
proggeramlug deleted the fix/7310-native-seh-decline branch August 3, 2026 13:37
proggeramlug added a commit that referenced this pull request Aug 12, 2026
…nfreeze the dialect corpora (#7998)

* fix(codegen): teach the in-process native backend today's RS4GC IR, refresh the frozen corpora

`PERRY_LLVM_INPROCESS=native` could not build any function containing an
RS4GC root slot — which is effectively every function. Every `main`
execution of `llvm-inprocess`'s native-backend job failed at

    in line: %r5 = alloca ptr addrspace(1)

The issue warned this was not a one-liner, and it was not: fixing each
shape only exposed the next. The family, in the order it surfaced:

  1. `basic_type` had no `addrspace(N)` arm — the reported failure.
  2. `ty_and_val` split `ptr addrspace(1) null` into type `ptr` and value
     `addrspace(1) null`. The qualifier belongs to the type but sits after
     a space, so OPERAND position needed the same treatment as type
     position.
  3. `constant` returned an addrspace(0) `null` regardless of the operand
     type, which the verifier rejects when stored into an
     `alloca ptr addrspace(1)`.
  4. Define lines carry `"frame-pointer"="non-leaf"` (a string attribute)
     and `gc "statepoint-example"` (which contains a space, so the
     whitespace-split attribute loop saw two junk tokens).
  5. Callsites carry `"gc-leaf-function"`.
  6. Landing pads are now `landingpad token cleanup`. `token` is not an
     inkwell `BasicType`, so that pad is built through llvm-sys. The
     `{ptr, i32} catch` shape still occurs and keeps its branch.

Two further defects were hiding behind the reader failure, both worse
than it:

**The native path returned assembly and called it an object.** The
statepoint backends compact the stack map at assembly time, so the plan
carries `-S`. The textual in-process path has always run the
rewrite-and-assemble step afterwards; the native and diff paths returned
the bytes straight to the object cache, and the link died with
`ld: unknown file type`. Factored into `linker::finish_native_emission`
and wired into all four native/diff emit sites.

**A natively-constructed module had NO GC strategy, so RS4GC never ran on
it.** `native_emit::synth_define_header` was a second, independent copy of
`LlFunction::to_ir`'s header renderer, written before
`"frame-pointer"="non-leaf"` and `gc "statepoint-example"` existed and
never updated. The result verifies, links and executes correctly on any
program that does not collect, while having no precise roots at all —
#7332's shape, and invisible to a behaviour-parity smoke arm by
construction. The two callers now share `LlFunction::define_header`, so
the next attribute reaches both; `function.rs` (compiled without the
`llvm-inprocess` feature, i.e. visible to per-PR CI) pins `to_ir`'s first
line against it.

**The corpora, which is the half worth more than the fix.** All three
tracked `.ll` files froze on 2026-08-03, 151 codegen commits earlier, and
contained zero `addrspace(1)`. `corpus_spike ... ok` proved the tests ran,
not that they test today's IR — CLAUDE.md's fourth way a gate cannot fail,
inside the liveness assert written to prevent it. The same thing had
happened nine days earlier (#7310, stale setjmp calls), which is the
argument against fixing it by hand again:

  * `scripts/refresh_llvm_inprocess_corpora.sh` regenerates all three from
    the built compiler (`--check` diffs instead of writing).
  * `scripts/check_llvm_corpus_currency.py` (added to `lint`) asserts every
    IR form the reader carries a dedicated branch for is PRESENT in the
    corpora, so a form that disappears fails and must be either refreshed
    or deleted per the kill policy. Sabotage-verified against the corpora
    this commit replaces: it names all 10 forms they lacked.
  * `llvm-inprocess.yml`'s existing age diagnostic was itself vacuous —
    `git log` on a depth-1 checkout printed `0` commits behind however
    stale the files were. Now `fetch-depth: 0`, and it fails loudly rather
    than printing a reassuring zero if history is ever missing again.

Corpora refreshed: 497 / 1082 / 420 `addrspace(1)` sites, EH corpus keeps
84 invoke edges and its personality clause. `dialect/mod.rs` crossed the
2000-line cap, so type/constant parsing moved to `dialect/types.rs`.

Validated locally against LLVM 22.1.4: 934 `perry-codegen` lib tests green
(all three corpus round-trips included); `PERRY_LLVM_INPROCESS=native`
compiles and runs the spike and the try/catch program with output
byte-identical to the textual backend.

Known remaining, NOT closed here and separate defects: `=diff` still
reports a byte mismatch on the spike (149,105 text vs 163,902 native — it
was 50,995 before the GC-strategy fix, i.e. the gap narrowed from "RS4GC
never ran" to a real but much smaller divergence), and the unit-split diff
arm fails with `call to undeclared @js_shadow_slot_set`. Neither was ever
reached in CI, because the native arm failed first.

Refs #7982.

* docs(changelog): fragment for #7998 (native backend RS4GC IR + corpus currency)

* docs(gc-handoff): KNOBS-NOTES for the #7991/#7982/#7737 batch

The knob-parse audit inventory (every GC-family env knob, how it parsed
before and after), the corpus-currency findings, and the #7737 triage —
including two things worth more than either fix:

  * `llvm-inprocess.yml`'s corpus-age diagnostic was itself vacuous. It asks
    `git log` how many IR-affecting commits landed since the corpora changed,
    on a depth-1 checkout — one commit to answer from, so it printed a
    confident 0 however stale the files were.
  * #7737 item 4's stated prerequisite has gone stale in the opposite
    direction: `gc-ratchet` has zero successes in its last 30 `main` runs and
    `gc-stress` failed the last three, so promoting either to required today
    would block every open PR.

---------

Co-authored-by: Ralph Küpper <ralph@skelpo.com>
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