--- /dev/null
+// 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);
+ }
+}
Attribute, Error, ItemFn, LitStr, Token,
};
+mod inline_assembly;
+
macro_rules! valid_enumerants_as_string {
($enumerant:ident) => {
concat!("`", stringify!($enumerant), "`")
}
}
-#[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>,
}
}
+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 {
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 {
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) => {
}
}
}
+ 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;