use crate::{
basic_block::BasicBlock,
context::{Context, Ptr},
operation::Operation,
region::Region,
};
#[derive(Clone, Copy, Debug)]
pub enum Order {
PreOrder,
PostOrder,
}
#[derive(Clone, Copy, Debug)]
pub enum Direction {
Forward,
Reverse,
}
pub struct WalkConfig {
pub regions_in_op: (Order, Direction),
pub ops_in_block: (Order, Direction),
pub blocks_in_region: (Order, Direction),
}
pub enum IRNode {
Operation(Ptr<Operation>),
BasicBlock(Ptr<BasicBlock>),
Region(Ptr<Region>),
}
pub mod interruptible {
use std::ops::ControlFlow;
use crate::{
basic_block::BasicBlock,
context::{Context, Ptr},
linked_list::{ContainsLinkedList, LinkedList},
operation::Operation,
region::Region,
};
use super::*;
pub enum WalkContinue {
Advance,
Skip,
}
impl WalkContinue {
pub fn is_advance(&self) -> bool {
matches!(self, Self::Advance)
}
pub fn is_skip(&self) -> bool {
matches!(self, Self::Skip)
}
}
pub type WalkResult<B> = ControlFlow<B, WalkContinue>;
pub fn walk_break<B>(b: B) -> WalkResult<B> {
WalkResult::Break(b)
}
pub fn walk_advance<B>() -> WalkResult<B> {
WalkResult::Continue(WalkContinue::Advance)
}
pub fn walk_skip<B>() -> WalkResult<B> {
WalkResult::Continue(WalkContinue::Skip)
}
pub mod mutable {
use super::*;
pub type WalkerCallback<State, B> = fn(&mut Context, &mut State, IRNode) -> WalkResult<B>;
pub fn walk_op<State, B>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Operation>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.regions_in_op.0 {
let c = callback(ctx, state, IRNode::Operation(root))?;
if c.is_skip() {
return walk_advance();
}
}
let num_regions = root.deref(ctx).num_regions();
match config.regions_in_op.1 {
Direction::Forward => {
for reg_idx in 0..num_regions {
let region = {
let root_ref = &*root.deref(ctx);
assert!(
reg_idx < root_ref.num_regions(),
"Region deleted during walk"
);
root_ref.get_region(reg_idx)
};
walk_region(ctx, state, config, region, callback)?;
}
}
Direction::Reverse => {
for i in (0..num_regions).rev() {
let region = {
let root_ref = &*root.deref(ctx);
assert!(i < root_ref.num_regions(), "Region deleted during walk");
root_ref.get_region(i)
};
walk_region(ctx, state, config, region, callback)?;
}
}
}
if let Order::PostOrder = config.regions_in_op.0 {
callback(ctx, state, IRNode::Operation(root))?;
}
walk_advance()
}
pub fn walk_region<State, B>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Region>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.blocks_in_region.0 {
let c = callback(ctx, state, IRNode::Region(root))?;
if c.is_skip() {
return walk_advance();
}
}
match config.blocks_in_region.1 {
Direction::Forward => {
let mut cur_block_opt = root.deref(ctx).get_head();
while let Some(cur_block) = cur_block_opt {
walk_block(ctx, state, config, cur_block, callback)?;
cur_block_opt = cur_block.deref(ctx).get_next();
}
}
Direction::Reverse => {
let mut cur_block_opt = root.deref(ctx).get_tail();
while let Some(cur_block) = cur_block_opt {
walk_block(ctx, state, config, cur_block, callback)?;
cur_block_opt = cur_block.deref(ctx).get_prev();
}
}
}
if let Order::PostOrder = config.blocks_in_region.0 {
callback(ctx, state, IRNode::Region(root))?;
}
walk_advance()
}
pub fn walk_block<State, B>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<BasicBlock>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.ops_in_block.0 {
let c = callback(ctx, state, IRNode::BasicBlock(root))?;
if c.is_skip() {
return walk_advance();
}
}
match config.ops_in_block.1 {
Direction::Forward => {
let mut cur_op_opt = root.deref(ctx).get_head();
while let Some(cur_op) = cur_op_opt {
walk_op(ctx, state, config, cur_op, callback)?;
cur_op_opt = cur_op.deref(ctx).get_next();
}
}
Direction::Reverse => {
let mut cur_op_opt = root.deref(ctx).get_tail();
while let Some(cur_op) = cur_op_opt {
walk_op(ctx, state, config, cur_op, callback)?;
cur_op_opt = cur_op.deref(ctx).get_prev();
}
}
}
if let Order::PostOrder = config.ops_in_block.0 {
callback(ctx, state, IRNode::BasicBlock(root))?;
}
walk_advance()
}
}
pub mod immutable {
use super::*;
pub type WalkerCallback<State, B> = fn(&Context, &mut State, IRNode) -> WalkResult<B>;
pub fn walk_op<State, B>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Operation>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.regions_in_op.0 {
let c = callback(ctx, state, IRNode::Operation(root))?;
if c.is_skip() {
return walk_advance();
}
}
let num_regions = root.deref(ctx).num_regions();
match config.regions_in_op.1 {
Direction::Forward => {
for reg_idx in 0..num_regions {
let region = {
let root_ref = &*root.deref(ctx);
assert!(
reg_idx < root_ref.num_regions(),
"Region deleted during walk"
);
root_ref.get_region(reg_idx)
};
walk_region(ctx, state, config, region, callback)?;
}
}
Direction::Reverse => {
for i in (0..num_regions).rev() {
let region = {
let root_ref = &*root.deref(ctx);
assert!(i < root_ref.num_regions(), "Region deleted during walk");
root_ref.get_region(i)
};
walk_region(ctx, state, config, region, callback)?;
}
}
}
if let Order::PostOrder = config.regions_in_op.0 {
callback(ctx, state, IRNode::Operation(root))?;
}
walk_advance()
}
pub fn walk_region<State, B>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Region>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.blocks_in_region.0 {
let c = callback(ctx, state, IRNode::Region(root))?;
if c.is_skip() {
return walk_advance();
}
}
match config.blocks_in_region.1 {
Direction::Forward => {
let mut cur_block_opt = root.deref(ctx).get_head();
while let Some(cur_block) = cur_block_opt {
walk_block(ctx, state, config, cur_block, callback)?;
cur_block_opt = cur_block.deref(ctx).get_next();
}
}
Direction::Reverse => {
let mut cur_block_opt = root.deref(ctx).get_tail();
while let Some(cur_block) = cur_block_opt {
walk_block(ctx, state, config, cur_block, callback)?;
cur_block_opt = cur_block.deref(ctx).get_prev();
}
}
}
if let Order::PostOrder = config.blocks_in_region.0 {
callback(ctx, state, IRNode::Region(root))?;
}
walk_advance()
}
pub fn walk_block<State, B>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<BasicBlock>,
callback: WalkerCallback<State, B>,
) -> WalkResult<B> {
if let Order::PreOrder = config.ops_in_block.0 {
let c = callback(ctx, state, IRNode::BasicBlock(root))?;
if c.is_skip() {
return walk_advance();
}
}
match config.ops_in_block.1 {
Direction::Forward => {
let mut cur_op_opt = root.deref(ctx).get_head();
while let Some(cur_op) = cur_op_opt {
walk_op(ctx, state, config, cur_op, callback)?;
cur_op_opt = cur_op.deref(ctx).get_next();
}
}
Direction::Reverse => {
let mut cur_op_opt = root.deref(ctx).get_tail();
while let Some(cur_op) = cur_op_opt {
walk_op(ctx, state, config, cur_op, callback)?;
cur_op_opt = cur_op.deref(ctx).get_prev();
}
}
}
if let Order::PostOrder = config.ops_in_block.0 {
callback(ctx, state, IRNode::BasicBlock(root))?;
}
walk_advance()
}
}
}
pub mod uninterruptible {
use super::*;
pub mod mutable {
use super::*;
pub type WalkerCallback<State> = fn(&mut Context, &mut State, IRNode);
pub fn walk_op<State>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Operation>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ = interruptible::mutable::walk_op(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
pub fn walk_region<State>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Region>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ =
interruptible::mutable::walk_region(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
pub fn walk_block<State>(
ctx: &mut Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<BasicBlock>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ =
interruptible::mutable::walk_block(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
}
pub mod immutable {
use super::*;
pub type WalkerCallback<State> = fn(&Context, &mut State, IRNode);
pub fn walk_op<State>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Operation>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ = interruptible::immutable::walk_op(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
pub fn walk_region<State>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<Region>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ =
interruptible::immutable::walk_region(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
pub fn walk_block<State>(
ctx: &Context,
state: &mut State,
config: &WalkConfig,
root: Ptr<BasicBlock>,
callback: WalkerCallback<State>,
) {
struct S<'a, State> {
state: &'a mut State,
callback: WalkerCallback<State>,
}
let mut s = S { state, callback };
let _ =
interruptible::immutable::walk_block(ctx, &mut s, config, root, |ctx, s, node| {
(s.callback)(ctx, s.state, node);
interruptible::walk_advance::<()>()
});
}
}
}
pub const WALKCONFIG_PREORDER_FORWARD: WalkConfig = WalkConfig {
regions_in_op: (Order::PreOrder, Direction::Forward),
ops_in_block: (Order::PreOrder, Direction::Forward),
blocks_in_region: (Order::PreOrder, Direction::Forward),
};
pub const WALKCONFIG_POSTORDER_FORWARD: WalkConfig = WalkConfig {
regions_in_op: (Order::PostOrder, Direction::Forward),
ops_in_block: (Order::PostOrder, Direction::Forward),
blocks_in_region: (Order::PostOrder, Direction::Forward),
};
pub const WALKCONFIG_PREORDER_REVERSE: WalkConfig = WalkConfig {
regions_in_op: (Order::PreOrder, Direction::Reverse),
ops_in_block: (Order::PreOrder, Direction::Reverse),
blocks_in_region: (Order::PreOrder, Direction::Reverse),
};
pub const WALKCONFIG_POSTORDER_REVERSE: WalkConfig = WalkConfig {
regions_in_op: (Order::PostOrder, Direction::Reverse),
ops_in_block: (Order::PostOrder, Direction::Reverse),
blocks_in_region: (Order::PostOrder, Direction::Reverse),
};