# SPDX-License-Identifier: Apache-2.0
#
# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R
#
# Copyright (C) 2022
# Chair of Electrical Design Automation
# Technical University of Munich
"""Utility classes and functions for instruction generation."""
from dataclasses import asdict, dataclass
from itertools import chain
from string import Template
from ... import M2ValueError
from ...metamodel import arch
from ...metamodel.code_info import LineInfo
from ...metamodel.utils import StaticType
from . import replacements
[docs]
data_type_map = {
arch.DataType.S: 'etiss_int',
arch.DataType.U: 'etiss_uint',
arch.DataType.NONE: 'void'
}
[docs]
MEM_VAL_REPL = 'mem_val_'
[docs]
FN_VAL_REPL = "fn_val_"
[docs]
def actual_size(size, min_=8, max_=128):
"""Calculate a fitting c datatype width for any arbitrary size."""
s = 1 << (size - 1).bit_length()
if s > max_:
raise M2ValueError("value too big")
return s if s >= min_ else min_
[docs]
class CodeString:
"""Code string object. Tracks generate C++ code and various metadata for recursive
code generation.
"""
[docs]
function_calls: "list[FnID]"
[docs]
line_infos: "list[LineInfo]"
def __init__(self, code, static, size, signed, regs_affected=None, line_infos=[]):
[docs]
self.static = StaticType(static)
self.mem_ids = []
[docs]
self.regs_affected = regs_affected if isinstance(regs_affected, set) else set()
[docs]
self.mem_corrected = False
[docs]
self.is_literal = False
self.function_calls = []
[docs]
self.check_trap = False
if isinstance(line_infos, LineInfo):
self.line_infos = [line_infos]
elif isinstance(line_infos, list):
self.line_infos = line_infos
else:
self.line_infos = []
@property
[docs]
def actual_size(self):
return actual_size(self.size)
@property
[docs]
def needs_fn_call(self):
return len(self.function_calls) > 0
@property
[docs]
def is_mem_access(self):
return len(self.mem_ids) > 0
@property
[docs]
def write_mem_ids(self):
for m in self.mem_ids:
if m.write:
yield m
@property
[docs]
def read_mem_ids(self):
for m in self.mem_ids:
if not m.write:
yield m
[docs]
def __str__(self):
return self.code
@dataclass
[docs]
class MemID:
"""Track a memory access across recursive code generation."""
@dataclass
[docs]
class FnID:
"""Track a required function call across recursive code generation."""
@dataclass
[docs]
class CodePartsContainer:
"""Container class to encapsulate different ETISS JIT code snippet types."""
[docs]
pre_initial_debug_returning: str = None
[docs]
initial_required: str = None
[docs]
optional_middle: str = None
[docs]
appended_required: str = None
[docs]
appended_optional: str = None
[docs]
appended_returning_required: str = None
[docs]
def generate(self):
return {name.replace("_", "").upper(): part for name, part in asdict(self).items() if part is not None}
[docs]
class TransformerContext:
"""Track miscellaneous information throughout the code generation process. Also
provides helper functions for staticness conversion etc.
"""
def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Memory]",
fields: "dict[str, arch.BitFieldDescr]", attributes: "list[arch.InstrAttribute]", functions: "dict[str, arch.Function]",
instr_size: int, native_size: int, arch_name: str, static_scalars: bool, intrinsics, generate_coverage: bool, ignore_static=False):
[docs]
self.constants = constants
[docs]
self.memories = memories
[docs]
self.memory_aliases = memory_aliases
[docs]
self.attributes = attributes if attributes else []
[docs]
self.functions = functions
[docs]
self.instr_size = instr_size
[docs]
self.native_size = native_size
[docs]
self.arch_name = arch_name
[docs]
self.intrinsics = intrinsics
[docs]
self.static_scalars = static_scalars
[docs]
self.generate_coverage = generate_coverage
[docs]
self.ignore_static = ignore_static
[docs]
self.found_code_infos = []
for _, mem_descr in chain(self.memories.items(), self.memory_aliases.items()):
if arch.MemoryAttribute.IS_PC in mem_descr.attributes:
self.pc_mem = mem_descr
break
[docs]
self.raise_fn: arch.Function = None
[docs]
self.mem_raise_fn: arch.Function = None
for fn_name, fn_def in self.functions.items():
if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes:
self.raise_fn = fn_def
if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn_def.attributes:
self.mem_raise_fn = fn_def
[docs]
self.generates_exception = False
[docs]
self.is_exception = False
[docs]
self.temp_var_count = 0
[docs]
self.affected_regs = set()
[docs]
self.dependent_regs = set()
[docs]
self.used_arch_data = False
[docs]
def make_static(self, val, signed=False):
"""Wrap a static expression."""
sign = {False: "U", True: ""}
if self.ignore_static:
return val
return Template(f'" + std::to_string({val}) + "{sign[signed]}LL').safe_substitute(**replacements.rename_static)
[docs]
def wrap_codestring(self, val, static=False):
"""Wrap an entire static line."""
if self.ignore_static or static:
return val
return f'cp.code() += "{val}\\n";'
[docs]
def get_constant_or_val(self, name_or_val):
"""Convenience accessor for constant values."""
if isinstance(name_or_val, int):
return name_or_val
return self.constants[name_or_val]