xref: /aosp_15_r20/external/mesa3d/src/intel/genxml/intel_genxml.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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