diff --git a/borealis/src/rust/mod.rs b/borealis/src/rust/mod.rs index 06d9c2b..9887068 100644 --- a/borealis/src/rust/mod.rs +++ b/borealis/src/rust/mod.rs @@ -24,7 +24,6 @@ use { ansi_term::Colour::{Green, Red}, color_eyre::eyre::Context, common::{identifiable::unique_id, intern::InternedString, HashMap}, - dot::{Edges, GraphWalk, LabelText, Labeller, Nodes}, itertools::Itertools, log::info, proc_macro2::{Ident, Span, TokenStream}, @@ -294,7 +293,7 @@ fn generate_decode_fn(writer: &mut W, boom: Rc>, sail: &s // let mut tree = DecodeTree::new(); // get all instructions and their formats - let insn_formats = get_instruction_entrypoint_fns(sail) + let mut insn_formats = get_instruction_entrypoint_fns(sail) .iter() .map(process_decode_function_clause) .map(|instr| { @@ -326,832 +325,365 @@ fn generate_decode_fn(writer: &mut W, boom: Rc>, sail: &s .collect(), ) }) - .collect::>(); + .collect::>>(); - build_tree_outer(&insn_formats); - //tom_build_tree(&insn_formats); - //tom2_build_tree(&insn_formats); - todo!(); - // quote! { - // fn decode(insn_data: u32) { + let tree = DecodeTree::new(); - // } - // }; + for (name, mut bits) in insn_formats { + bits.reverse(); + tree.insert(name, bits); + } + + tree.to_rs(); + panic!(); + //tree.codegen(), tree.as_dot() } -fn count_bit( - insns: &HashMap)>, - bit_idx: usize, - desired_bit: Bit, -) -> usize { - insns - .values() - .map(|(_, bits)| bits[bit_idx]) - .filter(|bit| *bit == desired_bit) - .count() +struct DecodeTree { + root: Node, } -/// Finds the bit index that most evenly divides the set of instructions -fn most_dividing_bit(insns: &HashMap)>) -> usize { - let mut best_bit = 0; - let mut best_diff = isize::MAX; +impl DecodeTree { + fn new() -> Self { + Self { + root: Node::new("root".into(), 0), + } + } - for i in 0..32 { - let zeros = count_bit(&insns, i, Bit::Zero); - let ones = count_bit(&insns, i, Bit::One); + fn insert(&self, name: InternedString, bits: Vec) { + self.root.insert(name, bits) + } - if zeros == 0 && ones == 0 { - continue; - } + fn to_rs(&self) { + let mut f = std::fs::File::create("./target/brig/aarch64.rs").unwrap(); - let diff = (isize::try_from(ones).unwrap() - isize::try_from(zeros).unwrap()).abs(); + let header = quote! { + //! Aarch64 borealis generated module DO NOT EDIT - if diff < best_diff { - best_bit = i; - best_diff = diff - } + fn bit_extract(value: u32, start_idx: usize, len: usize) -> u32 { + (value >> start_idx) & ((1 << len) - 1) + } + + fn main() { + decode(0x91000420); + panic!("fallthrough :("); + } + }; + + writeln!(&mut f, "{}", header.to_string()).unwrap(); + writeln!(&mut f, "fn decode(value: u32) {{").unwrap(); + Self::to_rs_rec(&mut f, self.root.clone()); + writeln!(&mut f, "}}").unwrap(); } - best_bit -} + fn to_rs_rec(w: &mut W, node: Node) { + if node.offset() == 32 { + if !node.constrained().is_empty() || node.unconstrained().is_some() { + panic!("leaf should not have children"); + } -type NodeId = u32; -type EdgeId = (NodeId, NodeId); + writeln!(w, "panic!({:?});", node.name(),).unwrap(); + } -#[derive(Default)] -struct Graph { - nodes: Vec, - edges: Vec, - edge_labels: HashMap, - node_labels: HashMap, -} + for constrained in node.constrained() { + let start_idx = node.offset(); + let len = constrained.len; + let pattern = constrained.value; -impl<'ast> Labeller<'ast, NodeId, EdgeId> for Graph { - fn graph_id(&'ast self) -> dot::Id<'ast> { - dot::Id::new("Decoder").unwrap() - } + writeln!( + w, + "if bit_extract(value, {start_idx}, {len}) == {pattern} {{" + ) + .unwrap(); + Self::to_rs_rec(w, constrained.target); - fn node_id(&'ast self, n: &NodeId) -> dot::Id<'ast> { - dot::Id::new(format!("node{n}")).unwrap() - } + writeln!(w, "}}").unwrap(); + } + + if let Some(unconstrained) = node.unconstrained() { + Self::to_rs_rec(w, unconstrained.target); + } - fn node_label(&'ast self, n: &NodeId) -> dot::LabelText<'ast> { - LabelText::EscStr( - self.node_labels - .get(n) - .cloned() - .unwrap_or(String::from("?")) - .into(), - ) - .into() + // fallthrough to unconstrained if exists } - fn node_shape(&'ast self, _: &NodeId) -> Option> { - Some(LabelText::LabelStr("Mrecord".into())) + fn to_dot(&self) { + let mut graph = graph::Graph::default(); + + Self::to_dot_node(self.root.clone(), &mut graph); + + dot::render(&graph, &mut stdout()).unwrap() } - fn edge_label(&'ast self, e: &EdgeId) -> LabelText<'ast> { - LabelText::LabelStr( - self.edge_labels - .get(e) - .cloned() - .unwrap_or(String::from("?")) - .into(), - ) + fn to_dot_node(node: Node, graph: &mut graph::Graph) -> u32 { + let id = unique_id(); + + graph.nodes.push(id); + graph + .node_labels + .insert(id, format!("{} | {}", node.name(), node.offset())); + + if let Some(unconstrained) = node.unconstrained() { + let child_id = Self::to_dot_node(unconstrained.target, graph); + graph.edges.push((id, child_id)); + graph + .edge_labels + .insert((id, child_id), format!("X:{}", unconstrained.len)); + } + + for constrained in node.constrained() { + let child_id = Self::to_dot_node(constrained.target, graph); + graph.edges.push((id, child_id)); + graph.edge_labels.insert( + (id, child_id), + format!("{:b}:{}", constrained.value, constrained.len), + ); + } + + id } } -impl<'ast> GraphWalk<'ast, NodeId, EdgeId> for Graph { - fn nodes(&'ast self) -> Nodes<'ast, NodeId> { - (&self.nodes).into() - } +#[derive(Clone)] +struct Node { + inner: Rc>, +} - fn edges(&'ast self) -> Edges<'ast, EdgeId> { - (&self.edges).into() +impl Node { + fn new(name: InternedString, offset: usize) -> Node { + Self { + inner: Rc::new(RefCell::new(NodeInner { + name, + offset, + unconstrained: None, + constrained: vec![], + })), + } } - fn source(&'ast self, edge: &EdgeId) -> NodeId { - edge.0.clone() - } + fn insert(&self, name: InternedString, bits: Vec) { + if self.offset() == 32 { + log::trace!("offset 32, finished"); + return; + } - fn target(&'ast self, edge: &EdgeId) -> NodeId { - edge.1.clone() - } -} + let prefix = get_prefix(&bits, self.offset()); + log::trace!("inserting {name} = {bits:?} prefix {prefix:?}"); -fn build_tree_outer(insns: &HashMap>) { - let mut out = std::fs::File::create("./target/brig/aarch64.rs").unwrap(); - writeln!(&mut out, "fn main() {{ decode(0x91500421) }}").unwrap(); - - writeln!( - &mut out, - "fn get_bit(insn_data: u32, bit_idx: u8) -> u32 {{ (insn_data >> (bit_idx)) & 1 }}" - ) - .unwrap(); - - writeln!(&mut out, "fn decode(insn_data: u32) {{").unwrap(); - writeln!(&mut out, "println!(\"decoding: {{:08x}}\", insn_data);").unwrap(); - - let mut graph = Graph::default(); - build_tree( - &mut out, - &mut graph, - &insns - .iter() - .map(|(name, bits)| (name.clone(), (Bit::Unknown, bits.clone()))) - .collect::>(), - vec![], - unique_id(), - Bit::Unknown, - ); + match prefix { + Prefix::Constrained { value, len } => { + // find existing transition + let constraineds = self.constrained(); + let existing = constraineds + .iter() + .find(|t| t.len == len && t.value == value); - writeln!(&mut out, "panic!();").unwrap(); + if let Some(existing) = existing { + existing.target.insert(name, bits); + } else { + let new_node = Node::new(name, self.offset() + len); + new_node.insert(name, bits); + self.insert_constrained(ConstrainedTransition { + value, + len, + target: new_node, + }); + } + } + Prefix::Unconstrained { len } => { + let unconstrained = self.unconstrained(); + match unconstrained { + None => { + let new_node = Node::new(name, self.offset() + len); + new_node.insert(name, bits); + + self.set_unconstrained(UnconstrainedTransition { + len, + target: new_node, + }); + } + Some(transition) => { + if len >= transition.len { + // first `len` bits are already handled, so just proceed + transition.target.insert(name, bits); + } else { + // transition is shorter so need to break transition in twain, and pass new node to first part + + // length of first transition is the prefix length + let t_len_a = len; + + // length of second transition is the remaining length of the original transition + let t_len_b = transition.len - len; + + if transition.len <= t_len_a { + panic!("who knows???") + } - writeln!(&mut out, "}}").unwrap(); + // create new intermediate node + let intermediate = + Node::new("intermediate".into(), self.offset() + t_len_a); - // dot::render(&graph, &mut stdout()).unwrap(); -} + // transition from current node to intermediate + let t_a = UnconstrainedTransition { + len: t_len_a, + target: intermediate.clone(), + }; -fn build_tree( - w: &mut W, - graph: &mut Graph, - insns: &HashMap)>, - parent_path: Vec, - parent_id: NodeId, - bit: Bit, -) { - let current_node_id = unique_id(); - graph.nodes.push(current_node_id); - let edge_id = (parent_id, current_node_id); - graph.edges.push(edge_id); - graph.edge_labels.insert(edge_id, format!("{bit:?}")); - - println!(); - println!("###############################"); - println!(); - println!("path: {parent_path:?}, parent: {parent_id:?}"); - let most_dividing_bit = most_dividing_bit(insns); - println!( - "in {} formats found {} as most dividing bit index:", - insns.len(), - most_dividing_bit - ); - for (name, (p, bits)) in insns.iter() { - print!("{name: >64}: "); - for (i, bit) in bits.iter().enumerate() { - if i == most_dividing_bit { - print!("{}", Red.paint(format!("{bit:?}")).to_string()); - } else if parent_path.contains(&i) { - print!("{}", Green.paint(format!("{bit:?}")).to_string()); - } else { - print!("{bit:?}"); + // transition from intermediate to original target + let t_b = UnconstrainedTransition { + len: t_len_b, + target: transition.target, + }; + + self.set_unconstrained(t_a); + intermediate.set_unconstrained(t_b); + + intermediate.insert(name, bits); + } + } + } } } - println!(); - writeln!(w, "// {p:?} {name}: {bits:?}").unwrap(); } - if insns.len() == 1 { - let (name, bits) = insns.iter().next().unwrap(); - - graph.node_labels.insert(current_node_id, format!("{name}")); - writeln!(w, "panic!(\"{name}: {bits:?}\");").unwrap(); - } else { - graph - .node_labels - .insert(current_node_id, format!("bit #{most_dividing_bit}")); + fn offset(&self) -> usize { + self.inner.borrow().offset } - let mut zeros = insns - .iter() - .filter(|(_, (_, bits))| bits[most_dividing_bit] == Bit::Zero) - .map(|(k, (_, v))| (k.clone(), (Bit::Zero, v.clone()))) - .collect::>(); - let mut ones = insns - .iter() - .filter(|(_, (_, bits))| bits[most_dividing_bit] == Bit::One) - .map(|(k, (_, v))| (k.clone(), (Bit::One, v.clone()))) - .collect::>(); - let unknown = insns - .iter() - .filter(|(_, (_, bits))| bits[most_dividing_bit] == Bit::Unknown) - .map(|(k, (_, v))| (k.clone(), (Bit::Unknown, v.clone()))) - .collect::>(); - - if zeros.is_empty() || ones.is_empty() { - return; + fn name(&self) -> InternedString { + self.inner.borrow().name } - // both "halves" contain all unknowns - zeros.extend(unknown.clone()); - ones.extend(unknown); - - let current_path = [parent_path.clone(), vec![most_dividing_bit]].concat(); - - writeln!( - w, - "if dbg!(get_bit(insn_data, {most_dividing_bit})) == 0 {{" - ) - .unwrap(); - build_tree( - w, - graph, - &zeros, - current_path.clone(), - current_node_id, - Bit::Zero, - ); - writeln!(w, "}} else {{").unwrap(); - build_tree( - w, - graph, - &ones, - current_path.clone(), - current_node_id, - Bit::One, - ); - writeln!(w, "}}").unwrap(); -} + fn unconstrained(&self) -> Option { + self.inner.borrow().unconstrained.clone() + } -/* -fn get_prefix(bits: &Vec, offset: usize) -> Vec { - let mut prefix = Vec::new(); + fn constrained(&self) -> Vec { + self.inner.borrow().constrained.clone() + } - let mut i = offset; - if bits[i] == Bit::Unknown { - while i < bits.len() && bits[i] == Bit::Unknown { - prefix.push(Bit::Unknown); - i += 1; - } - } else { - while i < bits.len() && bits[i] != Bit::Unknown { - prefix.push(bits[i]); - i += 1; - } + fn set_unconstrained(&self, t: UnconstrainedTransition) { + self.inner.borrow_mut().unconstrained = Some(t); } - prefix + fn insert_constrained(&self, t: ConstrainedTransition) { + self.inner.borrow_mut().constrained.push(t); + } } -fn bits_to_val(bits: &Vec) -> u32 { - let mut val = 0; - - for (i, bit) in bits.iter().enumerate() { - val |= match bit { - Bit::Zero => 0, - Bit::One => 1, - Bit::Unknown => panic!("impossible"), - } << i; - } +struct NodeInner { + name: InternedString, + offset: usize, + unconstrained: Option, + constrained: Vec, +} - val +#[derive(Clone)] +struct UnconstrainedTransition { + len: usize, + target: Node, } -#[derive(Debug)] -struct TomDecodeTransition { +#[derive(Clone)] +struct ConstrainedTransition { + value: u64, len: usize, - val: u32, - target: Box, + target: Node, } #[derive(Debug)] -struct TomDecodeNode { - instruction: InternedString, - offset: usize, - unconstrained_transition: Option, - transitions: Vec, +enum Prefix { + Constrained { value: u64, len: usize }, + Unconstrained { len: usize }, } -fn tom_build_tree(insns: &HashMap>) { - let mut root = TomDecodeNode { - instruction: "".into(), - offset: 0, - unconstrained_transition: None, - transitions: Vec::new(), - }; +fn get_prefix(bits: &[Bit], offset: usize) -> Prefix { + let first_bit = bits[offset]; - for (name, bits) in insns { - root.insert(name, bits); + let biterator = bits[offset..] + .iter() + .take_while(|bit| bit.is_unknown() == first_bit.is_unknown()); + + match first_bit { + Bit::Unknown => Prefix::Unconstrained { + len: biterator.count(), + }, + _ => { + let (value, len) = biterator + .enumerate() + .fold((0, 0), |(value, len), (idx, bit)| { + (value + (bit.value() << idx), len + 1) + }); + Prefix::Constrained { value, len } + } } +} - println!("{root:?}"); +mod graph { + use common::HashMap; + use dot::{Edges, GraphWalk, LabelText, Labeller, Nodes}; - todo!() -} + type NodeId = u32; + type EdgeId = (NodeId, NodeId); -impl TomDecodeNode { - fn insert(&mut self, name: &InternedString, bits: &Vec) { - println!("inserting {name}: {bits:?}"); + #[derive(Default)] + pub struct Graph { + pub nodes: Vec, + pub edges: Vec, + pub edge_labels: HashMap, + pub node_labels: HashMap, + } - if self.offset == bits.len() { - panic!("isa conflict detected"); + impl<'ast> Labeller<'ast, NodeId, EdgeId> for Graph { + fn graph_id(&'ast self) -> dot::Id<'ast> { + dot::Id::new("Decoder").unwrap() } - let prefix = get_prefix(bits, self.offset); - if prefix[0].is_unknown() { - if let Some(ref mut uc) = self.unconstrained_transition { - if prefix.len() >= uc.len { - uc.target.insert(name, bits); - } else { - todo!() - } - } else { - self.unconstrained_transition = Some(TomDecodeTransition { - len: prefix.len(), - val: 0, - target: Box::new(TomDecodeNode { - instruction: name.clone(), - offset: self.offset + prefix.len(), - unconstrained_transition: None, - transitions: Vec::new(), - }), - }); - } - } else { - let val = bits_to_val(&prefix); - if let Some(existing) = self.transitions.iter_mut().find(|t| t.val == val) { - existing.target.insert(name, bits); - } else { - self.transitions.push(TomDecodeTransition { - len: prefix.len(), - val, - target: Box::new(TomDecodeNode { - instruction: name.clone(), - offset: self.offset + prefix.len(), - unconstrained_transition: None, - transitions: Vec::new(), - }), - }) - } + fn node_id(&'ast self, n: &NodeId) -> dot::Id<'ast> { + dot::Id::new(format!("node{n}")).unwrap() } - } -} -*/ -fn compute_mask_and_value(bits: &Vec) -> (u32, u32) { - let mut mask = 0; - let mut val = 0; - - for (i, bit) in bits.iter().enumerate() { - match bit { - Bit::One => { - mask |= 1 << (31 - i); - val |= 1 << (31 - i); - } - Bit::Zero => { - mask |= 1 << (31 - i); - } - Bit::Unknown => { - // - } + fn node_label(&'ast self, n: &NodeId) -> dot::LabelText<'ast> { + LabelText::EscStr( + self.node_labels + .get(n) + .cloned() + .unwrap_or(String::from("?")) + .into(), + ) + .into() } - } - (mask, val) -} + fn node_shape(&'ast self, _: &NodeId) -> Option> { + Some(LabelText::LabelStr("Mrecord".into())) + } -fn tom2_build_tree(insns: &HashMap>) { - // TODO: There is probably something wrong with the endianness - // AND: addsub_immediate has specialised the "op" bit (bit 30) -- the other variant is missing (it should be an 'x') + fn edge_label(&'ast self, e: &EdgeId) -> LabelText<'ast> { + LabelText::LabelStr( + self.edge_labels + .get(e) + .cloned() + .unwrap_or(String::from("?")) + .into(), + ) + } + } - let mut out = std::fs::File::create("./target/brig/aarch64.rs").unwrap(); - writeln!(&mut out, "fn main() {{ decode(0xd1000420); }}").unwrap(); + impl<'ast> GraphWalk<'ast, NodeId, EdgeId> for Graph { + fn nodes(&'ast self) -> Nodes<'ast, NodeId> { + (&self.nodes).into() + } - writeln!(&mut out, "fn decode(insn_data: u32) {{").unwrap(); - writeln!(&mut out, "println!(\"decoding: {{:08x}}\", insn_data);").unwrap(); + fn edges(&'ast self) -> Edges<'ast, EdgeId> { + (&self.edges).into() + } - for (name, bits) in insns { - let (mask, value) = compute_mask_and_value(bits); - println!("{name}: {mask:08x} {value:08x}"); + fn source(&'ast self, edge: &EdgeId) -> NodeId { + edge.0.clone() + } - writeln!( - &mut out, - "if (insn_data & 0x{mask:08x}) == 0x{value:08x} {{\n// {bits:?}\npanic!(\"{name}\");\n}}" - ) - .unwrap(); + fn target(&'ast self, edge: &EdgeId) -> NodeId { + edge.1.clone() + } } - - writeln!(&mut out, "}}").unwrap(); - - todo!() } - -// #[derive(Debug)] -// struct DecodeTree { -// root: Option, -// } - -// impl DecodeTree { -// fn new() -> Self { -// Self { root: None } -// } - -// fn dump(&self) { -// let Some(ref root) = self.root else { -// return; -// }; - -// root.dump(vec![]); -// } - -// fn insert(&mut self, name: InternedString, bits: Vec) { -// //println!("{name: >64} {bits:?}"); - -// match &mut self.root { -// None => { -// self.root = Some(DecodeNode { -// name, -// bits, -// zero: None, -// one: None, -// x: None, -// }) -// } -// Some(ref mut root) => root.insert(name, bits), -// } -// } -// } - -// #[derive(Debug, Clone)] -// struct DecodeNode { -// pub name: InternedString, -// pub bits: Vec, -// pub zero: Option>, -// pub one: Option>, -// pub x: Option>, -// } - -// impl DecodeNode { -// pub fn insert_child(&mut self, name: InternedString, bits: Vec) { -// match bits[0] { -// Bit::Zero => match &mut self.zero { -// None => { -// self.zero = Some(Box::new(DecodeNode { -// name, -// bits: bits, -// zero: None, -// one: None, -// x: None, -// })) -// } -// Some(ref mut child) => child.insert(name, bits), -// }, -// Bit::One => match &mut self.one { -// None => { -// self.one = Some(Box::new(DecodeNode { -// name, -// bits: bits, -// zero: None, -// one: None, -// x: None, -// })) -// } -// Some(ref mut child) => child.insert(name, bits), -// }, -// Bit::Unknown => match &mut self.x { -// None => { -// self.x = Some(Box::new(DecodeNode { -// name, -// bits: bits, -// zero: None, -// one: None, -// x: None, -// })) -// } -// Some(ref mut child) => child.insert(name, bits), -// }, -// } -// } - -// pub fn insert(&mut self, name: InternedString, bits: Vec) { -// println!("inserting {name:?} = {bits:?}"); - -// for i in 0..min(self.bits.len(), bits.len()) { -// if self.bits[i] != bits[i] { -// let (shared_bits, old_bits) = self.bits.split_at(i); -// let old_bits = old_bits.to_owned(); - -// let (_, new_bits) = bits.split_at(i); -// let new_bits = new_bits.to_owned(); - -// println!("matched chunk differs @ {i}, shared_bits: {shared_bits:?}, old_bits: {old_bits:?}, new_bits: {new_bits:?}"); - -// self.bits = shared_bits.to_owned(); -// let old_name = self.name; -// self.name = format!("partial{}", common::identifiable::unique_id()).into(); - -// // two new children -// // first child has current name, old_bits, and current children - -// let child_a = DecodeNode { -// name: old_name, -// bits: old_bits.clone(), -// zero: self.zero.clone(), -// one: self.one.clone(), -// x: self.x.clone(), -// }; - -// match old_bits[0] { -// Bit::Zero => { -// self.zero = Some(Box::new(child_a)); -// self.one = None; -// self.x = None; -// } -// Bit::One => { -// self.zero = None; -// self.one = Some(Box::new(child_a)); -// self.x = None; -// } -// Bit::Unknown => { -// self.zero = None; -// self.one = None; -// self.x = Some(Box::new(child_a)); -// } -// } - -// // insert new child with new name and pattern -// self.insert_child(name, new_bits); - -// return; -// } -// } - -// println!("found no difference, inserting in child"); - -// self.insert_child(name, bits.split_at(self.bits.len()).1.to_owned()); - -// // panic!("attempted to insert decode with equal pattern as existing???") -// } - -// pub fn dump(&self, mut prefix: Vec) { -// prefix.extend_from_slice(&self.bits); - -// if prefix.len() == 32 { -// println!("{}: {prefix:?}", self.name); -// } else { -// if let Some(child) = &self.zero { -// child.dump(prefix.clone()); -// } -// if let Some(child) = &self.one { -// child.dump(prefix.clone()); -// } -// if let Some(child) = &self.x { -// child.dump(prefix.clone()); -// } -// } -// } - -// pub fn to_dot(&self) -> String { -// type NodeId = InternedString; -// type EdgeId = (NodeId, NodeId); - -// #[derive(Default)] -// struct Graph { -// nodes: Vec, -// edges: Vec, -// node_labels: HashMap>, -// } - -// impl<'ast> Labeller<'ast, NodeId, EdgeId> for Graph { -// fn graph_id(&'ast self) -> dot::Id<'ast> { -// dot::Id::new("Decoder").unwrap() -// } - -// fn node_id(&'ast self, n: &NodeId) -> dot::Id<'ast> { -// dot::Id::new((*n).clone().to_string()).unwrap() -// } - -// fn node_label(&'ast self, n: &NodeId) -> dot::LabelText<'ast> { -// let label = self.node_labels.get(n).cloned().unwrap(); - -// LabelText::EscStr(format!("{n} | {label:?}").into()) -// } - -// fn node_shape(&'ast self, _: &NodeId) -> Option> { -// Some(LabelText::LabelStr("record".into())) -// } - -// fn edge_label(&'ast self, e: &EdgeId) -> LabelText<'ast> { -// LabelText::LabelStr("".into()) -// } -// } - -// impl<'ast> GraphWalk<'ast, NodeId, EdgeId> for Graph { -// fn nodes(&'ast self) -> Nodes<'ast, NodeId> { -// (&self.nodes).into() -// } - -// fn edges(&'ast self) -> Edges<'ast, EdgeId> { -// (&self.edges).into() -// } - -// fn source(&'ast self, edge: &EdgeId) -> NodeId { -// edge.0.clone() -// } - -// fn target(&'ast self, edge: &EdgeId) -> NodeId { -// edge.1.clone() -// } -// } - -// let mut graph = Graph::default(); - -// fn recurse(graph: &mut Graph, node: &DecodeNode) { -// graph.nodes.push(node.name); -// graph.node_labels.insert(node.name, node.bits.clone()); - -// if let Some(child) = &node.zero { -// graph.edges.push((node.name, child.name)); -// recurse(graph, child); -// } -// if let Some(child) = &node.one { -// graph.edges.push((node.name, child.name)); -// recurse(graph, child); -// } -// if let Some(child) = &node.x { -// graph.edges.push((node.name, child.name)); -// recurse(graph, child); -// } -// } - -// recurse(&mut graph, self); - -// let mut out = vec![]; -// dot::render(&graph, &mut out).unwrap(); -// String::from_utf8(out).unwrap() -// } -// } - -// #[cfg(test)] -// mod tests { -// use crate::{boom::Bit, rust::DecodeTree}; - -// #[test] -// fn identitree() { -// let insns = vec![ -// ( -// "vector_crypto_aes_round_decode", -// [ -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// ], -// ), -// ( -// "vector_arithmetic_unary_add_pairwise_decode", -// [ -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::Unknown, -// Bit::Zero, -// ], -// ), -// ( -// "vector_arithmetic_unary_cmp_float_lessthan_simd_decode", -// [ -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::Unknown, -// Bit::One, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Unknown, -// Bit::Zero, -// ], -// ), -// ( -// "vector_arithmetic_unary_fp16_conv_float_tieaway_simd_decode", -// [ -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Unknown, -// Bit::Zero, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::One, -// Bit::One, -// Bit::One, -// Bit::Zero, -// Bit::Zero, -// Bit::Unknown, -// Bit::Zero, -// ], -// ), -// ]; - -// // insert many patterns -// let mut tree = DecodeTree::new(); -// for (name, bits) in insns { -// println!("top level insert {name:?} {bits:?}"); -// tree.insert(name.into(), bits.to_vec()); -// println!("dot: {}", tree.root.as_ref().unwrap().to_dot()); -// println!(); -// println!(); -// println!(); -// } - -// panic!(); - -// // test that they are always recovered -// } -// }