Skip to content

Commit 0cc6e12

Browse files
Kroisseclaude
andauthored
feat(trunk-ir): add arena IR transform passes and rewrite helpers (#439) (#459)
* feat(trunk-ir): add arena IR transform passes and rewrite helpers - Add DCE pass using use-chain analysis to eliminate dead operations - Add Global DCE pass with call graph analysis for dead function removal - Add SCF-to-CF lowering pass using split_block/inline_region_blocks/RAUW - Add arena rewrite helpers: split_block, inline_region_blocks, erase_op - Add add_block_arg and detach_op methods to IrContext - Fix register_pure_op\!/register_isolated_op\! macros to use raw_ident_str\! instead of stringify\!, enabling correct DCE detection for ops like arith.const Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(trunk-ir): fix two bugs in arena rewrite/transform helpers - `inline_region_blocks`: add early-return guard when `src_region == dest_region` to prevent a panic caused by aliased mutable borrow of the same region; add test `inline_region_blocks_same_region_is_noop` - `lower_scf_switch`: scan all case regions (not just the first) when inferring `result_ty` from yield values, so a yield in any case is found even when earlier cases are void; add test `lower_scf_switch_yield_in_later_case` Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(trunk-ir): fix global DCE dropping callees of abi functions Abi functions (marked with the `abi` attribute) are externally callable and must be treated as reachability roots so that their callees are not incorrectly eliminated. Previously, abi functions were preserved by a special guard in `filter_region`, but that guard only kept the functions themselves — any function called exclusively from an abi function was still silently removed. Fix the root cause: add abi functions to `reachability_roots` during analysis so the BFS traversal naturally keeps their entire transitive call graph alive. The now-redundant abi preservation guard in `filter_region` is removed. Rename the internal `entry_points` field to `reachability_roots` to reflect that abi functions, main/wasm exports, and custom roots are all treated uniformly as BFS starting points. The public API `GlobalDceConfig::extra_entry_points` is unchanged. Add test `abi_function_callees_are_reachable` to cover the fixed case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(trunk-ir): fix scf_to_cf lowering bugs for switch and yield - Remove stale `remove_op_from_block` call in `lower_scf_if`: after `split_block`, the scf op is already moved to the merge block, so the removal was a silent no-op; only `detach_op` is needed. - Fix `lower_scf_switch` result_ty determination: infer from the op's actual results instead of scanning yield ops, consistent with `lower_scf_if`. Since `scf.switch` has no result, `result_ty` is always `None`, eliminating the merge block / branch arg count mismatch. - Make `replace_yield_with_br` defensive by truncating yield values to the target block's expected arg count, preventing malformed `cf.br` ops. - Remove unused `find_yield_type` helper. - Update test: rename to `lower_scf_switch_no_result` and correct the assertion from 1 merge block arg to 0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f135f06 commit 0cc6e12

File tree

9 files changed

+2855
-4
lines changed

9 files changed

+2855
-4
lines changed

crates/trunk-ir/src/arena/context.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,19 @@ impl IrContext {
313313
self.block_arg_values[b].as_slice(&self.value_pool)
314314
}
315315

316+
/// Add a new argument to an existing block and return its `ValueRef`.
317+
pub fn add_block_arg(&mut self, block: BlockRef, arg: BlockArgData) -> ValueRef {
318+
let index = self.blocks[block].args.len() as u32;
319+
let ty = arg.ty;
320+
self.blocks[block].args.push(arg);
321+
let v = self.values.push(ValueData {
322+
def: ValueDef::BlockArg(block, index),
323+
ty,
324+
});
325+
self.block_arg_values[block].push(v, &mut self.value_pool);
326+
v
327+
}
328+
316329
/// Append an operation to the end of a block.
317330
///
318331
/// # Panics
@@ -361,6 +374,17 @@ impl IrContext {
361374
}
362375
}
363376

377+
/// Detach an operation from its parent block.
378+
///
379+
/// This is a convenience wrapper around `remove_op_from_block` that uses
380+
/// the operation's own `parent_block` field. Does nothing if the operation
381+
/// is not attached to any block.
382+
pub fn detach_op(&mut self, op: OpRef) {
383+
if let Some(block) = self.ops[op].parent_block {
384+
self.remove_op_from_block(block, op);
385+
}
386+
}
387+
364388
// ========================================================================
365389
// Region
366390
// ========================================================================

crates/trunk-ir/src/arena/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod ops;
1313
pub mod printer;
1414
pub mod refs;
1515
pub mod rewrite;
16+
pub mod transforms;
1617
pub mod types;
1718
pub mod walk;
1819

0 commit comments

Comments
 (0)