working on adding proc-macro that replaces macro_rules-based instrs! macro
authorJacob Lifshay <programmerjake@gmail.com>
Sat, 29 Aug 2020 01:15:09 +0000 (18:15 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Sat, 29 Aug 2020 01:15:09 +0000 (18:15 -0700)
Cargo.lock
Cargo.toml
power-instruction-analyzer-proc-macro/Cargo.toml [new file with mode: 0644]
power-instruction-analyzer-proc-macro/src/lib.rs [new file with mode: 0644]
src/instr_models.rs
src/lib.rs
src/python.rs
tests/test_power_instruction_analyzer.py

index 604234da7488111f60b0df45d3f414a8302e900e..964046b5c5b8256a84417fc29cf07908234ee8a5 100644 (file)
@@ -163,12 +163,22 @@ dependencies = [
 name = "power-instruction-analyzer"
 version = "0.1.0"
 dependencies = [
+ "power-instruction-analyzer-proc-macro",
  "pyo3",
  "serde",
  "serde_json",
  "serde_plain",
 ]
 
+[[package]]
+name = "power-instruction-analyzer-proc-macro"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "proc-macro-hack"
 version = "0.5.18"
index d7f17e6a7940d48d69d4d291bbd89c56520afb56..36f3a4d4e00cd85566e7e8b0a77f2f360f9f896a 100644 (file)
@@ -20,4 +20,13 @@ crate-type = ["rlib", "cdylib"]
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 serde_plain = "0.3"
-pyo3 = { version = "0.11", optional = true }
\ No newline at end of file
+pyo3 = { version = "0.11", optional = true }
+
+[dependencies.power-instruction-analyzer-proc-macro]
+path = "power-instruction-analyzer-proc-macro"
+
+[workspace]
+members = [
+    ".",
+    "power-instruction-analyzer-proc-macro",
+]
\ No newline at end of file
diff --git a/power-instruction-analyzer-proc-macro/Cargo.toml b/power-instruction-analyzer-proc-macro/Cargo.toml
new file mode 100644 (file)
index 0000000..3b72c25
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# See Notices.txt for copyright information
+[package]
+name = "power-instruction-analyzer-proc-macro"
+version = "0.1.0"
+authors = ["Jacob Lifshay <programmerjake@gmail.com>"]
+edition = "2018"
+license = "LGPL-2.1-or-later"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1.0"
+proc-macro2 = "1.0"
+syn = { version = "1.0", features = ["full", "parsing"] }
\ No newline at end of file
diff --git a/power-instruction-analyzer-proc-macro/src/lib.rs b/power-instruction-analyzer-proc-macro/src/lib.rs
new file mode 100644 (file)
index 0000000..deb5dd9
--- /dev/null
@@ -0,0 +1,362 @@
+// 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::fmt;
+use syn::{
+    braced, bracketed, parenthesized,
+    parse::{Parse, ParseStream},
+    parse_macro_input,
+    punctuated::Punctuated,
+    Attribute, Error, ItemFn, LitStr, Token,
+};
+
+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,
+    }
+}
+
+#[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>> {
+        todo!()
+    }
+    fn to_assembly_text(&self) -> syn::Result<String> {
+        let mut retval = String::new();
+        retval += "mfxer $1\n\
+                   and $1, $1, $7\n\
+                   mtxer $1\n";
+        todo!("map_instr_asm_args!([$($args)*], [$($results)*], []),");
+        retval += "\n\
+                   mfxer $1\n\
+                   mfcr $2\n";
+        Ok(retval)
+    }
+    fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
+        let Instruction {
+            enumerant,
+            fn_name,
+            inputs,
+            outputs,
+            instruction_name,
+        } = self;
+        let assembly_text = self.to_assembly_text()?;
+        let mut handle_inputs = Vec::<TokenStream>::new();
+        unimplemented!("fill handle_inputs");
+        let mut handle_outputs = Vec::<TokenStream>::new();
+        unimplemented!(
+            "fill handle_outputs\
+                        map_instr_results!(rt, xer, cr, retval, [$($results)*]);"
+        );
+        Ok(quote! {
+            pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
+                #![allow(unused_variables, unused_assignments)]
+                let InstructionInput {
+                    ra,
+                    rb,
+                    rc,
+                    carry,
+                } = inputs;
+                let rt: u64;
+                let xer: u64;
+                let cr: u32;
+                #(#handle_inputs)*
+                unsafe {
+                    llvm_asm!(
+                        #assembly_text
+                        : "=&b"(rt), "=&b"(xer), "=&b"(cr)
+                        : "b"(ra), "b"(rb), "b"(rc), "b"(0u64), "b"(!0x8000_0000u64)
+                        : "xer", "cr");
+                }
+                let mut retval = InstructionOutput::default();
+                #(#handle_outputs)*
+                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 })
+    }
+}
+
+#[proc_macro]
+pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+    let input = parse_macro_input!(input as Instructions);
+    match input.to_tokens() {
+        Ok(retval) => retval,
+        Err(err) => err.to_compile_error(),
+    }
+    .into()
+}
index 59ff2bb81c0e2b2316dd7c7a559811dc2bfce127..61fd42ae8b1a386ed30f723ee389c63a3f026fbb 100644 (file)
@@ -1,27 +1,29 @@
-use crate::{ConditionRegister, InstructionInput, InstructionResult, OverflowFlags};
+use crate::{
+    ConditionRegister, InstructionInput, InstructionOutput, InstructionResult, OverflowFlags,
+};
 
 macro_rules! create_instr_variants_ov_cr {
     ($fn:ident, $fno:ident, $fn_:ident, $fno_:ident, $iwidth:ident) => {
         pub fn $fn(inputs: InstructionInput) -> InstructionResult {
-            InstructionResult {
+            Ok(InstructionOutput {
                 overflow: None,
-                ..$fno(inputs)
-            }
+                ..$fno(inputs)?
+            })
         }
         pub fn $fn_(inputs: InstructionInput) -> InstructionResult {
-            let mut retval = $fno_(inputs);
+            let mut retval = $fno_(inputs)?;
             let mut cr0 = retval.cr0.as_mut().expect("expected cr0 to be set");
             cr0.so = false;
             retval.overflow = None;
-            retval
+            Ok(retval)
         }
         pub fn $fno_(inputs: InstructionInput) -> InstructionResult {
-            let mut retval = $fno(inputs);
+            let mut retval = $fno(inputs)?;
             let result = retval.rt.expect("expected rt to be set");
             let so = retval.overflow.expect("expected overflow to be set").so;
             let cr0 = ConditionRegister::from_signed_int(result as $iwidth, so);
             retval.cr0 = Some(cr0);
-            retval
+            Ok(retval)
         }
     };
 }
