Skip to content

feat(trunk-ir): add arena IR rewrite infrastructure (#437)#458

Merged
Kroisse merged 3 commits intomainfrom
feature/rewrite-infrastructure-arena
Feb 26, 2026
Merged

feat(trunk-ir): add arena IR rewrite infrastructure (#437)#458
Kroisse merged 3 commits intomainfrom
feature/rewrite-infrastructure-arena

Conversation

@Kroisse
Copy link
Owner

@Kroisse Kroisse commented Feb 26, 2026

Summary

Implements Milestone 2 of the arena IR porting effort: walk utilities and a full pattern-based rewrite framework for the arena IR, analogous to the existing Salsa-based infrastructure but without lifetimes and without functional rebuilds.

Closes #437

What was added

  • arena/walk.rs: walk_region, walk_block, walk_op, walk_typed for recursive traversal. Uses ControlFlow<B, WalkAction> with Advance/Skip variants to give callers control over descent.
  • arena/rewrite/pattern.rs: ArenaRewritePattern trait — the arena counterpart of RewritePattern.
  • arena/rewrite/rewriter.rs: PatternRewriter with RAUW-based mutation methods: replace_op, insert_op, erase_op, add_module_op. Operand access via rewriter.operand(i) mirrors the Salsa convention.
  • arena/rewrite/type_converter.rs: ArenaTypeConverter for registering per-dialect type-lowering callbacks used during dialect conversion.
  • arena/rewrite/conversion_target.rs: ArenaConversionTarget with legality checks (legal / illegal / dynamically-legal operations) and a verify pass that collects IllegalOp errors.
  • arena/rewrite/applicator.rs: PatternApplicator with visitor-based fixpoint iteration. Snapshots block op lists before each pass; skips deleted ops by checking parent_block validity. Exposes ApplyResult with iteration count, total change count, and fixpoint status.
  • arena/rewrite/mod.rs: ArenaModule wrapper (thin OpRef newtype for core.module), verifies dialect/name on construction, exposes body, ops, first_block, and name helpers.
  • op_interface.rs: is_pure_arena, is_removable_arena, is_isolated_arena — arena-native helpers that delegate to the existing inventory-based registry checks, enabling DCE and isolation queries without Salsa.

Why

The Salsa-based rewrite infrastructure recreates entire IR nodes on each mutation. The arena IR uses direct in-place mutation + RAUW (Replace All Uses With), which removes the need for value maps and produces ~5x less boilerplate per pass. This milestone makes it possible to begin porting actual lowering passes (boxing, closures, evidence) to the arena backend.

Key design decisions

  • No lifetimes — operations are referenced via OpRef/TypeRef/BlockRef/RegionRef entity indices rather than borrowed Salsa-tracked references. The IrContext is always passed explicitly.
  • RAUW-based mutationreplace_op rewires all uses of the old results to the new op's results in-place, without building a side-table value map.
  • Visitor-based fixpointPatternApplicator iterates until no pattern fires or max_iterations is reached, consistent with MLIR's applyPatternsAndFoldGreedily approach.
  • WalkAction::Skip — callers can suppress descent into nested regions (e.g., to avoid crossing isolation boundaries).

Test plan

  • 47 arena unit tests passing (cargo nextest run -p trunk-ir)
  • Tests cover: walk traversal and Skip semantics, PatternRewriter RAUW correctness, ArenaConversionTarget legality checks, PatternApplicator fixpoint iteration and max-iterations guard, ArenaModule wrapper construction and accessors

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added arena-based IR rewrite framework with pattern application, fixpoint-driven applicator, and mutation rewriter.
    • New conversion-target system with legality checks and post-rewrite verification reporting illegal ops.
    • Pluggable type converter with materialization support for lowering.
    • Recursive IR traversal API with fine-grained descent control (WalkAction).
    • Arena module wrapper and TypeDataBuilder for easier module/type handling and tests.

Implements walk utilities and a full pattern-based rewrite framework
for the arena IR, analogous to the existing Salsa-based infrastructure:

- arena/walk.rs: walk_region/walk_block/walk_op/walk_typed with
  WalkAction (Advance/Skip) control via ControlFlow
- arena/rewrite/pattern.rs: ArenaRewritePattern trait
- arena/rewrite/rewriter.rs: PatternRewriter with RAUW-based mutations
  (replace_op, insert_op, erase_op, add_module_op)
- arena/rewrite/type_converter.rs: ArenaTypeConverter for dialect lowering
- arena/rewrite/conversion_target.rs: ArenaConversionTarget with
  legality checks (legal/illegal/dynamically-legal operations)
- arena/rewrite/applicator.rs: PatternApplicator with fixpoint iteration
- op_interface.rs: is_pure_arena, is_removable_arena, is_isolated_arena
  helpers delegating to the existing registry-based checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8016453 and eeef6e7.

📒 Files selected for processing (9)
  • crates/trunk-ir/src/arena/context.rs
  • crates/trunk-ir/src/arena/dialect/mod.rs
  • crates/trunk-ir/src/arena/mod.rs
  • crates/trunk-ir/src/arena/printer.rs
  • crates/trunk-ir/src/arena/rewrite/applicator.rs
  • crates/trunk-ir/src/arena/rewrite/mod.rs
  • crates/trunk-ir/src/arena/rewrite/type_converter.rs
  • crates/trunk-ir/src/arena/types.rs
  • crates/trunk-ir/src/arena/walk.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/trunk-ir/src/arena/mod.rs

📝 Walkthrough

Walkthrough

Adds an arena-based rewrite and traversal subsystem: pattern applicator and rewriter with fixpoint iteration, conversion target legality checks, a pluggable type converter/materializer, recursive IR walkers, ArenaModule helpers, and TypeData builder plus related arena query APIs.

Changes

Cohort / File(s) Summary
Pattern Rewriting Core
crates/trunk-ir/src/arena/rewrite/applicator.rs, crates/trunk-ir/src/arena/rewrite/pattern.rs, crates/trunk-ir/src/arena/rewrite/rewriter.rs
New PatternApplicator (fixpoint iterations & ApplyResult), ArenaRewritePattern trait, and PatternRewriter + Mutations + apply_mutations implementing RAUW-style mutation application and prefix/module op handling.
Conversion & Type Conversion
crates/trunk-ir/src/arena/rewrite/conversion_target.rs, crates/trunk-ir/src/arena/rewrite/type_converter.rs
Introduces ArenaConversionTarget with dynamic legality checks and verify reporting IllegalOp, and ArenaTypeConverter with conversion functions, MaterializeResult, and materializer support.
Rewrite Module & Exports
crates/trunk-ir/src/arena/rewrite/mod.rs, crates/trunk-ir/src/arena/mod.rs
New rewrite submodules exposed and re-exported (applicator, conversion_target, pattern, rewriter, type_converter) plus ArenaModule wrapper and walk module re-export added to arena public surface.
Traversal Infrastructure
crates/trunk-ir/src/arena/walk.rs
Adds recursive walkers: walk_region, walk_block, walk_op, walk_typed with WalkAction control (Advance/Skip) and ControlFlow-based early exit.
TypeData Builder & Tests
crates/trunk-ir/src/arena/types.rs, crates/trunk-ir/src/arena/context.rs, crates/trunk-ir/src/arena/dialect/mod.rs, crates/trunk-ir/src/arena/printer.rs
Adds TypeDataBuilder and updates tests to use builder APIs instead of direct TypeData construction; re-exports TypeDataBuilder.
Arena Queries / Op Interface
crates/trunk-ir/src/op_interface.rs
Adds arena variants of op queries: is_pure_arena, is_removable_arena, and is_isolated_arena to enable arena-based checks without a salsa DB.

Sequence Diagram(s)

sequenceDiagram
    participant App as PatternApplicator
    participant Ctx as IrContext
    participant Pat as ArenaRewritePattern
    participant Rw as PatternRewriter
    participant Conv as ArenaConversionTarget

    App->>App: Snapshot top-level ops
    loop iterate until fixpoint or max_iterations
        App->>Ctx: traverse module body (walk)
        loop per op
            App->>Pat: match_and_rewrite(&mut ctx, op, rewriter)
            alt pattern matches
                Pat->>Rw: insert/replace/erase/add_module_op
                Rw-->>App: mutations accumulated
                App->>Ctx: apply_mutations(ctx, op, mutations)
            else no match
                Pat-->>App: no-op
            end
        end
    end
    App->>Conv: verify(&ctx, module_body)
    Conv-->>App: list of IllegalOp (if any)
Loading
sequenceDiagram
    participant User as Caller
    participant TC as ArenaTypeConverter
    participant ConvFn as ConversionFn
    participant Mat as Materializer
    participant Ctx as IrContext

    User->>TC: convert_type(ctx, ty)
    TC->>ConvFn: try conversion f(ctx, ty)
    alt conversion returns Some
        ConvFn-->>TC: Some(new_ty)
        TC-->>User: return Some(new_ty)
    else none match
        ConvFn-->>TC: None
        TC-->>User: None
    end

    User->>TC: materialize(ctx, loc, value, from, to)
    TC->>Mat: call materializer(...)
    Mat->>Ctx: create ops, new value
    Ctx-->>Mat: MaterializeResult{value, ops}
    Mat-->>TC: Some(MaterializeResult)
    TC-->>User: MaterializeResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 Hopped into the arena with a pen so light,

I matched, I rewrote, then fixed the sight.
Types transformed and ops bid adieu—
Fixpoint found, the pass ran true.
A carrot for tests and code that’s bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(trunk-ir): add arena IR rewrite infrastructure (#437)' clearly and accurately summarizes the main change: introducing rewrite infrastructure for the arena IR.
Linked Issues check ✅ Passed The PR successfully addresses all core objectives from issue #437: removes Salsa lifetime parameters, implements PatternRewriter with direct IrContext mutation and RAUW, implements PatternApplicator with worklist-style iteration and op validation, and introduces supporting infrastructure (walk utilities, type converter, conversion target).
Out of Scope Changes check ✅ Passed Changes to context.rs, dialect/mod.rs, and printer.rs involve only TypeData construction migration to TypeDataBuilder, which is a supporting change enabling cleaner API usage in tests and the rewrite infrastructure itself.
Docstring Coverage ✅ Passed Docstring coverage is 96.39% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/rewrite-infrastructure-arena

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

@Kroisse Kroisse self-assigned this Feb 26, 2026
@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.54%. Comparing base (6e0bafc) to head (eeef6e7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #458   +/-   ##
=======================================
  Coverage   78.54%   78.54%           
=======================================
  Files          10       10           
  Lines        4935     4935           
=======================================
  Hits         3876     3876           
  Misses       1059     1059           
Components Coverage Δ
TrunkIR ∅ <ø> (∅)
Frontend ∅ <ø> (∅)
Middle End ∅ <ø> (∅)
Driver 78.54% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/trunk-ir/src/arena/walk.rs (1)

27-31: Consider removing per-node to_vec() snapshots in read-only walkers.

These clones allocate at every recursion step. Iterating directly over slices can reduce traversal overhead for large modules.

Allocation-light traversal sketch
-    let blocks: Vec<BlockRef> = ctx.region(region).blocks.to_vec();
-    for block in blocks {
+    for &block in ctx.region(region).blocks.iter() {
         walk_block(ctx, block, f)?;
     }

-    let ops: Vec<OpRef> = ctx.block(block).ops.to_vec();
-    for op in ops {
+    for &op in ctx.block(block).ops.iter() {
         walk_op(ctx, op, f)?;
     }

-    let regions: Vec<RegionRef> = ctx.op(op).regions.to_vec();
-    for region in regions {
+    for &region in ctx.op(op).regions.iter() {
         walk_region(ctx, region, f)?;
     }

Also applies to: 40-44, 58-60

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/trunk-ir/src/arena/walk.rs` around lines 27 - 31, The code currently
takes per-node snapshots via ctx.region(region).blocks.to_vec() which causes
needless allocations; change the loops to iterate directly over the slice
returned by ctx.region(region).blocks (e.g., for block in
ctx.region(region).blocks.iter() or for &block in ...) and call walk_block(ctx,
block, f) without cloning the Vec. Replace the three occurrences (the to_vec()
usage at the shown block and the similar sites around lines 40-44 and 58-60) so
the walker reads the slice directly to avoid per-recursion allocations while
preserving the same read-only semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/trunk-ir/src/arena/rewrite/mod.rs`:
- Around line 25-48: Make the ArenaModule wrapper safe by hiding its inner OpRef
and avoiding unchecked region indexing: change the struct from pub struct
ArenaModule(pub OpRef) to pub struct ArenaModule(OpRef) (make the field
private), update new to remain the only public constructor (keep returning
Option<Self>), change the methods signatures to take &self (pub fn op(&self) ->
OpRef) and pub fn body(&self, ctx: &IrContext) ->
Option<super::refs::RegionRef>, and implement body to check
ctx.op(self.0).regions.get(0).cloned() (or return Result with an explanatory
error) instead of unconditionally indexing; this prevents external construction
and panics when the module has no regions.

In `@crates/trunk-ir/src/arena/rewrite/type_converter.rs`:
- Around line 87-90: is_empty() currently returns true when conversions is empty
even if a materializer is present; update the method (pub fn is_empty(&self) ->
bool) to consider the materializer as well by returning true only when
conversions.is_empty() AND self.materializer.is_none(), so any set materializer
makes is_empty() return false; locate the method using the symbol is_empty and
adjust the boolean expression to include checking self.materializer.

---

Nitpick comments:
In `@crates/trunk-ir/src/arena/walk.rs`:
- Around line 27-31: The code currently takes per-node snapshots via
ctx.region(region).blocks.to_vec() which causes needless allocations; change the
loops to iterate directly over the slice returned by ctx.region(region).blocks
(e.g., for block in ctx.region(region).blocks.iter() or for &block in ...) and
call walk_block(ctx, block, f) without cloning the Vec. Replace the three
occurrences (the to_vec() usage at the shown block and the similar sites around
lines 40-44 and 58-60) so the walker reads the slice directly to avoid
per-recursion allocations while preserving the same read-only semantics.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e0bafc and 8016453.

📒 Files selected for processing (9)
  • crates/trunk-ir/src/arena/mod.rs
  • crates/trunk-ir/src/arena/rewrite/applicator.rs
  • crates/trunk-ir/src/arena/rewrite/conversion_target.rs
  • crates/trunk-ir/src/arena/rewrite/mod.rs
  • crates/trunk-ir/src/arena/rewrite/pattern.rs
  • crates/trunk-ir/src/arena/rewrite/rewriter.rs
  • crates/trunk-ir/src/arena/rewrite/type_converter.rs
  • crates/trunk-ir/src/arena/walk.rs
  • crates/trunk-ir/src/op_interface.rs

Kroisse and others added 2 commits February 26, 2026 09:29
- Make ArenaModule's inner OpRef field private; replace direct construction
  with ArenaModule::new().expect() in tests
- Change body() to return Option<RegionRef> instead of panicking when the
  module op has no regions; update all callers in applicator.rs
- Fix ArenaTypeConverter::is_empty() to also consider materializer presence
- Remove unnecessary to_vec() allocations in walk_region, walk_block, and
  walk_op (ctx is &IrContext, so no mutation possible during iteration)
- Add 9 unit tests across mod.rs and type_converter.rs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ction sites

Add `TypeDataBuilder` with a fluent API mirroring `OperationDataBuilder`.
Defaults to empty params and attrs (the common case), with `.param()`,
`.params()`, and `.attr()` methods for incremental construction.

Migrate all 16 `TypeData { ... }` struct literal sites across 8 files to
use the builder. Clean up now-unused `BTreeMap` and `smallvec` imports in
test modules where the builder removes the need for them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Kroisse Kroisse merged commit f135f06 into main Feb 26, 2026
12 checks passed
@Kroisse Kroisse deleted the feature/rewrite-infrastructure-arena branch February 26, 2026 01:16
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.

Port pattern rewrite core to arena IR

1 participant