1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
4 use proc_macro2::{Ident, Span, TokenStream};
5 use quote::{quote, ToTokens, TokenStreamExt};
6 use std::{borrow::Cow, fmt, fmt::Write};
8 braced, bracketed, parenthesized,
9 parse::{Parse, ParseStream},
11 punctuated::Punctuated,
12 Attribute, Error, ItemFn, LitStr, Token,
15 macro_rules! valid_enumerants_as_string {
16 ($enumerant:ident) => {
17 concat!("`", stringify!($enumerant), "`")
19 ($enumerant1:ident, $enumerant2:ident) => {
20 concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`")
22 ($($enumerant:ident),+) => {
23 valid_enumerants_as_string!((), ($($enumerant),+))
25 (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => {
28 stringify!($first_enumerant),
31 stringify!($enumerant),
34 stringify!($last_enumerant),
38 (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => {
39 valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*))
46 macro_rules! ident_enum {
48 #[parse_error_msg = $parse_error_msg:literal]
49 enum $enum_name:ident {
55 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
56 enum $enum_name<T = Span> {
62 impl<T> fmt::Debug for $enum_name<T> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.write_str(self.name())
68 impl<T> $enum_name<T> {
69 fn enumerant(&self) -> $enum_name<()> {
72 $enum_name::$enumerant(_) => $enum_name::$enumerant(()),
76 fn name(&self) -> &'static str {
79 $enum_name::$enumerant(_) => stringify!($enumerant),
86 fn into_ident(self) -> Ident {
89 $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span),
95 impl ToTokens for $enum_name<Span> {
96 fn to_tokens(&self, tokens: &mut TokenStream) {
97 tokens.append(self.clone().into_ident());
101 impl Parse for $enum_name<Span> {
102 fn parse(input: ParseStream) -> syn::Result<Self> {
103 let id: Ident = input.parse()?;
105 if id == stringify!($enumerant) {
106 return Ok($enum_name::$enumerant(id.span()));
109 Err(Error::new_spanned(
113 ": valid values are: ",
114 valid_enumerants_as_string!($($enumerant),*)
123 #[parse_error_msg = "unknown instruction input"]
124 enum InstructionInput {
133 #[parse_error_msg = "unknown instruction output"]
134 enum InstructionOutput {
142 #[derive(Debug, Clone)]
143 enum AssemblyTextFragment {
149 struct InlineAssembly {
150 text: Vec<AssemblyTextFragment>,
152 inputs: Vec<TokenStream>,
153 outputs: Vec<TokenStream>,
154 clobbers: Vec<TokenStream>,
157 impl fmt::Write for InlineAssembly {
158 fn write_str(&mut self, s: &str) -> fmt::Result {
159 if let Some(AssemblyTextFragment::Text(v)) = self.text.last_mut() {
162 self.text.push(AssemblyTextFragment::Text(String::from(s)));
168 impl InlineAssembly {
169 fn new(text_span: Span) -> Self {
175 clobbers: Vec::new(),
178 fn to_text(&self) -> String {
179 let mut retval = String::new();
180 for text in &self.text {
182 AssemblyTextFragment::Text(text) => retval += text,
183 AssemblyTextFragment::InputIndex(index) => {
184 write!(retval, "{}", index + self.outputs.len()).unwrap();
186 AssemblyTextFragment::OutputIndex(index) => write!(retval, "{}", index).unwrap(),
191 fn write_input_index(&mut self, index: usize) -> fmt::Result {
192 self.text.push(AssemblyTextFragment::InputIndex(index));
195 fn write_output_index(&mut self, index: usize) -> fmt::Result {
196 self.text.push(AssemblyTextFragment::OutputIndex(index));
201 impl ToTokens for InlineAssembly {
202 fn to_tokens(&self, tokens: &mut TokenStream) {
210 let text = LitStr::new(&self.to_text(), text_span.clone());
212 llvm_asm!(#text : #(#outputs),* : #(#inputs),* : #(#clobbers),*)
214 value.to_tokens(tokens);
222 inputs: Punctuated<InstructionInput, Token!(,)>,
223 outputs: Punctuated<InstructionOutput, Token!(,)>,
224 instruction_name: LitStr,
227 impl Parse for Instruction {
228 fn parse(input: ParseStream) -> syn::Result<Self> {
229 input.parse::<Token!(#)>()?;
230 let enumerant_attr_tokens;
231 bracketed!(enumerant_attr_tokens in input);
232 let enumerant_name: Ident = enumerant_attr_tokens.parse()?;
233 if enumerant_name != "enumerant" {
234 return Err(Error::new_spanned(
236 "expected `#[enumerant = ...]` attribute",
239 enumerant_attr_tokens.parse::<Token!(=)>()?;
240 let enumerant: Ident = enumerant_attr_tokens.parse()?;
241 input.parse::<Token!(fn)>()?;
242 let fn_name: Ident = input.parse()?;
244 parenthesized!(inputs_tokens in input);
245 let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
246 input.parse::<Token!(->)>()?;
248 parenthesized!(outputs_tokens in input);
249 let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?;
251 braced!(body_tokens in input);
252 let instruction_name: LitStr = body_tokens.parse()?;
264 fn map_input_registers(&self) -> syn::Result<Vec<TokenStream>> {
265 let mut retval = Vec::new();
266 for input in &self.inputs {
268 InstructionInput::Ra(_) => retval.push(quote! {InstructionInputRegister::Ra}),
269 InstructionInput::Rb(_) => retval.push(quote! {InstructionInputRegister::Rb}),
270 InstructionInput::Rc(_) => retval.push(quote! {InstructionInputRegister::Rc}),
271 InstructionInput::Carry(_) => retval.push(quote! {InstructionInputRegister::Carry}),
276 fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
284 let mut asm = InlineAssembly::new(instruction_name.span());
285 let mut before_asm = Vec::<TokenStream>::new();
286 let mut after_asm = Vec::<TokenStream>::new();
287 for output in &self.outputs {
289 InstructionOutput::Rt(span) => {
290 unimplemented!("InstructionOutput::Rt");
292 InstructionOutput::Carry(span) => {
293 unimplemented!("InstructionOutput::Carry");
295 InstructionOutput::Overflow(span) => {
296 unimplemented!("InstructionOutput::Overflow");
298 InstructionOutput::CR0(span) => {
299 unimplemented!("InstructionOutput::CR0");
303 for input in &self.inputs {
305 InstructionInput::Ra(span) => {
306 unimplemented!("InstructionInput::Ra");
308 InstructionInput::Rb(span) => {
309 unimplemented!("InstructionInput::Rb");
311 InstructionInput::Rc(span) => {
312 unimplemented!("InstructionInput::Rc");
314 InstructionInput::Carry(span) => {
315 unimplemented!("InstructionInput::Carry");
320 pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
321 #![allow(unused_variables, unused_assignments)]
322 let InstructionInput {
335 let mut retval = InstructionOutput::default();
344 struct Instructions {
345 instructions: Vec<Instruction>,
349 fn to_tokens(&self) -> syn::Result<TokenStream> {
350 let mut fn_names = Vec::new();
351 let mut instr_enumerants = Vec::new();
352 let mut get_native_fn_match_cases = Vec::new();
353 let mut get_model_fn_match_cases = Vec::new();
354 let mut get_used_input_registers_match_cases = Vec::new();
355 let mut name_match_cases = Vec::new();
356 let mut enumerants = Vec::new();
357 let mut native_fn_tokens = Vec::new();
358 for instruction in &self.instructions {
366 fn_names.push(fn_name);
367 enumerants.push(enumerant);
368 instr_enumerants.push(quote! {
369 #[serde(rename = #instruction_name)]
372 get_native_fn_match_cases.push(quote! {
373 Self::#enumerant => native_instrs::#fn_name,
375 get_model_fn_match_cases.push(quote! {
376 Self::#enumerant => instr_models::#fn_name,
378 let mapped_input_registers = instruction.map_input_registers()?;
379 get_used_input_registers_match_cases.push(quote! {
380 Self::#enumerant => &[#(#mapped_input_registers),*],
382 name_match_cases.push(quote! {
383 Self::#enumerant => #instruction_name,
385 native_fn_tokens.push(instruction.to_native_fn_tokens()?);
388 #[cfg(feature = "python")]
389 macro_rules! wrap_all_instr_fns {
394 #(fn #fn_names(inputs: InstructionInput) -> InstructionOutput;)*
399 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
401 #(#instr_enumerants)*
405 #[cfg(feature = "native_instrs")]
406 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionOutput {
408 #(#get_native_fn_match_cases)*
411 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionOutput {
413 #(#get_model_fn_match_cases)*
416 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
418 #(#get_used_input_registers_match_cases)*
421 pub fn name(self) -> &'static str {
423 #(#name_match_cases)*
426 pub const VALUES: &'static [Self] = &[
427 #(Self::#enumerants,)*
431 #[cfg(feature = "native_instrs")]
432 pub mod native_instrs {
435 #(#native_fn_tokens)*
441 impl Parse for Instructions {
442 fn parse(input: ParseStream) -> syn::Result<Self> {
443 let mut instructions = Vec::new();
444 while !input.is_empty() {
445 instructions.push(input.parse()?);
447 Ok(Self { instructions })
452 pub fn instructions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
453 let input = parse_macro_input!(input as Instructions);
454 match input.to_tokens() {
455 Ok(retval) => retval,
456 Err(err) => err.to_compile_error(),