compiles new inline asm without errors
[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, AssemblyMetavariableId, AssemblyWithTextSpan};
5 use proc_macro2::{Ident, Span, TokenStream};
6 use quote::{quote, ToTokens, TokenStreamExt};
7 use std::{collections::HashMap, fmt, hash::Hash, mem};
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 ImmediateS16,
151 ImmediateU16,
152 Carry,
153 Overflow,
154 }
155 }
156
157 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
158 enum ImmediateShape {
159 S16,
160 U16,
161 }
162
163 impl ImmediateShape {
164 fn is_signed(self) -> bool {
165 match self {
166 ImmediateShape::S16 => true,
167 ImmediateShape::U16 => false,
168 }
169 }
170 fn bits(self) -> usize {
171 match self {
172 ImmediateShape::S16 | ImmediateShape::U16 => 16,
173 }
174 }
175 fn bit_mask(self) -> u64 {
176 match self.bits() {
177 64 => u64::MAX,
178 bits => (1u64 << bits) - 1,
179 }
180 }
181 fn value_to_string(self, bits: u64) -> String {
182 let bits = self.normalize_value(bits);
183 if self.is_signed() {
184 (bits as i64).to_string()
185 } else {
186 bits.to_string()
187 }
188 }
189 fn normalize_value(self, mut bits: u64) -> u64 {
190 bits &= self.bit_mask();
191 if self.is_signed() && (bits >> (self.bits() - 1)) != 0 {
192 bits |= !self.bit_mask();
193 }
194 bits
195 }
196 /// returns all the immediate values starting at zero and incrementing from there
197 fn values(self) -> impl Iterator<Item = u64> {
198 (0..=self.bit_mask()).map(move |v| self.normalize_value(v))
199 }
200 }
201
202 impl InstructionInputName {
203 fn get_immediate_shape(&self) -> Option<ImmediateShape> {
204 match self {
205 InstructionInputName::Ra(_)
206 | InstructionInputName::Rb(_)
207 | InstructionInputName::Rc(_)
208 | InstructionInputName::Carry(_)
209 | InstructionInputName::Overflow(_) => None,
210 InstructionInputName::ImmediateS16(_) => Some(ImmediateShape::S16),
211 InstructionInputName::ImmediateU16(_) => Some(ImmediateShape::U16),
212 }
213 }
214 fn get_instruction_input_register_tokens(&self) -> TokenStream {
215 match self {
216 InstructionInputName::Ra(_) => quote! {InstructionInputRegister::Ra},
217 InstructionInputName::Rb(_) => quote! {InstructionInputRegister::Rb},
218 InstructionInputName::Rc(_) => quote! {InstructionInputRegister::Rc},
219 InstructionInputName::ImmediateS16(_) => {
220 quote! {InstructionInputRegister::ImmediateS16}
221 }
222 InstructionInputName::ImmediateU16(_) => {
223 quote! {InstructionInputRegister::ImmediateU16}
224 }
225 InstructionInputName::Carry(_) => quote! {InstructionInputRegister::Carry},
226 InstructionInputName::Overflow(_) => quote! {InstructionInputRegister::Overflow},
227 }
228 }
229 }
230
231 ident_enum! {
232 #[parse_error_msg = "unknown instruction output"]
233 enum InstructionOutputName {
234 Rt,
235 Carry,
236 Overflow,
237 CR0,
238 CR1,
239 CR2,
240 CR3,
241 CR4,
242 CR5,
243 CR6,
244 CR7,
245 }
246 }
247
248 #[derive(Debug)]
249 struct InstructionArg<T: InstructionArgName> {
250 name: T,
251 register: Option<LitStr>,
252 }
253
254 impl<T: InstructionArgName> InstructionArg<T> {
255 fn error_if_register_is_specified(&self) -> syn::Result<()> {
256 match &self.register {
257 None => Ok(()),
258 Some(register) => Err(Error::new_spanned(
259 register,
260 format_args!("register specification not allowed on {}", self.name.name()),
261 )),
262 }
263 }
264 }
265
266 impl<T: InstructionArgName> Parse for InstructionArg<T> {
267 fn parse(input: ParseStream) -> syn::Result<Self> {
268 let name = input.parse()?;
269 let register = if input.peek(token::Paren) {
270 let register_tokens;
271 parenthesized!(register_tokens in input);
272 let register: LitStr = register_tokens.parse()?;
273 match &*register.value() {
274 "r1" => Err("stack pointer (r1) can't be used as instruction argument"),
275 "r2" => Err("TOC pointer (r2) can't be used as instruction argument"),
276 "r13" => {
277 Err("system thread id register (r13) can't be used as instruction argument")
278 }
279 "r0" | "r3" | "r4" | "r5" | "r6" | "r7" | "r8" | "r9" | "r10" | "r11" | "r12"
280 | "r14" | "r15" | "r16" | "r17" | "r18" | "r19" | "r20" | "r21" | "r22" | "r23"
281 | "r24" | "r25" | "r26" | "r27" | "r28" | "r29" | "r30" | "r31" => Ok(()),
282 _ => Err("unknown register: valid values are r0, r3..r12, r14..r31"),
283 }
284 .map_err(|msg| Error::new_spanned(&register, msg))?;
285 Some(register)
286 } else {
287 None
288 };
289 Ok(Self { name, register })
290 }
291 }
292
293 type InstructionInput = InstructionArg<InstructionInputName>;
294 type InstructionOutput = InstructionArg<InstructionOutputName>;
295
296 impl InstructionInput {
297 fn constraint(&self) -> TokenStream {
298 if let Some(register) = &self.register {
299 register.to_token_stream()
300 } else {
301 quote! { reg_nonzero }
302 }
303 }
304 }
305
306 impl InstructionOutput {
307 fn constraint(&self) -> TokenStream {
308 if let Some(register) = &self.register {
309 register.to_token_stream()
310 } else {
311 quote! { reg_nonzero }
312 }
313 }
314 }
315
316 #[derive(Debug)]
317 struct Instruction {
318 enumerant: Ident,
319 fn_name: Ident,
320 inputs: Punctuated<InstructionInput, Token!(,)>,
321 outputs: Punctuated<InstructionOutput, Token!(,)>,
322 instruction_name: LitStr,
323 literal_instruction_text: Option<LitStr>,
324 }
325
326 fn check_duplicate_free<'a, T: InstructionArgName + 'a>(
327 args: impl IntoIterator<Item = &'a InstructionArg<T>>,
328 ) -> syn::Result<()> {
329 let mut seen_args = HashMap::new();
330 for arg in args {
331 if let Some(prev_arg) = seen_args.insert(arg.name.enumerant(), arg) {
332 let mut error = Error::new(
333 arg.name.span().clone(),
334 format_args!(
335 "duplicate instruction argument: {}",
336 arg.name.clone().into_ident()
337 ),
338 );
339 error.combine(Error::new(
340 prev_arg.name.span().clone(),
341 format_args!(
342 "duplicate instruction argument: {}",
343 prev_arg.name.clone().into_ident()
344 ),
345 ));
346 return Err(error);
347 }
348 }
349 Ok(())
350 }
351
352 impl Parse for Instruction {
353 fn parse(input: ParseStream) -> syn::Result<Self> {
354 input.parse::<Token!(#)>()?;
355 let enumerant_attr_tokens;
356 bracketed!(enumerant_attr_tokens in input);
357 let enumerant_name: Ident = enumerant_attr_tokens.parse()?;
358 if enumerant_name != "enumerant" {
359 return Err(Error::new_spanned(
360 enumerant_name,
361 "expected `#[enumerant = ...]` attribute",
362 ));
363 }
364 enumerant_attr_tokens.parse::<Token!(=)>()?;
365 let enumerant: Ident = enumerant_attr_tokens.parse()?;
366 input.parse::<Token!(fn)>()?;
367 let fn_name: Ident = input.parse()?;
368 let inputs_tokens;
369 parenthesized!(inputs_tokens in input);
370 let inputs = inputs_tokens.parse_terminated(InstructionInput::parse)?;
371 check_duplicate_free(&inputs)?;
372 let mut found_immediate = false;
373 for input in &inputs {
374 if input.name.get_immediate_shape().is_some() {
375 if mem::replace(&mut found_immediate, true) {
376 return Err(Error::new_spanned(
377 &input.name,
378 "multiple immediates for an instruction are not supported",
379 ));
380 }
381 }
382 }
383 input.parse::<Token!(->)>()?;
384 let outputs_tokens;
385 parenthesized!(outputs_tokens in input);
386 let outputs = outputs_tokens.parse_terminated(InstructionOutput::parse)?;
387 check_duplicate_free(&outputs)?;
388 let body_tokens;
389 braced!(body_tokens in input);
390 let instruction_name: LitStr = body_tokens.parse()?;
391 let literal_instruction_text;
392 if body_tokens.peek(Token!(:)) {
393 body_tokens.parse::<Token!(:)>()?;
394 literal_instruction_text = Some(body_tokens.parse()?);
395 if found_immediate {
396 return Err(Error::new_spanned(
397 &literal_instruction_text,
398 "literal instruction text is not supported for instructions with immediates",
399 ));
400 }
401 } else {
402 literal_instruction_text = None;
403 }
404 Ok(Self {
405 enumerant,
406 fn_name,
407 inputs,
408 outputs,
409 instruction_name,
410 literal_instruction_text,
411 })
412 }
413 }
414
415 impl Instruction {
416 fn map_input_registers(&self) -> syn::Result<Vec<TokenStream>> {
417 let mut retval = Vec::new();
418 for input in &self.inputs {
419 retval.push(input.name.get_instruction_input_register_tokens());
420 }
421 Ok(retval)
422 }
423 fn to_native_fn_tokens(&self) -> syn::Result<TokenStream> {
424 let Instruction {
425 enumerant: _,
426 fn_name,
427 inputs,
428 outputs,
429 instruction_name,
430 literal_instruction_text,
431 } = self;
432 let asm_instr = Assembly::from(
433 literal_instruction_text
434 .as_ref()
435 .unwrap_or(instruction_name)
436 .value(),
437 );
438 let mut asm_instr_args = Vec::new();
439 let mut before_instr_asm_lines = Vec::<Assembly>::new();
440 let mut after_instr_asm_lines = Vec::<Assembly>::new();
441 let mut before_asm = Vec::<TokenStream>::new();
442 let mut after_asm = Vec::<TokenStream>::new();
443 let mut need_carry_output = false;
444 let mut need_overflow_output = false;
445 let mut need_cr_output = false;
446 for output in outputs {
447 match output.name {
448 InstructionOutputName::Rt(_) => {
449 before_asm.push(quote! {let rt: u64;});
450 let constraint = output.constraint();
451 asm_instr_args.push(assembly! {"{" output{out(#constraint) rt} "}" });
452 after_asm.push(quote! {retval.rt = Some(rt);});
453 }
454 InstructionOutputName::Carry(_) => {
455 output.error_if_register_is_specified()?;
456 need_carry_output = true;
457 }
458 InstructionOutputName::Overflow(_) => {
459 output.error_if_register_is_specified()?;
460 need_overflow_output = true;
461 }
462 InstructionOutputName::CR0(_) => {
463 output.error_if_register_is_specified()?;
464 need_cr_output = true;
465 after_asm.push(quote! {
466 retval.cr0 = Some(ConditionRegister::from_cr_field(cr, 0));
467 });
468 }
469 InstructionOutputName::CR1(_) => {
470 output.error_if_register_is_specified()?;
471 need_cr_output = true;
472 after_asm.push(quote! {
473 retval.cr1 = Some(ConditionRegister::from_cr_field(cr, 1));
474 });
475 }
476 InstructionOutputName::CR2(_) => {
477 output.error_if_register_is_specified()?;
478 need_cr_output = true;
479 after_asm.push(quote! {
480 retval.cr2 = Some(ConditionRegister::from_cr_field(cr, 2));
481 });
482 }
483 InstructionOutputName::CR3(_) => {
484 output.error_if_register_is_specified()?;
485 need_cr_output = true;
486 after_asm.push(quote! {
487 retval.cr3 = Some(ConditionRegister::from_cr_field(cr, 3));
488 });
489 }
490 InstructionOutputName::CR4(_) => {
491 output.error_if_register_is_specified()?;
492 need_cr_output = true;
493 after_asm.push(quote! {
494 retval.cr4 = Some(ConditionRegister::from_cr_field(cr, 4));
495 });
496 }
497 InstructionOutputName::CR5(_) => {
498 output.error_if_register_is_specified()?;
499 need_cr_output = true;
500 after_asm.push(quote! {
501 retval.cr5 = Some(ConditionRegister::from_cr_field(cr, 5));
502 });
503 }
504 InstructionOutputName::CR6(_) => {
505 output.error_if_register_is_specified()?;
506 need_cr_output = true;
507 after_asm.push(quote! {
508 retval.cr6 = Some(ConditionRegister::from_cr_field(cr, 6));
509 });
510 }
511 InstructionOutputName::CR7(_) => {
512 output.error_if_register_is_specified()?;
513 need_cr_output = true;
514 after_asm.push(quote! {
515 retval.cr7 = Some(ConditionRegister::from_cr_field(cr, 7));
516 });
517 }
518 }
519 }
520 let mut need_carry_input = false;
521 let mut need_overflow_input = false;
522 struct Immediate {
523 shape: ImmediateShape,
524 id: AssemblyMetavariableId,
525 }
526 let mut immediate: Option<Immediate> = None;
527 for input in inputs {
528 match input.name {
529 InstructionInputName::Ra(_) => {
530 before_asm.push(quote! {let ra: u64 = inputs.try_get_ra()?;});
531 let constraint = input.constraint();
532 asm_instr_args.push(assembly! {"{" input{in(#constraint) ra} "}"});
533 }
534 InstructionInputName::Rb(_) => {
535 before_asm.push(quote! {let rb: u64 = inputs.try_get_rb()?;});
536 let constraint = input.constraint();
537 asm_instr_args.push(assembly! {"{" input{in(#constraint) rb} "}"});
538 }
539 InstructionInputName::Rc(_) => {
540 before_asm.push(quote! {let rc: u64 = inputs.try_get_rc()?;});
541 let constraint = input.constraint();
542 asm_instr_args.push(assembly! {"{" input{in(#constraint) rc} "}"});
543 }
544 InstructionInputName::ImmediateS16(_) | InstructionInputName::ImmediateU16(_) => {
545 input.error_if_register_is_specified()?;
546 let shape = input.name.get_immediate_shape().unwrap();
547 let id = AssemblyMetavariableId::new();
548 assert!(immediate.is_none());
549 immediate = Some(Immediate { shape, id });
550 let mask = shape.bit_mask();
551 let instruction_input_register =
552 input.name.get_instruction_input_register_tokens();
553 before_asm.push(quote! {
554 let immediate: u64 = inputs.try_get_immediate(
555 #instruction_input_register
556 )? & #mask;
557 });
558 asm_instr_args.push(id.into());
559 }
560 InstructionInputName::Carry(_) => {
561 input.error_if_register_is_specified()?;
562 need_carry_input = true;
563 }
564 InstructionInputName::Overflow(_) => {
565 input.error_if_register_is_specified()?;
566 need_overflow_input = true;
567 }
568 }
569 }
570 if need_carry_input || need_carry_output || need_overflow_input || need_overflow_output {
571 before_asm.push(quote! {
572 let mut xer_in: u64 = 0;
573 let mut xer_mask_in: u64 = !0;
574 });
575 if need_carry_input || need_carry_output {
576 before_asm.push(quote! {
577 xer_mask_in &= !CarryFlags::XER_MASK;
578 });
579 }
580 if need_overflow_input || need_overflow_output {
581 before_asm.push(quote! {
582 xer_mask_in &= !OverflowFlags::XER_MASK;
583 });
584 }
585 if need_carry_input {
586 before_asm.push(quote! {
587 xer_in |= inputs.try_get_carry()?.to_xer();
588 });
589 }
590 if need_overflow_input {
591 before_asm.push(quote! {
592 xer_in |= inputs.try_get_overflow()?.to_xer();
593 });
594 }
595 before_asm.push(quote! {
596 let xer_out: u64;
597 });
598 let xer_out;
599 before_instr_asm_lines.push(assembly! {
600 "mfxer {" output(xer_out = {out(reg_nonzero) xer_out}) "}"
601 });
602 before_instr_asm_lines.push(assembly! {
603 "and {" (xer_out) "}, {" (xer_out) "}, {" input{in(reg_nonzero) xer_mask_in} "}"
604 });
605 before_instr_asm_lines.push(assembly! {
606 "or {" (xer_out) "}, {" (xer_out) "}, {" input{in(reg_nonzero) xer_in} "}"
607 });
608 before_instr_asm_lines.push(assembly! {
609 "mtxer {" (xer_out) "}" clobber{out("xer") _}
610 });
611 after_instr_asm_lines.push(assembly! {
612 "mfxer {" (xer_out) "}"
613 });
614 if need_carry_output {
615 after_asm.push(quote! {
616 retval.carry = Some(CarryFlags::from_xer(xer_out));
617 });
618 }
619 if need_overflow_output {
620 after_asm.push(quote! {
621 retval.overflow = Some(OverflowFlags::from_xer(xer_out));
622 });
623 }
624 }
625 if need_cr_output {
626 before_asm.push(quote! {
627 let cr: u32;
628 });
629 after_instr_asm_lines.push(assembly! {
630 "mfcr {" output{out(reg_nonzero) cr} "}" clobber{out("cr") _}
631 });
632 }
633 let mut asm_instrs = asm_instr;
634 let mut separator = " ";
635 for i in asm_instr_args {
636 if literal_instruction_text.is_some() {
637 let i = i.args_without_text();
638 append_assembly!(asm_instrs; (i));
639 } else {
640 append_assembly!(asm_instrs; (separator) (i));
641 }
642 separator = ", ";
643 }
644 if let Some(Immediate {
645 shape,
646 id: immediate_id,
647 }) = immediate
648 {
649 let shape: ImmediateShape = shape;
650 assert!(literal_instruction_text.is_none());
651 // save and restore lr and ctr ourselves since LLVM doesn't handle that properly
652 // see https://bugs.llvm.org/show_bug.cgi?id=47811
653 // and https://bugs.llvm.org/show_bug.cgi?id=47812
654 before_asm.push(quote! {let lr_temp: u64;});
655 let lr_temp;
656 before_instr_asm_lines
657 .push(assembly! {"mflr {" output(lr_temp = {out(reg_nonzero) lr_temp}) "}"});
658 after_instr_asm_lines.push(assembly! {"mtlr {" (lr_temp) "}"});
659 before_asm.push(quote! {let ctr_temp: u64;});
660 let ctr_temp;
661 before_instr_asm_lines
662 .push(assembly! {"mfctr {" output(ctr_temp = {out(reg_nonzero) ctr_temp}) "}"});
663 after_instr_asm_lines.push(assembly! {"mtctr {" (ctr_temp) "}"});
664 let template = mem::replace(&mut asm_instrs, assembly! {});
665 let target_temp;
666 before_asm.push(quote! {let target_temp: u64;});
667 let target_temp2;
668 before_asm.push(quote! {let target_temp2: u64;});
669 append_assembly! {
670 asm_instrs;
671 "bl 3f\n"
672 "4:\n"
673 "mulli {" output(target_temp = {out(reg_nonzero) target_temp}) "}, {" input{in(reg_nonzero) immediate} "}, 1f - 0f\n"
674 "addi {" (target_temp) "}, {" (target_temp) "}, 0f - 4b\n"
675 "mflr {" output(target_temp2 = {out(reg_nonzero) target_temp2}) "}\n"
676 "add {" (target_temp) "}, {" (target_temp) "}, {" (target_temp2) "}\n"
677 "mtctr {" (target_temp) "}\n"
678 "bctrl\n"
679 "b 2f\n"
680 "3:\n"
681 "blr\n"
682 };
683 let mut count = 0;
684 for (index, immediate) in shape.values().enumerate() {
685 count = index + 1;
686 match index {
687 0 => {
688 append_assembly! {asm_instrs; "0:\n"};
689 }
690 1 => {
691 append_assembly! {asm_instrs; "1:\n"};
692 }
693 _ => {}
694 }
695 let expanded_template = template
696 .replace_metavariables(|id| -> syn::Result<_> {
697 Ok(if id == immediate_id {
698 shape.value_to_string(immediate).into()
699 } else {
700 id.into()
701 })
702 })?
703 .text_without_args();
704 append_assembly! {asm_instrs; (expanded_template) "\n"};
705 append_assembly! {asm_instrs; "blr\n"};
706 }
707 assert!(count >= 1);
708 append_assembly! {asm_instrs; "2:"};
709 let args = template.args_without_text();
710 append_assembly! {asm_instrs; (args)};
711 }
712 let mut final_asm = assembly! {};
713 for i in before_instr_asm_lines {
714 append_assembly! {final_asm; (i) "\n"};
715 }
716 append_assembly!(final_asm; (asm_instrs));
717 for i in after_instr_asm_lines {
718 append_assembly! {final_asm; "\n" (i)};
719 }
720 let asm = AssemblyWithTextSpan {
721 asm: final_asm,
722 text_span: instruction_name.span(),
723 };
724 Ok(quote! {
725 pub fn #fn_name(inputs: InstructionInput) -> InstructionResult {
726 #![allow(unused_variables, unused_assignments, unused_mut)]
727 #(#before_asm)*
728 unsafe {
729 #asm;
730 }
731 let mut retval = InstructionOutput::default();
732 #(#after_asm)*
733 Ok(retval)
734 }
735 })
736 }
737 }
738
739 #[derive(Debug)]
740 pub(crate) struct Instructions {
741 instructions: Vec<Instruction>,
742 }
743
744 impl Instructions {
745 pub(crate) fn to_tokens(&self) -> syn::Result<TokenStream> {
746 let mut fn_names = Vec::new();
747 let mut instr_enumerants = Vec::new();
748 let mut get_native_fn_match_cases = Vec::new();
749 let mut get_model_fn_match_cases = Vec::new();
750 let mut get_used_input_registers_match_cases = Vec::new();
751 let mut name_match_cases = Vec::new();
752 let mut enumerants = Vec::new();
753 let mut native_fn_tokens = Vec::new();
754 for instruction in &self.instructions {
755 let Instruction {
756 enumerant,
757 fn_name,
758 inputs: _,
759 outputs: _,
760 instruction_name,
761 literal_instruction_text: _,
762 } = instruction;
763 fn_names.push(fn_name);
764 enumerants.push(enumerant);
765 instr_enumerants.push(quote! {
766 #[serde(rename = #instruction_name)]
767 #enumerant,
768 });
769 get_native_fn_match_cases.push(quote! {
770 Self::#enumerant => native_instrs::#fn_name,
771 });
772 get_model_fn_match_cases.push(quote! {
773 Self::#enumerant => instr_models::#fn_name,
774 });
775 let mapped_input_registers = instruction.map_input_registers()?;
776 get_used_input_registers_match_cases.push(quote! {
777 Self::#enumerant => &[#(#mapped_input_registers),*],
778 });
779 name_match_cases.push(quote! {
780 Self::#enumerant => #instruction_name,
781 });
782 native_fn_tokens.push(instruction.to_native_fn_tokens()?);
783 }
784 Ok(quote! {
785 #[cfg(feature = "python")]
786 macro_rules! wrap_all_instr_fns {
787 ($m:ident) => {
788 wrap_instr_fns! {
789 #![pymodule($m)]
790
791 #(fn #fn_names(inputs: InstructionInput) -> InstructionResult;)*
792 }
793 };
794 }
795
796 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
797 pub enum Instr {
798 #(#instr_enumerants)*
799 }
800
801 impl Instr {
802 #[cfg(feature = "native_instrs")]
803 pub fn get_native_fn(self) -> fn(InstructionInput) -> InstructionResult {
804 match self {
805 #(#get_native_fn_match_cases)*
806 }
807 }
808 pub fn get_model_fn(self) -> fn(InstructionInput) -> InstructionResult {
809 match self {
810 #(#get_model_fn_match_cases)*
811 }
812 }
813 pub fn get_used_input_registers(self) -> &'static [InstructionInputRegister] {
814 match self {
815 #(#get_used_input_registers_match_cases)*
816 }
817 }
818 pub fn name(self) -> &'static str {
819 match self {
820 #(#name_match_cases)*
821 }
822 }
823 pub const VALUES: &'static [Self] = &[
824 #(Self::#enumerants,)*
825 ];
826 }
827
828 #[cfg(feature = "native_instrs")]
829 pub mod native_instrs {
830 use super::*;
831
832 #(#native_fn_tokens)*
833 }
834 })
835 }
836 }
837
838 impl Parse for Instructions {
839 fn parse(input: ParseStream) -> syn::Result<Self> {
840 let mut instructions = Vec::new();
841 while !input.is_empty() {
842 instructions.push(input.parse()?);
843 }
844 Ok(Self { instructions })
845 }
846 }