use std::collections::{BTreeMap, BTreeSet}; use std::{env, fmt, mem, ptr}; use std::fs::{self, File}; use std::path::PathBuf; use std::time::Instant; use std::borrow::Cow; use std::io::Write; #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] enum ArgumentType<'n> { Enum(BTreeMap), Literal(u16), Displacement(u16), } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Argument<'p> { tp: ArgumentType<'p>, prefix: &'p str, prefix_alt: Option<&'p str>, suffix: &'p str, multiplier: u8, } impl<'p> Argument<'p> { fn len(&self) -> usize { let cnt = match &self.tp { ArgumentType::Enum(names) => names.len() as u16, ArgumentType::Literal(cnt) | ArgumentType::Displacement(cnt) => *cnt, }; if cnt.count_ones() != 1 { panic!("Argument read as having {} variants?", cnt); } cnt.trailing_zeros() as usize } fn matches(&self, disp: &str) -> bool { (disp.starts_with(self.prefix) || self.prefix_alt.map(|p| disp.starts_with(p)).unwrap_or(false)) && disp.ends_with(self.suffix) } fn search_terms(&self, repr_c: char) -> [Option>; 2] { [Some(self.search_terms_impl(self.prefix, repr_c)), self.prefix_alt.map(|p| self.search_terms_impl(p, repr_c))] } fn search_terms_impl<'pr>(&self, pref: &'pr str, repr_c: char) -> Cow<'pr, str> { match self.tp { ArgumentType::Enum(_) => format!("{}{}{}", pref, repr_c, self.suffix).into(), ArgumentType::Literal(_) => { assert!(self.suffix.is_empty()); pref.into() } ArgumentType::Displacement(_) => { assert!(self.suffix.is_empty()); if repr_c == 'd' { pref.into() } else { "!!!!!!!!!!!!".into() } } } } fn display_reassemble<'s, 'n>(&'s self, c: char) -> ArgumentDisplayReassemble<'s, 'p> { ArgumentDisplayReassemble(&self, c) } } struct ArgumentDisplayReassemble<'a, 'aa>(&'a Argument<'aa>, char); impl fmt::Display for ArgumentDisplayReassemble<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0.tp { ArgumentType::Enum(_) => write!(f, "{} as u16", self.1), ArgumentType::Literal(_) => write!(f, "{} as u16", self.1), ArgumentType::Displacement(_) => write!(f, "{}.0 as u16", self.1), } } } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] struct Instruction<'p> { disp: String, repr: String, comment: Vec>, args: Vec<&'p str>, } fn main() { println!("cargo:rerun-if-changed=src/lib.rs"); let file_s = fs::read_to_string("src/lib.rs").unwrap(); let t1 = Instant::now(); let mut arguments = BTreeMap::<&'static str, Argument>::new(); arguments.insert("u8", Argument { tp: ArgumentType::Literal(0xFF + 1), prefix: "imm", prefix_alt: None, suffix: "", multiplier: 0, }); arguments.insert("Displacement12", Argument { tp: ArgumentType::Displacement(0xFFF + 1), prefix: "disp", prefix_alt: Some("label"), suffix: "", multiplier: 0, }); arguments.insert("Displacement8", Argument { tp: ArgumentType::Displacement(0xFF + 1), prefix: "disp", prefix_alt: Some("label"), suffix: "", multiplier: 0, }); arguments.insert("Displacement4", Argument { tp: ArgumentType::Displacement(0xF + 1), prefix: "disp", prefix_alt: None, suffix: "", multiplier: 0, }); for arg in &["SuperHRegister", "SuperHRegisterBank", "SuperHFloatRegister", "SuperHExtendedFloatRegister", "SuperHVectorFloatRegister", "SuperHDoubleRegister", "SuperHExtendedDoubleRegister"] { let mut last_doc_comment = ""; let mut inside = false; let mut kv = BTreeMap::new(); let mut val = Argument { tp: ArgumentType::Enum(BTreeMap::new()), prefix: "", prefix_alt: None, suffix: "", multiplier: 0, }; let match_str = format!("enum {} {{", arg); for l in file_s.lines() { let l = l.trim(); if l.starts_with("/// ") { last_doc_comment = &l[4..]; } else if l.contains(&match_str) { let mut ps = last_doc_comment.split(|c| c == '*' || c == ' '); val.prefix = ps.next().expect("val.prefix"); val.suffix = ps.next().expect("val.suffix"); val.multiplier = ps.next().map(|m| m[..m.len() - 1].parse().expect("multiplier")).unwrap_or(1); inside = true; } else if inside && l.contains(" = ") { let eq_idx = l.find(" = ").unwrap(); kv.insert(l[eq_idx + " = ".len()..l.len() - 1].trim().parse().expect("numbar???"), l[0..eq_idx].trim()); } else if inside && l == "}" { break; } } val.tp = ArgumentType::Enum(kv); arguments.insert(arg, val); } let t2 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("argument_display.rs"))).expect("argument_display"); for (arg_name, arg) in arguments.iter().filter(|(_, arg)| match arg.tp { ArgumentType::Literal(_) => false, _ => true, }) { writeln!(outf, "impl fmt::Display for {} {{", arg_name).unwrap(); writeln!(outf, " fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{").unwrap(); match &arg.tp { ArgumentType::Enum(variants) => { writeln!(outf, " match self {{").unwrap(); for (val, name) in variants { writeln!(outf, " {}::{} => f.write_str(\"{}{}{}\"),", arg_name, name, arg.prefix, val * arg.multiplier, arg.suffix) .unwrap(); } writeln!(outf, " }}").unwrap(); } ArgumentType::Literal(_) => unreachable!(), ArgumentType::Displacement(_) => writeln!(outf, " self.0.fmt(f)").unwrap(), } writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); writeln!(outf).unwrap(); } } let t3 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("argument_try_from.rs"))).expect("argument_try_from"); for (arg_name, arg) in &arguments { match arg.tp { ArgumentType::Enum(ref variants) => { writeln!(outf, "impl TryFrom for {} {{", arg_name).unwrap(); writeln!(outf, " type Error = ();").unwrap(); writeln!(outf, " fn try_from(value: u8) -> Result {{").unwrap(); writeln!(outf, " match value {{").unwrap(); for (val, name) in variants { writeln!(outf, " {} => Ok({}::{}),", val, arg_name, name).unwrap(); } writeln!(outf, " _ => Err(()),").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); writeln!(outf).unwrap(); } _ => {}, } } } let t4 = Instant::now(); let mut instructions = BTreeMap::<&str, Instruction>::new(); { let mut doc_comment = vec![]; let mut inside = false; for l in file_s.lines() { let l = l.trim(); if l.is_empty() { continue; } if l.contains("enum SuperHInstruction {") { inside = true; } else if inside && l.starts_with("/// ") { doc_comment.push(Cow::from(&l[4..])); } else if inside && l.chars().next().unwrap().is_alphabetic() { if doc_comment.is_empty() { panic!("{} no doc?", l); } if doc_comment.len() >= 2 && doc_comment[1].starts_with('`') { doc_comment[0] = format!("{} {}", doc_comment[0], doc_comment[1]).into(); // merge repr split off by rustfmt doc_comment.remove(1); } // println!("{:#?} {}", doc_comment, l); let disp = doc_comment[0][0..doc_comment[0].find(" (").expect("no opening paren?")].trim(); let repr = &doc_comment[0][doc_comment[0].rfind(')').expect("no closing paren?") + 1..]; let repr = &repr[repr.find('`').expect("no starting `?") + 1..repr.rfind('`').expect("no closing `?")]; let sep_idx = l.find(|c| c == ',' || c == '(').unwrap(); let name = &l[0..sep_idx]; // println!("{} :: {} :: {}", name, disp, repr); let args = if l.chars().nth(sep_idx).unwrap() == ',' { vec![] } else { l[sep_idx + 1..l.len() - 1].split(|c| c == ',' || c == ')').map(str::trim).filter(|a| !a.is_empty()).collect() }; for arg in &args { if !arguments.contains_key(arg) { panic!("Argument type {} not known!", arg); } } instructions.insert(name, Instruction { disp: disp.to_string(), repr: repr.to_string(), comment: mem::replace(&mut doc_comment, vec![]), args: args, }); } else if inside && l == "}" { break; } } } let t5 = Instant::now(); let mut reassemble_instructions = vec![]; let mut display_instructions = vec![]; let mut feature_instructions = BTreeMap::, Vec<(&str, u8)>>::new(); let mut level_instructions = BTreeMap::, Vec<(&str, u8)>>::new(); let mut privilege_instructions = [vec![], vec![]]; for (instr_name, instr) in &instructions { if instr.repr.len() != 16 { panic!("{} repr {} long", instr_name, instr.repr.len()); } let mut base = 0u16; let mut arg0 = (0u16, '\0'); let mut arg1 = (0u16, '\0'); let mut arg2 = (0u16, '\0'); for c in instr.repr.chars() { base <<= 1; arg0.0 <<= 1; arg1.0 <<= 1; arg2.0 <<= 1; match c { '0' => {} '1' => base |= 1, _ if arg0.1 == '\0' || arg0.1 == c => { arg0.0 |= 1; arg0.1 = c; } _ if arg1.1 == '\0' || arg1.1 == c => { arg1.0 |= 1; arg1.1 = c; } _ if arg2.1 == '\0' || arg2.1 == c => { arg2.0 |= 1; arg2.1 = c; } _ => panic!("{} repr has {}", instr_name, c), } } let argn = (arg0.1 != '\0') as u8 + (arg1.1 != '\0') as u8 + (arg2.1 != '\0') as u8; if instr.args.len() != argn as usize { panic!("{} repr has {} args instead of {}", instr_name, argn, instr.args.len()); } let mut display_format = String::with_capacity(instr.disp.len()); { let mut arg012 = [&mut arg0, &mut arg1, &mut arg2]; let mut arg012 = &mut arg012[0..argn as usize]; let mut last_disp_idx = 0; // Order in arguments must match order in disp (this is checked here). This bit sorts arg[012] to match those as well. const FAKE_ARGUMENTS: &[&str] = &["", "@(R0", "FPUL", "PC)", "R0", "FR0", "DBR", "GBR", "SGR", "SPC", "SR", "SSR", "VBR", "FPSCR", "MACH", "MACL", "PR"]; for (mut disp_arg, arg_tp) in instr.disp.split(|c| c == ' ' || c == ',').skip(1).filter(|d| !FAKE_ARGUMENTS.contains(d)).zip(instr.args.iter()) { while disp_arg.starts_with(|ref c| ['(', '@', '-', '#'].contains(c)) { disp_arg = &disp_arg[1..]; } for &sfx in &[')', '+'] { if disp_arg.ends_with(sfx) { disp_arg = &disp_arg[..disp_arg.len() - 1] } } let arg = &arguments[arg_tp]; assert!(arg.matches(disp_arg), "{}: {} vs {}", instr_name, arg_tp, disp_arg); match arg012.iter().position(|da| arg.search_terms(da.1).iter().flatten().any(|st| st == disp_arg && da.0.count_ones() as usize == arg.len())) { Some(idx) => { unsafe { ptr::swap(arg012[idx] as *mut _, arg012[0] as *mut _) }; arg012 = &mut arg012[1..]; } None => { dbg!(instr_name); dbg!(disp_arg); dbg!(arg); dbg!(arg012); panic!("uhoh! poopy stinky!") } } let this_disp_idx = (disp_arg.as_ptr() as usize) - (instr.disp.as_str().as_ptr() as usize); display_format.push_str(&instr.disp[last_disp_idx..this_disp_idx]); display_format.push_str("{}"); last_disp_idx = this_disp_idx + disp_arg.len(); } display_format.push_str(&instr.disp[last_disp_idx..]); } // So, by this point, arg0 corresponds to instr.args[0], &c. let metadata = |name: &'static str| { instr.comment .iter() .find(|c| c.starts_with(name)) .into_iter() .flat_map(move |ol| ol[name.len()..].split(|c| c == ' ' || c == ',').filter(|i| !i.is_empty())) }; feature_instructions.entry(metadata("Features:").collect()).or_default().push((instr_name, argn)); level_instructions.entry(metadata("Level:").next()).or_default().push((instr_name, argn)); privilege_instructions[instr.comment.iter().any(|c| c == "Privileged") as usize].push((instr_name, argn)); struct InstrBareArgs<'a>(&'a [&'a (u16, char)], bool); impl fmt::Display for InstrBareArgs<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if !self.0.is_empty() { if self.1 { write!(f, "(")?; } for (_, c) in self.0 { write!(f, "{}, ", c)?; } if self.1 { write!(f, ")")?; } } Ok(()) } } let arg012 = [&arg0, &arg1, &arg2]; let arg012 = &arg012[0..argn as usize]; { struct InstrOrArgs<'a, 'i>(&'a [&'a (u16, char)], &'i Instruction<'i>, &'i BTreeMap<&'static str, Argument<'i>>); impl fmt::Display for InstrOrArgs<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (argument, (arg, c)) in self.1.args.iter().zip(self.0) { write!(f, " | (({}) << {})", self.2[&argument[..]].display_reassemble(*c), arg.trailing_zeros())?; } Ok(()) } } reassemble_instructions.push(format!("SuperHInstruction::{}{} => 0b{:04b}_{:04b}_{:04b}_{:04b}{},", instr_name, InstrBareArgs(arg012, true), (base & 0xF000) >> (4 * 3), (base & 0x0F00) >> (4 * 2), (base & 0x00F0) >> (4 * 1), (base & 0x000F) >> (4 * 0), InstrOrArgs(arg012, instr, &arguments))); } { struct InstrOrArgs<'a, 'i>(&'a [&'a (u16, char)], &'i Instruction<'i>, &'i BTreeMap<&'static str, Argument<'i>>); impl fmt::Display for InstrOrArgs<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (argument, (arg, c)) in self.1.args.iter().zip(self.0) { write!(f, " | (({}) << {})", self.2[&argument[..]].display_reassemble(*c), arg.trailing_zeros())?; } Ok(()) } } display_instructions.push(format!("SuperHInstruction::{}{} => write!(f, {:?}, {}), // {}", instr_name, InstrBareArgs(arg012, true), display_format, InstrBareArgs(arg012, false), instr.disp)); } } let t6 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("instruction_reassemble.rs"))) .expect("instruction_reassemble"); writeln!(outf, "impl Into for SuperHInstruction {{").unwrap(); writeln!(outf, " fn into(self) -> u16 {{").unwrap(); writeln!(outf, " match self {{").unwrap(); for ri in reassemble_instructions { writeln!(outf, " {}", ri).unwrap(); } writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); } let t7 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("instruction_display.rs"))).expect("instruction_display"); writeln!(outf, "impl fmt::Display for SuperHInstruction {{").unwrap(); writeln!(outf, " fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{").unwrap(); writeln!(outf, " match self {{").unwrap(); for di in display_instructions { writeln!(outf, " {}", di).unwrap(); } writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); } let t8 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("instruction_feature.rs"))).expect("instruction_feature"); writeln!(outf, "impl SuperHInstruction {{").unwrap(); writeln!(outf, " pub fn features(self) -> SuperHFeatures {{").unwrap(); writeln!(outf, " match self {{").unwrap(); for (features, instructions) in feature_instructions { for (i, &(instr, argn)) in instructions.iter().enumerate() { write!(outf, " SuperHInstruction::{}{} ", instr, if argn != 0 { "(..)" } else { "" }).unwrap(); if i != instructions.len() - 1 { writeln!(outf, "|").unwrap(); } } write!(outf, "=> SuperHFeatures::empty()").unwrap(); for f in features { write!(outf, "| SuperHFeatures::{}", f).unwrap(); } writeln!(outf, ",").unwrap(); } writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); } let t9 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("instruction_level.rs"))).expect("instruction_level"); writeln!(outf, "impl SuperHInstruction {{").unwrap(); writeln!(outf, " pub fn level(self) -> SuperHLevel {{").unwrap(); writeln!(outf, " match self {{").unwrap(); for (level, instructions) in level_instructions { for (i, &(instr, argn)) in instructions.iter().enumerate() { write!(outf, " SuperHInstruction::{}{} ", instr, if argn != 0 { "(..)" } else { "" }).unwrap(); if i != instructions.len() - 1 { writeln!(outf, "|").unwrap(); } } writeln!(outf, "=> SuperHLevel::{},", level.unwrap_or("Sh")).unwrap(); } writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); } let t10 = Instant::now(); { let mut outf = File::create(dbg!(PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")).join("instruction_privilege.rs"))).expect("instruction_privilege"); writeln!(outf, "impl SuperHInstruction {{").unwrap(); writeln!(outf, " pub fn is_privileged(self) -> bool {{").unwrap(); writeln!(outf, " match self {{").unwrap(); for (is, instructions) in privilege_instructions.iter().enumerate() { for (i, &(instr, argn)) in instructions.iter().enumerate() { write!(outf, " SuperHInstruction::{}{} ", instr, if argn != 0 { "(..)" } else { "" }).unwrap(); if i != instructions.len() - 1 { writeln!(outf, "|").unwrap(); } } writeln!(outf, "=> {},", is != 0).unwrap(); } writeln!(outf, " }}").unwrap(); writeln!(outf, " }}").unwrap(); writeln!(outf, "}}").unwrap(); } let t11 = Instant::now(); dbg!(t2 - t1); dbg!(t3 - t2); dbg!(t4 - t3); dbg!(t5 - t4); dbg!(t6 - t5); dbg!(t7 - t6); dbg!(t8 - t7); dbg!(t9 - t8); dbg!(t10 - t9); dbg!(t11 - t10); dbg!(t11 - t1); // panic!(); }