// SPDX-License-Identifier: LGPL-2.1-or-later
// See Notices.txt for copyright information
-use proc_macro2::{Span, TokenStream};
-use quote::{quote, quote_spanned, ToTokens};
+use proc_macro2::{Ident, Span, TokenStream};
+use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::{
collections::HashMap,
fmt::Write,
ops::{Deref, DerefMut},
sync::atomic::{AtomicU64, Ordering},
};
-use syn::{punctuated::Punctuated, LitStr, Token};
+use syn::{
+ ext::IdentExt,
+ parenthesized,
+ parse::{Parse, ParseStream},
+ punctuated::Punctuated,
+ token::Paren,
+ LitStr, Token,
+};
macro_rules! append_assembly {
($retval:ident;) => {};
}
AssemblyTextFragment::ArgIndex(id) => {
if let Some(index) = id_index_map.get(id) {
- write!(retval, "{}", index).unwrap();
+ write!(retval, "arg{}", index).unwrap();
} else {
panic!(
"unknown id in inline assembly arguments: id={:?}\n{:#?}",
}
})
.collect();
- args.extend(outputs.iter().map(ToTokens::to_token_stream));
- args.extend(inputs.iter().map(ToTokens::to_token_stream));
+ let mut named_args = Vec::new();
+ let mut unnamed_args = Vec::new();
+ for (index, tokens) in outputs
+ .iter()
+ .map(|v| &v.tokens)
+ .chain(inputs.iter().map(|v| &v.tokens))
+ .enumerate()
+ {
+ match syn::parse2::<AsmArg>(tokens.clone())
+ .unwrap_or_else(|e| panic!("failed to parse AsmArg: {}\nTokens:\n{}", e, tokens))
+ .reg
+ {
+ AsmArgReg::RegClass(_) => {
+ let id = format_ident!("arg{}", index);
+ named_args.push(quote! { #id = #tokens });
+ }
+ AsmArgReg::RegLit(_) => unnamed_args.push(tokens.clone()),
+ }
+ }
+ args.extend(named_args);
+ args.extend(unnamed_args);
args.extend(clobbers.iter().map(ToTokens::to_token_stream));
let value = quote! {
asm!(#args)
value.to_tokens(tokens);
}
}
+
+enum AsmArgReg {
+ RegClass(Ident),
+ RegLit(LitStr),
+}
+
+impl Parse for AsmArgReg {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ if input.peek(Ident::peek_any) {
+ Ok(Self::RegClass(input.call(Ident::parse_any)?))
+ } else {
+ Ok(Self::RegLit(input.parse()?))
+ }
+ }
+}
+
+#[allow(dead_code)]
+struct AsmArg {
+ io_kind: Ident,
+ paren: Paren,
+ reg: AsmArgReg,
+ body: TokenStream,
+}
+
+impl Parse for AsmArg {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let input2;
+ Ok(Self {
+ io_kind: input.call(Ident::parse_any)?,
+ paren: parenthesized!(input2 in input),
+ reg: input2.parse()?,
+ body: input.parse()?,
+ })
+ }
+}
"or {" (xer_out) "}, {" (xer_out) "}, {" input{in(reg_nonzero) xer_in} "}"
});
before_instr_asm_lines.push(assembly! {
- "mtxer $" (xer_out) clobber{"xer"}
+ "mtxer {" (xer_out) "}" clobber{out("xer") _}
});
after_instr_asm_lines.push(assembly! {
- "mfxer $" (xer_out)
+ "mfxer {" (xer_out) "}"
});
if need_carry_output {
after_asm.push(quote! {
let cr: u32;
});
after_instr_asm_lines.push(assembly! {
- "mfcr $" output{"=&b"(cr)} clobber{"cr"}
+ "mfcr {" output{out(reg_nonzero) cr} "}" clobber{out("cr") _}
});
}
let mut asm_instrs = asm_instr;
// and https://bugs.llvm.org/show_bug.cgi?id=47812
before_asm.push(quote! {let lr_temp: u64;});
let lr_temp;
- before_instr_asm_lines.push(assembly! {"mflr $" output(lr_temp = {"=&b"(lr_temp)})});
- after_instr_asm_lines.push(assembly! {"mtlr $" (lr_temp)});
+ before_instr_asm_lines
+ .push(assembly! {"mflr {" output(lr_temp = {out(reg_nonzero) lr_temp}) "}"});
+ after_instr_asm_lines.push(assembly! {"mtlr {" (lr_temp) "}"});
before_asm.push(quote! {let ctr_temp: u64;});
let ctr_temp;
- before_instr_asm_lines.push(assembly! {"mfctr $" output(ctr_temp = {"=&b"(ctr_temp)})});
- after_instr_asm_lines.push(assembly! {"mtctr $" (ctr_temp)});
+ before_instr_asm_lines
+ .push(assembly! {"mfctr {" output(ctr_temp = {out(reg_nonzero) ctr_temp}) "}"});
+ after_instr_asm_lines.push(assembly! {"mtctr {" (ctr_temp) "}"});
let template = mem::replace(&mut asm_instrs, assembly! {});
let target_temp;
before_asm.push(quote! {let target_temp: u64;});
asm_instrs;
"bl 3f\n"
"4:\n"
- "mulli $" output(target_temp = {"=&b"(target_temp)}) ", $" input{"b"(immediate)} ", 1f - 0f\n"
- "addi $" (target_temp) ", $" (target_temp) ", 0f - 4b\n"
- "mflr $" output(target_temp2 = {"=&b"(target_temp2)}) "\n"
- "add $" (target_temp) ", $" (target_temp) ", $" (target_temp2) "\n"
- "mtctr $" (target_temp) "\n"
+ "mulli {" output(target_temp = {out(reg_nonzero) target_temp}) "}, {" input{in(reg_nonzero) immediate} "}, 1f - 0f\n"
+ "addi {" (target_temp) "}, {" (target_temp) "}, 0f - 4b\n"
+ "mflr {" output(target_temp2 = {out(reg_nonzero) target_temp2}) "}\n"
+ "add {" (target_temp) "}, {" (target_temp) "}, {" (target_temp2) "}\n"
+ "mtctr {" (target_temp) "}\n"
"bctrl\n"
"b 2f\n"
"3:\n"
};
Ok(quote! {
pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
- #![allow(unused_variables, unused_assignments)]
+ #![allow(unused_variables, unused_assignments, unused_mut)]
#(#before_asm)*
unsafe {
#asm;
use instructions::Instructions;
use proc_macro::TokenStream;
+use quote::quote;
+use std::{env, fs, path::Path};
use syn::parse_macro_input;
#[proc_macro]
let input = parse_macro_input!(input as Instructions);
match input.to_tokens() {
Ok(retval) => {
- eprintln!("macro output:\n----------\n{}\n----------", retval);
- retval
+ fs::write(
+ Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("out.rs"),
+ retval.to_string(),
+ )
+ .unwrap();
+ quote! {
+ include!(concat!(env!("CARGO_MANIFEST_DIR"), "/out.rs"));
+ }
}
Err(err) => err.to_compile_error(),
}