code compiles
authorJacob Lifshay <programmerjake@gmail.com>
Thu, 3 Sep 2020 03:36:23 +0000 (20:36 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Thu, 3 Sep 2020 03:36:23 +0000 (20:36 -0700)
power-instruction-analyzer-proc-macro/Cargo.toml
power-instruction-analyzer-proc-macro/src/inline_assembly.rs
power-instruction-analyzer-proc-macro/src/instructions.rs [new file with mode: 0644]
power-instruction-analyzer-proc-macro/src/lib.rs
src/lib.rs
src/main.rs
src/python.rs
tests/test_power_instruction_analyzer.py

index 3b72c25a9203e7ceead6818dca7a891a530c8fb6..7c4df5c4ea73729ebf2fcbb9fd85361f07493046 100644 (file)
@@ -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
index a10e05078f5d50c1df20c9320e898c0741b37f0b..6378cb77c29359aafdad906e445ce032eaa70444 100644 (file)
 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<T: ToAssembly + ?Sized> ToAssembly for &'_ T {
+    fn append_to(&self, retval: &mut Assembly) {
+        (**self).append_to(retval);
+    }
+
+    fn to_assembly(&self) -> Assembly {
+        (**self).to_assembly()
+    }
+}
+
+impl<T: ToAssembly + ?Sized> ToAssembly for &'_ mut T {
+    fn append_to(&self, retval: &mut Assembly) {
+        (**self).append_to(retval);
+    }
+
+    fn to_assembly(&self) -> Assembly {
+        (**self).to_assembly()
+    }
+}
+
+impl<T: ToAssembly> ToAssembly for Box<T> {
+    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<AssemblyArgId> 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<Assembly> 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<Assembly> 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 (file)
index 0000000..e1a7988
--- /dev/null
@@ -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),*))
+    };
+    () => {
+        "<nothing>"
+    };
+}
+
+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<T = Span> {
+            $(
+                $enumerant(T),
+            )*
+        }
+
+        impl InstructionArg for $enum_name<Span> {
+            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<T> fmt::Debug for $enum_name<T> {
+            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                f.write_str(self.name())
+            }
+        }
+
+        impl<T> $enum_name<T> {
+            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<Span> {
+            fn to_tokens(&self, tokens: &mut TokenStream) {
+                tokens.append(self.clone().into_ident());
+            }
+        }
+
+        impl Parse for $enum_name<Span> {
+            fn parse(input: ParseStream) -> syn::Result<Self> {
+                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<InstructionInput, Token!(,)>,
+    outputs: Punctuated<InstructionOutput, Token!(,)>,
+    instruction_name: LitStr,
+}
+
+fn check_duplicate_free<'a, T: InstructionArg + 'a>(
+    args: impl IntoIterator<Item = &'a T>,
+) -> 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<Self> {
+        input.parse::<Token!(#)>()?;
+        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::<Token!(=)>()?;
+        let enumerant: Ident = enumerant_attr_tokens.parse()?;
+        input.parse::<Token!(fn)>()?;
+        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::<Token!(->)>()?;
+        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<Vec<TokenStream>> {
+        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<TokenStream> {
+        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::<Assembly>::new();
+        let mut after_instr_asm_lines = Vec::<Assembly>::new();
+        let mut before_asm = Vec::<TokenStream>::new();
+        let mut after_asm = Vec::<TokenStream>::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<Instruction>,
+}
+
+impl Instructions {
+    pub(crate) fn to_tokens(&self) -> syn::Result<TokenStream> {
+        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<Self> {
+        let mut instructions = Vec::new();
+        while !input.is_empty() {
+            instructions.push(input.parse()?);
+        }
+        Ok(Self { instructions })
+    }
+}
index b5eb2058f584d7ddfdf00eb5d37e4d79c3df820f..8dcce4ff30bfef29f992cdc828aa83dadb1c0d75 100644 (file)
 // 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),*))
-    };
-    () => {
-        "<nothing>"
-    };
-}
-
-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<T = Span> {
-            $(
-                $enumerant(T),
-            )*
-        }
-
-        impl<T> fmt::Debug for $enum_name<T> {
-            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                f.write_str(self.name())
-            }
-        }
-
-        impl<T> $enum_name<T> {
-            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<Span> {
-            fn to_tokens(&self, tokens: &mut TokenStream) {
-                tokens.append(self.clone().into_ident());
-            }
-        }
-
-        impl Parse for $enum_name<Span> {
-            fn parse(input: ParseStream) -> syn::Result<Self> {
-                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<AssemblyTextFragment>,
-    text_span: Option<Span>,
-    inputs: Vec<TokenStream>,
-    outputs: Vec<TokenStream>,
-    clobbers: Vec<TokenStream>,
-}
-
-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<String> 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<InstructionInput, Token!(,)>,
-    outputs: Punctuated<InstructionOutput, Token!(,)>,
-    instruction_name: LitStr,
-}
-
-impl Parse for Instruction {
-    fn parse(input: ParseStream) -> syn::Result<Self> {
-        input.parse::<Token!(#)>()?;
-        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::<Token!(=)>()?;
-        let enumerant: Ident = enumerant_attr_tokens.parse()?;
-        input.parse::<Token!(fn)>()?;
-        let fn_name: Ident = input.parse()?;
-        let inputs_tokens;
-        parenthesized!(inputs_tokens in input);
-        let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
-        input.parse::<Token!(->)>()?;
-        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<Vec<TokenStream>> {
-        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<TokenStream> {
-        let Instruction {
-            enumerant,
-            fn_name,
-            inputs,
-            outputs,
-            instruction_name,
-        } = self;
-        let mut asm = InlineAssembly::new(instruction_name.span());
-        let mut before_asm = Vec::<TokenStream>::new();
-        let mut after_asm = Vec::<TokenStream>::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<Instruction>,
-}
-
-impl Instructions {
-    fn to_tokens(&self) -> syn::Result<TokenStream> {
-        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<Self> {
-        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,
index 0121e4191f6db928bc92703027f7fa224e1766ff..f0ac587f293ff6ad601f80492184be3afca11d58 100644 (file)
@@ -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<T: Default + PartialEq>(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<u64>,
     #[serde(default, skip_serializing_if = "Option::is_none", flatten)]
     pub carry: Option<CarryFlags>,
+    #[serde(default, skip_serializing_if = "Option::is_none", flatten)]
+    pub overflow: Option<OverflowFlags>,
 }
 
 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."
     }
 
index 63044c8e9ca34ca10cbdfb5c48a05281c21d1054..f6fd4ac1e1555bfeb70a896fa000feb8ea867cb7 100644 (file)
@@ -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(())
 }
index e3313eb8ac38192ec8f7f905fe8cd6f460d25c01..4370c169a3b1bea9997ed29fa78efdbf921e0a5f 100644 (file)
@@ -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<InstructionOutput> {
                     $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<u64>,
@@ -297,6 +298,8 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
             rc: Option<u64>,
             #[set = set_carry]
             carry: Option<CarryFlags>,
+            #[set = set_overflow]
+            overflow: Option<OverflowFlags>,
         }
     }
 
index f4da1222430b23561a12183c088f8ce80c7b07b4..43c86b5828a0ed97df262f283902bdef36c81460 100644 (file)
@@ -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()