1*61046927SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*61046927SAndroid Build Coastguard Worker# Copyright © 2019, 2022 Intel Corporation 3*61046927SAndroid Build Coastguard Worker# SPDX-License-Identifier: MIT 4*61046927SAndroid Build Coastguard Worker 5*61046927SAndroid Build Coastguard Workerfrom __future__ import annotations 6*61046927SAndroid Build Coastguard Workerfrom collections import OrderedDict 7*61046927SAndroid Build Coastguard Workerimport copy 8*61046927SAndroid Build Coastguard Workerimport io 9*61046927SAndroid Build Coastguard Workerimport pathlib 10*61046927SAndroid Build Coastguard Workerimport os.path 11*61046927SAndroid Build Coastguard Workerimport re 12*61046927SAndroid Build Coastguard Workerimport xml.etree.ElementTree as et 13*61046927SAndroid Build Coastguard Workerimport typing 14*61046927SAndroid Build Coastguard Worker 15*61046927SAndroid Build Coastguard Workerif typing.TYPE_CHECKING: 16*61046927SAndroid Build Coastguard Worker class Args(typing.Protocol): 17*61046927SAndroid Build Coastguard Worker 18*61046927SAndroid Build Coastguard Worker files: typing.List[pathlib.Path] 19*61046927SAndroid Build Coastguard Worker validate: bool 20*61046927SAndroid Build Coastguard Worker quiet: bool 21*61046927SAndroid Build Coastguard Worker 22*61046927SAndroid Build Coastguard Worker 23*61046927SAndroid Build Coastguard Workerdef get_filename(element: et.Element) -> str: 24*61046927SAndroid Build Coastguard Worker return element.attrib['filename'] 25*61046927SAndroid Build Coastguard Worker 26*61046927SAndroid Build Coastguard Workerdef get_name(element: et.Element) -> str: 27*61046927SAndroid Build Coastguard Worker return element.attrib['name'] 28*61046927SAndroid Build Coastguard Worker 29*61046927SAndroid Build Coastguard Workerdef get_value(element: et.Element) -> int: 30*61046927SAndroid Build Coastguard Worker return int(element.attrib['value'], 0) 31*61046927SAndroid Build Coastguard Worker 32*61046927SAndroid Build Coastguard Workerdef get_start(element: et.Element) -> int: 33*61046927SAndroid Build Coastguard Worker return int(element.attrib['start'], 0) 34*61046927SAndroid Build Coastguard Worker 35*61046927SAndroid Build Coastguard Worker 36*61046927SAndroid Build Coastguard WorkerBASE_TYPES = { 37*61046927SAndroid Build Coastguard Worker 'address', 38*61046927SAndroid Build Coastguard Worker 'offset', 39*61046927SAndroid Build Coastguard Worker 'int', 40*61046927SAndroid Build Coastguard Worker 'uint', 41*61046927SAndroid Build Coastguard Worker 'bool', 42*61046927SAndroid Build Coastguard Worker 'float', 43*61046927SAndroid Build Coastguard Worker 'mbz', 44*61046927SAndroid Build Coastguard Worker 'mbo', 45*61046927SAndroid Build Coastguard Worker} 46*61046927SAndroid Build Coastguard Worker 47*61046927SAndroid Build Coastguard WorkerFIXED_PATTERN = re.compile(r"(s|u)(\d+)\.(\d+)") 48*61046927SAndroid Build Coastguard Worker 49*61046927SAndroid Build Coastguard Workerdef is_base_type(name: str) -> bool: 50*61046927SAndroid Build Coastguard Worker return name in BASE_TYPES or FIXED_PATTERN.match(name) is not None 51*61046927SAndroid Build Coastguard Worker 52*61046927SAndroid Build Coastguard Workerdef add_struct_refs(items: typing.OrderedDict[str, bool], node: et.Element) -> None: 53*61046927SAndroid Build Coastguard Worker if node.tag == 'field': 54*61046927SAndroid Build Coastguard Worker if 'type' in node.attrib and not is_base_type(node.attrib['type']): 55*61046927SAndroid Build Coastguard Worker t = node.attrib['type'] 56*61046927SAndroid Build Coastguard Worker items[t] = True 57*61046927SAndroid Build Coastguard Worker return 58*61046927SAndroid Build Coastguard Worker if node.tag not in {'struct', 'group'}: 59*61046927SAndroid Build Coastguard Worker return 60*61046927SAndroid Build Coastguard Worker for c in node: 61*61046927SAndroid Build Coastguard Worker add_struct_refs(items, c) 62*61046927SAndroid Build Coastguard Worker 63*61046927SAndroid Build Coastguard Worker 64*61046927SAndroid Build Coastguard Workerclass Struct(object): 65*61046927SAndroid Build Coastguard Worker def __init__(self, xml: et.Element): 66*61046927SAndroid Build Coastguard Worker self.xml = xml 67*61046927SAndroid Build Coastguard Worker self.name = xml.attrib['name'] 68*61046927SAndroid Build Coastguard Worker self.deps: typing.OrderedDict[str, Struct] = OrderedDict() 69*61046927SAndroid Build Coastguard Worker 70*61046927SAndroid Build Coastguard Worker def find_deps(self, struct_dict, enum_dict) -> None: 71*61046927SAndroid Build Coastguard Worker deps: typing.OrderedDict[str, bool] = OrderedDict() 72*61046927SAndroid Build Coastguard Worker add_struct_refs(deps, self.xml) 73*61046927SAndroid Build Coastguard Worker for d in deps.keys(): 74*61046927SAndroid Build Coastguard Worker if d in struct_dict: 75*61046927SAndroid Build Coastguard Worker self.deps[d] = struct_dict[d] 76*61046927SAndroid Build Coastguard Worker 77*61046927SAndroid Build Coastguard Worker def add_xml(self, items: typing.OrderedDict[str, et.Element]) -> None: 78*61046927SAndroid Build Coastguard Worker for d in self.deps.values(): 79*61046927SAndroid Build Coastguard Worker d.add_xml(items) 80*61046927SAndroid Build Coastguard Worker items[self.name] = self.xml 81*61046927SAndroid Build Coastguard Worker 82*61046927SAndroid Build Coastguard Worker 83*61046927SAndroid Build Coastguard Worker# ordering of the various tag attributes 84*61046927SAndroid Build Coastguard WorkerGENXML_DESC = { 85*61046927SAndroid Build Coastguard Worker 'genxml' : [ 'name', 'gen', ], 86*61046927SAndroid Build Coastguard Worker 'import' : [ 'name', ], 87*61046927SAndroid Build Coastguard Worker 'exclude' : [ 'name', ], 88*61046927SAndroid Build Coastguard Worker 'enum' : [ 'name', 'value', 'prefix', ], 89*61046927SAndroid Build Coastguard Worker 'struct' : [ 'name', 'length', ], 90*61046927SAndroid Build Coastguard Worker 'field' : [ 'name', 'start', 'end', 'type', 'default', 'prefix', 'nonzero' ], 91*61046927SAndroid Build Coastguard Worker 'instruction' : [ 'name', 'bias', 'length', 'engine', ], 92*61046927SAndroid Build Coastguard Worker 'value' : [ 'name', 'value', 'dont_use', ], 93*61046927SAndroid Build Coastguard Worker 'group' : [ 'count', 'start', 'size', ], 94*61046927SAndroid Build Coastguard Worker 'register' : [ 'name', 'length', 'num', ], 95*61046927SAndroid Build Coastguard Worker} 96*61046927SAndroid Build Coastguard Worker 97*61046927SAndroid Build Coastguard Worker 98*61046927SAndroid Build Coastguard Workerdef node_validator(old: et.Element, new: et.Element) -> bool: 99*61046927SAndroid Build Coastguard Worker """Compare to ElementTree Element nodes. 100*61046927SAndroid Build Coastguard Worker 101*61046927SAndroid Build Coastguard Worker There is no builtin equality method, so calling `et.Element == et.Element` is 102*61046927SAndroid Build Coastguard Worker equivalent to calling `et.Element is et.Element`. We instead want to compare 103*61046927SAndroid Build Coastguard Worker that the contents are the same, including the order of children and attributes 104*61046927SAndroid Build Coastguard Worker """ 105*61046927SAndroid Build Coastguard Worker return ( 106*61046927SAndroid Build Coastguard Worker # Check that the attributes are the same 107*61046927SAndroid Build Coastguard Worker old.tag == new.tag and 108*61046927SAndroid Build Coastguard Worker old.text == new.text and 109*61046927SAndroid Build Coastguard Worker (old.tail or "").strip() == (new.tail or "").strip() and 110*61046927SAndroid Build Coastguard Worker list(old.attrib.items()) == list(new.attrib.items()) and 111*61046927SAndroid Build Coastguard Worker len(old) == len(new) and 112*61046927SAndroid Build Coastguard Worker 113*61046927SAndroid Build Coastguard Worker # check that there are no unexpected attributes 114*61046927SAndroid Build Coastguard Worker set(new.attrib).issubset(GENXML_DESC[new.tag]) and 115*61046927SAndroid Build Coastguard Worker 116*61046927SAndroid Build Coastguard Worker # check that the attributes are sorted 117*61046927SAndroid Build Coastguard Worker list(new.attrib) == list(old.attrib) and 118*61046927SAndroid Build Coastguard Worker all(node_validator(f, s) for f, s in zip(old, new)) 119*61046927SAndroid Build Coastguard Worker ) 120*61046927SAndroid Build Coastguard Worker 121*61046927SAndroid Build Coastguard Worker 122*61046927SAndroid Build Coastguard Workerdef process_attribs(elem: et.Element) -> None: 123*61046927SAndroid Build Coastguard Worker valid = GENXML_DESC[elem.tag] 124*61046927SAndroid Build Coastguard Worker # sort and prune attributes 125*61046927SAndroid Build Coastguard Worker elem.attrib = OrderedDict(sorted(((k, v) for k, v in elem.attrib.items() if k in valid), 126*61046927SAndroid Build Coastguard Worker key=lambda x: valid.index(x[0]))) 127*61046927SAndroid Build Coastguard Worker for e in elem: 128*61046927SAndroid Build Coastguard Worker process_attribs(e) 129*61046927SAndroid Build Coastguard Worker 130*61046927SAndroid Build Coastguard Worker 131*61046927SAndroid Build Coastguard Workerdef sort_xml(xml: et.ElementTree) -> None: 132*61046927SAndroid Build Coastguard Worker genxml = xml.getroot() 133*61046927SAndroid Build Coastguard Worker 134*61046927SAndroid Build Coastguard Worker imports = xml.findall('import') 135*61046927SAndroid Build Coastguard Worker 136*61046927SAndroid Build Coastguard Worker enums = sorted(xml.findall('enum'), key=get_name) 137*61046927SAndroid Build Coastguard Worker enum_dict: typing.Dict[str, et.Element] = {} 138*61046927SAndroid Build Coastguard Worker for e in enums: 139*61046927SAndroid Build Coastguard Worker e[:] = sorted(e, key=get_value) 140*61046927SAndroid Build Coastguard Worker enum_dict[e.attrib['name']] = e 141*61046927SAndroid Build Coastguard Worker 142*61046927SAndroid Build Coastguard Worker # Structs are a bit annoying because they can refer to each other. We sort 143*61046927SAndroid Build Coastguard Worker # them alphabetically and then build a graph of dependencies. Finally we go 144*61046927SAndroid Build Coastguard Worker # through the alphabetically sorted list and print out dependencies first. 145*61046927SAndroid Build Coastguard Worker structs = sorted(xml.findall('./struct'), key=get_name) 146*61046927SAndroid Build Coastguard Worker wrapped_struct_dict: typing.Dict[str, Struct] = {} 147*61046927SAndroid Build Coastguard Worker for s in structs: 148*61046927SAndroid Build Coastguard Worker s[:] = sorted(s, key=get_start) 149*61046927SAndroid Build Coastguard Worker ws = Struct(s) 150*61046927SAndroid Build Coastguard Worker wrapped_struct_dict[ws.name] = ws 151*61046927SAndroid Build Coastguard Worker 152*61046927SAndroid Build Coastguard Worker for ws in wrapped_struct_dict.values(): 153*61046927SAndroid Build Coastguard Worker ws.find_deps(wrapped_struct_dict, enum_dict) 154*61046927SAndroid Build Coastguard Worker 155*61046927SAndroid Build Coastguard Worker sorted_structs: typing.OrderedDict[str, et.Element] = OrderedDict() 156*61046927SAndroid Build Coastguard Worker for s in structs: 157*61046927SAndroid Build Coastguard Worker _s = wrapped_struct_dict[s.attrib['name']] 158*61046927SAndroid Build Coastguard Worker _s.add_xml(sorted_structs) 159*61046927SAndroid Build Coastguard Worker 160*61046927SAndroid Build Coastguard Worker instructions = sorted(xml.findall('./instruction'), key=get_name) 161*61046927SAndroid Build Coastguard Worker for i in instructions: 162*61046927SAndroid Build Coastguard Worker i[:] = sorted(i, key=get_start) 163*61046927SAndroid Build Coastguard Worker 164*61046927SAndroid Build Coastguard Worker registers = sorted(xml.findall('./register'), key=get_name) 165*61046927SAndroid Build Coastguard Worker for r in registers: 166*61046927SAndroid Build Coastguard Worker r[:] = sorted(r, key=get_start) 167*61046927SAndroid Build Coastguard Worker 168*61046927SAndroid Build Coastguard Worker new_elems = (imports + enums + list(sorted_structs.values()) + 169*61046927SAndroid Build Coastguard Worker instructions + registers) 170*61046927SAndroid Build Coastguard Worker for n in new_elems: 171*61046927SAndroid Build Coastguard Worker process_attribs(n) 172*61046927SAndroid Build Coastguard Worker genxml[:] = new_elems 173*61046927SAndroid Build Coastguard Worker 174*61046927SAndroid Build Coastguard Worker 175*61046927SAndroid Build Coastguard Worker# `default_imports` documents which files should be imported for our 176*61046927SAndroid Build Coastguard Worker# genxml files. This is only useful if a genxml file does not already 177*61046927SAndroid Build Coastguard Worker# include imports. 178*61046927SAndroid Build Coastguard Worker# 179*61046927SAndroid Build Coastguard Worker# Basically, this allows the genxml_import.py tool used with the 180*61046927SAndroid Build Coastguard Worker# --import switch to know which files should be added as an import. 181*61046927SAndroid Build Coastguard Worker# (genxml_import.py uses GenXml.add_xml_imports, which relies on 182*61046927SAndroid Build Coastguard Worker# `default_imports`.) 183*61046927SAndroid Build Coastguard Workerdefault_imports = OrderedDict([ 184*61046927SAndroid Build Coastguard Worker ('gen4.xml', ()), 185*61046927SAndroid Build Coastguard Worker ('gen45.xml', ('gen4.xml',)), 186*61046927SAndroid Build Coastguard Worker ('gen5.xml', ('gen45.xml',)), 187*61046927SAndroid Build Coastguard Worker ('gen6.xml', ('gen5.xml',)), 188*61046927SAndroid Build Coastguard Worker ('gen7.xml', ('gen6.xml',)), 189*61046927SAndroid Build Coastguard Worker ('gen75.xml', ('gen7.xml',)), 190*61046927SAndroid Build Coastguard Worker ('gen8.xml', ('gen75.xml',)), 191*61046927SAndroid Build Coastguard Worker ('gen9.xml', ('gen8.xml',)), 192*61046927SAndroid Build Coastguard Worker ('gen11.xml', ('gen9.xml',)), 193*61046927SAndroid Build Coastguard Worker ('gen12.xml', ('gen11.xml',)), 194*61046927SAndroid Build Coastguard Worker ('gen125.xml', ('gen12.xml',)), 195*61046927SAndroid Build Coastguard Worker ('gen20.xml', ('gen125.xml',)), 196*61046927SAndroid Build Coastguard Worker ('gen20_rt.xml', ('gen125_rt.xml',)), 197*61046927SAndroid Build Coastguard Worker ]) 198*61046927SAndroid Build Coastguard Workerknown_genxml_files = list(default_imports.keys()) 199*61046927SAndroid Build Coastguard Worker 200*61046927SAndroid Build Coastguard Worker 201*61046927SAndroid Build Coastguard Workerdef genxml_path_to_key(path): 202*61046927SAndroid Build Coastguard Worker try: 203*61046927SAndroid Build Coastguard Worker return known_genxml_files.index(path.name) 204*61046927SAndroid Build Coastguard Worker except ValueError: 205*61046927SAndroid Build Coastguard Worker return len(known_genxml_files) 206*61046927SAndroid Build Coastguard Worker 207*61046927SAndroid Build Coastguard Worker 208*61046927SAndroid Build Coastguard Workerdef sort_genxml_files(files): 209*61046927SAndroid Build Coastguard Worker files.sort(key=genxml_path_to_key) 210*61046927SAndroid Build Coastguard Worker 211*61046927SAndroid Build Coastguard Workerclass GenXml(object): 212*61046927SAndroid Build Coastguard Worker def __init__(self, filename, import_xml=False, files=None): 213*61046927SAndroid Build Coastguard Worker if files is not None: 214*61046927SAndroid Build Coastguard Worker self.files = files 215*61046927SAndroid Build Coastguard Worker else: 216*61046927SAndroid Build Coastguard Worker self.files = set() 217*61046927SAndroid Build Coastguard Worker self.filename = pathlib.Path(filename) 218*61046927SAndroid Build Coastguard Worker 219*61046927SAndroid Build Coastguard Worker # Assert that the file hasn't already been loaded which would 220*61046927SAndroid Build Coastguard Worker # indicate a loop in genxml imports, and lead to infinite 221*61046927SAndroid Build Coastguard Worker # recursion. 222*61046927SAndroid Build Coastguard Worker assert self.filename not in self.files 223*61046927SAndroid Build Coastguard Worker 224*61046927SAndroid Build Coastguard Worker self.files.add(self.filename) 225*61046927SAndroid Build Coastguard Worker self.et = et.parse(self.filename) 226*61046927SAndroid Build Coastguard Worker if import_xml: 227*61046927SAndroid Build Coastguard Worker self.merge_imported() 228*61046927SAndroid Build Coastguard Worker 229*61046927SAndroid Build Coastguard Worker def process_imported(self, merge=False, drop_dupes=False): 230*61046927SAndroid Build Coastguard Worker """Processes imported genxml files. 231*61046927SAndroid Build Coastguard Worker 232*61046927SAndroid Build Coastguard Worker This helper function scans imported genxml files and has two 233*61046927SAndroid Build Coastguard Worker mutually exclusive operating modes. 234*61046927SAndroid Build Coastguard Worker 235*61046927SAndroid Build Coastguard Worker If `merge` is True, then items will be merged into the 236*61046927SAndroid Build Coastguard Worker `self.et` data structure. 237*61046927SAndroid Build Coastguard Worker 238*61046927SAndroid Build Coastguard Worker If `drop_dupes` is True, then any item that is a duplicate to 239*61046927SAndroid Build Coastguard Worker an item imported will be droped from the `self.et` data 240*61046927SAndroid Build Coastguard Worker structure. This is used by `self.optimize_xml_import` to 241*61046927SAndroid Build Coastguard Worker shrink the size of the genxml file by reducing duplications. 242*61046927SAndroid Build Coastguard Worker 243*61046927SAndroid Build Coastguard Worker """ 244*61046927SAndroid Build Coastguard Worker assert merge != drop_dupes 245*61046927SAndroid Build Coastguard Worker orig_elements = set(self.et.getroot()) 246*61046927SAndroid Build Coastguard Worker name_and_obj = lambda i: (get_name(i), i) 247*61046927SAndroid Build Coastguard Worker filter_ty = lambda s: filter(lambda i: i.tag == s, orig_elements) 248*61046927SAndroid Build Coastguard Worker filter_ty_item = lambda s: dict(map(name_and_obj, filter_ty(s))) 249*61046927SAndroid Build Coastguard Worker 250*61046927SAndroid Build Coastguard Worker # orig_by_tag stores items defined directly in the genxml 251*61046927SAndroid Build Coastguard Worker # file. If a genxml item is defined in the genxml directly, 252*61046927SAndroid Build Coastguard Worker # then any imported items of the same name are ignored. 253*61046927SAndroid Build Coastguard Worker orig_by_tag = { 254*61046927SAndroid Build Coastguard Worker 'enum': filter_ty_item('enum'), 255*61046927SAndroid Build Coastguard Worker 'struct': filter_ty_item('struct'), 256*61046927SAndroid Build Coastguard Worker 'instruction': filter_ty_item('instruction'), 257*61046927SAndroid Build Coastguard Worker 'register': filter_ty_item('register'), 258*61046927SAndroid Build Coastguard Worker } 259*61046927SAndroid Build Coastguard Worker 260*61046927SAndroid Build Coastguard Worker for item in orig_elements: 261*61046927SAndroid Build Coastguard Worker if item.tag == 'import': 262*61046927SAndroid Build Coastguard Worker assert 'name' in item.attrib 263*61046927SAndroid Build Coastguard Worker filename = os.path.split(item.attrib['name']) 264*61046927SAndroid Build Coastguard Worker exceptions = set() 265*61046927SAndroid Build Coastguard Worker for e in item: 266*61046927SAndroid Build Coastguard Worker assert e.tag == 'exclude' 267*61046927SAndroid Build Coastguard Worker exceptions.add(e.attrib['name']) 268*61046927SAndroid Build Coastguard Worker # We should be careful to restrict loaded files to 269*61046927SAndroid Build Coastguard Worker # those under the source or build trees. For now, only 270*61046927SAndroid Build Coastguard Worker # allow siblings of the current xml file. 271*61046927SAndroid Build Coastguard Worker assert filename[0] == '', 'Directories not allowed with import' 272*61046927SAndroid Build Coastguard Worker filename = os.path.join(os.path.dirname(self.filename), 273*61046927SAndroid Build Coastguard Worker filename[1]) 274*61046927SAndroid Build Coastguard Worker assert os.path.exists(filename), f'{self.filename} {filename}' 275*61046927SAndroid Build Coastguard Worker 276*61046927SAndroid Build Coastguard Worker # Here we load the imported genxml file. We set 277*61046927SAndroid Build Coastguard Worker # `import_xml` to true so that any imports in the 278*61046927SAndroid Build Coastguard Worker # imported genxml will be merged during the loading 279*61046927SAndroid Build Coastguard Worker # process. 280*61046927SAndroid Build Coastguard Worker # 281*61046927SAndroid Build Coastguard Worker # The `files` parameter is a set of files that have 282*61046927SAndroid Build Coastguard Worker # been loaded, and it is used to prevent any cycles 283*61046927SAndroid Build Coastguard Worker # (infinite recursion) while loading imported genxml 284*61046927SAndroid Build Coastguard Worker # files. 285*61046927SAndroid Build Coastguard Worker genxml = GenXml(filename, import_xml=True, files=self.files) 286*61046927SAndroid Build Coastguard Worker imported_elements = set(genxml.et.getroot()) 287*61046927SAndroid Build Coastguard Worker 288*61046927SAndroid Build Coastguard Worker # `to_add` is a set of items that were imported an 289*61046927SAndroid Build Coastguard Worker # should be merged into the `self.et` data structure. 290*61046927SAndroid Build Coastguard Worker # This is only used when the `merge` parameter is 291*61046927SAndroid Build Coastguard Worker # True. 292*61046927SAndroid Build Coastguard Worker to_add = set() 293*61046927SAndroid Build Coastguard Worker # `to_remove` is a set of items that can safely be 294*61046927SAndroid Build Coastguard Worker # imported since the item is equivalent. This is only 295*61046927SAndroid Build Coastguard Worker # used when the `drop_duped` parameter is True. 296*61046927SAndroid Build Coastguard Worker to_remove = set() 297*61046927SAndroid Build Coastguard Worker for i in imported_elements: 298*61046927SAndroid Build Coastguard Worker if i.tag not in orig_by_tag: 299*61046927SAndroid Build Coastguard Worker continue 300*61046927SAndroid Build Coastguard Worker if i.attrib['name'] in exceptions: 301*61046927SAndroid Build Coastguard Worker continue 302*61046927SAndroid Build Coastguard Worker if i.attrib['name'] in orig_by_tag[i.tag]: 303*61046927SAndroid Build Coastguard Worker if merge: 304*61046927SAndroid Build Coastguard Worker # An item with this same name was defined 305*61046927SAndroid Build Coastguard Worker # in the genxml directly. There we should 306*61046927SAndroid Build Coastguard Worker # ignore (not merge) the imported item. 307*61046927SAndroid Build Coastguard Worker continue 308*61046927SAndroid Build Coastguard Worker else: 309*61046927SAndroid Build Coastguard Worker if drop_dupes: 310*61046927SAndroid Build Coastguard Worker # Since this item is not the imported 311*61046927SAndroid Build Coastguard Worker # genxml, we can't consider dropping it. 312*61046927SAndroid Build Coastguard Worker continue 313*61046927SAndroid Build Coastguard Worker if merge: 314*61046927SAndroid Build Coastguard Worker to_add.add(i) 315*61046927SAndroid Build Coastguard Worker else: 316*61046927SAndroid Build Coastguard Worker assert drop_dupes 317*61046927SAndroid Build Coastguard Worker orig_element = orig_by_tag[i.tag][i.attrib['name']] 318*61046927SAndroid Build Coastguard Worker if not node_validator(i, orig_element): 319*61046927SAndroid Build Coastguard Worker continue 320*61046927SAndroid Build Coastguard Worker to_remove.add(orig_element) 321*61046927SAndroid Build Coastguard Worker 322*61046927SAndroid Build Coastguard Worker if len(to_add) > 0: 323*61046927SAndroid Build Coastguard Worker # Now that we have scanned through all the items 324*61046927SAndroid Build Coastguard Worker # in the imported genxml file, if any items were 325*61046927SAndroid Build Coastguard Worker # found which should be merged, we add them into 326*61046927SAndroid Build Coastguard Worker # our `self.et` data structure. After this it will 327*61046927SAndroid Build Coastguard Worker # be as if the items had been directly present in 328*61046927SAndroid Build Coastguard Worker # the genxml file. 329*61046927SAndroid Build Coastguard Worker assert len(to_remove) == 0 330*61046927SAndroid Build Coastguard Worker self.et.getroot().extend(list(to_add)) 331*61046927SAndroid Build Coastguard Worker sort_xml(self.et) 332*61046927SAndroid Build Coastguard Worker elif len(to_remove) > 0: 333*61046927SAndroid Build Coastguard Worker self.et.getroot()[:] = list(orig_elements - to_remove) 334*61046927SAndroid Build Coastguard Worker sort_xml(self.et) 335*61046927SAndroid Build Coastguard Worker 336*61046927SAndroid Build Coastguard Worker def merge_imported(self): 337*61046927SAndroid Build Coastguard Worker """Merge imported items from genxml imports. 338*61046927SAndroid Build Coastguard Worker 339*61046927SAndroid Build Coastguard Worker Genxml <import> tags specify that elements should be brought 340*61046927SAndroid Build Coastguard Worker in from another genxml source file. After this function is 341*61046927SAndroid Build Coastguard Worker called, these elements will become part of the `self.et` data 342*61046927SAndroid Build Coastguard Worker structure as if the elements had been directly included in the 343*61046927SAndroid Build Coastguard Worker genxml directly. 344*61046927SAndroid Build Coastguard Worker 345*61046927SAndroid Build Coastguard Worker Items from imported genxml files will be completely ignore if 346*61046927SAndroid Build Coastguard Worker an item with the same name is already defined in the genxml 347*61046927SAndroid Build Coastguard Worker file. 348*61046927SAndroid Build Coastguard Worker 349*61046927SAndroid Build Coastguard Worker """ 350*61046927SAndroid Build Coastguard Worker self.process_imported(merge=True) 351*61046927SAndroid Build Coastguard Worker 352*61046927SAndroid Build Coastguard Worker def flatten_imported(self): 353*61046927SAndroid Build Coastguard Worker """Flattens the genxml to not include any imports 354*61046927SAndroid Build Coastguard Worker 355*61046927SAndroid Build Coastguard Worker Essentially this helper will put the `self.et` into a state 356*61046927SAndroid Build Coastguard Worker that includes all imported items directly, and does not 357*61046927SAndroid Build Coastguard Worker contain any <import> tags. This is used by the 358*61046927SAndroid Build Coastguard Worker genxml_import.py with the --flatten switch to "undo" any 359*61046927SAndroid Build Coastguard Worker genxml imports. 360*61046927SAndroid Build Coastguard Worker 361*61046927SAndroid Build Coastguard Worker """ 362*61046927SAndroid Build Coastguard Worker self.merge_imported() 363*61046927SAndroid Build Coastguard Worker root = self.et.getroot() 364*61046927SAndroid Build Coastguard Worker imports = root.findall('import') 365*61046927SAndroid Build Coastguard Worker for i in imports: 366*61046927SAndroid Build Coastguard Worker root.remove(i) 367*61046927SAndroid Build Coastguard Worker 368*61046927SAndroid Build Coastguard Worker def add_xml_imports(self): 369*61046927SAndroid Build Coastguard Worker """Adds imports to the genxml file. 370*61046927SAndroid Build Coastguard Worker 371*61046927SAndroid Build Coastguard Worker Using the `default_imports` structure, we add imports to the 372*61046927SAndroid Build Coastguard Worker genxml file. 373*61046927SAndroid Build Coastguard Worker 374*61046927SAndroid Build Coastguard Worker """ 375*61046927SAndroid Build Coastguard Worker # `imports` is a set of filenames currently imported by the 376*61046927SAndroid Build Coastguard Worker # genxml. 377*61046927SAndroid Build Coastguard Worker imports = self.et.findall('import') 378*61046927SAndroid Build Coastguard Worker imports = set(map(lambda el: el.attrib['name'], imports)) 379*61046927SAndroid Build Coastguard Worker new_elements = [] 380*61046927SAndroid Build Coastguard Worker self_flattened = copy.deepcopy(self) 381*61046927SAndroid Build Coastguard Worker self_flattened.flatten_imported() 382*61046927SAndroid Build Coastguard Worker old_names = { el.attrib['name'] for el in self_flattened.et.getroot() } 383*61046927SAndroid Build Coastguard Worker for import_xml in default_imports.get(self.filename.name, tuple()): 384*61046927SAndroid Build Coastguard Worker if import_xml in imports: 385*61046927SAndroid Build Coastguard Worker # This genxml is already imported, so we don't need to 386*61046927SAndroid Build Coastguard Worker # add it as an import. 387*61046927SAndroid Build Coastguard Worker continue 388*61046927SAndroid Build Coastguard Worker el = et.Element('import', {'name': import_xml}) 389*61046927SAndroid Build Coastguard Worker import_path = self.filename.with_name(import_xml) 390*61046927SAndroid Build Coastguard Worker imported_genxml = GenXml(import_path, import_xml=True) 391*61046927SAndroid Build Coastguard Worker imported_names = { el.attrib['name'] 392*61046927SAndroid Build Coastguard Worker for el in imported_genxml.et.getroot() 393*61046927SAndroid Build Coastguard Worker if el.tag != 'import' } 394*61046927SAndroid Build Coastguard Worker # Importing this genxml could add some new items. When 395*61046927SAndroid Build Coastguard Worker # adding a genxml import, we don't want to add new items, 396*61046927SAndroid Build Coastguard Worker # unless they were already in the current genxml. So, we 397*61046927SAndroid Build Coastguard Worker # put them into a list of items to exclude when importing 398*61046927SAndroid Build Coastguard Worker # the genxml. 399*61046927SAndroid Build Coastguard Worker exclude_names = imported_names - old_names 400*61046927SAndroid Build Coastguard Worker for n in sorted(exclude_names): 401*61046927SAndroid Build Coastguard Worker el.append(et.Element('exclude', {'name': n})) 402*61046927SAndroid Build Coastguard Worker new_elements.append(el) 403*61046927SAndroid Build Coastguard Worker if len(new_elements) > 0: 404*61046927SAndroid Build Coastguard Worker self.et.getroot().extend(new_elements) 405*61046927SAndroid Build Coastguard Worker sort_xml(self.et) 406*61046927SAndroid Build Coastguard Worker 407*61046927SAndroid Build Coastguard Worker def optimize_xml_import(self): 408*61046927SAndroid Build Coastguard Worker """Optimizes the genxml by dropping items that can be imported 409*61046927SAndroid Build Coastguard Worker 410*61046927SAndroid Build Coastguard Worker Scans genxml <import> tags, and loads the imported file. If 411*61046927SAndroid Build Coastguard Worker any item in the imported file is a duplicate to an item in the 412*61046927SAndroid Build Coastguard Worker genxml file, then it will be droped from the `self.et` data 413*61046927SAndroid Build Coastguard Worker structure. 414*61046927SAndroid Build Coastguard Worker 415*61046927SAndroid Build Coastguard Worker """ 416*61046927SAndroid Build Coastguard Worker self.process_imported(drop_dupes=True) 417*61046927SAndroid Build Coastguard Worker 418*61046927SAndroid Build Coastguard Worker def filter_engines(self, engines): 419*61046927SAndroid Build Coastguard Worker changed = False 420*61046927SAndroid Build Coastguard Worker items = [] 421*61046927SAndroid Build Coastguard Worker for item in self.et.getroot(): 422*61046927SAndroid Build Coastguard Worker # When an instruction doesn't have the engine specified, 423*61046927SAndroid Build Coastguard Worker # it is considered to be for all engines. Otherwise, we 424*61046927SAndroid Build Coastguard Worker # check to see if it's tagged for the engines requested. 425*61046927SAndroid Build Coastguard Worker if item.tag == 'instruction' and 'engine' in item.attrib: 426*61046927SAndroid Build Coastguard Worker i_engines = set(item.attrib["engine"].split('|')) 427*61046927SAndroid Build Coastguard Worker if not (i_engines & engines): 428*61046927SAndroid Build Coastguard Worker # Drop this instruction because it doesn't support 429*61046927SAndroid Build Coastguard Worker # the requested engine types. 430*61046927SAndroid Build Coastguard Worker changed = True 431*61046927SAndroid Build Coastguard Worker continue 432*61046927SAndroid Build Coastguard Worker items.append(item) 433*61046927SAndroid Build Coastguard Worker if changed: 434*61046927SAndroid Build Coastguard Worker self.et.getroot()[:] = items 435*61046927SAndroid Build Coastguard Worker 436*61046927SAndroid Build Coastguard Worker def filter_symbols(self, symbol_list): 437*61046927SAndroid Build Coastguard Worker symbols_allowed = {} 438*61046927SAndroid Build Coastguard Worker for sym in symbol_list: 439*61046927SAndroid Build Coastguard Worker symbols_allowed[sym] = sym 440*61046927SAndroid Build Coastguard Worker 441*61046927SAndroid Build Coastguard Worker changed = False 442*61046927SAndroid Build Coastguard Worker items = [] 443*61046927SAndroid Build Coastguard Worker for item in self.et.getroot(): 444*61046927SAndroid Build Coastguard Worker if item.tag in ('instruction', 'struct', 'register') and \ 445*61046927SAndroid Build Coastguard Worker item.attrib['name'] not in symbols_allowed: 446*61046927SAndroid Build Coastguard Worker # Drop the item from the tree 447*61046927SAndroid Build Coastguard Worker changed = True 448*61046927SAndroid Build Coastguard Worker continue 449*61046927SAndroid Build Coastguard Worker items.append(item) 450*61046927SAndroid Build Coastguard Worker if changed: 451*61046927SAndroid Build Coastguard Worker self.et.getroot()[:] = items 452*61046927SAndroid Build Coastguard Worker 453*61046927SAndroid Build Coastguard Worker def sort(self): 454*61046927SAndroid Build Coastguard Worker sort_xml(self.et) 455*61046927SAndroid Build Coastguard Worker 456*61046927SAndroid Build Coastguard Worker def sorted_copy(self): 457*61046927SAndroid Build Coastguard Worker clone = copy.deepcopy(self) 458*61046927SAndroid Build Coastguard Worker clone.sort() 459*61046927SAndroid Build Coastguard Worker return clone 460*61046927SAndroid Build Coastguard Worker 461*61046927SAndroid Build Coastguard Worker def is_equivalent_xml(self, other): 462*61046927SAndroid Build Coastguard Worker if len(self.et.getroot()) != len(other.et.getroot()): 463*61046927SAndroid Build Coastguard Worker return False 464*61046927SAndroid Build Coastguard Worker return all(node_validator(old, new) 465*61046927SAndroid Build Coastguard Worker for old, new in zip(self.et.getroot(), other.et.getroot())) 466*61046927SAndroid Build Coastguard Worker 467*61046927SAndroid Build Coastguard Worker def write_file(self): 468*61046927SAndroid Build Coastguard Worker try: 469*61046927SAndroid Build Coastguard Worker old_genxml = GenXml(self.filename) 470*61046927SAndroid Build Coastguard Worker if self.is_equivalent_xml(old_genxml): 471*61046927SAndroid Build Coastguard Worker return 472*61046927SAndroid Build Coastguard Worker except Exception: 473*61046927SAndroid Build Coastguard Worker pass 474*61046927SAndroid Build Coastguard Worker 475*61046927SAndroid Build Coastguard Worker b_io = io.BytesIO() 476*61046927SAndroid Build Coastguard Worker et.indent(self.et, space=' ') 477*61046927SAndroid Build Coastguard Worker self.et.write(b_io, encoding="utf-8", xml_declaration=True) 478*61046927SAndroid Build Coastguard Worker b_io.write(b'\n') 479*61046927SAndroid Build Coastguard Worker 480*61046927SAndroid Build Coastguard Worker tmp = self.filename.with_suffix(f'{self.filename.suffix}.tmp') 481*61046927SAndroid Build Coastguard Worker tmp.write_bytes(b_io.getvalue()) 482*61046927SAndroid Build Coastguard Worker tmp.replace(self.filename) 483