@@ -29,11 +31,11 @@ macro_rules! create_instr_variants_ov_cr {
 macro_rules! create_instr_variants_cr {
     ($fn:ident, $fn_:ident, $iwidth:ident) => {
         pub fn $fn_(inputs: InstructionInput) -> InstructionResult {
-            let mut retval = $fn(inputs);
+            let mut retval = $fn(inputs)?;
             let result = retval.rt.expect("expected rt to be set");
             let cr0 = ConditionRegister::from_signed_int(result as $iwidth, false);
             retval.cr0 = Some(cr0);
-            retval
+            Ok(retval)
         }
     };
 }
@@ -41,38 +43,38 @@ macro_rules! create_instr_variants_cr {
 create_instr_variants_ov_cr!(add, addo, add_, addo_, i64);
 
 pub fn addo(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64;
-    let rb = inputs.rb as i64;
+    let ra = inputs.try_get_ra()? as i64;
+    let rb = inputs.try_get_rb()? as i64;
     let (result, ov) = ra.overflowing_add(rb);
     let result = result as u64;
     let ov32 = (ra as i32).overflowing_add(rb as i32).1;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags { so: ov, ov, ov32 }),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(subf, subfo, subf_, subfo_, i64);
 
 pub fn subfo(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64;
-    let rb = inputs.rb as i64;
+    let ra = inputs.try_get_ra()? as i64;
+    let rb = inputs.try_get_rb()? as i64;
     let (result, ov) = rb.overflowing_sub(ra);
     let result = result as u64;
     let ov32 = (rb as i32).overflowing_sub(ra as i32).1;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags { so: ov, ov, ov32 }),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(divde, divdeo, divde_, divdeo_, i64);
 
 pub fn divdeo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = i128::from(inputs.ra as i64) << 64;
-    let divisor = i128::from(inputs.rb as i64);
+    let dividend = i128::from(inputs.try_get_ra()? as i64) << 64;
+    let divisor = i128::from(inputs.try_get_rb()? as i64);
     let overflow;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i128::min_value()) {
@@ -88,18 +90,18 @@ pub fn divdeo(inputs: InstructionInput) -> InstructionResult {
             overflow = false;
         }
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(divdeu, divdeuo, divdeu_, divdeuo_, i64);
 
 pub fn divdeuo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = u128::from(inputs.ra) << 64;
-    let divisor = u128::from(inputs.rb);
+    let dividend = u128::from(inputs.try_get_ra()?) << 64;
+    let divisor = u128::from(inputs.try_get_rb()?);
     let overflow;
     let result;
     if divisor == 0 {
@@ -115,18 +117,18 @@ pub fn divdeuo(inputs: InstructionInput) -> InstructionResult {
             overflow = false;
         }
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(divd, divdo, divd_, divdo_, i64);
 
 pub fn divdo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as i64;
-    let divisor = inputs.rb as i64;
+    let dividend = inputs.try_get_ra()? as i64;
+    let divisor = inputs.try_get_rb()? as i64;
     let overflow;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
@@ -136,18 +138,18 @@ pub fn divdo(inputs: InstructionInput) -> InstructionResult {
         result = (dividend / divisor) as u64;
         overflow = false;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(divdu, divduo, divdu_, divduo_, i64);
 
 pub fn divduo(inputs: InstructionInput) -> InstructionResult {
-    let dividend: u64 = inputs.ra;
-    let divisor: u64 = inputs.rb;
+    let dividend: u64 = inputs.try_get_ra()?;
+    let divisor: u64 = inputs.try_get_rb()?;
     let overflow;
     let result;
     if divisor == 0 {
@@ -157,19 +159,19 @@ pub fn divduo(inputs: InstructionInput) -> InstructionResult {
         result = dividend / divisor;
         overflow = false;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
 create_instr_variants_ov_cr!(divwe, divweo, divwe_, divweo_, i64);
 
 pub fn divweo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = i64::from(inputs.ra as i32) << 32;
-    let divisor = i64::from(inputs.rb as i32);
+    let dividend = i64::from(inputs.try_get_ra()? as i32) << 32;
+    let divisor = i64::from(inputs.try_get_rb()? as i32);
     let overflow;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
@@ -185,19 +187,19 @@ pub fn divweo(inputs: InstructionInput) -> InstructionResult {
             overflow = false;
         }
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
 create_instr_variants_ov_cr!(divweu, divweuo, divweu_, divweuo_, i64);
 
 pub fn divweuo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = u64::from(inputs.ra as u32) << 32;
-    let divisor = u64::from(inputs.rb as u32);
+    let dividend = u64::from(inputs.try_get_ra()? as u32) << 32;
+    let divisor = u64::from(inputs.try_get_rb()? as u32);
     let overflow;
     let result;
     if divisor == 0 {
@@ -213,19 +215,19 @@ pub fn divweuo(inputs: InstructionInput) -> InstructionResult {
             overflow = false;
         }
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
 create_instr_variants_ov_cr!(divw, divwo, divw_, divwo_, i64);
 
 pub fn divwo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as i32;
-    let divisor = inputs.rb as i32;
+    let dividend = inputs.try_get_ra()? as i32;
+    let divisor = inputs.try_get_rb()? as i32;
     let overflow;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) {
@@ -235,19 +237,19 @@ pub fn divwo(inputs: InstructionInput) -> InstructionResult {
         result = (dividend / divisor) as u32 as u64;
         overflow = false;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 // ISA doesn't define compare results -- POWER9 apparently uses i64 instead of i32
 create_instr_variants_ov_cr!(divwu, divwuo, divwu_, divwuo_, i64);
 
 pub fn divwuo(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as u32;
-    let divisor = inputs.rb as u32;
+    let dividend = inputs.try_get_ra()? as u32;
+    let divisor = inputs.try_get_rb()? as u32;
     let overflow;
     let result;
     if divisor == 0 {
@@ -257,183 +259,183 @@ pub fn divwuo(inputs: InstructionInput) -> InstructionResult {
         result = (dividend / divisor) as u64;
         overflow = false;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn modsd(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as i64;
-    let divisor = inputs.rb as i64;
+    let dividend = inputs.try_get_ra()? as i64;
+    let divisor = inputs.try_get_rb()? as i64;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i64::min_value()) {
         result = 0;
     } else {
         result = (dividend % divisor) as u64;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn modud(inputs: InstructionInput) -> InstructionResult {
-    let dividend: u64 = inputs.ra;
-    let divisor: u64 = inputs.rb;
+    let dividend: u64 = inputs.try_get_ra()?;
+    let divisor: u64 = inputs.try_get_rb()?;
     let result;
     if divisor == 0 {
         result = 0;
     } else {
         result = dividend % divisor;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn modsw(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as i32;
-    let divisor = inputs.rb as i32;
+    let dividend = inputs.try_get_ra()? as i32;
+    let divisor = inputs.try_get_rb()? as i32;
     let result;
     if divisor == 0 || (divisor == -1 && dividend == i32::min_value()) {
         result = 0;
     } else {
         result = (dividend % divisor) as u64;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn moduw(inputs: InstructionInput) -> InstructionResult {
-    let dividend = inputs.ra as u32;
-    let divisor = inputs.rb as u32;
+    let dividend = inputs.try_get_ra()? as u32;
+    let divisor = inputs.try_get_rb()? as u32;
     let result;
     if divisor == 0 {
         result = 0;
     } else {
         result = (dividend % divisor) as u64;
     }
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(mullw, mullwo, mullw_, mullwo_, i64);
 
 pub fn mullwo(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i32 as i64;
-    let rb = inputs.rb as i32 as i64;
+    let ra = inputs.try_get_ra()? as i32 as i64;
+    let rb = inputs.try_get_rb()? as i32 as i64;
     let result = ra.wrapping_mul(rb) as u64;
     let overflow = result as i32 as i64 != result as i64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_cr!(mulhw, mulhw_, i32);
 
 pub fn mulhw(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i32 as i64;
-    let rb = inputs.rb as i32 as i64;
+    let ra = inputs.try_get_ra()? as i32 as i64;
+    let rb = inputs.try_get_rb()? as i32 as i64;
     let result = (ra * rb) >> 32;
     let mut result = result as u32 as u64;
     result |= result << 32;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_cr!(mulhwu, mulhwu_, i32);
 
 pub fn mulhwu(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as u32 as u64;
-    let rb = inputs.rb as u32 as u64;
+    let ra = inputs.try_get_ra()? as u32 as u64;
+    let rb = inputs.try_get_rb()? as u32 as u64;
     let result = (ra * rb) >> 32;
     let mut result = result as u32 as u64;
     result |= result << 32;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_ov_cr!(mulld, mulldo, mulld_, mulldo_, i64);
 
 pub fn mulldo(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64;
-    let rb = inputs.rb as i64;
+    let ra = inputs.try_get_ra()? as i64;
+    let rb = inputs.try_get_rb()? as i64;
     let result = ra.wrapping_mul(rb) as u64;
     let overflow = ra.checked_mul(rb).is_none();
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
         overflow: Some(OverflowFlags::from_overflow(overflow)),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_cr!(mulhd, mulhd_, i64);
 
 pub fn mulhd(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64 as i128;
-    let rb = inputs.rb as i64 as i128;
+    let ra = inputs.try_get_ra()? as i64 as i128;
+    let rb = inputs.try_get_rb()? as i64 as i128;
     let result = ((ra * rb) >> 64) as i64;
     let result = result as u64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 create_instr_variants_cr!(mulhdu, mulhdu_, i64);
 
 pub fn mulhdu(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as u128;
-    let rb = inputs.rb as u128;
+    let ra = inputs.try_get_ra()? as u128;
+    let rb = inputs.try_get_rb()? as u128;
     let result = ((ra * rb) >> 64) as u64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn maddhd(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64 as i128;
-    let rb = inputs.rb as i64 as i128;
-    let rc = inputs.rc as i64 as i128;
+    let ra = inputs.try_get_ra()? as i64 as i128;
+    let rb = inputs.try_get_rb()? as i64 as i128;
+    let rc = inputs.try_get_rc()? as i64 as i128;
     let result = ((ra * rb + rc) >> 64) as u64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn maddhdu(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as u128;
-    let rb = inputs.rb as u128;
-    let rc = inputs.rc as u128;
+    let ra = inputs.try_get_ra()? as u128;
+    let rb = inputs.try_get_rb()? as u128;
+    let rc = inputs.try_get_rc()? as u128;
     let result = ((ra * rb + rc) >> 64) as u64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
 
 pub fn maddld(inputs: InstructionInput) -> InstructionResult {
-    let ra = inputs.ra as i64;
-    let rb = inputs.rb as i64;
-    let rc = inputs.rc as i64;
+    let ra = inputs.try_get_ra()? as i64;
+    let rb = inputs.try_get_rb()? as i64;
+    let rc = inputs.try_get_rc()? as i64;
     let result = ra.wrapping_mul(rb).wrapping_add(rc) as u64;
-    InstructionResult {
+    Ok(InstructionOutput {
         rt: Some(result),
-        ..InstructionResult::default()
-    }
+        ..InstructionOutput::default()
+    })
 }
index a67adf28726fee7a333005d5cef3b1d5291bbfc6..0121e4191f6db928bc92703027f7fa224e1766ff 100644 (file)
@@ -9,13 +9,14 @@ compile_error!("native_instrs feature requires target_arch to be powerpc64");
 pub mod instr_models;
 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},
 };
-use serde_plain::forward_display_to_serde;
 
 fn is_default<T: Default + PartialEq>(v: &T) -> bool {
     T::default() == *v
@@ -139,7 +140,7 @@ impl ConditionRegister {
 }
 
 #[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
-pub struct InstructionResult {
+pub struct InstructionOutput {
     #[serde(
         default,
         skip_serializing_if = "Option::is_none",
@@ -179,6 +180,10 @@ impl fmt::Display for MissingInstructionInput {
     }
 }
 
+impl std::error::Error for MissingInstructionInput {}
+
+pub type InstructionResult = Result<InstructionOutput, MissingInstructionInput>;
+
 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
 pub enum InstructionInputRegister {
     #[serde(rename = "ra")]
@@ -187,7 +192,7 @@ pub enum InstructionInputRegister {
     Rb,
     #[serde(rename = "rc")]
     Rc,
-    #[serde(rename = "ca")]
+    #[serde(rename = "carry")]
     Carry,
 }
 
@@ -195,8 +200,12 @@ forward_display_to_serde!(InstructionInputRegister);
 
 #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
 pub struct InstructionInput {
-    #[serde(with = "serde_hex::SerdeHex")]
-    pub ra: u64,
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        with = "serde_hex::SerdeHex"
+    )]
+    pub ra: Option<u64>,
     #[serde(
         default,
         skip_serializing_if = "Option::is_none",
@@ -213,6 +222,39 @@ pub struct InstructionInput {
     pub carry: Option<CarryFlags>,
 }
 
+macro_rules! impl_instr_try_get {
+    (
+        $(
+            $vis:vis fn $fn:ident -> $return_type:ty { .$field:ident else $error_enum:ident }
+        )+
+    ) => {
+        impl InstructionInput {
+            $(
+                $vis fn $fn(self) -> Result<$return_type, MissingInstructionInput> {
+                    self.$field.ok_or(MissingInstructionInput {
+                        input: InstructionInputRegister::$error_enum,
+                    })
+                }
+            )+
+        }
+    };
+}
+
+impl_instr_try_get! {
+    pub fn try_get_ra -> u64 {
+        .ra else Ra
+    }
+    pub fn try_get_rb -> u64 {
+        .rb else Rb
+    }
+    pub fn try_get_rc -> u64 {
+        .rc else Rc
+    }
+    pub fn try_get_carry -> CarryFlags {
+        .carry else Carry
+    }
+}
+
 fn is_false(v: &bool) -> bool {
     !v
 }
@@ -223,8 +265,8 @@ pub struct TestCase {
     #[serde(flatten)]
     pub inputs: InstructionInput,
     #[serde(default, skip_serializing_if = "Option::is_none")]
-    pub native_outputs: Option<InstructionResult>,
-    pub model_outputs: InstructionResult,
+    pub native_outputs: Option<InstructionOutput>,
+    pub model_outputs: InstructionOutput,
     #[serde(default, skip_serializing_if = "is_false")]
     pub model_mismatch: bool,
 }
@@ -359,6 +401,7 @@ macro_rules! instr {
                 ra,
                 rb,
                 rc,
+                carry,
             } = inputs;
             let rt: u64;
             let xer: u64;
@@ -379,7 +422,7 @@ macro_rules! instr {
                     : "b"(ra), "b"(rb), "b"(rc), "b"(0u64), "b"(!0x8000_0000u64)
                     : "xer", "cr");
             }
-            let mut retval = InstructionResult::default();
+            let mut retval = InstructionOutput::default();
             map_instr_results!(rt, xer, cr, retval, [$($results)*]);
             retval
         }
@@ -401,7 +444,7 @@ macro_rules! instrs {
                 wrap_instr_fns! {
                     #![pymodule($m)]
 
-                    $(fn $fn(inputs: InstructionInput) -> InstructionResult;)*
+                    $(fn $fn(inputs: InstructionInput) -> InstructionOutput;)*
                 }
             };
         }
@@ -416,14 +459,14 @@ macro_rules! instrs {
 
         impl Instr {
             #[cfg(feature = "native_instrs")]
-            pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult {
+            pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionOutput {
                 match self {
                     $(
                         Self::$enumerant => native_instrs::$fn,
                     )+
                 }
             }
-            pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult {
+            pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionOutput {
                 match self {
                     $(
                         Self::$enumerant => instr_models::$fn,
@@ -467,299 +510,295 @@ macro_rules! instrs {
     };
 }
 
-instrs! {
+instructions! {
     // add
     #[enumerant = Add]
-    fn add(ra, rb) -> (rt) {
+    fn add(Ra, Rb) -> (Rt) {
         "add"
     }
     #[enumerant = AddO]
-    fn addo(ra, rb) -> (rt, ov) {
+    fn addo(Ra, Rb) -> (Rt, Overflow) {
         "addo"
     }
     #[enumerant = Add_]
-    fn add_(ra, rb) -> (rt, cr0) {
+    fn add_(Ra, Rb) -> (Rt, CR0) {
         "add."
     }
     #[enumerant = AddO_]
-    fn addo_(ra, rb) -> (rt, ov, cr0) {
+    fn addo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "addo."
     }
 
     // subf
     #[enumerant = SubF]
-    fn subf(ra, rb) -> (rt) {
+    fn subf(Ra, Rb) -> (Rt) {
         "subf"
     }
     #[enumerant = SubFO]
-    fn subfo(ra, rb) -> (rt, ov) {
+    fn subfo(Ra, Rb) -> (Rt, Overflow) {
         "subfo"
     }
     #[enumerant = SubF_]
-    fn subf_(ra, rb) -> (rt, cr0) {
+    fn subf_(Ra, Rb) -> (Rt, CR0) {
         "subf."
     }
     #[enumerant = SubFO_]
-    fn subfo_(ra, rb) -> (rt, ov, cr0) {
+    fn subfo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "subfo."
     }
 
     // divde
     #[enumerant = DivDE]
-    fn divde(ra, rb) -> (rt) {
+    fn divde(Ra, Rb) -> (Rt) {
         "divde"
     }
     #[enumerant = DivDEO]
-    fn divdeo(ra, rb) -> (rt, ov) {
+    fn divdeo(Ra, Rb) -> (Rt, Overflow) {
         "divdeo"
     }
     #[enumerant = DivDE_]
-    fn divde_(ra, rb) -> (rt, cr0) {
+    fn divde_(Ra, Rb) -> (Rt, CR0) {
         "divde."
     }
     #[enumerant = DivDEO_]
-    fn divdeo_(ra, rb) -> (rt, ov, cr0) {
+    fn divdeo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divdeo."
     }
 
     // divdeu
     #[enumerant = DivDEU]
-    fn divdeu(ra, rb) -> (rt) {
+    fn divdeu(Ra, Rb) -> (Rt) {
         "divdeu"
     }
     #[enumerant = DivDEUO]
-    fn divdeuo(ra, rb) -> (rt, ov) {
+    fn divdeuo(Ra, Rb) -> (Rt, Overflow) {
         "divdeuo"
     }
     #[enumerant = DivDEU_]
-    fn divdeu_(ra, rb) -> (rt, cr0) {
+    fn divdeu_(Ra, Rb) -> (Rt, CR0) {
         "divdeu."
     }
     #[enumerant = DivDEUO_]
-    fn divdeuo_(ra, rb) -> (rt, ov, cr0) {
+    fn divdeuo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divdeuo."
     }
 
     // divd
     #[enumerant = DivD]
-    fn divd(ra, rb) -> (rt) {
+    fn divd(Ra, Rb) -> (Rt) {
         "divd"
     }
     #[enumerant = DivDO]
-    fn divdo(ra, rb) -> (rt, ov) {
+    fn divdo(Ra, Rb) -> (Rt, Overflow) {
         "divdo"
     }
     #[enumerant = DivD_]
-    fn divd_(ra, rb) -> (rt, cr0) {
+    fn divd_(Ra, Rb) -> (Rt, CR0) {
         "divd."
     }
     #[enumerant = DivDO_]
-    fn divdo_(ra, rb) -> (rt, ov, cr0) {
+    fn divdo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divdo."
     }
 
     // divdu
     #[enumerant = DivDU]
-    fn divdu(ra, rb) -> (rt) {
+    fn divdu(Ra, Rb) -> (Rt) {
         "divdu"
     }
     #[enumerant = DivDUO]
-    fn divduo(ra, rb) -> (rt, ov) {
+    fn divduo(Ra, Rb) -> (Rt, Overflow) {
         "divduo"
     }
     #[enumerant = DivDU_]
-    fn divdu_(ra, rb) -> (rt, cr0) {
+    fn divdu_(Ra, Rb) -> (Rt, CR0) {
         "divdu."
     }
     #[enumerant = DivDUO_]
-    fn divduo_(ra, rb) -> (rt, ov, cr0) {
+    fn divduo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divduo."
     }
 
     // divwe
     #[enumerant = DivWE]
-    fn divwe(ra, rb) -> (rt) {
+    fn divwe(Ra, Rb) -> (Rt) {
         "divwe"
     }
     #[enumerant = DivWEO]
-    fn divweo(ra, rb) -> (rt, ov) {
+    fn divweo(Ra, Rb) -> (Rt, Overflow) {
         "divweo"
     }
     #[enumerant = DivWE_]
-    fn divwe_(ra, rb) -> (rt, cr0) {
+    fn divwe_(Ra, Rb) -> (Rt, CR0) {
         "divwe."
     }
     #[enumerant = DivWEO_]
-    fn divweo_(ra, rb) -> (rt, ov, cr0) {
+    fn divweo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divweo."
     }
 
     // divweu
     #[enumerant = DivWEU]
-    fn divweu(ra, rb) -> (rt) {
+    fn divweu(Ra, Rb) -> (Rt) {
         "divweu"
     }
     #[enumerant = DivWEUO]
-    fn divweuo(ra, rb) -> (rt, ov) {
+    fn divweuo(Ra, Rb) -> (Rt, Overflow) {
         "divweuo"
     }
     #[enumerant = DivWEU_]
-    fn divweu_(ra, rb) -> (rt, cr0) {
+    fn divweu_(Ra, Rb) -> (Rt, CR0) {
         "divweu."
     }
     #[enumerant = DivWEUO_]
-    fn divweuo_(ra, rb) -> (rt, ov, cr0) {
+    fn divweuo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divweuo."
     }
 
     // divw
     #[enumerant = DivW]
-    fn divw(ra, rb) -> (rt) {
+    fn divw(Ra, Rb) -> (Rt) {
         "divw"
     }
     #[enumerant = DivWO]
-    fn divwo(ra, rb) -> (rt, ov) {
+    fn divwo(Ra, Rb) -> (Rt, Overflow) {
         "divwo"
     }
     #[enumerant = DivW_]
-    fn divw_(ra, rb) -> (rt, cr0) {
+    fn divw_(Ra, Rb) -> (Rt, CR0) {
         "divw."
     }
     #[enumerant = DivWO_]
-    fn divwo_(ra, rb) -> (rt, ov, cr0) {
+    fn divwo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divwo."
     }
 
     // divwu
     #[enumerant = DivWU]
-    fn divwu(ra, rb) -> (rt) {
+    fn divwu(Ra, Rb) -> (Rt) {
         "divwu"
     }
     #[enumerant = DivWUO]
-    fn divwuo(ra, rb) -> (rt, ov) {
+    fn divwuo(Ra, Rb) -> (Rt, Overflow) {
         "divwuo"
     }
     #[enumerant = DivWU_]
-    fn divwu_(ra, rb) -> (rt, cr0) {
+    fn divwu_(Ra, Rb) -> (Rt, CR0) {
         "divwu."
     }
     #[enumerant = DivWUO_]
-    fn divwuo_(ra, rb) -> (rt, ov, cr0) {
+    fn divwuo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "divwuo."
     }
 
     // mod*
     #[enumerant = ModSD]
-    fn modsd(ra, rb) -> (rt) {
+    fn modsd(Ra, Rb) -> (Rt) {
         "modsd"
     }
     #[enumerant = ModUD]
-    fn modud(ra, rb) -> (rt) {
+    fn modud(Ra, Rb) -> (Rt) {
         "modud"
     }
     #[enumerant = ModSW]
-    fn modsw(ra, rb) -> (rt) {
+    fn modsw(Ra, Rb) -> (Rt) {
         "modsw"
     }
     #[enumerant = ModUW]
-    fn moduw(ra, rb) -> (rt) {
+    fn moduw(Ra, Rb) -> (Rt) {
         "moduw"
     }
 
     // mullw
     #[enumerant = MulLW]
-    fn mullw(ra, rb) -> (rt) {
+    fn mullw(Ra, Rb) -> (Rt) {
         "mullw"
     }
     #[enumerant = MulLWO]
-    fn mullwo(ra, rb) -> (rt, ov) {
+    fn mullwo(Ra, Rb) -> (Rt, Overflow) {
         "mullwo"
     }
     #[enumerant = MulLW_]
-    fn mullw_(ra, rb) -> (rt, cr0) {
+    fn mullw_(Ra, Rb) -> (Rt, CR0) {
         "mullw."
     }
     #[enumerant = MulLWO_]
-    fn mullwo_(ra, rb) -> (rt, ov, cr0) {
+    fn mullwo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "mullwo."
     }
 
     // mulhw
     #[enumerant = MulHW]
-    fn mulhw(ra, rb) -> (rt) {
+    fn mulhw(Ra, Rb) -> (Rt) {
         "mulhw"
     }
     #[enumerant = MulHW_]
-    fn mulhw_(ra, rb) -> (rt, cr0) {
+    fn mulhw_(Ra, Rb) -> (Rt, CR0) {
         "mulhw."
     }
 
     // mulhwu
     #[enumerant = MulHWU]
-    fn mulhwu(ra, rb) -> (rt) {
+    fn mulhwu(Ra, Rb) -> (Rt) {
         "mulhwu"
     }
     #[enumerant = MulHWU_]
-    fn mulhwu_(ra, rb) -> (rt, cr0) {
+    fn mulhwu_(Ra, Rb) -> (Rt, CR0) {
         "mulhwu."
     }
 
     // mulld
     #[enumerant = MulLD]
-    fn mulld(ra, rb) -> (rt) {
+    fn mulld(Ra, Rb) -> (Rt) {
         "mulld"
     }
     #[enumerant = MulLDO]
-    fn mulldo(ra, rb) -> (rt, ov) {
+    fn mulldo(Ra, Rb) -> (Rt, Overflow) {
         "mulldo"
     }
     #[enumerant = MulLD_]
-    fn mulld_(ra, rb) -> (rt, cr0) {
+    fn mulld_(Ra, Rb) -> (Rt, CR0) {
         "mulld."
     }
     #[enumerant = MulLDO_]
-    fn mulldo_(ra, rb) -> (rt, ov, cr0) {
+    fn mulldo_(Ra, Rb) -> (Rt, Overflow, CR0) {
         "mulldo."
     }
 
     // mulhd
     #[enumerant = MulHD]
-    fn mulhd(ra, rb) -> (rt) {
+    fn mulhd(Ra, Rb) -> (Rt) {
         "mulhd"
     }
     #[enumerant = MulHD_]
-    fn mulhd_(ra, rb) -> (rt, cr0) {
+    fn mulhd_(Ra, Rb) -> (Rt, CR0) {
         "mulhd."
     }
 
     // mulhdu
     #[enumerant = MulHDU]
-    fn mulhdu(ra, rb) -> (rt) {
+    fn mulhdu(Ra, Rb) -> (Rt) {
         "mulhdu"
     }
     #[enumerant = MulHDU_]
-    fn mulhdu_(ra, rb) -> (rt, cr0) {
+    fn mulhdu_(Ra, Rb) -> (Rt, CR0) {
         "mulhdu."
     }
 
     // madd*
     #[enumerant = MAddHD]
-    fn maddhd(ra, rb, rc) -> (rt) {
+    fn maddhd(Ra, Rb, Rc) -> (Rt) {
         "maddhd"
     }
     #[enumerant = MAddHDU]
-    fn maddhdu(ra, rb, rc) -> (rt) {
+    fn maddhdu(Ra, Rb, Rc) -> (Rt) {
         "maddhdu"
     }
     #[enumerant = MAddLD]
-    fn maddld(ra, rb, rc) -> (rt) {
+    fn maddld(Ra, Rb, Rc) -> (Rt) {
         "maddld"
     }
 }
 
 // must be after instrs macro call since it uses a macro definition
 mod python;
-}
-
-// must be after instrs macro call since it uses a macro definition
-mod python;
index 40153deebec42b0ecaa07ab98f7a63eb6305e9c9..e3313eb8ac38192ec8f7f905fe8cd6f460d25c01 100644 (file)
@@ -4,7 +4,7 @@
 #![cfg(feature = "python")]
 
 use crate::{
-    CarryFlags, ConditionRegister, Instr, InstructionInput, InstructionResult, OverflowFlags,
+    CarryFlags, ConditionRegister, Instr, InstructionInput, InstructionOutput, OverflowFlags,
 };
 use pyo3::{prelude::*, wrap_pyfunction, PyObjectProtocol};
 use std::{borrow::Cow, cell::RefCell, fmt};
@@ -286,11 +286,11 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
         #[pymodule(m)]
         #[pyclass(name = InstructionInput)]
         #[wrapped(value: InstructionInput)]
-        #[args(ra, rb="None", rc="None", carry="None")]
+        #[args(ra="None", rb="None", rc="None", carry="None")]
         #[text_signature = "(ra, rb, rc, carry)"]
         struct PyInstructionInput {
             #[set = set_ra]
-            ra: u64,
+            ra: Option<u64>,
             #[set = set_rb]
             rb: Option<u64>,
             #[set = set_rc]
@@ -302,11 +302,12 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
 
     wrap_type! {
         #[pymodule(m)]
-        #[pyclass(name = InstructionResult)]
-        #[wrapped(value: InstructionResult)]
+        #[pyclass(name = InstructionOutput)]
+        #[wrapped(value: InstructionOutput)]
         #[args(
             rt="None",
             overflow="None",
+            carry="None",
             cr0="None",
             cr1="None",
             cr2="None",
@@ -319,6 +320,7 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
         #[text_signature = "(\
             rt=None, \
             overflow=None, \
+            carry=None, \
             cr0=None, \
             cr1=None, \
             cr2=None, \
@@ -328,11 +330,13 @@ fn power_instruction_analyzer(_py: Python, m: &PyModule) -> PyResult<()> {
             cr6=None, \
             cr7=None)"
         ]
-        struct PyInstructionResult {
+        struct PyInstructionOutput {
             #[set = set_rt]
             rt: Option<u64>,
             #[set = set_overflow]
             overflow: Option<OverflowFlags>,
+            #[set = set_carry]
+            carry: Option<CarryFlags>,
             #[set = set_cr0]
             cr0: Option<ConditionRegister>,
             #[set = set_cr1]
index 3fb483fde60e53f1fc97796dfddb3f860722ac69..f4da1222430b23561a12183c088f8ce80c7b07b4 100644 (file)
@@ -83,14 +83,14 @@ class TestInstructionInput(unittest.TestCase):
                          "InstructionInput(ra=123, rb=456, rc=789)")
 
 
-class TestInstructionResult(unittest.TestCase):
+class TestInstructionOutput(unittest.TestCase):
     def test_text_signature(self):
-        self.assertEqual(pia.InstructionResult.__text_signature__,
+        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)")
 
     def test_fields(self):
-        v = pia.InstructionResult(
+        v = pia.InstructionOutput(
             overflow=pia.OverflowFlags(so=False, ov=False, ov32=True))
         self.assertIsNone(v.rt)
         self.assertIsNotNone(v.overflow)
@@ -113,7 +113,7 @@ class TestInstructionResult(unittest.TestCase):
         self.assertIsNotNone(v.cr2)
 
     def test_str_repr(self):
-        v = pia.InstructionResult(
+        v = pia.InstructionOutput(
             overflow=pia.OverflowFlags(so=False, ov=False, ov32=True),
             cr0=pia.ConditionRegister(lt=True, gt=True, eq=True, so=True),
             cr2=pia.ConditionRegister(lt=False, gt=False, eq=False, so=False))
@@ -122,7 +122,7 @@ class TestInstructionResult(unittest.TestCase):
                          + '"cr0":{"lt":true,"gt":true,"eq":true,"so":true},'
                          + '"cr2":{"lt":false,"gt":false,"eq":false,"so":false}}')
         self.assertEqual(repr(v),
-                         "InstructionResult(rt=None, "
+                         "InstructionOutput(rt=None, "
                          + "overflow=OverflowFlags(so=False, ov=False, ov32=True), "
                          + "cr0=ConditionRegister(lt=True, gt=True, eq=True, so=True), "
                          + "cr1=None, "
@@ -139,7 +139,7 @@ class TestDivInstrs(unittest.TestCase):
                 fn = getattr(pia, fn_name)
                 self.assertEqual(fn.__text_signature__, "(inputs)")
                 results = fn(v)
-                self.assertIsInstance(results, pia.InstructionResult)
+                self.assertIsInstance(results, pia.InstructionOutput)
 
 
 if __name__ == "__main__":