1extern crate proc_macro;
2use litrs::{BoolLit, IntegerLit};
3use proc_macro::{Delimiter, TokenStream, TokenTree};
4
5#[proc_macro]
7pub fn i_assemble(input: TokenStream) -> TokenStream {
8 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
9 let name = i.to_string();
10 let lower = name.to_lowercase();
11 format!(
12 "
13 if operands.len() != 3 {{
14 Err(\"{lower} instruction requires 3 operands\".to_owned())
15 }} else {{
16 Ok(Instruction::{name}{{
17 dest: IRegister::try_from(operands[0])?,
18 src: IRegister::try_from(operands[1])?,
19 imm: IImmediate::try_from(parse_int(operands[2])?)?,
20 }})
21 }}"
22 )
23 .parse()
24 .unwrap()
25 } else {
26 panic!("expected identifier");
27 }
28}
29
30#[proc_macro]
32pub fn r_assemble(input: TokenStream) -> TokenStream {
33 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
34 let name = i.to_string();
35 let lower = name.to_lowercase();
36 format!(
37 "
38 if operands.len() != 3 {{
39 Err(\"{lower} instruction requires 3 operands\".to_owned())
40 }} else {{
41 Ok(Instruction::{name}{{
42 dest: IRegister::try_from(operands[0])?,
43 src1: IRegister::try_from(operands[1])?,
44 src2: IRegister::try_from(operands[2])?,
45 }})
46 }}"
47 )
48 .parse()
49 .unwrap()
50 } else {
51 panic!("expected identifier");
52 }
53}
54
55#[proc_macro]
57pub fn l_assemble(input: TokenStream) -> TokenStream {
58 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
59 let name = i.to_string();
60 let lower = name.to_lowercase();
61 format!(
62 "
63 if operands.len() != 2 {{
64 Err(\"{lower} instruction requires 2 operands\".to_owned())
65 }} else {{
66 let (base, offset) = parse_address_expression(operands[1])?;
67 Ok(Instruction::{name}{{
68 dest: IRegister::try_from(operands[0])?,
69 base,
70 offset: IImmediate::try_from(offset)?,
71 }})
72 }}"
73 )
74 .parse()
75 .unwrap()
76 } else {
77 panic!("expected identifier");
78 }
79}
80#[proc_macro]
82pub fn s_assemble(input: TokenStream) -> TokenStream {
83 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
84 let name = i.to_string();
85 let lower = name.to_lowercase();
86 format!(
87 "
88 if operands.len() != 2 {{
89 Err(\"{lower} instruction requires 2 operands\".to_owned())
90 }} else {{
91 let (base, offset) = parse_address_expression(operands[1])?;
92 Ok(Instruction::{name}{{
93 src: IRegister::try_from(operands[0])?,
94 base,
95 offset: SImmediate::try_from(offset)?,
96 }})
97 }}"
98 )
99 .parse()
100 .unwrap()
101 } else {
102 panic!("expected identifier");
103 }
104}
105
106#[proc_macro]
108pub fn b_assemble(input: TokenStream) -> TokenStream {
109 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
119 let name = i.to_string();
120 let lower = name.to_lowercase();
121 format!(
122 "
123 if operands.len() != 3 {{
124 Err(\"{lower} instruction requires 3 operands\".to_owned())
125 }} else {{
126 Ok(Instruction::{name}{{
127 src1: IRegister::try_from(operands[0])?,
128 src2: IRegister::try_from(operands[1])?,
129 offset: BImmediate::try_from(parse_int(operands[2])?)?,
130 }})
131 }}"
132 )
133 .parse()
134 .unwrap()
135 } else {
136 panic!("expected identifier");
137 }
138}
139
140#[proc_macro]
142pub fn sh_assemble(input: TokenStream) -> TokenStream {
143 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
144 let name = i.to_string();
145 let lower = name.to_lowercase();
146 format!(
147 "
148 if operands.len() != 3 {{
149 Err(\"{lower} instruction requires 3 operands\".to_owned())
150 }} else {{
151 Ok(Instruction::{name}{{
152 dest: IRegister::try_from(operands[0])?,
153 src: IRegister::try_from(operands[1])?,
154 shamt: Shamt::try_from(parse_int(operands[2])?)?,
155 }})
156 }}"
157 )
158 .parse()
159 .unwrap()
160 } else {
161 panic!("expected identifier");
162 }
163}
164
165#[proc_macro]
167pub fn shw_assemble(input: TokenStream) -> TokenStream {
168 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
169 let name = i.to_string();
170 let lower = name.to_lowercase();
171 format!(
172 "
173 if operands.len() != 3 {{
174 Err(\"{lower} instruction requires 3 operands\".to_owned())
175 }} else {{
176 Ok(Instruction::{name}{{
177 dest: IRegister::try_from(operands[0])?,
178 src: IRegister::try_from(operands[1])?,
179 shamt: ShamtW::try_from(parse_int(operands[2])?)?,
180 }})
181 }}"
182 )
183 .parse()
184 .unwrap()
185 } else {
186 panic!("expected identifier");
187 }
188}
189
190#[proc_macro]
191pub fn amo_assemble(input: TokenStream) -> TokenStream {
192 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
193 let name = i.to_string();
194 let lower = name.to_lowercase();
195 let dname = name.clone() + "D";
196 let wname = name.clone() + "W";
197 let p = format!(
198 "
199 if mnemonics.len() == 1 {{
200 Err(\"{lower} must have size (w/d)\".to_owned())
201 }} else if mnemonics.len() == 2 {{
202 if mnemonics[1] == \"w\" {{
203 Ok(Instruction::{wname}{{
204 dest: IRegister::try_from(operands[0])?,
205 addr: IRegister::try_from(operands[1])?,
206 src: IRegister::try_from(operands[2])?,
207 rl: false,
208 aq: false,
209 }})
210 }} else if mnemonics[1] == \"d\" {{
211 Ok(Instruction::{dname}{{
212 dest: IRegister::try_from(operands[0])?,
213 addr: IRegister::try_from(operands[1])?,
214 src: IRegister::try_from(operands[2])?,
215 rl: false,
216 aq: false,
217 }})
218 }} else {{
219 Err(\"size of {lower} instruction must be word (w) or doubleword (d)\".to_owned())
220 }}
221 }} else if mnemonics.len() == 3 {{
222 let (aq, rl) = match mnemonics[2] {{
223 \"\" => (false, false),
224 \"aq\" => (true, false),
225 \"rl\" => (false, true),
226 \"aqrl\" => (true, true),
227 _ => return Err(\"ordering should be (aq)(rl)\".to_owned()),
228 }};
229 if mnemonics[1] == \"w\" {{
230 Ok(Instruction::{wname}{{
231 dest: IRegister::try_from(operands[0])?,
232 addr: IRegister::try_from(operands[1])?,
233 src: IRegister::try_from(operands[2])?,
234 aq,
235 rl,
236 }})
237 }} else if mnemonics[1] == \"d\" {{
238 Ok(Instruction::{dname}{{
239 dest: IRegister::try_from(operands[0])?,
240 addr: IRegister::try_from(operands[1])?,
241 src: IRegister::try_from(operands[2])?,
242 aq,
243 rl,
244 }})
245 }} else {{
246 Err(\"size of {lower} isntruction must be word (w) or doubleword (d)\".to_owned())
247 }}
248 }} else {{
249 Err(\"{lower} instruction has too many suffixes, expected {lower}.size.ordering\".to_owned())
250 }}
251 "
252 );
253 match p.parse() {
254 Ok(t) => t,
255 Err(_) => "".parse().unwrap(),
256 }
257 } else {
258 panic!("expected identifier");
259 }
260}
261
262#[proc_macro]
264pub fn fr_assemble(input: TokenStream) -> TokenStream {
265 let mut i = input.into_iter();
266 if let TokenTree::Ident(n) = i.next().unwrap()
267 && let Ok(rm) = BoolLit::try_from(i.next().unwrap())
268 {
269 let name = n.to_string();
270 let sname = name.clone() + "S";
271 let dname = name.clone() + "D";
272 let lower = name.to_lowercase();
273 let rm = rm.value();
274
275 let rm_str = if rm {
276 format!(
277 "let rm = if operands.len() == 3 {{
278 RoundingMode::DYN
279 }}else if operands.len() == 4 {{
280 RoundingMode::from_str(operands[3])?
281 }}else {{
282 return Err(\"{lower} instruction requires 3 or 4 operands\".to_owned());
283 }};"
284 )
285 } else {
286 format!(
287 "
288 if operands.len() != 3 {{
289 return Err(\"{lower} instruction requires 3 operands\".to_owned());
290 }}
291 "
292 )
293 };
294
295 format!(
296 "
297 {{
298 {rm_str}
299
300 match mnemonics.get(1) {{
301 Some(&\"s\") => Ok(Instruction::{sname} {{
302 dest: FRegister::try_from(operands[0])?,
303 src1: FRegister::try_from(operands[1])?,
304 src2: FRegister::try_from(operands[2])?,
305 {}
306 }}),
307 Some(&\"d\") => Ok(Instruction::{dname} {{
308 dest: FRegister::try_from(operands[0])?,
309 src1: FRegister::try_from(operands[1])?,
310 src2: FRegister::try_from(operands[2])?,
311 {}
312 }}),
313 Some(_) => Err(\"{lower} instructions requires prefix {{s,d}}\".to_owned()),
314 None => Err(\"{lower} instructions requires prefix {{s,d}}\".to_owned()),
315 }}
316 }}
317 ",
318 if rm { "rm," } else { "" },
319 if rm { "rm," } else { "" },
320 )
321 .parse()
322 .unwrap()
323 } else {
324 panic!("expected identifier");
325 }
326}
327
328#[proc_macro]
330pub fn fr3_assemble(input: TokenStream) -> TokenStream {
331 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
332 let name = i.to_string();
333 let sname = name.clone() + "S";
334 let dname = name.clone() + "D";
335 let lower = name.to_lowercase();
336 format!(
337 "
338 {{
339 let rm = if operands.len() == 4 {{
340 RoundingMode::DYN
341 }}else if operands.len() == 5 {{
342 RoundingMode::from_str(operands[4])?
343 }}else {{
344 return Err(\"{lower} instruction requires 4 or 5 operands\".to_owned());
345 }};
346
347 match mnemonics.get(1) {{
348 Some(&\"s\") => Ok(Instruction::{sname} {{
349 dest: FRegister::try_from(operands[0])?,
350 src1: FRegister::try_from(operands[1])?,
351 src2: FRegister::try_from(operands[2])?,
352 src3: FRegister::try_from(operands[3])?,
353 rm,
354 }}),
355 Some(&\"d\") => Ok(Instruction::{dname} {{
356 dest: FRegister::try_from(operands[0])?,
357 src1: FRegister::try_from(operands[1])?,
358 src2: FRegister::try_from(operands[2])?,
359 src3: FRegister::try_from(operands[3])?,
360 rm,
361 }}),
362 Some(_) => Err(\"{lower} instructions requires prefix {{s,d}}\".to_owned()),
363 None => Err(\"{lower} instructions requires prefix {{s,d}}\".to_owned()),
364 }}
365 }}
366 "
367 )
368 .parse()
369 .unwrap()
370 } else {
371 panic!("expected identifier");
372 }
373}
374
375#[proc_macro]
377pub fn ci_assemble(input: TokenStream) -> TokenStream {
378 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
379 let name = i.to_string();
380 let lower = name.to_lowercase();
381 format!(
382 "
383 if operands.len() != 2 {{
384 Err(\"c.{lower} instruction requires 2 operands\".to_owned())
385 }} else {{
386 Ok(CInstruction::{name}{{
387 dest: IRegister::try_from(operands[0])?,
388 imm: CIImmediate::try_from(parse_int(operands[1])?)?,
389 }})
390 }}"
391 )
392 .parse()
393 .unwrap()
394 } else {
395 panic!("expected identifier");
396 }
397}
398
399#[proc_macro]
401pub fn cr_assemble(input: TokenStream) -> TokenStream {
402 if let TokenTree::Ident(i) = input.into_iter().next().unwrap() {
403 let name = i.to_string();
404 let lower = name.to_lowercase();
405 format!(
406 "
407 if operands.len() != 2 {{
408 Err(\"c.{lower} instruction requires 2 operands\".to_owned())
409 }} else {{
410 Ok(CInstruction::{name}{{
411 dest: CIRegister::try_from(operands[0])?,
412 src: CIRegister::try_from(operands[1])?,
413 }})
414 }}"
415 )
416 .parse()
417 .unwrap()
418 } else {
419 panic!("expected identifier");
420 }
421}
422
423#[derive(Clone, Debug, Copy)]
425struct ImmPart {
426 base: u8,
428 size: u8,
430 location: u8,
432}
433
434impl ImmPart {
435 pub fn from_token_tree(input: TokenTree) -> Self {
436 if let TokenTree::Group(g) = input {
437 if g.delimiter() != Delimiter::Parenthesis {
438 panic!("group delimiter should be Parenthesis");
439 }
440 let t: Vec<TokenTree> = g.stream().into_iter().collect();
441 if t.len() != 3 {
442 panic!("group should have 3 elements has {}", t.len());
443 }
444 let base: u8 = IntegerLit::try_from(t[0].clone()).unwrap().value().unwrap();
445 let size: u8 = IntegerLit::try_from(t[1].clone()).unwrap().value().unwrap();
446 let location: u8 = IntegerLit::try_from(t[2].clone()).unwrap().value().unwrap();
447 ImmPart {
448 base,
449 size,
450 location,
451 }
452 } else {
453 panic!("expected group got {}", input.to_string());
454 }
455 }
456
457 pub fn extract_instruction(&self, index: usize, input: &str) -> String {
459 format!(
460 "let part{index} = ({input} >> {}) & ((1 << {}) - 1);\n",
461 self.location, self.size
462 )
463 }
464
465 pub fn insert_immediate(&self, index: usize) -> String {
467 if index == 0 {
468 format!("(part{index} << {})", self.base)
469 } else {
470 format!(" | (part{index} << {})", self.base)
471 }
472 }
473
474 pub fn extract_immediate(&self, index: usize, input: &str) -> String {
476 format!(
477 "let part{index} = ({input} >> {}) & ((1 << {}) - 1);\n",
478 self.base, self.size
479 )
480 }
481
482 pub fn insert_instruction(&self, index: usize) -> String {
484 if index == 0 {
485 format!("(part{index} << {})", self.location)
486 } else {
487 format!(" | (part{index} << {})", self.location)
488 }
489 }
490}
491
492#[proc_macro]
493pub fn make_immediate(input: TokenStream) -> TokenStream {
494 let mut i = input.into_iter();
495 if let TokenTree::Ident(name) = i.next().unwrap()
496 && let Ok(signed) = BoolLit::try_from(i.next().unwrap())
497 && let Ok(compressed) = BoolLit::try_from(i.next().unwrap())
498 {
499 let name = name.to_string();
500 let signed = signed.value();
501 let compressed = compressed.value();
502
503 let parts: Vec<ImmPart> = i.map(|t| ImmPart::from_token_tree(t)).collect();
504
505 let align: u8 = parts.iter().map(|part| part.base).min().unwrap();
506 let align_pattern = (1 << align) - 1;
507 let size: u8 = parts
508 .iter()
509 .map(|part| part.base + part.size)
510 .max()
511 .unwrap();
512
513 let typ = match (signed, size > 16) {
516 (true, true) => "i32",
517 (false, true) => "u32",
518 (true, false) => "i16",
519 (false, false) => "u16",
520 };
521 let instr_typ = if compressed { "u16" } else { "u32" };
523
524 let struct_string = format!(
525 "
526 #[derive(Debug, PartialEq, Copy, Clone)]
527 pub struct {name} {{
528 val: {typ},
529 }}
530 "
531 );
532
533 let bounds_condition = if signed {
534 format!(
535 "value > 2i64.pow({}) - 1 || value < -2i64.pow({})",
536 size - 1,
537 size - 1
538 )
539 } else {
540 format!("value > 2i64.pow({}) - 1 || value < 0", size)
541 };
542
543 let impl_string = format!(
544 "
545 impl TryFrom<i64> for {name} {{
546 type Error = String;
547
548 fn try_from(value: i64) -> Result<Self, Self::Error> {{
549 if {bounds_condition} {{
550 Err(format!(\"attempted to construct out of range {name}\"))
551 }}else if value & {align_pattern} != 0 {{
552 Err(format!(\"attempted to construct unaligned {name}\"))
553 }}else {{
554 Ok({name} {{ val: value as {typ} }})
555 }}
556 }}
557 }}
558
559 impl Into<i64> for {name} {{
560 fn into(self) -> i64 {{
561 self.val.into()
562 }}
563 }}
564
565 impl {name} {{
566 pub fn val(self) -> i64 {{
567 return self.into()
568 }}
569 }}
570
571 "
572 );
573
574 let extract_fn = {
575 let extractions: String = parts
576 .iter()
577 .enumerate()
578 .map(|(i, part)| part.extract_instruction(i, "x"))
579 .collect();
580
581 let insertions: String = parts
582 .iter()
583 .enumerate()
584 .map(|(i, part)| part.insert_immediate(i))
585 .collect();
586
587 let insert = format!("let i: {typ} = ({insertions}) as {typ};");
588
589 let sign_extension = if signed {
590 if typ == "i16" {
591 format!("let i2: i16 = (i << {}) >> {};", 16 - size, 16 - size)
592 }else {
593 format!("let i2: i32 = (i << {}) >> {};", 32 - size, 32 - size)
594 }
595
596 } else {
597 format!("let i2: {typ} = i as {typ};")
598 };
599
600 let ret = format!("{name} {{ val: i2}}");
601
602 if compressed {
603 format!(
604 "
605 impl {name} {{
606 pub fn from_u16(x: u16) -> Self {{
607 {extractions}
608 {insert}
609 {sign_extension}
610 {ret}
611 }}
612 }}
613 "
614 )
615 } else {
616 format!(
617 "
618 impl {name} {{
619 pub fn from_u32(x: u32) -> Self {{
620 {extractions}
621 {insert}
622 {sign_extension}
623 {ret}
624 }}
625 }}
626 "
627 )
628 }
629 };
630
631 let insert_fn = {
632 let extractions: String = parts
633 .iter()
634 .enumerate()
635 .map(|(i, part)| part.extract_immediate(i, "x"))
636 .collect();
637
638 let insertions: String = parts
639 .iter()
640 .enumerate()
641 .map(|(i, part)| part.insert_instruction(i))
642 .collect();
643
644 let ret = format!("return ({insertions}) as {instr_typ};");
645
646 format!(
647 "
648 impl {name} {{
649 pub fn to_{instr_typ}(&self) -> {instr_typ} {{
650 let x: {instr_typ} = (self.val() & ((1 << {size}) - 1)) as {instr_typ};
651 {extractions}
652 {ret}
653 }}
654 }}
655 "
656 )
657 };
658
659 let display_string = format!(
660 "
661 impl Display for {name} {{
662 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), alloc::fmt::Error> {{
663 write!(f, \"{{}}\", self.val)
664 }}
665 }}"
666 );
667
668 let final_str = format!(
669 "
670 {struct_string}
671 {impl_string}
672 {extract_fn}
673 {insert_fn}
674 {display_string}
675 "
676 );
677 final_str.parse().unwrap()
680 } else {
681 panic!("first token should be an Identifier")
682 }
683}