Path: blob/main/cranelift/codegen/src/traversals.rs
1693 views
//! Traversals over the IR.12use crate::ir;3use alloc::vec::Vec;4use core::fmt::Debug;5use core::hash::Hash;6use cranelift_entity::EntitySet;78/// A low-level DFS traversal event: either entering or exiting the traversal of9/// a block.10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]11pub enum Event {12/// Entering traversal of a block.13///14/// Processing a block upon this event corresponds to a pre-order,15/// depth-first traversal.16Enter,1718/// Exiting traversal of a block.19///20/// Processing a block upon this event corresponds to a post-order,21/// depth-first traversal.22Exit,23}2425/// A depth-first traversal.26///27/// This is a fairly low-level traversal type, and is generally intended to be28/// used as a building block for making specific pre-order or post-order29/// traversals for whatever problem is at hand.30///31/// This type may be reused multiple times across different passes or functions32/// and will internally reuse any heap allocations its already made.33///34/// Traversal is not recursive.35#[derive(Debug, Default, Clone)]36pub struct Dfs {37stack: Vec<(Event, ir::Block)>,38seen: EntitySet<ir::Block>,39}4041impl Dfs {42/// Construct a new depth-first traversal.43pub fn new() -> Self {44Self::default()45}4647/// Perform a depth-first traversal over the given function.48///49/// Yields pairs of `(Event, ir::Block)`.50///51/// This iterator can be used to perform either pre- or post-order52/// traversals, or a combination of the two.53pub fn iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsIter<'a> {54self.clear();55if let Some(e) = func.layout.entry_block() {56self.stack.push((Event::Enter, e));57}58DfsIter { dfs: self, func }59}6061/// Perform a pre-order traversal over the given function.62///63/// Yields `ir::Block` items.64pub fn pre_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPreOrderIter<'a> {65DfsPreOrderIter(self.iter(func))66}6768/// Perform a post-order traversal over the given function.69///70/// Yields `ir::Block` items.71pub fn post_order_iter<'a>(&'a mut self, func: &'a ir::Function) -> DfsPostOrderIter<'a> {72DfsPostOrderIter(self.iter(func))73}7475/// Clear this DFS, but keep its allocations for future reuse.76pub fn clear(&mut self) {77let Dfs { stack, seen } = self;78stack.clear();79seen.clear();80}81}8283/// An iterator that yields pairs of `(Event, ir::Block)` items as it performs a84/// depth-first traversal over its associated function.85pub struct DfsIter<'a> {86dfs: &'a mut Dfs,87func: &'a ir::Function,88}8990impl Iterator for DfsIter<'_> {91type Item = (Event, ir::Block);9293fn next(&mut self) -> Option<(Event, ir::Block)> {94loop {95let (event, block) = self.dfs.stack.pop()?;9697if event == Event::Enter {98let first_time_seeing = self.dfs.seen.insert(block);99if !first_time_seeing {100continue;101}102103self.dfs.stack.push((Event::Exit, block));104self.dfs.stack.extend(105self.func106.block_successors(block)107// Heuristic: chase the children in reverse. This puts108// the first successor block first in the postorder, all109// other things being equal, which tends to prioritize110// loop backedges over out-edges, putting the edge-block111// closer to the loop body and minimizing live-ranges in112// linear instruction space. This heuristic doesn't have113// any effect on the computation of dominators, and is114// purely for other consumers of the postorder we cache115// here.116.rev()117// This is purely an optimization to avoid additional118// iterations of the loop, and is not required; it's119// merely inlining the check from the outer conditional120// of this case to avoid the extra loop iteration. This121// also avoids potential excess stack growth.122.filter(|block| !self.dfs.seen.contains(*block))123.map(|block| (Event::Enter, block)),124);125}126127return Some((event, block));128}129}130}131132/// An iterator that yields `ir::Block` items during a depth-first, pre-order133/// traversal over its associated function.134pub struct DfsPreOrderIter<'a>(DfsIter<'a>);135136impl Iterator for DfsPreOrderIter<'_> {137type Item = ir::Block;138139fn next(&mut self) -> Option<Self::Item> {140loop {141match self.0.next()? {142(Event::Enter, b) => return Some(b),143(Event::Exit, _) => continue,144}145}146}147}148149/// An iterator that yields `ir::Block` items during a depth-first, post-order150/// traversal over its associated function.151pub struct DfsPostOrderIter<'a>(DfsIter<'a>);152153impl Iterator for DfsPostOrderIter<'_> {154type Item = ir::Block;155156fn next(&mut self) -> Option<Self::Item> {157loop {158match self.0.next()? {159(Event::Exit, b) => return Some(b),160(Event::Enter, _) => continue,161}162}163}164}165166#[cfg(test)]167mod tests {168use super::*;169use crate::cursor::{Cursor, FuncCursor};170use crate::ir::{Function, InstBuilder, TrapCode, types::I32};171172#[test]173fn test_dfs_traversal() {174let _ = env_logger::try_init();175176let mut func = Function::new();177178let block0 = func.dfg.make_block();179let v0 = func.dfg.append_block_param(block0, I32);180let block1 = func.dfg.make_block();181let block2 = func.dfg.make_block();182let block3 = func.dfg.make_block();183184let mut cur = FuncCursor::new(&mut func);185186// block0(v0):187// br_if v0, block2, trap_block188cur.insert_block(block0);189cur.ins().brif(v0, block2, &[], block3, &[]);190191// block3:192// trap user0193cur.insert_block(block3);194cur.ins().trap(TrapCode::unwrap_user(1));195196// block1:197// v1 = iconst.i32 1198// v2 = iadd v0, v1199// jump block0(v2)200cur.insert_block(block1);201let v1 = cur.ins().iconst(I32, 1);202let v2 = cur.ins().iadd(v0, v1);203cur.ins().jump(block0, &[v2.into()]);204205// block2:206// return v0207cur.insert_block(block2);208cur.ins().return_(&[v0]);209210let mut dfs = Dfs::new();211212assert_eq!(213dfs.iter(&func).collect::<Vec<_>>(),214vec![215(Event::Enter, block0),216(Event::Enter, block2),217(Event::Exit, block2),218(Event::Enter, block3),219(Event::Exit, block3),220(Event::Exit, block0)221],222);223}224225#[test]226fn multiple_successors_to_the_same_block() {227let _ = env_logger::try_init();228229let mut func = Function::new();230231let block0 = func.dfg.make_block();232let block1 = func.dfg.make_block();233234let mut cur = FuncCursor::new(&mut func);235236// block0(v0):237// v1 = iconst.i32 36238// v2 = iconst.i32 42239// br_if v0, block1(v1), block1(v2)240cur.insert_block(block0);241let v0 = cur.func.dfg.append_block_param(block0, I32);242let v1 = cur.ins().iconst(ir::types::I32, 36);243let v2 = cur.ins().iconst(ir::types::I32, 42);244cur.ins()245.brif(v0, block1, &[v1.into()], block1, &[v2.into()]);246247// block1(v3: i32):248// return v3249cur.insert_block(block1);250let v3 = cur.func.dfg.append_block_param(block1, I32);251cur.ins().return_(&[v3]);252253let mut dfs = Dfs::new();254255// We should only enter `block1` once.256assert_eq!(257dfs.iter(&func).collect::<Vec<_>>(),258vec![259(Event::Enter, block0),260(Event::Enter, block1),261(Event::Exit, block1),262(Event::Exit, block0),263],264);265266// We should only iterate over `block1` once in a pre-order traversal.267assert_eq!(268dfs.pre_order_iter(&func).collect::<Vec<_>>(),269vec![block0, block1],270);271272// We should only iterate over `block1` once in a post-order traversal.273assert_eq!(274dfs.post_order_iter(&func).collect::<Vec<_>>(),275vec![block1, block0],276);277}278}279280281