diff options
author | Jordan Justen <jordan.l.justen@intel.com> | 2022-12-23 01:59:37 -0800 |
---|---|---|
committer | Jordan Justen <jordan.l.justen@intel.com> | 2023-08-07 14:58:57 -0700 |
commit | df27f28c77df2760fedcabf905a8c2e3120617b2 (patch) | |
tree | f30ee4886d9d3df676dc9f3ed903d64274db92e6 | |
parent | 97d54b4ede2031b063283b001359dd99742fcef5 (diff) |
intel/genxml: Split some genxml sorting code into a intel_genxml module
Signed-off-by: Jordan Justen <jordan.l.justen@intel.com>
Acked-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com>
-rwxr-xr-x | src/intel/genxml/gen_sort_tags.py | 162 | ||||
-rwxr-xr-x | src/intel/genxml/intel_genxml.py | 166 | ||||
-rw-r--r-- | src/intel/genxml/meson.build | 2 |
3 files changed, 170 insertions, 160 deletions
diff --git a/src/intel/genxml/gen_sort_tags.py b/src/intel/genxml/gen_sort_tags.py index 12ffe6c353e..2e74c3225bc 100755 --- a/src/intel/genxml/gen_sort_tags.py +++ b/src/intel/genxml/gen_sort_tags.py @@ -3,169 +3,13 @@ # SPDX-License-Identifier: MIT from __future__ import annotations -from collections import OrderedDict import argparse import copy +import intel_genxml import pathlib -import re import xml.etree.ElementTree as et import typing -if typing.TYPE_CHECKING: - class Args(typing.Protocol): - - files: typing.List[pathlib.Path] - validate: bool - quiet: bool - - -def get_filename(element: et.Element) -> str: - return element.attrib['filename'] - -def get_name(element: et.Element) -> str: - return element.attrib['name'] - -def get_value(element: et.Element) -> int: - return int(element.attrib['value'], 0) - -def get_start(element: et.Element) -> int: - return int(element.attrib['start'], 0) - - -BASE_TYPES = { - 'address', - 'offset', - 'int', - 'uint', - 'bool', - 'float', - 'mbz', - 'mbo', -} - -FIXED_PATTERN = re.compile(r"(s|u)(\d+)\.(\d+)") - -def is_base_type(name: str) -> bool: - return name in BASE_TYPES or FIXED_PATTERN.match(name) is not None - -def add_struct_refs(items: typing.OrderedDict[str, bool], node: et.Element) -> None: - if node.tag == 'field': - if 'type' in node.attrib and not is_base_type(node.attrib['type']): - t = node.attrib['type'] - items[t] = True - return - if node.tag not in {'struct', 'group'}: - return - for c in node: - add_struct_refs(items, c) - - -class Struct(object): - def __init__(self, xml: et.Element): - self.xml = xml - self.name = xml.attrib['name'] - self.deps: typing.OrderedDict[str, Struct] = OrderedDict() - - def find_deps(self, struct_dict, enum_dict) -> None: - deps: typing.OrderedDict[str, bool] = OrderedDict() - add_struct_refs(deps, self.xml) - for d in deps.keys(): - if d in struct_dict: - self.deps[d] = struct_dict[d] - else: - assert d in enum_dict - - def add_xml(self, items: typing.OrderedDict[str, et.Element]) -> None: - for d in self.deps.values(): - d.add_xml(items) - items[self.name] = self.xml - - -# ordering of the various tag attributes -GENXML_DESC = { - 'genxml' : [ 'name', 'gen', ], - 'enum' : [ 'name', 'value', 'prefix', ], - 'struct' : [ 'name', 'length', ], - 'field' : [ 'name', 'start', 'end', 'type', 'default', 'prefix', 'nonzero' ], - 'instruction' : [ 'name', 'bias', 'length', 'engine', ], - 'value' : [ 'name', 'value', 'dont_use', ], - 'group' : [ 'count', 'start', 'size', ], - 'register' : [ 'name', 'length', 'num', ], -} - - -def node_validator(old: et.Element, new: et.Element) -> bool: - """Compare to ElementTree Element nodes. - - There is no builtin equality method, so calling `et.Element == et.Element` is - equivalent to calling `et.Element is et.Element`. We instead want to compare - that the contents are the same, including the order of children and attributes - """ - return ( - # Check that the attributes are the same - old.tag == new.tag and - old.text == new.text and - old.tail == new.tail and - list(old.attrib.items()) == list(new.attrib.items()) and - len(old) == len(new) and - - # check that there are no unexpected attributes - set(new.attrib).issubset(GENXML_DESC[new.tag]) and - - # check that the attributes are sorted - list(new.attrib) == list(old.attrib) and - all(node_validator(f, s) for f, s in zip(old, new)) - ) - - -def process_attribs(elem: et.Element) -> None: - valid = GENXML_DESC[elem.tag] - # sort and prune attributes - elem.attrib = OrderedDict(sorted(((k, v) for k, v in elem.attrib.items() if k in valid), - key=lambda x: valid.index(x[0]))) - for e in elem: - process_attribs(e) - - -def process(xml: et.ElementTree) -> None: - genxml = xml.getroot() - enums = sorted(xml.findall('enum'), key=get_name) - enum_dict: typing.Dict[str, et.Element] = {} - for e in enums: - e[:] = sorted(e, key=get_value) - enum_dict[e.attrib['name']] = e - - # Structs are a bit annoying because they can refer to each other. We sort - # them alphabetically and then build a graph of dependencies. Finally we go - # through the alphabetically sorted list and print out dependencies first. - structs = sorted(xml.findall('./struct'), key=get_name) - wrapped_struct_dict: typing.Dict[str, Struct] = {} - for s in structs: - s[:] = sorted(s, key=get_start) - ws = Struct(s) - wrapped_struct_dict[ws.name] = ws - - for ws in wrapped_struct_dict.values(): - ws.find_deps(wrapped_struct_dict, enum_dict) - - sorted_structs: typing.OrderedDict[str, et.Element] = OrderedDict() - for s in structs: - _s = wrapped_struct_dict[s.attrib['name']] - _s.add_xml(sorted_structs) - - instructions = sorted(xml.findall('./instruction'), key=get_name) - for i in instructions: - i[:] = sorted(i, key=get_start) - - registers = sorted(xml.findall('./register'), key=get_name) - for r in registers: - r[:] = sorted(r, key=get_start) - - new_elems = enums + list(sorted_structs.values()) + instructions + registers - for n in new_elems: - process_attribs(n) - genxml[:] = new_elems - def main() -> None: parser = argparse.ArgumentParser() @@ -182,11 +26,11 @@ def main() -> None: xml = et.parse(filename) original = copy.deepcopy(xml) if args.validate else xml - process(xml) + intel_genxml.sort_xml(xml) if args.validate: for old, new in zip(original.getroot(), xml.getroot()): - assert node_validator(old, new), f'{filename} is invalid, run gen_sort_tags.py and commit that' + assert intel_genxml.node_validator(old, new), f'{filename} is invalid, run gen_sort_tags.py and commit that' else: tmp = filename.with_suffix(f'{filename.suffix}.tmp') et.indent(xml, space=' ') diff --git a/src/intel/genxml/intel_genxml.py b/src/intel/genxml/intel_genxml.py new file mode 100755 index 00000000000..dfbf8c10690 --- /dev/null +++ b/src/intel/genxml/intel_genxml.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# Copyright © 2019, 2022 Intel Corporation +# SPDX-License-Identifier: MIT + +from __future__ import annotations +from collections import OrderedDict +import copy +import pathlib +import re +import xml.etree.ElementTree as et +import typing + +if typing.TYPE_CHECKING: + class Args(typing.Protocol): + + files: typing.List[pathlib.Path] + validate: bool + quiet: bool + + +def get_filename(element: et.Element) -> str: + return element.attrib['filename'] + +def get_name(element: et.Element) -> str: + return element.attrib['name'] + +def get_value(element: et.Element) -> int: + return int(element.attrib['value'], 0) + +def get_start(element: et.Element) -> int: + return int(element.attrib['start'], 0) + + +BASE_TYPES = { + 'address', + 'offset', + 'int', + 'uint', + 'bool', + 'float', + 'mbz', + 'mbo', +} + +FIXED_PATTERN = re.compile(r"(s|u)(\d+)\.(\d+)") + +def is_base_type(name: str) -> bool: + return name in BASE_TYPES or FIXED_PATTERN.match(name) is not None + +def add_struct_refs(items: typing.OrderedDict[str, bool], node: et.Element) -> None: + if node.tag == 'field': + if 'type' in node.attrib and not is_base_type(node.attrib['type']): + t = node.attrib['type'] + items[t] = True + return + if node.tag not in {'struct', 'group'}: + return + for c in node: + add_struct_refs(items, c) + + +class Struct(object): + def __init__(self, xml: et.Element): + self.xml = xml + self.name = xml.attrib['name'] + self.deps: typing.OrderedDict[str, Struct] = OrderedDict() + + def find_deps(self, struct_dict, enum_dict) -> None: + deps: typing.OrderedDict[str, bool] = OrderedDict() + add_struct_refs(deps, self.xml) + for d in deps.keys(): + if d in struct_dict: + self.deps[d] = struct_dict[d] + else: + assert d in enum_dict + + def add_xml(self, items: typing.OrderedDict[str, et.Element]) -> None: + for d in self.deps.values(): + d.add_xml(items) + items[self.name] = self.xml + + +# ordering of the various tag attributes +GENXML_DESC = { + 'genxml' : [ 'name', 'gen', ], + 'enum' : [ 'name', 'value', 'prefix', ], + 'struct' : [ 'name', 'length', ], + 'field' : [ 'name', 'start', 'end', 'type', 'default', 'prefix', 'nonzero' ], + 'instruction' : [ 'name', 'bias', 'length', 'engine', ], + 'value' : [ 'name', 'value', 'dont_use', ], + 'group' : [ 'count', 'start', 'size', ], + 'register' : [ 'name', 'length', 'num', ], +} + + +def node_validator(old: et.Element, new: et.Element) -> bool: + """Compare to ElementTree Element nodes. + + There is no builtin equality method, so calling `et.Element == et.Element` is + equivalent to calling `et.Element is et.Element`. We instead want to compare + that the contents are the same, including the order of children and attributes + """ + return ( + # Check that the attributes are the same + old.tag == new.tag and + old.text == new.text and + old.tail == new.tail and + list(old.attrib.items()) == list(new.attrib.items()) and + len(old) == len(new) and + + # check that there are no unexpected attributes + set(new.attrib).issubset(GENXML_DESC[new.tag]) and + + # check that the attributes are sorted + list(new.attrib) == list(old.attrib) and + all(node_validator(f, s) for f, s in zip(old, new)) + ) + + +def process_attribs(elem: et.Element) -> None: + valid = GENXML_DESC[elem.tag] + # sort and prune attributes + elem.attrib = OrderedDict(sorted(((k, v) for k, v in elem.attrib.items() if k in valid), + key=lambda x: valid.index(x[0]))) + for e in elem: + process_attribs(e) + + +def sort_xml(xml: et.ElementTree) -> None: + genxml = xml.getroot() + enums = sorted(xml.findall('enum'), key=get_name) + enum_dict: typing.Dict[str, et.Element] = {} + for e in enums: + e[:] = sorted(e, key=get_value) + enum_dict[e.attrib['name']] = e + + # Structs are a bit annoying because they can refer to each other. We sort + # them alphabetically and then build a graph of dependencies. Finally we go + # through the alphabetically sorted list and print out dependencies first. + structs = sorted(xml.findall('./struct'), key=get_name) + wrapped_struct_dict: typing.Dict[str, Struct] = {} + for s in structs: + s[:] = sorted(s, key=get_start) + ws = Struct(s) + wrapped_struct_dict[ws.name] = ws + + for ws in wrapped_struct_dict.values(): + ws.find_deps(wrapped_struct_dict, enum_dict) + + sorted_structs: typing.OrderedDict[str, et.Element] = OrderedDict() + for s in structs: + _s = wrapped_struct_dict[s.attrib['name']] + _s.add_xml(sorted_structs) + + instructions = sorted(xml.findall('./instruction'), key=get_name) + for i in instructions: + i[:] = sorted(i, key=get_start) + + registers = sorted(xml.findall('./register'), key=get_name) + for r in registers: + r[:] = sorted(r, key=get_start) + + new_elems = enums + list(sorted_structs.values()) + instructions + registers + for n in new_elems: + process_attribs(n) + genxml[:] = new_elems diff --git a/src/intel/genxml/meson.build b/src/intel/genxml/meson.build index bd889f1b1f8..b8bfaa33cad 100644 --- a/src/intel/genxml/meson.build +++ b/src/intel/genxml/meson.build @@ -36,7 +36,7 @@ gen_xml_rt_files = [ 'gen125_rt.xml', ] -gen_pack_header_deps = files('util.py') +gen_pack_header_deps = files('intel_genxml.py', 'util.py') genX_xml_h = custom_target( 'genX_xml.h', |