use std::{ collections::{hash_map::Entry, HashMap}, fs::File, io::Write, str::FromStr, }; use pratt::{Affix, Associativity, PrattParser, Precedence, Result as PrattResult}; use proc_macro2::{token_stream, Ident, Literal, Spacing, Span, TokenStream, TokenTree}; use quote::{__private::ext::RepToTokensExt, quote, ToTokens}; use syn::{parse::Parser, LitInt}; use crate::{load_isa, rustfmt, to_rust_variant, Field, Isa, Modifier, Opcode}; type Error = Box; type Result = std::result::Result; macro_rules! token_stream { ($stream:ident) => { TokenStream::from_iter($stream.into_iter()) }; } pub(crate) fn asm_main() -> Result<()> { let isa = load_isa()?; let mut unformatted_code = Vec::::new(); writeln!(&mut unformatted_code, "{}", quote! { use crate::prelude::*; })?; writeln!(&mut unformatted_code, "{}", gen_fields(&isa)?)?; writeln!(&mut unformatted_code, "{}", gen_opcode_from_str(&isa)?)?; let formatted_code = rustfmt(unformatted_code); File::create("./asm/src/generated.rs")?.write_all(&formatted_code)?; Ok(()) } fn gen_apply_field(field: &Field) -> Result { let mut val = quote! { value as u32 }; let val_shift = field.shift_left; if field.shift_left > 0 { val = quote!((#val >> #val_shift)); } // https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend if field.signed { // let mask2 = 1u32 << (self.bits.0.len() - 1); // let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site()); // val = quote!((((#val ^ #mask2).wrapping_sub(#mask2)) as i32)) } else { // val = quote! { #val }; } if field.split { val = quote!((((#val & 0b11111_00000u32) >> 5u32) | ((#val & 0b00000_11111u32) << 5u32)) as u32); } let mask = (1u32 << field.bits.0.len()) - 1; if mask != 0xFFFF_FFFF { let mask = LitInt::new(&format!("0x{:x}", mask), Span::call_site()); val = quote!((#val & #mask)); } let shift = 32 - field.bits.0.end; if shift > 0 { val = quote!((#val << #shift)); } let ident = to_rust_variant(&field.name)?; Ok(quote! { Field::#ident => code | #val, }) } fn gen_fields(isa: &Isa) -> Result { let fields = TokenStream::from_iter( isa.fields .iter() .filter(|field| !field.bits.0.is_empty()) .map(|field| -> Result { let ident = to_rust_variant(field.name.as_str())?; Ok(quote! { #ident, }) }) .try_collect::()?, ); let field_match = TokenStream::from_iter( isa.fields .iter() .filter(|field| !field.bits.0.is_empty()) .map(|field| gen_apply_field(field)) .try_collect::()?, ); return Ok(quote! { pub enum Field { #fields } pub const fn apply_field(code: u32, field: Field, value: i32) -> u32 { match field { #field_match } } }); } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_gpr() { assert_eq!(parse_gpr("r0").unwrap(), (0, "")); assert_eq!(parse_gpr("r31").unwrap(), (31, "")); assert_eq!(parse_gpr("1234").unwrap(), (1234, "")); } } struct OpcodeInfo { code: u32, args: Vec, } fn gen_opcode_from_str(isa: &Isa) -> Result { let mut map_builder = phf_codegen::Map::::new(); let mut opcode_map = HashMap::>>::new(); for opcode in &isa.opcodes { let mut modifiers = Vec::::with_capacity(opcode.modifiers.len()); for modifier_name in &opcode.modifiers { modifiers.push(match isa.modifiers.iter().find(|m| m.name == *modifier_name) { Some(modifier) => modifier.clone(), None => return Err(Error::from(format!("Modifier {} not found", modifier_name))), }); } 'outer: for bits in 0..(1 << modifiers.len()) { let mut suffix = String::new(); let mut set_bits = 0u32; for (idx, modifier) in modifiers.iter().enumerate() { if bits & (1 << idx) != 0 { if set_bits & (1 << modifier.bit) != 0 { // Incompatible combination continue 'outer; } set_bits |= 1 << modifier.bit; suffix.push(modifier.suffix); } } let name = format!("{}{}", opcode.name, suffix); let info = OpcodeInfo { code: opcode.pattern, args: opcode.args.clone() }; match opcode_map.entry(name) { Entry::Occupied(mut entry) => { let vec = entry.get_mut(); if vec.len() < opcode.args.len() + 1 { vec.resize_with(opcode.args.len() + 1, || None); } vec[opcode.args.len()] = Some(info); } Entry::Vacant(entry) => { let mut vec = Vec::>::new(); vec.resize_with(opcode.args.len() + 1, || None); vec[opcode.args.len()] = Some(info); entry.insert(vec); } } // println!("Adding opcode {}", name); // let quoted_name = format!("\"{}\"", name); // map_builder.entry(name, quoted_name.as_str()); } } for mnemonic in &isa.mnemonics { let opcode = isa.opcodes.iter().find(|o| o.name == mnemonic.opcode).ok_or_else(|| { Error::from(format!("Opcode {} not found for {}", mnemonic.opcode, mnemonic.name)) })?; let modifier_names = mnemonic.modifiers.as_ref().unwrap_or(&opcode.modifiers); let mut modifiers = Vec::::with_capacity(modifier_names.len()); for modifier_name in modifier_names { modifiers.push(match isa.modifiers.iter().find(|m| m.name == *modifier_name) { Some(modifier) => modifier.clone(), None => return Err(Error::from(format!("Modifier {} not found", modifier_name))), }); } 'outer: for bits in 0..(1 << modifiers.len()) { let mut suffix = String::new(); let mut set_bits = 0u32; for (idx, modifier) in modifiers.iter().enumerate() { if bits & (1 << idx) != 0 { if set_bits & (1 << modifier.bit) != 0 { // Incompatible combination continue 'outer; } set_bits |= 1 << modifier.bit; suffix.push(modifier.suffix); } } let name = format!("{}{}", mnemonic.name, suffix); let mut code = opcode.pattern; { let tokens: TokenStream = mnemonic.condition.parse()?; let mut iter = tokens.into_iter(); let mut vec = Vec::::new(); loop { match parse_token(&mut iter) { Some(token) => vec.push(token), None => break, } } let expr = ExprParser.parse(vec.into_iter()).unwrap(); match apply_expr(code, expr, isa)? { ExprResult::Int(out) => { code = out; } _ => unreachable!(), } } // for arg in mnemonic.args { // code = apply_field(code, ) // } let info = OpcodeInfo { code, args: mnemonic.args.clone() }; match opcode_map.entry(name) { Entry::Occupied(mut entry) => { let vec = entry.get_mut(); if vec.len() < mnemonic.args.len() + 1 { vec.resize_with(mnemonic.args.len() + 1, || None); } vec[mnemonic.args.len()] = Some(info); } Entry::Vacant(entry) => { let mut vec = Vec::>::new(); vec.resize_with(mnemonic.args.len() + 1, || None); vec[mnemonic.args.len()] = Some(info); entry.insert(vec); } } // println!("Adding mnemonic {}", name); // let quoted_name = format!("\"{}\"", name); // map_builder.entry(name, quoted_name.as_str()); } } for (name, infos) in opcode_map { let opcodes = TokenStream::from_iter(infos.iter().map(|info| { if let Some(info) = info { let code = LitInt::new(&format!("0x{:x}", info.code), Span::call_site()); let args = TokenStream::from_iter(info.args.iter().map(|arg| { let arg_s = arg.split_once('=').map(|(first, _)| first).unwrap_or(arg); let ident = to_rust_variant(arg_s).unwrap(); quote! { Field::#ident, } })); quote! { Some(OpcodeInfo { code: #code, args: &[ #args ], }), } } else { quote! { None, } } })); map_builder.entry(name, quote! { &[#opcodes] }.to_string().as_str()); } let map: TokenStream = map_builder.build().to_string().parse()?; return Ok(quote! { struct OpcodeInfo { code: u32, args: &'static [Field], } static OPCODES: phf::Map<&'static str, &'static [Option]> = #map; fn opcode_from_str(str: &str) -> Option<&'static [Option]> { OPCODES.get(str).map(|x| *x) } }); } fn apply_field(code: u32, field: &Field, value: i32) -> u32 { let mut val = value as u32; let val_shift = field.shift_left; if field.shift_left > 0 { val = val >> val_shift; } // https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend if field.signed { // let mask2 = 1u32 << (self.bits.0.len() - 1); // let mask2 = LitInt::new(&format!("0x{:x}", mask2), Span::call_site()); // val = quote!((((#val ^ #mask2).wrapping_sub(#mask2)) as i32)) } else { // val = quote! { #val }; } if field.split { val = (((val & 0b11111_00000u32) >> 5u32) | ((val & 0b00000_11111u32) << 5u32)) as u32; } let mask = (1u32 << field.bits.0.len()) - 1; if mask != 0xFFFF_FFFF { val = val & mask; } let shift = 32 - field.bits.0.end; if shift > 0 { val = val << shift; } code | val } #[derive(Debug, PartialEq)] enum Operator { BitAnd, LogicalAnd, Equal, } #[derive(Debug)] enum ParsedToken { Ident(Ident), Group(Vec), Literal(Literal), Operator(Operator), } #[derive(Debug)] pub enum Expr { BinOp(Box, BinOpKind, Box), // UnOp(UnOpKind, Box), Literal(Literal), Ident(Ident), } #[derive(Debug)] pub enum BinOpKind { // & BitAnd, // && LogicalAnd, // == Eq, } #[derive(Debug)] pub enum UnOp {} fn parse_token(iter: &mut token_stream::IntoIter) -> Option { match iter.next() { Some(TokenTree::Group(group)) => { let mut iter = group.stream().into_iter(); let mut vec = Vec::::new(); loop { match parse_token(&mut iter) { Some(token) => vec.push(token), None => break, } } Some(ParsedToken::Group(vec)) } Some(TokenTree::Punct(mut punct)) => { let mut str = String::new(); str.push(punct.as_char()); while punct.spacing() == Spacing::Joint { match iter.next() { Some(TokenTree::Punct(new_punct)) => { punct = new_punct; } token => panic!("unexpected token {:?}", token), } str.push(punct.as_char()); } Some(ParsedToken::Operator(match str.as_str() { "&" => Operator::BitAnd, "&&" => Operator::LogicalAnd, "==" => Operator::Equal, op => todo!("operator {}", op), })) } Some(TokenTree::Ident(ident)) => Some(ParsedToken::Ident(ident)), Some(TokenTree::Literal(literal)) => Some(ParsedToken::Literal(literal)), None => None, } } struct ExprParser; impl PrattParser for ExprParser where I: Iterator { type Error = pratt::NoError; type Input = ParsedToken; type Output = Expr; fn query(&mut self, tree: &ParsedToken) -> PrattResult { let affix = match tree { ParsedToken::Operator(Operator::BitAnd) => { Affix::Infix(Precedence(3), Associativity::Left) } ParsedToken::Operator(Operator::Equal) => { Affix::Infix(Precedence(2), Associativity::Left) } ParsedToken::Operator(Operator::LogicalAnd) => { Affix::Infix(Precedence(1), Associativity::Left) } ParsedToken::Group(_) | ParsedToken::Literal(_) | ParsedToken::Ident(_) => { Affix::Nilfix } }; Ok(affix) } // Construct a primary expression, e.g. a number fn primary(&mut self, tree: ParsedToken) -> PrattResult { let expr = match tree { ParsedToken::Ident(num) => Expr::Ident(num), ParsedToken::Literal(literal) => Expr::Literal(literal), ParsedToken::Group(group) => self.parse(&mut group.into_iter()).unwrap(), _ => unreachable!(), }; Ok(expr) } // Construct a binary infix expression, e.g. 1+1 fn infix(&mut self, lhs: Expr, tree: ParsedToken, rhs: Expr) -> PrattResult { let op = match tree { ParsedToken::Operator(Operator::BitAnd) => BinOpKind::BitAnd, ParsedToken::Operator(Operator::LogicalAnd) => BinOpKind::LogicalAnd, ParsedToken::Operator(Operator::Equal) => BinOpKind::Eq, _ => unreachable!(), }; Ok(Expr::BinOp(Box::new(lhs), op, Box::new(rhs))) } fn prefix(&mut self, _tree: ParsedToken, _rhs: Expr) -> PrattResult { unreachable!() } fn postfix(&mut self, _lhs: Expr, _tree: ParsedToken) -> PrattResult { unreachable!() } } #[derive(Debug)] enum ExprResult { Int(u32), Ident(String), } fn apply_expr(mut code: u32, expr: Expr, isa: &Isa) -> Result { match expr { Expr::BinOp(lhs, kind, rhs) => match kind { BinOpKind::BitAnd => match *lhs { // ignoring rhs Expr::Ident(ident) => Ok(ExprResult::Ident(ident.to_string())), other => todo!("BitAnd {:?}", other), }, BinOpKind::LogicalAnd => { code = match apply_expr(code, *lhs, isa)? { ExprResult::Int(code) => code, _ => unreachable!(), }; code = match apply_expr(code, *rhs, isa)? { ExprResult::Int(code) => code, _ => unreachable!(), }; Ok(ExprResult::Int(code)) } BinOpKind::Eq => { let field_name = match apply_expr(code, *lhs, isa)? { ExprResult::Ident(ident) => ident, other => todo!("eq lhs {:?}", other), }; let field = isa .fields .iter() .find(|field| field.name == field_name) .ok_or_else(|| Error::from(format!("Field {} not found", field_name)))?; let value = match apply_expr(code, *rhs, isa)? { ExprResult::Int(value) => value, // other => todo!("eq rhs {:?}", other), _ => return Ok(ExprResult::Int(code)) }; code = apply_field(code, field, value as i32); Ok(ExprResult::Int(code)) } }, Expr::Literal(lit) => Ok(ExprResult::Int(LitInt::from(lit).base10_parse()?)), Expr::Ident(id) => Ok(ExprResult::Ident(id.to_string())), } }