xref: /aosp_15_r20/external/mesa3d/src/etnaviv/isa/isa.rs (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 // Copyright © 2024 Igalia S.L.
2 // SPDX-License-Identifier: MIT
3 
4 use indexmap::IndexMap;
5 use roxmltree::Document;
6 use std::collections::HashMap;
7 
8 /// A structure representing a bitset.
9 #[derive(Debug)]
10 pub struct Bitset<'a> {
11     pub name: &'a str,
12     pub displayname: Option<&'a str>,
13     pub extends: Option<&'a str>,
14     pub meta: HashMap<&'a str, &'a str>,
15 }
16 
17 /// A structure representing a value in a bitset enum.
18 #[derive(Debug)]
19 pub struct BitSetEnumValue<'a> {
20     pub display: &'a str,
21     pub name: Option<&'a str>,
22 }
23 
24 /// A structure representing a bitset enum.
25 #[derive(Debug)]
26 pub struct BitSetEnum<'a> {
27     pub name: &'a str,
28     pub values: Vec<BitSetEnumValue<'a>>,
29 }
30 
31 /// A structure representing a bitset template.
32 #[derive(Debug)]
33 pub struct BitsetTemplate<'a> {
34     pub display: &'a str,
35 }
36 
37 /// A structure representing an Instruction Set Architecture (ISA),
38 /// containing bitsets and enums.
39 pub struct ISA<'a> {
40     pub bitsets: IndexMap<&'a str, Bitset<'a>>,
41     pub enums: IndexMap<&'a str, BitSetEnum<'a>>,
42     pub templates: IndexMap<&'a str, BitsetTemplate<'a>>,
43 }
44 
45 impl<'a> ISA<'a> {
46     /// Creates a new `ISA` by loading bitsets and enums from a parsed XML document.
new(doc: &'a Document) -> Self47     pub fn new(doc: &'a Document) -> Self {
48         let mut isa = ISA {
49             bitsets: IndexMap::new(),
50             enums: IndexMap::new(),
51             templates: IndexMap::new(),
52         };
53 
54         isa.load_from_document(doc);
55         isa
56     }
57 
58     /// Collects metadata for a given bitset by walking the `extends` chain in reverse order.
collect_meta(&self, name: &'a str) -> HashMap<&'a str, &'a str>59     pub fn collect_meta(&self, name: &'a str) -> HashMap<&'a str, &'a str> {
60         let mut meta = HashMap::new();
61         let mut chain = Vec::new();
62         let mut current = Some(name);
63 
64         // Gather the chain of bitsets
65         while let Some(item) = current {
66             if let Some(bitset) = self.bitsets.get(item) {
67                 chain.push(bitset);
68                 current = bitset.extends;
69             } else {
70                 current = None;
71             }
72         }
73 
74         // Collect metadata in reverse order so children's metadata overwrites their parent's.
75         for bitset in chain.into_iter().rev() {
76             meta.extend(&bitset.meta);
77         }
78 
79         meta
80     }
81 
82     /// Loads bitsets and enums from a parsed XML document into the `ISA`.
load_from_document(&mut self, doc: &'a Document)83     fn load_from_document(&mut self, doc: &'a Document) {
84         doc.descendants()
85             .filter(|node| node.is_element() && node.has_tag_name("template"))
86             .for_each(|value| {
87                 let name = value.attribute("name").unwrap();
88                 let display = value.text().unwrap();
89 
90                 self.templates.insert(name, BitsetTemplate { display });
91             });
92 
93         doc.descendants()
94             .filter(|node| node.is_element() && node.has_tag_name("enum"))
95             .for_each(|node| {
96                 let values = node
97                     .children()
98                     .filter(|node| node.is_element() && node.has_tag_name("value"))
99                     .map(|value| {
100                         let display = value.attribute("display").unwrap();
101                         let name = value.attribute("name");
102 
103                         BitSetEnumValue { display, name }
104                     })
105                     .collect();
106 
107                 let name = node.attribute("name").unwrap();
108 
109                 self.enums.insert(name, BitSetEnum { name, values });
110             });
111 
112         doc.descendants()
113             .filter(|node| node.is_element() && node.has_tag_name("bitset"))
114             .for_each(|node| {
115                 let name = node.attribute("name").unwrap();
116                 let displayname = node.attribute("displayname");
117                 let extends = node.attribute("extends");
118                 let meta_nodes = node
119                     .children()
120                     .filter(|child| child.is_element() && child.has_tag_name("meta"));
121 
122                 // We can have multiple <meta> tags, which we need to combine.
123                 let combined_meta: HashMap<_, _> = meta_nodes
124                     .flat_map(|m| m.attributes())
125                     .map(|attr| (attr.name(), attr.value()))
126                     .collect();
127 
128                 self.bitsets.insert(
129                     name,
130                     Bitset {
131                         name,
132                         displayname,
133                         extends,
134                         meta: combined_meta,
135                     },
136                 );
137             });
138     }
139 }
140 
141 #[cfg(test)]
142 mod tests {
143     use super::*;
144 
145     #[test]
test_collect_meta()146     fn test_collect_meta() {
147         let mut isa = ISA {
148             bitsets: IndexMap::new(),
149             enums: IndexMap::new(),
150             templates: IndexMap::new(),
151         };
152         isa.bitsets.insert(
153             "bitset1",
154             Bitset {
155                 name: "bitset1",
156                 extends: None,
157                 meta: HashMap::from([("key1", "value1")]),
158             },
159         );
160         isa.bitsets.insert(
161             "bitset2",
162             Bitset {
163                 name: "bitset2",
164                 extends: Some("bitset1"),
165                 meta: HashMap::from([("key2", "value2")]),
166             },
167         );
168         isa.bitsets.insert(
169             "bitset3",
170             Bitset {
171                 name: "bitset3",
172                 extends: Some("bitset2"),
173                 meta: HashMap::from([("key3", "value3")]),
174             },
175         );
176 
177         let meta = isa.collect_meta("bitset3");
178         assert_eq!(meta.get("key1"), Some(&"value1"));
179         assert_eq!(meta.get("key2"), Some(&"value2"));
180         assert_eq!(meta.get("key3"), Some(&"value3"));
181     }
182 
183     #[test]
test_load_from_document()184     fn test_load_from_document() {
185         let xml_data = r#"
186         <isa>
187             <bitset name="bitset1">
188                 <meta key1="value1"/>
189                 <meta key2="value2"/>
190             </bitset>
191             <bitset name="bitset2" extends="bitset1"/>
192             <enum name="enum1">
193                 <value display="val1" val="0"/>
194                 <value display="val2" val="1"/>
195             </enum>
196         </isa>
197         "#;
198 
199         let doc = Document::parse(xml_data).unwrap();
200         let isa = ISA::new(&doc);
201 
202         let bitset1 = isa.bitsets.get(&"bitset1").unwrap();
203         assert_eq!(bitset1.name, "bitset1");
204         assert_eq!(bitset1.meta.get("key1"), Some(&"value1"));
205         assert_eq!(bitset1.meta.get("key2"), Some(&"value2"));
206 
207         let bitset2 = isa.bitsets.get(&"bitset2").unwrap();
208         assert_eq!(bitset2.name, "bitset2");
209         assert_eq!(bitset2.extends, Some("bitset1"));
210 
211         let enum1 = isa.enums.get(&"enum1").unwrap();
212         assert_eq!(enum1.name, "enum1");
213         assert_eq!(enum1.values.len(), 2);
214         assert_eq!(enum1.values[0].display, "val1");
215         assert_eq!(enum1.values[1].display, "val2");
216     }
217 }
218