diff --git a/Cargo.lock b/Cargo.lock index def3d08..643af78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "0.6.11" @@ -147,6 +156,7 @@ checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" name = "borealis" version = "0.3.0" dependencies = [ + "ansi_term", "bincode", "clap", "color-eyre", diff --git a/borealis/Cargo.toml b/borealis/Cargo.toml index 1255d51..78f09d4 100644 --- a/borealis/Cargo.toml +++ b/borealis/Cargo.toml @@ -37,3 +37,4 @@ quote = "1.0.35" syn = { version = "2.0.48", features = ["full", "parsing"] } prettyplease = "0.2.16" proc-macro2 = "1.0.78" +ansi_term = "0.12.1" diff --git a/borealis/src/genc/format.rs b/borealis/src/genc/format.rs index 9c2dd06..425d2b3 100644 --- a/borealis/src/genc/format.rs +++ b/borealis/src/genc/format.rs @@ -7,7 +7,6 @@ use {common::intern::InternedString, itertools::Itertools, std::fmt::Display}; pub struct InstructionFormat(pub Vec); impl Display for InstructionFormat { - #[allow(unstable_name_collisions)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0.iter().map(ToString::to_string).join(" ")) } diff --git a/borealis/src/rust/mod.rs b/borealis/src/rust/mod.rs index 06d8ce1..06d9c2b 100644 --- a/borealis/src/rust/mod.rs +++ b/borealis/src/rust/mod.rs @@ -5,10 +5,13 @@ use { boom::{control_flow::ControlFlowBlock, Ast, Bit, Statement}, genc::{ codegen::{ - format::{extract_format, flatten_expression, is_see_assignment}, + format::{ + extract_format, flatten_expression, is_see_assignment, + process_decode_function_clause, + }, instruction::{generate_execute_entrypoint, get_instruction_entrypoint_fns}, }, - format::InstructionFormat, + format::{InstructionFormat, Segment, SegmentContent}, }, passes::{ self, builtin_fns::AddBuiltinFns, cycle_finder::CycleFinder, @@ -18,9 +21,11 @@ use { }, rust::emit::Emit, }, + ansi_term::Colour::{Green, Red}, color_eyre::eyre::Context, - common::{intern::InternedString, HashMap}, + common::{identifiable::unique_id, intern::InternedString, HashMap}, dot::{Edges, GraphWalk, LabelText, Labeller, Nodes}, + itertools::Itertools, log::info, proc_macro2::{Ident, Span, TokenStream}, quote::quote, @@ -32,7 +37,7 @@ use { cell::RefCell, cmp::min, collections::LinkedList, - io::{Cursor, Write}, + io::{stdout, Cursor, Write}, rc::Rc, }, }; @@ -286,486 +291,867 @@ pub fn generate_fns( } fn generate_decode_fn(writer: &mut W, boom: Rc>, sail: &sail_ast::Ast) { - let mut tree = DecodeTree::new(); + // let mut tree = DecodeTree::new(); - // set up entrypoints in GenC execute behaviours + // get all instructions and their formats let insn_formats = get_instruction_entrypoint_fns(sail) - .into_iter() - .map(|clause| { - log::trace!( - "Processing decode function clause @ {}", - clause.annotation.location - ); - - let (pat, body) = match &clause.inner.pattern_match.inner { - PatternMatchAux::Expression(pat, body) => (pat, body), - PatternMatchAux::When(pat, _, body) => { - log::debug!("Function clause has condition, ignoring..."); - (pat, body) - } - }; - - let ExpressionAux::Block(expressions) = &*body.inner else { - panic!("Body was not Block"); - }; - - assert_eq!(expressions.len(), 2); + .iter() + .map(process_decode_function_clause) + .map(|instr| { + ( + instr.mangled_name, + instr + .format + .0 + .iter() + .map(|Segment { content, length }| match content { + SegmentContent::Variable(_) => vec![Bit::Unknown; *length], + SegmentContent::Constant(val) => { + let mut bits = vec![Bit::Unknown; *length]; + // + + for i in 0..*length { + match (val >> i) & 1 { + 0 => bits[i] = Bit::Zero, + 1 => bits[i] = Bit::One, + _ => unreachable!(), + } + } + + bits.reverse(); + bits + } + }) + .flatten() + .collect(), + ) + }) + .collect::>(); - assert!(is_see_assignment(expressions.front().unwrap())); + build_tree_outer(&insn_formats); + //tom_build_tree(&insn_formats); + //tom2_build_tree(&insn_formats); + todo!(); + // quote! { + // fn decode(insn_data: u32) { - let expressions = flatten_expression(expressions.back().unwrap()); + // } + // }; +} - let execute_function_name = { - let Some(Expression { inner, .. }) = expressions.last() else { - panic!() - }; +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() +} - let ExpressionAux::Application(ident, ..) = &**inner else { - panic!() - }; +/// 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; - ident.as_interned() - }; + for i in 0..32 { + let zeros = count_bit(&insns, i, Bit::Zero); + let ones = count_bit(&insns, i, Bit::One); - (execute_function_name, extract_format(&pat.inner)) - }) - .collect::>(); + if zeros == 0 && ones == 0 { + continue; + } - let mut count = 0; - for (name, bits) in insn_formats { - count += 1; - tree.insert(name, bits); + let diff = (isize::try_from(ones).unwrap() - isize::try_from(zeros).unwrap()).abs(); - if count == 20 { - println!("{}", tree.root.unwrap().to_dot()); - panic!(); + if diff < best_diff { + best_bit = i; + best_diff = diff } } - todo!(); - // quote! { - // fn decode(insn_data: u32) { - - // } - // }; + best_bit } -#[derive(Debug)] -struct DecodeTree { - root: Option, +type NodeId = u32; +type EdgeId = (NodeId, NodeId); + +#[derive(Default)] +struct Graph { + nodes: Vec, + edges: Vec, + edge_labels: HashMap, + node_labels: HashMap, } -impl DecodeTree { - fn new() -> Self { - Self { root: None } +impl<'ast> Labeller<'ast, NodeId, EdgeId> for Graph { + fn graph_id(&'ast self) -> dot::Id<'ast> { + dot::Id::new("Decoder").unwrap() } - fn dump(&self) { - let Some(ref root) = self.root else { - return; - }; + fn node_id(&'ast self, n: &NodeId) -> dot::Id<'ast> { + dot::Id::new(format!("node{n}")).unwrap() + } - root.dump(vec![]); + fn node_label(&'ast self, n: &NodeId) -> dot::LabelText<'ast> { + LabelText::EscStr( + self.node_labels + .get(n) + .cloned() + .unwrap_or(String::from("?")) + .into(), + ) + .into() } - 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), - } + fn node_shape(&'ast self, _: &NodeId) -> Option> { + Some(LabelText::LabelStr("Mrecord".into())) } -} -#[derive(Debug, Clone)] -struct DecodeNode { - pub name: InternedString, - pub bits: Vec, - pub zero: Option>, - pub one: Option>, - pub x: Option>, + fn edge_label(&'ast self, e: &EdgeId) -> LabelText<'ast> { + LabelText::LabelStr( + self.edge_labels + .get(e) + .cloned() + .unwrap_or(String::from("?")) + .into(), + ) + } } -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), - }, - } +impl<'ast> GraphWalk<'ast, NodeId, EdgeId> for Graph { + fn nodes(&'ast self) -> Nodes<'ast, NodeId> { + (&self.nodes).into() } - 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)); - } - } + fn edges(&'ast self) -> Edges<'ast, EdgeId> { + (&self.edges).into() + } - // insert new child with new name and pattern - self.insert_child(name, new_bits); + fn source(&'ast self, edge: &EdgeId) -> NodeId { + edge.0.clone() + } + + fn target(&'ast self, edge: &EdgeId) -> NodeId { + edge.1.clone() + } +} + +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, + ); + + writeln!(&mut out, "panic!();").unwrap(); - return; + writeln!(&mut out, "}}").unwrap(); + + // dot::render(&graph, &mut stdout()).unwrap(); +} + +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:?}"); } } + println!(); + writeln!(w, "// {p:?} {name}: {bits:?}").unwrap(); + } - println!("found no difference, inserting in child"); + if insns.len() == 1 { + let (name, bits) = insns.iter().next().unwrap(); - self.insert_child(name, bits.split_at(self.bits.len()).1.to_owned()); + 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}")); + } - // panic!("attempted to insert decode with equal pattern as existing???") + 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; } - pub fn dump(&self, mut prefix: Vec) { - prefix.extend_from_slice(&self.bits); + // 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 get_prefix(bits: &Vec, offset: usize) -> Vec { + let mut prefix = Vec::new(); - 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()); - } + 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; } } - pub fn to_dot(&self) -> String { - type NodeId = InternedString; - type EdgeId = (NodeId, NodeId); + prefix +} - #[derive(Default)] - struct Graph { - nodes: Vec, - edges: Vec, - node_labels: HashMap>, - } +fn bits_to_val(bits: &Vec) -> u32 { + let mut val = 0; - impl<'ast> Labeller<'ast, NodeId, EdgeId> for Graph { - fn graph_id(&'ast self) -> dot::Id<'ast> { - dot::Id::new("Decoder").unwrap() - } + for (i, bit) in bits.iter().enumerate() { + val |= match bit { + Bit::Zero => 0, + Bit::One => 1, + Bit::Unknown => panic!("impossible"), + } << i; + } - fn node_id(&'ast self, n: &NodeId) -> dot::Id<'ast> { - dot::Id::new((*n).clone().to_string()).unwrap() - } + val +} - fn node_label(&'ast self, n: &NodeId) -> dot::LabelText<'ast> { - let label = self.node_labels.get(n).cloned().unwrap(); +#[derive(Debug)] +struct TomDecodeTransition { + len: usize, + val: u32, + target: Box, +} - LabelText::EscStr(format!("{n} | {label:?}").into()) - } +#[derive(Debug)] +struct TomDecodeNode { + instruction: InternedString, + offset: usize, + unconstrained_transition: Option, + transitions: Vec, +} - fn node_shape(&'ast self, _: &NodeId) -> Option> { - Some(LabelText::LabelStr("record".into())) - } +fn tom_build_tree(insns: &HashMap>) { + let mut root = TomDecodeNode { + instruction: "".into(), + offset: 0, + unconstrained_transition: None, + transitions: Vec::new(), + }; - fn edge_label(&'ast self, e: &EdgeId) -> LabelText<'ast> { - LabelText::LabelStr("".into()) - } - } + for (name, bits) in insns { + root.insert(name, bits); + } - impl<'ast> GraphWalk<'ast, NodeId, EdgeId> for Graph { - fn nodes(&'ast self) -> Nodes<'ast, NodeId> { - (&self.nodes).into() - } + println!("{root:?}"); - fn edges(&'ast self) -> Edges<'ast, EdgeId> { - (&self.edges).into() - } + todo!() +} - fn source(&'ast self, edge: &EdgeId) -> NodeId { - edge.0.clone() - } +impl TomDecodeNode { + fn insert(&mut self, name: &InternedString, bits: &Vec) { + println!("inserting {name}: {bits:?}"); - fn target(&'ast self, edge: &EdgeId) -> NodeId { - edge.1.clone() - } + if self.offset == bits.len() { + panic!("isa conflict detected"); } - let mut graph = Graph::default(); + 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 recurse(graph: &mut Graph, node: &DecodeNode) { - graph.nodes.push(node.name); - graph.node_labels.insert(node.name, node.bits.clone()); +fn compute_mask_and_value(bits: &Vec) -> (u32, u32) { + let mut mask = 0; + let mut val = 0; - if let Some(child) = &node.zero { - graph.edges.push((node.name, child.name)); - recurse(graph, child); + for (i, bit) in bits.iter().enumerate() { + match bit { + Bit::One => { + mask |= 1 << (31 - i); + val |= 1 << (31 - i); } - if let Some(child) = &node.one { - graph.edges.push((node.name, child.name)); - recurse(graph, child); + Bit::Zero => { + mask |= 1 << (31 - i); } - if let Some(child) = &node.x { - graph.edges.push((node.name, child.name)); - recurse(graph, child); + Bit::Unknown => { + // } } - - recurse(&mut graph, self); - - let mut out = vec![]; - dot::render(&graph, &mut out).unwrap(); - String::from_utf8(out).unwrap() } + + (mask, val) } -#[cfg(test)] -mod tests { - use crate::{boom::Bit, rust::DecodeTree}; +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') - #[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!(); - } + let mut out = std::fs::File::create("./target/brig/aarch64.rs").unwrap(); + writeln!(&mut out, "fn main() {{ decode(0xd1000420); }}").unwrap(); + + writeln!(&mut out, "fn decode(insn_data: u32) {{").unwrap(); + writeln!(&mut out, "println!(\"decoding: {{:08x}}\", insn_data);").unwrap(); - panic!(); + for (name, bits) in insns { + let (mask, value) = compute_mask_and_value(bits); + println!("{name}: {mask:08x} {value:08x}"); - // test that they are always recovered + writeln!( + &mut out, + "if (insn_data & 0x{mask:08x}) == 0x{value:08x} {{\n// {bits:?}\npanic!(\"{name}\");\n}}" + ) + .unwrap(); } + + 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 +// } +// }