use crate::ir::*;
pub fn dfs_in_order<'instr>(
visitor: &mut impl Visitor<'instr>,
func: &'instr LocalFunction,
start: InstrSeqId,
) {
let mut stack: Vec<(InstrSeqId, usize)> = vec![(start, 0)];
'traversing_blocks: while let Some((seq_id, index)) = stack.pop() {
let seq = func.block(seq_id);
if index == 0 {
visitor.start_instr_seq(seq);
seq.visit(visitor);
}
'traversing_instrs: for (index, (instr, loc)) in seq.instrs.iter().enumerate().skip(index) {
log::trace!("dfs_in_order: visit_instr({:?})", instr);
visitor.visit_instr(instr, loc);
log::trace!("dfs_in_order: ({:?}).visit(..)", instr);
instr.visit(visitor);
match instr {
Instr::Block(Block { seq }) | Instr::Loop(Loop { seq }) => {
stack.push((seq_id, index + 1));
stack.push((*seq, 0));
continue 'traversing_blocks;
}
Instr::IfElse(IfElse {
consequent,
alternative,
}) => {
stack.push((seq_id, index + 1));
stack.push((*alternative, 0));
stack.push((*consequent, 0));
continue 'traversing_blocks;
}
Instr::TryTable(TryTable { seq, catches }) => {
stack.push((seq_id, index + 1));
for catch in catches.iter() {
log::trace!("dfs_in_order: ({:?}).visit(..)", catch);
catch.visit(visitor);
}
stack.push((*seq, 0));
continue 'traversing_blocks;
}
Instr::Try(Try { seq, catches }) => {
stack.push((seq_id, index + 1));
for catch in catches.iter() {
log::trace!("dfs_in_order: ({:?}).visit(..)", catch);
catch.visit(visitor);
}
for catch in catches.iter().rev() {
match catch {
LegacyCatch::Catch { handler, .. }
| LegacyCatch::CatchAll { handler } => {
stack.push((*handler, 0));
}
LegacyCatch::Delegate { .. } => {
}
}
}
stack.push((*seq, 0));
continue 'traversing_blocks;
}
_ => continue 'traversing_instrs,
}
}
visitor.end_instr_seq(seq);
}
}
pub fn dfs_pre_order_mut(
visitor: &mut impl VisitorMut,
func: &mut LocalFunction,
start: InstrSeqId,
) {
let mut stack = vec![start];
while let Some(seq_id) = stack.pop() {
let seq = func.block_mut(seq_id);
visitor.start_instr_seq_mut(seq);
seq.visit_mut(visitor);
for (instr, loc) in &mut seq.instrs {
visitor.visit_instr_mut(instr, loc);
instr.visit_mut(visitor);
match instr {
Instr::Block(Block { seq }) | Instr::Loop(Loop { seq }) => {
stack.push(*seq);
}
Instr::IfElse(IfElse {
consequent,
alternative,
}) => {
stack.push(*alternative);
stack.push(*consequent);
}
Instr::TryTable(TryTable { seq, catches }) => {
for catch in catches.iter_mut() {
catch.visit_mut(visitor);
}
stack.push(*seq);
}
Instr::Try(Try { seq, catches }) => {
for catch in catches.iter_mut() {
catch.visit_mut(visitor);
}
for catch in catches.iter().rev() {
match catch {
LegacyCatch::Catch { handler, .. }
| LegacyCatch::CatchAll { handler } => {
stack.push(*handler);
}
LegacyCatch::Delegate { .. } => {
}
}
}
stack.push(*seq);
}
_ => {}
}
}
visitor.end_instr_seq_mut(seq);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct TestVisitor {
visits: Vec<String>,
}
impl TestVisitor {
fn push(&mut self, s: impl ToString) {
self.visits.push(s.to_string());
}
}
impl<'a> Visitor<'a> for TestVisitor {
fn start_instr_seq(&mut self, _: &'a InstrSeq) {
self.push("start");
}
fn end_instr_seq(&mut self, _: &'a InstrSeq) {
self.push("end");
}
fn visit_const(&mut self, c: &Const) {
match c.value {
Value::I32(x) => self.push(x),
_ => unreachable!(),
}
}
fn visit_drop(&mut self, _: &Drop) {
self.push("drop");
}
fn visit_block(&mut self, _: &Block) {
self.push("block");
}
fn visit_if_else(&mut self, _: &IfElse) {
self.push("if-else");
}
fn visit_try_table(&mut self, _: &TryTable) {
self.push("try-table");
}
fn visit_try(&mut self, _: &Try) {
self.push("try");
}
fn visit_tag_id(&mut self, _: &crate::TagId) {
self.push("tag");
}
}
impl VisitorMut for TestVisitor {
fn start_instr_seq_mut(&mut self, _: &mut InstrSeq) {
self.push("start");
}
fn end_instr_seq_mut(&mut self, _: &mut InstrSeq) {
self.push("end");
}
fn visit_const_mut(&mut self, c: &mut Const) {
match &mut c.value {
Value::I32(x) => {
self.push(*x);
*x += 1;
}
_ => unreachable!(),
}
}
fn visit_drop_mut(&mut self, _: &mut Drop) {
self.push("drop");
}
fn visit_block_mut(&mut self, _: &mut Block) {
self.push("block");
}
fn visit_if_else_mut(&mut self, _: &mut IfElse) {
self.push("if-else");
}
fn visit_try_table_mut(&mut self, _: &mut TryTable) {
self.push("try-table");
}
fn visit_try_mut(&mut self, _: &mut Try) {
self.push("try");
}
fn visit_tag_id_mut(&mut self, _: &mut crate::TagId) {
self.push("tag");
}
}
fn make_test_func(module: &mut crate::Module) -> &mut LocalFunction {
let block_ty = module.types.add(&[], &[]);
let tag_type = module.types.add(&[], &[]);
let tag_id = module.tags.add(tag_type);
let mut builder = crate::FunctionBuilder::new(&mut module.types, &[], &[]);
let try_block = Instr::Try(Try {
seq: builder
.dangling_instr_seq(block_ty)
.i32_const(7)
.drop()
.id(),
catches: vec![
LegacyCatch::Catch {
tag: tag_id,
handler: builder
.dangling_instr_seq(block_ty)
.i32_const(8)
.drop()
.id(),
},
LegacyCatch::CatchAll {
handler: builder
.dangling_instr_seq(block_ty)
.i32_const(9)
.drop()
.id(),
},
],
});
builder
.func_body()
.i32_const(1)
.drop()
.block(block_ty, |block| {
block
.i32_const(2)
.drop()
.if_else(
block_ty,
|then| {
then.i32_const(3).drop();
},
|else_| {
else_.i32_const(4).drop();
},
)
.i32_const(5)
.drop();
})
.i32_const(6)
.drop()
.instr(try_block)
.i32_const(10)
.drop();
let func_id = builder.finish(vec![], &mut module.funcs);
module.funcs.get_mut(func_id).kind.unwrap_local_mut()
}
#[test]
fn dfs_in_order() {
let mut module = crate::Module::default();
let func = make_test_func(&mut module);
let mut visitor = TestVisitor::default();
crate::ir::dfs_in_order(&mut visitor, func, func.entry_block());
let mut expected = vec![];
expected.extend(vec!["start", "1", "drop", "block"]);
expected.extend(vec!["start", "2", "drop", "if-else"]);
expected.extend(vec!["start", "3", "drop", "end"]);
expected.extend(vec!["start", "4", "drop", "end"]);
expected.extend(vec!["5", "drop", "end"]);
expected.extend(vec!["6", "drop", "try", "tag"]);
expected.extend(vec!["start", "7", "drop", "end"]);
expected.extend(vec!["start", "8", "drop", "end"]);
expected.extend(vec!["start", "9", "drop", "end"]);
expected.extend(vec!["10", "drop", "end"]);
assert_eq!(
visitor.visits,
expected.iter().map(|s| s.to_string()).collect::<Vec<_>>()
);
}
#[test]
fn dfs_pre_order_mut() {
let mut module = crate::Module::default();
let func = make_test_func(&mut module);
let mut visitor = TestVisitor::default();
crate::ir::dfs_pre_order_mut(&mut visitor, func, func.entry_block());
let mut expected = vec![];
expected.extend(vec![
"start", "1", "drop", "block", "6", "drop", "try", "tag", "10", "drop", "end",
]);
expected.extend(vec!["start", "7", "drop", "end"]);
expected.extend(vec!["start", "8", "drop", "end"]);
expected.extend(vec!["start", "9", "drop", "end"]);
expected.extend(vec!["start", "2", "drop", "if-else", "5", "drop", "end"]);
expected.extend(vec!["start", "3", "drop", "end"]);
expected.extend(vec!["start", "4", "drop", "end"]);
assert_eq!(
visitor.visits,
expected.iter().map(|s| s.to_string()).collect::<Vec<_>>()
);
visitor.visits.clear();
crate::ir::dfs_in_order(&mut visitor, func, func.entry_block());
let mut expected = vec![];
expected.extend(vec!["start", "2", "drop", "block"]);
expected.extend(vec!["start", "3", "drop", "if-else"]);
expected.extend(vec!["start", "4", "drop", "end"]);
expected.extend(vec!["start", "5", "drop", "end"]);
expected.extend(vec!["6", "drop", "end"]);
expected.extend(vec!["7", "drop", "try", "tag"]);
expected.extend(vec!["start", "8", "drop", "end"]);
expected.extend(vec!["start", "9", "drop", "end"]);
expected.extend(vec!["start", "10", "drop", "end"]);
expected.extend(vec!["11", "drop", "end"]);
assert_eq!(
visitor.visits,
expected.iter().map(|s| s.to_string()).collect::<Vec<_>>()
);
}
#[test]
fn dfs_in_order_try_table() {
let mut module = crate::Module::default();
let tag_type = module.types.add(&[crate::ValType::I32], &[]);
let tag_id = module.tags.add(tag_type);
let block_ty = module.types.add(&[], &[]);
let mut builder = crate::FunctionBuilder::new(&mut module.types, &[], &[]);
let mut func_body = builder.func_body();
let try_body = func_body.dangling_instr_seq(block_ty).id();
func_body.instr_seq(try_body).i32_const(5).drop();
let catch_label = func_body.dangling_instr_seq(block_ty).id();
let catch_all_label = func_body.dangling_instr_seq(block_ty).id();
func_body
.i32_const(1)
.drop()
.instr(TryTable {
seq: try_body,
catches: vec![
TryTableCatch::Catch {
tag: tag_id,
label: catch_label,
},
TryTableCatch::CatchAll {
label: catch_all_label,
},
],
})
.i32_const(99)
.drop();
let func_id = builder.finish(vec![], &mut module.funcs);
let func = module.funcs.get_mut(func_id).kind.unwrap_local_mut();
let mut visitor = TestVisitor::default();
crate::ir::dfs_in_order(&mut visitor, func, func.entry_block());
let expected = [
"start",
"1",
"drop",
"try-table",
"tag",
"start",
"5",
"drop",
"end", "99",
"drop",
"end",
];
assert_eq!(
visitor.visits,
expected.iter().map(|s| s.to_string()).collect::<Vec<_>>()
);
}
}