Source code for m2isar.backends.etiss.instruction_utils

# 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] mem_ids: "list[MemID]"
[docs] function_calls: "list[FnID]"
[docs] line_infos: "list[LineInfo]"
def __init__(self, code, static, size, signed, regs_affected=None, line_infos=[]):
[docs] self.code = code
[docs] self.static = StaticType(static)
[docs] self.size = size
[docs] self.signed = signed
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
[docs] def __format__(self, format_spec): return self.code
@dataclass
[docs] class MemID: """Track a memory access across recursive code generation."""
[docs] mem_space: arch.Memory
[docs] mem_id: int
[docs] index: CodeString
[docs] access_size: int
[docs] write: bool = None
@dataclass
[docs] class FnID: """Track a required function call across recursive code generation."""
[docs] fn_call: arch.Function
[docs] fn_id: int
[docs] args: CodeString
@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] def format(self, mapping={}, **kwargs): for name in asdict(self): part = getattr(self, name) if not part: continue formatted = Template(part).safe_substitute(mapping, **kwargs) setattr(self, name, formatted)
[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.fields = fields
[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 = []
[docs] self.code_lines = []
[docs] self.pc_reg = None
[docs] self.pc_mem = None
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.mem_var_count = 0
[docs] self.fn_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]