riscv_codec_proc_macros/
lib.rs

1extern crate proc_macro;
2use litrs::{BoolLit, IntegerLit};
3use proc_macro::{Delimiter, TokenStream, TokenTree};
4
5/// Assembles an i-type instruction
6#[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/// Assembles an r-type instruction
31#[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/// Assembles a load type instruction
56#[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/// Assembles a store instruction
81#[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/// Assembles a branc instruction
107#[proc_macro]
108pub fn b_assemble(input: TokenStream) -> TokenStream {
109    // if operands.len() != 3 {
110    //     Err("blt instruction requires 3 operands".to_owned())
111    // } else {
112    //     Ok(Instruction::BLT(
113    //         IRegister::try_from(operands[0])?,
114    //         IRegister::try_from(operands[1])?,
115    //         parse_int(operands[2])? as i16,
116    //     ))
117    // }
118    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/// Assembles a shift immediate instruction
141#[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/// Assembles a shift immediate word instruction
166#[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// assembles an fr type instruction
263#[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// assembles an fr3 type instruction
329#[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/// Assembles a ci-type instruction
376#[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/// Assembles a cr-type instruction
400#[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// part of an immediate
424#[derive(Clone, Debug, Copy)]
425struct ImmPart {
426    /// the beginning index of this part in the immediate value
427    base: u8,
428    /// the size of this immediate part
429    size: u8,
430    /// the location of this part in the *instruction*
431    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    /// generates the code to extract this immediate part from an instruction
458    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    /// generates the code to insert this immediate part into an immediate value
466    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    /// generates the code to extract this immediate part from an immediate value
475    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    /// generates the code to insert this immediate part into an instruction value
483    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        // the type to use for the immediate
514        // let typ = if signed { "i32" } else { "u32" };
515        let typ = match (signed, size > 16) {
516            (true, true) => "i32",
517            (false, true) => "u32",
518            (true, false) => "i16",
519            (false, false) => "u16",
520        };
521        // the type to use for the instruction
522        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        // println!("{}", final_str);
678
679        final_str.parse().unwrap()
680    } else {
681        panic!("first token should be an Identifier")
682    }
683}