a53b2c06a67bd71438188f4bf7850c0b90c35037
[power-instruction-analyzer.git] / power-instruction-analyzer-proc-macro / src / instructions.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // See Notices.txt for copyright information
3
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};
8 use syn::{
9 braced, bracketed, parenthesized,
10 parse::{Parse, ParseStream},
11 punctuated::Punctuated,
12 token, Error, LitStr, Token,
13 };
14
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;
21 }
22
23 macro_rules! valid_enumerants_as_string {
24 ($enumerant:ident) => {
25 concat!("`", stringify!($enumerant), "`")
26 };
27 ($enumerant1:ident, $enumerant2:ident) => {
28 concat!("`", stringify!($enumerant1), "` and `", stringify!($enumerant2), "`")
29 };
30 ($($enumerant:ident),+) => {
31 valid_enumerants_as_string!((), ($($enumerant),+))
32 };
33 (($first_enumerant:ident, $($enumerant:ident,)+), ($last_enumerant:ident)) => {
34 concat!(
35 "`",
36 stringify!($first_enumerant),
37 $(
38 "`, `",
39 stringify!($enumerant),
40 )+
41 "`, and `",
42 stringify!($last_enumerant),
43 "`"
44 )
45 };
46 (($($enumerants:ident,)*), ($next_enumerant:ident, $($rest:ident),*)) => {
47 valid_enumerants_as_string!(($($enumerants,)* $next_enumerant,), ($($rest),*))
48 };
49 () => {
50 "<nothing>"
51 };
52 }
53
54 macro_rules! ident_enum {
55 (
56 #[parse_error_msg = $parse_error_msg:literal]
57 enum $enum_name:ident {
58 $(
59 $enumerant:ident,
60 )*
61 }
62 ) => {
63 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
64 enum $enum_name<T = Span> {
65 $(
66 $enumerant(T),
67 )*
68 }
69
70 impl InstructionArgName for $enum_name<Span> {
71 type Enumerant = $enum_name<()>;
72 fn enumerant(&self) -> Self::Enumerant {
73 $enum_name::enumerant(self)
74 }
75 fn span(&self) -> &Span {
76 match self {
77 $(
78 $enum_name::$enumerant(span) => span,
79 )*
80 }
81 }
82 fn name(&self) -> &'static str {
83 $enum_name::name(self)
84 }
85 fn into_ident(self) -> Ident {
86 match self {
87 $(
88 $enum_name::$enumerant(span) => Ident::new(stringify!($enumerant), span),
89 )*
90 }
91 }
92 }
93
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())
97 }
98 }
99
100 impl<T> $enum_name<T> {
101 fn enumerant(&self) -> $enum_name<()> {
102 match self {
103 $(
104 $enum_name::$enumerant(_) => $enum_name::$enumerant(()),
105 )*
106 }
107 }
108 fn name(&self) -> &'static str {
109 match self {
110 $(
111 $enum_name::$enumerant(_) => stringify!($enumerant),
112 )*
113 }
114 }
115 }
116
117 impl ToTokens for $enum_name<Span> {
118 fn to_tokens(&self, tokens: &mut TokenStream) {
119 tokens.append(self.clone().into_ident());
120 }
121 }
122
123 impl Parse for $enum_name<Span> {
124 fn parse(input: ParseStream) -> syn::Result<Self> {
125 let id: Ident = input.parse()?;
126 $(
127 if id == stringify!($enumerant) {
128 return Ok($enum_name::$enumerant(id.span()));
129 }
130 )*
131 Err(Error::new_spanned(
132 id,
133 concat!(
134 $parse_error_msg,
135 ": valid values are: ",
136 valid_enumerants_as_string!($($enumerant),*)
137 )
138 ))
139 }
140 }
141 };
142 }
143
144 ident_enum! {
145 #[parse_error_msg = "unknown instruction input"]
146 enum InstructionInputName {
147 Ra,
148 Rb,
149 Rc,
150 Carry,
151 Overflow,
152 }
153 }
154
155 ident_enum! {
156 #[parse_error_msg = "unknown instruction output"]
157 enum InstructionOutputName {
158 Rt,
159 Carry,
160 Overflow,
161 CR0,
162 CR1,
163 CR2,
164 CR3,
165 CR4,
166 CR5,
167 CR6,
168 CR7,
169 }
170 }
171
172 #[derive(Debug)]
173 struct InstructionArg<T: InstructionArgName> {
174 name: T,
175 register: Option<LitStr>,
176 }
177
178 impl<T: InstructionArgName> InstructionArg<T> {
179 fn error_if_register_is_specified(&self) -> syn::Result<()> {
180 match &self.register {
181 None => Ok(()),
182 Some(register) => Err(Error::new_spanned(
183 register,
184 format_args!("register specification not allowed on {}", self.name.name()),
185 )),
186 }
187 }
188 }
189
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) {
194 let register_tokens;
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"),
200 "r13" => {
201 Err("system thread id register (r13) can't be used as instruction argument")
202 }
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"),
207 }
208 .map_err(|msg| Error::new_spanned(&register, msg))?;
209 Some(register)
210 } else {
211 None
212 };
213 Ok(Self { name, register })
214 }
215 }
216
217 type InstructionInput = InstructionArg<InstructionInputName>;
218 type InstructionOutput = InstructionArg<InstructionOutputName>;
219
220 impl InstructionInput {
221 fn constraint(&self) -> LitStr {
222 if let Some(register) = &self.register {
223 LitStr::new(&format!("{{{}}}", register.value()), register.span())
224 } else {
225 LitStr::new("b", Span::call_site())
226 }
227 }
228 }
229
230 impl InstructionOutput {
231 fn constraint(&self) -> LitStr {
232 if let Some(register) = &self.register {
233 LitStr::new(&format!("=&{{{}}}", register.value()), register.span())
234 } else {
235 LitStr::new("=&b", Span::call_site())
236 }
237 }
238 }
239
240 #[derive(Debug)]
241 struct Instruction {
242 enumerant: Ident,
243 fn_name: Ident,
244 inputs: Punctuated<InstructionInput, Token!(,)>,
245 outputs: Punctuated<InstructionOutput, Token!(,)>,
246 instruction_name: LitStr,
247 literal_instruction_text: Option<LitStr>,
248 }
249
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();
254 for arg in args {
255 if let Some(prev_arg) = seen_args.insert(arg.name.enumerant(), arg) {
256 let mut error = Error::new(
257 arg.name.span().clone(),
258 format_args!(
259 "duplicate instruction argument: {}",
260 arg.name.clone().into_ident()
261 ),
262 );
263 error.combine(Error::new(
264 prev_arg.name.span().clone(),
265 format_args!(
266 "duplicate instruction argument: {}",
267 prev_arg.name.clone().into_ident()
268 ),
269 ));
270 return Err(error);
271 }
272 }
273 Ok(())
274 }
275
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(
284 enumerant_name,
285 "expected `#[enumerant = ...]` attribute",
286 ));
287 }
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()?;
292 let inputs_tokens;
293 parenthesized!(inputs_tokens in input);
294 let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
295 check_duplicate_free(&inputs)?;
296 input.parse::<Token!(->)>()?;
297 let outputs_tokens;
298 parenthesized!(outputs_tokens in input);
299 let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?;
300 check_duplicate_free(&outputs)?;
301 let body_tokens;
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()?);
308 } else {
309 literal_instruction_text = None;
310 }
311 Ok(Self {
312 enumerant,
313 fn_name,
314 inputs,
315 outputs,
316 instruction_name,
317 literal_instruction_text,
318 })
319 }
320 }
321
322 impl Instruction {
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},
332 });
333 }
334 Ok(retval)
335 }
336 fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
337 let Instruction {
338 enumerant: _,
339 fn_name,
340 inputs,
341 outputs,
342 instruction_name,
343 literal_instruction_text,
344 } = self;
345 let asm_instr = Assembly::from(
346 literal_instruction_text
347 .as_ref()
348 .unwrap_or(instruction_name)
349 .value(),
350 );
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 {
360 match output.name {
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);});
366 }
367 InstructionOutputName::Carry(_) => {
368 output.error_if_register_is_specified()?;
369 need_carry_output = true;
370 }
371 InstructionOutputName::Overflow(_) => {
372 output.error_if_register_is_specified()?;
373 need_overflow_output = true;
374 }
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));
380 });
381 }
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));
387 });
388 }
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));
394 });
395 }
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));
401 });
402 }
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));
408 });
409 }
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));
415 });
416 }
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));
422 });
423 }
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));
429 });
430 }
431 }
432 }
433 let mut need_carry_input = false;
434 let mut need_overflow_input = false;
435 for input in inputs {
436 match input.name {
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)} });
441 }
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)} });
446 }
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)} });
451 }
452 InstructionInputName::Carry(_) => {
453 input.error_if_register_is_specified()?;
454 need_carry_input = true;
455 }
456 InstructionInputName::Overflow(_) => {
457 input.error_if_register_is_specified()?;
458 need_overflow_input = true;
459 }
460 }
461 }
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;
466 });
467 if need_carry_input || need_carry_output {
468 before_asm.push(quote! {
469 xer_mask_in &= !CarryFlags::XER_MASK;
470 });
471 }
472 if need_overflow_input || need_overflow_output {
473 before_asm.push(quote! {
474 xer_mask_in &= !OverflowFlags::XER_MASK;
475 });
476 }
477 if need_carry_input {
478 before_asm.push(quote! {
479 xer_in |= inputs.try_get_carry()?.to_xer();
480 });
481 }
482 if need_overflow_input {
483 before_asm.push(quote! {
484 xer_in |= inputs.try_get_overflow()?.to_xer();
485 });
486 }
487 before_asm.push(quote! {
488 let xer_out: u64;
489 });
490 let xer_out;
491 before_instr_asm_lines.push(assembly! {
492 "mfxer $" output(xer_out = {"=&b"(xer_out)})
493 });
494 before_instr_asm_lines.push(assembly! {
495 "and $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_mask_in)}
496 });
497 before_instr_asm_lines.push(assembly! {
498 "or $" (xer_out) ", $" (xer_out) ", $" input{"b"(xer_in)}
499 });
500 before_instr_asm_lines.push(assembly! {
501 "mtxer $" (xer_out) clobber{"xer"}
502 });
503 after_instr_asm_lines.push(assembly! {
504 "mfxer $" (xer_out)
505 });
506 if need_carry_output {
507 after_asm.push(quote! {
508 retval.carry = Some(CarryFlags::from_xer(xer_out));
509 });
510 }
511 if need_overflow_output {
512 after_asm.push(quote! {
513 retval.overflow = Some(OverflowFlags::from_xer(xer_out));
514 });
515 }
516 }
517 if need_cr_output {
518 before_asm.push(quote! {
519 let cr: u32;
520 });
521 after_instr_asm_lines.push(assembly! {
522 "mfcr $" output{"=&b"(cr)} clobber{"cr"}
523 });
524 }
525 let mut final_asm = assembly! {};
526 for i in before_instr_asm_lines {
527 append_assembly! {final_asm; (i) "\n"};
528 }
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));
535 } else {
536 append_assembly!(final_asm; (separator) (i));
537 }
538 separator = ", ";
539 }
540 for i in after_instr_asm_lines {
541 append_assembly! {final_asm; "\n" (i)};
542 }
543 let asm = AssemblyWithTextSpan {
544 asm: final_asm,
545 text_span: instruction_name.span(),
546 };
547 Ok(quote! {
548 pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
549 #![allow(unused_variables, unused_assignments)]
550 #(#before_asm)*
551 unsafe {
552 #asm;
553 }
554 let mut retval = InstructionOutput::default();
555 #(#after_asm)*
556 Ok(retval)
557 }
558 })
559 }
560 }
561
562 #[derive(Debug)]
563 pub(crate) struct Instructions {
564 instructions: Vec<Instruction>,
565 }
566
567 impl Instructions {
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 {
578 let Instruction {
579 enumerant,
580 fn_name,
581 inputs: _,
582 outputs: _,
583 instruction_name,
584 literal_instruction_text: _,
585 } = instruction;
586 fn_names.push(fn_name);
587 enumerants.push(enumerant);
588 instr_enumerants.push(quote! {
589 #[serde(rename = #instruction_name)]
590 #enumerant,
591 });
592 get_native_fn_match_cases.push(quote! {
593 Self::#enumerant => native_instrs::#fn_name,
594 });
595 get_model_fn_match_cases.push(quote! {
596 Self::#enumerant => instr_models::#fn_name,
597 });
598 let mapped_input_registers = instruction.map_input_registers()?;
599 get_used_input_registers_match_cases.push(quote! {
600 Self::#enumerant => &[#(#mapped_input_registers),*],
601 });
602 name_match_cases.push(quote! {
603 Self::#enumerant => #instruction_name,
604 });
605 native_fn_tokens.push(instruction.to_native_fn_tokens()?);
606 }
607 Ok(quote! {
608 #[cfg(feature = "python")]
609 macro_rules! wrap_all_instr_fns {
610 ($m:ident) => {
611 wrap_instr_fns! {
612 #![pymodule($m)]
613
614 #(fn #fn_names(inputs: InstructionInput) -> InstructionResult;)*
615 }
616 };
617 }
618
619 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
620 pub enum Instr {
621 #(#instr_enumerants)*
622 }
623
624 impl Instr {
625 #[cfg(feature = "native_instrs")]
626 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult {
627 match self {
628 #(#get_native_fn_match_cases)*
629 }
630 }
631 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult {
632 match self {
633 #(#get_model_fn_match_cases)*
634 }
635 }
636 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
637 match self {
638 #(#get_used_input_registers_match_cases)*
639 }
640 }
641 pub fn name(self) -> &'static str {
642 match self {
643 #(#name_match_cases)*
644 }
645 }
646 pub const VALUES: &'static [Self] = &[
647 #(Self::#enumerants,)*
648 ];
649 }
650
651 #[cfg(feature = "native_instrs")]
652 pub mod native_instrs {
653 use super::*;
654
655 #(#native_fn_tokens)*
656 }
657 })
658 }
659 }
660
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()?);
666 }
667 Ok(Self { instructions })
668 }
669 }