Source code for microprobe.target.uarch.element

# Copyright 2011-2021 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""":mod:`microprobe.target.uarch.element` module

"""

# Futures
from __future__ import absolute_import

# Built-in modules
import abc
import hashlib
import os

# Third party modules

# Own modules
from microprobe.exceptions import MicroprobeArchitectureDefinitionError
from microprobe.property import PropertyHolder, import_properties
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import RejectingDict
from microprobe.utils.typeguard_decorator import typeguard_testsuite
from microprobe.utils.yaml import read_yaml

# Constants
SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "schemas",
                      "element.yaml")

LOG = get_logger(__name__)
__all__ = [
    "import_definition", "MicroarchitectureElement",
    "GenericMicroarchitectureElement"
]


# Functions
[docs] @typeguard_testsuite def import_definition(cls, filenames, element_types): """ """ LOG.debug("Start importing microarchitecture elements") elements = RejectingDict() elements_subelements = RejectingDict() for filename in filenames: element_data = read_yaml(filename, SCHEMA) if element_data is None: continue for elem in element_data: name = elem["Name"] parent = elem.get("Parent", None) subelements = elem.get("Subelements", []) repeat = elem.get("Repeat", None) rfrom = 0 rto = 0 replace = "0" try: elem_type = element_types[elem["Type"]] except KeyError: raise MicroprobeArchitectureDefinitionError( "Unknown " "microarchitecture element type in " "microarchitecture element definition " " '%s' found in '%s'" % (name, filename)) descr = elem.get("Description", elem_type.description) if repeat: rfrom = repeat["From"] replace = "%s" % rfrom rto = repeat["To"] for index in range(rfrom, rto + 1): cname = name.replace(replace, "%d" % index) cdescr = descr.replace(replace, "%d" % index) element = cls(cname, cdescr, elem_type) try: elements[cname] = element elements_subelements[cname] = subelements except ValueError: raise MicroprobeArchitectureDefinitionError( "Duplicated microarchitecture element " "definition '%s' found in '%s'" % (name, filename)) LOG.debug(element) for filename in filenames: import_properties(filename, elements) for elem, subelements in elements_subelements.items(): try: subelements_instances = [elements[item] for item in subelements] except KeyError as exc: raise MicroprobeArchitectureDefinitionError( "Undefined sub-element '%s' in element " "definition '%s'. Check following " "files: %s" % (exc, elem, filenames)) elements[elem].set_subelements(subelements_instances) element_list = list(elements.values()) fixing_hierarchy = True LOG.info("Start building element hierarchy...") fix_pass = 0 while fixing_hierarchy: fix_pass += 1 LOG.debug("Start building element hierarchy... pass %d", fix_pass) fixing_hierarchy = False for element in element_list: parents = [ item for item in element_list if element in item.subelements ] if len(parents) > 1: # needs duplication LOG.debug("Element %s has %d parents", element, len(parents)) for parent in sorted(parents): LOG.debug("Duplicating for parent: %s", parent) # Create a new copy new_element = cls(element.name, element.description, element.type) new_element.set_subelements(element.subelements) element_list.append(new_element) # Update parent to point to the new copy new_subelements = parent.subelements new_subelements.remove(element) new_subelements.append(new_element) parent.set_subelements(new_subelements) fixing_hierarchy = True element_list.remove(element) LOG.info("Finish building element hierarchy") # Check correctness of the structure and set parents LOG.info("Checking element hierarchy...") top_element = None for element in element_list: parents = [ item for item in element_list if element in item.subelements ] if len(parents) > 1: raise MicroprobeArchitectureDefinitionError( "Wrong hierarchy of microarchitecture " "elements. The definition of element" " '%s' has multiple parents: '%s'." % (element, [str(elem) for elem in parents])) elif len(parents) == 0: if top_element is not None: raise MicroprobeArchitectureDefinitionError( "Wrong hierarchy of microarchitecture " "elements. There are at least two top " "elements: '%s' and '%s'. Define a single " "parent element for all the hierarchy." % (element, top_element)) top_element = element else: element.set_parent_element(parents[0]) if top_element is None: raise MicroprobeArchitectureDefinitionError( "Wrong hierarchy of microarchitecture " "elements. There is not a top element." " Define a single parent element for all " "the hierarchy.") LOG.info("Element hierarchy correct") elem_dict = dict([(element.full_name, element) for element in element_list]) for filename in filenames: import_properties(filename, elem_dict) LOG.info("End importing elements") return elem_dict
# Classes
[docs] @typeguard_testsuite class MicroarchitectureElement(PropertyHolder, metaclass=abc.ABCMeta): """ """
[docs] @abc.abstractmethod def __init__(self): pass
@property @abc.abstractmethod def name(self) -> str: raise NotImplementedError @property @abc.abstractmethod def full_name(self) -> str: raise NotImplementedError @property @abc.abstractmethod def description(self) -> str: raise NotImplementedError @property @abc.abstractmethod def type(self): raise NotImplementedError @property @abc.abstractmethod def depth(self): raise NotImplementedError @property @abc.abstractmethod def subelements(self): raise NotImplementedError
[docs] @abc.abstractmethod def set_subelements(self, subelements): """ :param subelements: """ raise NotImplementedError
@property @abc.abstractmethod def parent(self): raise NotImplementedError @property @abc.abstractmethod def parents(self): raise NotImplementedError
[docs] @abc.abstractmethod def set_parent_element(self, parent): """ :param parent: """ raise NotImplementedError
[docs] @abc.abstractmethod def closest_common_element(self, element): """ :param element: """ raise NotImplementedError
@abc.abstractmethod def __str__(self): """Return the string representation of this element""" raise NotImplementedError
[docs] @typeguard_testsuite class GenericMicroarchitectureElement(MicroarchitectureElement): """ """ _cmp_attributes = ["depth", "type", "full_name", "name", "description"]
[docs] def __init__(self, name, descr, mtype): """ :param name: :param descr: :param mtype: """ super(GenericMicroarchitectureElement, self).__init__() self._name = name self._descr = descr self._type = mtype self._parent = None self._subelements = {} self._hash = int( hashlib.sha512( (self.name + self.description + self.full_name + str(self.type) + str(self.depth)).encode()).hexdigest(), 16)
@property def name(self): return self._name @property def full_name(self): if self._parent is None: return self._name return "%s_%s" % (self._name, self.parent.full_name) @property def parents(self): if self._parent is None: return [] return [self.parent] + self.parent.parents @property def description(self): return self._descr @property def type(self): return self._type @property def subelements(self): return list(self._subelements.values()) @property def parent(self): return self._parent @property def depth(self): if self._parent is None: return 0 return 1 + self.parent.depth
[docs] def set_parent_element(self, parent): """ :param parent: """ self._parent = parent assert self in parent.subelements
[docs] def set_subelements(self, subelements): """ :param subelements: """ self._subelements = dict([(element.name, element) for element in subelements]) for subelement in subelements: subelement.set_parent_element(self)
[docs] def closest_common_element(self, element): """ :param element: """ own_parents = self.parents other_parents = element.parents for elem in own_parents: if elem in other_parents: return elem return None
def __hash__(self): return self._hash def __str__(self): """Return the string representation of this element""" return "%s('%s','%s','%s')" % (self.__class__.__name__, self.name, self.full_name, self.description) def _check_cmp(self, other): if not isinstance(other, self.__class__): raise NotImplementedError("%s != %s" % (other.__class__, self.__class__)) def __eq__(self, other): """x.__eq__(y) <==> x==y""" self._check_cmp(other) for attr in self._cmp_attributes: if not getattr(self, attr) == getattr(other, attr): return False return True def __ne__(self, other): """x.__ne__(y) <==> x!=y""" self._check_cmp(other) for attr in self._cmp_attributes: if not getattr(self, attr) == getattr(other, attr): return True return False def __lt__(self, other): """x.__lt__(y) <==> x<y""" self._check_cmp(other) for attr in self._cmp_attributes: if getattr(self, attr) < getattr(other, attr): return True elif getattr(self, attr) > getattr(other, attr): return False return False def __gt__(self, other): """x.__gt__(y) <==> x>y""" self._check_cmp(other) for attr in self._cmp_attributes: if getattr(self, attr) > getattr(other, attr): return True elif getattr(self, attr) < getattr(other, attr): return False return False def __le__(self, other): """x.__le__(y) <==> x<=y""" self._check_cmp(other) for attr in self._cmp_attributes: if getattr(self, attr) <= getattr(other, attr): continue else: return False return True def __ge__(self, other): """x.__ge__(y) <==> x>=y""" self._check_cmp(other) for attr in self._cmp_attributes: if getattr(self, attr) >= getattr(other, attr): continue else: return False return True