# 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` package
"""
# Futures
from __future__ import absolute_import, annotations
# Built-in modules
import abc
import os
from typing import TYPE_CHECKING, List
# Third party modules
# Own modules
import microprobe.target.uarch.element
import microprobe.target.uarch.element_type
from microprobe import MICROPROBE_RC
from microprobe.exceptions import MicroprobeYamlFormatError
from microprobe.property import PropertyHolder, import_properties
from microprobe.target.uarch.cache import cache_hierarchy_from_elements
from microprobe.utils.imp import get_object_from_module, import_definition
from microprobe.utils.logger import get_logger
from microprobe.utils.misc import findfiles
from microprobe.utils.typeguard_decorator import typeguard_testsuite
from microprobe.utils.yaml import read_yaml
# Local modules
# Type hinting
if TYPE_CHECKING:
from microprobe.target import Target, Definition
# Constants
SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "schemas",
"microarchitecture.yaml")
DEFAULT_UARCH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"default", "microarchitecture.yaml")
LOG = get_logger(__name__)
__all__ = [
"import_microarchitecture_definition",
"find_microarchitecture_definitions", "Microarchitecture",
"GenericMicroarchitecture", "GenericCPUMicroarchitecture"
]
# Functions
@typeguard_testsuite
def _read_uarch_extensions(uarchdefs, path: str):
""" """
if "Extends" in uarchdefs[-1]:
uarchdefval = uarchdefs[-1]["Extends"]
del uarchdefs[-1]["Extends"]
if not os.path.isabs(uarchdefval):
uarchdefval = os.path.join(path, uarchdefval)
uarchdef = read_yaml(
os.path.join(uarchdefval, "microarchitecture.yaml"), SCHEMA)
uarchdef["Path"] = uarchdefval
uarchdefs.append(uarchdef)
_read_uarch_extensions(uarchdefs, uarchdefval)
@typeguard_testsuite
def _read_yaml_definition(uarchdefs, path: str):
"""
:param uarchdefs:
:param path:
"""
uarchdef = read_yaml(os.path.join(path, "microarchitecture.yaml"), SCHEMA)
uarchdef["Path"] = path
uarchdefs.append(uarchdef)
_read_uarch_extensions(uarchdefs, path)
baseuarch = read_yaml(DEFAULT_UARCH, SCHEMA)
baseuarch["Path"] = DEFAULT_UARCH
uarchdefs.append(baseuarch)
complete_uarchdef = {}
uarchdefs.reverse()
for uarchdef in uarchdefs:
for key, val in uarchdef.items():
if not isinstance(val, dict):
complete_uarchdef[key] = uarchdef[key]
else:
override = val.get("Override", False)
if key not in complete_uarchdef:
complete_uarchdef[key] = {}
for key2 in val:
if key2 in ["YAML", "Modules", "Path"]:
if key2 not in complete_uarchdef[key]:
complete_uarchdef[key][key2] = []
if os.path.isabs(val[key2]):
if override:
complete_uarchdef[key][key2] = [val[key2]]
else:
complete_uarchdef[key][key2].append(val[key2])
else:
if override:
complete_uarchdef[key][key2] = [
os.path.join(uarchdef["Path"], val[key2])
]
else:
complete_uarchdef[key][key2].append(
os.path.join(uarchdef["Path"], val[key2]))
elif key2 == "Module":
if val[key2].startswith("microprobe"):
val[key2] = os.path.join(os.path.dirname(__file__),
"..", "..", "..",
val[key2])
if os.path.isabs(val[key2]):
complete_uarchdef[key][key2] = val[key2]
else:
complete_uarchdef[key][key2] = os.path.join(
uarchdef["Path"], val[key2])
else:
complete_uarchdef[key][key2] = val[key2]
return complete_uarchdef
[docs]
@typeguard_testsuite
def import_microarchitecture_definition(path: str):
"""Imports a Microarchitecture definition given a path
:param path:
"""
LOG.info("Start microarchitecture import")
LOG.debug("Importing definition from '%s'", path)
if not os.path.isabs(path):
path = os.path.abspath(path)
uarchdef = _read_yaml_definition([], path)
element_types, force = import_definition(
uarchdef, os.path.join(path, "microarchitecture.yaml"), "Element_type",
getattr(microprobe.target.uarch, 'element_type'), None)
element, force = import_definition(uarchdef,
os.path.join(path,
"microarchitecture.yaml"),
"Element",
getattr(microprobe.target.uarch,
'element'),
element_types,
force=force)
uarch_cls = get_object_from_module(uarchdef["Microarchitecture"]["Class"],
uarchdef["Microarchitecture"]["Module"])
uarch = uarch_cls(uarchdef["Name"], uarchdef["Description"], element,
uarchdef["Instruction_properties"]["Path"])
import_properties(os.path.join(path, "microarchitecture.yaml"),
{uarchdef["Name"]: uarch})
LOG.info("Microarchitecture '%s' imported", uarch)
return uarch
[docs]
@typeguard_testsuite
def find_microarchitecture_definitions(paths: List[str] | None = None):
if paths is None:
paths = []
paths = paths + MICROPROBE_RC["microarchitecture_paths"] \
+ MICROPROBE_RC["default_paths"]
results: List[Definition] = []
uarchfiles = findfiles(paths, "^microarchitecture.yaml$")
if len(uarchfiles) > 0:
from microprobe.target import Definition
for uarchfile in uarchfiles:
try:
isadef = read_yaml(uarchfile, SCHEMA)
except MicroprobeYamlFormatError as exc:
LOG.info("Exception: %s", exc)
LOG.info("Skipping '%s'", uarchfile)
continue
try:
definition = Definition(uarchfile, isadef["Name"],
isadef["Description"])
if (definition not in results
and not definition.name.endswith("common")):
results.append(definition)
except TypeError as exc:
# Skip bad definitions
LOG.info("Exception: %s", exc)
LOG.info("Skipping '%s'", uarchfile)
continue
return results
# Classes
[docs]
@typeguard_testsuite
class Microarchitecture(PropertyHolder, metaclass=abc.ABCMeta):
""" """
[docs]
@abc.abstractmethod
def __init__(self):
pass
@property
@abc.abstractmethod
def name(self) -> str:
raise NotImplementedError
@property
@abc.abstractmethod
def description(self) -> str:
raise NotImplementedError
@property
@abc.abstractmethod
def elements(self):
raise NotImplementedError
[docs]
@abc.abstractmethod
def add_properties_to_isa(self, instructions):
"""
:param instructions:
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def full_report(self):
raise NotImplementedError
@abc.abstractmethod
def __str__(self) -> str:
raise NotImplementedError
[docs]
@abc.abstractmethod
def set_target(self, target: Target):
"""
:param target:
"""
raise NotImplementedError
@property
@abc.abstractmethod
def target(self):
raise NotImplementedError
[docs]
@typeguard_testsuite
class GenericMicroarchitecture(Microarchitecture):
""" """
[docs]
def __init__(self, name: str, descr: str, elements,
instruction_properties_defs):
"""
:param name:
:param descr:
:param elements:
:param instruction_properties_defs:
"""
super(GenericMicroarchitecture, self).__init__()
self._name = name
self._descr = descr
self._elements = elements
self._target: Target | None = None
self._instruction_property_defs = instruction_properties_defs
@property
def name(self):
return self._name
@property
def description(self):
return self._descr
@property
def elements(self):
return self._elements
@property
def target(self):
return self._target
[docs]
def set_target(self, target: Target):
"""
:param target:
"""
self._target = target
[docs]
def add_properties_to_isa(self, instructions):
"""
:param instructions:
"""
for cfile in self._instruction_property_defs:
import_properties(cfile, instructions)
[docs]
def full_report(self):
rstr = "-" * 80 + "\n"
rstr += "Microarchitecture Name: %s\n" % self.name
rstr += "Microarchitecture Description: %s\n" % self.name
rstr += "-" * 80 + "\n"
rstr += "Element Types:\n"
for elem in sorted(set([elem.type
for elem in self.elements.values()])):
rstr += str(elem) + "\n"
rstr += "-" * 80 + "\n"
rstr += "Elements:\n"
for elem in sorted(self.elements.values()):
rstr += str(elem) + "\n"
rstr += "-" * 80 + "\n"
return rstr
def __str__(self):
return "%s('%s', '%s')" % (self.__class__.__name__, self.name,
self.description)
[docs]
@typeguard_testsuite
class GenericCPUMicroarchitecture(GenericMicroarchitecture):
"""Generic CPU Microarchitecture
Generic CPU microarchitecture. Assumes a cache hierarchy
"""
[docs]
def __init__(self, name: str, descr: str, elements,
instruction_properties_defs):
"""
:param name:
:param descr:
:param elements:
:param instruction_properties_defs:
"""
super(GenericCPUMicroarchitecture,
self).__init__(name, descr, elements,
instruction_properties_defs)
self._cache_hierarchy = cache_hierarchy_from_elements(elements)
@property
def cache_hierarchy(self):
return self._cache_hierarchy