diff --git a/m2isar/backends/coverage/id_transform.py b/m2isar/backends/coverage/id_transform.py index 8748f67..a31c539 100644 --- a/m2isar/backends/coverage/id_transform.py +++ b/m2isar/backends/coverage/id_transform.py @@ -57,11 +57,7 @@ def _(self, expr: behav.ConcatOperation, context: "IdMatcherContext"): self.generate(expr.right, context) @generate.register - def _(self, expr: behav.NumberLiteral, context: "IdMatcherContext"): - self._store_id(expr, context) - - @generate.register - def _(self, expr: behav.IntLiteral, context: "IdMatcherContext"): + def _(self, expr: behav.Literal, context: "IdMatcherContext"): self._store_id(expr, context) @generate.register diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 1ecbdb0..7389363 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -10,11 +10,15 @@ import logging import pathlib +from itertools import chain +from typing import Union from mako.template import Template + +from .instruction_utils import actual_size from ... import M2TypeError -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info, attribute_info from . import BlockEndType from .instruction_generator import (generate_fields, generate_instruction_callback) @@ -22,17 +26,24 @@ logger = logging.getLogger("arch_writer") -def write_child_reg_def(reg: arch.Memory, regs: "list[str]"): - """Recursively generate register declarations""" +def write_child_reg_def(reg: Union[arch.Memory, arch.RegisterBank, arch.Register], regs: "list[str]"): + """Recursively generate child declarations for Memories and Register(Banks)""" logger.debug("processing register %s", reg) - if arch.MemoryAttribute.IS_PC in reg.attributes or arch.MemoryAttribute.IS_MAIN_MEM in reg.attributes: + if attribute_info.RegisterAttribute.IS_PC in reg.attributes or attribute_info.MemoryAttribute.IS_MAIN_MEM in reg.attributes: logger.debug("this register is either the PC or main memory, skipping") return - array_txt = f"[{reg.data_range.length}]" if reg.data_range.length > 1 else "" + assert(isinstance(reg.ty, (type_info.ArrayType, type_info.PrimitiveType, type_info.PointerType))) + if isinstance(reg.ty, type_info.ArrayType): + array_txt = f"[{arch.get_const_or_val(reg.ty.length)}]" + elif isinstance(reg.ty, type_info.PointerType): + array_txt = f"[{arch.get_const_or_val(reg.data_range.length)}]" if arch.get_const_or_val(reg.data_range.length) > 1 else "" + else: + array_txt = "" + + if hasattr(reg, "children"): - if len(reg.children) > 0: logger.debug("processing children") for child in reg.children: write_child_reg_def(child, regs) @@ -40,24 +51,40 @@ def write_child_reg_def(reg: arch.Memory, regs: "list[str]"): # registers with children (aliases) are defined as two arrays: # 1) array of pointers, used for actual access # 2) array of actual data type, for every index which is not aliased - regs.append(f"etiss_uint{reg.actual_size} *{reg.name}{array_txt}") - regs.append(f"etiss_uint{reg.actual_size} ins_{reg.name}{array_txt}") + if isinstance(reg.ty, type_info.ArrayType): + size = actual_size(reg.ty.element_type.size) + elif isinstance(reg.ty, type_info.PrimitiveType): + size = actual_size(reg.ty.size) + else: + raise "Register Types needs to be of Array or PrimitiveType" + + if (len(reg.children) > 0): + regs.append(f"etiss_uint{size} *{reg.name}{array_txt}") + regs.append(f"etiss_uint{size} ins_{reg.name}{array_txt}") + else: + regs.append(f"etiss_uint{actual_size(reg.ty.size)} {reg.name}{array_txt}") + else: - regs.append(f"etiss_uint{reg.actual_size} {reg.name}{array_txt}") + regs.append(f"etiss_uint{actual_size(reg.ty.size)} {reg.name}{array_txt}") def write_arch_struct(core: arch.CoreDef, start_time: str, output_path: pathlib.Path): arch_struct_template = Template(filename=str(template_dir/'etiss_arch_struct.mako')) regs = [] + mems = [] logger.info("writing architecture struct") - for _, mem_desc in core.memories.items(): - write_child_reg_def(mem_desc, regs) + assert core.memories is not None and core.register_banks is not None + for _, reg_desc in chain(core.register_banks.items()): + write_child_reg_def(reg_desc, regs) + for _, mem_desc in chain(core.memories.items()): + write_child_reg_def(mem_desc, mems) txt = arch_struct_template.render( start_time=start_time, core_name=core.name, - regs=regs + regs=regs, + mems=mems ) with open(output_path / f"{core.name}.h", "w", encoding="utf-8") as f: @@ -77,8 +104,9 @@ def write_arch_header(core: arch.CoreDef, start_time: str, output_path: pathlib. with open(output_path / f"{core.name}Arch.h", "w", encoding="utf-8") as f: f.write(txt) -def build_reg_hierarchy(reg: arch.Memory, ptr_regs: "list[arch.Memory]", actual_regs: "list[arch.Memory]", - alias_regs: "dict[arch.Memory, arch.Memory]", initval_regs: "list[arch.Memory]"): +def build_reg_hierarchy(reg: Union[arch.Memory, arch.Alias, arch.Register, arch.RegisterBank], + ptr_regs: "list[arch.Memory]", actual_regs: "list[arch.Memory]", + alias_regs: "dict[arch.Memory, arch.Memory]", initval_regs: "list[arch.Memory]"): """Populate the passed lists with memory objects of their category. ptr_regs: Registers that need to be a pointer within ETISS @@ -91,16 +119,21 @@ def build_reg_hierarchy(reg: arch.Memory, ptr_regs: "list[arch.Memory]", actual_ if reg._initval: initval_regs.append(reg) - if len(reg.children) > 0: - for child in reg.children: - if child.is_main_mem: - logger.warning("main memory is a child memory of %s", reg) - continue - build_reg_hierarchy(child, ptr_regs, actual_regs, alias_regs, initval_regs) - alias_regs[child] = reg - ptr_regs.append(reg) + if isinstance(reg, (arch.Memory, arch.Register, arch.RegisterBank)): + if len(reg.children) > 0: + for child in reg.children: + if hasattr(child, "is_main_mem"): + if child.is_main_mem: + logger.warning("main memory is a child memory of %s", reg) + continue + build_reg_hierarchy(child, ptr_regs, actual_regs, alias_regs, initval_regs) + alias_regs[child] = reg + ptr_regs.append(reg) + else: + actual_regs.append(reg) else: - actual_regs.append(reg) + assert isinstance(reg, arch.Alias) + return def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, aliased_regnames: bool=True): """Generate {CoreName}Arch.cpp file. Contains mainly register initialization code.""" @@ -115,15 +148,16 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat logger.info("writing architecture class file") # determine memory types - for _, mem_desc in core.memories.items(): - if mem_desc.is_main_mem: - continue + for _, mem_desc in chain(core.memories.items(), core.register_banks.items(), core.memory_aliases.items(), core.register_aliases.items()): + if hasattr(mem_desc, "is_main_mem"): + if mem_desc.is_main_mem: + continue build_reg_hierarchy(mem_desc, ptr_regs, actual_regs, alias_regs, initval_regs) # generate main register file names for ETISS's 'char* reg_name[]' - reg_names = [f"{core.main_reg_file.name}{n}" for n in range(core.main_reg_file.data_range.length)] + reg_names = [f"{core.main_reg_file.name}{n}" for n in range(core.main_reg_file.ty.length)] if core.float_reg_file is not None: - reg_names += [f"{core.float_reg_file.name}{n}" for n in range(core.float_reg_file.data_range.length)] + reg_names += [f"{core.float_reg_file.name}{n}" for n in range(core.float_reg_file.ty.length)] # TODO(annnnna42): add float reg names (F0-F31) here # if main register file entries have aliases optionally use these for 'char* reg_name[]' @@ -142,7 +176,9 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat actual_regs=actual_regs, alias_regs=alias_regs, initval_regs=initval_regs, - procno_memory=core.procno_memory + procno_memory=core.procno_memory, + type_info=type_info, + arch=arch, ) with open(output_path / f"{core.name}Arch.cpp", "w", encoding="utf-8") as f: @@ -166,13 +202,26 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: logger.info("writing architecture specific header") + core.main_reg_file.ty.element_type.size = arch.get_const_or_val(core.main_reg_file.ty.element_type.size) + core.main_reg_file.ty.length = arch.get_const_or_val(core.main_reg_file.ty.length) + if core.float_reg_file is not None: + core.float_reg_file.ty.element_type.size = arch.get_const_or_val(core.float_reg_file.ty.element_type.size) + core.float_reg_file.ty.length = arch.get_const_or_val(core.float_reg_file.ty.length) + if core.vector_reg_file is not None: + core.vector_reg_file.ty.element_type.size = arch.get_const_or_val(core.vector_reg_file.ty.element_type.size) + core.vector_reg_file.ty.length = arch.get_const_or_val(core.vector_reg_file.ty.length) + if core.csr_reg_file is not None: + core.csr_reg_file.ty.element_type.size = arch.get_const_or_val(core.csr_reg_file.ty.element_type.size) + core.csr_reg_file.ty.length = arch.get_const_or_val(core.csr_reg_file.ty.length) + txt = arch_specific_header_template.render( start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, float_reg=core.float_reg_file, vector_reg=core.vector_reg_file, - csr_reg=core.csr_reg_file + csr_reg=core.csr_reg_file, + arch=arch ) with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: @@ -215,12 +264,12 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa error_fn = None for fn in core.functions.values(): - if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes: error_fn = fn break for fn in core.functions.values(): - if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break @@ -228,8 +277,8 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa if error_fn is not None: for bitsize in core.instr_classes: - error_bitfield = arch.BitField("error_code", arch.RangeSpec(31, 0), arch.DataType.U) - error_instr = arch.Instruction(f"trap_entry {bitsize}", {arch.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None) + error_bitfield = arch.BitField("error_code", arch.RangeSpec(31, 0), type_info.TypeKind.UINT) + error_instr = arch.Instruction(f"trap_entry {bitsize}", {attribute_info.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None) error_bitfield_descr = error_instr.fields.get("error_code") error_op = behav.Operation([ behav.ProcedureCall(error_fn, [behav.NamedReference(error_bitfield_descr)]) @@ -245,8 +294,8 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa global_irq_en_mask = None if core.global_irq_en_memory is not None: - attr = core.global_irq_en_memory.attributes[arch.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN][0] - if not isinstance(attr, behav.IntLiteral): + attr = core.global_irq_en_memory.attributes[attribute_info.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN][0] + if not isinstance(attr, behav.Literal): raise M2TypeError(f"IRQ enable mask of {core.global_irq_en_memory.name} is not compile static") global_irq_en_mask = attr.value diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index ac32ff0..c170d12 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -12,16 +12,17 @@ from mako.template import Template -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info, attribute_info from . import BlockEndType, instruction_utils from .instruction_transform import InstructionTransformVisitor +from .instruction_utils import actual_size from .templates import template_dir logger = logging.getLogger("instruction_generator") def generate_arg_str(arg: arch.FnParam): arg_name = f" {arg.name}" if arg.name is not None else "" - return f'{instruction_utils.data_type_map[arg.data_type]}{arg.actual_size}{arg_name}' + return f'{instruction_utils.data_type_map[arg.ty.kind]}{actual_size(arg.ty.size)}{arg_name}' def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: bool, generate_coverage: bool): """Return a generator object to generate function behavior code. Uses function @@ -33,7 +34,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo fn_template = Template(filename=str(template_dir/'etiss_function.mako')) - core_default_width = core.constants['XLEN'].value + core_default_width = core.parameters['XLEN'].value core_name = core.name for fn_name, fn_def in core.functions.items(): @@ -42,13 +43,14 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo if fn_def.extern and not decls_only: continue - return_type = instruction_utils.data_type_map[fn_def.data_type] - if fn_def.size: - return_type += f'{fn_def.actual_size}' + return_type = instruction_utils.data_type_map[fn_def.ty.kind] + if fn_def.ty.size: + return_type += f'{actual_size(fn_def.ty.size)}' # set up a transformer context and generate code - context = instruction_utils.TransformerContext(core.constants, core.memories, core.memory_aliases, fn_def.args, fn_def.attributes, - core.functions, 0, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, True) + context = instruction_utils.TransformerContext(core.parameters, core.memories, core.memory_aliases, core.register_banks, + core.register_aliases, fn_def.args, fn_def.attributes, core.functions, + 0, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, True) logger.debug("generating code for %s", fn_name) @@ -66,7 +68,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo args_list = [generate_arg_str(arg) for arg in fn_def.args.values()] # if function needs access to ETISS architecture data, add these as arguments to the function - if arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or (not fn_def.extern and not fn_def.static): + if attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or (not fn_def.extern and not fn_def.static): args_list = ['ETISS_CPU * const cpu', 'ETISS_System * const system', 'void * const * const plugin_pointers'] + args_list fn_args = ', '.join(args_list) @@ -105,8 +107,8 @@ def generate_fields(core_default_width, instr_def: arch.Instruction): if enc.name not in seen_fields: # first encounter of this parameter, instantiate a new integer for it seen_fields[enc.name] = 255 - width = instr_def.fields[enc.name].actual_size - fields_code += f'{instruction_utils.data_type_map[enc.data_type]}{width} {enc.name} = 0;\n' + width = actual_size(instr_def.fields[enc.name].ty.size) + fields_code += f'{instruction_utils.data_type_map[enc.ty.kind]}{width} {enc.name} = 0;\n' lower = enc.range.lower length = enc.range.length @@ -131,7 +133,7 @@ def generate_fields(core_default_width, instr_def: arch.Instruction): asm_printer_code.append(f'{field_name}=" + std::to_string({field_name}) + "') # generate sign extension if necessary - if field_descr.data_type == arch.DataType.S and field_descr.size < core_default_width: + if field_descr.ty.kind == type_info.TypeKind.INT and field_descr.size < core_default_width: fields_code += '\n' fields_code += f'struct {{etiss_int{core_default_width} x:{field_descr.size};}} {field_name}_ext;\n' fields_code += f'{field_name} = {field_name}_ext.x = {field_name};' @@ -146,20 +148,20 @@ def generate_instruction_callback(core: arch.CoreDef, instr_def: arch.Instructio instr_name = instr_def.name core_name = core.name misc_code = [] - core_default_width = core.constants['XLEN'].value + core_default_width = core.parameters['XLEN'].value fields_code, _, _, enc_idx = fields callback_template = Template(filename=str(template_dir/'etiss_instruction_callback.mako')) - context = instruction_utils.TransformerContext(core.constants, core.memories, core.memory_aliases, instr_def.fields, instr_def.attributes, - core.functions, enc_idx, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, False) + context = instruction_utils.TransformerContext(core.parameters, core.memories, core.memory_aliases, core.register_banks, core.register_aliases, instr_def.fields, instr_def.attributes, + core.functions, enc_idx, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, False) # force a block end if necessary - if ((arch.InstrAttribute.NO_CONT in instr_def.attributes - and arch.InstrAttribute.COND not in instr_def.attributes + if ((attribute_info.InstrAttribute.NO_CONT in instr_def.attributes + and attribute_info.InstrAttribute.COND not in instr_def.attributes and block_end_on == BlockEndType.UNCOND) or ( - arch.InstrAttribute.NO_CONT in instr_def.attributes + attribute_info.InstrAttribute.NO_CONT in instr_def.attributes and block_end_on == BlockEndType.ALL) ): @@ -196,7 +198,7 @@ def generate_instructions(core: arch.CoreDef, static_scalars: bool, block_end_on error_fn = None for fn in core.functions.values(): - if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break @@ -224,20 +226,20 @@ def gen_rand_suffix(length: int = 8): instr_def.attributes = [] # generate instruction parameter extraction code - fields = generate_fields(core.constants['XLEN'].value, instr_def) + fields = generate_fields(core.parameters['XLEN'].value, instr_def) fields_code, asm_printer_code, seen_fields, enc_idx = fields code_string = f'{code:#0{int(enc_idx/4)}x}' mask_string = f'{mask:#0{int(enc_idx/4)}x}' - if arch.InstrAttribute.ENABLE in instr_def.attributes: - cond = instr_def.attributes[arch.InstrAttribute.ENABLE] + if attribute_info.InstrAttribute.ENABLE in instr_def.attributes: + cond = instr_def.attributes[attribute_info.InstrAttribute.ENABLE] new_op = behav.Operation([ behav.Conditional( [cond[0]], [ instr_def.operation.statements, - behav.ProcedureCall(error_fn, [behav.IntLiteral(-11)]) + behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.TypeKind.NONE)]) ] ) ]) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 62eb3a4..0fc3e51 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -15,13 +15,13 @@ from functools import singledispatchmethod from ... import M2NameError, M2SyntaxError, M2ValueError, flatten -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info, attribute_info from ...metamodel.code_info import LineInfoPlacement from ...metamodel.utils.ExprVisitor import ExprVisitor from . import CodeInfoTracker, replacements from .instruction_utils import (FN_VAL_REPL, MEM_VAL_REPL, CodePartsContainer, - CodeString, FnID, MemID, StaticType, - TransformerContext, data_type_map) + CodeString, FnID, MemID, TransformerContext, + data_type_map, actual_size) # pylint: disable=unused-argument @@ -86,7 +86,7 @@ def _(self, expr: behav.Operation, context: TransformerContext): code_lines.append(context.wrap_codestring(f"etiss_coverage_count({len(before_line_infos)}, {', '.join(before_line_infos)});")) for f_id in arg.function_calls: - code_lines.append(context.wrap_codestring(f'{data_type_map[f_id.fn_call.data_type]}{f_id.fn_call.actual_size} {FN_VAL_REPL}{f_id.fn_id};', arg.static)) + code_lines.append(context.wrap_codestring(f'{data_type_map[f_id.fn_call.data_type]}{actual_size(f_id.fn_call.size)} {FN_VAL_REPL}{f_id.fn_id};', arg.static)) code_lines.append(context.wrap_codestring(f'{FN_VAL_REPL}{f_id.fn_id} = {f_id.args};', arg.static)) code_lines.append(context.wrap_codestring('if (cpu->return_pending) goto instr_exit_" + std::to_string(ic.current_address_) + ";', arg.static)) @@ -117,22 +117,22 @@ def _(self, expr: behav.Operation, context: TransformerContext): return_conditions = [] return_needed = any(( context.generates_exception, - arch.InstrAttribute.NO_CONT in context.attributes, - arch.InstrAttribute.COND in context.attributes, - arch.InstrAttribute.FLUSH in context.attributes + attribute_info.InstrAttribute.NO_CONT in context.attributes, + attribute_info.InstrAttribute.COND in context.attributes, + attribute_info.InstrAttribute.FLUSH in context.attributes )) if context.generates_exception: return_conditions.append("cpu->return_pending") return_conditions.append("cpu->exception") - if arch.InstrAttribute.NO_CONT in context.attributes and arch.InstrAttribute.COND in context.attributes: + if attribute_info.InstrAttribute.NO_CONT in context.attributes and attribute_info.InstrAttribute.COND in context.attributes: return_conditions.append(f'cpu->nextPc != " + std::to_string(ic.current_address_ + {int(context.instr_size / 8)}) + "ULL') - elif arch.InstrAttribute.NO_CONT in context.attributes: + elif attribute_info.InstrAttribute.NO_CONT in context.attributes: return_conditions.clear() - if arch.InstrAttribute.FLUSH in context.attributes: + if attribute_info.InstrAttribute.FLUSH in context.attributes: container.initial_required = 'cp.code() += "cpu->exception = ETISS_RETURNCODE_RELOADBLOCKS;\\n";\n' + container.initial_required return_conditions.clear() @@ -140,7 +140,7 @@ def _(self, expr: behav.Operation, context: TransformerContext): cond_str = ("if (" + " || ".join(return_conditions) + ") ") if return_conditions else "" container.appended_returning_required = f'cp.code() += "{cond_str}return cpu->exception;\\n";' - elif arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in context.attributes: + elif attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in context.attributes: container.initial_required = "cpu->return_pending = 1;\ncpu->exception = 0;\n" + container.initial_required return container @@ -149,12 +149,12 @@ def _(self, expr: behav.Operation, context: TransformerContext): def _(self, expr: behav.Block, context: TransformerContext): stmts = [self.generate(stmt, context) for stmt in expr.statements] - pre = [CodeString("{ // block", StaticType.READ, None, None, line_infos=expr.line_info)] - post = [CodeString("} // block", StaticType.READ, None, None)] + pre = [CodeString("{ // block", attribute_info.StaticAttribute.READ, None, None, line_infos=expr.line_info)] + post = [CodeString("} // block", attribute_info.StaticAttribute.READ, None, None)] if not context.ignore_static: - pre.append(CodeString("{ // block", StaticType.NONE, None, None)) - post.insert(0, CodeString("} // block", StaticType.NONE, None, None)) + pre.append(CodeString("{ // block", attribute_info.StaticAttribute.NONE, None, None)) + post.insert(0, CodeString("} // block", attribute_info.StaticAttribute.NONE, None, None)) return pre + stmts + post @@ -168,13 +168,13 @@ def _(self, expr: behav.Return, context: TransformerContext): c.code = f'return {c.code};' c.line_infos.append(expr.line_info) else: - c = CodeString("return;", StaticType.RW, None, None, line_infos=expr.line_info) + c = CodeString("return;", attribute_info.StaticAttribute.RW, None, None, line_infos=expr.line_info) return c @generate.register def _(self, expr: behav.Break, context: TransformerContext): - return CodeString("break;", StaticType.RW, None, None, line_infos=expr.line_info) + return CodeString("break;", attribute_info.StaticAttribute.RW, None, None, line_infos=expr.line_info) @generate.register def _(self, expr: behav.ScalarDefinition, context: TransformerContext): @@ -182,20 +182,20 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): a variable instantiation.""" if context.static_scalars: if context.ignore_static: - static = StaticType.RW + static = attribute_info.StaticAttribute.RW else: - static = expr.scalar.static + static = expr.scalar.attributes["static"] else: - static = StaticType.NONE + static = attribute_info.StaticAttribute.NONE - actual_size = 1 << (expr.scalar.size - 1).bit_length() + actual_size = 1 << (expr.scalar.ty.size - 1).bit_length() actual_size = max(actual_size, 8) return CodeString( - f'{data_type_map[expr.scalar.data_type]}{actual_size} {expr.scalar.name}', + f'{data_type_map[expr.scalar.ty.kind]}{actual_size} {expr.scalar.name}', static, - expr.scalar.size, - expr.scalar.data_type == arch.DataType.S, + expr.scalar.ty.size, + expr.scalar.ty.kind == type_info.TypeKind.INT, line_infos=expr.line_info, ) @@ -214,7 +214,7 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): # determine if procedure call is entirely static - static = StaticType.READ if fn.static and all(arg.static != StaticType.NONE for arg in fn_args) else StaticType.NONE + static = attribute_info.StaticAttribute.READ if fn.static and all(arg.static != attribute_info.StaticAttribute.NONE for arg in fn_args) else attribute_info.StaticAttribute.NONE # convert singular static arguments if not static: @@ -224,7 +224,7 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): arg.code = context.make_static(arg.code, arg.signed) # generate argument string, add ETISS arch data if required - arch_args = ['cpu', 'system', 'plugin_pointers'] if arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn.attributes or (not fn.static and not fn.extern) else [] + arch_args = ['cpu', 'system', 'plugin_pointers'] if attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn.attributes or (not fn.static and not fn.extern) else [] arg_str = ', '.join(arch_args + [arg.code for arg in fn_args]) # check if any argument is a memory access @@ -237,13 +237,13 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): # add special behavior if this function is an exception entry point exc_code = "" - if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: context.generates_exception = True - if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes: context.generates_exception = True - if fn.size is not None: + if fn.ty.size is not None: exc_code = "cpu->exception = " c = CodeString(f'{exc_code}{fn.name}({arg_str});', static, None, None, line_infos=[expr.line_info] + [x.line_infos for x in fn_args]) @@ -251,11 +251,11 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): if fn.throws and not context.ignore_static: c.check_trap = True - cond = "if (cpu->return_pending) " if fn.throws == arch.FunctionThrows.MAYBE else "" + cond = "if (cpu->return_pending) " if fn.throws == attribute_info.FunctionThrows.MAYBE else "" c2 = CodeString(cond + 'goto instr_exit_" + std::to_string(ic.current_address_) + ";', static, None, None) - pre = [CodeString("{ // procedure", StaticType.READ, None, None), CodeString("{ // procedure", StaticType.NONE, None, None)] - post = [CodeString("} // procedure", StaticType.NONE, None, None), CodeString("} // procedure", StaticType.READ, None, None)] + pre = [CodeString("{ // procedure", attribute_info.StaticAttribute.READ, None, None), CodeString("{ // procedure", attribute_info.StaticAttribute.NONE, None, None)] + post = [CodeString("} // procedure", attribute_info.StaticAttribute.NONE, None, None), CodeString("} // procedure", attribute_info.StaticAttribute.READ, None, None)] return pre + [c, c2] + post @@ -278,7 +278,7 @@ def _(self, expr: behav.FunctionCall, context: TransformerContext): fn = ref # determine if function call is entirely static - static = StaticType.READ if fn.static and all(arg.static != StaticType.NONE for arg in fn_args) else StaticType.NONE + static = attribute_info.StaticAttribute.READ if fn.static and all(arg.static != attribute_info.StaticAttribute.NONE for arg in fn_args) else attribute_info.StaticAttribute.NONE # convert singular static arguments if not static: @@ -288,20 +288,20 @@ def _(self, expr: behav.FunctionCall, context: TransformerContext): arg.code = context.make_static(arg.code, arg.signed) # generate argument string, add ETISS arch data if required - arch_args = ['cpu', 'system', 'plugin_pointers'] if arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn.attributes or (not fn.static and not fn.extern) else [] + arch_args = ['cpu', 'system', 'plugin_pointers'] if attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn.attributes or (not fn.static and not fn.extern) else [] arg_str = ', '.join(arch_args + [arg.code for arg in fn_args]) # keep track of signedness of function return value - signed = fn.data_type == arch.DataType.S + signed = fn.ty.kind == type_info.TypeKind.INT # keep track of affected registers regs_affected = set(chain.from_iterable([arg.regs_affected for arg in fn_args])) - c = CodeString(f'{fn.name}({arg_str})', static, fn.size, signed, regs_affected, [expr.line_info] + [x.line_infos for x in fn_args]) + c = CodeString(f'{fn.name}({arg_str})', static, fn.ty.size, signed, regs_affected, [expr.line_info] + [x.line_infos for x in fn_args]) c.mem_ids = list(chain.from_iterable([arg.mem_ids for arg in fn_args])) if fn.throws and not context.ignore_static: fn_id = FnID(fn, context.fn_var_count, c) - repl_c = CodeString(f'{FN_VAL_REPL}{context.fn_var_count}', static, fn.size, signed, regs_affected) + repl_c = CodeString(f'{FN_VAL_REPL}{context.fn_var_count}', static, fn.ty.size, signed, regs_affected) repl_c.mem_ids = list(chain.from_iterable([arg.mem_ids for arg in fn_args])) repl_c.function_calls.append(fn_id) context.fn_var_count += 1 @@ -329,14 +329,14 @@ def _(self, expr: behav.Assignment, context: TransformerContext): expr_str: CodeString = self.generate(expr.expr, context) # check staticness - static = bool(target.static & StaticType.WRITE) and bool(expr_str.static) + static = bool(target.static & attribute_info.StaticAttribute.WRITE) and bool(expr_str.static) - if not expr_str.static and bool(target.static & StaticType.WRITE) and not context.ignore_static: + if not expr_str.static and bool(target.static & attribute_info.StaticAttribute.WRITE) and not context.ignore_static: raise M2ValueError('Static target cannot be assigned to non-static expression!') # convert assignment value staticness if expr_str.static and not expr_str.is_literal: - if bool(target.static & StaticType.WRITE): + if bool(target.static & attribute_info.StaticAttribute.WRITE): if context.ignore_static: expr_str.code = Template(f'{expr_str.code}').safe_substitute(**replacements.rename_dynamic) else: @@ -345,7 +345,7 @@ def _(self, expr: behav.Assignment, context: TransformerContext): expr_str.code = context.make_static(expr_str.code, expr_str.signed) # convert target staticness - if bool(target.static & StaticType.READ): + if bool(target.static & attribute_info.StaticAttribute.READ): target.code = Template(target.code).safe_substitute(replacements.rename_write) # keep track of affected and dependent registers @@ -353,10 +353,10 @@ def _(self, expr: behav.Assignment, context: TransformerContext): context.dependent_regs.update(expr_str.regs_affected) if not target.is_mem_access and not expr_str.is_mem_access: - if target.actual_size > target.size: + if actual_size(arch.get_const_or_val(target.size)) > arch.get_const_or_val(target.size): if target.signed: - shift = target.actual_size - target.size - expr_str.code = f'(((etiss_int{target.actual_size})({expr_str.code})) << {shift}) >> {shift}' + shift = actual_size(target.size) - target.size + expr_str.code = f'(((etiss_int{actual_size(target.size)})({expr_str.code})) << {shift}) >> {shift}' else: mask = (1 << target.size) - 1 mask_bits = log2(mask) @@ -410,7 +410,7 @@ def _(self, expr: behav.BinaryOperation, context: TransformerContext): c = CodeString( f'{left.code} {op.value} {right.code}', left.static and right.static, - left.size if left.size > right.size else right.size, + arch.get_const_or_val(left.size) if arch.get_const_or_val(left.size) > arch.get_const_or_val(right.size) else arch.get_const_or_val(right.size), left.signed or right.signed, set.union(left.regs_affected, right.regs_affected), [expr.line_info] + left.line_infos + right.line_infos, @@ -537,7 +537,7 @@ def _(self, expr: behav.Ternary, context: TransformerContext): then_expr = self.generate(expr.then_expr, context) else_expr = self.generate(expr.else_expr, context) - static = StaticType.NONE not in [x.static for x in (cond, then_expr, else_expr)] + static = attribute_info.StaticAttribute.NONE not in [x.static for x in (cond, then_expr, else_expr)] # convert singular static sub-components if not static: @@ -569,19 +569,18 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): # if only width should be changed assume data type remains unchanged if expr.data_type is None: - expr.data_type = arch.DataType.S if expr_str.signed else arch.DataType.U + expr.data_type = type_info.TypeKind.INT if expr_str.signed else type_info.TypeKind.UINT # if only data type should be changed assume width remains unchanged if expr.size is None: expr._size = expr_str.size - expr._actual_size = expr_str.actual_size code_str = expr_str.code # sign extension for non-2^N datatypes - if expr.data_type == arch.DataType.S and expr_str.actual_size != expr_str.size: - target_size = expr.actual_size + if expr.data_type == type_info.TypeKind.INT and actual_size(expr_str.size) != expr_str.size: + target_size = actual_size(expr.size) if isinstance(expr.size, int): code_str = f'((etiss_int{target_size})(((etiss_int{target_size}){expr_str.code}) << ({target_size - expr.size})) >> ({target_size - expr.size}))' @@ -590,9 +589,9 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): # normal type conversion # TODO: check if behavior adheres to CoreDSL 2 spec else: - code_str = f'({data_type_map[expr.data_type]}{expr.actual_size})({code_str})' + code_str = f'({data_type_map[expr.data_type]}{actual_size(expr.size)})({code_str})' - c = CodeString(code_str, expr_str.static, expr.size, expr.data_type == arch.DataType.S, expr_str.regs_affected, line_infos=[expr.line_info] + expr_str.line_infos) + c = CodeString(code_str, expr_str.static, expr.size, expr.data_type == type_info.TypeKind.INT, expr_str.regs_affected, line_infos=[expr.line_info] + expr_str.line_infos) c.mem_ids = expr_str.mem_ids return c @@ -604,55 +603,85 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # extract referred object referred_var = expr.reference - static = StaticType.NONE + static = attribute_info.StaticAttribute.NONE name = referred_var.name # check if static name replacement is needed if name in replacements.rename_static: name = f'${{{name}}}' - static = StaticType.READ + static = attribute_info.StaticAttribute.READ # check which type of reference has to be generated if isinstance(referred_var, arch.Memory): - # architecture constant + # architecture constant parameter if not static: ref = "*" if len(referred_var.children) > 0 else "" name = f"{ref}{replacements.default_prefix}{name}" signed = False - size = referred_var.size + size = referred_var.ty.size + context.used_arch_data = True + + elif isinstance(referred_var, arch.RegisterBank): + # architecture constant parameter + if not static: + ref = "*" if len(referred_var.children) > 0 else "" + name = f"{ref}{replacements.default_prefix}{name}" + signed = False + size = (referred_var.ty.length)*(referred_var.ty.element_type.size) + context.used_arch_data = True + + elif isinstance(referred_var, arch.Register): + # architecture constant parameter + if not static: + ref = "*" if len(referred_var.children) > 0 else "" + name = f"{ref}{replacements.default_prefix}{name}" + signed = False + size = referred_var.ty.size + context.used_arch_data = True + + elif isinstance(referred_var, arch.Alias): + # architecture constant parameter + # Limitation: Alias does not haven children + if not static: + ref = "" + name = f"{ref}{replacements.default_prefix}{name}" + signed = False + assert(referred_var.ty, type_info.PrimitiveType) + size = referred_var.ty.size context.used_arch_data = True elif isinstance(referred_var, arch.BitFieldDescr): # function argument - signed = referred_var.data_type == arch.DataType.S - size = referred_var.size - static = StaticType.READ - - elif isinstance(referred_var, arch.Scalar): - signed = referred_var.data_type == arch.DataType.S - size = referred_var.size + signed = referred_var.ty.kind == type_info.TypeKind.INT + size = referred_var.ty.size + static = attribute_info.StaticAttribute.READ + + elif isinstance(referred_var, arch.Variable): + assert isinstance(referred_var.ty, type_info.PrimitiveType) + signed = referred_var.ty.kind == type_info.TypeKind.INT + size = referred_var.ty.size if context.static_scalars: - static = referred_var.static + static = referred_var.attributes.get("static") - elif isinstance(referred_var, arch.Constant): + elif isinstance(referred_var, arch.Parameter): signed = referred_var.value < 0 size = context.native_size - static = StaticType.READ + static = attribute_info.StaticAttribute.READ name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): - signed = referred_var.data_type == arch.DataType.S - size = referred_var.size - static = StaticType.RW + signed = referred_var.ty.kind == type_info.TypeKind.INT + size = referred_var.ty.size + static = attribute_info.StaticAttribute.RW elif isinstance(referred_var, arch.Intrinsic): if context.ignore_static: raise TypeError("intrinsic not allowed in function") - signed = referred_var.data_type == arch.DataType.S - size = referred_var.size - static = StaticType.READ + signed = referred_var.ty.kind == type_info.TypeKind.INT + size = referred_var.ty.size + static = attribute_info.StaticAttribute.READ if referred_var == context.intrinsics["__encoding_size"]: name = str(context.instr_size // 8) @@ -661,7 +690,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): raise TypeError("wrong type") if context.ignore_static: - static = StaticType.RW + static = attribute_info.StaticAttribute.RW return CodeString(name, static, size, signed, line_infos=expr.line_info) @@ -673,27 +702,35 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): # generate index expression index = self.generate(expr.index, context) + right = self.generate(expr.right, context) if expr.right else None referred_mem = expr.reference if isinstance(referred_mem, arch.Memory): context.used_arch_data = True - size = referred_mem.size + if isinstance(referred_mem, type_info.PrimitiveType): + size = referred_mem.ty.size + else: + size = referred_mem.ty.element_type.size # convert static index expression index_code = index.code if index.static and not context.ignore_static and not index.is_literal: index.code = context.make_static(index.code, index.signed) + if right is not None: + if right.static and not context.ignore_static and not right.is_literal: + right.code = context.make_static(right.code, right.signed) + if context.ignore_static: - static = StaticType.RW + static = attribute_info.StaticAttribute.RW else: - static = StaticType.NONE + static = attribute_info.StaticAttribute.NONE - if arch.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: + if attribute_info.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: # generate memory access if main memory is accessed - size = expr.inferred_type._width + size = expr.ty.size c = CodeString(f'{MEM_VAL_REPL}{context.mem_var_count}', static, size, False, line_infos=[expr.line_info] + index.line_infos) if (expr.right != None): # Use a simple base address on one site atleast for ranged_mem access. @@ -717,7 +754,7 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): if len(referred_mem.children) > 0: code_str = '*' + code_str c = CodeString(code_str, static, size, False, line_infos=[expr.line_info] + index.line_infos) - if arch.MemoryAttribute.IS_MAIN_REG in referred_mem.attributes: + if attribute_info.RegisterAttribute.IS_MAIN_REG in referred_mem.attributes: c.regs_affected.add(index_code) return c @@ -730,7 +767,7 @@ def _(self, expr: behav.SliceOperation, context: TransformerContext): left = self.generate(expr.left, context) right = self.generate(expr.right, context) - static = StaticType.NONE not in [x.static for x in (expr_str, left, right)] + static = attribute_info.StaticAttribute.NONE not in [x.static for x in (expr_str, left, right)] if not static: if expr_str.static and not expr_str.is_literal: @@ -791,42 +828,50 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): return c @generate.register - def _(self, expr: behav.NumberLiteral, context: TransformerContext): - """Generate generic number literal. Currently unused.""" - lit = int(expr.value) - size = min(lit.bit_length(), 64) - sign = lit < 0 - - twocomp_lit = (lit + (1 << 64)) % (1 << 64) - - postfix = "U" if not sign else "" - postfix += "LL" - - return CodeString(str(twocomp_lit) + postfix, True, size, sign, line_infos=expr.line_info) - - @generate.register - def _(self, expr: behav.IntLiteral, context: TransformerContext): - """Generate an integer literal.""" - lit = int(expr.value) - size = min(expr.bit_size, 128) - sign = expr.signed + def _(self, expr: behav.Literal, context: TransformerContext): + if expr.ty.kind is type_info.TypeKind.STR: + return CodeString(f'"{expr.value}"', attribute_info.StaticAttribute.READ, None, False, line_infos=expr.line_info) + + # old number NumberLiteral + elif expr.ty.kind == type_info.TypeKind.NONE: + lit = int(expr.value) + size = min(lit.bit_length(), 64) + sign = lit <= 0 + + + # TODO: Look in diff. U is sometimes there for negative vals!!! + twocomp_lit = (lit + (1 << 64)) % (1 << 64) + postfix = "U" if not sign else "" + postfix += "LL" + return CodeString(str(twocomp_lit) + postfix, True, size, sign, line_infos=expr.line_info) + # IntLiteral + else: + assert(expr.ty.kind in [type_info.TypeKind.INT, type_info.TypeKind.UINT]) + lit = int(expr.value) + if expr.ty.size is None: + size = lit.bit_length() + size = min(expr.ty.size, 128) + + if expr.value <= 0 or expr.ty.kind == type_info.TypeKind.INT: + # raise M2ValueError('Negative literal value cannot be represented as unsigned integer!') + sign = True + else: + sign = False - minus = "" - if lit > 0 and sign and (lit >> (size - 1)) & 1: - minus = "-" - _ = (lit + (1 << size)) % (1 << size) + minus = "" + if lit > 0 and sign and (lit >> (size - 1)) & 1: + minus = "-" + sign = True - postfix = "U" if not sign else "" - postfix += "LL" + _ = (lit + (1 << size)) % (1 << size) - ret = CodeString(minus + str(lit) + postfix, True, size, sign, line_infos=expr.line_info) - ret.is_literal = True - return ret + postfix = "U" if not sign else "" + postfix += "LL" - @generate.register - def _(self, expr: behav.StringLiteral, context: TransformerContext): - return CodeString(f'"{expr.value}"', StaticType.READ, None, False, line_infos=expr.line_info) + ret = CodeString(minus + str(lit) + postfix, True, size, sign, line_infos=expr.line_info) + ret.is_literal = True + return ret @generate.register def _(self, expr: behav.CodeLiteral, context: TransformerContext): diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index e700483..e6dd796 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -13,15 +13,14 @@ from string import Template from ... import M2ValueError -from ...metamodel import arch +from ...metamodel import arch, type_info, attribute_info from ...metamodel.code_info import LineInfo -from ...metamodel.utils import StaticType from . import replacements data_type_map = { - arch.DataType.S: 'etiss_int', - arch.DataType.U: 'etiss_uint', - arch.DataType.NONE: 'void' + type_info.TypeKind.INT: 'etiss_int', + type_info.TypeKind.UINT: 'etiss_uint', + type_info.TypeKind.VOID: 'void' } @@ -31,7 +30,7 @@ def actual_size(size, min_=8, max_=128): """Calculate a fitting c datatype width for any arbitrary size.""" - s = 1 << (size - 1).bit_length() + s = 1 << (arch.get_const_or_val(size) - 1).bit_length() if s > max_: raise M2ValueError("value too big") @@ -48,7 +47,7 @@ class CodeString: def __init__(self, code, static, size, signed, regs_affected=None, line_infos=[]): self.code = code - self.static = StaticType(static) + self.static = attribute_info.StaticAttribute(static) self.size = size self.signed = signed self.mem_ids = [] @@ -64,9 +63,6 @@ def __init__(self, code, static, size, signed, regs_affected=None, line_infos=[] else: self.line_infos = [] - @property - def actual_size(self): - return actual_size(self.size) @property def needs_fn_call(self): @@ -138,13 +134,16 @@ class TransformerContext: 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: bool = False): + def __init__(self, parameters: "dict[str, arch.Parameter]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Alias]", + registers: "dict[str, arch.Memory]", register_aliases: "dict[str, arch.Memory]", fields: "dict[str, arch.BitFieldDescr]", + attributes: "list[attribute_info.InstrAttribute]", functions: "dict[str, arch.Function]", instr_size: int, native_size: int, + arch_name: str, static_scalars: bool, intrinsics, generate_coverage: bool, ignore_static: bool = False): - self.constants = constants + self.parameters = parameters self.memories = memories self.memory_aliases = memory_aliases + self.registers = registers + self.register_aliases = register_aliases self.fields = fields self.attributes = attributes if attributes else [] self.functions = functions @@ -164,18 +163,18 @@ def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, a self.pc_reg = None 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 + for _, reg_descr in chain(self.registers.items(), self.register_aliases.items()): + if attribute_info.RegisterAttribute.IS_PC in reg_descr.attributes: + self.pc_mem = reg_descr break self.raise_fn: arch.Function = None 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: + if attribute_info.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: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn_def.attributes: self.mem_raise_fn = fn_def self.generates_exception = False @@ -210,4 +209,4 @@ def get_constant_or_val(self, name_or_val): if isinstance(name_or_val, int): return name_or_val - return self.constants[name_or_val] + return self.parameters[name_or_val] diff --git a/m2isar/backends/etiss/templates/etiss_arch_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_cpp.mako index 0d047ec..8cab0bb 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_cpp.mako @@ -81,8 +81,8 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) cpu->cpuCycleTime_ps = 31250; % for reg in ptr_regs: - % if reg.range.length > 1: - for (int i = 0; i < ${reg.data_range.length}; ++i) + % if isinstance(reg.ty, type_info.ArrayType): + for (int i = 0; i < ${arch.get_const_or_val(reg.ty.length)}; ++i) { ${core_name.lower()}cpu->ins_${reg.name}[i] = 0; ${core_name.lower()}cpu->${reg.name}[i] = &${core_name.lower()}cpu->ins_${reg.name}[i]; @@ -94,9 +94,10 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) % endfor % for reg in actual_regs: + % if not isinstance(reg, (arch.Memory, arch.Alias)): % if not reg.is_pc: - % if reg.range.length > 1: - for (int i = 0; i < ${reg.data_range.length}; ++i) + % if isinstance(reg.ty, type_info.ArrayType): + for (int i = 0; i < ${arch.get_const_or_val(reg.ty.length)}; ++i) { ${core_name.lower()}cpu->${reg.name}[i] = 0; } @@ -104,31 +105,34 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) ${core_name.lower()}cpu->${reg.name} = 0; % endif % endif + % endif % endfor % for reg, parent in alias_regs.items(): -<% ref = "" if len(reg.children) > 0 else "&" %>\ - % if reg.range.length > 1: - for (int i = 0; i < ${reg.range.length}; ++i) +<% ref = "&" %>\ + % if isinstance(reg.ty, type_info.ArrayType): + for (int i = 0; i < ${arch.get_const_or_val(reg.ty.length)}; ++i) { - ${core_name.lower()}cpu->${parent.name}[${reg.range.lower} + i] = ${ref}${core_name.lower()}cpu->${reg.name}[i]; + ${core_name.lower()}cpu->${parent.name}[i] = ${ref}${core_name.lower()}cpu->${reg.name}[i]; } % else: + % if not isinstance(reg, (arch.Memory, arch.Alias)): % if reg.is_pc: - ${core_name.lower()}cpu->${parent.name}[${reg.range.lower}] = (etiss_uint${reg.size}*)&(cpu->instructionPointer); + ${core_name.lower()}cpu->${parent.name}[0] = (etiss_uint${reg.size}*)&(cpu->instructionPointer); % else: - % if parent.range.length > 1: - ${core_name.lower()}cpu->${parent.name}[${reg.range.lower}] = ${ref}${core_name.lower()}cpu->${reg.name}; + % if isinstance(reg.ty, type_info.ArrayType): + ${core_name.lower()}cpu->${parent.name}[0] = ${ref}${core_name.lower()}cpu->${reg.name}; % else: ${core_name.lower()}cpu->${parent.name} = ${ref}${core_name.lower()}cpu->${reg.name}; % endif % endif % endif + % endif % endfor % for reg in initval_regs: -<% ref = "*" if len(reg.children) > 0 else "" %>\ - % if reg.range.length > 1: +<% ref = "*" if isinstance(reg.ty, type_info.ArrayType) else "" %>\ + % if isinstance(reg.ty, type_info.ArrayType): % for idx, val in reg._initval.items(): <% suffix = "ULL" if val > 0 else "LL" %>\ ${ref}${core_name.lower()}cpu->${reg.name}[${idx}] = ${val}${suffix}; @@ -140,7 +144,7 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) % endif % endfor % if procno_memory is not None: -<% ref = "*" if len(reg.children) > 0 else "" %>\ +<% ref = "*" if isinstance(reg.ty, type_info.ArrayType) else "" %>\ ${ref}${core_name.lower()}cpu->${procno_memory.name} = coreno_; % endif } diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index 69582d4..c2c70b8 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -44,23 +44,23 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore std::string mapRegister(unsigned index) { % if mapping is None: - if (index < ${main_reg.range.length}) + if (index < ${main_reg.ty.length}) { std::stringstream ss; ss << "${main_reg.name}" << index; return ss.str(); } % if float_reg is not None: - if ((${main_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 1})) + if ((${main_reg.ty.length} < index) && (index < ${main_reg.ty.length + float_reg.ty.length + 1})) { std::stringstream ss; - ss << "${float_reg.name}" << (index - ${main_reg.range.length + 1}); + ss << "${float_reg.name}" << (index - ${main_reg.ty.length + 1}); return ss.str(); } - if ((${main_reg.range.length + float_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 5})) + if ((${main_reg.ty.length + float_reg.ty.length} < index) && (index < ${main_reg.ty.length + float_reg.ty.length + 5})) { // FCSR std::stringstream ss; - ss << "CSR" << (index - ${main_reg.range.length + float_reg.range.length + 1}); + ss << "CSR" << (index - ${main_reg.ty.length + float_reg.ty.length + 1}); return ss.str(); } % endif @@ -73,7 +73,7 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore return "${name2}"; // ${name} % endfor % else: - case ${main_reg.range.length}: + case ${main_reg.ty.length}: return "instructionPointer"; % endif /************************************************************************** @@ -88,7 +88,7 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore unsigned mappedRegisterCount() { // Modify according to sent register number - return ${main_reg.range.length + 1}; + return ${main_reg.ty.length + 1}; } etiss::uint64 getInstructionPointer(ETISS_CPU *cpu) { return cpu->instructionPointer; } diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index c0949a1..70d0217 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -191,20 +191,20 @@ etiss::InterruptVector *${core_name}Arch::createInterruptVector(ETISS_CPU *cpu) return 0; % if irq_en_reg is not None and irq_pending_reg is not None: -<% en_ref = "" if len(irq_en_reg.children) > 0 else "&" %>\ -<% pending_ref = "" if len(irq_pending_reg.children) > 0 else "&" %>\ - std::vector vec; - std::vector mask; +<% en_ref = "&" %>\ +<% pending_ref = "&" %>\ + std::vector vec; + std::vector mask; vec.push_back(${en_ref}((${core_name} *)cpu)->${irq_pending_reg.name}); mask.push_back(${pending_ref}((${core_name} *)cpu)->${irq_en_reg.name}); - return new etiss::MappedInterruptVector(vec, mask); + return new etiss::MappedInterruptVector(vec, mask); % else: - std::vector vec; - std::vector mask; + std::vector vec; + std::vector mask; - return new etiss::MappedInterruptVector(vec, mask); + return new etiss::MappedInterruptVector(vec, mask); % endif } @@ -216,8 +216,8 @@ void ${core_name}Arch::deleteInterruptVector(etiss::InterruptVector *vec, ETISS_ % if global_irq_en_reg is not None: etiss::InterruptEnable *${core_name}Arch::createInterruptEnable(ETISS_CPU *cpu) { -<% ref = "" if len(global_irq_en_reg.children) > 0 else "&" %>\ - return new etiss::MappedInterruptEnable(${ref}((${core_name} *)cpu)->${global_irq_en_reg.name}, ${global_irq_en_mask}); +<% ref = "&" %>\ + return new etiss::MappedInterruptEnable(${ref}((${core_name} *)cpu)->${global_irq_en_reg.name}, ${global_irq_en_mask}); } void ${core_name}Arch::deleteInterruptEnable(etiss::InterruptEnable *en, ETISS_CPU *cpu) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index 5de2720..0e99ca0 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -46,7 +46,7 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field std::string("${main_reg.name}") + etiss::toString(gprid), std::string("${main_reg.name}") + etiss::toString(gprid), R|W, - ${int(main_reg.size / 8)} + ${(int(main_reg.ty.length)* int(main_reg.ty.element_type.size) / 8)} ), // clang-format on gprid_(gprid) @@ -60,7 +60,7 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int(main_reg.size / 8)} + ${(int(main_reg.ty.length)* int(main_reg.ty.element_type.size) / 8)} ), // clang-format on gprid_(gprid) @@ -88,9 +88,9 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: - *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.ty.element_type.size}) val; % else: - ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.ty.element_type.size}) val; % endif // clang-format on } @@ -109,7 +109,7 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field std::string("${float_reg.name}")+etiss::toString(gprid), std::string("${float_reg.name}")+etiss::toString(gprid), R|W, - ${int(float_reg.size / 8)} + ${(int(float_reg.ty.length)* int(float_reg.ty.element_type.size) / 8)} ), gprid_(gprid) // clang-format on @@ -122,7 +122,7 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int(float_reg.size / 8)} + ${(int(float_reg.ty.length)* int(float_reg.ty.element_type.size) / 8)} ), gprid_(gprid) // clang-format on @@ -150,9 +150,9 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: - *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.ty.element_type.size}) val; % else: - ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.ty.element_type.size}) val; % endif // clang-format on } @@ -172,7 +172,7 @@ class VectorRegField_${core_name} : public etiss::VirtualStruct::Field std::string("${vector_reg.name}")+etiss::toString(gprid), std::string("${vector_reg.name}")+etiss::toString(gprid), R|W, - ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ${int((vector_reg.ty.length // 32) // (vector_reg.ty.element_type.size // 8))} ), gprid_(gprid) // clang-format on @@ -185,7 +185,7 @@ class VectorRegField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ${int((vector_reg.ty.length // 32) // (vector_reg.ty.element_type.size // 8))} ), gprid_(gprid) // clang-format on @@ -227,7 +227,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field std::string("${csr_reg.name}")+etiss::toString(gprid), std::string("${csr_reg.name}")+etiss::toString(gprid), R|W, - ${int(csr_reg.size / 8)} + ${int(csr_reg.ty.element_type.size / 8)} ), gprid_(gprid) // clang-format on @@ -240,7 +240,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int(csr_reg.size / 8)} + ${int(csr_reg.ty.element_type.size / 8)} ), gprid_(gprid) // clang-format on @@ -254,7 +254,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field { // clang-format off assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.size}) gprid_); + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.ty.element_type.size}) gprid_); // clang-format on } @@ -263,7 +263,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field // clang-format off assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.size}) val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.ty.element_type.size}) val); // clang-format on } }; @@ -280,7 +280,7 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field "instructionPointer", "instructionPointer", R|W, - ${int(main_reg.size / 8)} + ${int(main_reg.ty.element_type.size / 8)} ) // clang-format on { @@ -302,7 +302,7 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field // clang-format off assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.size}) val; + ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.ty.element_type.size}) val; // clang-format on } }; diff --git a/m2isar/backends/etiss/templates/etiss_arch_struct.mako b/m2isar/backends/etiss/templates/etiss_arch_struct.mako index 94ce4a0..f2c10f0 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_struct.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_struct.mako @@ -29,6 +29,9 @@ extern "C" % for reg in regs: ${reg}; % endfor + % for mem in mems: + ${mem}; + % endfor }; #pragma pack(pop) // undo changes diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 82a4492..e321dc5 100644 --- a/m2isar/backends/etiss/virtualstruct_utils.py +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -14,7 +14,7 @@ from collections import defaultdict import xml.etree.ElementTree as ET from xml.etree import ElementTree, ElementInclude -from ...metamodel.arch import MemoryAttribute +from ...metamodel import arch, type_info, attribute_info DEFAULT_ALIASES = {"zero": "x0", "ra": "x1", "sp": "x2", "gp": "x3", "tp": "x4", "t0": "x5", "t1": "x6", "t2": "x7", "s0": "x8", "fp": "x8", "s1": "x9", "a0": "x10", "a1": "x11", "a2": "x12", "a3": "x13", "a4": "x14", "a5": "x15", "a6": "x16", "a7": "x17", "s2": "x18", "s3": "x19", "s4": "x20", "s5": "x21", "s6": "x22", "s7": "x23", "s8": "x24", "s9": "x25", "s10": "x26", "s11": "x27", "t3": "x28", "t4": "x29", "t5": "x30", "t6": "x31"} @@ -155,23 +155,31 @@ def get_gdb_mapping(mapping: dict, memories: dict, memory_aliases: dict): return gdb_mapping -def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): +def get_virtualstruct_regs(mapping: dict, + registers: "dict[str, Union[arch.RegisterBank, arch.Register]]", + register_aliases: "dict[str, arch.Alias]", + memories: "dict[str, arch.Memory]", + memory_aliases: "dict[str, arch.Alias]"): main_reg = None float_reg = None vector_reg = None csr_reg = None pc_reg = None - for mem in memories.values(): - if mem.is_pc: + for mem in registers.values(): + if attribute_info.RegisterAttribute.IS_PC in mem.attributes: pc_reg = mem - elif MemoryAttribute.IS_MAIN_REG in mem.attributes or mem.name == "X": + elif attribute_info.RegisterAttribute.IS_MAIN_REG in mem.attributes or mem.name == "X": main_reg = mem - elif MemoryAttribute.IS_FLOAT_REG in mem.attributes or mem.name == "F": + elif attribute_info.RegisterAttribute.IS_FLOAT_REG in mem.attributes or mem.name == "F": float_reg = mem - elif MemoryAttribute.IS_VECTOR_REG in mem.attributes or mem.name == "F": + elif attribute_info.RegisterAttribute.IS_VECTOR_REG in mem.attributes or mem.name == "V": vector_reg = mem - elif MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name == "CSR": + + #CSRREG is memory + for mem in memories.values(): + if attribute_info.MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name == "CSR": csr_reg = mem + aliased_csrs = set() if csr_reg is not None: for mem in memory_aliases.values(): @@ -179,8 +187,8 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): if mem.range.length == 1: idx = mem.range.lower aliased_csrs.add(idx) - assert main_reg is not None, "Unable to identify main_reg" - assert pc_reg is not None, "Unable to identify pc_reg" + assert main_reg is not None and isinstance(main_reg, arch.RegisterBank), "Unable to identify main_reg" + assert pc_reg is not None and isinstance(pc_reg, arch.Register), "Unable to identify pc_reg" VIRTUALSTRUCT_CLASSES = { main_reg.name: "RegField", **({float_reg.name: "FloatRegField"} if float_reg is not None else {}), @@ -190,8 +198,8 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): } aliased_csrs_only = True DEFAULT_VIRTUALSTRUCT_REGS = { - "RegField": [range(0, main_reg.range.length)], - **({"FloatRegField": [range(0, float_reg.range.length)]} if float_reg is not None else {}), + "RegField": [range(0, arch.get_const_or_val(main_reg.ty.length))], + **({"FloatRegField": [range(0, arch.get_const_or_val(float_reg.ty.length) )]} if float_reg is not None else {}), # **({"VectorRegField": [range(0, vector_reg.range.length)]} if vector_reg is not None else {}), **({"VectorRegField": [range(0, 32)]} if vector_reg is not None else {}), **({"CSRField": list(sorted(aliased_csrs)) if aliased_csrs_only else [range(0, csr_reg.range.length)]} if csr_reg is not None else {}), @@ -202,7 +210,7 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): if mapping is not None: for regnum, data in mapping.items(): name, sz = data - resolved = resolve_reg(name, memories, memory_aliases) + resolved = resolve_reg(name, registers, register_aliases) assert resolved is not None, f"Register lookup failed: {name}" if resolved is not None: mem, idx = resolved diff --git a/m2isar/backends/etiss/writer.py b/m2isar/backends/etiss/writer.py index 8624e0a..b742d73 100755 --- a/m2isar/backends/etiss/writer.py +++ b/m2isar/backends/etiss/writer.py @@ -161,7 +161,7 @@ def main(): for core_name, core in cores.items(): logger.info("processing model %s", core_name) mapping = descr_mapping.get(core_name) - virtualstruct_regs = get_virtualstruct_regs(mapping, core.memories, core.memory_aliases) + virtualstruct_regs = get_virtualstruct_regs(mapping, core.register_banks, core.register_aliases, core.memories, core.memory_aliases) gdb_mapping = get_gdb_mapping(mapping, core.memories, core.memory_aliases) # create output files path diff --git a/m2isar/backends/isa_manual/visitor.py b/m2isar/backends/isa_manual/visitor.py index d9bd3d8..0e4dbab 100644 --- a/m2isar/backends/isa_manual/visitor.py +++ b/m2isar/backends/isa_manual/visitor.py @@ -55,11 +55,7 @@ def _(self, expr: behav.ConcatOperation, writer): self.generate(expr.right, writer) @generate.register - def _(self, expr: behav.IntLiteral, writer): - writer.write(expr.value) - - @generate.register - def _(self, expr: behav.IntLiteral, writer): + def _(self, expr: behav.Literal, writer): writer.write(expr.value) @generate.register @@ -134,7 +130,7 @@ def _(self, expr: behav.UnaryOperation, writer): @generate.register def _(self, expr: behav.NamedReference, writer): writer.write(expr.reference.name) - if isinstance(expr.reference, (arch.Constant, arch.Memory, arch.Scalar)): + if isinstance(expr.reference, (arch.Parameter, arch.Memory, arch.Scalar)): pass @generate.register diff --git a/m2isar/backends/isa_manual/writer.py b/m2isar/backends/isa_manual/writer.py index 53c5e90..9c48c60 100644 --- a/m2isar/backends/isa_manual/writer.py +++ b/m2isar/backends/isa_manual/writer.py @@ -17,6 +17,8 @@ from collections import defaultdict from mako.template import Template +from m2isar.metamodel import type_info + from .utils import generate_encoding from .visitor import ISAmanualVisitor @@ -143,11 +145,11 @@ def leave_block(self, br=True, nl=True): self.write("}", nl=nl) def write_type(self, data_type, size): - if data_type == arch.DataType.U: + if data_type == type_info.TypeKind.UINT: self.write("unsigned") - elif data_type == arch.DataType.S: + elif data_type == type_info.TypeKind.INT: self.write("signed") - elif data_type == arch.DataType.NONE: + elif data_type == type_info.TypeKind.VOID: self.write("void") else: raise NotImplementedError(f"Unsupported type: {data_type}") diff --git a/m2isar/backends/viewer/treegen.py b/m2isar/backends/viewer/treegen.py index aef529a..f3d864e 100644 --- a/m2isar/backends/viewer/treegen.py +++ b/m2isar/backends/viewer/treegen.py @@ -58,6 +58,7 @@ def visit_binary_operation(self, expr: behav.BinaryOperation, context: "TreeGenC context.pop() context.tree.insert(context.parent, tk.END, text="Op", values=(expr.op.value,)) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) context.pop() @@ -77,6 +78,8 @@ def visit_slice_operation(self, expr: behav.SliceOperation, context: "TreeGenCon self.generate(expr.right, context) context.pop() + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) + context.pop() @@ -92,19 +95,19 @@ def concat_operation(self, expr: behav.ConcatOperation, context: "TreeGenContext self.generate(expr.right, context) context.pop() + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) + context.pop() @generate.register - def number_literal(self, expr: behav.NumberLiteral, context: "TreeGenContext"): + def number_literal(self, expr: behav.Literal, context: "TreeGenContext"): context.tree.insert(context.parent, tk.END, text="Number Literal", values=(expr.value,)) - - @generate.register - def int_literal(self, expr: behav.IntLiteral, context: "TreeGenContext"): - context.tree.insert(context.parent, tk.END, text="Int Literal", values=(expr.value,)) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) @generate.register def scalar_definition(self, expr: behav.ScalarDefinition, context: "TreeGenContext"): context.tree.insert(context.parent, tk.END, text="Scalar Definition", values=(expr.scalar.name,)) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) @generate.register def break_(self, expr: behav.Break, context: "TreeGenContext"): @@ -173,6 +176,8 @@ def ternary(self, expr: behav.Ternary, context: "TreeGenContext"): self.generate(expr.else_expr, context) context.pop() + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) + context.pop() @generate.register @@ -195,18 +200,22 @@ def unary_operation(self, expr: behav.UnaryOperation, context: "TreeGenContext") context.pop() context.tree.insert(context.parent, tk.END, text="Op", values=(expr.op.value,)) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) context.pop() @generate.register def named_reference(self, expr: behav.NamedReference, context: "TreeGenContext"): context.tree.insert(context.parent, tk.END, text="Named Reference", values=(f"{expr.reference}",)) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) @generate.register def indexed_reference(self, expr: behav.IndexedReference, context: "TreeGenContext"): context.push(context.tree.insert(context.parent, tk.END, text="Indexed Reference")) - context.tree.insert(context.parent, tk.END, text="Reference", values=(f"{expr.reference}",)) + context.push(context.tree.insert(context.parent, tk.END, text="Reference", values=(f"{expr.reference}",))) + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.reference.ty,)) + context.pop() # If LHS is a complex expression => RHS also an expression # TODO: Little Endian only so far supported @@ -234,6 +243,8 @@ def indexed_reference(self, expr: behav.IndexedReference, context: "TreeGenConte self.generate(expr.index, context) context.pop() + context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) + context.pop() @generate.register diff --git a/m2isar/backends/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index 57cbecb..3aea429 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -21,7 +21,7 @@ from m2isar.backends.viewer.utils import TreeGenContext -from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch +from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch, type_info from ...metamodel.utils.expr_preprocessor import (process_attributes, process_functions, process_instructions) @@ -107,20 +107,33 @@ def main(): for core_name, core_def in sorted(cores.items()): core_id = tree.insert("", tk.END, text=core_name) - # add constants to tree - consts_id = tree.insert(core_id, tk.END, text="Constants") - for const_name, const_def in sorted(core_def.constants.items()): + # add parameters to tree + consts_id = tree.insert(core_id, tk.END, text="Parameters") + for const_name, const_def in sorted(core_def.parameters.items()): tree.insert(consts_id, tk.END, text=const_name, values=(const_def.value,)) # add memories to tree mems_id = tree.insert(core_id, tk.END, text="Memories") for mem_name, mem_def in sorted(core_def.memories.items()): - tree.insert(mems_id, tk.END, text=mem_name, values=(f"{mem_def.range.upper}:{mem_def.range.lower} ({mem_def.range.length}), {mem_def.size}",)) + tree.insert(mems_id, tk.END, text=mem_name, values=(f"{arch.get_const_or_val(mem_def.ty.length)}:0 ({mem_def.ty.element_type.kind}), {arch.get_const_or_val(mem_def.ty.element_type.size)}",)) # add memory aliases to tree - alias_id = tree.insert(core_id, tk.END, text="Memory Aliases") + mem_alias_id = tree.insert(core_id, tk.END, text="Memory Aliases") for mem_name, mem_def in sorted(core_def.memory_aliases.items()): - tree.insert(alias_id, tk.END, text=f"{mem_name} ({mem_def.parent.name})", values=(f"{mem_def.range.upper}:{mem_def.range.lower} ({mem_def.range.length}), {mem_def.size}",)) + tree.insert(mem_alias_id, tk.END, text=f"{mem_name} ({mem_def.parent.name})", values=(f"{mem_def.range.upper}:{mem_def.range.lower} ({mem_def.range.length}), {mem_def.ty.size}",)) + + # add memories to tree + regs_id = tree.insert(core_id, tk.END, text="Register Banks") + for reg_name, reg_def in sorted(core_def.register_banks.items()): + length = arch.get_const_or_val(reg_def.ty.length) if isinstance(reg_def.ty, type_info.ArrayType) else 1 + kind = reg_def.ty.element_type.kind if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.kind + size = reg_def.ty.element_type.size if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.size + tree.insert(regs_id, tk.END, text=reg_name, values=(f"NR_ELE: {length}, ELE_TYPE {kind} {size}",)) + + # add memory aliases to tree + reg_alias_id = tree.insert(core_id, tk.END, text="Register Bank Aliases") + for reg_name, reg_def in sorted(core_def.register_aliases.items()): + tree.insert(reg_alias_id, tk.END, text=f"{reg_name} ({reg_def.parent.name})", values=(f"{reg_def.range.upper}:{reg_def.range.lower} ({reg_def.range.length})",)) # add auxillary attributes tree.insert(core_id, tk.END, text="Main Memory Object", values=(core_def.main_memory,)) @@ -134,7 +147,7 @@ def main(): fn_id = tree.insert(fns_id, tk.END, text=fn_name, values=("extern" if fn_def.extern else "")) # add returns and throws information - return_str = "None" if fn_def.size is None else f"{fn_def.data_type} {fn_def.size}" + return_str = "None" if fn_def.ty.size is None else f"{fn_def.ty.kind} {fn_def.ty.size}" tree.insert(fn_id, tk.END, text="Return", values=(return_str,)) tree.insert(fn_id, tk.END, text="Throws", values=(fn_def.throws)) @@ -151,7 +164,7 @@ def main(): params_id = tree.insert(fn_id, tk.END, text="Parameters") for param_name, param_def in fn_def.args.items(): - tree.insert(params_id, tk.END, text=param_name, values=(f"{param_def.data_type} {param_def.size}",)) + tree.insert(params_id, tk.END, text=param_name, values=(f"{param_def.ty.kind} {param_def.ty.size}",)) # generate and add function behavior context = TreeGenContext(tree, fn_id) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 79ded9a..a3db027 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -12,7 +12,7 @@ from ... import (M2DuplicateError, M2NameError, M2TypeError, M2ValueError, flatten) -from ...metamodel import arch, behav, intrinsics +from ...metamodel import arch, behav, type_info, attribute_info, intrinsics from ...metamodel.code_info import FunctionInfoFactory from .parser_gen import CoreDSL2Parser, CoreDSL2Visitor from .utils import RADIX, SHORTHANDS, SIGNEDNESS @@ -24,24 +24,26 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): """ANTLR visitor to build an M2-ISA-R architecture model of a CoreDSL 2 specification.""" - _constants: "dict[str, arch.Constant]" + _parameters: "dict[str, arch.Parameter]" _instructions: "list[arch.Instruction]" _functions: "dict[str, arch.Function]" _always_blocks: "dict[str, arch.AlwaysBlock]" _instruction_sets: "dict[str, arch.InstructionSet]" _read_types: "dict[str, str]" _memories: "dict[str, arch.Memory]" - _memory_aliases: "dict[str, arch.Memory]" + _memory_aliases: "dict[str, arch.Alias]" + _register_banks: "dict[str, Union[arch.RegisterBank, arch.Register]]" + _register_aliases: "dict[str, arch.Alias]" _overwritten_instrs: "list[tuple[arch.Instruction, arch.Instruction]]" _instr_classes: "set[int]" - _main_reg_file: Union[arch.Memory, None] - _float_reg_file: Union[arch.Memory, None] - _vector_reg_file: Union[arch.Memory, None] - _csr_reg_file: Union[arch.Memory, None] + _main_reg_file: Union[arch.RegisterBank, None] + _float_reg_file: Union[arch.RegisterBank, None] + _vector_reg_file: Union[arch.RegisterBank, None] + _csr_reg_file: Union[arch.RegisterBank, None] def __init__(self): super().__init__() - self._constants = {} + self._parameters = {} # self._instructions = {} self._instructions = [] self._functions = {} @@ -50,6 +52,8 @@ def __init__(self): self._read_types = {} self._memories = {} self._memory_aliases = {} + self._register_banks = {} + self._register_aliases = {} self._overwritten_instrs = [] self._instr_classes = set() @@ -67,13 +71,13 @@ def visitBit_field(self, ctx: CoreDSL2Parser.Bit_fieldContext): # instantiate M2-ISA-R objects range_spec = arch.RangeSpec(left.value, right.value) - return arch.BitField(ctx.name.text, range_spec, arch.DataType.U) + return arch.BitField(ctx.name.text, range_spec, type_info.TypeKind.UINT) def visitBit_value(self, ctx: CoreDSL2Parser.Bit_valueContext): """Generate a fixed encoding part.""" val = self.visit(ctx.value) - return arch.BitVal(val.bit_size, val.value) + return arch.BitVal(val.ty.size, val.value) def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): """Generate a top-level instruction set object.""" @@ -89,18 +93,23 @@ def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): # generate flat list of instruction set contents contents = flatten([self.visit(obj) for obj in ctx.sections]) - constants = {} + parameters = {} memories = {} + register_banks = {} functions = {} # instructions = {} instructions = [] # group contents by type for item in contents: - if isinstance(item, arch.Constant): - constants[item.name] = item + if isinstance(item, arch.Parameter): + parameters[item.name] = item elif isinstance(item, arch.Memory): memories[item.name] = item + elif isinstance(item, (arch.RegisterBank, arch.Register)): + register_banks[item.name] = item + elif isinstance(item, arch.Alias): # Aliases are basically handled of children of memories+register banks + pass elif isinstance(item, arch.Function): functions[item.name] = item item.ext_name = name @@ -114,7 +123,7 @@ def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): raise M2ValueError("unexpected item encountered") # instantiate M2-ISA-R object - i = arch.InstructionSet(name, extension, constants, memories, functions, instructions) + i = arch.InstructionSet(name, extension, parameters, memories, register_banks, functions, instructions) if name in self._instruction_sets: raise M2DuplicateError(f"instruction set \"{name}\" already defined") @@ -142,9 +151,9 @@ def visitCore_def(self, ctx: CoreDSL2Parser.Core_defContext): name = ctx.name.text c = arch.CoreDef(name, list(self._read_types.keys()), None, - self._constants, self._memories, self._memory_aliases, - self._functions, self._instructions, self._instr_classes, - intrinsics) + self._parameters, self._memories, self._memory_aliases, + self._register_banks, self._register_aliases, self._functions, + self._instructions, self._instr_classes, intrinsics) return c @@ -203,8 +212,8 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex # decode attributes attributes = dict([self.visit(obj) for obj in ctx.attributes]) - if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in attributes: - attributes[arch.FunctionAttribute.ETISS_NEEDS_ARCH] = [] + if attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in attributes: + attributes[attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH] = [] # decode return type and name type_ = self.visit(ctx.type_) @@ -219,11 +228,11 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex params = [params] return_size = None - data_type = arch.DataType.NONE + data_type = type_info.TypeKind.VOID - if isinstance(type_, arch.IntegerType): - return_size = type_._width - data_type = arch.DataType.S if type_.signed else arch.DataType.U + if isinstance(type_, type_info.PrimitiveType): + return_size = type_.size + data_type = type_.kind f = arch.Function(name, attributes, return_size, data_type, params, ctx.behavior, ctx.extern is not None) if not f.extern: @@ -255,7 +264,7 @@ def visitParameter_declaration(self, ctx: CoreDSL2Parser.Parameter_declarationCo if ctx.decl.size: size = [self.visit(obj) for obj in ctx.decl.size] - p = arch.FnParam(name, type_._width, arch.DataType.S if type_.signed else arch.DataType.U) + p = arch.FnParam(name, type_.size, type_.kind) return p def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): @@ -268,6 +277,7 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): tick_pos = text.find("'") # decode verilog-style literal + value = None if tick_pos != -1: width = int(text[:tick_pos]) radix = text[tick_pos+1] @@ -286,7 +296,8 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): else: width = value.bit_length() - return behav.IntLiteral(value, width) + kind = type_info.TypeKind.UINT if value >=0 else type_info.TypeKind.INT + return behav.Literal(value, kind, width) def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): """Generate a declaration.""" @@ -298,6 +309,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # extract data type type_ = self.visit(ctx.type_) + assert isinstance(type_, (type_info.PrimitiveType, type_info.PointerType)) # extract list of contained declarations for the given type decls: "list[CoreDSL2Parser.DeclaratorContext]" = ctx.declarations @@ -309,9 +321,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # generate a register alias - if type_.ptr == "&": + if isinstance(type_, type_info.PointerType): # error out on duplicate declaration - if name in self._memory_aliases: + if name in self._memory_aliases or name in self._register_aliases: raise M2DuplicateError(f"memory {name} already defined") # assume default size @@ -338,34 +350,102 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # raise ValueError(f"range mismatch for \"{name}\"") # instantiate M2-ISA-R object, keep track of parent - child relations - m = arch.Memory(name, range_spec, type_._width, attributes) - m.parent = reference - m.parent.children.append(m) + + alias = arch.Alias(name, reference, range_spec, type_, attributes) + self._memory_aliases[name] = alias + # # TODO: Decide if alias can have a range ??? + # Might make sense to do this for vreg0...vreg31 -> + # register unsigned V[32][8] [[is_main_reg]]; + + # // register aliases + # alias vreg0 = V[0]; + #vs + # unsigned& vreg0[16] = V[0][7:0] && V[1][7:0]; + # or even more complex: + # unsigned& vreg0.5[4] = V[0][3:0]; + + alias.parent.children.append(alias) # keep track of this declaration globally - self._memory_aliases[name] = m + if isinstance(reference, (arch.RegisterBank, arch.Register)): + self._register_aliases[name] = alias + elif isinstance(reference, arch.Memory): + self._memory_aliases[name] = alias + else: + raise M2TypeError("invalid alias reference type") # keep track of this declaration for this declaration statement - ret_decls.append(m) + ret_decls.append(alias) # normal declaration else: # no storage specifier -> implementation parameter, "Constant" in M2-ISA-R if len(storage) == 0: - if name in self._constants: - raise M2DuplicateError(f"constant {name} already defined") + if name in self._parameters: + raise M2DuplicateError(f"Parameter {name} already defined") # extract initializer if present init = None if decl.init is not None: init = self.visit(decl.init) - c = arch.Constant(name, init, [], type_._width, type_.signed) + signed = True if type_.kind == type_info.TypeKind.INT else False + + c = arch.Parameter(name, init, [], type_.size, signed) - self._constants[name] = c + self._parameters[name] = c ret_decls.append(c) # register and extern declaration: "Memory" object in M2-ISA-R - elif "register" in storage or "extern" in storage: + elif "register" in storage: + if name in self._register_banks: + raise M2DuplicateError(f"register bank {name} already defined") + + size = [1] + init = None + attributes = {} + + if decl.size: + size = [self.visit(obj) for obj in decl.size] + + if len(size) > 1: + raise NotImplementedError("arrays with more than one dimension are not supported") + + if decl.init is not None: + init = self.visit(decl.init) + + if decl.attributes: + attributes = dict([self.visit(obj) for obj in decl.attributes]) + + # TODO: Constant might be 1 as well and makes a RegisterBank to Register ... [1] should be illegal anyways? + if isinstance(size[0], (arch.Parameter, behav.NamedReference)): + m = arch.RegisterBank(name, size[0], type_.kind, type_.size, attributes) + elif isinstance(size[0], behav.Literal): + if size[0].value >= 1: + m = arch.RegisterBank(name, size[0].value, type_.kind, type_.size, attributes) + elif size[0].value == 1: + m = arch.Register(name, type_.kind, type_.size, attributes) + else: + M2ValueError("Size is negative for Registerbank") + elif isinstance(size[0], int): + # Unset Case: =1 + m = arch.Register(name, type_.kind, type_.size, attributes) + else: + raise NotImplementedError("Only constant Parameter and Int allowed as dimension for register size") + + # attach init value to register bank object + if init is not None: + m._initval[None] = exprInterpretVisitor.generate(init, None) + + if attribute_info.RegisterAttribute.IS_MAIN_REG in attributes: + self._main_reg_file = m + if attribute_info.RegisterAttribute.IS_FLOAT_REG in attributes: + self._float_reg_file = m + if attribute_info.RegisterAttribute.IS_VECTOR_REG in attributes: + self._vector_reg_file = m + + self._register_banks[name] = m + ret_decls.append(m) + elif "extern" in storage: if name in self._memories: raise M2DuplicateError(f"memory {name} already defined") @@ -385,20 +465,13 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.attributes: attributes = dict([self.visit(obj) for obj in decl.attributes]) - range_spec = arch.RangeSpec(size[0]) - m = arch.Memory(name, range_spec, type_._width, attributes) + m = arch.Memory(name, type_.kind, type_.size, size[0], attributes) # attach init value to memory object if init is not None: m._initval[None] = exprInterpretVisitor.generate(init, None) - if arch.MemoryAttribute.IS_MAIN_REG in attributes: - self._main_reg_file = m - if arch.MemoryAttribute.IS_FLOAT_REG in attributes: - self._float_reg_file = m - if arch.MemoryAttribute.IS_VECTOR_REG in attributes: - self._vector_reg_file = m - if arch.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": + if attribute_info.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": self._csr_reg_file = m self._memories[name] = m @@ -409,7 +482,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): def visitType_specifier(self, ctx: CoreDSL2Parser.Type_specifierContext): type_ = self.visit(ctx.type_) if ctx.ptr: - type_.ptr = ctx.ptr.text + type_ = type_info.PointerType(type_) + # type_.ptr = ctx.ptr.text return type_ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): @@ -433,22 +507,24 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): width = self.visit(ctx.shorthand) # type check width - if isinstance(width, behav.IntLiteral): + if isinstance(width, behav.Literal): width = width.value elif isinstance(width, behav.NamedReference): width = width.reference else: raise M2TypeError("width has wrong type") - return arch.IntegerType(width, signed, None) + kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT + + return type_info.PrimitiveType(kind, width) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type.""" - return arch.VoidType(None) + return type_info.PrimitiveType(type_info.TypeKind.VOID, None) def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool (alias for unsigned<1>).""" - return arch.IntegerType(1, False, None) + return type_info.PrimitiveType(type_info.TypeKind.UINT, 1) def visitBinary_expression(self, ctx: CoreDSL2Parser.Binary_expressionContext): """Generate a binary expression.""" @@ -480,7 +556,8 @@ def visitReference_expression(self, ctx: CoreDSL2Parser.Reference_expressionCont name = ctx.ref.text # try to resolve the reference, error out if invalid - ref = self._constants.get(name) or self._memories.get(name) or self._memory_aliases.get(name) + ref = self._parameters.get(name) or self._memories.get(name) or self._memory_aliases.get(name) \ + or self._register_banks.get(name) or self._register_aliases.get(name) if ref is None: raise M2NameError(f"reference \"{name}\" could not be resolved") return behav.NamedReference(ref) @@ -495,7 +572,8 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) return SIGNEDNESS[ctx.children[0].symbol.text] def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): - return behav.IntLiteral(SHORTHANDS[ctx.children[0].symbol.text]) + value = SHORTHANDS[ctx.children[0].symbol.text] + return behav.Literal(value, type_info.TypeKind.NONE) def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionContext): """Generate an assignment. """ @@ -506,12 +584,11 @@ def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionCo # if LHS is a reference, assign RHS as its default value if isinstance(left, behav.NamedReference): - if isinstance(left.reference, arch.Constant): + if isinstance(left.reference, arch.Parameter): left.reference.value = exprInterpretVisitor.generate(right, None) elif isinstance(left.reference, arch.Memory): left.reference._initval[None] = exprInterpretVisitor.generate(right, None) - elif isinstance(left, behav.IndexedReference): left.reference._initval[exprInterpretVisitor.generate(left.index, None)] = exprInterpretVisitor.generate(right, None) @@ -521,9 +598,10 @@ def visitAttribute(self, ctx: CoreDSL2Parser.AttributeContext): name = ctx.name.text # read attribute from enums - attr = arch.InstrAttribute._member_map_.get(name.upper()) or \ - arch.MemoryAttribute._member_map_.get(name.upper()) or \ - arch.FunctionAttribute._member_map_.get(name.upper()) + attr = attribute_info.InstrAttribute._member_map_.get(name.upper()) or \ + attribute_info.MemoryAttribute._member_map_.get(name.upper()) or \ + attribute_info.FunctionAttribute._member_map_.get(name.upper()) \ + or attribute_info.RegisterAttribute._member_map_.get(name.upper()) # warn if attribute is unknown to M2-ISA-R if attr is None: diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 8e308a4..e6d4043 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -9,13 +9,12 @@ import copy import dataclasses import logging -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union from ... import M2NameError, M2SyntaxError, M2TypeError, flatten -from ...metamodel import arch, behav, intrinsics +from ...metamodel import arch, behav, type_info, intrinsics, attribute_info from ...metamodel.code_info import (BranchEntryInfoFactory, BranchInfo, LineInfoFactory, LineInfoPlacement) -from ...metamodel.utils import StaticType from .parser_gen import CoreDSL2Parser, CoreDSL2Visitor from .utils import BOOLCONST, RADIX, SHORTHANDS, SIGNEDNESS from .expr_interpreter import ExprInterpreterVisitor @@ -32,14 +31,17 @@ class BehaviorModelBuilder(CoreDSL2Visitor): of a CoreDSL 2 specification. """ - def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Memory]", - fields: "dict[str, arch.BitFieldDescr]", functions: "dict[str, arch.Function]", warned_fns: "set[str]"): + def __init__(self, parameters: "dict[str, arch.Parameter]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Alias]", + register_banks: "dict[str, Union[arch.RegisterBank, arch.Register]]", register_aliases: "dict[str, arch.Alias]", fields: "dict[str, arch.BitFieldDescr]", + functions: "dict[str, arch.Function]", warned_fns: "set[str]"): super().__init__() - self._constants = constants + self._parameters = parameters self._memories = memories self._memory_aliases = memory_aliases + self._register_banks = register_banks + self._register_aliases = register_aliases self._fields = fields self._scalars = {} self._functions = functions @@ -91,7 +93,7 @@ def visitMethod_call(self, ctx: "CoreDSL2Parser.Method_callContext"): if ref is None: raise M2NameError(f"function \"{name}\" is not defined") - if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in ref.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in ref.attributes: raise M2SyntaxError(f"exception entry function \"{name}\" must be called as procedure") # generate method arguments @@ -127,7 +129,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # instantiate a scalar and its definition - s = arch.Scalar(name, None, StaticType.NONE, type_.width, arch.DataType.S if type_.signed else arch.DataType.U) + s = arch.Variable(name, type_, attribute_info.StaticAttribute.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) @@ -136,7 +138,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init: init = self.visit(decl.init) else: - init = behav.IntLiteral(0) + init = behav.Literal(0, type_.kind, type_.size) a = behav.Assignment(sd, init, LineInfoFactory.make(decl.start.source[1].fileName, decl.start.start, decl.stop.stop, decl.start.line, decl.stop.line)) ret_decls.append(a) @@ -276,7 +278,7 @@ def visitParens_expression(self, ctx: CoreDSL2Parser.Parens_expressionContext): def visitSlice_expression(self, ctx: CoreDSL2Parser.Slice_expressionContext): """Generate a slice expression. Depending on context, this is translated to either an actual :class:`m2isar.metamodel.behav.SliceOperation`or - an :class:`m2isar.metamodel.behav.IndexedReference` if a :class:`m2isar.metamodel.arch.Memory + an :class:`m2isar.metamodel.behav.IndexedReference` if a :class:`m2isar.metamodel.arch.Memory/RegisterBank object is to be sliced. """ @@ -285,14 +287,19 @@ def visitSlice_expression(self, ctx: CoreDSL2Parser.Slice_expressionContext): left = self.visit(ctx.left) right = self.visit(ctx.right) if ctx.right else left - if isinstance(expr, behav.NamedReference) and isinstance(expr.reference, arch.Memory) and expr.reference.data_range.length > 1: + # TODO distinguish between Multi-Dimensional Slices and BitSlice with ArrayType/PrimitiveType??? + if isinstance(expr, behav.NamedReference) and isinstance(expr.reference, (arch.Memory, arch.RegisterBank)): + assert(expr.reference.ty, type_info.ArrayType) #Dont duplicate index to differentiate between index and ranged access if right == left: - return behav.IndexedReference(expr.reference, left, None, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.IndexedReference(expr.reference, left, None, \ + LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) else: - return behav.IndexedReference(expr.reference, left, right, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.IndexedReference(expr.reference, left, right, \ + LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) else: - return behav.SliceOperation(expr, left, right, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.SliceOperation(expr, left, right, \ + LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitConcat_expression(self, ctx: CoreDSL2Parser.Concat_expressionContext): """Generate a concatenation expression.""" @@ -300,7 +307,8 @@ def visitConcat_expression(self, ctx: CoreDSL2Parser.Concat_expressionContext): left = self.visit(ctx.left) right = self.visit(ctx.right) - return behav.ConcatOperation(left, right, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.ConcatOperation(left, right, \ + LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionContext): """Generate an assignment. If a combined arithmetic-assignment is present, @@ -326,9 +334,11 @@ def visitReference_expression(self, ctx: CoreDSL2Parser.Reference_expressionCont var = self._scalars.get(name) or \ self._fields.get(name) or \ - self._constants.get(name) or \ + self._parameters.get(name) or \ self._memory_aliases.get(name) or \ self._memories.get(name) or \ + self._register_aliases.get(name) or \ + self._register_banks.get(name) or \ intrinsics.get(name) if var is None: @@ -352,7 +362,9 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): value = int(text, 0) width = value.bit_length() - return behav.IntLiteral(value, width, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + kind = type_info.TypeKind.INT if value <= 0 else type_info.TypeKind.UINT + + return behav.Literal(value, kind, width, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitCharacter_constant(self, ctx: CoreDSL2Parser.Character_constantContext): """Generate a character literal. Converts directly to uint8.""" @@ -361,7 +373,7 @@ def visitCharacter_constant(self, ctx: CoreDSL2Parser.Character_constantContext) value = min(ord(text.replace("'", "")), 255) - return behav.IntLiteral(value, 8, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.Literal(value, type_info.TypeKind.UINT, size=8, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitString_constant(self, ctx: CoreDSL2Parser.String_constantContext): text: str = ctx.value.text @@ -369,14 +381,14 @@ def visitString_constant(self, ctx: CoreDSL2Parser.String_constantContext): assert text[0] == '"' and text[-1] == '"' text = text[1:-1] - return behav.StringLiteral(text, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.Literal(text, type_info.TypeKind.STR, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitBool_constant(self, ctx: CoreDSL2Parser.Bool_constantContext): """Generate a boolean literal. Converts directly to uint1.""" text: str = ctx.value.text - return behav.IntLiteral(BOOLCONST[text], 1, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.Literal(BOOLCONST[text], type_info.TypeKind.UINT, size=1, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitCast_expression(self, ctx: CoreDSL2Parser.Cast_expressionContext): """Generate a type cast.""" @@ -384,22 +396,22 @@ def visitCast_expression(self, ctx: CoreDSL2Parser.Cast_expressionContext): expr = self.visit(ctx.right) if ctx.type_: type_ = self.visit(ctx.type_) - sign = arch.DataType.S if type_.signed else arch.DataType.U - size = type_.width + kind = type_.kind + size = type_.size if ctx.sign: sign = self.visit(ctx.sign) - sign = arch.DataType.S if sign else arch.DataType.U + kind = type_info.TypeKind.INT if sign else type_info.TypeKind.UINT size = None - return behav.TypeConv(sign, size, expr, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.TypeConv(kind, size, expr, LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) def visitType_specifier(self, ctx: CoreDSL2Parser.Type_specifierContext): """Generate a generic type specifier.""" type_ = self.visit(ctx.type_) if ctx.ptr: - type_.ptr = ctx.ptr.text + type_ = type_info.PointerType(type_) return type_ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): @@ -422,17 +434,19 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): else: raise M2TypeError("width has wrong type") - return arch.IntegerType(width, signed, None) + kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT + + return type_info.PrimitiveType(kind, width) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type specifier.""" - return arch.VoidType(None) + return type_info.PrimitiveType(type_info.TypeKind.VOID, None) def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool type specifier. Aliases to unsigned<1>.""" - return arch.IntegerType(1, False, None) + return type_info.PrimitiveType(type_info.TypeKind.UINT, 1) def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext): """Generate integer signedness.""" @@ -442,4 +456,4 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): """Lookup a shorthand type specifier.""" - return behav.IntLiteral(SHORTHANDS[ctx.children[0].symbol.text], line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) + return behav.Literal(SHORTHANDS[ctx.children[0].symbol.text], type_info.TypeKind.NONE, line_info=LineInfoFactory.make(ctx.start.source[1].fileName, ctx.start.start, ctx.stop.stop, ctx.start.line, ctx.stop.line)) diff --git a/m2isar/frontends/coredsl2/expr_interpreter.py b/m2isar/frontends/coredsl2/expr_interpreter.py index 582b85b..be86978 100644 --- a/m2isar/frontends/coredsl2/expr_interpreter.py +++ b/m2isar/frontends/coredsl2/expr_interpreter.py @@ -26,16 +26,12 @@ def _(self, expr: behav.Group, context): return self.generate(expr.expr, context) @generate.register - def _(self, expr: behav.NumberLiteral, context): - return expr.value - - @generate.register - def _(self, expr: behav.IntLiteral, context): - return expr.value + def _(self, expr: behav.Literal, context): + return int(expr.value) @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Constant) and expr.reference.value is not None: + if isinstance(expr.reference, arch.Parameter) and expr.reference.value is not None: return expr.reference.value raise M2ValueError("non-interpretable value encountered") diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 853cabb..2282dbb 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -14,7 +14,7 @@ import sys from ... import M2Error, M2SyntaxError -from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch, behav +from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch, behav, type_info, attribute_info from ...metamodel.utils.expr_simplifier import ExprSimplifierVisitor from ...metamodel.code_info import CodeInfoBase from .architecture_model_builder import ArchitectureModelBuilder @@ -27,12 +27,12 @@ from ...transforms.validate_behav.validate import validate_behav from ...warnings import add_warnings_flags, KNOWN_WARNINGS -def try_eval_bool(operation, constants: "dict[str, arch.Constant]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Memory]", +def try_eval_bool(operation, parameters: "dict[str, arch.Parameter]", memories: "dict[str, arch.Memory]", memory_aliases: "dict[str, arch.Memory]", fields: "dict[str, arch.BitFieldDescr]", functions: "dict[str, arch.Function]", warned_fns: "set[str]"): simplifier = ExprSimplifierVisitor() # TODO: switch to ExprInterpreterVisitor? op = simplifier.generate(operation, None) - if not isinstance(op, behav.IntLiteral): + if not isinstance(op, behav.Literal): return None return op.value != 0 @@ -101,9 +101,9 @@ def main(): warned_fns = set() - logger.debug("checking core constants") + logger.debug("checking core parameters") unassigned_const = False - for const in core_def.constants.values(): + for const in core_def.parameters.values(): if const.value is None: logger.critical("constant %s in core %s has no value assigned!", const.name, core_name) unassigned_const = True @@ -113,20 +113,29 @@ def main(): logger.debug("evaluating core parameters") - for const_def in core_def.constants.values(): + for const_def in core_def.parameters.values(): const_def._value = const_def.value for mem_def in itertools.chain(core_def.memories.values(), core_def.memory_aliases.values()): - mem_def._size = mem_def.size - mem_def.range._lower_base = mem_def.range.lower_base - mem_def.range._upper_base = mem_def.range.upper_base + if isinstance(mem_def.ty, type_info.ArrayType): + size = arch.get_const_or_val(mem_def.ty.element_type.size) + elif isinstance(mem_def.ty, type_info.PrimitiveType): + size = arch.get_const_or_val(mem_def.ty.size) + else: + assert(isinstance(mem_def.ty, type_info.PointerType)) # TODO: PointerType handlind looks like cancer!!!! + if isinstance(mem_def.ty.ty, type_info.ArrayType): + size = arch.get_const_or_val(mem_def.ty.ty.element_type.size) + else: + size = arch.get_const_or_val(mem_def.ty.ty.size) + mem_def.ty.size = size for attr_name, attr_ops in mem_def.attributes.items(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - {}, core_def.functions, warned_fns) + + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + {}, {}, {}, core_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: @@ -135,13 +144,43 @@ def main(): mem_def.attributes[attr_name] = ops + for reg_def in itertools.chain(core_def.register_banks.values(), core_def.register_aliases.values()): + if isinstance(reg_def.ty, type_info.ArrayType): + reg_def.ty.element_type.size = arch.get_const_or_val(reg_def.ty.element_type.size) + reg_def.ty.length = arch.get_const_or_val(reg_def.ty.length) + elif isinstance(reg_def.ty, type_info.PrimitiveType): + reg_def.ty.size = arch.get_const_or_val(reg_def.ty.size) + else: + assert(isinstance(reg_def.ty, type_info.PointerType)) # TODO: PointerType handlind looks like cancer!!!! + pass + # if isinstance(reg_def.ty.ty, type_info.ArrayType): + # reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.element_type.size) + # else: + # reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.size) + + for attr_name, attr_ops in reg_def.attributes.items(): + ops = [] + for attr_op in attr_ops: + try: + + behav_builder = BehaviorModelBuilder(core_def.parameters, {}, {},core_def.register_banks, + core_def.register_aliases, {}, core_def.functions, warned_fns) + op = behav_builder.visit(attr_op) + ops.append(op) + except M2Error as e: + logger.critical("error processing attribute \"%s\" of memory \"%s\": %s", attr_name, fn_def.name, e) + sys.exit(1) + + reg_def.attributes[attr_name] = ops + for fn_def in core_def.functions.values(): if isinstance(fn_def.operation, behav.Operation) and not fn_def.extern: raise M2SyntaxError(f"non-extern function {fn_def.name} has no body") - fn_def._size = fn_def.size + fn_def.ty.size = arch.get_const_or_val(fn_def.ty.size) + fn_def._size = fn_def.ty.size for fn_arg in fn_def.args.values(): - fn_arg._size = fn_arg.size + fn_arg._size = fn_arg.ty.size fn_arg._width = fn_arg.width logger.debug("generating function behavior") @@ -154,8 +193,8 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - fn_def.args, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, fn_def.args, core_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: @@ -164,8 +203,8 @@ def main(): fn_def.attributes[attr_name] = ops - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - fn_def.args, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, fn_def.args, core_def.functions, warned_fns) if not isinstance(fn_def.operation, behav.Operation): try: @@ -194,8 +233,8 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - {}, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, {}, core_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: @@ -204,8 +243,8 @@ def main(): block_def.attributes[attr_name] = ops - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - {}, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, {}, core_def.functions, warned_fns) try: op = behav_builder.visit(block_def.operation) @@ -229,8 +268,8 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - instr_def.fields, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, instr_def.fields, core_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: @@ -238,20 +277,20 @@ def main(): sys.exit(1) instr_def.attributes[attr_name] = ops - if arch.InstrAttribute.ENABLE in instr_def.attributes: - enable_attr = instr_def.attributes[arch.InstrAttribute.ENABLE] + if attribute_info.InstrAttribute.ENABLE in instr_def.attributes: + enable_attr = instr_def.attributes[attribute_info.InstrAttribute.ENABLE] assert isinstance(enable_attr, list) assert len(enable_attr) == 1 enable_attr = enable_attr[0] - enable = try_eval_bool(enable_attr, core_def.constants, core_def.memories, core_def.memory_aliases, instr_def.fields, core_def.functions, warned_fns) + enable = try_eval_bool(enable_attr, core_def.parameters, core_def.memories, core_def.memory_aliases, instr_def.fields, core_def.functions, warned_fns) if enable is not None: assert isinstance(enable, bool) - instr_def.attributes.pop(arch.InstrAttribute.ENABLE) + instr_def.attributes.pop(attribute_info.InstrAttribute.ENABLE) if not enable: continue - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - instr_def.fields, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.parameters, core_def.memories, core_def.memory_aliases, + core_def.register_banks, core_def.register_aliases, instr_def.fields, core_def.functions, warned_fns) try: op = behav_builder.visit(instr_def.operation) @@ -271,7 +310,7 @@ def main(): behav.BinaryOperation( behav.NamedReference(core_def.pc_memory), behav.Operator("+"), - behav.IntLiteral(int(instr_def.size/8)) + behav.Literal(int(instr_def.size/8), type_info.TypeKind.UINT) ) ) diff --git a/m2isar/frontends/coredsl2/utils.py b/m2isar/frontends/coredsl2/utils.py index 30f8a4b..d1966a0 100644 --- a/m2isar/frontends/coredsl2/utils.py +++ b/m2isar/frontends/coredsl2/utils.py @@ -9,6 +9,8 @@ import antlr4 import antlr4.error.ErrorListener +from m2isar.metamodel import arch + from ... import M2SyntaxError from .parser_gen import CoreDSL2Lexer, CoreDSL2Parser diff --git a/m2isar/metamodel/__init__.py b/m2isar/metamodel/__init__.py index 9752298..cc40100 100644 --- a/m2isar/metamodel/__init__.py +++ b/m2isar/metamodel/__init__.py @@ -37,6 +37,8 @@ from pathlib import Path from dataclasses import dataclass +from m2isar.metamodel import type_info + from . import arch, behav, code_info M2_METAMODEL_VERSION = 3 @@ -71,7 +73,7 @@ def patch_model(module): param.annotation.generate = fn intrinsic_defs = [ - arch.Intrinsic("__encoding_size", 16, arch.DataType.U) + arch.Intrinsic("__encoding_size", 16, type_info.TypeKind.UINT), ] intrinsics = {x.name: x for x in intrinsic_defs} diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 36da83a..c723a96 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -14,23 +14,23 @@ import dataclasses import itertools from collections import defaultdict -from enum import Enum, IntEnum, auto -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Union from m2isar.frontends.coredsl2.expr_interpreter import ExprInterpreterVisitor +from m2isar.metamodel import type_info, attribute_info from .. import M2TypeError -from .behav import BaseNode, Operation, NumberLiteral +from .behav import BaseNode, IndexedReference, Operation, Literal if TYPE_CHECKING: from .code_info import FunctionInfo exprInterpretVisitor = ExprInterpreterVisitor() def get_const_or_val(arg) -> int: - if isinstance(arg, Constant): + if isinstance(arg, Parameter): return arg.value - if isinstance(arg, NumberLiteral): - arg = arg.value + if isinstance(arg, Literal): + arg = int(arg.value) if isinstance(arg, BaseNode): arg = exprInterpretVisitor.generate(arg, None) @@ -52,15 +52,15 @@ def __str__(self) -> str: def __repr__(self) -> str: return f'<{type(self).__name__} object>: name={self.name}' -ValOrConst = Union[int, "Constant"] +ValOrConst = Union[int, "Parameter"] class SizedRefOrConst(Named): """A simple base class for an object with a name and a size. - Size can be either an int, a Constant or a statically resolvable + Size can be either an int, a Parameter or a statically resolvable expression, expressed by a BaseNode. """ - _size: Union[int, "Constant", "BaseNode"] + _size: Union[int, "Parameter", "BaseNode"] """The size of the object""" def __init__(self, name, size: ValOrConst): @@ -76,36 +76,24 @@ def size(self) -> int: return None return int(ret) - @property - def actual_size(self): - """Returns the bits needed in multiples of eight to represent the - resolved size of the object. - """ - - if self.size is None: - return None - - temp = 1 << (self.size - 1).bit_length() - return temp if temp >= 8 else 8 - def __str__(self) -> str: - return f'{super().__str__()}, size={self.size}, actual_size={self.actual_size}' + return f'{super().__str__()}, size={self.size}' -class Constant(SizedRefOrConst): - """An object holding a constant value. Should have a value at some point, also holds attributes - and signedness information. +class Parameter(SizedRefOrConst): + """An object holding a ("Parameter") Parameter value. + Should have a value at some point, also holds attributes and signedness information. """ - _value: Union[int, "Constant", "BaseNode"] + _value: Union[int, "Parameter", "BaseNode"] """The value this object holds. Can be an int, another constant or a statically resolvable BaseNode.""" - attributes: "dict[ConstAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.ConstAttribute, list[BaseNode]]" """A dictionary of attributes, mapping attribute type to a list of attribute arguments.""" signed: bool """The signedness of this constant.""" - def __init__(self, name, value: Union[int, "Constant", "BaseNode"], attributes: "dict[ConstAttribute, list[BaseNode]]", size=None, signed=False): + def __init__(self, name, value: Union[int, "Parameter", "BaseNode"], attributes: "dict[attribute_info.ConstAttribute, list[BaseNode]]", size=None, signed=False): self._value = value self.attributes = attributes if attributes else {} self.signed = signed @@ -129,13 +117,13 @@ def __repr__(self) -> str: class RangeSpec: """A class holding a range to denote a range of indices or width of a memory bank.""" - _upper_base: Union[int, "Constant", "BaseNode"] + _upper_base: Union[int, "Parameter", "BaseNode"] """The upper bound of the range. Can be an int, a constant or a statically resolvable BaseNode.""" - _lower_base: Union[int, "Constant", "BaseNode"] + _lower_base: Union[int, "Parameter", "BaseNode"] """The lower bound of the range. Can be an int, a constant or a statically resolvable BaseNode.""" - _upper_power: Union[int, "Constant", "BaseNode"] + _upper_power: Union[int, "Parameter", "BaseNode"] """Obsolete, do not use""" - _lower_power: Union[int, "Constant", "BaseNode"] + _lower_power: Union[int, "Parameter", "BaseNode"] """Obsolete, do not use""" def __init__(self, upper_base: ValOrConst, lower_base: ValOrConst=None, upper_power: ValOrConst=1, lower_power: ValOrConst=1): @@ -201,231 +189,262 @@ def length(self): def __str__(self) -> str: return f', len {self.length}: {self.upper_base}:{self.lower_base}' -class MemoryAttribute(Enum): - IS_PC = auto() - IS_MAIN_MEM = auto() - IS_MAIN_REG = auto() - IS_FLOAT_REG = auto() - IS_VECTOR_REG = auto() - IS_CSR_REG = auto() - DELETE = auto() - ETISS_CAN_FAIL = auto() - ETISS_IS_GLOBAL_IRQ_EN = auto() - ETISS_IS_IRQ_EN = auto() - ETISS_IS_IRQ_PENDING = auto() - ETISS_IS_PROCNO = auto() - -class ConstAttribute(Enum): - IS_REG_WIDTH = auto() - IS_ADDR_WIDTH = auto() - -class InstrAttribute(Enum): - NO_CONT = auto() - COND = auto() - FLUSH = auto() - SIM_EXIT = auto() - ENABLE = auto() - ETISS_ERROR_INSTRUCTION = auto() - -class FunctionAttribute(Enum): - ETISS_STATICFN = auto() - ETISS_NEEDS_ARCH = auto() - ETISS_TRAP_ENTRY_FN = auto() - ETISS_TRAP_TRANSLATE_FN = auto() - -class FunctionThrows(IntEnum): - NO = 0 - YES = 1 - MAYBE = 2 - -class DataType(Enum): - NONE = auto() - U = auto() - S = auto() - F = auto() - D = auto() - Q = auto() - B = auto() - -class DataType2: - """A datatype base class, only holds information on whether it is a pointer.""" - - ptr: Any - - def __init__(self, ptr) -> None: - self.ptr = ptr - -class VoidType(DataType2): - """A void datatype, automatically assumes native size.""" - -class IntegerType(DataType2): - """An integer datatype with width and sign information.""" - - _width: Union[int, "Constant", "BaseNode"] - signed: bool - def __init__(self, width: Union[int, "Constant", "BaseNode"], signed: bool, ptr): - self._width = width - self.signed = signed +class FnParam(Named): + """A function parameter.""" + + ty: type_info.PrimitiveType + _width: Union[int, "Parameter", "BaseNode"] + """The array width of this parameter.""" - super().__init__(ptr) + def __init__(self, name, size, kind: type_info.TypeKind, width=1): + self.ty = type_info.PrimitiveType(kind, size) + self._width = width + super().__init__(name) @property def width(self): - """Returns the resolved width value.""" + """Returns the resolved array width value.""" return get_const_or_val(self._width) def __str__(self) -> str: - return f'{super().__repr__()}, width={self.width}, signed={self.signed}' + return f'{super().__str__()}, type={self.ty}' + +# =========================================================== +# START +# ARCHITECTURE used within the functional +# behavior of instructions and functions. +# =========================================================== +class Symbol(Named): + """A simple base class for a symbol, which is a named object that + can be used as an operand in an instruction or function.""" + def __init__( + self, + name: str, + ty: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType, type_info.BitFieldType, type_info.PointerType], + attributes : dict = {} + ): + self.ty = ty + self.attributes = attributes + super().__init__(name) + + +class Variable(Symbol): + """A variable is only defined in the functional behavior of a function, but not in the architectural part. + Archtiectural Parts are depicted as Register Banks or Memories, but not as variables. + However, we need to define variables for intermediate results, ...""" + def __init__( + self, + name: str, + ty: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType], + static: attribute_info.StaticAttribute = attribute_info.StaticAttribute.RW, + value=None, # Compile Time information + ): + assert isinstance(ty, (type_info.PrimitiveType, type_info.ArrayType)) + + if isinstance(ty, type_info.PrimitiveType): + assert ty.kind.is_scalar + elif isinstance(ty, type_info.ArrayType): + # only allow 1D arrays for now + assert ty.element_type.kind.is_scalar + + # optional: only for parameters/literals + self.value = value + + super().__init__(name, ty, attributes={"static": static}) + +class Intrinsic(Symbol): - def __repr__(self): - return self.__str__() + value: int + def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind, value: int = None): + self.value = value + super().__init__(name, type_info.PrimitiveType(kind, get_const_or_val(size))) - @property - def actual_width(self): - """Returns the resolved width value rounded to the nearest multiple of 8.""" - if self._width is None: - return None +class RegisterBank(Symbol): + """A class representing a register bank. A register bank combines structured registers, + which is used to represent registers in the architectural part of an M2-ISA-R model.""" + children: "list[Memory]" + _initval: "dict[int, Union[int, Parameter, BaseNode]]" - temp = 1 << (self.width - 1).bit_length() - return temp if temp >= 8 else 8 + def __init__(self, name, nr_ele: Union[int, Parameter], kind: type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): + self.children = [] + self._initval = {} + ty = type_info.ArrayType(type_info.PrimitiveType(kind, size), nr_ele) -class FnParam(SizedRefOrConst): - """A function parameter.""" + super().__init__(name, ty, attributes) - data_type: DataType - _width: Union[int, "Constant", "BaseNode"] - """The array width of this parameter.""" + # TODO: Implement this + def initval(self, idx=None): + """Return the initial value for the given index.""" + return get_const_or_val(self._initval[idx]) - def __init__(self, name, size, data_type: DataType, width=1): - self.data_type = data_type - self._width = width - super().__init__(name, size) + @property + def is_pc(self): + """Return true if this memory is tagged as being the program counter.""" + return attribute_info.RegisterAttribute.IS_PC in self.attributes - @property - def width(self): - """Returns the resolved array width value.""" + @property + def is_gpr(self): + """Return true if this memory is tagged as being a general-purpose register.""" + return attribute_info.RegisterAttribute.IS_GPR_REG in self.attributes - return get_const_or_val(self._width) - def __str__(self) -> str: - return f'{super().__str__()}, data_type={self.data_type}' +# be careful: This is only for single defined regs (No Alias or indexedReference) +class Register(Symbol): + """A class representing a register. A register is a single defined Symbols. Dont mix it up + bit alias that are IndexedReference of already declared Symbols. + This class should simplify different handling to register bank. + And is used to represent registers in the architectural part of an M2-ISA-R model.""" + children: "list[Memory]" + _initval: "dict[Union[int, Parameter, BaseNode]]" -class Scalar(SizedRefOrConst): - """A scalar variable object, used mainly in behavior descriptions.""" + def __init__(self, name, kind: type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): + self.children = [] + self._initval = {} + ty = type_info.PrimitiveType(kind, size) - value: int - static: bool - data_type: DataType + super().__init__(name, ty, attributes) - def __init__(self, name, value: int, static: bool, size, data_type: DataType): - self.value = value - self.static = static - self.data_type = data_type - super().__init__(name, size) + # TODO: Implement this + def initval(self): + """Return the initial value for the given index.""" -class Intrinsic(SizedRefOrConst): + return get_const_or_val(self._initval) - value: int - data_type: DataType + @property + def is_pc(self): + """Return true if this memory is tagged as being the program counter.""" + return attribute_info.RegisterAttribute.IS_PC in self.attributes - def __init__(self, name, size: ValOrConst, data_type: DataType, value: int = None): - self.data_type = data_type - self.value = value - super().__init__(name, size) + @property + def is_gpr(self): + """Return true if this memory is tagged as being a general-purpose register.""" + return attribute_info.RegisterAttribute.IS_GPR_REG in self.attributes -class Memory(SizedRefOrConst): +#Idea extern [const volatile]<- atleast store it +#class Port -> raise ... + + +class Memory(Symbol): """A generic memory object. Can have children, which alias to specific indices of their parent memory. Has a variable array size, can therefore represent both scalar and array registers and/or memories. """ - attributes: "dict[MemoryAttribute, list[BaseNode]]" range: RangeSpec children: "list[Memory]" - parent: Union['Memory', None] - _initval: "dict[int, Union[int, Constant, BaseNode]]" + parent: "Union['Memory', None]" + _initval: "dict[int, Union[int, Parameter, BaseNode]]" - def __init__(self, name, range_: RangeSpec, size, attributes: "dict[MemoryAttribute, list[BaseNode]]"): - self.attributes = attributes if attributes else {} - self.range = range_ + def __init__(self, name, kind : type_info.TypeKind, size, length, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.children = [] - self.parent = None self._initval = {} - super().__init__(name, size) - - def initval(self, idx=None): - """Return the initial value for the given index.""" + self.parent = None # Just Legacy + assert kind.is_numeric + super().__init__(name, type_info.ArrayType(type_info.PrimitiveType(kind, size), length), attributes) - return get_const_or_val(self._initval[idx]) @property - def data_range(self): - """Returns a RangeSpec object with upper=range.upper-range.lower, lower=0.""" + def is_main_mem(self): + """Return true if this memory is tagged as being the main memory array.""" + return attribute_info.MemoryAttribute.IS_MAIN_MEM in self.attributes - if self.range.upper is None or self.range.lower is None: - return None - return RangeSpec(self.range.upper - self.range.lower, 0) +# TODO: decide later if you wanna keep lhs information : +# unsigned& S0 = X[8]; vs +# Intention: alias S0 <- X[8]; +class Alias(Symbol): + """A class representing an (potentially ranged) alias to a Register/Memory/RegisterBank entity, + which refer to the architectural part of an M2-ISA-R model. This access might be ranged""" + parent: Union[Memory, RegisterBank] + _initval = 0 - @property - def is_pc(self): - """Return true if this memory is tagged as being the program counter.""" - return MemoryAttribute.IS_PC in self.attributes + def __init__(self, name, parent: Union[Memory, RegisterBank], range: RangeSpec, type: type_info.PointerType, attributes: dict = {}): + self.parent = parent + self.range = range + self.ty = type + assert isinstance(parent.ty, (type_info.ArrayType, type_info.PrimitiveType)) + super().__init__(name, type_info.PointerType(parent.ty), attributes) - @property - def is_main_mem(self): - """Return true if this memory is tagged as being the main memory array.""" - return MemoryAttribute.IS_MAIN_MEM in self.attributes -@dataclasses.dataclass -class BitVal: - """A class representing a fixed bit sequence in an instruction encoding. - Modeled as length and integral value. - """ + @property + def data_range(self): + """Returns a RangeSpec object with upper=range.upper-range.lower, lower=0.""" + + if self.range.upper is None or self.range.lower is None: + return None + + return RangeSpec(self.range.upper - self.range.lower, 0) + + @property + def length(self): + """Returns the length of the range using following algorithm: + if self.upper is None: return None + elif self.lower is None: return self.upper + else return self.upper - self.lower + 1 + """ + + if self.range.upper is None: + return None + + if self.range.lower is None: + return self.range.upper + + return self.range.upper - self.range.lower + 1 + +# ============================================================ +# END +# ARCHITECTURE used within the functional +# behavior of instructions and functions. +# =========================================================== - length: int - value: int -class BitField(Named): +class BitField(Symbol): """A class representing an operand in an instruction encoding. Can be split into multiple parts, if the operand is split over two or more bit ranges. """ range: RangeSpec - data_type: DataType + kind: type_info.TypeKind - def __init__(self, name, _range: RangeSpec, data_type: DataType): + def __init__(self, name, _range: RangeSpec, kind: type_info.TypeKind): self.range = _range - self.data_type = data_type - if not self.data_type: - self.data_type = DataType.U + if not kind: + self.ty = type_info.BitFieldType(type_info.TypeKind.UINT) - super().__init__(name) + super().__init__(name, type_info.BitFieldType(kind), attributes={}) def __str__(self) -> str: - return f'{super().__repr__()}, range={self.range}, data_type={self.data_type}' + return f'{super().__repr__()}, range={self.range}, data_type={self.kind}' def __repr__(self): return self.__str__() -class BitFieldDescr(SizedRefOrConst): +@dataclasses.dataclass +class BitVal: + """A class representing a fixed bit sequence in an instruction encoding. + Modeled as length and integral value. + """ + + length: int + value: int + +class BitFieldDescr(Named): """A class representing a full instruction operand. Has no information about the actual bits it is composed of, for that use BitField. """ + def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind): + self.ty = type_info.PrimitiveType(kind, get_const_or_val(size)) - def __init__(self, name, size: ValOrConst, data_type: DataType): - self.data_type = data_type - - super().__init__(name, size) + super().__init__(name) class Instruction(SizedRefOrConst): """A class representing an instruction.""" - attributes: "dict[InstrAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.InstrAttribute, list[BaseNode]]" encoding: "list[Union[BitField, BitVal]]" mnemonic: str assembly: str @@ -433,13 +452,13 @@ class Instruction(SizedRefOrConst): ext_name: str fields: "dict[str, BitFieldDescr]" - scalars: "dict[str, Scalar]" + scalars: "dict[str, Symbol]" throws: bool mask: int code: int - def __init__(self, name, attributes: "dict[InstrAttribute, list[BaseNode]]", encoding: "list[Union[BitField, BitVal]]", + def __init__(self, name, attributes: "dict[attribute_info.InstrAttribute, list[BaseNode]]", encoding: "list[Union[BitField, BitVal]]", mnemonic: str, assembly: str, operation: Operation, function_info: "FunctionInfo"): self.ext_name = "" @@ -464,12 +483,12 @@ def __init__(self, name, attributes: "dict[InstrAttribute, list[BaseNode]]", enc if e.name in self.fields: f = self.fields[e.name] - if f.data_type != e.data_type: + if f.ty.kind != e.ty.kind: raise M2TypeError(f'non-matching datatypes for BitField {e.name} in instruction {name}') - if e.range.upper + 1 > f._size: - f._size = e.range.upper + 1 + if e.range.upper + 1 > f.ty.size: + f.ty.size = e.range.upper + 1 else: - f = BitFieldDescr(e.name, e.range.upper + 1, e.data_type) + f = BitFieldDescr(e.name, e.range.upper + 1, e.ty.kind) self.fields[e.name] = f else: self.mask |= (2**e.length - 1) << self._size @@ -481,26 +500,26 @@ def __str__(self) -> str: code_and_mask = f'code={self.code:#0{self.size+2}x}, mask={self.mask:#0{self.size+2}x}' return f'{super().__str__()}, ext_name={self.ext_name}, {code_and_mask}' -class Function(SizedRefOrConst): +class Function(Named): """A class representing a function.""" - attributes: "dict[FunctionAttribute, list[BaseNode]]" - data_type: DataType + attributes: "dict[attribute_info.FunctionAttribute, list[BaseNode]]" + ty: type_info.FunctionType args: "list[FnParam]" operation: "Operation" extern: bool ext_name: str - scalars: "dict[str, Scalar]" + scalars: "dict[str, Symbol]" throws: bool - static: bool + static: attribute_info.StaticAttribute - def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", return_len, data_type: DataType, args: "list[FnParam]", + def __init__(self, name, attributes: "dict[attribute_info.FunctionAttribute, list[BaseNode]]", return_len, kind: type_info.TypeKind, args: "list[FnParam]", operation: "Operation", extern: bool=False, function_info: "FunctionInfo"=None): self.ext_name = "" - self.data_type = data_type self.attributes = attributes if attributes else {} + self.ty = type_info.FunctionType(return_len, kind) self.scalars = {} self.throws = False if args is None: @@ -519,13 +538,13 @@ def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", self.args[arg_name] = arg self.operation = operation if operation is not None else Operation([]) - self.static = False + self.static = attribute_info.StaticAttribute.NONE self.extern = extern - super().__init__(name, return_len) + super().__init__(name) def __str__(self) -> str: - return f'{super().__str__()}, data_type={self.data_type}' + return f'{super().__str__()}, type={self.ty}' def extract_memory_alias(memories: "list[Memory]"): """Extract and separate parent and children memories from the given list @@ -547,8 +566,23 @@ def extract_memory_alias(memories: "list[Memory]"): return parents, aliases + +def extract_register_alias(register_banks: "list[RegisterBank]"): + """Extract and separate parent and children register banks from the given list + of register bank objects.""" + + parents = {} + aliases = {} + for m in register_banks: + for c in m.children: + aliases[c.name] = c + parents[c.name] = m + + return parents, aliases + + class AlwaysBlock(Named): - attributes: "dict[FunctionAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.FunctionAttribute, list[BaseNode]]" operation: "Operation" def __init__(self, name: str, attributes, operation): @@ -558,16 +592,17 @@ def __init__(self, name: str, attributes, operation): super().__init__(name) class InstructionSet(Named): - """A class representing an InstructionSet collection. Bundles constants, memories, functions + """A class representing an InstructionSet collection. Bundles parameters, memories, functions and instructions under a common name. """ - def __init__(self, name, extension: "list[str]", constants: "dict[str, Constant]", memories: "dict[str, Memory]", - functions: "dict[str, Function]", instructions: "dict[tuple[int, int], Instruction]"): + def __init__(self, name, extension: "list[str]", parameters: "dict[str, Parameter]", memories: "dict[str, Memory]", + register_banks: "dict[str, RegisterBank]", functions: "dict[str, Function]", instructions: "dict[tuple[int, int], Instruction]"): self.extension = extension - self.constants = constants + self.parameters = parameters self.memories, self.memory_aliases = extract_memory_alias(memories.values()) + self.register_banks, self.register_aliases = extract_register_alias(register_banks.values()) self.functions = functions self.instructions = instructions @@ -576,15 +611,18 @@ def __init__(self, name, extension: "list[str]", constants: "dict[str, Constant] class CoreDef(Named): """A class representing an entire CPU core. Contains the collected attributes of multiple InstructionSets.""" - def __init__(self, name, contributing_types: "list[str]", template: str, constants: "dict[str, Constant]", memories: "dict[str, Memory]", - memory_aliases: "dict[str, Memory]", functions: "dict[str, Function]", instructions: "dict[tuple[int, int], Instruction] | list[Instruction]", - instr_classes: "set[int]", intrinsics: "dict[str, Intrinsic]"): + def __init__(self, name, contributing_types: "list[str]", template: str, parameters: "dict[str, Parameter]", memories: "dict[str, Memory]", + memory_aliases: "dict[str, Alias]", register_banks: "dict[str, Union[RegisterBank, Register]]", register_aliases: "dict[str, Alias]", + functions: "dict[str, Function]", instructions: "dict[tuple[int, int], Instruction] | list[Instruction]", instr_classes: "set[int]", + intrinsics: "dict[str, Intrinsic]"): self.contributing_types = contributing_types self.template = template - self.constants = constants + self.parameters = parameters self.memories = memories self.memory_aliases = memory_aliases + self.register_banks = register_banks + self.register_aliases = register_aliases self.functions = functions self.instructions = instructions self.instr_classes = instr_classes @@ -609,26 +647,30 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.functions_by_ext[fn_def.ext_name][fn_name] = fn_def for mem in itertools.chain(self.memories.values(), self.memory_aliases.values()): - if MemoryAttribute.IS_MAIN_REG in mem.attributes: - self.main_reg_file = mem - if MemoryAttribute.IS_FLOAT_REG in mem.attributes: - self.float_reg_file = mem - if MemoryAttribute.IS_VECTOR_REG in mem.attributes: - self.vector_reg_file = mem - if MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name.upper() == "CSR": - self.csr_reg_file = mem - elif MemoryAttribute.IS_PC in mem.attributes: - self.pc_memory = mem - elif MemoryAttribute.IS_MAIN_MEM in mem.attributes: + if attribute_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: self.main_memory = mem - elif MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN in mem.attributes: self.global_irq_en_memory = mem - elif MemoryAttribute.ETISS_IS_PROCNO in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_PROCNO in mem.attributes: self.procno_memory = mem - elif MemoryAttribute.ETISS_IS_IRQ_EN in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_IRQ_EN in mem.attributes: self.irq_en_memory = mem - elif MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: self.irq_pending_memory = mem + elif attribute_info.MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name.upper() == "CSR": + self.csr_reg_file = mem + + + for regs in itertools.chain(self.register_banks.values(), self.register_aliases.values()): + if attribute_info.RegisterAttribute.IS_MAIN_REG in regs.attributes: + self.main_reg_file = regs + elif attribute_info.RegisterAttribute.IS_FLOAT_REG in regs.attributes: + self.float_reg_file = regs + elif attribute_info.RegisterAttribute.IS_VECTOR_REG in regs.attributes: + self.vector_reg_file = regs + elif attribute_info.RegisterAttribute.IS_PC in regs.attributes: + self.pc_memory = regs + super().__init__(name) @@ -650,4 +692,4 @@ def instructions_by_class(self): self._instructions_by_class = defaultdict(dict) for (code, mask), instr_def in self.instructions.items(): self._instructions_by_class[instr_def.size][(code, mask)] = instr_def - return self._instructions_by_class + return self._instructions_by_class \ No newline at end of file diff --git a/m2isar/metamodel/attribute_info.py b/m2isar/metamodel/attribute_info.py new file mode 100644 index 0000000..c9d26e4 --- /dev/null +++ b/m2isar/metamodel/attribute_info.py @@ -0,0 +1,57 @@ + +from enum import Enum, IntEnum, auto, IntFlag +from dataclasses import dataclass + +class MemoryAttribute(Enum): + IS_MAIN_MEM = auto() + IS_CSR_REG = auto() + DELETE = auto() + ETISS_CAN_FAIL = auto() + ETISS_IS_GLOBAL_IRQ_EN = auto() + ETISS_IS_IRQ_EN = auto() + ETISS_IS_IRQ_PENDING = auto() + ETISS_IS_PROCNO = auto() + +class RegisterAttribute(Enum): + IS_PC = auto() + IS_MAIN_REG = auto() + IS_FLOAT_REG = auto() + IS_VECTOR_REG = auto() + +class FunctionAttribute(Enum): + ETISS_STATICFN = auto() + ETISS_NEEDS_ARCH = auto() + ETISS_TRAP_ENTRY_FN = auto() + ETISS_TRAP_TRANSLATE_FN = auto() + +class FunctionThrows(IntEnum): + NO = 0 + YES = 1 + MAYBE = 2 + +class ConstAttribute(Enum): + IS_REG_WIDTH = auto() + IS_ADDR_WIDTH = auto() + +class InstrAttribute(Enum): + NO_CONT = auto() + COND = auto() + FLUSH = auto() + SIM_EXIT = auto() + ENABLE = auto() + ETISS_ERROR_INSTRUCTION = auto() + + +class StaticAttribute(IntFlag): + """Describes the staticness of a Scalar or Function""" + + NONE = 0 #no access at all + READ = auto() # read access only + WRITE = auto() # write access only + RW = READ | WRITE + +@dataclass +class ScalarStaticnessContext: + """A datakeeping class for the scalar staticness transformations.""" + + context_is_static: StaticAttribute = StaticAttribute.RW \ No newline at end of file diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index 85889b8..faf7e7a 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -20,11 +20,13 @@ on which translation module is loaded using :func:`patch_model`. """ -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Union, Optional +from .type_info import PrimitiveType, TypeKind + if TYPE_CHECKING: - from .arch import (BitFieldDescr, Constant, FnParam, Function, Intrinsic, - Memory, Scalar) + from .arch import (BitFieldDescr, Parameter, FnParam, Function, Intrinsic, + Memory, Variable, RegisterBank) from .code_info import LineInfo # pylint: disable=abstract-method @@ -36,7 +38,7 @@ class BaseNode: def __init__(self, line_info: "LineInfo"=None) -> None: self.line_info = line_info - self.inferred_type = None + self.ty = None def generate(self, context): raise NotImplementedError() @@ -91,62 +93,32 @@ def __init__(self, left: BaseNode, right: BaseNode, line_info=None) -> None: self.left = left self.right = right -class NumberLiteral(BaseNode): - """A class holding a generic number literal.""" - def __init__(self, value, line_info=None): +class Literal(BaseNode): + def __init__(self, value:int, kind: TypeKind = TypeKind.INT, size=None, base: Optional[int]=10, line_info=None): super().__init__(line_info) - self._value = value - - def __repr__(self): - return f"NumberLiteral({self.value})" - - @property - def value(self) -> int: - """Returns the resolved value.""" - if isinstance(self, IntLiteral): - return int(self._value) - return self._value - - -class IntLiteral(NumberLiteral): - """A more precise class holding only integer literals.""" - - def __init__(self, value: int, bit_size: int=None, signed: bool=None, line_info=None): - super().__init__(value, line_info) - - if bit_size is None: - self.bit_size = value.bit_length() - else: - self.bit_size = bit_size - - if isinstance(self.bit_size, IntLiteral): - self.bit_size = self.bit_size.value - self.bit_size = max(1, self.bit_size) - assert self.bit_size is not None + assert kind.is_literal + self._value: Union[int, str] = value + self.ty = PrimitiveType(kind, size) # assigned during type checking - if signed is None: - self.signed = value <= 0 - else: - self.signed = signed + #Optional type information (not always given) + self.base: Optional[int] = base # 2, 10, 16 def __repr__(self): - return f"IntLiteral({self.value}, {self.bit_size}, {self.signed})" + return f"Literal(value={self.value}, type={self.ty}, base={self.base})" def __int__(self): - return self.value - - -class StringLiteral(BaseNode): - """A string constant""" - - def __init__(self, value: str): - super().__init__() - self.value = value + if isinstance(self.value, int): + return int(self.value, self.base) + else: + raise ValueError(f"Cannot convert {self.value} to int") - def __repr__(self): - return f"StringLiteral(\"{self.value}\")" + # compile time constant + @property + def value(self) -> int: + """Returns the resolved value.""" + return int(self._value) class Assignment(BaseNode): @@ -195,7 +167,7 @@ class ScalarDefinition(BaseNode): declaring it, use the scalar definition as LHS of an assignment statement. """ - def __init__(self, scalar: "Scalar", line_info=None): + def __init__(self, scalar: "Variable", line_info=None): super().__init__(line_info) self.scalar = scalar @@ -218,17 +190,17 @@ def __init__(self, op: Operator, right: BaseNode, line_info=None): self.right = right class NamedReference(BaseNode): - """A named reference to a :class:`arch.Memory`, BitFieldDescr, Scalar, Constant or FnParam.""" + """A named reference to a :class:`arch.Memory`, BitFieldDescr, Variable, Parameter or FnParam.""" - def __init__(self, reference: Union["Memory", "BitFieldDescr", "Scalar", "Constant", "FnParam", "Intrinsic"], line_info=None): + def __init__(self, reference: Union["Memory", "BitFieldDescr", "Variable", "Parameter", "FnParam", "Intrinsic"], line_info=None): super().__init__(line_info) self.reference = reference class IndexedReference(BaseNode): - """An indexed reference to a :class:`..arch.Memory`. Can optionally specify a range of indices + """An indexed reference to a :class:`..arch.Memory/RegisterBank`. Can optionally specify a range of indices using the `right` parameter.""" - def __init__(self, reference: "Memory", index: BaseNode, right: BaseNode=None, line_info=None): + def __init__(self, reference: "Union[Memory, RegisterBank]", index: BaseNode, right: BaseNode=None, line_info=None): super().__init__(line_info) self.reference = reference self.index = index @@ -245,7 +217,7 @@ def __init__(self, data_type, size, expr: BaseNode, line_info=None): @property def size(self) -> int: """Returns the resolved size.""" - if isinstance(self._size, IntLiteral): + if isinstance(self._size, Literal): return int(self._size) return self._size diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py new file mode 100644 index 0000000..94342f4 --- /dev/null +++ b/m2isar/metamodel/type_info.py @@ -0,0 +1,92 @@ +# 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) 2026 +# Chair of Embedded Computing Systems +# Technical University of Vienna + +"""This module contains helper data_type classes for modeling Symbol and data types +for the architectural part of an M2-ISA-R model. The architectural part is +anything but the functional behavior of functions and instructions. +""" + +from dataclasses import dataclass +from enum import Enum, auto +from typing import Any, Union +import logging + +logger = logging.getLogger("type_info_logger") +class TypeKind(Enum): + NONE = auto() # NumberLiteral with no type, e.g. 0 or 1 + VOID = auto() + UINT = auto() + INT = auto() + CHAR = auto() + # BOOL = auto() Expressed as unsigned<1> + FLOAT = auto() + STR = auto() + + @property + def is_int(self): + return self in (TypeKind.INT, TypeKind.UINT) + + @property + def is_numeric(self): + return self in (TypeKind.INT, TypeKind.UINT, TypeKind.FLOAT) + + @property + def is_void(self): + return self == TypeKind.VOID + + @property + def is_scalar(self): + return self in (TypeKind.INT, TypeKind.UINT, TypeKind.CHAR, TypeKind.FLOAT) + + @property + def is_literal(self): + return self is not (TypeKind.VOID, TypeKind.NONE) + + +class PrimitiveType: + def __init__(self, kind: TypeKind, size: int): + self.kind = kind + self.size = size + def __str__(self): + return f"{self.kind.name}<{self.size}>" + +class BitFieldType(): + def __init__(self, kind: TypeKind): + self.kind = kind + def __str__(self): return f"BITFIELD<{self.kind.name}>" + +class FloatType: + def __init__(self, exponent: int, mantissa: int, size: int): + self.exponent = exponent + self.mantissa = mantissa + self.size = size + def __str__(self): return f"FLOAT" + +class ArrayType: + def __init__(self, element_type : Union[PrimitiveType, FloatType], length: int): + self.element_type : Union[PrimitiveType, FloatType] = element_type + self.length = length # allow shaped later or TYPE_ARRAY in element_type? + def __str__(self): + return f"ARRAY<{self.element_type}, {self.length}>" + +@dataclass +class PointerType: + ty: Union[PrimitiveType, FloatType, ArrayType] + def __str__(self): + return f"POINTER<{self.ty}>" + +class FunctionType(): + size : int + kind : TypeKind + + def __init__(self, size: int, kind: TypeKind): + self.size = size + self.kind = kind + + def __str__(self): + return f"FUNCTION<{self.kind.name} {self.size}>" \ No newline at end of file diff --git a/m2isar/metamodel/utils/ExprMutator.py b/m2isar/metamodel/utils/ExprMutator.py index 0f78707..8a0ee20 100644 --- a/m2isar/metamodel/utils/ExprMutator.py +++ b/m2isar/metamodel/utils/ExprMutator.py @@ -78,15 +78,7 @@ def visit_concat_operation(self, expr: behav.ConcatOperation, context): return expr @default_visit.register - def visit_number_literal(self, expr: behav.NumberLiteral, context): - return expr - - @default_visit.register - def visit_int_literal(self, expr: behav.IntLiteral, context): - return expr - - @default_visit.register - def visit_string_literal(self, expr: behav.StringLiteral, context): + def visit_literal(self, expr: behav.Literal, context): return expr @default_visit.register diff --git a/m2isar/metamodel/utils/ExprVisitor.py b/m2isar/metamodel/utils/ExprVisitor.py index 3c2071a..bfd16f9 100644 --- a/m2isar/metamodel/utils/ExprVisitor.py +++ b/m2isar/metamodel/utils/ExprVisitor.py @@ -67,15 +67,7 @@ def visit_concat_operation(self, expr: behav.ConcatOperation, context): self.generate(expr.right, context) @default_visit.register - def visit_number_literal(self, expr: behav.NumberLiteral, context): - pass - - @default_visit.register - def visit_int_literal(self, expr: behav.IntLiteral, context): - pass - - @default_visit.register - def visit_string_literal(self, expr: behav.StringLiteral, context): + def visit_number_literal(self, expr: behav.Literal, context): pass @default_visit.register diff --git a/m2isar/metamodel/utils/__init__.py b/m2isar/metamodel/utils/__init__.py index 2f042c9..e2e3ffc 100644 --- a/m2isar/metamodel/utils/__init__.py +++ b/m2isar/metamodel/utils/__init__.py @@ -7,21 +7,3 @@ # Technical University of Munich """This module contains utility functions for working with M2-ISA-R model hierarchies.""" - -from dataclasses import dataclass -from enum import IntFlag, auto - - -class StaticType(IntFlag): - """Describes the staticness of a Scalar or Function""" - - NONE = 0 - READ = auto() - WRITE = auto() - RW = READ | WRITE - -@dataclass -class ScalarStaticnessContext: - """A datakeeping class for the scalar staticness transformations.""" - - context_is_static: StaticType = StaticType.RW diff --git a/m2isar/metamodel/utils/expr_preprocessor.py b/m2isar/metamodel/utils/expr_preprocessor.py index c0541c9..8433050 100644 --- a/m2isar/metamodel/utils/expr_preprocessor.py +++ b/m2isar/metamodel/utils/expr_preprocessor.py @@ -14,8 +14,7 @@ from itertools import chain from ... import M2ValueError -from .. import arch -from . import ScalarStaticnessContext +from .. import arch, attribute_info from .expr_simplifier import ExprSimplifierVisitor from .function_staticness import FunctionStaticnessVisitor from .function_throws import FunctionThrowsVisitor @@ -48,22 +47,22 @@ def process_functions(core: arch.CoreDef): logger.debug("checking throws for fn %s", fn_name) throws = function_throws_visitor.generate(fn_def.operation, None) - fn_def.throws = throws or arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes + fn_def.throws = throws or attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes - context = ScalarStaticnessContext() + context = attribute_info.ScalarStaticnessContext() logger.debug("examining scalar staticness for fn %s", fn_name) scalar_staticness_visitor.generate(fn_def.operation, context) logger.debug("examining function staticness for fn %s", fn_name) - if arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes and arch.FunctionAttribute.ETISS_STATICFN in fn_def.attributes: + if attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes and attribute_info.FunctionAttribute.ETISS_STATICFN in fn_def.attributes: raise M2ValueError("etiss_needs_arch and etiss_staticfn not allowed together, in function %s", fn_name) - #if not fn_def.extern and (arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or arch.FunctionAttribute.ETISS_STATICFN in fn_def.attributes): + #if not fn_def.extern and (attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or attribute_info.FunctionAttribute.ETISS_STATICFN in fn_def.attributes): # raise M2ValueError("etiss_needs_arch and etiss_staticfn only allowed for extern functions, in function %s", fn_name) - if fn_def.extern or arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes: - if arch.FunctionAttribute.ETISS_STATICFN in fn_def.attributes: + if fn_def.extern or attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes: + if attribute_info.FunctionAttribute.ETISS_STATICFN in fn_def.attributes: fn_def.static = True else: @@ -85,6 +84,6 @@ def process_instructions(core: arch.CoreDef): throws = function_throws_visitor.generate(instr_def.operation, None) instr_def.throws = throws - context = ScalarStaticnessContext() + context = attribute_info.ScalarStaticnessContext() logger.debug("examining staticness for instr %s", instr_def.name) scalar_staticness_visitor.generate(instr_def.operation, context) diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index 155ffcf..aa18252 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -9,18 +9,18 @@ """A transformation module for simplifying M2-ISA-R behavior expressions. The following simplifications are done: -* Resolvable :class:`m2isar.metamodel.arch.Constant` s are replaced by - `m2isar.metamodel.arch.IntLiteral` s representing their value +* Resolvable :class:`m2isar.metamodel.arch.Parameter` s are replaced by + `m2isar.metamodel.arch.Literal` s representing their value * Fully resolvable arithmetic operations are carried out and their results - represented as a matching :class:`m2isar.metamodel.arch.IntLiteral` + represented as a matching :class:`m2isar.metamodel.arch.Literal` * Conditions and loops with fully resolvable conditions are either discarded entirely or transformed into code blocks without any conditions * Ternaries with fully resolvable conditions are transformed into only the matching part -* Type conversions of :class:`m2isar.metamodel.arch.IntLiteral` s apply the desired - type directly to the :class:`IntLiteral` and discard the type conversion +* Type conversions of :class:`m2isar.metamodel.arch.Literal` s apply the desired + type directly to the :class:`Literal` and discard the type conversion """ -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info from .ExprVisitor import ExprVisitor from functools import singledispatchmethod @@ -60,37 +60,45 @@ def _(self, expr: behav.BinaryOperation, context): expr.left = self.generate(expr.left, context) expr.right = self.generate(expr.right, context) - if isinstance(expr.left, behav.IntLiteral) and isinstance(expr.right, (behav.NamedReference, behav.IndexedReference)): - if expr.left.bit_size < expr.right.reference.size: - expr.left.bit_size = expr.right.reference.size + if isinstance(expr.left, behav.Literal) and isinstance(expr.right, (behav.NamedReference, behav.IndexedReference)): + if expr.left.ty.size < arch.get_const_or_val(expr.right.reference.ty.size): + expr.left.ty.size = arch.get_const_or_val(expr.right.reference.ty.size) - if isinstance(expr.right, behav.IntLiteral) and isinstance(expr.left, (behav.NamedReference, behav.IndexedReference)): - if expr.right.bit_size < expr.left.reference.size: - expr.right.bit_size = expr.left.reference.size + if isinstance(expr.right, behav.Literal) and isinstance(expr.left, (behav.NamedReference, behav.IndexedReference)): + if isinstance(expr.left.ty, type_info.ArrayType): + if expr.right.ty.size < arch.get_const_or_val(expr.left.reference.ty.element_type.size): + expr.right.ty.size = arch.get_const_or_val(expr.left.reference.ty.element_type.size) + else: + if expr.right.ty.size < arch.get_const_or_val(expr.left.ty.size): + expr.right.ty.size = arch.get_const_or_val(expr.left.ty.size) - if isinstance(expr.left, behav.IntLiteral) and isinstance(expr.right, behav.IntLiteral): + if isinstance(expr.left, behav.Literal) and isinstance(expr.right, behav.Literal): # pylint: disable=eval-used res: int = int(eval(f"{expr.left.value}{expr.op.value}{expr.right.value}")) - return behav.IntLiteral(res, max(expr.left.bit_size, expr.right.bit_size, res.bit_length())) + if res < 0 or expr.left.ty.kind == type_info.TypeKind.INT or expr.right.ty.kind == type_info.TypeKind.INT: + kind = type_info.TypeKind.INT + else: + kind = type_info.TypeKind.UINT + return behav.Literal(res, kind, max(arch.get_const_or_val(expr.left.ty.size), arch.get_const_or_val(expr.right.ty.size), res.bit_length())) if expr.op.value == "&&": - if isinstance(expr.left, behav.IntLiteral): + if isinstance(expr.left, behav.Literal): if expr.left.value: return expr.right return expr.left - if isinstance(expr.right, behav.IntLiteral): + if isinstance(expr.right, behav.Literal): if expr.right.value: return expr.left return expr.right if expr.op.value == "||": - if isinstance(expr.left, behav.IntLiteral): + if isinstance(expr.left, behav.Literal): if expr.left.value: return expr.left return expr.right - if isinstance(expr.right, behav.IntLiteral): + if isinstance(expr.right, behav.Literal): if expr.right.value: return expr.right return expr.left @@ -113,15 +121,7 @@ def _(self, expr: behav.ConcatOperation, context): return expr @generate.register - def _(self, expr: behav.NumberLiteral, context): - return expr - - @generate.register - def _(self, expr: behav.IntLiteral, context): - return expr - - @generate.register - def _(self, expr: behav.StringLiteral, context): + def _(self, expr: behav.Literal, context): return expr @generate.register @@ -137,9 +137,9 @@ def _(self, expr: behav.Assignment, context): expr.target = self.generate(expr.target, context) expr.expr = self.generate(expr.expr, context) - if isinstance(expr.expr, behav.IntLiteral) and isinstance(expr.target, (behav.NamedReference, behav.IndexedReference)): - if expr.expr.bit_size < expr.target.reference.size: - expr.expr.bit_size = expr.target.reference.size + if isinstance(expr.expr, behav.Literal) and isinstance(expr.target, (behav.NamedReference, behav.IndexedReference)): + if expr.expr.ty.size < arch.get_const_or_val(expr.target.ty.size): + expr.expr.ty.size = arch.get_const_or_val(expr.target.ty.size) return expr @@ -154,7 +154,7 @@ def _(self, expr: behav.Conditional, context): stmts = [] for cond, stmt in zip(expr.conds, expr.stmts): - if isinstance(cond, behav.IntLiteral): + if isinstance(cond, behav.Literal): if cond.value: return stmt else: @@ -163,7 +163,7 @@ def _(self, expr: behav.Conditional, context): eval_false = False if len(expr.conds) < len(expr.stmts): - if eval_false and isinstance(expr.conds[-1], behav.IntLiteral): + if eval_false and isinstance(expr.conds[-1], behav.Literal): if not cond.value: # pylint: disable=undefined-loop-variable return expr.stmts[-1] stmts.append(expr.stmts[-1]) @@ -186,7 +186,7 @@ def _(self, expr: behav.Ternary, context): expr.then_expr = self.generate(expr.then_expr, context) expr.else_expr = self.generate(expr.else_expr, context) - if isinstance(expr.cond, behav.IntLiteral): + if isinstance(expr.cond, behav.Literal): if expr.cond.value: return expr.then_expr @@ -204,17 +204,25 @@ def _(self, expr: behav.Return, context): @generate.register def _(self, expr: behav.UnaryOperation, context): expr.right = self.generate(expr.right, context) - if isinstance(expr.right, behav.IntLiteral): + if isinstance(expr.right, behav.Literal): # pylint: disable=eval-used res: int = eval(f"{expr.op.value}{expr.right.value}") - return behav.IntLiteral(res, max(expr.right.bit_size, res.bit_length())) + if res < 0: + kind = type_info.TypeKind.INT + else: + kind = expr.right.ty.kind + return behav.Literal(res, kind, max(expr.right.ty.size, res.bit_length())) return expr @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Constant): - return behav.IntLiteral(expr.reference.value, expr.reference.size, expr.reference.signed) + if isinstance(expr.reference, arch.Parameter): + if expr.reference.signed: + kind = type_info.TypeKind.INT + else: + kind = type_info.TypeKind.UINT + return behav.Literal(expr.reference.value, kind, arch.get_const_or_val(expr.reference.size)) return expr @@ -227,15 +235,15 @@ def _(self, expr: behav.IndexedReference, context): @generate.register def _(self, expr: behav.TypeConv, context): expr.expr = self.generate(expr.expr, context) - if isinstance(expr.expr, behav.IntLiteral): + if isinstance(expr.expr, behav.Literal): size = expr.size if size is None: - assert expr.inferred_type is not None - size = expr.inferred_type.width + assert expr.ty is not None + size = expr.ty.size assert size is not None expr.expr.bit_size = size assert expr.expr.bit_size is not None - expr.expr.signed = expr.data_type == arch.DataType.S + expr.expr.signed = expr.data_type == type_info.TypeKind.INT return expr.expr return expr @@ -256,7 +264,7 @@ def _(self, expr: behav.ProcedureCall, context): def _(self, expr: behav.Group, context): expr.expr = self.generate(expr.expr, context) - if isinstance(expr.expr, behav.IntLiteral): + if isinstance(expr.expr, behav.Literal): return expr.expr return expr diff --git a/m2isar/metamodel/utils/function_staticness.py b/m2isar/metamodel/utils/function_staticness.py index e8d25d9..6744946 100644 --- a/m2isar/metamodel/utils/function_staticness.py +++ b/m2isar/metamodel/utils/function_staticness.py @@ -62,15 +62,7 @@ def _(self, expr: behav.ConcatOperation, context): return all([left, right]) @generate.register - def _(self, expr: behav.NumberLiteral, context): - return True - - @generate.register - def _(self, expr: behav.IntLiteral, context): - return True - - @generate.register - def _(self, expr: behav.StringLiteral, context): + def _(self, expr: behav.Literal, context): return True @generate.register @@ -128,15 +120,15 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Scalar): - return expr.reference.static + if isinstance(expr.reference, arch.Variable): + return expr.reference.attributes.get("static") static_map = { arch.Memory: False, arch.BitFieldDescr: True, - arch.Constant: True, + arch.Parameter: True, arch.FnParam: True, - arch.Scalar: True, + arch.Variable: True, arch.Intrinsic: False } diff --git a/m2isar/metamodel/utils/function_throws.py b/m2isar/metamodel/utils/function_throws.py index 3489fbc..2f891ae 100644 --- a/m2isar/metamodel/utils/function_throws.py +++ b/m2isar/metamodel/utils/function_throws.py @@ -13,7 +13,7 @@ from operator import or_ from typing import Any -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info, attribute_info from .ExprVisitor import ExprVisitor # pylint: disable=unused-argument @@ -35,12 +35,12 @@ def _(self, expr: behav.Operation, context): else: statements.append(temp) - return reduce(or_, statements, arch.FunctionThrows.NO) + return reduce(or_, statements, attribute_info.FunctionThrows.NO) @generate.register def _(self, expr: behav.Block, context): stmts = [self.generate(x, context) for x in expr.statements] - return reduce(or_, stmts, arch.FunctionThrows.NO) + return reduce(or_, stmts, attribute_info.FunctionThrows.NO) @generate.register def _(self, expr: behav.BinaryOperation, context): @@ -65,24 +65,16 @@ def _(self, expr: behav.ConcatOperation, context): return reduce(or_, [left, right]) @generate.register - def _(self, expr: behav.NumberLiteral, context): - return arch.FunctionThrows.NO - - @generate.register - def _(self, expr: behav.IntLiteral, context): - return arch.FunctionThrows.NO - - @generate.register - def _(self, expr: behav.StringLiteral, context): - return arch.FunctionThrows.NO + def _(self, expr: behav.Literal, context): + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.ScalarDefinition, context): - return arch.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Break, context): - return arch.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Assignment, context): @@ -98,7 +90,7 @@ def _(self, expr: behav.Conditional, context): conds.extend(stmts) - return arch.FunctionThrows.MAYBE if reduce(or_, conds) else arch.FunctionThrows.NO + return attribute_info.FunctionThrows.MAYBE if reduce(or_, conds) else attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Loop, context): @@ -121,7 +113,7 @@ def _(self, expr: behav.Return, context): if expr.expr is not None: return self.generate(expr.expr, context) - return arch.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.UnaryOperation, context): @@ -131,15 +123,15 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Memory) and arch.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: - return arch.FunctionThrows.YES + if isinstance(expr.reference, arch.Memory) and attribute_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: + return attribute_info.FunctionThrows.YES - return arch.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.IndexedReference, context): - if isinstance(expr.reference, arch.Memory) and arch.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: - return arch.FunctionThrows.YES + if isinstance(expr.reference, arch.Memory) and attribute_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: + return attribute_info.FunctionThrows.YES return self.generate(expr.index, context) @@ -152,16 +144,16 @@ def _(self, expr: behav.TypeConv, context): @generate.register def _(self, expr: behav.Callable, context): args = [self.generate(arg, context) for arg in expr.args] - throws = getattr(expr.ref_or_name, "throws", arch.FunctionThrows.NO) - args.append(throws if isinstance(throws, arch.FunctionThrows) else cast_to_throws(throws)) + throws = getattr(expr.ref_or_name, "throws", attribute_info.FunctionThrows.NO) + args.append(throws if isinstance(throws, attribute_info.FunctionThrows) else cast_to_throws(throws)) return reduce(or_, args) @generate.register def _(self, expr: behav.ProcedureCall, context): args = [self.generate(arg, context) for arg in expr.args] - throws = getattr(expr.ref_or_name, "throws", arch.FunctionThrows.NO) - args.append(throws if isinstance(throws, arch.FunctionThrows) else cast_to_throws(throws)) + throws = getattr(expr.ref_or_name, "throws", attribute_info.FunctionThrows.NO) + args.append(throws if isinstance(throws, attribute_info.FunctionThrows) else cast_to_throws(throws)) return reduce(or_, args) @@ -172,8 +164,8 @@ def _(self, expr: behav.Group, context): return expr_result -def cast_to_throws(throws: Any) -> arch.FunctionThrows: +def cast_to_throws(throws: Any) -> attribute_info.FunctionThrows: """Cast unknown throws values into FunctionThrows for robust visitor dispatch.""" if isinstance(throws, bool): - return arch.FunctionThrows.YES if throws else arch.FunctionThrows.NO - return arch.FunctionThrows(throws) + return attribute_info.FunctionThrows.YES if throws else attribute_info.FunctionThrows.NO + return attribute_info.FunctionThrows(throws) diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index fd172ae..f195979 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -14,8 +14,7 @@ from functools import singledispatchmethod from typing import Any, cast -from ...metamodel import arch, behav -from ...metamodel.utils import ScalarStaticnessContext, StaticType +from ...metamodel import arch, behav, attribute_info from .ExprVisitor import ExprVisitor # pylint: disable=unused-argument @@ -28,7 +27,7 @@ def generate(self, expr: behav.BaseNode, context=None): raise NotImplementedError(f"No visit method implemented for type {type(expr).__name__} in {type(expr).__name__}") @generate.register - def _(self, expr: behav.Operation, context: ScalarStaticnessContext): + def _(self, expr: behav.Operation, context: attribute_info.ScalarStaticnessContext): statements = [] for stmt in expr.statements: temp = self.generate(stmt, context) @@ -44,18 +43,18 @@ def _(self, expr: behav.Block, context): stmts = [self.generate(x, context) for x in expr.statements] valid = [s for s in stmts if s is not None] if not valid: - return StaticType.NONE + return attribute_info.StaticAttribute.NONE return min(valid) @generate.register - def _(self, expr: behav.BinaryOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.BinaryOperation, context: attribute_info.ScalarStaticnessContext): left = self.generate(expr.left, context) right = self.generate(expr.right, context) return min(left, right) @generate.register - def _(self, expr: behav.SliceOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.SliceOperation, context: attribute_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) left = self.generate(expr.left, context) right = self.generate(expr.right, context) @@ -63,68 +62,61 @@ def _(self, expr: behav.SliceOperation, context: ScalarStaticnessContext): return min(expr_result, left, right) @generate.register - def _(self, expr: behav.ConcatOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.ConcatOperation, context: attribute_info.ScalarStaticnessContext): left = self.generate(expr.left, context) right = self.generate(expr.right, context) return min(left, right) @generate.register - def _(self, expr: behav.NumberLiteral, context: ScalarStaticnessContext): - return StaticType.READ + def _(self, expr: behav.Literal, context: attribute_info.ScalarStaticnessContext): + return attribute_info.StaticAttribute.READ - @generate.register - def _(self, expr: behav.IntLiteral, context: ScalarStaticnessContext): - return StaticType.READ - - @generate.register - def _(self, expr: behav.StringLiteral, context: ScalarStaticnessContext): - return StaticType.READ @generate.register - def _(self, expr: behav.ScalarDefinition, context: ScalarStaticnessContext): + def _(self, expr: behav.ScalarDefinition, context: attribute_info.ScalarStaticnessContext): scalar = cast(Any, expr.scalar) - scalar.static = StaticType.RW - return StaticType.RW + scalar.attributes["static"] = attribute_info.StaticAttribute.RW + return attribute_info.StaticAttribute.RW @generate.register def _(self, expr: behav.Break, context): - return StaticType.READ + return attribute_info.StaticAttribute.READ @generate.register - def _(self, expr: behav.Assignment, context: ScalarStaticnessContext): + def _(self, expr: behav.Assignment, context: attribute_info.ScalarStaticnessContext): self.generate(expr.target, context) - if context.context_is_static != StaticType.NONE or isinstance(expr.target, behav.ScalarDefinition): + if context.context_is_static != attribute_info.StaticAttribute.NONE or isinstance(expr.target, behav.ScalarDefinition): expr_static = self.generate(expr.expr, context) - if expr_static != StaticType.NONE: - expr_static = StaticType.RW + if expr_static != attribute_info.StaticAttribute.NONE: + expr_static = attribute_info.StaticAttribute.RW else: - expr_static = StaticType.NONE + expr_static = attribute_info.StaticAttribute.NONE - if isinstance(expr.target, behav.NamedReference) and isinstance(expr.target.reference, arch.Scalar): + if isinstance(expr.target, behav.NamedReference) and isinstance(expr.target.reference, arch.Variable): target_ref = cast(Any, expr.target.reference) - target_ref.static &= expr_static + target_ref.attributes["static"] &= expr_static if isinstance(expr.target, behav.ScalarDefinition): target_scalar = cast(Any, expr.target.scalar) - target_scalar.static &= expr_static + target_scalar.attributes["static"] &= expr_static @generate.register - def _(self, expr: behav.Conditional, context: ScalarStaticnessContext): + def _(self, expr: behav.Conditional, context: attribute_info.ScalarStaticnessContext): conds = [self.generate(x, context) for x in expr.conds] stmt_context = dataclasses.replace(context, context_is_static=min(conds)) _ = [self.generate(x, stmt_context) for x in expr.stmts] @generate.register - def _(self, expr: behav.Loop, context: ScalarStaticnessContext): + def _(self, expr: behav.Loop, context: attribute_info.ScalarStaticnessContext): cond = self.generate(expr.cond, context) stmt_context = dataclasses.replace(context, context_is_static=cond) _ = [self.generate(x, stmt_context) for x in expr.stmts] @generate.register - def _(self, expr: behav.Ternary, context: ScalarStaticnessContext): + def _(self, expr: behav.Ternary, context: attribute_info.ScalarStaticnessContext): cond = self.generate(expr.cond, context) then_expr = self.generate(expr.then_expr, context) else_expr = self.generate(expr.else_expr, context) @@ -132,62 +124,65 @@ def _(self, expr: behav.Ternary, context: ScalarStaticnessContext): return min(cond, then_expr, else_expr) @generate.register - def _(self, expr: behav.Return, context: ScalarStaticnessContext): + def _(self, expr: behav.Return, context: attribute_info.ScalarStaticnessContext): if expr.expr is not None: return self.generate(expr.expr, context) - return StaticType.RW + return attribute_info.StaticAttribute.RW @generate.register - def _(self, expr: behav.UnaryOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.UnaryOperation, context: attribute_info.ScalarStaticnessContext): right = self.generate(expr.right, context) return right @generate.register - def _(self, expr: behav.NamedReference, context: ScalarStaticnessContext): - if isinstance(expr.reference, arch.Scalar): - return expr.reference.static + def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticnessContext): + if isinstance(expr.reference, arch.Variable): + return expr.reference.attributes.get("static") static_map = { - arch.Memory: StaticType.NONE, - arch.BitFieldDescr: StaticType.READ, - arch.Constant: StaticType.READ, - arch.FnParam: StaticType.READ + arch.Memory: attribute_info.StaticAttribute.NONE, + arch.RegisterBank: attribute_info.StaticAttribute.NONE, + arch.Register: attribute_info.StaticAttribute.NONE, + arch.Alias: attribute_info.StaticAttribute.NONE, + arch.BitFieldDescr: attribute_info.StaticAttribute.READ, + arch.Parameter: attribute_info.StaticAttribute.READ, + arch.FnParam: attribute_info.StaticAttribute.READ } - return static_map.get(type(expr.reference), StaticType.NONE) + return static_map.get(type(expr.reference), attribute_info.StaticAttribute.NONE) @generate.register - def _(self, expr: behav.IndexedReference, context: ScalarStaticnessContext): + def _(self, expr: behav.IndexedReference, context: attribute_info.ScalarStaticnessContext): self.generate(expr.index, context) - return StaticType.NONE + return attribute_info.StaticAttribute.NONE @generate.register - def _(self, expr: behav.TypeConv, context: ScalarStaticnessContext): + def _(self, expr: behav.TypeConv, context: attribute_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) return expr_result @generate.register - def _(self, expr: behav.Callable, context: ScalarStaticnessContext): + def _(self, expr: behav.Callable, context: attribute_info.ScalarStaticnessContext): args = [self.generate(arg, context) for arg in expr.args] is_static = bool(getattr(expr.ref_or_name, "static", False)) - args.append(StaticType.READ if is_static else StaticType.NONE) + args.append(attribute_info.StaticAttribute.READ if is_static else attribute_info.StaticAttribute.NONE) return min(args) @generate.register - def _(self, expr: behav.ProcedureCall, context: ScalarStaticnessContext): + def _(self, expr: behav.ProcedureCall, context: attribute_info.ScalarStaticnessContext): args = [self.generate(arg, context) for arg in expr.args] is_static = bool(getattr(expr.ref_or_name, "static", False)) - args.append(StaticType.READ if is_static else StaticType.NONE) + args.append(attribute_info.StaticAttribute.READ if is_static else attribute_info.StaticAttribute.NONE) return min(args) @generate.register - def _(self, expr: behav.Group, context: ScalarStaticnessContext): + def _(self, expr: behav.Group, context: attribute_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) return expr_result diff --git a/m2isar/transforms/infer_types/transform.py b/m2isar/transforms/infer_types/transform.py index 92b5034..0d5a879 100644 --- a/m2isar/transforms/infer_types/transform.py +++ b/m2isar/transforms/infer_types/transform.py @@ -35,6 +35,9 @@ def infer_types(model_obj): for _, instr_def in core_def.instructions.items(): logger.debug("inferring types for instr %s", instr_def.name) mutator.generate(instr_def.operation, None) + for _, func_def in core_def.functions.items(): + logger.debug("inferring types for functions %s", func_def.name) + mutator.generate(func_def.operation, None) for _, set_def in model_obj.sets.items(): logger.debug("inferring types for set %s", set_def.name) for _, instr_def in set_def.instructions.items(): diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index b3504cf..3bb5e18 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -9,15 +9,15 @@ """A transformation module for simplifying M2-ISA-R behavior expressions. The following simplifications are done: -* Resolvable :class:`m2isar.metamodel.arch.Constant` s are replaced by - `m2isar.metamodel.arch.IntLiteral` s representing their value +* Resolvable :class:`m2isar.metamodel.arch.Parameter` s are replaced by + `m2isar.metamodel.arch.Literal` s representing their value * Fully resolvable arithmetic operations are carried out and their results - represented as a matching :class:`m2isar.metamodel.arch.IntLiteral` + represented as a matching :class:`m2isar.metamodel.arch.Literal` * Conditions and loops with fully resolvable conditions are either discarded entirely or transformed into code blocks without any conditions * Ternaries with fully resolvable conditions are transformed into only the matching part -* Type conversions of :class:`m2isar.metamodel.arch.IntLiteral` s apply the desired - type directly to the :class:`IntLiteral` and discard the type conversion +* Type conversions of :class:`m2isar.metamodel.arch.Literal` s apply the desired + type directly to the :class:`Literal` and discard the type conversion """ import logging @@ -25,7 +25,7 @@ from functools import singledispatchmethod from copy import deepcopy -from m2isar.metamodel import arch, behav +from m2isar.metamodel import arch, behav, type_info from ...metamodel.utils.ExprMutator import ExprMutator logger = logging.getLogger("infer_types") @@ -60,16 +60,16 @@ def _(self, expr: behav.BinaryOperation, context): # see: https://github.com/Minres/CoreDSL/wiki/Expressions#arithmetic-type-rules if expr.op.value in ["+", "-", "*", "/", "%", "|", "&", "^", "<<", ">>"]: - if expr.left.inferred_type is None or expr.right.inferred_type is None: + if expr.left.ty is None or expr.right.ty is None: logger.warning("Slice Operation needs inferred type. Skipping...") - expr.inferred_type = None + expr.ty = None return expr - assert isinstance(expr.left.inferred_type, arch.IntegerType) - assert isinstance(expr.right.inferred_type, arch.IntegerType) - w1 = expr.left.inferred_type._width - w2 = expr.right.inferred_type._width - s1 = expr.left.inferred_type.signed - s2 = expr.right.inferred_type.signed + assert isinstance(expr.left.ty, type_info.PrimitiveType) + assert isinstance(expr.right.ty, type_info.PrimitiveType) + w1 = arch.get_const_or_val(expr.left.ty.size) + w2 = arch.get_const_or_val(expr.right.ty.size) + s1 = True if expr.left.ty.kind == type_info.TypeKind.INT else False + s2 = True if expr.right.ty.kind == type_info.TypeKind.INT else False if expr.op.value == "+": if not s1 and not s2: wr = max(w1, w2) + 1 @@ -118,13 +118,15 @@ def _(self, expr: behav.BinaryOperation, context): elif expr.op.value in [">>", "<<"]: wr = w1 sr = s1 - expr.inferred_type = arch.IntegerType(wr, sr, None) + + kind = type_info.TypeKind.INT if sr else type_info.TypeKind.UINT + expr.ty = type_info.PrimitiveType(kind, wr) else: if expr.op.value in ["||", "&&"]: - expr.inferred_type = arch.IntegerType(1, False, None) # unsigned<1> / bool + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) # unsigned<1> / bool elif expr.op.value in ["<", ">", "==", "!=", ">=", "<="]: - expr.inferred_type = arch.IntegerType(1, False, None) # unsigned<1> / bool - assert expr.inferred_type is not None + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) # unsigned<1> / bool + assert expr.ty is not None return expr @@ -136,25 +138,33 @@ def _(self, expr: behav.SliceOperation, context): expr.right = self.generate(expr.right, context) # type inference - if expr.expr.inferred_type is None: + if expr.expr.ty is None: logger.warning("Slice Operation needs inferred type. Skipping...") return expr - assert isinstance(expr.expr.inferred_type, arch.IntegerType) - ty = expr.expr.inferred_type + assert isinstance(expr.expr.ty, (type_info.PrimitiveType)) + ty = expr.expr.ty # For non-static slices, we cann not infer the type! - if not isinstance(expr.left, behav.IntLiteral): + if not isinstance(expr.left, behav.Literal): logger.warning("Can not infer type of non-static slice operation. Skipping...") return expr lval = expr.left.value - if not isinstance(expr.right, behav.IntLiteral): + if not isinstance(expr.right, behav.Literal): logger.warning("Can not infer type of non-static slice operation. Skipping...") return expr rval = expr.right.value width = lval - rval + 1 if lval > rval else rval - lval + 1 ty_ = copy(ty) - ty_._width = width - expr.inferred_type = ty_ + if isinstance(ty_, type_info.PrimitiveType): + ty_.size = width + elif isinstance(ty_, type_info.ArrayType): + if width == 1: # Array -> PrimitiveType + ty_ = ty_.element_type + else: # Array Slice + ty_.length = arch.get_const_or_val(width) + else: + raise f"Type Slicing not supported for type {ty_}" + expr.ty = ty_ return expr @@ -162,45 +172,37 @@ def _(self, expr: behav.SliceOperation, context): def _(self, expr: behav.ConcatOperation, context): expr.left = self.generate(expr.left, context) expr.right = self.generate(expr.right, context) - if expr.left.inferred_type is None: + if expr.left.ty is None: logger.warning("Concat Operation needs inferred type. Skipping...") return expr - if expr.right.inferred_type is None: + if expr.right.ty is None: logger.warning("Concat Operation needs inferred type. Skipping...") return expr - width = expr.left.inferred_type.width + expr.right.inferred_type.width - ty = arch.IntegerType(width, False, None) - expr.inferred_type = ty - - return expr - - - @generate.register - def _(self, expr: behav.IntLiteral, context): - if isinstance(expr, behav.IntLiteral): - bit_size = expr.bit_size - signed = expr.signed + width = arch.get_const_or_val(expr.left.ty.size) + arch.get_const_or_val(expr.right.ty.size) + size = arch.get_const_or_val(width) + ty = type_info.PrimitiveType(type_info.TypeKind.UINT, size) + expr.ty = ty - expr.inferred_type = arch.IntegerType(bit_size, signed, None) return expr + # behav.IntLiteral @generate.register - def _(self, expr: behav.IntLiteral, context): + def _(self, expr: behav.Literal, context): # type inference - bit_size = expr.bit_size - signed = expr.signed - - expr.inferred_type = arch.IntegerType(bit_size, signed, None) + assert(expr.ty.size is not None) + assert(expr.ty.kind.is_int) + expr.ty = type_info.PrimitiveType(expr.ty.kind, expr.ty.size) return expr @generate.register def _(self, expr: behav.ScalarDefinition, context): # type inference - signed = expr.scalar.data_type == arch.DataType.S - width = expr.scalar.size - expr.inferred_type = arch.IntegerType(width, signed, None) + assert isinstance(expr.scalar.ty, type_info.PrimitiveType) + assert expr.scalar.ty.size is not None + assert expr.scalar.ty.kind.is_int + expr.ty = expr.scalar.ty return expr @generate.register @@ -212,7 +214,7 @@ def _(self, expr: behav.Assignment, context): # expr.target.scalar.value = expr.expr.value # type inference - expr.inferred_type = None + expr.ty = None return expr @@ -223,7 +225,7 @@ def _(self, expr: behav.Conditional, context): stmts = [] for stmt in expr.stmts: if isinstance(stmt, list): # TODO: legacy? - new = [seld.generate(y, context) for y in stmt] + new = [self.generate(y, context) for y in stmt] else: new = self.generate(stmt, context) stmts.append(new) @@ -246,14 +248,14 @@ def _(self, expr: behav.Ternary, context): expr.else_expr = self.generate(expr.else_expr, context) # TODO - then_ty = expr.then_expr.inferred_type - else_ty = expr.else_expr.inferred_type + then_ty = expr.then_expr.ty + else_ty = expr.else_expr.ty if then_ty and else_ty: # assert then_ty.signed == else_ty.signed - wt = then_ty.width - we = else_ty.width + wt = arch.get_const_or_val(then_ty.size) + we = arch.get_const_or_val(else_ty.size) wr = max(wt, we) - expr.inferred_type = arch.IntegerType(wr, True, None) + expr.ty = type_info.PrimitiveType(type_info.TypeKind.INT, wr) return expr @@ -267,17 +269,17 @@ def _(self, expr: behav.Return, context): @generate.register def _(self, expr: behav.UnaryOperation, context): expr.right = self.generate(expr.right, context) - if expr.right.inferred_type: - w1 = expr.right.inferred_type.width + if expr.right.ty: + w1 = expr.right.ty.size if expr.op.value == "-": - inferred_type = arch.IntegerType(w1 + 1, True, None) + ty = type_info.PrimitiveType(type_info.TypeKind.INT, w1 + 1) elif expr.op.value == "~": - inferred_type = arch.IntegerType(w1, True, None) + ty = type_info.PrimitiveType(type_info.TypeKind.INT, w1) elif expr.op.value == "!": - inferred_type = arch.IntegerType(1, False, None) + ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) else: - inferred_type = None - expr.inferred_type = inferred_type + ty = None + expr.ty = ty return expr @@ -285,27 +287,36 @@ def _(self, expr: behav.UnaryOperation, context): def _(self, expr: behav.NamedReference, context): reference = expr.reference - # type inference - # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): - assert expr.reference.data_type in [arch.DataType.U, arch.DataType.S] - ty = arch.IntegerType(reference.size, reference.data_type == arch.DataType.S, None) - expr.inferred_type = ty - - elif isinstance(reference, arch.Scalar): - dt = reference.data_type - sz = reference.size - assert dt in [arch.DataType.U, arch.DataType.S] - signed = dt == arch.DataType.S - ty = arch.IntegerType(sz, signed, None) - expr.inferred_type = ty - elif isinstance(reference, arch.Memory): - expr.inferred_type = arch.IntegerType(reference.size, False, None) + assert reference.ty.kind.is_int + expr.ty = reference.ty + elif isinstance(reference, (arch.Variable, arch.Memory, arch.RegisterBank, arch.Register)): + assert isinstance(reference.ty, (type_info.PrimitiveType, type_info.ArrayType)) + # Constant-time calculation of sizes + if isinstance(reference.ty, type_info.PrimitiveType): + reference.ty.size = arch.get_const_or_val(reference.ty.size) + elif isinstance(reference.ty, type_info.ArrayType): + reference.ty.length = arch.get_const_or_val(reference.ty.length) + expr.ty = reference.ty + elif isinstance(reference, arch.Alias): # propagate type from aliased mem or reg bank + assert(reference.length == 1) + if isinstance(reference.parent, (arch.Memory, arch.RegisterBank)): + # expr.ty = self.generate(behav.NamedReference(reference.parent), context) + expr.ty = reference.parent.ty.element_type + assert (expr.ty is not None) + elif isinstance(reference.parent, arch.Register): + expr.ty = reference.parent.ty + assert (expr.ty is not None) + else: + raise NotImplementedError(f"Alias parent type {type(reference.parent)} not supported for type inference") elif isinstance(reference, arch.Intrinsic): - assert expr.reference.data_type in [arch.DataType.U, arch.DataType.S] - expr.inferred_type = arch.IntegerType(reference.size, reference.data_type == arch.DataType.S, None) - elif isinstance(reference, arch.Constant): - expr.inferred_type = arch.IntegerType(reference.size, reference.signed, None) + assert reference.ty.kind.is_int and isinstance(reference.ty, type_info.PrimitiveType) + expr.ty = reference.ty + elif isinstance(reference, arch.Parameter): + kind = type_info.TypeKind.INT if reference.signed else type_info.TypeKind.UINT + expr.ty = type_info.PrimitiveType(kind, reference.size) + elif isinstance(reference, arch.FnParam): + expr.ty = reference.ty else: assert False, "Unhandled reference" @@ -315,28 +326,33 @@ def _(self, expr: behav.NamedReference, context): def _(self, expr: behav.IndexedReference, context): expr.index = self.generate(expr.index, context) + assert isinstance(expr.reference, (arch.Memory, arch.RegisterBank)) + # expr.reference = self.generate(behav.NamedReference(expr.reference), context) + + # if expr.right is not None: + # expr.right = self.generate(expr.right, context) + # type inference - assert isinstance(expr.reference, arch.Memory) - ty = arch.DataType.U # TODO: Memory class should keep track of dtype, not only size? - assert ty in [arch.DataType.U, arch.DataType.S] - single_mem_acc_size = expr.reference.size + assert expr.reference.ty.element_type.kind.is_int + single_mem_acc_size = expr.reference.ty.element_type.size ## Simple eval check for ranged access. # Little-endian interpretation: # - lhs > rhs → width = lhs - rhs + 1 # - lhs == rhs → width = 8 bits - if expr.right == None: + if expr.right is None: size = single_mem_acc_size - if expr.right != None: - lhs_offset = helper_expr_size(expr.index) - rhs_offset = helper_expr_size(expr.right) - assert(lhs_offset >= rhs_offset) - size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size + else: + lhs_offset = helper_expr_size(expr.index) + rhs_offset = helper_expr_size(expr.right) + assert(lhs_offset >= rhs_offset) + size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size - ty_ = arch.IntegerType(size, ty == arch.DataType.S, None) + ty_ = type_info.PrimitiveType(expr.reference.ty.element_type.kind, size) - expr.inferred_type = ty_ + expr.ty = ty_ + assert expr.ty is not None return expr @@ -344,31 +360,28 @@ def _(self, expr: behav.IndexedReference, context): def _(self, expr: behav.TypeConv, context): expr.expr = self.generate(expr.expr, context) - ty = deepcopy(expr.expr.inferred_type) + ty = deepcopy(expr.expr.ty) if ty is None: logger.warning("Type conv needs inferred type. Skipping...") return expr - assert isinstance(ty, arch.IntegerType) - assert expr.data_type in [arch.DataType.U, arch.DataType.S] - ty.signed = expr.data_type == arch.DataType.S + assert isinstance(ty, type_info.PrimitiveType) + assert expr.data_type.is_int + ty.signed = expr.data_type == type_info.TypeKind.INT if expr.size is not None: - ty._width = expr.size + ty.size = expr.size # type inference - expr.inferred_type = ty + expr.ty = ty return expr @generate.register def _(self, expr: behav.Callable, context): if isinstance(expr.ref_or_name, arch.Function): - if expr.ref_or_name.data_type == arch.DataType.NONE: - signed = None - else: - assert expr.ref_or_name.data_type in [arch.DataType.U, arch.DataType.S] - signed = expr.ref_or_name.data_type == arch.DataType.S - width = expr.ref_or_name.size - expr.inferred_type = arch.IntegerType(width, signed, None) + if not(expr.ref_or_name.ty.kind == type_info.TypeKind.VOID): + assert expr.ref_or_name.ty.kind.is_int + width = arch.get_const_or_val(expr.ref_or_name.ty.size) + expr.ty = type_info.PrimitiveType(expr.ref_or_name.ty.kind, arch.get_const_or_val(width)) expr.args = [self.generate(stmt, context) for stmt in expr.args] return expr @@ -384,11 +397,11 @@ def _(self, expr: behav.Callable, context): def _(self, expr: behav.Group, context): expr.expr = self.generate(expr.expr, context) - if isinstance(expr.expr, behav.IntLiteral): + if isinstance(expr.expr, behav.Literal): return expr.expr # type inference - expr.inferred_type = expr.expr.inferred_type + expr.ty = expr.expr.ty return expr @@ -408,11 +421,11 @@ def helper_expr_size(sub_expr: behav.BaseNode): assert isinstance(expr.left, behav.NamedReference) if expr.op.value == "+": - if type(expr.right) == behav.IntLiteral: + if type(expr.right) == behav.Literal: return int(expr.right.value) elif expr.op.value == "-": - if type(expr.right) == behav.IntLiteral: + if type(expr.right) == behav.Literal: return (-1 * int(expr.right.value)) else: raise(f"Not supported Operation value Type {expr.op.value} within mem access range") diff --git a/m2isar/transforms/validate_behav/visitor.py b/m2isar/transforms/validate_behav/visitor.py index b6b59c4..840df19 100644 --- a/m2isar/transforms/validate_behav/visitor.py +++ b/m2isar/transforms/validate_behav/visitor.py @@ -43,22 +43,22 @@ def _(self, expr: behav.BinaryOperation, context): self.generate(expr.right, context) op = expr.op - assert expr.left.inferred_type is not None - assert expr.right.inferred_type is not None - if op.value in ["|", "&", "^"] and expr.left.inferred_type.width != expr.right.inferred_type.width: + assert expr.left.ty is not None + assert expr.right.ty is not None + if op.value in ["|", "&", "^"] and expr.left.ty.width != expr.right.ty.width: context.emit_warning(f"Bitwise operations with differently size operands are discouraged.", "bit-op-missmatch", logger=logger, line_info=expr.line_info) - if op.value in ["<<", ">>", ">>>"] and expr.right.inferred_type.signed: + if op.value in ["<<", ">>", ">>>"] and expr.right.ty.signed: context.emit_warning(f"Shift by signed amount", "shift-signed", logger=logger, line_info=expr.line_info) - if op.value in ["<", "<=", ">", ">=", "==", "!="] and expr.left.inferred_type.signed != expr.right.inferred_type.signed: - if isinstance(expr.left, behav.IntLiteral) and expr.left.value == 0: + if op.value in ["<", "<=", ">", ">=", "==", "!="] and expr.left.ty.signed != expr.right.ty.signed: + if isinstance(expr.left, behav.Literal) and expr.left.value == 0: pass - if isinstance(expr.right, behav.IntLiteral) and expr.right.value == 0: + if isinstance(expr.right, behav.Literal) and expr.right.value == 0: pass else: context.emit_warning(f"Signed vs. unsigned comparison", "sign-compare", logger=logger, line_info=expr.line_info) # TODO: also check possible range of non-literal rhs? - if op.value == "<<" and isinstance(expr.right, behav.IntLiteral) and expr.left.inferred_type.width <= expr.right.value: - context.emit_warning(f"Shift count overflow for << operation ({expr.left.inferred_type.width} vs. {expr.right.value})", "shift-overflow", logger=logger, line_info=expr.line_info) + if op.value == "<<" and isinstance(expr.right, behav.Literal) and expr.left.ty.width <= expr.right.value: + context.emit_warning(f"Shift count overflow for << operation ({expr.left.ty.width} vs. {expr.right.value})", "shift-overflow", logger=logger, line_info=expr.line_info) @generate.register @@ -73,27 +73,19 @@ def _(self, expr: behav.ConcatOperation, context): self.generate(expr.right, context) @generate.register - def _(self, expr: behav.NumberLiteral, context): - pass - - @generate.register - def _(self, expr: behav.IntLiteral, context): - pass - - @generate.register - def _(self, expr: behav.StringLiteral, context): + def _(self, expr: behav.Literal, context): pass @generate.register def _(self, expr: behav.Assignment, context): self.generate(expr.target, context) self.generate(expr.expr, context) - assert expr.target.inferred_type is not None - assert expr.expr.inferred_type is not None - if expr.target.inferred_type.width < expr.expr.inferred_type.width: - context.emit_warning(f"Implicit truncation {expr.expr.inferred_type.width} -> {expr.target.inferred_type.width} found", "implicit-trunc", logger=logger, line_info=expr.line_info) - if expr.target.inferred_type.width > expr.expr.inferred_type.width: - context.emit_warning(f"Implicit extend {expr.expr.inferred_type.width} -> {expr.target.inferred_type.width} found", "implicit-extend", logger=logger, line_info=expr.line_info) + assert expr.target.ty is not None + assert expr.expr.ty is not None + if expr.target.ty.width < expr.expr.ty.width: + context.emit_warning(f"Implicit truncation {expr.expr.ty.width} -> {expr.target.ty.width} found", "implicit-trunc", logger=logger, line_info=expr.line_info) + if expr.target.ty.width > expr.expr.ty.width: + context.emit_warning(f"Implicit extend {expr.expr.ty.width} -> {expr.target.ty.width} found", "implicit-extend", logger=logger, line_info=expr.line_info) @generate.register def _(self, expr: behav.Conditional, context):