From 6128df3da4db0b460a3750ddd7a84053096a21ce Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Sep 2020 19:15:08 -0700 Subject: [PATCH] add inline assembly module --- .../src/inline_assembly.rs | 276 ++++++++++++++++++ .../src/lib.rs | 82 +++--- 2 files changed, 325 insertions(+), 33 deletions(-) create mode 100644 power-instruction-analyzer-proc-macro/src/inline_assembly.rs diff --git a/power-instruction-analyzer-proc-macro/src/inline_assembly.rs b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs new file mode 100644 index 0000000..a10e050 --- /dev/null +++ b/power-instruction-analyzer-proc-macro/src/inline_assembly.rs @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// See Notices.txt for copyright information + +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, + sync::atomic::{AtomicU64, Ordering}, +}; +use syn::LitStr; + +pub(crate) trait ToAssembly { + fn to_assembly(&self) -> Assembly; +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) struct AssemblyArgId(u64); + +impl AssemblyArgId { + pub(crate) fn new() -> Self { + // don't start at zero to help avoid confusing id with indexes + static NEXT_ID: AtomicU64 = AtomicU64::new(1000); + AssemblyArgId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) + } +} + +macro_rules! impl_assembly_arg { + ( + $vis:vis struct $name:ident { + tokens: TokenStream, + $( + $id:ident: AssemblyArgId, + )? + } + ) => { + #[derive(Debug, Clone)] + $vis 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() + } + + fn into_token_stream(self) -> TokenStream { + self.tokens + } + + fn to_tokens(&self, tokens: &mut TokenStream) { + self.tokens.to_tokens(tokens) + } + } + + impl From for $name { + fn from(tokens: TokenStream) -> Self { + Self { + tokens, + $($id: AssemblyArgId::new(),)? + } + } + } + }; +} + +impl_assembly_arg! { + pub(crate) struct AssemblyInputArg { + tokens: TokenStream, + id: AssemblyArgId, + } +} + +impl_assembly_arg! { + pub(crate) struct AssemblyOutputArg { + tokens: TokenStream, + id: AssemblyArgId, + } +} + +impl_assembly_arg! { + pub(crate) struct AssemblyClobber { + tokens: TokenStream, + } +} + +#[derive(Debug, Clone)] +pub(crate) enum AssemblyTextFragment { + Text(String), + ArgIndex(AssemblyArgId), +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct Assembly { + text_fragments: Vec, + inputs: Vec, + outputs: Vec, + clobbers: Vec, +} + +impl From for Assembly { + fn from(text: String) -> Self { + Self { + text_fragments: vec![AssemblyTextFragment::Text(text)], + ..Self::default() + } + } +} + +impl From<&'_ str> for Assembly { + fn from(text: &str) -> Self { + String::from(text).into() + } +} + +impl Assembly { + pub(crate) fn new() -> Self { + Self::default() + } + pub(crate) fn to_text(&self) -> String { + let mut id_index_map = HashMap::new(); + for (index, id) in self + .outputs + .iter() + .map(|v| v.id) + .chain(self.inputs.iter().map(|v| v.id)) + .enumerate() + { + if let Some(old_index) = id_index_map.insert(id, index) { + panic!( + "duplicate id in inline assembly arguments: #{} and #{}\n{:#?}", + old_index, index, self + ); + } + } + let mut retval = String::new(); + for text_fragment in &self.text_fragments { + match text_fragment { + AssemblyTextFragment::Text(text) => retval += text, + AssemblyTextFragment::ArgIndex(id) => { + if let Some(index) = id_index_map.get(id) { + write!(retval, "{}", index).unwrap(); + } else { + panic!( + "unknown id in inline assembly arguments: id={:?}\n{:#?}", + id, self + ); + } + } + } + } + retval + } +} + +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 { + 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)); + } + } + } + inputs.extend_from_slice(&rhs.inputs); + outputs.extend_from_slice(&rhs.outputs); + clobbers.extend_from_slice(&rhs.clobbers); + } +} + +impl AddAssign for Assembly { + fn add_assign(&mut self, rhs: Assembly) { + *self += &rhs; + } +} + +impl Add for Assembly { + type Output = Assembly; + + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; + self + } +} + +impl Add<&'_ Assembly> for Assembly { + type Output = Assembly; + + fn add(mut self, rhs: &Assembly) -> Self::Output { + self += rhs; + self + } +} + +impl Add for &'_ Assembly { + type Output = Assembly; + + fn add(self, rhs: Assembly) -> Self::Output { + Assembly::clone(self) + rhs + } +} + +impl Add<&'_ Assembly> for &'_ Assembly { + type Output = Assembly; + + fn add(self, rhs: &Assembly) -> Self::Output { + Assembly::clone(self) + rhs + } +} + +#[derive(Debug, Clone)] +pub(crate) struct AssemblyWithTextSpan { + pub(crate) asm: Assembly, + pub(crate) text_span: Span, +} + +impl Deref for AssemblyWithTextSpan { + type Target = Assembly; + + fn deref(&self) -> &Self::Target { + &self.asm + } +} + +impl DerefMut for AssemblyWithTextSpan { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.asm + } +} + +impl ToTokens for AssemblyWithTextSpan { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + asm: + Assembly { + text_fragments: _, + inputs, + outputs, + clobbers, + }, + text_span, + } = self; + let text = LitStr::new(&self.to_text(), text_span.clone()); + let value = quote! { + llvm_asm!(#text : #(#outputs),* : #(#inputs),* : #(#clobbers),*) + }; + value.to_tokens(tokens); + } +} diff --git a/power-instruction-analyzer-proc-macro/src/lib.rs b/power-instruction-analyzer-proc-macro/src/lib.rs index 30f208a..b5eb205 100644 --- a/power-instruction-analyzer-proc-macro/src/lib.rs +++ b/power-instruction-analyzer-proc-macro/src/lib.rs @@ -12,6 +12,8 @@ use syn::{ Attribute, Error, ItemFn, LitStr, Token, }; +mod inline_assembly; + macro_rules! valid_enumerants_as_string { ($enumerant:ident) => { concat!("`", stringify!($enumerant), "`") @@ -139,16 +141,9 @@ ident_enum! { } } -#[derive(Debug, Clone)] -enum AssemblyTextFragment { - Text(String), - InputIndex(usize), - OutputIndex(usize), -} - struct InlineAssembly { text: Vec, - text_span: Span, + text_span: Option, inputs: Vec, outputs: Vec, clobbers: Vec, @@ -165,6 +160,24 @@ impl fmt::Write for InlineAssembly { } } +impl From for InlineAssembly { + fn from(s: String) -> Self { + InlineAssembly { + text: vec![AssemblyTextFragment::Text(s)], + text_span: None, + inputs: Vec::new(), + outputs: Vec::new(), + clobbers: Vec::new(), + } + } +} + +impl From<&'_ str> for InlineAssembly { + fn from(s: &'_ str) -> Self { + String::from(s).into() + } +} + impl InlineAssembly { fn new(text_span: Span) -> Self { Self { @@ -196,6 +209,16 @@ impl InlineAssembly { 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 { @@ -284,25 +307,11 @@ impl Instruction { let mut asm = InlineAssembly::new(instruction_name.span()); let mut before_asm = Vec::::new(); let mut after_asm = Vec::::new(); - 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"); - } - } - } 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) => { @@ -316,18 +325,25 @@ impl Instruction { } } } + 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)] - let InstructionInput { - ra, - rb, - rc, - carry, - } = inputs; - let rt: u64; - let xer: u64; - let cr: u32; #(#before_asm)* unsafe { #asm; -- 2.30.2