From 2fb81cb85e8cb74a6021ee846c3f99eb00ff24e3 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 2 Sep 2020 20:36:23 -0700 Subject: [PATCH] code compiles --- .../Cargo.toml | 2 +- .../src/inline_assembly.rs | 279 ++++++--- .../src/instructions.rs | 564 ++++++++++++++++++ .../src/lib.rs | 467 +-------------- src/lib.rs | 332 ++--------- src/main.rs | 71 ++- src/python.rs | 11 +- tests/test_power_instruction_analyzer.py | 63 +- 8 files changed, 937 insertions(+), 852 deletions(-) create mode 100644 power-instruction-analyzer-proc-macro/src/instructions.rs diff --git a/power-instruction-analyzer-proc-macro/Cargo.toml b/power-instruction-analyzer-proc-macro/Cargo.toml index 3b72c25..7c4df5c 100644 --- a/power-instruction-analyzer-proc-macro/Cargo.toml +++ b/power-instruction-analyzer-proc-macro/Cargo.toml @@ -13,4 +13,4 @@ proc-macro = true [dependencies] quote = "1.0" proc-macro2 = "1.0" -syn = { version = "1.0", features = ["full", "parsing"] } \ No newline at end of file +syn = { version = "1.0", features = ["full", "parsing", "extra-traits"] } \ No newline at end of file diff --git a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs index a10e050..6378cb7 100644 --- a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs +++ b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs @@ -4,20 +4,146 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use std::{ - borrow::Cow, collections::HashMap, fmt::Write, - hash::{Hash, Hasher}, - marker::PhantomPinned, - ops::{Add, AddAssign, Deref, DerefMut}, - pin::Pin, - rc::Rc, + hash::Hash, + ops::{Deref, DerefMut}, sync::atomic::{AtomicU64, Ordering}, }; use syn::LitStr; +macro_rules! append_assembly { + ($retval:ident;) => {}; + ($retval:ident; $lit:literal $($tt:tt)*) => { + $crate::inline_assembly::ToAssembly::append_to($lit, &mut $retval); + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; input($arg_id:ident = {$($arg_tt:tt)*}) $($tt:tt)*) => { + { + let (arg, arg_id) = $crate::inline_assembly::Assembly::make_input(quote! {$($arg_tt)*}); + $crate::inline_assembly::ToAssembly::append_to(&arg, &mut $retval); + $arg_id = arg_id; + } + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; input{$($arg_tt:tt)*} $($tt:tt)*) => { + { + let (arg, _arg_id) = $crate::inline_assembly::Assembly::make_input(quote! {$($arg_tt)*}); + $crate::inline_assembly::ToAssembly::append_to(&arg, &mut $retval); + } + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; output($arg_id:ident = {$($arg_tt:tt)*}) $($tt:tt)*) => { + { + let (arg, arg_id) = $crate::inline_assembly::Assembly::make_output(quote! {$($arg_tt)*}); + $crate::inline_assembly::ToAssembly::append_to(&arg, &mut $retval); + $arg_id = arg_id; + } + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; output{$($arg_tt:tt)*} $($tt:tt)*) => { + { + let (arg, _arg_id) = $crate::inline_assembly::Assembly::make_output(quote! {$($arg_tt)*}); + $crate::inline_assembly::ToAssembly::append_to(&arg, &mut $retval); + } + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; clobber{$($arg_tt:tt)*} $($tt:tt)*) => { + $crate::inline_assembly::ToAssembly::append_to( + &$crate::inline_assembly::Assembly::make_clobber(quote::quote! {$($arg_tt)*}), + &mut $retval + ); + append_assembly!($retval; $($tt)*); + }; + ($retval:ident; ($arg_id:ident) $($tt:tt)*) => { + $crate::inline_assembly::ToAssembly::append_to(&$arg_id, &mut $retval); + append_assembly!($retval; $($tt)*); + }; +} + +macro_rules! assembly { + () => { + $crate::inline_assembly::Assembly::new() + }; + ($($tt:tt)*) => { + { + let mut retval = $crate::inline_assembly::Assembly::new(); + append_assembly!(retval; $($tt)*); + retval + } + }; +} + pub(crate) trait ToAssembly { - fn to_assembly(&self) -> Assembly; + /// appends `self` to `retval` + fn append_to(&self, retval: &mut Assembly); + + fn to_assembly(&self) -> Assembly { + let mut retval = Assembly::default(); + self.append_to(&mut retval); + retval + } + + fn into_assembly(self) -> Assembly + where + Self: Sized, + { + let mut retval = Assembly::default(); + self.append_to(&mut retval); + retval + } +} + +impl ToAssembly for &'_ T { + fn append_to(&self, retval: &mut Assembly) { + (**self).append_to(retval); + } + + fn to_assembly(&self) -> Assembly { + (**self).to_assembly() + } +} + +impl ToAssembly for &'_ mut T { + fn append_to(&self, retval: &mut Assembly) { + (**self).append_to(retval); + } + + fn to_assembly(&self) -> Assembly { + (**self).to_assembly() + } +} + +impl ToAssembly for Box { + fn append_to(&self, retval: &mut Assembly) { + (**self).append_to(retval); + } + + fn to_assembly(&self) -> Assembly { + (**self).to_assembly() + } + + fn into_assembly(self) -> Assembly { + (*self).into_assembly() + } +} + +impl ToAssembly for str { + fn append_to(&self, retval: &mut Assembly) { + if let Some(AssemblyTextFragment::Text(text)) = retval.text_fragments.last_mut() { + *text += self; + } else { + retval + .text_fragments + .push(AssemblyTextFragment::Text(self.into())); + } + } +} + +impl ToAssembly for String { + fn append_to(&self, retval: &mut Assembly) { + str::append_to(&self, retval) + } } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -31,9 +157,17 @@ impl AssemblyArgId { } } +impl ToAssembly for AssemblyArgId { + fn append_to(&self, retval: &mut Assembly) { + retval + .text_fragments + .push(AssemblyTextFragment::ArgIndex(*self)); + } +} + macro_rules! impl_assembly_arg { ( - $vis:vis struct $name:ident { + struct $name:ident { tokens: TokenStream, $( $id:ident: AssemblyArgId, @@ -41,17 +175,11 @@ macro_rules! impl_assembly_arg { } ) => { #[derive(Debug, Clone)] - $vis struct $name { + struct $name { tokens: TokenStream, $($id: AssemblyArgId,)? } - impl $name { - $vis fn new(tokens: impl ToTokens) -> Self { - tokens.into_token_stream().into() - } - } - impl ToTokens for $name { fn to_token_stream(&self) -> TokenStream { self.tokens.clone() @@ -78,21 +206,21 @@ macro_rules! impl_assembly_arg { } impl_assembly_arg! { - pub(crate) struct AssemblyInputArg { + struct AssemblyInputArg { tokens: TokenStream, id: AssemblyArgId, } } impl_assembly_arg! { - pub(crate) struct AssemblyOutputArg { + struct AssemblyOutputArg { tokens: TokenStream, id: AssemblyArgId, } } impl_assembly_arg! { - pub(crate) struct AssemblyClobber { + struct AssemblyClobber { tokens: TokenStream, } } @@ -126,10 +254,55 @@ impl From<&'_ str> for Assembly { } } +impl From for Assembly { + fn from(arg_id: AssemblyArgId) -> Self { + Self { + text_fragments: vec![AssemblyTextFragment::ArgIndex(arg_id)], + ..Self::default() + } + } +} + +impl From<&'_ AssemblyArgId> for Assembly { + fn from(arg_id: &AssemblyArgId) -> Self { + Self::from(*arg_id) + } +} + impl Assembly { pub(crate) fn new() -> Self { Self::default() } + pub(crate) fn make_input(tokens: impl ToTokens) -> (Self, AssemblyArgId) { + let input: AssemblyInputArg = tokens.into_token_stream().into(); + let id = input.id; + ( + Self { + text_fragments: vec![AssemblyTextFragment::ArgIndex(id)], + inputs: vec![input], + ..Self::default() + }, + id, + ) + } + pub(crate) fn make_output(tokens: impl ToTokens) -> (Self, AssemblyArgId) { + let output: AssemblyOutputArg = tokens.into_token_stream().into(); + let id = output.id; + ( + Self { + text_fragments: vec![AssemblyTextFragment::ArgIndex(id)], + outputs: vec![output], + ..Self::default() + }, + id, + ) + } + pub(crate) fn make_clobber(tokens: impl ToTokens) -> Self { + Self { + clobbers: vec![tokens.into_token_stream().into()], + ..Self::default() + } + } pub(crate) fn to_text(&self) -> String { let mut id_index_map = HashMap::new(); for (index, id) in self @@ -166,75 +339,29 @@ impl Assembly { } } -impl AddAssign<&'_ Assembly> for Assembly { - fn add_assign(&mut self, rhs: &Assembly) { - let Self { - text_fragments, - inputs, - outputs, - clobbers, - } = self; - text_fragments.reserve(rhs.text_fragments.len()); - for text_fragment in &rhs.text_fragments { +impl ToAssembly for Assembly { + fn append_to(&self, retval: &mut Assembly) { + retval.text_fragments.reserve(self.text_fragments.len()); + for text_fragment in &self.text_fragments { match *text_fragment { - AssemblyTextFragment::Text(ref rhs_text) => { - if let Some(AssemblyTextFragment::Text(text)) = text_fragments.last_mut() { - *text += rhs_text; - } else { - text_fragments.push(AssemblyTextFragment::Text(rhs_text.clone())); - } - } - AssemblyTextFragment::ArgIndex(id) => { - self.text_fragments.push(AssemblyTextFragment::ArgIndex(id)); - } + AssemblyTextFragment::Text(ref text) => text.append_to(retval), + AssemblyTextFragment::ArgIndex(id) => id.append_to(retval), } } - inputs.extend_from_slice(&rhs.inputs); - outputs.extend_from_slice(&rhs.outputs); - clobbers.extend_from_slice(&rhs.clobbers); - } -} - -impl AddAssign for Assembly { - fn add_assign(&mut self, rhs: Assembly) { - *self += &rhs; + retval.inputs.extend_from_slice(&self.inputs); + retval.outputs.extend_from_slice(&self.outputs); + retval.clobbers.extend_from_slice(&self.clobbers); } -} -impl Add for Assembly { - type Output = Assembly; - - fn add(mut self, rhs: Self) -> Self::Output { - self += rhs; - self + fn to_assembly(&self) -> Assembly { + self.clone() } -} -impl Add<&'_ Assembly> for Assembly { - type Output = Assembly; - - fn add(mut self, rhs: &Assembly) -> Self::Output { - self += rhs; + fn into_assembly(self) -> Assembly { self } } -impl Add for &'_ Assembly { - type Output = Assembly; - - fn add(self, rhs: Assembly) -> Self::Output { - Assembly::clone(self) + rhs - } -} - -impl Add<&'_ Assembly> for &'_ Assembly { - type Output = Assembly; - - fn add(self, rhs: &Assembly) -> Self::Output { - Assembly::clone(self) + rhs - } -} - #[derive(Debug, Clone)] pub(crate) struct AssemblyWithTextSpan { pub(crate) asm: Assembly, diff --git a/power-instruction-analyzer-proc-macro/src/instructions.rs b/power-instruction-analyzer-proc-macro/src/instructions.rs new file mode 100644 index 0000000..e1a7988 --- /dev/null +++ b/power-instruction-analyzer-proc-macro/src/instructions.rs @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// See Notices.txt for copyright information + +use crate::inline_assembly::{Assembly, AssemblyWithTextSpan}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, ToTokens, TokenStreamExt}; +use std::{collections::HashMap, fmt, hash::Hash}; +use syn::{ + braced, bracketed, parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Error, LitStr, Token, +}; + +trait InstructionArg: Clone + fmt::Debug + ToTokens + Parse { + type Enumerant: Copy + Eq + Hash + fmt::Debug; + fn enumerant(&self) -> Self::Enumerant; + fn span(&self) -> &Span; + fn name(&self) -> &'static str; + fn into_ident(self) -> Ident; +} + +macro_rules! valid_enumerants_as_string { + ($enumerant:ident) => { + concat!("`", stringify!($enumerant), "`") + }; + ($enumerant1:ident, $enumerant2:ident) => { + concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`") + }; + ($($enumerant:ident),+) => { + valid_enumerants_as_string!((), ($($enumerant),+)) + }; + (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => { + concat!( + "`", + stringify!($first_enumerant), + $( + "`, `", + stringify!($enumerant), + )+ + "`, and `", + stringify!($last_enumerant), + "`" + ) + }; + (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => { + valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*)) + }; + () => { + "" + }; +} + +macro_rules! ident_enum { + ( + #[parse_error_msg = $parse_error_msg:literal] + enum $enum_name:ident { + $( + $enumerant:ident, + )* + } + ) => { + #[derive(Copy, Clone, Eq, PartialEq, Hash)] + enum $enum_name { + $( + $enumerant(T), + )* + } + + impl InstructionArg for $enum_name { + type Enumerant = $enum_name<()>; + fn enumerant(&self) -> Self::Enumerant { + $enum_name::enumerant(self) + } + fn span(&self) -> &Span { + match self { + $( + $enum_name::$enumerant(span) => span, + )* + } + } + fn name(&self) -> &'static str { + $enum_name::name(self) + } + fn into_ident(self) -> Ident { + match self { + $( + $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span), + )* + } + } + } + + impl fmt::Debug for $enum_name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } + } + + impl $enum_name { + fn enumerant(&self) -> $enum_name<()> { + match self { + $( + $enum_name::$enumerant(_) => $enum_name::$enumerant(()), + )* + } + } + fn name(&self) -> &'static str { + match self { + $( + $enum_name::$enumerant(_) => stringify!($enumerant), + )* + } + } + } + + impl ToTokens for $enum_name { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append(self.clone().into_ident()); + } + } + + impl Parse for $enum_name { + fn parse(input: ParseStream) -> syn::Result { + let id: Ident = input.parse()?; + $( + if id == stringify!($enumerant) { + return Ok($enum_name::$enumerant(id.span())); + } + )* + Err(Error::new_spanned( + id, + concat!( + $parse_error_msg, + ": valid values are: ", + valid_enumerants_as_string!($($enumerant),*) + ) + )) + } + } + }; +} + +ident_enum! { + #[parse_error_msg = "unknown instruction input"] + enum InstructionInput { + Ra, + Rb, + Rc, + Carry, + Overflow, + } +} + +ident_enum! { + #[parse_error_msg = "unknown instruction output"] + enum InstructionOutput { + Rt, + Carry, + Overflow, + CR0, + CR1, + CR2, + CR3, + CR4, + CR5, + CR6, + CR7, + } +} + +#[derive(Debug)] +struct Instruction { + enumerant: Ident, + fn_name: Ident, + inputs: Punctuated, + outputs: Punctuated, + instruction_name: LitStr, +} + +fn check_duplicate_free<'a, T: InstructionArg + 'a>( + args: impl IntoIterator, +) -> syn::Result<()> { + let mut seen_args = HashMap::new(); + for arg in args { + if let Some(prev_arg) = seen_args.insert(arg.enumerant(), arg) { + let mut error = Error::new( + arg.span().clone(), + format_args!( + "duplicate instruction argument: {}", + arg.clone().into_ident() + ), + ); + error.combine(Error::new( + prev_arg.span().clone(), + format_args!( + "duplicate instruction argument: {}", + prev_arg.clone().into_ident() + ), + )); + return Err(error); + } + } + Ok(()) +} + +impl Parse for Instruction { + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + let enumerant_attr_tokens; + bracketed!(enumerant_attr_tokens in input); + let enumerant_name: Ident = enumerant_attr_tokens.parse()?; + if enumerant_name != "enumerant" { + return Err(Error::new_spanned( + enumerant_name, + "expected `#[enumerant = ...]` attribute", + )); + } + enumerant_attr_tokens.parse::()?; + let enumerant: Ident = enumerant_attr_tokens.parse()?; + input.parse::()?; + let fn_name: Ident = input.parse()?; + let inputs_tokens; + parenthesized!(inputs_tokens in input); + let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?; + check_duplicate_free(&inputs)?; + input.parse::)>()?; + let outputs_tokens; + parenthesized!(outputs_tokens in input); + let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?; + check_duplicate_free(&outputs)?; + let body_tokens; + braced!(body_tokens in input); + let instruction_name: LitStr = body_tokens.parse()?; + Ok(Self { + enumerant, + fn_name, + inputs, + outputs, + instruction_name, + }) + } +} + +impl Instruction { + fn map_input_registers(&self) -> syn::Result> { + let mut retval = Vec::new(); + for input in &self.inputs { + retval.push(match input { + InstructionInput::Ra(_) => quote! {InstructionInputRegister::Ra}, + InstructionInput::Rb(_) => quote! {InstructionInputRegister::Rb}, + InstructionInput::Rc(_) => quote! {InstructionInputRegister::Rc}, + InstructionInput::Carry(_) => quote! {InstructionInputRegister::Carry}, + InstructionInput::Overflow(_) => quote! {InstructionInputRegister::Overflow}, + }); + } + Ok(retval) + } + fn to_native_fn_tokens(&self) -> syn::Result { + let Instruction { + enumerant: _, + fn_name, + inputs, + outputs, + instruction_name, + } = self; + let asm_instr = Assembly::from(instruction_name.value()); + let mut asm_instr_args = Vec::new(); + let mut before_instr_asm_lines = Vec::::new(); + let mut after_instr_asm_lines = Vec::::new(); + let mut before_asm = Vec::::new(); + let mut after_asm = Vec::::new(); + let mut need_carry_output = false; + let mut need_overflow_output = false; + let mut need_cr_output = false; + for output in outputs { + match output { + InstructionOutput::Rt(_) => { + before_asm.push(quote! {let rt: u64;}); + asm_instr_args.push(assembly! {"$" output{"=&b"(rt)} }); + after_asm.push(quote! {retval.rt = Some(rt);}); + } + InstructionOutput::Carry(_) => { + need_carry_output = true; + } + InstructionOutput::Overflow(_) => { + need_overflow_output = true; + } + InstructionOutput::CR0(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr0 = Some(ConditionRegister::from_cr_field(cr, 0)); + }); + } + InstructionOutput::CR1(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr1 = Some(ConditionRegister::from_cr_field(cr, 1)); + }); + } + InstructionOutput::CR2(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr2 = Some(ConditionRegister::from_cr_field(cr, 2)); + }); + } + InstructionOutput::CR3(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr3 = Some(ConditionRegister::from_cr_field(cr, 3)); + }); + } + InstructionOutput::CR4(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr4 = Some(ConditionRegister::from_cr_field(cr, 4)); + }); + } + InstructionOutput::CR5(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr5 = Some(ConditionRegister::from_cr_field(cr, 5)); + }); + } + InstructionOutput::CR6(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr6 = Some(ConditionRegister::from_cr_field(cr, 6)); + }); + } + InstructionOutput::CR7(_) => { + need_cr_output = true; + after_asm.push(quote! { + retval.cr7 = Some(ConditionRegister::from_cr_field(cr, 7)); + }); + } + } + } + let mut need_carry_input = false; + let mut need_overflow_input = false; + for input in inputs { + match input { + InstructionInput::Ra(_) => { + before_asm.push(quote! {let ra: u64 = inputs.try_get_ra()?;}); + asm_instr_args.push(assembly! {"$" input{"b"(ra)} }); + } + InstructionInput::Rb(_) => { + before_asm.push(quote! {let rb: u64 = inputs.try_get_rb()?;}); + asm_instr_args.push(assembly! {"$" input{"b"(rb)} }); + } + InstructionInput::Rc(_) => { + before_asm.push(quote! {let rc: u64 = inputs.try_get_rc()?;}); + asm_instr_args.push(assembly! {"$" input{"b"(rc)} }); + } + InstructionInput::Carry(_) => { + need_carry_input = true; + } + InstructionInput::Overflow(_) => { + need_overflow_input = true; + } + } + } + if need_carry_input || need_carry_output || need_overflow_input || need_overflow_output { + before_asm.push(quote! { + let mut xer_in: u64 = 0; + let mut xer_mask_in: u64 = !0; + }); + if need_carry_input || need_carry_output { + before_asm.push(quote! { + xer_mask_in &= !CarryFlags::XER_MASK; + }); + } + if need_overflow_input || need_overflow_output { + before_asm.push(quote! { + xer_mask_in &= !OverflowFlags::XER_MASK; + }); + } + if need_carry_input { + before_asm.push(quote! { + xer_in |= inputs.try_get_carry()?.to_xer(); + }); + } + if need_overflow_input { + before_asm.push(quote! { + xer_in |= inputs.try_get_overflow()?.to_xer(); + }); + } + before_asm.push(quote! { + let xer_out: u64; + }); + let xer_out; + before_instr_asm_lines.push(assembly! { + "mfxer $" output(xer_out = {"=&b"(xer_out)}) + }); + before_instr_asm_lines.push(assembly! { + "and $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_mask_in)} + }); + before_instr_asm_lines.push(assembly! { + "or $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_in)} + }); + before_instr_asm_lines.push(assembly! { + "mtxer $" (xer_out) clobber{"xer"} + }); + after_instr_asm_lines.push(assembly! { + "mfxer $" (xer_out) + }); + if need_carry_output { + after_asm.push(quote! { + retval.carry = Some(CarryFlags::from_xer(xer_out)); + }); + } + if need_overflow_output { + after_asm.push(quote! { + retval.overflow = Some(OverflowFlags::from_xer(xer_out)); + }); + } + } + if need_cr_output { + before_asm.push(quote! { + let cr: u32; + }); + after_instr_asm_lines.push(assembly! { + "mfcr $" output{"=&b"(cr)} clobber{"cr"} + }); + } + let mut final_asm = assembly! {}; + for i in before_instr_asm_lines { + append_assembly! {final_asm; (i) "\n"}; + } + append_assembly!(final_asm; (asm_instr)); + let mut separator = " "; + for i in asm_instr_args { + append_assembly!(final_asm; (separator) (i)); + separator = ", "; + } + for i in after_instr_asm_lines { + append_assembly! {final_asm; "\n" (i)}; + } + let asm = AssemblyWithTextSpan { + asm: final_asm, + text_span: instruction_name.span(), + }; + Ok(quote! { + pub fn #fn_name(inputs: InstructionInput) -> InstructionResult { + #![allow(unused_variables, unused_assignments)] + #(#before_asm)* + unsafe { + #asm; + } + let mut retval = InstructionOutput::default(); + #(#after_asm)* + Ok(retval) + } + }) + } +} + +#[derive(Debug)] +pub(crate) struct Instructions { + instructions: Vec, +} + +impl Instructions { + pub(crate) fn to_tokens(&self) -> syn::Result { + let mut fn_names = Vec::new(); + let mut instr_enumerants = Vec::new(); + let mut get_native_fn_match_cases = Vec::new(); + let mut get_model_fn_match_cases = Vec::new(); + let mut get_used_input_registers_match_cases = Vec::new(); + let mut name_match_cases = Vec::new(); + let mut enumerants = Vec::new(); + let mut native_fn_tokens = Vec::new(); + for instruction in &self.instructions { + let Instruction { + enumerant, + fn_name, + inputs: _, + outputs: _, + instruction_name, + } = instruction; + fn_names.push(fn_name); + enumerants.push(enumerant); + instr_enumerants.push(quote! { + #[serde(rename = #instruction_name)] + #enumerant, + }); + get_native_fn_match_cases.push(quote! { + Self::#enumerant => native_instrs::#fn_name, + }); + get_model_fn_match_cases.push(quote! { + Self::#enumerant => instr_models::#fn_name, + }); + let mapped_input_registers = instruction.map_input_registers()?; + get_used_input_registers_match_cases.push(quote! { + Self::#enumerant => &[#(#mapped_input_registers),*], + }); + name_match_cases.push(quote! { + Self::#enumerant => #instruction_name, + }); + native_fn_tokens.push(instruction.to_native_fn_tokens()?); + } + Ok(quote! { + #[cfg(feature = "python")] + macro_rules! wrap_all_instr_fns { + ($m:ident) => { + wrap_instr_fns! { + #![pymodule($m)] + + #(fn #fn_names(inputs: InstructionInput) -> InstructionResult;)* + } + }; + } + + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] + pub enum Instr { + #(#instr_enumerants)* + } + + impl Instr { + #[cfg(feature = "native_instrs")] + pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult { + match self { + #(#get_native_fn_match_cases)* + } + } + pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult { + match self { + #(#get_model_fn_match_cases)* + } + } + pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] { + match self { + #(#get_used_input_registers_match_cases)* + } + } + pub fn name(self) -> &'static str { + match self { + #(#name_match_cases)* + } + } + pub const VALUES: &'static [Self] = &[ + #(Self::#enumerants,)* + ]; + } + + #[cfg(feature = "native_instrs")] + pub mod native_instrs { + use super::*; + + #(#native_fn_tokens)* + } + }) + } +} + +impl Parse for Instructions { + fn parse(input: ParseStream) -> syn::Result { + let mut instructions = Vec::new(); + while !input.is_empty() { + instructions.push(input.parse()?); + } + Ok(Self { instructions }) + } +} diff --git a/power-instruction-analyzer-proc-macro/src/lib.rs b/power-instruction-analyzer-proc-macro/src/lib.rs index b5eb205..8dcce4f 100644 --- a/power-instruction-analyzer-proc-macro/src/lib.rs +++ b/power-instruction-analyzer-proc-macro/src/lib.rs @@ -1,471 +1,16 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // See Notices.txt for copyright information -use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, ToTokens, TokenStreamExt}; -use std::{borrow::Cow, fmt, fmt::Write}; -use syn::{ - braced, bracketed, parenthesized, - parse::{Parse, ParseStream}, - parse_macro_input, - punctuated::Punctuated, - Attribute, Error, ItemFn, LitStr, Token, -}; - +#[macro_use] mod inline_assembly; +mod instructions; -macro_rules! valid_enumerants_as_string { - ($enumerant:ident) => { - concat!("`", stringify!($enumerant), "`") - }; - ($enumerant1:ident, $enumerant2:ident) => { - concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`") - }; - ($($enumerant:ident),+) => { - valid_enumerants_as_string!((), ($($enumerant),+)) - }; - (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => { - concat!( - "`", - stringify!($first_enumerant), - $( - "`, `", - stringify!($enumerant), - )+ - "`, and `", - stringify!($last_enumerant), - "`" - ) - }; - (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => { - valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*)) - }; - () => { - "" - }; -} - -macro_rules! ident_enum { - ( - #[parse_error_msg = $parse_error_msg:literal] - enum $enum_name:ident { - $( - $enumerant:ident, - )* - } - ) => { - #[derive(Copy, Clone, Eq, PartialEq, Hash)] - enum $enum_name { - $( - $enumerant(T), - )* - } - - impl fmt::Debug for $enum_name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.name()) - } - } - - impl $enum_name { - fn enumerant(&self) -> $enum_name<()> { - match self { - $( - $enum_name::$enumerant(_) => $enum_name::$enumerant(()), - )* - } - } - fn name(&self) -> &'static str { - match self { - $( - $enum_name::$enumerant(_) => stringify!($enumerant), - )* - } - } - } - - impl $enum_name { - fn into_ident(self) -> Ident { - match self { - $( - $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span), - )* - } - } - } - - impl ToTokens for $enum_name { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.append(self.clone().into_ident()); - } - } - - impl Parse for $enum_name { - fn parse(input: ParseStream) -> syn::Result { - let id: Ident = input.parse()?; - $( - if id == stringify!($enumerant) { - return Ok($enum_name::$enumerant(id.span())); - } - )* - Err(Error::new_spanned( - id, - concat!( - $parse_error_msg, - ": valid values are: ", - valid_enumerants_as_string!($($enumerant),*) - ) - )) - } - } - }; -} - -ident_enum! { - #[parse_error_msg = "unknown instruction input"] - enum InstructionInput { - Ra, - Rb, - Rc, - Carry, - } -} - -ident_enum! { - #[parse_error_msg = "unknown instruction output"] - enum InstructionOutput { - Rt, - Carry, - Overflow, - CR0, - } -} - -struct InlineAssembly { - text: Vec, - text_span: Option, - inputs: Vec, - outputs: Vec, - clobbers: Vec, -} - -impl fmt::Write for InlineAssembly { - fn write_str(&mut self, s: &str) -> fmt::Result { - if let Some(AssemblyTextFragment::Text(v)) = self.text.last_mut() { - *v += s; - } else { - self.text.push(AssemblyTextFragment::Text(String::from(s))); - } - Ok(()) - } -} - -impl From for InlineAssembly { - fn from(s: String) -> Self { - InlineAssembly { - text: vec![AssemblyTextFragment::Text(s)], - text_span: None, - inputs: Vec::new(), - outputs: Vec::new(), - clobbers: Vec::new(), - } - } -} - -impl From<&'_ str> for InlineAssembly { - fn from(s: &'_ str) -> Self { - String::from(s).into() - } -} - -impl InlineAssembly { - fn new(text_span: Span) -> Self { - Self { - text: Vec::new(), - text_span, - inputs: Vec::new(), - outputs: Vec::new(), - clobbers: Vec::new(), - } - } - fn to_text(&self) -> String { - let mut retval = String::new(); - for text in &self.text { - match text { - AssemblyTextFragment::Text(text) => retval += text, - AssemblyTextFragment::InputIndex(index) => { - write!(retval, "{}", index + self.outputs.len()).unwrap(); - } - AssemblyTextFragment::OutputIndex(index) => write!(retval, "{}", index).unwrap(), - } - } - retval - } - fn write_input_index(&mut self, index: usize) -> fmt::Result { - self.text.push(AssemblyTextFragment::InputIndex(index)); - Ok(()) - } - fn write_output_index(&mut self, index: usize) -> fmt::Result { - self.text.push(AssemblyTextFragment::OutputIndex(index)); - Ok(()) - } - fn add_input(&mut self, input: TokenStream) -> usize { - let retval = self.inputs.len(); - self.inputs.push(input); - retval - } - fn add_output(&mut self, output: TokenStream) -> usize { - let retval = self.outputs.len(); - self.outputs.push(output); - retval - } -} - -impl ToTokens for InlineAssembly { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - text: _, - text_span, - inputs, - outputs, - clobbers, - } = self; - let text = LitStr::new(&self.to_text(), text_span.clone()); - let value = quote! { - llvm_asm!(#text : #(#outputs),* : #(#inputs),* : #(#clobbers),*) - }; - value.to_tokens(tokens); - } -} - -#[derive(Debug)] -struct Instruction { - enumerant: Ident, - fn_name: Ident, - inputs: Punctuated, - outputs: Punctuated, - instruction_name: LitStr, -} - -impl Parse for Instruction { - fn parse(input: ParseStream) -> syn::Result { - input.parse::()?; - let enumerant_attr_tokens; - bracketed!(enumerant_attr_tokens in input); - let enumerant_name: Ident = enumerant_attr_tokens.parse()?; - if enumerant_name != "enumerant" { - return Err(Error::new_spanned( - enumerant_name, - "expected `#[enumerant = ...]` attribute", - )); - } - enumerant_attr_tokens.parse::()?; - let enumerant: Ident = enumerant_attr_tokens.parse()?; - input.parse::()?; - let fn_name: Ident = input.parse()?; - let inputs_tokens; - parenthesized!(inputs_tokens in input); - let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?; - input.parse::)>()?; - let outputs_tokens; - parenthesized!(outputs_tokens in input); - let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?; - let body_tokens; - braced!(body_tokens in input); - let instruction_name: LitStr = body_tokens.parse()?; - Ok(Self { - enumerant, - fn_name, - inputs, - outputs, - instruction_name, - }) - } -} - -impl Instruction { - fn map_input_registers(&self) -> syn::Result> { - let mut retval = Vec::new(); - for input in &self.inputs { - match input { - InstructionInput::Ra(_) => retval.push(quote! {InstructionInputRegister::Ra}), - InstructionInput::Rb(_) => retval.push(quote! {InstructionInputRegister::Rb}), - InstructionInput::Rc(_) => retval.push(quote! {InstructionInputRegister::Rc}), - InstructionInput::Carry(_) => retval.push(quote! {InstructionInputRegister::Carry}), - } - } - Ok(retval) - } - fn to_native_fn_tokens(&self) -> syn::Result { - let Instruction { - enumerant, - fn_name, - inputs, - outputs, - instruction_name, - } = self; - let mut asm = InlineAssembly::new(instruction_name.span()); - let mut before_asm = Vec::::new(); - let mut after_asm = Vec::::new(); - for input in &self.inputs { - match input { - InstructionInput::Ra(span) => { - before_asm.push(quote! {let ra = inputs.ra;}); - let input_index = asm.add_input(quote! {"b"(ra)}); - unimplemented!("InstructionInput::Ra"); - } - InstructionInput::Rb(span) => { - unimplemented!("InstructionInput::Rb"); - } - InstructionInput::Rc(span) => { - unimplemented!("InstructionInput::Rc"); - } - InstructionInput::Carry(span) => { - unimplemented!("InstructionInput::Carry"); - } - } - } - for output in &self.outputs { - match output { - InstructionOutput::Rt(span) => { - unimplemented!("InstructionOutput::Rt"); - } - InstructionOutput::Carry(span) => { - unimplemented!("InstructionOutput::Carry"); - } - InstructionOutput::Overflow(span) => { - unimplemented!("InstructionOutput::Overflow"); - } - InstructionOutput::CR0(span) => { - unimplemented!("InstructionOutput::CR0"); - } - } - } - Ok(quote! { - pub fn #fn_name(inputs: InstructionInput) -> InstructionResult { - #![allow(unused_variables, unused_assignments)] - #(#before_asm)* - unsafe { - #asm; - } - let mut retval = InstructionOutput::default(); - #(#after_asm)* - retval - } - }) - } -} - -#[derive(Debug)] -struct Instructions { - instructions: Vec, -} - -impl Instructions { - fn to_tokens(&self) -> syn::Result { - let mut fn_names = Vec::new(); - let mut instr_enumerants = Vec::new(); - let mut get_native_fn_match_cases = Vec::new(); - let mut get_model_fn_match_cases = Vec::new(); - let mut get_used_input_registers_match_cases = Vec::new(); - let mut name_match_cases = Vec::new(); - let mut enumerants = Vec::new(); - let mut native_fn_tokens = Vec::new(); - for instruction in &self.instructions { - let Instruction { - enumerant, - fn_name, - inputs, - outputs, - instruction_name, - } = instruction; - fn_names.push(fn_name); - enumerants.push(enumerant); - instr_enumerants.push(quote! { - #[serde(rename = #instruction_name)] - #enumerant, - }); - get_native_fn_match_cases.push(quote! { - Self::#enumerant => native_instrs::#fn_name, - }); - get_model_fn_match_cases.push(quote! { - Self::#enumerant => instr_models::#fn_name, - }); - let mapped_input_registers = instruction.map_input_registers()?; - get_used_input_registers_match_cases.push(quote! { - Self::#enumerant => &[#(#mapped_input_registers),*], - }); - name_match_cases.push(quote! { - Self::#enumerant => #instruction_name, - }); - native_fn_tokens.push(instruction.to_native_fn_tokens()?); - } - Ok(quote! { - #[cfg(feature = "python")] - macro_rules! wrap_all_instr_fns { - ($m:ident) => { - wrap_instr_fns! { - #![pymodule($m)] - - #(fn #fn_names(inputs: InstructionInput) -> InstructionOutput;)* - } - }; - } - - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] - pub enum Instr { - #(#instr_enumerants)* - } - - impl Instr { - #[cfg(feature = "native_instrs")] - pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionOutput { - match self { - #(#get_native_fn_match_cases)* - } - } - pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionOutput { - match self { - #(#get_model_fn_match_cases)* - } - } - pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] { - match self { - #(#get_used_input_registers_match_cases)* - } - } - pub fn name(self) -> &'static str { - match self { - #(#name_match_cases)* - } - } - pub const VALUES: &'static [Self] = &[ - #(Self::#enumerants,)* - ]; - } - - #[cfg(feature = "native_instrs")] - pub mod native_instrs { - use super::*; - - #(#native_fn_tokens)* - } - }) - } -} - -impl Parse for Instructions { - fn parse(input: ParseStream) -> syn::Result { - let mut instructions = Vec::new(); - while !input.is_empty() { - instructions.push(input.parse()?); - } - Ok(Self { instructions }) - } -} +use instructions::Instructions; +use proc_macro::TokenStream; +use syn::parse_macro_input; #[proc_macro] -pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn instructions(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as Instructions); match input.to_tokens() { Ok(retval) => retval, diff --git a/src/lib.rs b/src/lib.rs index 0121e41..f0ac587 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,15 +12,7 @@ mod serde_hex; use power_instruction_analyzer_proc_macro::instructions; use serde::{Deserialize, Serialize}; use serde_plain::forward_display_to_serde; -use std::{ - cmp::Ordering, - fmt, - ops::{Index, IndexMut}, -}; - -fn is_default(v: &T) -> bool { - T::default() == *v -} +use std::{cmp::Ordering, fmt}; // powerpc bit numbers count from MSB to LSB const fn get_xer_bit_mask(powerpc_bit_num: usize) -> u64 { @@ -47,6 +39,7 @@ macro_rules! xer_subset { $( $field_vis const $mask_name: u64 = get_xer_bit_mask($powerpc_bit_num); )+ + $struct_vis const XER_MASK: u64 = $(Self::$mask_name)|+; pub const fn from_xer(xer: u64) -> Self { Self { $( @@ -194,11 +187,13 @@ pub enum InstructionInputRegister { Rc, #[serde(rename = "carry")] Carry, + #[serde(rename = "overflow")] + Overflow, } forward_display_to_serde!(InstructionInputRegister); -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)] pub struct InstructionInput { #[serde( default, @@ -220,6 +215,8 @@ pub struct InstructionInput { pub rc: Option, #[serde(default, skip_serializing_if = "Option::is_none", flatten)] pub carry: Option, + #[serde(default, skip_serializing_if = "Option::is_none", flatten)] + pub overflow: Option, } macro_rules! impl_instr_try_get { @@ -253,6 +250,9 @@ impl_instr_try_get! { pub fn try_get_carry -> CarryFlags { .carry else Carry } + pub fn try_get_overflow -> OverflowFlags { + .overflow else Overflow + } } fn is_false(v: &bool) -> bool { @@ -278,238 +278,6 @@ pub struct WholeTest { pub any_model_mismatch: bool, } -#[cfg(feature = "native_instrs")] -macro_rules! map_instr_asm_args { - ([], [], []) => { - "" - }; - ([], [], [$string0:literal $($strings:literal)*]) => { - concat!(" ", $string0, $(", ", $strings),*) - }; - ([$($args:ident)*], [rt $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$0"]) - }; - ([ra $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$3"]) - }; - ([rb $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$4"]) - }; - ([rc $($args:ident)*], [$($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)* "$5"]) - }; - ([$($args:ident)*], [ov $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr0 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr1 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr2 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr3 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr4 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr5 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr6 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; - ([$($args:ident)*], [cr7 $($results:ident)*], [$($strings:literal)*]) => { - map_instr_asm_args!([$($args)*], [$($results)*], [$($strings)*]) - }; -} - -macro_rules! map_instr_input_registers { - ([], [$($reg:expr,)*]) => { - [$($reg,)*] - }; - ([ra $($args:ident)*], [$($reg:expr,)*]) => { - map_instr_input_registers!([$($args)*], [InstructionInputRegister::Ra, $($reg,)*]) - }; - ([rb $($args:ident)*], [$($reg:expr,)*]) => { - map_instr_input_registers!([$($args)*], [InstructionInputRegister::Rb, $($reg,)*]) - }; - ([rc $($args:ident)*], [$($reg:expr,)*]) => { - map_instr_input_registers!([$($args)*], [InstructionInputRegister::Rc, $($reg,)*]) - }; -} - -#[cfg(feature = "native_instrs")] -macro_rules! map_instr_results { - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, []) => {}; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [rt $($args:ident)*]) => { - $retval.rt = Some($rt); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [ov $($args:ident)*]) => { - $retval.overflow = Some(OverflowFlags::from_xer($xer)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr0 $($args:ident)*]) => { - $retval.cr0 = Some(ConditionRegister::from_cr_field($cr, 0)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr1 $($args:ident)*]) => { - $retval.cr1 = Some(ConditionRegister::from_cr_field($cr, 1)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr2 $($args:ident)*]) => { - $retval.cr2 = Some(ConditionRegister::from_cr_field($cr, 2)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr3 $($args:ident)*]) => { - $retval.cr3 = Some(ConditionRegister::from_cr_field($cr, 3)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr4 $($args:ident)*]) => { - $retval.cr4 = Some(ConditionRegister::from_cr_field($cr, 4)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr5 $($args:ident)*]) => { - $retval.cr5 = Some(ConditionRegister::from_cr_field($cr, 5)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr6 $($args:ident)*]) => { - $retval.cr6 = Some(ConditionRegister::from_cr_field($cr, 6)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; - ($rt:ident, $xer:ident, $cr:ident, $retval:ident, [cr7 $($args:ident)*]) => { - $retval.cr7 = Some(ConditionRegister::from_cr_field($cr, 7)); - map_instr_results!($rt, $xer, $cr, $retval, [$($args)*]); - }; -} - -#[cfg(feature = "native_instrs")] -macro_rules! instr { - ( - #[enumerant = $enumerant:ident] - fn $fn:ident($($args:ident),*) -> ($($results:ident),*) { - $instr:literal - } - ) => { - pub fn $fn(inputs: InstructionInput) -> InstructionResult { - #![allow(unused_variables, unused_assignments)] - let InstructionInput { - ra, - rb, - rc, - carry, - } = inputs; - let rt: u64; - let xer: u64; - let cr: u32; - unsafe { - llvm_asm!( - concat!( - "mfxer $1\n", - "and $1, $1, $7\n", - "mtxer $1\n", - $instr, " ", - map_instr_asm_args!([$($args)*], [$($results)*], []), - "\n", - "mfxer $1\n", - "mfcr $2\n", - ) - : "=&b"(rt), "=&b"(xer), "=&b"(cr) - : "b"(ra), "b"(rb), "b"(rc), "b"(0u64), "b"(!0x8000_0000u64) - : "xer", "cr"); - } - let mut retval = InstructionOutput::default(); - map_instr_results!(rt, xer, cr, retval, [$($results)*]); - retval - } - }; -} - -macro_rules! instrs { - ( - $( - #[enumerant = $enumerant:ident] - fn $fn:ident($($args:ident),*) -> ($($results:ident),*) { - $instr:literal - } - )+ - ) => { - #[cfg(feature = "python")] - macro_rules! wrap_all_instr_fns { - ($m:ident) => { - wrap_instr_fns! { - #![pymodule($m)] - - $(fn $fn(inputs: InstructionInput) -> InstructionOutput;)* - } - }; - } - - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] - pub enum Instr { - $( - #[serde(rename = $instr)] - $enumerant, - )+ - } - - impl Instr { - #[cfg(feature = "native_instrs")] - pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionOutput { - match self { - $( - Self::$enumerant => native_instrs::$fn, - )+ - } - } - pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionOutput { - match self { - $( - Self::$enumerant => instr_models::$fn, - )+ - } - } - pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] { - match self { - $( - Self::$enumerant => &map_instr_input_registers!([$($args)*], []), - )+ - } - } - pub fn name(self) -> &'static str { - match self { - $( - Self::$enumerant => $instr, - )+ - } - } - pub const VALUES: &'static [Self] = &[ - $( - Self::$enumerant, - )+ - ]; - } - - #[cfg(feature = "native_instrs")] - pub mod native_instrs { - use super::*; - - $( - instr! { - #[enumerant = $enumerant] - fn $fn($($args),*) -> ($($results),*) { - $instr - } - } - )+ - } - }; -} - instructions! { // add #[enumerant = Add] @@ -517,15 +285,15 @@ instructions! { "add" } #[enumerant = AddO] - fn addo(Ra, Rb) -> (Rt, Overflow) { + fn addo(Ra, Rb, Overflow) -> (Rt, Overflow) { "addo" } #[enumerant = Add_] - fn add_(Ra, Rb) -> (Rt, CR0) { + fn add_(Ra, Rb, Overflow) -> (Rt, CR0) { "add." } #[enumerant = AddO_] - fn addo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn addo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "addo." } @@ -535,15 +303,15 @@ instructions! { "subf" } #[enumerant = SubFO] - fn subfo(Ra, Rb) -> (Rt, Overflow) { + fn subfo(Ra, Rb, Overflow) -> (Rt, Overflow) { "subfo" } #[enumerant = SubF_] - fn subf_(Ra, Rb) -> (Rt, CR0) { + fn subf_(Ra, Rb, Overflow) -> (Rt, CR0) { "subf." } #[enumerant = SubFO_] - fn subfo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn subfo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "subfo." } @@ -553,15 +321,15 @@ instructions! { "divde" } #[enumerant = DivDEO] - fn divdeo(Ra, Rb) -> (Rt, Overflow) { + fn divdeo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divdeo" } #[enumerant = DivDE_] - fn divde_(Ra, Rb) -> (Rt, CR0) { + fn divde_(Ra, Rb, Overflow) -> (Rt, CR0) { "divde." } #[enumerant = DivDEO_] - fn divdeo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divdeo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divdeo." } @@ -571,15 +339,15 @@ instructions! { "divdeu" } #[enumerant = DivDEUO] - fn divdeuo(Ra, Rb) -> (Rt, Overflow) { + fn divdeuo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divdeuo" } #[enumerant = DivDEU_] - fn divdeu_(Ra, Rb) -> (Rt, CR0) { + fn divdeu_(Ra, Rb, Overflow) -> (Rt, CR0) { "divdeu." } #[enumerant = DivDEUO_] - fn divdeuo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divdeuo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divdeuo." } @@ -589,15 +357,15 @@ instructions! { "divd" } #[enumerant = DivDO] - fn divdo(Ra, Rb) -> (Rt, Overflow) { + fn divdo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divdo" } #[enumerant = DivD_] - fn divd_(Ra, Rb) -> (Rt, CR0) { + fn divd_(Ra, Rb, Overflow) -> (Rt, CR0) { "divd." } #[enumerant = DivDO_] - fn divdo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divdo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divdo." } @@ -607,15 +375,15 @@ instructions! { "divdu" } #[enumerant = DivDUO] - fn divduo(Ra, Rb) -> (Rt, Overflow) { + fn divduo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divduo" } #[enumerant = DivDU_] - fn divdu_(Ra, Rb) -> (Rt, CR0) { + fn divdu_(Ra, Rb, Overflow) -> (Rt, CR0) { "divdu." } #[enumerant = DivDUO_] - fn divduo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divduo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divduo." } @@ -625,15 +393,15 @@ instructions! { "divwe" } #[enumerant = DivWEO] - fn divweo(Ra, Rb) -> (Rt, Overflow) { + fn divweo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divweo" } #[enumerant = DivWE_] - fn divwe_(Ra, Rb) -> (Rt, CR0) { + fn divwe_(Ra, Rb, Overflow) -> (Rt, CR0) { "divwe." } #[enumerant = DivWEO_] - fn divweo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divweo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divweo." } @@ -643,15 +411,15 @@ instructions! { "divweu" } #[enumerant = DivWEUO] - fn divweuo(Ra, Rb) -> (Rt, Overflow) { + fn divweuo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divweuo" } #[enumerant = DivWEU_] - fn divweu_(Ra, Rb) -> (Rt, CR0) { + fn divweu_(Ra, Rb, Overflow) -> (Rt, CR0) { "divweu." } #[enumerant = DivWEUO_] - fn divweuo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divweuo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divweuo." } @@ -661,15 +429,15 @@ instructions! { "divw" } #[enumerant = DivWO] - fn divwo(Ra, Rb) -> (Rt, Overflow) { + fn divwo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divwo" } #[enumerant = DivW_] - fn divw_(Ra, Rb) -> (Rt, CR0) { + fn divw_(Ra, Rb, Overflow) -> (Rt, CR0) { "divw." } #[enumerant = DivWO_] - fn divwo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divwo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divwo." } @@ -679,15 +447,15 @@ instructions! { "divwu" } #[enumerant = DivWUO] - fn divwuo(Ra, Rb) -> (Rt, Overflow) { + fn divwuo(Ra, Rb, Overflow) -> (Rt, Overflow) { "divwuo" } #[enumerant = DivWU_] - fn divwu_(Ra, Rb) -> (Rt, CR0) { + fn divwu_(Ra, Rb, Overflow) -> (Rt, CR0) { "divwu." } #[enumerant = DivWUO_] - fn divwuo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn divwuo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "divwuo." } @@ -715,15 +483,15 @@ instructions! { "mullw" } #[enumerant = MulLWO] - fn mullwo(Ra, Rb) -> (Rt, Overflow) { + fn mullwo(Ra, Rb, Overflow) -> (Rt, Overflow) { "mullwo" } #[enumerant = MulLW_] - fn mullw_(Ra, Rb) -> (Rt, CR0) { + fn mullw_(Ra, Rb, Overflow) -> (Rt, CR0) { "mullw." } #[enumerant = MulLWO_] - fn mullwo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn mullwo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "mullwo." } @@ -733,7 +501,7 @@ instructions! { "mulhw" } #[enumerant = MulHW_] - fn mulhw_(Ra, Rb) -> (Rt, CR0) { + fn mulhw_(Ra, Rb, Overflow) -> (Rt, CR0) { "mulhw." } @@ -743,7 +511,7 @@ instructions! { "mulhwu" } #[enumerant = MulHWU_] - fn mulhwu_(Ra, Rb) -> (Rt, CR0) { + fn mulhwu_(Ra, Rb, Overflow) -> (Rt, CR0) { "mulhwu." } @@ -753,15 +521,15 @@ instructions! { "mulld" } #[enumerant = MulLDO] - fn mulldo(Ra, Rb) -> (Rt, Overflow) { + fn mulldo(Ra, Rb, Overflow) -> (Rt, Overflow) { "mulldo" } #[enumerant = MulLD_] - fn mulld_(Ra, Rb) -> (Rt, CR0) { + fn mulld_(Ra, Rb, Overflow) -> (Rt, CR0) { "mulld." } #[enumerant = MulLDO_] - fn mulldo_(Ra, Rb) -> (Rt, Overflow, CR0) { + fn mulldo_(Ra, Rb, Overflow) -> (Rt, Overflow, CR0) { "mulldo." } @@ -771,7 +539,7 @@ instructions! { "mulhd" } #[enumerant = MulHD_] - fn mulhd_(Ra, Rb) -> (Rt, CR0) { + fn mulhd_(Ra, Rb, Overflow) -> (Rt, CR0) { "mulhd." } @@ -781,7 +549,7 @@ instructions! { "mulhdu" } #[enumerant = MulHDU_] - fn mulhdu_(Ra, Rb) -> (Rt, CR0) { + fn mulhdu_(Ra, Rb, Overflow) -> (Rt, CR0) { "mulhdu." } diff --git a/src/main.rs b/src/main.rs index 63044c8..f6fd4ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,8 @@ // See Notices.txt for copyright information use power_instruction_analyzer::{ - Instr, InstructionInput, InstructionInputRegister, TestCase, WholeTest, + CarryFlags, Instr, InstructionInput, InstructionInputRegister, MissingInstructionInput, + OverflowFlags, TestCase, WholeTest, }; const TEST_VALUES: &[u64] = &[ @@ -19,36 +20,69 @@ const TEST_VALUES: &[u64] = &[ 0x1234_5678_7FFF_FFFF, ]; +const BOOL_VALUES: &[bool] = &[false, true]; + fn call_with_inputs( mut inputs: InstructionInput, input_registers: &[InstructionInputRegister], - f: &mut impl FnMut(InstructionInput), -) { + f: &mut impl FnMut(InstructionInput) -> Result<(), MissingInstructionInput>, +) -> Result<(), MissingInstructionInput> { if let Some((&input_register, input_registers)) = input_registers.split_first() { - for &i in TEST_VALUES { - inputs[input_register] = i; - call_with_inputs(inputs, input_registers, f); + match input_register { + InstructionInputRegister::Ra => { + for &i in TEST_VALUES { + inputs.ra = Some(i); + call_with_inputs(inputs, input_registers, f)?; + } + } + InstructionInputRegister::Rb => { + for &i in TEST_VALUES { + inputs.rb = Some(i); + call_with_inputs(inputs, input_registers, f)?; + } + } + InstructionInputRegister::Rc => { + for &i in TEST_VALUES { + inputs.rc = Some(i); + call_with_inputs(inputs, input_registers, f)?; + } + } + InstructionInputRegister::Carry => { + for &ca in BOOL_VALUES { + for &ca32 in BOOL_VALUES { + inputs.carry = Some(CarryFlags { ca, ca32 }); + call_with_inputs(inputs, input_registers, f)?; + } + } + } + InstructionInputRegister::Overflow => { + for &so in BOOL_VALUES { + for &ov in BOOL_VALUES { + for &ov32 in BOOL_VALUES { + inputs.overflow = Some(OverflowFlags { so, ov, ov32 }); + call_with_inputs(inputs, input_registers, f)?; + } + } + } + } } } else { - f(inputs); + f(inputs)?; } + Ok(()) } -fn main() { +fn main() -> Result<(), String> { let mut test_cases = Vec::new(); let mut any_model_mismatch = false; for &instr in Instr::VALUES { call_with_inputs( - InstructionInput { - ra: 0, - rb: 0, - rc: 0, - }, + InstructionInput::default(), instr.get_used_input_registers(), - &mut |inputs| { - let model_outputs = instr.get_model_fn()(inputs); + &mut |inputs| -> Result<(), _> { + let model_outputs = instr.get_model_fn()(inputs)?; #[cfg(feature = "native_instrs")] - let native_outputs = Some(instr.get_native_fn()(inputs)); + let native_outputs = Some(instr.get_native_fn()(inputs)?); #[cfg(not(feature = "native_instrs"))] let native_outputs = None; let model_mismatch = match native_outputs { @@ -63,8 +97,10 @@ fn main() { model_outputs, model_mismatch, }); + Ok(()) }, - ); + ) + .map_err(|err| format!("instruction {}: {}", instr.name(), err))?; } let whole_test = WholeTest { test_cases, @@ -72,4 +108,5 @@ fn main() { }; serde_json::to_writer_pretty(std::io::stdout().lock(), &whole_test).unwrap(); println!(); + Ok(()) } diff --git a/src/python.rs b/src/python.rs index e3313eb..4370c16 100644 --- a/src/python.rs +++ b/src/python.rs @@ -6,7 +6,7 @@ use crate::{ CarryFlags, ConditionRegister, Instr, InstructionInput, InstructionOutput, OverflowFlags, }; -use pyo3::{prelude::*, wrap_pyfunction, PyObjectProtocol}; +use pyo3::{exceptions::ValueError, prelude::*, wrap_pyfunction, PyObjectProtocol}; use std::{borrow::Cow, cell::RefCell, fmt}; trait ToPythonRepr { @@ -222,8 +222,9 @@ macro_rules! wrap_instr_fns { #[pyfunction $($pyfunction_args)?] #[text_signature = "(inputs)"] $(#[$meta])* - fn $name(inputs: $inputs) -> $result { + fn $name(inputs: $inputs) -> PyResult { $crate::instr_models::$name(inputs) + .map_err(|err| ValueError::py_err(err.to_string())) } $m.add_wrapped(wrap_pyfunction!($name))?; @@ -286,8 +287,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { #[pymodule(m)] #[pyclass(name = InstructionInput)] #[wrapped(value: InstructionInput)] - #[args(ra="None", rb="None", rc="None", carry="None")] - #[text_signature = "(ra, rb, rc, carry)"] + #[args(ra="None", rb="None", rc="None", carry="None", overflow="None")] + #[text_signature = "(ra, rb, rc, carry, overflow)"] struct PyInstructionInput { #[set = set_ra] ra: Option, @@ -297,6 +298,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> { rc: Option, #[set = set_carry] carry: Option, + #[set = set_overflow] + overflow: Option, } } diff --git a/tests/test_power_instruction_analyzer.py b/tests/test_power_instruction_analyzer.py index f4da122..43c86b5 100644 --- a/tests/test_power_instruction_analyzer.py +++ b/tests/test_power_instruction_analyzer.py @@ -30,6 +30,28 @@ class TestOverflowFlags(unittest.TestCase): "OverflowFlags(so=False, ov=False, ov32=True)") +class TestCarryFlags(unittest.TestCase): + def test_text_signature(self): + self.assertEqual(pia.CarryFlags.__text_signature__, + "(ca, ca32)") + + def test_fields(self): + v = pia.CarryFlags(ca=False, ca32=True) + self.assertEqual(v.ca, False) + self.assertEqual(v.ca32, True) + v.ca = True + self.assertEqual(v.ca, True) + v.ca32 = False + self.assertEqual(v.ca32, False) + + def test_str_repr(self): + v = pia.CarryFlags(ca=False, ca32=True) + self.assertEqual(str(v), + '{"ca":false,"ca32":true}') + self.assertEqual(repr(v), + "CarryFlags(ca=False, ca32=True)") + + class TestConditionRegister(unittest.TestCase): def test_text_signature(self): self.assertEqual(pia.ConditionRegister.__text_signature__, @@ -84,15 +106,19 @@ class TestInstructionInput(unittest.TestCase): class TestInstructionOutput(unittest.TestCase): + maxDiff = 1000 + def test_text_signature(self): self.assertEqual(pia.InstructionOutput.__text_signature__, - "(rt=None, overflow=None, cr0=None, cr1=None, " - + "cr2=None, cr3=None, cr4=None, cr5=None, cr6=None, cr7=None)") + "(rt=None, overflow=None, carry=None, cr0=None, " + "cr1=None, cr2=None, cr3=None, cr4=None, cr5=None, " + "cr6=None, cr7=None)") def test_fields(self): v = pia.InstructionOutput( overflow=pia.OverflowFlags(so=False, ov=False, ov32=True)) self.assertIsNone(v.rt) + self.assertIsNone(v.carry) self.assertIsNotNone(v.overflow) self.assertEqual(v.overflow.so, False) self.assertEqual(v.overflow.ov, False) @@ -111,23 +137,30 @@ class TestInstructionOutput(unittest.TestCase): self.assertIsNone(v.overflow) v.cr2 = pia.ConditionRegister(lt=False, gt=False, eq=False, so=False) self.assertIsNotNone(v.cr2) + v.carry = pia.CarryFlags(ca=False, ca32=True) + self.assertIsNotNone(v.carry) + self.assertEqual(v.carry.ca, False) + self.assertEqual(v.carry.ca32, True) def test_str_repr(self): v = pia.InstructionOutput( overflow=pia.OverflowFlags(so=False, ov=False, ov32=True), + carry=pia.CarryFlags(ca=True, ca32=False), cr0=pia.ConditionRegister(lt=True, gt=True, eq=True, so=True), cr2=pia.ConditionRegister(lt=False, gt=False, eq=False, so=False)) self.assertEqual(str(v), - '{"so":false,"ov":false,"ov32":true,' - + '"cr0":{"lt":true,"gt":true,"eq":true,"so":true},' - + '"cr2":{"lt":false,"gt":false,"eq":false,"so":false}}') + '{"so":false,"ov":false,"ov32":true,"ca":true,' + '"ca32":false,"cr0":{"lt":true,"gt":true,"eq":true,' + '"so":true},"cr2":{"lt":false,"gt":false,"eq":false,' + '"so":false}}') self.assertEqual(repr(v), - "InstructionOutput(rt=None, " - + "overflow=OverflowFlags(so=False, ov=False, ov32=True), " - + "cr0=ConditionRegister(lt=True, gt=True, eq=True, so=True), " - + "cr1=None, " - + "cr2=ConditionRegister(lt=False, gt=False, eq=False, so=False), " - + "cr3=None, cr4=None, cr5=None, cr6=None, cr7=None)") + "InstructionOutput(rt=None, overflow=OverflowFlags(" + "so=False, ov=False, ov32=True), carry=CarryFlags(" + "ca=True, ca32=False), cr0=ConditionRegister(lt=True," + " gt=True, eq=True, so=True), cr1=None, " + "cr2=ConditionRegister(lt=False, gt=False, eq=False, " + "so=False), cr3=None, cr4=None, cr5=None, cr6=None, " + "cr7=None)") class TestDivInstrs(unittest.TestCase): @@ -141,6 +174,14 @@ class TestDivInstrs(unittest.TestCase): results = fn(v) self.assertIsInstance(results, pia.InstructionOutput) + def test_exception(self): + with self.assertRaisesRegex(ValueError, "missing instruction input"): + v = pia.InstructionInput(ra=1) + pia.mulldo_(v) + with self.assertRaisesRegex(ValueError, "missing instruction input"): + v = pia.InstructionInput(ra=1, rb=1) + pia.mulldo_(v) + if __name__ == "__main__": unittest.main() -- 2.30.2