[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
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)]
}
}
+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,
}
) => {
#[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()
}
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,
}
}
}
}
+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
}
}
-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,
--- /dev/null
+// 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 })
+ }
+}
// 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,
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 {
$(
$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 {
$(
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,
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 {
pub fn try_get_carry -> CarryFlags {
.carry else Carry
}
+ pub fn try_get_overflow -> OverflowFlags {
+ .overflow else Overflow
+ }
}
fn is_false(v: &bool) -> bool {
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]
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"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."
}
"mulhw"
}
#[enumerant = MulHW_]
- fn mulhw_(Ra, Rb) -> (Rt, CR0) {
+ fn mulhw_(Ra, Rb, Overflow) -> (Rt, CR0) {
"mulhw."
}
"mulhwu"
}
#[enumerant = MulHWU_]
- fn mulhwu_(Ra, Rb) -> (Rt, CR0) {
+ fn mulhwu_(Ra, Rb, Overflow) -> (Rt, CR0) {
"mulhwu."
}
"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."
}
"mulhd"
}
#[enumerant = MulHD_]
- fn mulhd_(Ra, Rb) -> (Rt, CR0) {
+ fn mulhd_(Ra, Rb, Overflow) -> (Rt, CR0) {
"mulhd."
}
"mulhdu"
}
#[enumerant = MulHDU_]
- fn mulhdu_(Ra, Rb) -> (Rt, CR0) {
+ fn mulhdu_(Ra, Rb, Overflow) -> (Rt, CR0) {
"mulhdu."
}
// 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] = &[
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 {
model_outputs,
model_mismatch,
});
+ Ok(())
},
- );
+ )
+ .map_err(|err| format!("instruction {}: {}", instr.name(), err))?;
}
let whole_test = WholeTest {
test_cases,
};
serde_json::to_writer_pretty(std::io::stdout().lock(), &whole_test).unwrap();
println!();
+ Ok(())
}
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 {
#[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))?;
#[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>,
rc: Option<u64>,
#[set = set_carry]
carry: Option<CarryFlags>,
+ #[set = set_overflow]
+ overflow: Option<OverflowFlags>,
}
}
"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__,
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)
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):
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()