1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
4 use crate::inline_assembly::{Assembly, AssemblyWithTextSpan};
5 use proc_macro2::{Ident, Span, TokenStream};
6 use quote::{quote, ToTokens, TokenStreamExt};
7 use std::{collections::HashMap, fmt, hash::Hash};
9 braced, bracketed, parenthesized,
10 parse::{Parse, ParseStream},
11 punctuated::Punctuated,
12 token, Error, LitStr, Token,
15 trait InstructionArgName: Clone + fmt::Debug + ToTokens + Parse {
16 type Enumerant: Copy + Eq + Hash + fmt::Debug;
17 fn enumerant(&self) -> Self::Enumerant;
18 fn span(&self) -> &Span;
19 fn name(&self) -> &'static str;
20 fn into_ident(self) -> Ident;
23 macro_rules! valid_enumerants_as_string {
24 ($enumerant:ident) => {
25 concat!("`", stringify!($enumerant), "`")
27 ($enumerant1:ident, $enumerant2:ident) => {
28 concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`")
30 ($($enumerant:ident),+) => {
31 valid_enumerants_as_string!((), ($($enumerant),+))
33 (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => {
36 stringify!($first_enumerant),
39 stringify!($enumerant),
42 stringify!($last_enumerant),
46 (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => {
47 valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*))
54 macro_rules! ident_enum {
56 #[parse_error_msg = $parse_error_msg:literal]
57 enum $enum_name:ident {
63 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
64 enum $enum_name<T = Span> {
70 impl InstructionArgName for $enum_name<Span> {
71 type Enumerant = $enum_name<()>;
72 fn enumerant(&self) -> Self::Enumerant {
73 $enum_name::enumerant(self)
75 fn span(&self) -> &Span {
78 $enum_name::$enumerant(span) => span,
82 fn name(&self) -> &'static str {
83 $enum_name::name(self)
85 fn into_ident(self) -> Ident {
88 $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span),
94 impl<T> fmt::Debug for $enum_name<T> {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.write_str(self.name())
100 impl<T> $enum_name<T> {
101 fn enumerant(&self) -> $enum_name<()> {
104 $enum_name::$enumerant(_) => $enum_name::$enumerant(()),
108 fn name(&self) -> &'static str {
111 $enum_name::$enumerant(_) => stringify!($enumerant),
117 impl ToTokens for $enum_name<Span> {
118 fn to_tokens(&self, tokens: &mut TokenStream) {
119 tokens.append(self.clone().into_ident());
123 impl Parse for $enum_name<Span> {
124 fn parse(input: ParseStream) -> syn::Result<Self> {
125 let id: Ident = input.parse()?;
127 if id == stringify!($enumerant) {
128 return Ok($enum_name::$enumerant(id.span()));
131 Err(Error::new_spanned(
135 ": valid values are: ",
136 valid_enumerants_as_string!($($enumerant),*)
145 #[parse_error_msg = "unknown instruction input"]
146 enum InstructionInputName {
156 #[parse_error_msg = "unknown instruction output"]
157 enum InstructionOutputName {
173 struct InstructionArg<T: InstructionArgName> {
175 register: Option<LitStr>,
178 impl<T: InstructionArgName> InstructionArg<T> {
179 fn error_if_register_is_specified(&self) -> syn::Result<()> {
180 match &self.register {
182 Some(register) => Err(Error::new_spanned(
184 format_args!("register specification not allowed on {}", self.name.name()),
190 impl<T: InstructionArgName> Parse for InstructionArg<T> {
191 fn parse(input: ParseStream) -> syn::Result<Self> {
192 let name = input.parse()?;
193 let register = if input.peek(token::Paren) {
195 parenthesized!(register_tokens in input);
196 let register: LitStr = register_tokens.parse()?;
197 match &*register.value() {
198 "r1" => Err("stack pointer (r1) can't be used as instruction argument"),
199 "r2" => Err("TOC pointer (r2) can't be used as instruction argument"),
201 Err("system thread id register (r13) can't be used as instruction argument")
203 "r0" | "r3" | "r4" | "r5" | "r6" | "r7" | "r8" | "r9" | "r10" | "r11" | "r12"
204 | "r14" | "r15" | "r16" | "r17" | "r18" | "r19" | "r20" | "r21" | "r22" | "r23"
205 | "r24" | "r25" | "r26" | "r27" | "r28" | "r29" | "r30" | "r31" => Ok(()),
206 _ => Err("unknown register: valid values are r0, r3..r12, r14..r31"),
208 .map_err(|msg| Error::new_spanned(®ister, msg))?;
213 Ok(Self { name, register })
217 type InstructionInput = InstructionArg<InstructionInputName>;
218 type InstructionOutput = InstructionArg<InstructionOutputName>;
220 impl InstructionInput {
221 fn constraint(&self) -> LitStr {
222 if let Some(register) = &self.register {
223 LitStr::new(&format!("{{{}}}", register.value()), register.span())
225 LitStr::new("b", Span::call_site())
230 impl InstructionOutput {
231 fn constraint(&self) -> LitStr {
232 if let Some(register) = &self.register {
233 LitStr::new(&format!("=&{{{}}}", register.value()), register.span())
235 LitStr::new("=&b", Span::call_site())
244 inputs: Punctuated<InstructionInput, Token!(,)>,
245 outputs: Punctuated<InstructionOutput, Token!(,)>,
246 instruction_name: LitStr,
247 literal_instruction_text: Option<LitStr>,
250 fn check_duplicate_free<'a, T: InstructionArgName + 'a>(
251 args: impl IntoIterator<Item = &'a InstructionArg<T>>,
252 ) -> syn::Result<()> {
253 let mut seen_args = HashMap::new();
255 if let Some(prev_arg) = seen_args.insert(arg.name.enumerant(), arg) {
256 let mut error = Error::new(
257 arg.name.span().clone(),
259 "duplicate instruction argument: {}",
260 arg.name.clone().into_ident()
263 error.combine(Error::new(
264 prev_arg.name.span().clone(),
266 "duplicate instruction argument: {}",
267 prev_arg.name.clone().into_ident()
276 impl Parse for Instruction {
277 fn parse(input: ParseStream) -> syn::Result<Self> {
278 input.parse::<Token!(#)>()?;
279 let enumerant_attr_tokens;
280 bracketed!(enumerant_attr_tokens in input);
281 let enumerant_name: Ident = enumerant_attr_tokens.parse()?;
282 if enumerant_name != "enumerant" {
283 return Err(Error::new_spanned(
285 "expected `#[enumerant = ...]` attribute",
288 enumerant_attr_tokens.parse::<Token!(=)>()?;
289 let enumerant: Ident = enumerant_attr_tokens.parse()?;
290 input.parse::<Token!(fn)>()?;
291 let fn_name: Ident = input.parse()?;
293 parenthesized!(inputs_tokens in input);
294 let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
295 check_duplicate_free(&inputs)?;
296 input.parse::<Token!(->)>()?;
298 parenthesized!(outputs_tokens in input);
299 let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?;
300 check_duplicate_free(&outputs)?;
302 braced!(body_tokens in input);
303 let instruction_name: LitStr = body_tokens.parse()?;
304 let literal_instruction_text;
305 if body_tokens.peek(Token!(:)) {
306 body_tokens.parse::<Token!(:)>()?;
307 literal_instruction_text = Some(body_tokens.parse()?);
309 literal_instruction_text = None;
317 literal_instruction_text,
323 fn map_input_registers(&self) -> syn::Result<Vec<TokenStream>> {
324 let mut retval = Vec::new();
325 for input in &self.inputs {
326 retval.push(match input.name {
327 InstructionInputName::Ra(_) => quote! {InstructionInputRegister::Ra},
328 InstructionInputName::Rb(_) => quote! {InstructionInputRegister::Rb},
329 InstructionInputName::Rc(_) => quote! {InstructionInputRegister::Rc},
330 InstructionInputName::Carry(_) => quote! {InstructionInputRegister::Carry},
331 InstructionInputName::Overflow(_) => quote! {InstructionInputRegister::Overflow},
336 fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
343 literal_instruction_text,
345 let asm_instr = Assembly::from(
346 literal_instruction_text
348 .unwrap_or(instruction_name)
351 let mut asm_instr_args = Vec::new();
352 let mut before_instr_asm_lines = Vec::<Assembly>::new();
353 let mut after_instr_asm_lines = Vec::<Assembly>::new();
354 let mut before_asm = Vec::<TokenStream>::new();
355 let mut after_asm = Vec::<TokenStream>::new();
356 let mut need_carry_output = false;
357 let mut need_overflow_output = false;
358 let mut need_cr_output = false;
359 for output in outputs {
361 InstructionOutputName::Rt(_) => {
362 before_asm.push(quote! {let rt: u64;});
363 let constraint = output.constraint();
364 asm_instr_args.push(assembly! {"$" output{#constraint(rt)} });
365 after_asm.push(quote! {retval.rt = Some(rt);});
367 InstructionOutputName::Carry(_) => {
368 output.error_if_register_is_specified()?;
369 need_carry_output = true;
371 InstructionOutputName::Overflow(_) => {
372 output.error_if_register_is_specified()?;
373 need_overflow_output = true;
375 InstructionOutputName::CR0(_) => {
376 output.error_if_register_is_specified()?;
377 need_cr_output = true;
378 after_asm.push(quote! {
379 retval.cr0 = Some(ConditionRegister::from_cr_field(cr, 0));
382 InstructionOutputName::CR1(_) => {
383 output.error_if_register_is_specified()?;
384 need_cr_output = true;
385 after_asm.push(quote! {
386 retval.cr1 = Some(ConditionRegister::from_cr_field(cr, 1));
389 InstructionOutputName::CR2(_) => {
390 output.error_if_register_is_specified()?;
391 need_cr_output = true;
392 after_asm.push(quote! {
393 retval.cr2 = Some(ConditionRegister::from_cr_field(cr, 2));
396 InstructionOutputName::CR3(_) => {
397 output.error_if_register_is_specified()?;
398 need_cr_output = true;
399 after_asm.push(quote! {
400 retval.cr3 = Some(ConditionRegister::from_cr_field(cr, 3));
403 InstructionOutputName::CR4(_) => {
404 output.error_if_register_is_specified()?;
405 need_cr_output = true;
406 after_asm.push(quote! {
407 retval.cr4 = Some(ConditionRegister::from_cr_field(cr, 4));
410 InstructionOutputName::CR5(_) => {
411 output.error_if_register_is_specified()?;
412 need_cr_output = true;
413 after_asm.push(quote! {
414 retval.cr5 = Some(ConditionRegister::from_cr_field(cr, 5));
417 InstructionOutputName::CR6(_) => {
418 output.error_if_register_is_specified()?;
419 need_cr_output = true;
420 after_asm.push(quote! {
421 retval.cr6 = Some(ConditionRegister::from_cr_field(cr, 6));
424 InstructionOutputName::CR7(_) => {
425 output.error_if_register_is_specified()?;
426 need_cr_output = true;
427 after_asm.push(quote! {
428 retval.cr7 = Some(ConditionRegister::from_cr_field(cr, 7));
433 let mut need_carry_input = false;
434 let mut need_overflow_input = false;
435 for input in inputs {
437 InstructionInputName::Ra(_) => {
438 before_asm.push(quote! {let ra: u64 = inputs.try_get_ra()?;});
439 let constraint = input.constraint();
440 asm_instr_args.push(assembly! {"$" input{#constraint(ra)} });
442 InstructionInputName::Rb(_) => {
443 before_asm.push(quote! {let rb: u64 = inputs.try_get_rb()?;});
444 let constraint = input.constraint();
445 asm_instr_args.push(assembly! {"$" input{#constraint(rb)} });
447 InstructionInputName::Rc(_) => {
448 before_asm.push(quote! {let rc: u64 = inputs.try_get_rc()?;});
449 let constraint = input.constraint();
450 asm_instr_args.push(assembly! {"$" input{#constraint(rc)} });
452 InstructionInputName::Carry(_) => {
453 input.error_if_register_is_specified()?;
454 need_carry_input = true;
456 InstructionInputName::Overflow(_) => {
457 input.error_if_register_is_specified()?;
458 need_overflow_input = true;
462 if need_carry_input || need_carry_output || need_overflow_input || need_overflow_output {
463 before_asm.push(quote! {
464 let mut xer_in: u64 = 0;
465 let mut xer_mask_in: u64 = !0;
467 if need_carry_input || need_carry_output {
468 before_asm.push(quote! {
469 xer_mask_in &= !CarryFlags::XER_MASK;
472 if need_overflow_input || need_overflow_output {
473 before_asm.push(quote! {
474 xer_mask_in &= !OverflowFlags::XER_MASK;
477 if need_carry_input {
478 before_asm.push(quote! {
479 xer_in |= inputs.try_get_carry()?.to_xer();
482 if need_overflow_input {
483 before_asm.push(quote! {
484 xer_in |= inputs.try_get_overflow()?.to_xer();
487 before_asm.push(quote! {
491 before_instr_asm_lines.push(assembly! {
492 "mfxer $" output(xer_out = {"=&b"(xer_out)})
494 before_instr_asm_lines.push(assembly! {
495 "and $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_mask_in)}
497 before_instr_asm_lines.push(assembly! {
498 "or $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_in)}
500 before_instr_asm_lines.push(assembly! {
501 "mtxer $" (xer_out) clobber{"xer"}
503 after_instr_asm_lines.push(assembly! {
506 if need_carry_output {
507 after_asm.push(quote! {
508 retval.carry = Some(CarryFlags::from_xer(xer_out));
511 if need_overflow_output {
512 after_asm.push(quote! {
513 retval.overflow = Some(OverflowFlags::from_xer(xer_out));
518 before_asm.push(quote! {
521 after_instr_asm_lines.push(assembly! {
522 "mfcr $" output{"=&b"(cr)} clobber{"cr"}
525 let mut final_asm = assembly! {};
526 for i in before_instr_asm_lines {
527 append_assembly! {final_asm; (i) "\n"};
529 append_assembly!(final_asm; (asm_instr));
530 let mut separator = " ";
531 for i in asm_instr_args {
532 if literal_instruction_text.is_some() {
533 let i = i.args_without_text();
534 append_assembly!(final_asm; (i));
536 append_assembly!(final_asm; (separator) (i));
540 for i in after_instr_asm_lines {
541 append_assembly! {final_asm; "\n" (i)};
543 let asm = AssemblyWithTextSpan {
545 text_span: instruction_name.span(),
548 pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
549 #![allow(unused_variables, unused_assignments)]
554 let mut retval = InstructionOutput::default();
563 pub(crate) struct Instructions {
564 instructions: Vec<Instruction>,
568 pub(crate) fn to_tokens(&self) -> syn::Result<TokenStream> {
569 let mut fn_names = Vec::new();
570 let mut instr_enumerants = Vec::new();
571 let mut get_native_fn_match_cases = Vec::new();
572 let mut get_model_fn_match_cases = Vec::new();
573 let mut get_used_input_registers_match_cases = Vec::new();
574 let mut name_match_cases = Vec::new();
575 let mut enumerants = Vec::new();
576 let mut native_fn_tokens = Vec::new();
577 for instruction in &self.instructions {
584 literal_instruction_text: _,
586 fn_names.push(fn_name);
587 enumerants.push(enumerant);
588 instr_enumerants.push(quote! {
589 #[serde(rename = #instruction_name)]
592 get_native_fn_match_cases.push(quote! {
593 Self::#enumerant => native_instrs::#fn_name,
595 get_model_fn_match_cases.push(quote! {
596 Self::#enumerant => instr_models::#fn_name,
598 let mapped_input_registers = instruction.map_input_registers()?;
599 get_used_input_registers_match_cases.push(quote! {
600 Self::#enumerant => &[#(#mapped_input_registers),*],
602 name_match_cases.push(quote! {
603 Self::#enumerant => #instruction_name,
605 native_fn_tokens.push(instruction.to_native_fn_tokens()?);
608 #[cfg(feature = "python")]
609 macro_rules! wrap_all_instr_fns {
614 #(fn #fn_names(inputs: InstructionInput) -> InstructionResult;)*
619 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
621 #(#instr_enumerants)*
625 #[cfg(feature = "native_instrs")]
626 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult {
628 #(#get_native_fn_match_cases)*
631 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult {
633 #(#get_model_fn_match_cases)*
636 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
638 #(#get_used_input_registers_match_cases)*
641 pub fn name(self) -> &'static str {
643 #(#name_match_cases)*
646 pub const VALUES: &'static [Self] = &[
647 #(Self::#enumerants,)*
651 #[cfg(feature = "native_instrs")]
652 pub mod native_instrs {
655 #(#native_fn_tokens)*
661 impl Parse for Instructions {
662 fn parse(input: ParseStream) -> syn::Result<Self> {
663 let mut instructions = Vec::new();
664 while !input.is_empty() {
665 instructions.push(input.parse()?);
667 Ok(Self { instructions })