1*61046927SAndroid Build Coastguard Worker // Copyright © 2024 Igalia S.L.
2*61046927SAndroid Build Coastguard Worker // SPDX-License-Identifier: MIT
3*61046927SAndroid Build Coastguard Worker
4*61046927SAndroid Build Coastguard Worker use crate::isa::{BitSetEnumValue, ISA};
5*61046927SAndroid Build Coastguard Worker
6*61046927SAndroid Build Coastguard Worker use proc_macro::TokenStream;
7*61046927SAndroid Build Coastguard Worker use proc_macro2::TokenStream as TokenStream2;
8*61046927SAndroid Build Coastguard Worker use quote::quote;
9*61046927SAndroid Build Coastguard Worker use quote::ToTokens;
10*61046927SAndroid Build Coastguard Worker use roxmltree::Document;
11*61046927SAndroid Build Coastguard Worker use std::fs;
12*61046927SAndroid Build Coastguard Worker use std::path::Path;
13*61046927SAndroid Build Coastguard Worker use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, Expr, ExprLit, Lit, Meta};
14*61046927SAndroid Build Coastguard Worker
15*61046927SAndroid Build Coastguard Worker mod isa;
16*61046927SAndroid Build Coastguard Worker
17*61046927SAndroid Build Coastguard Worker /// Parses the derive input to extract file paths from attributes
18*61046927SAndroid Build Coastguard Worker ///
19*61046927SAndroid Build Coastguard Worker /// # Returns
20*61046927SAndroid Build Coastguard Worker /// A tuple containing the paths to the ISA and static rules files
21*61046927SAndroid Build Coastguard Worker ///
22*61046927SAndroid Build Coastguard Worker /// # Panics
23*61046927SAndroid Build Coastguard Worker /// Panics if the necessary attributes are not found or are in the wrong format
parse_derive(ast: &DeriveInput) -> (String, String)24*61046927SAndroid Build Coastguard Worker pub(crate) fn parse_derive(ast: &DeriveInput) -> (String, String) {
25*61046927SAndroid Build Coastguard Worker // Collect attributes with the name "isa"
26*61046927SAndroid Build Coastguard Worker let isa_attr = ast
27*61046927SAndroid Build Coastguard Worker .attrs
28*61046927SAndroid Build Coastguard Worker .iter()
29*61046927SAndroid Build Coastguard Worker .find(|attr| {
30*61046927SAndroid Build Coastguard Worker let path = attr.meta.path();
31*61046927SAndroid Build Coastguard Worker path.is_ident("isa")
32*61046927SAndroid Build Coastguard Worker })
33*61046927SAndroid Build Coastguard Worker .expect("An ISA file needs to be provided with the #[isa = \"PATH\"] attribute");
34*61046927SAndroid Build Coastguard Worker
35*61046927SAndroid Build Coastguard Worker // Get the path from the "isa" attribute
36*61046927SAndroid Build Coastguard Worker let isa_path = get_attribute(isa_attr);
37*61046927SAndroid Build Coastguard Worker
38*61046927SAndroid Build Coastguard Worker // Collect attributes with the name "static_rules_file"
39*61046927SAndroid Build Coastguard Worker let static_rules_attr = ast
40*61046927SAndroid Build Coastguard Worker .attrs
41*61046927SAndroid Build Coastguard Worker .iter()
42*61046927SAndroid Build Coastguard Worker .find(|attr| {
43*61046927SAndroid Build Coastguard Worker let path = attr.meta.path();
44*61046927SAndroid Build Coastguard Worker path.is_ident("static_rules_file")
45*61046927SAndroid Build Coastguard Worker })
46*61046927SAndroid Build Coastguard Worker .expect("A static pest rules file needs to be provided with the #[static_rules_file = \"PATH\"] attribute");
47*61046927SAndroid Build Coastguard Worker
48*61046927SAndroid Build Coastguard Worker // Get the path from the "static_rules_file" attribute
49*61046927SAndroid Build Coastguard Worker let static_rules_path = get_attribute(static_rules_attr);
50*61046927SAndroid Build Coastguard Worker
51*61046927SAndroid Build Coastguard Worker (isa_path, static_rules_path)
52*61046927SAndroid Build Coastguard Worker }
53*61046927SAndroid Build Coastguard Worker
54*61046927SAndroid Build Coastguard Worker /// Extracts the string value from a name-value attribute
55*61046927SAndroid Build Coastguard Worker ///
56*61046927SAndroid Build Coastguard Worker /// # Panics
57*61046927SAndroid Build Coastguard Worker /// Panics if the attribute is not in the expected format
get_attribute(attr: &Attribute) -> String58*61046927SAndroid Build Coastguard Worker fn get_attribute(attr: &Attribute) -> String {
59*61046927SAndroid Build Coastguard Worker match &attr.meta {
60*61046927SAndroid Build Coastguard Worker Meta::NameValue(name_value) => match &name_value.value {
61*61046927SAndroid Build Coastguard Worker Expr::Lit(ExprLit {
62*61046927SAndroid Build Coastguard Worker lit: Lit::Str(string),
63*61046927SAndroid Build Coastguard Worker ..
64*61046927SAndroid Build Coastguard Worker }) => {
65*61046927SAndroid Build Coastguard Worker if name_value.path.is_ident("isa") || name_value.path.is_ident("static_rules_file")
66*61046927SAndroid Build Coastguard Worker {
67*61046927SAndroid Build Coastguard Worker string.value()
68*61046927SAndroid Build Coastguard Worker } else {
69*61046927SAndroid Build Coastguard Worker panic!("Attribute must be a file path")
70*61046927SAndroid Build Coastguard Worker }
71*61046927SAndroid Build Coastguard Worker }
72*61046927SAndroid Build Coastguard Worker _ => panic!("Attribute must be a string"),
73*61046927SAndroid Build Coastguard Worker },
74*61046927SAndroid Build Coastguard Worker _ => panic!("Attribute must be of the form `key = \"...\"`"),
75*61046927SAndroid Build Coastguard Worker }
76*61046927SAndroid Build Coastguard Worker }
77*61046927SAndroid Build Coastguard Worker
78*61046927SAndroid Build Coastguard Worker /// Formats an enum value as a string in uppercase with underscores
format_enum_value_str(enum_name: &str, enum_value: &str) -> String79*61046927SAndroid Build Coastguard Worker fn format_enum_value_str(enum_name: &str, enum_value: &str) -> String {
80*61046927SAndroid Build Coastguard Worker format!("{}_{}", enum_name, enum_value.replace(['.', '[', ']'], "")).to_ascii_uppercase()
81*61046927SAndroid Build Coastguard Worker }
82*61046927SAndroid Build Coastguard Worker
83*61046927SAndroid Build Coastguard Worker /// Retrieves and formats the enum value string from a `BitSetEnumValue`
get_enum_value_str(enum_name: &str, enum_value: &BitSetEnumValue) -> String84*61046927SAndroid Build Coastguard Worker fn get_enum_value_str(enum_name: &str, enum_value: &BitSetEnumValue) -> String {
85*61046927SAndroid Build Coastguard Worker format_enum_value_str(enum_name, enum_value.name.unwrap_or(enum_value.display))
86*61046927SAndroid Build Coastguard Worker }
87*61046927SAndroid Build Coastguard Worker
88*61046927SAndroid Build Coastguard Worker /// Generates the implementation of `FromPestRule` for enums in the ISA
generate_from_rule_impl_enums(isa: &ISA) -> TokenStream289*61046927SAndroid Build Coastguard Worker fn generate_from_rule_impl_enums(isa: &ISA) -> TokenStream2 {
90*61046927SAndroid Build Coastguard Worker isa.enums
91*61046927SAndroid Build Coastguard Worker .values()
92*61046927SAndroid Build Coastguard Worker .map(|e| {
93*61046927SAndroid Build Coastguard Worker let enum_name_str = format!("isa_{}", e.name.trim_start_matches('#'));
94*61046927SAndroid Build Coastguard Worker
95*61046927SAndroid Build Coastguard Worker let enum_name = syn::Ident::new(&enum_name_str, proc_macro2::Span::call_site());
96*61046927SAndroid Build Coastguard Worker let match_arms: Vec<_> = e
97*61046927SAndroid Build Coastguard Worker .values
98*61046927SAndroid Build Coastguard Worker .iter()
99*61046927SAndroid Build Coastguard Worker .filter(|v| !v.display.is_empty() && v.display != ".____")
100*61046927SAndroid Build Coastguard Worker .map(|v| {
101*61046927SAndroid Build Coastguard Worker let variant_name = syn::Ident::new(
102*61046927SAndroid Build Coastguard Worker get_enum_value_str(&enum_name_str, v).as_str(),
103*61046927SAndroid Build Coastguard Worker proc_macro2::Span::call_site(),
104*61046927SAndroid Build Coastguard Worker );
105*61046927SAndroid Build Coastguard Worker let rule_name = syn::Ident::new(
106*61046927SAndroid Build Coastguard Worker &to_upper_camel_case(v.name.unwrap_or(v.display), false),
107*61046927SAndroid Build Coastguard Worker proc_macro2::Span::call_site(),
108*61046927SAndroid Build Coastguard Worker );
109*61046927SAndroid Build Coastguard Worker quote! { Rule::#rule_name => #enum_name::#variant_name }
110*61046927SAndroid Build Coastguard Worker })
111*61046927SAndroid Build Coastguard Worker .collect();
112*61046927SAndroid Build Coastguard Worker
113*61046927SAndroid Build Coastguard Worker quote! {
114*61046927SAndroid Build Coastguard Worker impl FromPestRule for #enum_name {
115*61046927SAndroid Build Coastguard Worker fn from_rule(rule: Rule) -> Self where Self: Sized {
116*61046927SAndroid Build Coastguard Worker match rule {
117*61046927SAndroid Build Coastguard Worker #(#match_arms),*,
118*61046927SAndroid Build Coastguard Worker _ => panic!("Unexpected rule: {:?}", rule),
119*61046927SAndroid Build Coastguard Worker }
120*61046927SAndroid Build Coastguard Worker }
121*61046927SAndroid Build Coastguard Worker }
122*61046927SAndroid Build Coastguard Worker }
123*61046927SAndroid Build Coastguard Worker })
124*61046927SAndroid Build Coastguard Worker .collect()
125*61046927SAndroid Build Coastguard Worker }
126*61046927SAndroid Build Coastguard Worker
127*61046927SAndroid Build Coastguard Worker /// Generates the implementation of `FromPestRule` for ISA opcodes
generate_from_rule_impl_opc(isa: &ISA) -> TokenStream2128*61046927SAndroid Build Coastguard Worker fn generate_from_rule_impl_opc(isa: &ISA) -> TokenStream2 {
129*61046927SAndroid Build Coastguard Worker let instr_name = syn::Ident::new("isa_opc", proc_macro2::Span::call_site());
130*61046927SAndroid Build Coastguard Worker
131*61046927SAndroid Build Coastguard Worker let match_arms: Vec<_> = isa
132*61046927SAndroid Build Coastguard Worker .bitsets
133*61046927SAndroid Build Coastguard Worker .values()
134*61046927SAndroid Build Coastguard Worker .filter(|bitset| !bitset.name.starts_with('#'))
135*61046927SAndroid Build Coastguard Worker .map(|instr| {
136*61046927SAndroid Build Coastguard Worker let variant_name = syn::Ident::new(
137*61046927SAndroid Build Coastguard Worker format_enum_value_str("isa_opc", instr.name).as_str(),
138*61046927SAndroid Build Coastguard Worker proc_macro2::Span::call_site(),
139*61046927SAndroid Build Coastguard Worker );
140*61046927SAndroid Build Coastguard Worker
141*61046927SAndroid Build Coastguard Worker let pest_rule = format!("Opc_{}", instr.name);
142*61046927SAndroid Build Coastguard Worker
143*61046927SAndroid Build Coastguard Worker let rule_name = syn::Ident::new(
144*61046927SAndroid Build Coastguard Worker &to_upper_camel_case(pest_rule.as_str(), true),
145*61046927SAndroid Build Coastguard Worker proc_macro2::Span::call_site(),
146*61046927SAndroid Build Coastguard Worker );
147*61046927SAndroid Build Coastguard Worker quote! { Rule::#rule_name => #instr_name::#variant_name }
148*61046927SAndroid Build Coastguard Worker })
149*61046927SAndroid Build Coastguard Worker .collect();
150*61046927SAndroid Build Coastguard Worker
151*61046927SAndroid Build Coastguard Worker quote! {
152*61046927SAndroid Build Coastguard Worker impl FromPestRule for isa_opc {
153*61046927SAndroid Build Coastguard Worker fn from_rule(rule: Rule) -> Self where Self: Sized {
154*61046927SAndroid Build Coastguard Worker match rule {
155*61046927SAndroid Build Coastguard Worker #(#match_arms),*,
156*61046927SAndroid Build Coastguard Worker _ => panic!("Unexpected rule: {:?}", rule),
157*61046927SAndroid Build Coastguard Worker }
158*61046927SAndroid Build Coastguard Worker }
159*61046927SAndroid Build Coastguard Worker }
160*61046927SAndroid Build Coastguard Worker }
161*61046927SAndroid Build Coastguard Worker }
162*61046927SAndroid Build Coastguard Worker
163*61046927SAndroid Build Coastguard Worker /// Main derive function to generate the parser
derive_parser(input: TokenStream) -> TokenStream164*61046927SAndroid Build Coastguard Worker fn derive_parser(input: TokenStream) -> TokenStream {
165*61046927SAndroid Build Coastguard Worker let mut ast: DeriveInput = parse_macro_input!(input as DeriveInput);
166*61046927SAndroid Build Coastguard Worker let root = "../src/etnaviv/isa/";
167*61046927SAndroid Build Coastguard Worker let (isa_filename, static_rules_filename) = parse_derive(&ast);
168*61046927SAndroid Build Coastguard Worker let isa_path = Path::new(&root).join(isa_filename);
169*61046927SAndroid Build Coastguard Worker let static_rules_path = Path::new(&root).join(static_rules_filename);
170*61046927SAndroid Build Coastguard Worker
171*61046927SAndroid Build Coastguard Worker // Load the XML document
172*61046927SAndroid Build Coastguard Worker let xml_content = fs::read_to_string(isa_path).expect("Failed to read XML file");
173*61046927SAndroid Build Coastguard Worker let doc = Document::parse(&xml_content).expect("Failed to parse XML");
174*61046927SAndroid Build Coastguard Worker let isa = ISA::new(&doc);
175*61046927SAndroid Build Coastguard Worker
176*61046927SAndroid Build Coastguard Worker // Load the static rules
177*61046927SAndroid Build Coastguard Worker let mut grammar =
178*61046927SAndroid Build Coastguard Worker fs::read_to_string(static_rules_path).expect("Failed to read static rules pest file");
179*61046927SAndroid Build Coastguard Worker
180*61046927SAndroid Build Coastguard Worker // Append generated grammar rules
181*61046927SAndroid Build Coastguard Worker grammar.push_str(&generate_peg_grammar(&isa));
182*61046927SAndroid Build Coastguard Worker
183*61046927SAndroid Build Coastguard Worker // Add grammar as an attribute to the AST
184*61046927SAndroid Build Coastguard Worker ast.attrs.push(parse_quote! {
185*61046927SAndroid Build Coastguard Worker #[grammar_inline = #grammar]
186*61046927SAndroid Build Coastguard Worker });
187*61046927SAndroid Build Coastguard Worker
188*61046927SAndroid Build Coastguard Worker // Generate the token streams for the parser, trait, and rule implementations
189*61046927SAndroid Build Coastguard Worker let tokens_parser = pest_generator::derive_parser(ast.to_token_stream(), false);
190*61046927SAndroid Build Coastguard Worker let tokens_from_rule_enums = generate_from_rule_impl_enums(&isa);
191*61046927SAndroid Build Coastguard Worker let tokens_from_rule_opc = generate_from_rule_impl_opc(&isa);
192*61046927SAndroid Build Coastguard Worker
193*61046927SAndroid Build Coastguard Worker // Combine all token streams into one
194*61046927SAndroid Build Coastguard Worker let tokens = quote! {
195*61046927SAndroid Build Coastguard Worker #tokens_parser
196*61046927SAndroid Build Coastguard Worker
197*61046927SAndroid Build Coastguard Worker pub trait FromPestRule {
198*61046927SAndroid Build Coastguard Worker fn from_rule(rule: Rule) -> Self where Self: Sized;
199*61046927SAndroid Build Coastguard Worker }
200*61046927SAndroid Build Coastguard Worker
201*61046927SAndroid Build Coastguard Worker #tokens_from_rule_enums
202*61046927SAndroid Build Coastguard Worker #tokens_from_rule_opc
203*61046927SAndroid Build Coastguard Worker };
204*61046927SAndroid Build Coastguard Worker
205*61046927SAndroid Build Coastguard Worker tokens.into()
206*61046927SAndroid Build Coastguard Worker }
207*61046927SAndroid Build Coastguard Worker
208*61046927SAndroid Build Coastguard Worker /// Generates PEG grammar rules for enums
generate_peg_grammar_enums(isa: &ISA) -> String209*61046927SAndroid Build Coastguard Worker fn generate_peg_grammar_enums(isa: &ISA) -> String {
210*61046927SAndroid Build Coastguard Worker let mut grammar = String::new();
211*61046927SAndroid Build Coastguard Worker
212*61046927SAndroid Build Coastguard Worker for e in isa.enums.values() {
213*61046927SAndroid Build Coastguard Worker let mut values: Vec<_> = e
214*61046927SAndroid Build Coastguard Worker .values
215*61046927SAndroid Build Coastguard Worker .iter()
216*61046927SAndroid Build Coastguard Worker .filter(|v| !v.display.is_empty() && v.display != ".____")
217*61046927SAndroid Build Coastguard Worker .collect();
218*61046927SAndroid Build Coastguard Worker
219*61046927SAndroid Build Coastguard Worker // From the pest docs:
220*61046927SAndroid Build Coastguard Worker // The choice operator, written as a vertical line |, is ordered. The PEG
221*61046927SAndroid Build Coastguard Worker // expression first | second means "try first; but if it fails, try second instead".
222*61046927SAndroid Build Coastguard Worker //
223*61046927SAndroid Build Coastguard Worker // We need to sort our enum to be able to parse eg th1.xxxx and t1.xxxx
224*61046927SAndroid Build Coastguard Worker values.sort_by(|a, b| b.display.cmp(a.display));
225*61046927SAndroid Build Coastguard Worker
226*61046927SAndroid Build Coastguard Worker let rule_name = to_upper_camel_case(e.name.trim_start_matches('#'), true);
227*61046927SAndroid Build Coastguard Worker
228*61046927SAndroid Build Coastguard Worker let value_names: Vec<_> = values
229*61046927SAndroid Build Coastguard Worker .iter()
230*61046927SAndroid Build Coastguard Worker .map(|enum_value| {
231*61046927SAndroid Build Coastguard Worker to_upper_camel_case(enum_value.name.unwrap_or(enum_value.display), false)
232*61046927SAndroid Build Coastguard Worker })
233*61046927SAndroid Build Coastguard Worker .collect();
234*61046927SAndroid Build Coastguard Worker
235*61046927SAndroid Build Coastguard Worker grammar.push_str(&format!(
236*61046927SAndroid Build Coastguard Worker "{} = {{ {} }}\n",
237*61046927SAndroid Build Coastguard Worker rule_name,
238*61046927SAndroid Build Coastguard Worker value_names.join(" | ")
239*61046927SAndroid Build Coastguard Worker ));
240*61046927SAndroid Build Coastguard Worker
241*61046927SAndroid Build Coastguard Worker for value in &values {
242*61046927SAndroid Build Coastguard Worker let variant_name = to_upper_camel_case(value.name.unwrap_or(value.display), false);
243*61046927SAndroid Build Coastguard Worker grammar.push_str(&format!(
244*61046927SAndroid Build Coastguard Worker " {} = {{ \"{}\" }}\n",
245*61046927SAndroid Build Coastguard Worker variant_name, value.display
246*61046927SAndroid Build Coastguard Worker ));
247*61046927SAndroid Build Coastguard Worker }
248*61046927SAndroid Build Coastguard Worker
249*61046927SAndroid Build Coastguard Worker grammar.push('\n')
250*61046927SAndroid Build Coastguard Worker }
251*61046927SAndroid Build Coastguard Worker
252*61046927SAndroid Build Coastguard Worker grammar
253*61046927SAndroid Build Coastguard Worker }
254*61046927SAndroid Build Coastguard Worker
255*61046927SAndroid Build Coastguard Worker /// Generates PEG grammar rules for instructions
generate_peg_grammar_instructions(isa: &ISA) -> String256*61046927SAndroid Build Coastguard Worker fn generate_peg_grammar_instructions(isa: &ISA) -> String {
257*61046927SAndroid Build Coastguard Worker let mut grammar = String::new();
258*61046927SAndroid Build Coastguard Worker
259*61046927SAndroid Build Coastguard Worker // Collect instructions that do not start with "#"
260*61046927SAndroid Build Coastguard Worker let instructions: Vec<_> = isa
261*61046927SAndroid Build Coastguard Worker .bitsets
262*61046927SAndroid Build Coastguard Worker .values()
263*61046927SAndroid Build Coastguard Worker .filter(|bitset| !bitset.name.starts_with('#'))
264*61046927SAndroid Build Coastguard Worker .collect();
265*61046927SAndroid Build Coastguard Worker
266*61046927SAndroid Build Coastguard Worker // Generate instruction names
267*61046927SAndroid Build Coastguard Worker let instruction_names: Vec<_> = instructions
268*61046927SAndroid Build Coastguard Worker .iter()
269*61046927SAndroid Build Coastguard Worker .map(|instruction| format!("Opc{}", to_upper_camel_case(instruction.name, true)))
270*61046927SAndroid Build Coastguard Worker .collect();
271*61046927SAndroid Build Coastguard Worker
272*61046927SAndroid Build Coastguard Worker // Join instruction names and append to grammar
273*61046927SAndroid Build Coastguard Worker grammar.push_str(&format!(
274*61046927SAndroid Build Coastguard Worker "instruction = _{{ {} }}\n",
275*61046927SAndroid Build Coastguard Worker instruction_names.join(" | ")
276*61046927SAndroid Build Coastguard Worker ));
277*61046927SAndroid Build Coastguard Worker
278*61046927SAndroid Build Coastguard Worker for (instruction, opcode) in std::iter::zip(instructions, instruction_names) {
279*61046927SAndroid Build Coastguard Worker let meta = isa.collect_meta(instruction.name);
280*61046927SAndroid Build Coastguard Worker let type_ = meta.get("type").copied().unwrap_or("");
281*61046927SAndroid Build Coastguard Worker
282*61046927SAndroid Build Coastguard Worker // Prepare rule parts
283*61046927SAndroid Build Coastguard Worker let mut rule_parts = Vec::new();
284*61046927SAndroid Build Coastguard Worker rule_parts.push(format!(
285*61046927SAndroid Build Coastguard Worker "\"{}\"",
286*61046927SAndroid Build Coastguard Worker instruction.displayname.unwrap_or(instruction.name)
287*61046927SAndroid Build Coastguard Worker ));
288*61046927SAndroid Build Coastguard Worker
289*61046927SAndroid Build Coastguard Worker let template_key = format!("INSTR_{}", type_.to_ascii_uppercase());
290*61046927SAndroid Build Coastguard Worker let flags = isa
291*61046927SAndroid Build Coastguard Worker .templates
292*61046927SAndroid Build Coastguard Worker .get(template_key.as_str())
293*61046927SAndroid Build Coastguard Worker .map_or("", |template| template.display.trim());
294*61046927SAndroid Build Coastguard Worker
295*61046927SAndroid Build Coastguard Worker // Process flags
296*61046927SAndroid Build Coastguard Worker // Convert the XML string to a vec and filter out not wanted NAME.
297*61046927SAndroid Build Coastguard Worker // e.g.: {NAME}{DST_FULL}{SAT}{COND}{SKPHP}{TYPE}{PMODE}{THREAD}{RMODE} to
298*61046927SAndroid Build Coastguard Worker // ["Dst_full", "Sat", "Cond", "Skphp", "Type", "Pmode", "Thread", "Rounding"]
299*61046927SAndroid Build Coastguard Worker flags
300*61046927SAndroid Build Coastguard Worker .split(&['{', '}'])
301*61046927SAndroid Build Coastguard Worker .filter(|part| !part.trim().is_empty() && *part != "NAME")
302*61046927SAndroid Build Coastguard Worker .for_each(|part| {
303*61046927SAndroid Build Coastguard Worker let part = if part == "RMODE" { "Rounding" } else { part };
304*61046927SAndroid Build Coastguard Worker rule_parts.push(format!("{}?", to_upper_camel_case(part, false)));
305*61046927SAndroid Build Coastguard Worker });
306*61046927SAndroid Build Coastguard Worker
307*61046927SAndroid Build Coastguard Worker let has_dest = meta
308*61046927SAndroid Build Coastguard Worker .get("has_dest")
309*61046927SAndroid Build Coastguard Worker .map(|b| b.parse::<bool>())
310*61046927SAndroid Build Coastguard Worker .unwrap_or(Ok(false))
311*61046927SAndroid Build Coastguard Worker .expect("has_dest must be a bool value (true|false)");
312*61046927SAndroid Build Coastguard Worker
313*61046927SAndroid Build Coastguard Worker let rule_part = match (has_dest, type_) {
314*61046927SAndroid Build Coastguard Worker (true, "load_store") => "(Dest | DstMemAddr) ~ \",\"",
315*61046927SAndroid Build Coastguard Worker (true, _) => "Dest ~ \",\"",
316*61046927SAndroid Build Coastguard Worker (false, _) => "DestVoid ~ \",\"",
317*61046927SAndroid Build Coastguard Worker };
318*61046927SAndroid Build Coastguard Worker
319*61046927SAndroid Build Coastguard Worker rule_parts.push(rule_part.to_string());
320*61046927SAndroid Build Coastguard Worker
321*61046927SAndroid Build Coastguard Worker if type_ == "tex" {
322*61046927SAndroid Build Coastguard Worker rule_parts.push("TexSrc ~ \",\"".to_string());
323*61046927SAndroid Build Coastguard Worker }
324*61046927SAndroid Build Coastguard Worker
325*61046927SAndroid Build Coastguard Worker let possible_srcs = if type_ == "cf" { 2 } else { 3 };
326*61046927SAndroid Build Coastguard Worker let valid_srcs: Vec<_> = meta
327*61046927SAndroid Build Coastguard Worker .get("valid_srcs")
328*61046927SAndroid Build Coastguard Worker .unwrap_or(&"")
329*61046927SAndroid Build Coastguard Worker .split('|')
330*61046927SAndroid Build Coastguard Worker .filter_map(|s| s.parse::<usize>().ok())
331*61046927SAndroid Build Coastguard Worker .collect();
332*61046927SAndroid Build Coastguard Worker
333*61046927SAndroid Build Coastguard Worker for i in 0..possible_srcs {
334*61046927SAndroid Build Coastguard Worker if valid_srcs.contains(&i) {
335*61046927SAndroid Build Coastguard Worker rule_parts.push("Src".to_string());
336*61046927SAndroid Build Coastguard Worker } else {
337*61046927SAndroid Build Coastguard Worker rule_parts.push("SrcVoid".to_string());
338*61046927SAndroid Build Coastguard Worker }
339*61046927SAndroid Build Coastguard Worker if i + 1 < possible_srcs {
340*61046927SAndroid Build Coastguard Worker rule_parts.push("\",\"".to_string());
341*61046927SAndroid Build Coastguard Worker }
342*61046927SAndroid Build Coastguard Worker }
343*61046927SAndroid Build Coastguard Worker
344*61046927SAndroid Build Coastguard Worker if type_ == "cf" {
345*61046927SAndroid Build Coastguard Worker rule_parts.push("\",\"".to_string());
346*61046927SAndroid Build Coastguard Worker rule_parts.push("Target".to_string());
347*61046927SAndroid Build Coastguard Worker }
348*61046927SAndroid Build Coastguard Worker
349*61046927SAndroid Build Coastguard Worker grammar.push_str(&format!(
350*61046927SAndroid Build Coastguard Worker " {} = {{ {} }}\n",
351*61046927SAndroid Build Coastguard Worker opcode,
352*61046927SAndroid Build Coastguard Worker rule_parts.join(" ~ ")
353*61046927SAndroid Build Coastguard Worker ));
354*61046927SAndroid Build Coastguard Worker }
355*61046927SAndroid Build Coastguard Worker
356*61046927SAndroid Build Coastguard Worker grammar
357*61046927SAndroid Build Coastguard Worker }
358*61046927SAndroid Build Coastguard Worker
359*61046927SAndroid Build Coastguard Worker /// Combines the PEG grammar rules for enums and instructions
generate_peg_grammar(isa: &ISA) -> String360*61046927SAndroid Build Coastguard Worker fn generate_peg_grammar(isa: &ISA) -> String {
361*61046927SAndroid Build Coastguard Worker let mut grammar = String::new();
362*61046927SAndroid Build Coastguard Worker
363*61046927SAndroid Build Coastguard Worker grammar.push_str(&generate_peg_grammar_enums(isa));
364*61046927SAndroid Build Coastguard Worker grammar.push_str(&generate_peg_grammar_instructions(isa));
365*61046927SAndroid Build Coastguard Worker grammar.push_str("instructions = _{ SOI ~ (instruction ~ NEWLINE?)* ~ EOI }");
366*61046927SAndroid Build Coastguard Worker
367*61046927SAndroid Build Coastguard Worker grammar
368*61046927SAndroid Build Coastguard Worker }
369*61046927SAndroid Build Coastguard Worker
370*61046927SAndroid Build Coastguard Worker /// Converts a string to UpperCamelCase
371*61046927SAndroid Build Coastguard Worker ///
372*61046927SAndroid Build Coastguard Worker /// # Arguments
373*61046927SAndroid Build Coastguard Worker /// * `s` - The input string
374*61046927SAndroid Build Coastguard Worker /// * `replace_underscores` - Whether to replace underscores with spaces
to_upper_camel_case(s: &str, replace_underscores: bool) -> String375*61046927SAndroid Build Coastguard Worker fn to_upper_camel_case(s: &str, replace_underscores: bool) -> String {
376*61046927SAndroid Build Coastguard Worker // remove unwanted characters
377*61046927SAndroid Build Coastguard Worker let mut s = s.replace(['.', '[', ']'], "");
378*61046927SAndroid Build Coastguard Worker
379*61046927SAndroid Build Coastguard Worker // optionally replace underscores with spaces
380*61046927SAndroid Build Coastguard Worker if replace_underscores {
381*61046927SAndroid Build Coastguard Worker s = s.replace('_', " ");
382*61046927SAndroid Build Coastguard Worker }
383*61046927SAndroid Build Coastguard Worker
384*61046927SAndroid Build Coastguard Worker // capitalize the first letter of each word and join them
385*61046927SAndroid Build Coastguard Worker s.split_whitespace()
386*61046927SAndroid Build Coastguard Worker .map(|word| {
387*61046927SAndroid Build Coastguard Worker let mut chars = word.chars();
388*61046927SAndroid Build Coastguard Worker match chars.next() {
389*61046927SAndroid Build Coastguard Worker Some(first) => first
390*61046927SAndroid Build Coastguard Worker .to_uppercase()
391*61046927SAndroid Build Coastguard Worker .chain(chars.flat_map(|c| c.to_lowercase()))
392*61046927SAndroid Build Coastguard Worker .collect(),
393*61046927SAndroid Build Coastguard Worker None => String::new(),
394*61046927SAndroid Build Coastguard Worker }
395*61046927SAndroid Build Coastguard Worker })
396*61046927SAndroid Build Coastguard Worker .collect()
397*61046927SAndroid Build Coastguard Worker }
398*61046927SAndroid Build Coastguard Worker
399*61046927SAndroid Build Coastguard Worker /// Procedural macro to derive the ISA parser
400*61046927SAndroid Build Coastguard Worker #[proc_macro_derive(IsaParser, attributes(isa, static_rules_file))]
derive_isaspec_parser(input: TokenStream) -> TokenStream401*61046927SAndroid Build Coastguard Worker pub fn derive_isaspec_parser(input: TokenStream) -> TokenStream {
402*61046927SAndroid Build Coastguard Worker derive_parser(input)
403*61046927SAndroid Build Coastguard Worker }
404*61046927SAndroid Build Coastguard Worker
405*61046927SAndroid Build Coastguard Worker #[cfg(test)]
406*61046927SAndroid Build Coastguard Worker mod lib {
407*61046927SAndroid Build Coastguard Worker use super::*;
408*61046927SAndroid Build Coastguard Worker use crate::isa::{BitSetEnum, BitSetEnumValue, Bitset, BitsetTemplate, ISA};
409*61046927SAndroid Build Coastguard Worker use indexmap::IndexMap;
410*61046927SAndroid Build Coastguard Worker use std::collections::HashMap;
411*61046927SAndroid Build Coastguard Worker
412*61046927SAndroid Build Coastguard Worker #[test]
derive_ok()413*61046927SAndroid Build Coastguard Worker fn derive_ok() {
414*61046927SAndroid Build Coastguard Worker let definition = "
415*61046927SAndroid Build Coastguard Worker #[other_attr]
416*61046927SAndroid Build Coastguard Worker #[isa = \"myfile.isa\"]
417*61046927SAndroid Build Coastguard Worker #[static_rules_file = \"static_rules.pest\"]
418*61046927SAndroid Build Coastguard Worker pub struct MyParser<'a, T>;
419*61046927SAndroid Build Coastguard Worker ";
420*61046927SAndroid Build Coastguard Worker let ast = syn::parse_str(definition).unwrap();
421*61046927SAndroid Build Coastguard Worker let (isa, static_rules) = parse_derive(&ast);
422*61046927SAndroid Build Coastguard Worker assert_eq!(isa, "myfile.isa");
423*61046927SAndroid Build Coastguard Worker assert_eq!(static_rules, "static_rules.pest");
424*61046927SAndroid Build Coastguard Worker }
425*61046927SAndroid Build Coastguard Worker
426*61046927SAndroid Build Coastguard Worker #[test]
427*61046927SAndroid Build Coastguard Worker #[should_panic(expected = "Attribute must be a string")]
derive_wrong_arg_isa()428*61046927SAndroid Build Coastguard Worker fn derive_wrong_arg_isa() {
429*61046927SAndroid Build Coastguard Worker let definition = "
430*61046927SAndroid Build Coastguard Worker #[other_attr]
431*61046927SAndroid Build Coastguard Worker #[isa = 1]
432*61046927SAndroid Build Coastguard Worker #[static_rules_file = \"static_rules.pest\"]
433*61046927SAndroid Build Coastguard Worker pub struct MyParser<'a, T>;
434*61046927SAndroid Build Coastguard Worker ";
435*61046927SAndroid Build Coastguard Worker let ast = syn::parse_str(definition).unwrap();
436*61046927SAndroid Build Coastguard Worker parse_derive(&ast);
437*61046927SAndroid Build Coastguard Worker }
438*61046927SAndroid Build Coastguard Worker
439*61046927SAndroid Build Coastguard Worker #[test]
440*61046927SAndroid Build Coastguard Worker #[should_panic(expected = "Attribute must be a string")]
derive_wrong_arg_static_rules_file()441*61046927SAndroid Build Coastguard Worker fn derive_wrong_arg_static_rules_file() {
442*61046927SAndroid Build Coastguard Worker let definition = "
443*61046927SAndroid Build Coastguard Worker #[other_attr]
444*61046927SAndroid Build Coastguard Worker #[isa = \"test.xml\"]
445*61046927SAndroid Build Coastguard Worker #[static_rules_file = 1]
446*61046927SAndroid Build Coastguard Worker pub struct MyParser<'a, T>;
447*61046927SAndroid Build Coastguard Worker ";
448*61046927SAndroid Build Coastguard Worker let ast = syn::parse_str(definition).unwrap();
449*61046927SAndroid Build Coastguard Worker parse_derive(&ast);
450*61046927SAndroid Build Coastguard Worker }
451*61046927SAndroid Build Coastguard Worker
452*61046927SAndroid Build Coastguard Worker #[test]
453*61046927SAndroid Build Coastguard Worker #[should_panic(
454*61046927SAndroid Build Coastguard Worker expected = "An ISA file needs to be provided with the #[isa = \"PATH\"] attribute"
455*61046927SAndroid Build Coastguard Worker )]
derive_no_isa()456*61046927SAndroid Build Coastguard Worker fn derive_no_isa() {
457*61046927SAndroid Build Coastguard Worker let definition = "
458*61046927SAndroid Build Coastguard Worker #[other_attr]
459*61046927SAndroid Build Coastguard Worker pub struct MyParser<'a, T>;
460*61046927SAndroid Build Coastguard Worker ";
461*61046927SAndroid Build Coastguard Worker let ast = syn::parse_str(definition).unwrap();
462*61046927SAndroid Build Coastguard Worker parse_derive(&ast);
463*61046927SAndroid Build Coastguard Worker }
464*61046927SAndroid Build Coastguard Worker
465*61046927SAndroid Build Coastguard Worker #[test]
test_to_upper_camel_case()466*61046927SAndroid Build Coastguard Worker fn test_to_upper_camel_case() {
467*61046927SAndroid Build Coastguard Worker assert_eq!(to_upper_camel_case("test_string", true), "TestString");
468*61046927SAndroid Build Coastguard Worker assert_eq!(to_upper_camel_case("test_string", false), "Test_string");
469*61046927SAndroid Build Coastguard Worker assert_eq!(to_upper_camel_case("[Test]_String", true), "TestString");
470*61046927SAndroid Build Coastguard Worker assert_eq!(to_upper_camel_case("[Test]_String", false), "Test_string");
471*61046927SAndroid Build Coastguard Worker assert_eq!(
472*61046927SAndroid Build Coastguard Worker to_upper_camel_case("multiple_words_string", true),
473*61046927SAndroid Build Coastguard Worker "MultipleWordsString"
474*61046927SAndroid Build Coastguard Worker );
475*61046927SAndroid Build Coastguard Worker }
476*61046927SAndroid Build Coastguard Worker
mock_isa() -> ISA<'static>477*61046927SAndroid Build Coastguard Worker fn mock_isa() -> ISA<'static> {
478*61046927SAndroid Build Coastguard Worker let mut bitsets = IndexMap::new();
479*61046927SAndroid Build Coastguard Worker let mut enums = IndexMap::new();
480*61046927SAndroid Build Coastguard Worker let mut templates = IndexMap::new();
481*61046927SAndroid Build Coastguard Worker
482*61046927SAndroid Build Coastguard Worker // Add mock data for bitsets, enums, and templates
483*61046927SAndroid Build Coastguard Worker // Example for bitsets
484*61046927SAndroid Build Coastguard Worker bitsets.insert(
485*61046927SAndroid Build Coastguard Worker "bitset1",
486*61046927SAndroid Build Coastguard Worker Bitset {
487*61046927SAndroid Build Coastguard Worker name: "bitset1",
488*61046927SAndroid Build Coastguard Worker extends: None,
489*61046927SAndroid Build Coastguard Worker meta: HashMap::from([("type", "alu"), ("has_dest", "true"), ("valid_srcs", "0")]),
490*61046927SAndroid Build Coastguard Worker },
491*61046927SAndroid Build Coastguard Worker );
492*61046927SAndroid Build Coastguard Worker
493*61046927SAndroid Build Coastguard Worker // Example for enums
494*61046927SAndroid Build Coastguard Worker enums.insert(
495*61046927SAndroid Build Coastguard Worker "enum1",
496*61046927SAndroid Build Coastguard Worker BitSetEnum {
497*61046927SAndroid Build Coastguard Worker name: "enum1",
498*61046927SAndroid Build Coastguard Worker values: vec![
499*61046927SAndroid Build Coastguard Worker BitSetEnumValue {
500*61046927SAndroid Build Coastguard Worker display: "val1",
501*61046927SAndroid Build Coastguard Worker name: Some("val1_name"),
502*61046927SAndroid Build Coastguard Worker },
503*61046927SAndroid Build Coastguard Worker BitSetEnumValue {
504*61046927SAndroid Build Coastguard Worker display: "val2",
505*61046927SAndroid Build Coastguard Worker name: Some("val2_name"),
506*61046927SAndroid Build Coastguard Worker },
507*61046927SAndroid Build Coastguard Worker ],
508*61046927SAndroid Build Coastguard Worker },
509*61046927SAndroid Build Coastguard Worker );
510*61046927SAndroid Build Coastguard Worker
511*61046927SAndroid Build Coastguard Worker // Example for templates
512*61046927SAndroid Build Coastguard Worker templates.insert(
513*61046927SAndroid Build Coastguard Worker "INSTR_ALU",
514*61046927SAndroid Build Coastguard Worker BitsetTemplate {
515*61046927SAndroid Build Coastguard Worker display: "{DST_FULL}{SAT}{COND}",
516*61046927SAndroid Build Coastguard Worker },
517*61046927SAndroid Build Coastguard Worker );
518*61046927SAndroid Build Coastguard Worker
519*61046927SAndroid Build Coastguard Worker ISA {
520*61046927SAndroid Build Coastguard Worker bitsets,
521*61046927SAndroid Build Coastguard Worker enums,
522*61046927SAndroid Build Coastguard Worker templates,
523*61046927SAndroid Build Coastguard Worker }
524*61046927SAndroid Build Coastguard Worker }
525*61046927SAndroid Build Coastguard Worker
526*61046927SAndroid Build Coastguard Worker #[test]
test_generate_peg_grammar_enums()527*61046927SAndroid Build Coastguard Worker fn test_generate_peg_grammar_enums() {
528*61046927SAndroid Build Coastguard Worker let isa = mock_isa();
529*61046927SAndroid Build Coastguard Worker let grammar = generate_peg_grammar_enums(&isa);
530*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("Enum1 = { Val2 | Val1 }"));
531*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("Val1 = { \"val1\" }"));
532*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("Val2 = { \"val2\" }"));
533*61046927SAndroid Build Coastguard Worker }
534*61046927SAndroid Build Coastguard Worker
535*61046927SAndroid Build Coastguard Worker #[test]
test_generate_peg_grammar_instructions()536*61046927SAndroid Build Coastguard Worker fn test_generate_peg_grammar_instructions() {
537*61046927SAndroid Build Coastguard Worker let isa = mock_isa();
538*61046927SAndroid Build Coastguard Worker let grammar = generate_peg_grammar_instructions(&isa);
539*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("instructions = _{ OpcBitset1 }"));
540*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("OpcBitset1 = { \"bitset1\" ~ Dst_full? ~ Sat? ~ Cond? ~ Dest ~ \",\" ~ Src ~ \",\" ~ SrcVoid ~ \",\" ~ SrcVoid }"));
541*61046927SAndroid Build Coastguard Worker }
542*61046927SAndroid Build Coastguard Worker
543*61046927SAndroid Build Coastguard Worker #[test]
test_generate_peg_grammar()544*61046927SAndroid Build Coastguard Worker fn test_generate_peg_grammar() {
545*61046927SAndroid Build Coastguard Worker let isa = mock_isa();
546*61046927SAndroid Build Coastguard Worker let grammar = generate_peg_grammar(&isa);
547*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("Enum1 = { Val2 | Val1 }"));
548*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("instructions = _{ OpcBitset1 }"));
549*61046927SAndroid Build Coastguard Worker assert!(grammar.contains("OpcBitset1 = { \"bitset1\" ~ Dst_full? ~ Sat? ~ Cond? ~ Dest ~ \",\" ~ Src ~ \",\" ~ SrcVoid ~ \",\" ~ SrcVoid }"));
550*61046927SAndroid Build Coastguard Worker }
551*61046927SAndroid Build Coastguard Worker }
552