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"
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
--- /dev/null
+# 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
--- /dev/null
+// 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()
+}
-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)
}
};
}
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)
}
};
}
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()) {
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 {
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()) {
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 {
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()) {
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 {
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()) {
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 {
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()
+ })
}
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
}
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
-pub struct InstructionResult {
+pub struct InstructionOutput {
#[serde(
default,
skip_serializing_if = "Option::is_none",
}
}
+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")]
Rb,
#[serde(rename = "rc")]
Rc,
- #[serde(rename = "ca")]
+ #[serde(rename = "carry")]
Carry,
}
#[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",
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
}
#[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,
}
ra,
rb,
rc,
+ carry,
} = inputs;
let rt: u64;
let xer: u64;
: "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
}
wrap_instr_fns! {
#![pymodule($m)]
- $(fn $fn(inputs: InstructionInput) -> InstructionResult;)*
+ $(fn $fn(inputs: InstructionInput) -> InstructionOutput;)*
}
};
}
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,
};
}
-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;
#![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};
#[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]
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",
#[text_signature = "(\
rt=None, \
overflow=None, \
+ carry=None, \
cr0=None, \
cr1=None, \
cr2=None, \
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]
"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)
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))
+ '"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, "
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__":