add inline assembly module
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 2 Sep 2020 02:15:08 +0000 (19:15 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 2 Sep 2020 02:15:08 +0000 (19:15 -0700)
power-instruction-analyzer-proc-macro/src/inline_assembly.rs [new file with mode: 0644]
power-instruction-analyzer-proc-macro/src/lib.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 (file)
index 0000000..a10e050
--- /dev/null
@@ -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<TokenStream> 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<AssemblyTextFragment>,
+    inputs: Vec<AssemblyInputArg>,
+    outputs: Vec<AssemblyOutputArg>,
+    clobbers: Vec<AssemblyClobber>,
+}
+
+impl From<String> 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<Assembly> 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<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,
+    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);
+    }
+}
index 30f208a5119710784de5ec170d649a9b4f1f2f78..b5eb2058f584d7ddfdf00eb5d37e4d79c3df820f 100644 (file)
@@ -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<AssemblyTextFragment>,
-    text_span: Span,
+    text_span: Option<Span>,
     inputs: Vec<TokenStream>,
     outputs: Vec<TokenStream>,
     clobbers: Vec<TokenStream>,
@@ -165,6 +160,24 @@ impl fmt::Write for InlineAssembly {
     }
 }
 
+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 {
@@ -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::<TokenStream>::new();
         let mut after_asm = Vec::<TokenStream>::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;