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