From 83474859a2cbf0797576e516e034c6a45b034b2f Mon Sep 17 00:00:00 2001 From: jokap11 Date: Fri, 24 Apr 2026 19:20:14 +0200 Subject: [PATCH 01/33] [Metamodel] Refactor Literal and one common PrimitiveKind type Merge Literal subclasses to one main DataType removed by PrimitiveKind --- m2isar/backends/etiss/architecture_writer.py | 2 +- .../backends/etiss/instruction_generator.py | 4 +- .../backends/etiss/instruction_transform.py | 65 +++++++++-------- .../coredsl2/architecture_model_builder.py | 12 ++-- .../coredsl2/behavior_model_builder.py | 12 ++-- m2isar/frontends/coredsl2/expr_interpreter.py | 8 +-- m2isar/frontends/coredsl2/parser.py | 4 +- m2isar/metamodel/arch.py | 6 +- m2isar/metamodel/behav.py | 69 ++++++------------- m2isar/metamodel/type_info.py | 26 +++++++ m2isar/metamodel/utils/ExprMutator.py | 10 +-- m2isar/metamodel/utils/ExprVisitor.py | 10 +-- m2isar/metamodel/utils/expr_simplifier.py | 58 +++++++--------- m2isar/metamodel/utils/function_staticness.py | 10 +-- m2isar/metamodel/utils/function_throws.py | 10 +-- m2isar/metamodel/utils/scalar_staticness.py | 9 +-- m2isar/transforms/infer_types/visitor.py | 30 +++----- m2isar/transforms/validate_behav/visitor.py | 10 +-- 18 files changed, 146 insertions(+), 209 deletions(-) create mode 100644 m2isar/metamodel/type_info.py diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 6bb671d3..92e4a8fe 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -208,7 +208,7 @@ 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): + 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 ac32ff01..8044ff1f 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -12,7 +12,7 @@ from mako.template import Template -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info from . import BlockEndType, instruction_utils from .instruction_transform import InstructionTransformVisitor from .templates import template_dir @@ -237,7 +237,7 @@ def gen_rand_suffix(length: int = 8): [cond[0]], [ instr_def.operation.statements, - behav.ProcedureCall(error_fn, [behav.IntLiteral(-11)]) + behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.PrimitiveKind.S)]) ] ) ]) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 1050c91b..74e53af5 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -15,7 +15,7 @@ from functools import singledispatchmethod from ... import M2NameError, M2SyntaxError, M2ValueError, flatten -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info from ...metamodel.code_info import LineInfoPlacement from ...metamodel.utils.ExprVisitor import ExprVisitor from . import CodeInfoTracker, replacements @@ -789,42 +789,41 @@ 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 - - minus = "" - if lit > 0 and sign and (lit >> (size - 1)) & 1: - minus = "-" + def _(self, expr: behav.Literal, context: TransformerContext): + if expr.kind is type_info.PrimitiveKind.STR: + return CodeString(f'"{expr.value}"', StaticType.READ, None, False, line_infos=expr.line_info) + else: + # old number NumberLiteral + if expr.size is 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.kind in [type_info.PrimitiveKind.S, type_info.PrimitiveKind.U]) + lit = int(expr.value) + size = min(expr.size, 128) + sign = True if expr.kind is type_info.PrimitiveKind.S else False - _ = (lit + (1 << size)) % (1 << size) + minus = "" + if lit > 0 and sign and (lit >> (size - 1)) & 1: + minus = "-" - 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/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 3739fd03..ca14757c 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, intrinsics, type_info from ...metamodel.code_info import FunctionInfoFactory from .parser_gen import CoreDSL2Parser, CoreDSL2Visitor from .utils import RADIX, SHORTHANDS, SIGNEDNESS @@ -67,7 +67,7 @@ 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.size, val.value) def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): """Generate a top-level instruction set object.""" @@ -280,7 +280,8 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): else: width = value.bit_length() - return behav.IntLiteral(value, width) + kind = type_info.PrimitiveKind.U if value >=0 else type_info.PrimitiveKind.S + return behav.Literal(value, kind, width) def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): """Generate a declaration.""" @@ -421,7 +422,7 @@ 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 @@ -483,7 +484,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.PrimitiveKind.S if value<0 else type_info.PrimitiveKind.U) def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionContext): """Generate an assignment. """ diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 8e308a4d..8dc3cf10 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING from ... import M2NameError, M2SyntaxError, M2TypeError, flatten -from ...metamodel import arch, behav, intrinsics +from ...metamodel import arch, behav, type_info, intrinsics from ...metamodel.code_info import (BranchEntryInfoFactory, BranchInfo, LineInfoFactory, LineInfoPlacement) from ...metamodel.utils import StaticType @@ -136,7 +136,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_info.PrimitiveKind.U) 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) @@ -352,7 +352,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.PrimitiveKind.S if value <= 0 else type_info.PrimitiveKind.U + + 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 +363,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.PrimitiveKind.U, 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 @@ -442,4 +444,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.PrimitiveKind.U, 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 582b85bb..ffda8cd0 100644 --- a/m2isar/frontends/coredsl2/expr_interpreter.py +++ b/m2isar/frontends/coredsl2/expr_interpreter.py @@ -26,12 +26,8 @@ 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): diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 853cabbc..470a87a3 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 from ...metamodel.utils.expr_simplifier import ExprSimplifierVisitor from ...metamodel.code_info import CodeInfoBase from .architecture_model_builder import ArchitectureModelBuilder @@ -271,7 +271,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.PrimitiveKind.U) ) ) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 6761b164..a0bffbf6 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -19,7 +19,7 @@ from m2isar.frontends.coredsl2.expr_interpreter import ExprInterpreterVisitor from .. import M2TypeError -from .behav import BaseNode, Operation, NumberLiteral +from .behav import BaseNode, Operation, Literal if TYPE_CHECKING: from .code_info import FunctionInfo @@ -29,8 +29,8 @@ def get_const_or_val(arg) -> int: if isinstance(arg, Constant): 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) diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index 85889b80..cf565051 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -21,6 +21,8 @@ """ from typing import TYPE_CHECKING, Union +from .type_info import PrimitiveKind + if TYPE_CHECKING: from .arch import (BitFieldDescr, Constant, FnParam, Function, Intrinsic, @@ -91,62 +93,35 @@ 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 : PrimitiveKind = None, size=None, base=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 + self._value : Union[int, str] = value + self.kind = kind # assigned during type checking - -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() + #Optional type information (not always given) + if size is None: + self.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 - - if signed is None: - self.signed = value <= 0 - else: - self.signed = signed + self.size = size # optional (important for ISA DSL!) + self.base: int = base # 2, 10, 16 def __repr__(self): - return f"IntLiteral({self.value}, {self.bit_size}, {self.signed})" + return f"Literal(value={self.value}, kind={self.kind}, size={self.size}, 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): @@ -245,7 +220,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 00000000..302b36b6 --- /dev/null +++ b/m2isar/metamodel/type_info.py @@ -0,0 +1,26 @@ +# 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 enum import Enum, auto + + +class PrimitiveKind(Enum): + NONE = auto() + U = auto() + S = auto() + F = auto() + D = auto() + Q = auto() + BOOL = auto() + STR = auto() + diff --git a/m2isar/metamodel/utils/ExprMutator.py b/m2isar/metamodel/utils/ExprMutator.py index 0f787071..8a0ee202 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 3c2071a3..bfd16f97 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/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index 155ffcf6..a50644d2 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -20,7 +20,7 @@ type directly to the :class:`IntLiteral` 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,37 @@ 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.size < expr.right.reference.size: + expr.left.size = expr.right.reference.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 expr.right.size < expr.left.reference.size: + expr.right.size = expr.left.reference.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())) + return behav.Literal(res, type_info.PrimitiveKind.U, max(expr.left.size, expr.right.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 +113,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 +129,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.size < expr.target.reference.size: + expr.expr.size = expr.target.reference.size return expr @@ -154,7 +146,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 +155,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 +178,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 +196,17 @@ 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())) + return behav.Literal(res, type_info.PrimitiveKind.U, max(expr.right.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) + return behav.Literal(expr.reference.value, type_info.PrimitiveKind.S if expr.reference.signed else type_info.PrimitiveKind.U, expr.reference.size) return expr @@ -227,7 +219,7 @@ 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 @@ -256,7 +248,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 e8d25d91..db2a0f0d 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 diff --git a/m2isar/metamodel/utils/function_throws.py b/m2isar/metamodel/utils/function_throws.py index 3489fbc9..96c545d5 100644 --- a/m2isar/metamodel/utils/function_throws.py +++ b/m2isar/metamodel/utils/function_throws.py @@ -65,15 +65,7 @@ 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): + def _(self, expr: behav.Literal, context): return arch.FunctionThrows.NO @generate.register diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index fd172ae3..78ffaff9 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -70,16 +70,9 @@ def _(self, expr: behav.ConcatOperation, context: ScalarStaticnessContext): return min(left, right) @generate.register - def _(self, expr: behav.NumberLiteral, context: ScalarStaticnessContext): + def _(self, expr: behav.Literal, context: ScalarStaticnessContext): return StaticType.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): diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index b3504cf6..b86744c5 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -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") @@ -142,11 +142,11 @@ def _(self, expr: behav.SliceOperation, context): assert isinstance(expr.expr.inferred_type, arch.IntegerType) ty = expr.expr.inferred_type # 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 @@ -175,23 +175,15 @@ def _(self, expr: behav.ConcatOperation, context): return expr + # behav.IntLiteral @generate.register - def _(self, expr: behav.IntLiteral, context): - if isinstance(expr, behav.IntLiteral): - bit_size = expr.bit_size - signed = expr.signed - - expr.inferred_type = arch.IntegerType(bit_size, signed, None) - return expr - - - @generate.register - def _(self, expr: behav.IntLiteral, context): + def _(self, expr: behav.Literal, context): # type inference - bit_size = expr.bit_size - signed = expr.signed + assert((expr.size is not None)) + assert(expr.kind in [type_info.PrimitiveKind.U, type_info.PrimitiveKind.S]) + signed = True if expr.kind is type_info.PrimitiveKind.S else False - expr.inferred_type = arch.IntegerType(bit_size, signed, None) + expr.inferred_type = arch.IntegerType(expr.size, signed, None) return expr @@ -223,7 +215,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) @@ -384,7 +376,7 @@ 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 diff --git a/m2isar/transforms/validate_behav/visitor.py b/m2isar/transforms/validate_behav/visitor.py index b6b59c44..a5f7a55a 100644 --- a/m2isar/transforms/validate_behav/visitor.py +++ b/m2isar/transforms/validate_behav/visitor.py @@ -73,15 +73,7 @@ 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 From 80e6787c0fd1cdd93f229a1e43c046b2d7fc4e19 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Mon, 27 Apr 2026 14:22:42 +0200 Subject: [PATCH 02/33] [MetaModel] Move Primitive+Literal Types+Literals into own file Try to use everywhere type.kind instead of signed Rename _width to size to get a constant access point in inferTypes --- m2isar/backends/viewer/treegen.py | 6 +- .../coredsl2/architecture_model_builder.py | 31 ++++--- .../coredsl2/behavior_model_builder.py | 26 +++--- m2isar/frontends/coredsl2/parser.py | 2 +- m2isar/metamodel/__init__.py | 4 +- m2isar/metamodel/arch.py | 79 +++------------- m2isar/metamodel/behav.py | 12 +-- m2isar/metamodel/type_info.py | 52 +++++++++-- m2isar/metamodel/utils/expr_simplifier.py | 20 ++-- m2isar/transforms/infer_types/visitor.py | 91 ++++++++++--------- 10 files changed, 150 insertions(+), 173 deletions(-) diff --git a/m2isar/backends/viewer/treegen.py b/m2isar/backends/viewer/treegen.py index aef529a3..8afeb28c 100644 --- a/m2isar/backends/viewer/treegen.py +++ b/m2isar/backends/viewer/treegen.py @@ -95,13 +95,9 @@ def concat_operation(self, expr: behav.ConcatOperation, context: "TreeGenContext 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,)) - @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,)) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index ca14757c..da8a92b5 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -61,13 +61,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.TYPE_UINT) def visitBit_value(self, ctx: CoreDSL2Parser.Bit_valueContext): """Generate a fixed encoding part.""" val = self.visit(ctx.value) - return arch.BitVal(val.size, val.value) + return arch.BitVal(val.type.size, val.value) def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): """Generate a top-level instruction set object.""" @@ -213,11 +213,11 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex params = [params] return_size = None - data_type = arch.DataType.NONE + data_type = type_info.TypeKind.TYPE_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.IntegerType): + 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: @@ -249,7 +249,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): @@ -280,7 +280,7 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): else: width = value.bit_length() - kind = type_info.PrimitiveKind.U if value >=0 else type_info.PrimitiveKind.S + kind = type_info.TypeKind.TYPE_UINT if value >=0 else type_info.TypeKind.TYPE_INT return behav.Literal(value, kind, width) def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): @@ -293,6 +293,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # extract data type type_ = self.visit(ctx.type_) + assert isinstance(type_, type_info.IntegerType) # extract list of contained declarations for the given type decls: "list[CoreDSL2Parser.DeclaratorContext]" = ctx.declarations @@ -333,7 +334,7 @@ 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 = arch.Memory(name, range_spec, type_.size, attributes) m.parent = reference m.parent.children.append(m) @@ -354,7 +355,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init is not None: init = self.visit(decl.init) - c = arch.Constant(name, init, [], type_._width, type_.signed) + c = arch.Constant(name, init, [], type_.size, True if type_.kind == type_info.TypeKind.TYPE_INT else False) self._constants[name] = c ret_decls.append(c) @@ -381,7 +382,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): 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, range_spec, type_.size, attributes) # attach init value to memory object if init is not None: @@ -429,15 +430,15 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): else: raise M2TypeError("width has wrong type") - return arch.IntegerType(width, signed, None) + return type_info.IntegerType(width, signed) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type.""" - return arch.VoidType(None) + return type_info.PrimitiveType(type_info.TypeKind.TYPE_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.IntegerType(1, False) def visitBinary_expression(self, ctx: CoreDSL2Parser.Binary_expressionContext): """Generate a binary expression.""" @@ -485,7 +486,7 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): value = SHORTHANDS[ctx.children[0].symbol.text] - return behav.Literal(value, type_info.PrimitiveKind.S if value<0 else type_info.PrimitiveKind.U) + return behav.Literal(value, type_info.TypeKind.TYPE_NONE) def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionContext): """Generate an assignment. """ diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 8dc3cf10..f32e50ac 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -127,7 +127,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.Scalar(name, None, StaticType.NONE, type_.size, type_.kind) self._scalars[name] = s sd = behav.ScalarDefinition(s) @@ -136,7 +136,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init: init = self.visit(decl.init) else: - init = behav.Literal(0, type_info.PrimitiveKind.U) + init = behav.Literal(0, type_info.TypeKind.TYPE_NONE) 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) @@ -352,7 +352,7 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): value = int(text, 0) width = value.bit_length() - kind = type_info.PrimitiveKind.S if value <= 0 else type_info.PrimitiveKind.U + kind = type_info.TypeKind.TYPE_INT if value <= 0 else type_info.TypeKind.TYPE_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)) @@ -363,7 +363,7 @@ def visitCharacter_constant(self, ctx: CoreDSL2Parser.Character_constantContext) value = min(ord(text.replace("'", "")), 255) - return behav.Literal(value, type_info.PrimitiveKind.U, size=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.TYPE_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 @@ -371,14 +371,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.TYPE_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.TYPE_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.""" @@ -386,12 +386,12 @@ 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 + sign = type_.kind + size = type_.size if ctx.sign: sign = self.visit(ctx.sign) - sign = arch.DataType.S if sign else arch.DataType.U + sign = type_info.TypeKind.TYPE_INT if sign else type_info.TypeKind.TYPE_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)) @@ -424,17 +424,17 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): else: raise M2TypeError("width has wrong type") - return arch.IntegerType(width, signed, None) + return type_info.IntegerType(width, signed) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type specifier.""" - return arch.VoidType(None) + return type_info.PrimitiveType(type_info.TypeKind.TYPE_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.IntegerType(1, False) def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext): """Generate integer signedness.""" @@ -444,4 +444,4 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): """Lookup a shorthand type specifier.""" - return behav.Literal(SHORTHANDS[ctx.children[0].symbol.text], type_info.PrimitiveKind.U, 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.TYPE_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/parser.py b/m2isar/frontends/coredsl2/parser.py index 470a87a3..f6d4178b 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -271,7 +271,7 @@ def main(): behav.BinaryOperation( behav.NamedReference(core_def.pc_memory), behav.Operator("+"), - behav.Literal(int(instr_def.size/8), type_info.PrimitiveKind.U) + behav.Literal(int(instr_def.size/8), type_info.TypeKind.TYPE_UINT) ) ) diff --git a/m2isar/metamodel/__init__.py b/m2isar/metamodel/__init__.py index 97522984..02e6061c 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.TYPE_UINT), ] intrinsics = {x.name: x for x in intrinsic_defs} diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index a0bffbf6..5c3225e7 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -17,6 +17,7 @@ from enum import Enum, IntEnum, auto from typing import TYPE_CHECKING, Any, Union from m2isar.frontends.coredsl2.expr_interpreter import ExprInterpreterVisitor +from m2isar.metamodel import type_info from .. import M2TypeError from .behav import BaseNode, Operation, Literal @@ -235,69 +236,15 @@ class FunctionThrows(IntEnum): 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 - - super().__init__(ptr) - - @property - def width(self): - """Returns the resolved width value.""" - - return get_const_or_val(self._width) - - def __str__(self) -> str: - return f'{super().__repr__()}, width={self.width}, signed={self.signed}' - - def __repr__(self): - return self.__str__() - - - @property - def actual_width(self): - """Returns the resolved width value rounded to the nearest multiple of 8.""" - - if self._width is None: - return None - - temp = 1 << (self.width - 1).bit_length() - return temp if temp >= 8 else 8 class FnParam(SizedRefOrConst): """A function parameter.""" - data_type: DataType + data_type: type_info.TypeKind _width: Union[int, "Constant", "BaseNode"] """The array width of this parameter.""" - def __init__(self, name, size, data_type: DataType, width=1): + def __init__(self, name, size, data_type: type_info.TypeKind, width=1): self.data_type = data_type self._width = width super().__init__(name, size) @@ -316,9 +263,9 @@ class Scalar(SizedRefOrConst): value: int static: bool - data_type: DataType + data_type: type_info.TypeKind - def __init__(self, name, value: int, static: bool, size, data_type: DataType): + def __init__(self, name, value: int, static: bool, size, data_type: type_info.TypeKind): self.value = value self.static = static self.data_type = data_type @@ -327,9 +274,9 @@ def __init__(self, name, value: int, static: bool, size, data_type: DataType): class Intrinsic(SizedRefOrConst): value: int - data_type: DataType + data_type: type_info.TypeKind - def __init__(self, name, size: ValOrConst, data_type: DataType, value: int = None): + def __init__(self, name, size: ValOrConst, data_type: type_info.TypeKind, value: int = None): self.data_type = data_type self.value = value super().__init__(name, size) @@ -393,13 +340,13 @@ class BitField(Named): """ range: RangeSpec - data_type: DataType + data_type: type_info.TypeKind - def __init__(self, name, _range: RangeSpec, data_type: DataType): + def __init__(self, name, _range: RangeSpec, data_type: type_info.TypeKind): self.range = _range self.data_type = data_type if not self.data_type: - self.data_type = DataType.U + self.data_type = type_info.TypeKind.TYPE_UINT super().__init__(name) @@ -414,7 +361,7 @@ class BitFieldDescr(SizedRefOrConst): the actual bits it is composed of, for that use BitField. """ - def __init__(self, name, size: ValOrConst, data_type: DataType): + def __init__(self, name, size: ValOrConst, data_type: type_info.TypeKind): self.data_type = data_type super().__init__(name, size) @@ -482,7 +429,7 @@ class Function(SizedRefOrConst): """A class representing a function.""" attributes: "dict[FunctionAttribute, list[BaseNode]]" - data_type: DataType + data_type: type_info.TypeKind args: "list[FnParam]" operation: "Operation" extern: bool @@ -492,7 +439,7 @@ class Function(SizedRefOrConst): throws: bool static: bool - def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", return_len, data_type: DataType, args: "list[FnParam]", + def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", return_len, data_type: type_info.TypeKind, args: "list[FnParam]", operation: "Operation", extern: bool=False, function_info: "FunctionInfo"=None): self.ext_name = "" diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index cf565051..0877ac71 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -21,7 +21,7 @@ """ from typing import TYPE_CHECKING, Union -from .type_info import PrimitiveKind +from .type_info import PrimitiveType, TypeKind if TYPE_CHECKING: @@ -95,21 +95,17 @@ def __init__(self, left: BaseNode, right: BaseNode, line_info=None) -> None: class Literal(BaseNode): - def __init__(self, value:int, kind : PrimitiveKind = None, size=None, base=10, line_info=None): + def __init__(self, value:int, kind : TypeKind = None, size=None, base=10, line_info=None): super().__init__(line_info) self._value : Union[int, str] = value - self.kind = kind # assigned during type checking + self.type = PrimitiveType(kind, size) # assigned during type checking #Optional type information (not always given) - if size is None: - self.size = value.bit_length() - else: - self.size = size # optional (important for ISA DSL!) self.base: int = base # 2, 10, 16 def __repr__(self): - return f"Literal(value={self.value}, kind={self.kind}, size={self.size}, base={self.base})" + return f"Literal(value={self.value}, type={self.type}, base={self.base})" def __int__(self): if isinstance(self.value, int): diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 302b36b6..dffab3e4 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -12,15 +12,49 @@ """ from enum import Enum, auto +from typing import Any, Union, Optional -class PrimitiveKind(Enum): - NONE = auto() - U = auto() - S = auto() - F = auto() - D = auto() - Q = auto() - BOOL = auto() - STR = auto() +class TypeKind(Enum): + TYPE_NONE = auto() # NumberLiteral with no type, e.g. 0 or 1 + TYPE_VOID = auto() + TYPE_UINT = auto() + TYPE_INT = auto() + TYPE_BOOL = auto() + TYPE_FLOAT = auto() + TYPE_STR = auto() + TYPE_ARRAY = auto() +class PrimitiveType: + def __init__(self, kind: TypeKind, size: int): + self.kind = kind + self.size = size + + +#removed get_const_or_val +class IntegerType(PrimitiveType): + def __init__(self, size: int, signed: bool, ptr: Any=False): + self.ptr = ptr + super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) + + @property + def actual_width(self): + """Returns the resolved width value rounded to the nearest multiple of 8.""" + + if self.size is None: + return None + + temp = 1 << (self.size - 1).bit_length() + return temp if temp >= 8 else 8 + + +class FloatType: + def __init__(self, exponent: int, mantissa: int, size: int): + self.exponent = exponent + self.mantissa = mantissa + self.size = size + +class ArrayType: + def __init__(self, element_type : Union[PrimitiveType, FloatType], length: int): + self.element_kind : Union[PrimitiveType, FloatType] = element_type + self.length = length # allow shaped later or TYPE_ARRAY in element_type? \ No newline at end of file diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index a50644d2..222b389e 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -61,17 +61,17 @@ def _(self, expr: behav.BinaryOperation, context): expr.right = self.generate(expr.right, context) if isinstance(expr.left, behav.Literal) and isinstance(expr.right, (behav.NamedReference, behav.IndexedReference)): - if expr.left.size < expr.right.reference.size: + if expr.left.type.size < expr.right.reference.size: expr.left.size = expr.right.reference.size if isinstance(expr.right, behav.Literal) and isinstance(expr.left, (behav.NamedReference, behav.IndexedReference)): - if expr.right.size < expr.left.reference.size: - expr.right.size = expr.left.reference.size + if expr.right.type.size < expr.left.reference.size: + expr.right.type.size = expr.left.reference.size 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.Literal(res, type_info.PrimitiveKind.U, max(expr.left.size, expr.right.size, res.bit_length())) + return behav.Literal(res, type_info.TypeKind.TYPE_UINT, max(expr.left.type.size, expr.right.type.size, res.bit_length())) if expr.op.value == "&&": if isinstance(expr.left, behav.Literal): @@ -130,8 +130,8 @@ def _(self, expr: behav.Assignment, context): expr.expr = self.generate(expr.expr, context) if isinstance(expr.expr, behav.Literal) and isinstance(expr.target, (behav.NamedReference, behav.IndexedReference)): - if expr.expr.size < expr.target.reference.size: - expr.expr.size = expr.target.reference.size + if expr.expr.type.size < expr.target.reference.size: + expr.expr.type.size = expr.target.reference.size return expr @@ -199,14 +199,14 @@ def _(self, expr: behav.UnaryOperation, context): if isinstance(expr.right, behav.Literal): # pylint: disable=eval-used res: int = eval(f"{expr.op.value}{expr.right.value}") - return behav.Literal(res, type_info.PrimitiveKind.U, max(expr.right.size, res.bit_length())) + return behav.Literal(res, type_info.TypeKind.TYPE_UINT, max(expr.right.type.size, res.bit_length())) return expr @generate.register def _(self, expr: behav.NamedReference, context): if isinstance(expr.reference, arch.Constant): - return behav.Literal(expr.reference.value, type_info.PrimitiveKind.S if expr.reference.signed else type_info.PrimitiveKind.U, expr.reference.size) + return behav.Literal(expr.reference.value, type_info.TypeKind.TYPE_INT if expr.reference.signed else type_info.TypeKind.TYPE_UINT, expr.reference.size) return expr @@ -223,11 +223,11 @@ def _(self, expr: behav.TypeConv, context): size = expr.size if size is None: assert expr.inferred_type is not None - size = expr.inferred_type.width + size = expr.inferred_type.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.TYPE_INT return expr.expr return expr diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index b86744c5..fa1ce14e 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -64,12 +64,12 @@ def _(self, expr: behav.BinaryOperation, context): logger.warning("Slice Operation needs inferred type. Skipping...") expr.inferred_type = 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.inferred_type, type_info.IntegerType) + assert isinstance(expr.right.inferred_type, type_info.IntegerType) + w1 = expr.left.inferred_type.size + w2 = expr.right.inferred_type.size + s1 = True if expr.left.inferred_type.kind == type_info.TypeKind.TYPE_INT else False + s2 = True if expr.right.inferred_type.kind == type_info.TypeKind.TYPE_INT else False if expr.op.value == "+": if not s1 and not s2: wr = max(w1, w2) + 1 @@ -118,12 +118,12 @@ def _(self, expr: behav.BinaryOperation, context): elif expr.op.value in [">>", "<<"]: wr = w1 sr = s1 - expr.inferred_type = arch.IntegerType(wr, sr, None) + expr.inferred_type = type_info.IntegerType(wr, sr) else: if expr.op.value in ["||", "&&"]: - expr.inferred_type = arch.IntegerType(1, False, None) # unsigned<1> / bool + expr.inferred_type = type_info.IntegerType(1, False) # unsigned<1> / bool elif expr.op.value in ["<", ">", "==", "!=", ">=", "<="]: - expr.inferred_type = arch.IntegerType(1, False, None) # unsigned<1> / bool + expr.inferred_type = type_info.IntegerType(1, False) # unsigned<1> / bool assert expr.inferred_type is not None return expr @@ -139,7 +139,7 @@ def _(self, expr: behav.SliceOperation, context): if expr.expr.inferred_type is None: logger.warning("Slice Operation needs inferred type. Skipping...") return expr - assert isinstance(expr.expr.inferred_type, arch.IntegerType) + assert isinstance(expr.expr.inferred_type, type_info.IntegerType) ty = expr.expr.inferred_type # For non-static slices, we cann not infer the type! if not isinstance(expr.left, behav.Literal): @@ -168,8 +168,9 @@ def _(self, expr: behav.ConcatOperation, context): if expr.right.inferred_type 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) + width = expr.left.inferred_type.size + expr.right.inferred_type.size + size = arch.get_const_or_val(width) + ty = type_info.IntegerType(size, False) expr.inferred_type = ty return expr @@ -179,20 +180,20 @@ def _(self, expr: behav.ConcatOperation, context): @generate.register def _(self, expr: behav.Literal, context): # type inference - assert((expr.size is not None)) - assert(expr.kind in [type_info.PrimitiveKind.U, type_info.PrimitiveKind.S]) - signed = True if expr.kind is type_info.PrimitiveKind.S else False + assert(expr.type.size is not None) + assert(expr.type.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT]) + signed = True if expr.type.kind is type_info.TypeKind.TYPE_INT else False - expr.inferred_type = arch.IntegerType(expr.size, signed, None) + expr.inferred_type = type_info.IntegerType(expr.type.size, signed) return expr @generate.register def _(self, expr: behav.ScalarDefinition, context): # type inference - signed = expr.scalar.data_type == arch.DataType.S + signed = expr.scalar.data_type == type_info.TypeKind.TYPE_INT width = expr.scalar.size - expr.inferred_type = arch.IntegerType(width, signed, None) + expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(width), signed) return expr @generate.register @@ -242,10 +243,10 @@ def _(self, expr: behav.Ternary, context): else_ty = expr.else_expr.inferred_type if then_ty and else_ty: # assert then_ty.signed == else_ty.signed - wt = then_ty.width - we = else_ty.width + wt = then_ty.size + we = else_ty.size wr = max(wt, we) - expr.inferred_type = arch.IntegerType(wr, True, None) + expr.inferred_type = type_info.IntegerType(wr, True) return expr @@ -260,13 +261,13 @@ def _(self, expr: behav.Return, context): def _(self, expr: behav.UnaryOperation, context): expr.right = self.generate(expr.right, context) if expr.right.inferred_type: - w1 = expr.right.inferred_type.width + w1 = expr.right.inferred_type.size if expr.op.value == "-": - inferred_type = arch.IntegerType(w1 + 1, True, None) + inferred_type = type_info.IntegerType(w1 + 1, True) elif expr.op.value == "~": - inferred_type = arch.IntegerType(w1, True, None) + inferred_type = type_info.IntegerType(w1, True) elif expr.op.value == "!": - inferred_type = arch.IntegerType(1, False, None) + inferred_type = type_info.IntegerType(1, False) else: inferred_type = None expr.inferred_type = inferred_type @@ -280,24 +281,24 @@ def _(self, expr: behav.NamedReference, context): # 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) + assert expr.reference.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + ty = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.data_type == type_info.TypeKind.TYPE_INT) 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) + assert dt in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + signed = dt == type_info.TypeKind.TYPE_INT + ty = type_info.IntegerType(arch.get_const_or_val(sz), signed) expr.inferred_type = ty elif isinstance(reference, arch.Memory): - expr.inferred_type = arch.IntegerType(reference.size, False, None) + expr.inferred_type = type_info.IntegerType(reference.size, False) 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) + assert expr.reference.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.data_type == type_info.TypeKind.TYPE_INT) elif isinstance(reference, arch.Constant): - expr.inferred_type = arch.IntegerType(reference.size, reference.signed, None) + expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.signed) else: assert False, "Unhandled reference" @@ -309,8 +310,8 @@ def _(self, expr: behav.IndexedReference, 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] + ty = type_info.TypeKind.TYPE_UINT # TODO: Memory class should keep track of dtype, not only size? + assert ty in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] single_mem_acc_size = expr.reference.size ## Simple eval check for ranged access. @@ -326,7 +327,7 @@ def _(self, expr: behav.IndexedReference, context): 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.IntegerType(size, ty == type_info.TypeKind.TYPE_INT) expr.inferred_type = ty_ @@ -340,9 +341,9 @@ def _(self, expr: behav.TypeConv, context): 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.IntegerType) + assert expr.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + ty.signed = expr.data_type == type_info.TypeKind.TYPE_INT if expr.size is not None: ty._width = expr.size @@ -354,13 +355,13 @@ def _(self, expr: behav.TypeConv, context): @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: + if expr.ref_or_name.data_type == type_info.TypeKind.TYPE_VOID: 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) + assert expr.ref_or_name.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + signed = expr.ref_or_name.data_type == type_info.TypeKind.TYPE_INT + width = arch.get_const_or_val(expr.ref_or_name.size) + expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(width), signed) expr.args = [self.generate(stmt, context) for stmt in expr.args] return expr From 5bc24ac62f010a8bc079b24ca6e35c93986db2c3 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Mon, 27 Apr 2026 14:23:32 +0200 Subject: [PATCH 03/33] [Backend] Use type_info Use type.kind instead of signed size instead of _width --- m2isar/backends/etiss/architecture_writer.py | 4 +- .../backends/etiss/instruction_generator.py | 4 +- .../backends/etiss/instruction_transform.py | 78 ++++++++++--------- m2isar/backends/etiss/instruction_utils.py | 8 +- m2isar/backends/isa_manual/writer.py | 8 +- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 92e4a8fe..f188516c 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -14,7 +14,7 @@ from mako.template import Template from ... import M2TypeError -from ...metamodel import arch, behav +from ...metamodel import arch, behav, type_info from . import BlockEndType from .instruction_generator import (generate_fields, generate_instruction_callback) @@ -190,7 +190,7 @@ 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_bitfield = arch.BitField("error_code", arch.RangeSpec(31, 0), type_info.TypeKind.TYPE_UINT) error_instr = arch.Instruction(f"trap_entry {bitsize}", {arch.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None) error_bitfield_descr = error_instr.fields.get("error_code") error_op = behav.Operation([ diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index 8044ff1f..9c01ae88 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -131,7 +131,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.data_type == type_info.TypeKind.TYPE_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};' @@ -237,7 +237,7 @@ def gen_rand_suffix(length: int = 8): [cond[0]], [ instr_def.operation.statements, - behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.PrimitiveKind.S)]) + behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.TypeKind.TYPE_NONE)]) ] ) ]) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 74e53af5..b8f560b6 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -195,7 +195,7 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): f'{data_type_map[expr.scalar.data_type]}{actual_size} {expr.scalar.name}', static, expr.scalar.size, - expr.scalar.data_type == arch.DataType.S, + expr.scalar.data_type == type_info.TypeKind.TYPE_INT, line_infos=expr.line_info, ) @@ -292,7 +292,7 @@ def _(self, expr: behav.FunctionCall, context: TransformerContext): 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.data_type == type_info.TypeKind.TYPE_INT # keep track of affected registers regs_affected = set(chain.from_iterable([arg.regs_affected for arg in fn_args])) @@ -569,7 +569,7 @@ 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.TYPE_INT if expr_str.signed else type_info.TypeKind.TYPE_UINT # if only data type should be changed assume width remains unchanged if expr.size is None: @@ -580,7 +580,7 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): 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: + if expr.data_type == type_info.TypeKind.TYPE_INT and expr_str.actual_size != expr_str.size: target_size = expr.actual_size if isinstance(expr.size, int): @@ -592,7 +592,7 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): else: code_str = f'({data_type_map[expr.data_type]}{expr.actual_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.TYPE_INT, expr_str.regs_affected, line_infos=[expr.line_info] + expr_str.line_infos) c.mem_ids = expr_str.mem_ids return c @@ -625,12 +625,12 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): elif isinstance(referred_var, arch.BitFieldDescr): # function argument - signed = referred_var.data_type == arch.DataType.S + signed = referred_var.data_type == type_info.TypeKind.TYPE_INT size = referred_var.size static = StaticType.READ elif isinstance(referred_var, arch.Scalar): - signed = referred_var.data_type == arch.DataType.S + signed = referred_var.data_type == type_info.TypeKind.TYPE_INT size = referred_var.size if context.static_scalars: static = referred_var.static @@ -642,7 +642,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): - signed = referred_var.data_type == arch.DataType.S + signed = referred_var.data_type == type_info.TypeKind.TYPE_INT size = referred_var.size static = StaticType.RW @@ -650,7 +650,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): if context.ignore_static: raise TypeError("intrinsic not allowed in function") - signed = referred_var.data_type == arch.DataType.S + signed = referred_var.data_type == type_info.TypeKind.TYPE_INT size = referred_var.size static = StaticType.READ @@ -693,7 +693,7 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): if arch.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: # generate memory access if main memory is accessed - size = expr.inferred_type._width + size = expr.inferred_type.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. @@ -790,40 +790,42 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): @generate.register def _(self, expr: behav.Literal, context: TransformerContext): - if expr.kind is type_info.PrimitiveKind.STR: + if expr.type.kind is type_info.TypeKind.TYPE_STR: return CodeString(f'"{expr.value}"', StaticType.READ, None, False, line_infos=expr.line_info) + + # old number NumberLiteral + elif expr.type.kind == type_info.TypeKind.TYPE_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: - # old number NumberLiteral - if expr.size is 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.kind in [type_info.PrimitiveKind.S, type_info.PrimitiveKind.U]) - lit = int(expr.value) - size = min(expr.size, 128) - sign = True if expr.kind is type_info.PrimitiveKind.S else False + assert(expr.type.kind in [type_info.TypeKind.TYPE_INT, type_info.TypeKind.TYPE_UINT]) + lit = int(expr.value) + if expr.type.size is None: + size = lit.bit_length() + size = min(expr.type.size, 128) + sign = True if expr.type.kind is type_info.TypeKind.TYPE_INT else False - minus = "" - if lit > 0 and sign and (lit >> (size - 1)) & 1: - minus = "-" + minus = "" + if lit > 0 and sign and (lit >> (size - 1)) & 1: + minus = "-" - _ = (lit + (1 << size)) % (1 << size) + _ = (lit + (1 << size)) % (1 << size) - postfix = "U" if not sign else "" - postfix += "LL" + postfix = "U" if not sign else "" + postfix += "LL" - ret = CodeString(minus + str(lit) + postfix, True, size, sign, line_infos=expr.line_info) - ret.is_literal = True - return ret + 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 e700483d..ef14fd88 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -13,15 +13,15 @@ from string import Template from ... import M2ValueError -from ...metamodel import arch +from ...metamodel import arch, type_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.TYPE_INT: 'etiss_int', + type_info.TypeKind.TYPE_UINT: 'etiss_uint', + type_info.TypeKind.TYPE_VOID: 'void' } diff --git a/m2isar/backends/isa_manual/writer.py b/m2isar/backends/isa_manual/writer.py index 53c5e90f..0508dbab 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.TYPE_UINT: self.write("unsigned") - elif data_type == arch.DataType.S: + elif data_type == type_info.TypeKind.TYPE_INT: self.write("signed") - elif data_type == arch.DataType.NONE: + elif data_type == type_info.TypeKind.TYPE_NONE: self.write("void") else: raise NotImplementedError(f"Unsupported type: {data_type}") From 0fb43f700f4b64446d80dc6c986c733bb82fba65 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Tue, 28 Apr 2026 09:01:48 +0200 Subject: [PATCH 04/33] [Metamodel] Remove IntLiteral occurances --- m2isar/backends/coverage/id_transform.py | 6 +----- m2isar/backends/isa_manual/visitor.py | 6 +----- m2isar/frontends/coredsl2/parser.py | 2 +- m2isar/metamodel/type_info.py | 4 ++-- m2isar/metamodel/utils/expr_simplifier.py | 8 ++++---- m2isar/transforms/infer_types/visitor.py | 12 ++++++------ m2isar/transforms/validate_behav/visitor.py | 6 +++--- 7 files changed, 18 insertions(+), 26 deletions(-) diff --git a/m2isar/backends/coverage/id_transform.py b/m2isar/backends/coverage/id_transform.py index 8748f67c..a31c539f 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/isa_manual/visitor.py b/m2isar/backends/isa_manual/visitor.py index d9bd3d84..7b6a1412 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 diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index f6d4178b..4f67840d 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -32,7 +32,7 @@ def try_eval_bool(operation, constants: "dict[str, arch.Constant]", memories: "d 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 diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index dffab3e4..b842c3b7 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -12,7 +12,7 @@ """ from enum import Enum, auto -from typing import Any, Union, Optional +from typing import Any, Union class TypeKind(Enum): @@ -33,7 +33,7 @@ def __init__(self, kind: TypeKind, size: int): #removed get_const_or_val class IntegerType(PrimitiveType): - def __init__(self, size: int, signed: bool, ptr: Any=False): + def __init__(self, size: int, signed: bool, ptr: Any=None): self.ptr = ptr super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index 222b389e..0d733f68 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -10,14 +10,14 @@ simplifications are done: * Resolvable :class:`m2isar.metamodel.arch.Constant` s are replaced by - `m2isar.metamodel.arch.IntLiteral` s representing their value + `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, type_info diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index fa1ce14e..c36ebba8 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -10,14 +10,14 @@ simplifications are done: * Resolvable :class:`m2isar.metamodel.arch.Constant` s are replaced by - `m2isar.metamodel.arch.IntLiteral` s representing their value + `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 @@ -401,11 +401,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 a5f7a55a..4b9c5439 100644 --- a/m2isar/transforms/validate_behav/visitor.py +++ b/m2isar/transforms/validate_behav/visitor.py @@ -50,14 +50,14 @@ def _(self, expr: behav.BinaryOperation, context): if op.value in ["<<", ">>", ">>>"] and expr.right.inferred_type.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 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: + if op.value == "<<" and isinstance(expr.right, behav.Literal) 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) From 58ba68cdc00cbdf00674d3454e341ab867bd42de Mon Sep 17 00:00:00 2001 From: jokap11 Date: Wed, 29 Apr 2026 13:14:02 +0200 Subject: [PATCH 05/33] [MetaModel] Switch Typing System ETISS broken because of actual_size --- m2isar/backends/etiss/architecture_writer.py | 14 +- .../backends/etiss/instruction_generator.py | 18 +- .../backends/etiss/instruction_transform.py | 74 ++++---- m2isar/backends/etiss/instruction_utils.py | 6 +- .../templates/etiss_arch_specific_cpp.mako | 14 +- .../templates/etiss_arch_specific_h.mako | 12 +- m2isar/backends/isa_manual/writer.py | 2 +- m2isar/backends/viewer/viewer.py | 8 +- .../coredsl2/architecture_model_builder.py | 17 +- .../coredsl2/behavior_model_builder.py | 12 +- m2isar/frontends/coredsl2/parser.py | 8 +- m2isar/frontends/coredsl2/utils.py | 2 + m2isar/metamodel/arch.py | 172 ++++++++++-------- m2isar/metamodel/behav.py | 8 +- m2isar/metamodel/type_info.py | 70 ++++++- m2isar/metamodel/utils/expr_preprocessor.py | 12 +- m2isar/metamodel/utils/expr_simplifier.py | 34 ++-- m2isar/metamodel/utils/function_throws.py | 40 ++-- m2isar/transforms/infer_types/visitor.py | 129 ++++++------- m2isar/transforms/validate_behav/visitor.py | 26 +-- 20 files changed, 395 insertions(+), 283 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index f188516c..cd56f913 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -26,7 +26,7 @@ def write_child_reg_def(reg: arch.Memory, regs: "list[str]"): """Recursively generate register declarations""" logger.debug("processing register %s", reg) - if arch.MemoryAttribute.IS_PC in reg.attributes or arch.MemoryAttribute.IS_MAIN_MEM in reg.attributes: + if type_info.MemoryAttribute.IS_PC in reg.attributes or type_info.MemoryAttribute.IS_MAIN_MEM in reg.attributes: logger.debug("this register is either the PC or main memory, skipping") return @@ -40,10 +40,10 @@ 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}") + regs.append(f"etiss_uint{reg.ty.actual_size} *{reg.name}{array_txt}") + regs.append(f"etiss_uint{reg.ty.actual_size} ins_{reg.name}{array_txt}") else: - regs.append(f"etiss_uint{reg.actual_size} {reg.name}{array_txt}") + regs.append(f"etiss_uint{reg.ty.actual_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')) @@ -177,12 +177,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 type_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 type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break @@ -207,7 +207,7 @@ 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] + attr = core.global_irq_en_memory.attributes[type_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 9c01ae88..701a7070 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -21,7 +21,7 @@ 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]}{arg.ty.actual_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 @@ -42,9 +42,9 @@ 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'{fn_def.ty.actual_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, @@ -66,7 +66,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 type_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 +105,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 = instr_def.fields[enc.name].ty.actual_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 +131,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 == type_info.TypeKind.TYPE_INT and field_descr.size < core_default_width: + if field_descr.ty.kind == type_info.TypeKind.TYPE_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};' @@ -196,7 +196,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 type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index b8f560b6..f8d6624e 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -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 type_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 @@ -188,14 +188,14 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): else: static = StaticType.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 == type_info.TypeKind.TYPE_INT, + expr.scalar.ty.size, + expr.scalar.ty.kind == type_info.TypeKind.TYPE_INT, line_infos=expr.line_info, ) @@ -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 type_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 type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: context.generates_exception = True - if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes: + if type_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,7 +251,7 @@ 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 == type_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)] @@ -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 type_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 == type_info.TypeKind.TYPE_INT + signed = fn.ty.kind == type_info.TypeKind.TYPE_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 @@ -620,18 +620,19 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): 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.BitFieldDescr): # function argument - signed = referred_var.data_type == type_info.TypeKind.TYPE_INT - size = referred_var.size + signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + size = referred_var.ty.size static = StaticType.READ elif isinstance(referred_var, arch.Scalar): - signed = referred_var.data_type == type_info.TypeKind.TYPE_INT - size = referred_var.size + assert isinstance(referred_var.ty, type_info.PrimitiveType) + signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + size = referred_var.ty.size if context.static_scalars: static = referred_var.static @@ -642,16 +643,16 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): - signed = referred_var.data_type == type_info.TypeKind.TYPE_INT - size = referred_var.size + signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + size = referred_var.ty.size static = StaticType.RW elif isinstance(referred_var, arch.Intrinsic): if context.ignore_static: raise TypeError("intrinsic not allowed in function") - signed = referred_var.data_type == type_info.TypeKind.TYPE_INT - size = referred_var.size + signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + size = referred_var.ty.size static = StaticType.READ if referred_var == context.intrinsics["__encoding_size"]: @@ -679,7 +680,7 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): if isinstance(referred_mem, arch.Memory): context.used_arch_data = True - size = referred_mem.size + size = referred_mem.ty.size # convert static index expression index_code = index.code @@ -691,9 +692,9 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): else: static = StaticType.NONE - if arch.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: + if type_info.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: # generate memory access if main memory is accessed - size = expr.inferred_type.size + 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. @@ -715,7 +716,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 type_info.MemoryAttribute.IS_MAIN_REG in referred_mem.attributes: c.regs_affected.add(index_code) return c @@ -790,11 +791,11 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): @generate.register def _(self, expr: behav.Literal, context: TransformerContext): - if expr.type.kind is type_info.TypeKind.TYPE_STR: + if expr.ty.kind is type_info.TypeKind.TYPE_STR: return CodeString(f'"{expr.value}"', StaticType.READ, None, False, line_infos=expr.line_info) # old number NumberLiteral - elif expr.type.kind == type_info.TypeKind.TYPE_NONE: + elif expr.ty.kind == type_info.TypeKind.TYPE_NONE: lit = int(expr.value) size = min(lit.bit_length(), 64) sign = lit < 0 @@ -807,16 +808,23 @@ def _(self, expr: behav.Literal, context: TransformerContext): return CodeString(str(twocomp_lit) + postfix, True, size, sign, line_infos=expr.line_info) # IntLiteral else: - assert(expr.type.kind in [type_info.TypeKind.TYPE_INT, type_info.TypeKind.TYPE_UINT]) + assert(expr.ty.kind in [type_info.TypeKind.TYPE_INT, type_info.TypeKind.TYPE_UINT]) lit = int(expr.value) - if expr.type.size is None: + if expr.ty.size is None: size = lit.bit_length() - size = min(expr.type.size, 128) - sign = True if expr.type.kind is type_info.TypeKind.TYPE_INT else False + size = min(expr.ty.size, 128) + + if expr.value < 0 or expr.ty.kind == type_info.TypeKind.TYPE_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 = "-" + sign = True _ = (lit + (1 << size)) % (1 << size) diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index ef14fd88..5b9c9986 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -165,7 +165,7 @@ def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, a 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: + if type_info.MemoryAttribute.IS_PC in mem_descr.attributes: self.pc_mem = mem_descr break @@ -173,9 +173,9 @@ def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, a 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 type_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 type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn_def.attributes: self.mem_raise_fn = fn_def self.generates_exception = False diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 647dd38b..b36db663 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -167,18 +167,18 @@ etiss::InterruptVector *${core_name}Arch::createInterruptVector(ETISS_CPU *cpu) % 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; + 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 } @@ -191,7 +191,7 @@ void ${core_name}Arch::deleteInterruptVector(etiss::InterruptVector *vec, ETISS_ 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}); + 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 2449af46..1a2b257a 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -45,7 +45,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.size / 8)} ), // clang-format on gprid_(gprid) @@ -59,7 +59,7 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int(main_reg.size / 8)} + ${int(main_reg.ty.size / 8)} ), // clang-format on gprid_(gprid) @@ -85,9 +85,9 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field // clang-format off 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.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.size}) val; % endif // clang-format on } @@ -103,7 +103,7 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field "instructionPointer", "instructionPointer", R|W, - ${int(main_reg.size / 8)} + ${int(main_reg.ty.size / 8)} ) // clang-format on { @@ -123,7 +123,7 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field { // clang-format off 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.size}) val; // clang-format on } }; diff --git a/m2isar/backends/isa_manual/writer.py b/m2isar/backends/isa_manual/writer.py index 0508dbab..1fd2a986 100644 --- a/m2isar/backends/isa_manual/writer.py +++ b/m2isar/backends/isa_manual/writer.py @@ -149,7 +149,7 @@ def write_type(self, data_type, size): self.write("unsigned") elif data_type == type_info.TypeKind.TYPE_INT: self.write("signed") - elif data_type == type_info.TypeKind.TYPE_NONE: + elif data_type == type_info.TypeKind.TYPE_VOID: self.write("void") else: raise NotImplementedError(f"Unsupported type: {data_type}") diff --git a/m2isar/backends/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index fb897fe8..1d311ca0 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -115,12 +115,12 @@ def main(): # 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"{mem_def.range.upper}:{mem_def.range.lower} ({mem_def.range.length}), {mem_def.ty.size}",)) # add memory aliases to tree 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(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 auxillary attributes tree.insert(core_id, tk.END, text="Main Memory Object", values=(core_def.main_memory,)) @@ -133,7 +133,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)) @@ -150,7 +150,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 da8a92b5..e700a6ea 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -67,7 +67,7 @@ def visitBit_value(self, ctx: CoreDSL2Parser.Bit_valueContext): """Generate a fixed encoding part.""" val = self.visit(ctx.value) - return arch.BitVal(val.type.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.""" @@ -197,8 +197,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 type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in attributes: + attributes[type_info.FunctionAttribute.ETISS_NEEDS_ARCH] = [] # decode return type and name type_ = self.visit(ctx.type_) @@ -262,6 +262,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] @@ -355,7 +356,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init is not None: init = self.visit(decl.init) - c = arch.Constant(name, init, [], type_.size, True if type_.kind == type_info.TypeKind.TYPE_INT else False) + signed = False if type_.kind == type_info.TypeKind.TYPE_INT else False + + c = arch.Constant(name, init, [], type_.size, signed) self._constants[name] = c ret_decls.append(c) @@ -388,7 +391,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if init is not None: m._initval[None] = exprInterpretVisitor.generate(init, None) - if arch.MemoryAttribute.IS_MAIN_REG in attributes: + if type_info.MemoryAttribute.IS_MAIN_REG in attributes: self._main_reg_file = m self._memories[name] = m @@ -513,8 +516,8 @@ def visitAttribute(self, ctx: CoreDSL2Parser.AttributeContext): # 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()) + type_info.MemoryAttribute._member_map_.get(name.upper()) or \ + type_info.FunctionAttribute._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 f32e50ac..d7c5f766 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -91,7 +91,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 type_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 +127,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_.size, type_.kind) + s = arch.Scalar(name, type_.kind, type_.size, None, StaticType.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) @@ -136,7 +136,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init: init = self.visit(decl.init) else: - init = behav.Literal(0, type_info.TypeKind.TYPE_NONE) + 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) @@ -386,15 +386,15 @@ def visitCast_expression(self, ctx: CoreDSL2Parser.Cast_expressionContext): expr = self.visit(ctx.right) if ctx.type_: type_ = self.visit(ctx.type_) - sign = type_.kind + kind = type_.kind size = type_.size if ctx.sign: sign = self.visit(ctx.sign) - sign = type_info.TypeKind.TYPE_INT if sign else type_info.TypeKind.TYPE_UINT + kind = type_info.TypeKind.TYPE_INT if sign else type_info.TypeKind.TYPE_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.""" diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 4f67840d..59ebe8c9 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -117,7 +117,8 @@ def main(): 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.ty.size = arch.get_const_or_val(mem_def.ty.size) + mem_def._size = mem_def.ty.size mem_def.range._lower_base = mem_def.range.lower_base mem_def.range._upper_base = mem_def.range.upper_base @@ -139,9 +140,10 @@ def main(): 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") diff --git a/m2isar/frontends/coredsl2/utils.py b/m2isar/frontends/coredsl2/utils.py index 30f8a4bd..d1966a09 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/arch.py b/m2isar/metamodel/arch.py index 5c3225e7..986c2ab8 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -202,17 +202,6 @@ 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() - 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() @@ -225,29 +214,18 @@ class InstrAttribute(Enum): 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 FnParam(SizedRefOrConst): +class FnParam(Named): """A function parameter.""" - data_type: type_info.TypeKind + ty: type_info.IntegerType _width: Union[int, "Constant", "BaseNode"] """The array width of this parameter.""" - def __init__(self, name, size, data_type: type_info.TypeKind, width=1): - self.data_type = data_type + def __init__(self, name, size, kind: type_info.TypeKind, width=1): + self.ty = type_info.IntegerType(size, True if kind == type_info.TypeKind.TYPE_INT else False) self._width = width - super().__init__(name, size) + super().__init__(name) @property def width(self): @@ -256,50 +234,95 @@ def width(self): return get_const_or_val(self._width) def __str__(self) -> str: - return f'{super().__str__()}, data_type={self.data_type}' + return f'{super().__str__()}, type={self.ty}' -class Scalar(SizedRefOrConst): +class Scalar(Named): """A scalar variable object, used mainly in behavior descriptions.""" - value: int + ty: Union[type_info.PrimitiveType] static: bool - data_type: type_info.TypeKind + value: int + + def __init__(self, + name, + kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], + size : int, + value: int, + static: bool, # Compile Time information + storage=None + ): + self.ty = type_info.PrimitiveType(kind, size) if isinstance(kind, type_info.TypeKind) else kind - def __init__(self, name, value: int, static: bool, size, data_type: type_info.TypeKind): + self.static = static + + # optional: only for constants/literals self.value = value + + # optional: backend info (register, memory, etc.) + self.storage = storage + super().__init__(name) + + +class Array(Named): + """A variable object for an Array, used mainly in behavior descriptions.""" + + ty: type_info.ArrayType + static: bool + values: list[int] + + def __init__(self, + name, + kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], + size : int, + length : int, + values: list[int], + static: bool, # Compile Time information + storage=None + ): + # Explcit casting for now allowed??? + self.ty = type_info.ArrayType(type_info.PrimitiveType(kind, size), length) if isinstance(kind, type_info.TypeKind) else kind + self.static = static - self.data_type = data_type - super().__init__(name, size) -class Intrinsic(SizedRefOrConst): + # optional: only for constants/literals + self.values = values + + # optional: backend info (register, memory, etc.) + self.storage = storage + super().__init__(name) + + +class Intrinsic(Named): value: int - data_type: type_info.TypeKind + ty: type_info.PrimitiveType - def __init__(self, name, size: ValOrConst, data_type: type_info.TypeKind, value: int = None): - self.data_type = data_type + def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind, value: int = None): + self.ty = type_info.PrimitiveType(kind, get_const_or_val(size)) self.value = value - super().__init__(name, size) + super().__init__(name) -class Memory(SizedRefOrConst): +class Memory(Named): """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]]" + ty : type_info.MemoryType range: RangeSpec + attributes: "dict[type_info.MemoryAttribute, list[BaseNode]]" children: "list[Memory]" parent: Union['Memory', None] _initval: "dict[int, Union[int, Constant, BaseNode]]" - def __init__(self, name, range_: RangeSpec, size, attributes: "dict[MemoryAttribute, list[BaseNode]]"): + def __init__(self, name, range_: RangeSpec, size, attributes: "dict[type_info.MemoryAttribute, list[BaseNode]]"): + self.ty = type_info.MemoryType(size) self.attributes = attributes if attributes else {} self.range = range_ self.children = [] self.parent = None self._initval = {} - super().__init__(name, size) + super().__init__(name) def initval(self, idx=None): """Return the initial value for the given index.""" @@ -318,12 +341,12 @@ def data_range(self): @property def is_pc(self): """Return true if this memory is tagged as being the program counter.""" - return MemoryAttribute.IS_PC in self.attributes + return type_info.MemoryAttribute.IS_PC in self.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 + return type_info.MemoryAttribute.IS_MAIN_MEM in self.attributes @dataclasses.dataclass class BitVal: @@ -340,31 +363,30 @@ class BitField(Named): """ range: RangeSpec - data_type: type_info.TypeKind + kind: type_info.TypeKind - def __init__(self, name, _range: RangeSpec, data_type: type_info.TypeKind): + 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 = type_info.TypeKind.TYPE_UINT + self.ty = type_info.BitFieldType(kind) + if not kind: + self.ty = type_info.BitFieldType(type_info.TypeKind.TYPE_UINT) super().__init__(name) 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): +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.IntegerType(get_const_or_val(size), True if kind == type_info.TypeKind.TYPE_INT else False) - def __init__(self, name, size: ValOrConst, data_type: type_info.TypeKind): - self.data_type = data_type - - super().__init__(name, size) + super().__init__(name) class Instruction(SizedRefOrConst): """A class representing an instruction.""" @@ -408,12 +430,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 @@ -425,11 +447,11 @@ 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: type_info.TypeKind + attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]" + ty: type_info.FunctionType args: "list[FnParam]" operation: "Operation" extern: bool @@ -439,12 +461,12 @@ class Function(SizedRefOrConst): throws: bool static: bool - def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", return_len, data_type: type_info.TypeKind, args: "list[FnParam]", + def __init__(self, name, attributes: "dict[type_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: @@ -466,10 +488,10 @@ def __init__(self, name, attributes: "dict[FunctionAttribute, list[BaseNode]]", self.static = False 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 @@ -492,7 +514,7 @@ def extract_memory_alias(memories: "list[Memory]"): return parents, aliases class AlwaysBlock(Named): - attributes: "dict[FunctionAttribute, list[BaseNode]]" + attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]" operation: "Operation" def __init__(self, name: str, attributes, operation): @@ -550,19 +572,19 @@ 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: + if type_info.MemoryAttribute.IS_MAIN_REG in mem.attributes: self.main_reg_file = mem - elif MemoryAttribute.IS_PC in mem.attributes: + elif type_info.MemoryAttribute.IS_PC in mem.attributes: self.pc_memory = mem - elif MemoryAttribute.IS_MAIN_MEM in mem.attributes: + elif type_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: self.main_memory = mem - elif MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN in mem.attributes: + elif type_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 type_info.MemoryAttribute.ETISS_IS_PROCNO in mem.attributes: self.procno_memory = mem - elif MemoryAttribute.ETISS_IS_IRQ_EN in mem.attributes: + elif type_info.MemoryAttribute.ETISS_IS_IRQ_EN in mem.attributes: self.irq_en_memory = mem - elif MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: + elif type_info.MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: self.irq_pending_memory = mem super().__init__(name) diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index 0877ac71..d298e058 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -38,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() @@ -95,17 +95,17 @@ def __init__(self, left: BaseNode, right: BaseNode, line_info=None) -> None: class Literal(BaseNode): - def __init__(self, value:int, kind : TypeKind = None, size=None, base=10, line_info=None): + def __init__(self, value:int, kind : TypeKind = TypeKind.TYPE_INT, size=None, base=10, line_info=None): super().__init__(line_info) self._value : Union[int, str] = value - self.type = PrimitiveType(kind, size) # assigned during type checking + self.ty = PrimitiveType(kind, size) # assigned during type checking #Optional type information (not always given) self.base: int = base # 2, 10, 16 def __repr__(self): - return f"Literal(value={self.value}, type={self.type}, base={self.base})" + return f"Literal(value={self.value}, type={self.ty}, base={self.base})" def __int__(self): if isinstance(self.value, int): diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index b842c3b7..38107f73 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -11,7 +11,7 @@ anything but the functional behavior of functions and instructions. """ -from enum import Enum, auto +from enum import Enum, IntEnum, auto from typing import Any, Union @@ -30,6 +30,9 @@ def __init__(self, kind: TypeKind, size: int): self.kind = kind self.size = size +class BitFieldType(): + def __init__(self, kind: TypeKind): + self.kind = kind #removed get_const_or_val class IntegerType(PrimitiveType): @@ -38,8 +41,8 @@ def __init__(self, size: int, signed: bool, ptr: Any=None): super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) @property - def actual_width(self): - """Returns the resolved width value rounded to the nearest multiple of 8.""" + def actual_size(self): + """Returns the resolved size value rounded to the nearest multiple of 8.""" if self.size is None: return None @@ -57,4 +60,63 @@ def __init__(self, exponent: int, mantissa: int, size: int): class ArrayType: def __init__(self, element_type : Union[PrimitiveType, FloatType], length: int): self.element_kind : Union[PrimitiveType, FloatType] = element_type - self.length = length # allow shaped later or TYPE_ARRAY in element_type? \ No newline at end of file + self.length = length # allow shaped later or TYPE_ARRAY in element_type? + + +class MemoryAttribute(Enum): + IS_PC = auto() + IS_MAIN_MEM = auto() + IS_MAIN_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 MemoryType: + size : int + + def __init__(self, size: int): + self.size = size + + @property + def actual_size(self): + """Returns the resolved size value rounded to the nearest multiple of 8.""" + + if self.size is None: + return None + + temp = 1 << (self.size - 1).bit_length() + return temp if temp >= 8 else 8 + + +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 FunctionType(): + size : int + kind : TypeKind + + def __init__(self, size: int, kind: TypeKind): + self.size = size + self.kind = kind + + @property + def actual_size(self): + """Returns the resolved size value rounded to the nearest multiple of 8.""" + + if self.size is None: + return None + + temp = 1 << (self.size - 1).bit_length() + return temp if temp >= 8 else 8 \ No newline at end of file diff --git a/m2isar/metamodel/utils/expr_preprocessor.py b/m2isar/metamodel/utils/expr_preprocessor.py index c0541c9b..25ec3973 100644 --- a/m2isar/metamodel/utils/expr_preprocessor.py +++ b/m2isar/metamodel/utils/expr_preprocessor.py @@ -14,7 +14,7 @@ from itertools import chain from ... import M2ValueError -from .. import arch +from .. import arch, type_info from . import ScalarStaticnessContext from .expr_simplifier import ExprSimplifierVisitor from .function_staticness import FunctionStaticnessVisitor @@ -48,7 +48,7 @@ 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 type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes context = ScalarStaticnessContext() logger.debug("examining scalar staticness for fn %s", fn_name) @@ -56,14 +56,14 @@ def process_functions(core: arch.CoreDef): 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 type_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes and type_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 (type_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or type_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 type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes: + if type_info.FunctionAttribute.ETISS_STATICFN in fn_def.attributes: fn_def.static = True else: diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index 0d733f68..f3992e0b 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -61,17 +61,21 @@ def _(self, expr: behav.BinaryOperation, context): expr.right = self.generate(expr.right, context) if isinstance(expr.left, behav.Literal) and isinstance(expr.right, (behav.NamedReference, behav.IndexedReference)): - if expr.left.type.size < expr.right.reference.size: - expr.left.size = expr.right.reference.size + 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.Literal) and isinstance(expr.left, (behav.NamedReference, behav.IndexedReference)): - if expr.right.type.size < expr.left.reference.size: - expr.right.type.size = expr.left.reference.size + if expr.right.ty.size < arch.get_const_or_val(expr.left.reference.ty.size): + expr.right.ty.size = arch.get_const_or_val(expr.left.reference.ty.size) 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.Literal(res, type_info.TypeKind.TYPE_UINT, max(expr.left.type.size, expr.right.type.size, res.bit_length())) + if res < 0 or expr.left.ty.kind == type_info.TypeKind.TYPE_INT or expr.right.ty.kind == type_info.TypeKind.TYPE_INT: + kind = type_info.TypeKind.TYPE_INT + else: + kind = type_info.TypeKind.TYPE_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.Literal): @@ -130,8 +134,8 @@ def _(self, expr: behav.Assignment, context): expr.expr = self.generate(expr.expr, context) if isinstance(expr.expr, behav.Literal) and isinstance(expr.target, (behav.NamedReference, behav.IndexedReference)): - if expr.expr.type.size < expr.target.reference.size: - expr.expr.type.size = expr.target.reference.size + if expr.expr.ty.size < arch.get_const_or_val(expr.target.reference.ty.size): + expr.expr.ty.size = arch.get_const_or_val(expr.target.reference.ty.size) return expr @@ -199,14 +203,22 @@ def _(self, expr: behav.UnaryOperation, context): if isinstance(expr.right, behav.Literal): # pylint: disable=eval-used res: int = eval(f"{expr.op.value}{expr.right.value}") - return behav.Literal(res, type_info.TypeKind.TYPE_UINT, max(expr.right.type.size, res.bit_length())) + if res < 0: + kind = type_info.TypeKind.TYPE_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.Literal(expr.reference.value, type_info.TypeKind.TYPE_INT if expr.reference.signed else type_info.TypeKind.TYPE_UINT, expr.reference.size) + if expr.reference.signed: + kind = type_info.TypeKind.TYPE_INT + else: + kind = type_info.TypeKind.TYPE_UINT + return behav.Literal(expr.reference.value, kind, arch.get_const_or_val(expr.reference.size)) return expr @@ -222,8 +234,8 @@ def _(self, expr: behav.TypeConv, context): if isinstance(expr.expr, behav.Literal): size = expr.size if size is None: - assert expr.inferred_type is not None - size = expr.inferred_type.size + 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 diff --git a/m2isar/metamodel/utils/function_throws.py b/m2isar/metamodel/utils/function_throws.py index 96c545d5..f7573b72 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 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, type_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, type_info.FunctionThrows.NO) @generate.register def _(self, expr: behav.BinaryOperation, context): @@ -66,15 +66,15 @@ def _(self, expr: behav.ConcatOperation, context): @generate.register def _(self, expr: behav.Literal, context): - return arch.FunctionThrows.NO + return type_info.FunctionThrows.NO @generate.register def _(self, expr: behav.ScalarDefinition, context): - return arch.FunctionThrows.NO + return type_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Break, context): - return arch.FunctionThrows.NO + return type_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Assignment, context): @@ -90,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 type_info.FunctionThrows.MAYBE if reduce(or_, conds) else type_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Loop, context): @@ -113,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 type_info.FunctionThrows.NO @generate.register def _(self, expr: behav.UnaryOperation, context): @@ -123,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 type_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: + return type_info.FunctionThrows.YES - return arch.FunctionThrows.NO + return type_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 type_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: + return type_info.FunctionThrows.YES return self.generate(expr.index, context) @@ -144,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", type_info.FunctionThrows.NO) + args.append(throws if isinstance(throws, type_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", type_info.FunctionThrows.NO) + args.append(throws if isinstance(throws, type_info.FunctionThrows) else cast_to_throws(throws)) return reduce(or_, args) @@ -164,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) -> type_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 type_info.FunctionThrows.YES if throws else type_info.FunctionThrows.NO + return type_info.FunctionThrows(throws) diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index c36ebba8..d21e4951 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -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, type_info.IntegerType) - assert isinstance(expr.right.inferred_type, type_info.IntegerType) - w1 = expr.left.inferred_type.size - w2 = expr.right.inferred_type.size - s1 = True if expr.left.inferred_type.kind == type_info.TypeKind.TYPE_INT else False - s2 = True if expr.right.inferred_type.kind == type_info.TypeKind.TYPE_INT else False + assert isinstance(expr.left.ty, type_info.IntegerType) + assert isinstance(expr.right.ty, type_info.IntegerType) + 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.TYPE_INT else False + s2 = True if expr.right.ty.kind == type_info.TypeKind.TYPE_INT else False if expr.op.value == "+": if not s1 and not s2: wr = max(w1, w2) + 1 @@ -118,13 +118,13 @@ def _(self, expr: behav.BinaryOperation, context): elif expr.op.value in [">>", "<<"]: wr = w1 sr = s1 - expr.inferred_type = type_info.IntegerType(wr, sr) + expr.ty = type_info.IntegerType(wr, sr) else: if expr.op.value in ["||", "&&"]: - expr.inferred_type = type_info.IntegerType(1, False) # unsigned<1> / bool + expr.ty = type_info.IntegerType(1, False) # unsigned<1> / bool elif expr.op.value in ["<", ">", "==", "!=", ">=", "<="]: - expr.inferred_type = type_info.IntegerType(1, False) # unsigned<1> / bool - assert expr.inferred_type is not None + expr.ty = type_info.IntegerType(1, False) # unsigned<1> / bool + assert expr.ty is not None return expr @@ -136,11 +136,11 @@ 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, type_info.IntegerType) - ty = expr.expr.inferred_type + assert isinstance(expr.expr.ty, type_info.IntegerType) + ty = expr.expr.ty # For non-static slices, we cann not infer the type! if not isinstance(expr.left, behav.Literal): logger.warning("Can not infer type of non-static slice operation. Skipping...") @@ -152,8 +152,8 @@ def _(self, expr: behav.SliceOperation, context): 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_ + ty_.size = width + expr.ty = ty_ return expr @@ -162,16 +162,16 @@ 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.size + expr.right.inferred_type.size + 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.IntegerType(size, False) - expr.inferred_type = ty + expr.ty = ty return expr @@ -180,20 +180,20 @@ def _(self, expr: behav.ConcatOperation, context): @generate.register def _(self, expr: behav.Literal, context): # type inference - assert(expr.type.size is not None) - assert(expr.type.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT]) - signed = True if expr.type.kind is type_info.TypeKind.TYPE_INT else False - - expr.inferred_type = type_info.IntegerType(expr.type.size, signed) + assert(expr.ty.size is not None) + assert(expr.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT]) + signed = True if expr.ty.kind == type_info.TypeKind.TYPE_INT else False #or arch.get_const_or_val(expr.value) < 0 else False + expr.ty = type_info.IntegerType(expr.ty.size, signed) return expr @generate.register def _(self, expr: behav.ScalarDefinition, context): # type inference - signed = expr.scalar.data_type == type_info.TypeKind.TYPE_INT - width = expr.scalar.size - expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(width), signed) + assert isinstance(expr.scalar.ty, type_info.PrimitiveType) + assert expr.scalar.ty.size is not None + assert expr.scalar.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + expr.ty = expr.scalar.ty return expr @generate.register @@ -205,7 +205,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 @@ -239,14 +239,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.size - we = else_ty.size + 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 = type_info.IntegerType(wr, True) + expr.ty = type_info.IntegerType(wr, True) return expr @@ -260,17 +260,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.size + if expr.right.ty: + w1 = expr.right.ty.size if expr.op.value == "-": - inferred_type = type_info.IntegerType(w1 + 1, True) + ty = type_info.IntegerType(w1 + 1, True) elif expr.op.value == "~": - inferred_type = type_info.IntegerType(w1, True) + ty = type_info.IntegerType(w1, True) elif expr.op.value == "!": - inferred_type = type_info.IntegerType(1, False) + ty = type_info.IntegerType(1, False) else: - inferred_type = None - expr.inferred_type = inferred_type + ty = None + expr.ty = ty return expr @@ -281,24 +281,25 @@ def _(self, expr: behav.NamedReference, context): # type inference # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): - assert expr.reference.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] - ty = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.data_type == type_info.TypeKind.TYPE_INT) - expr.inferred_type = ty + assert expr.reference.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) + expr.ty = ty elif isinstance(reference, arch.Scalar): - dt = reference.data_type - sz = reference.size + assert isinstance(reference.ty, type_info.PrimitiveType) + dt = reference.ty.kind + sz = reference.ty.size assert dt in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] signed = dt == type_info.TypeKind.TYPE_INT - ty = type_info.IntegerType(arch.get_const_or_val(sz), signed) - expr.inferred_type = ty + ty = type_info.IntegerType(sz, signed) + expr.ty = ty elif isinstance(reference, arch.Memory): - expr.inferred_type = type_info.IntegerType(reference.size, False) + expr.ty = type_info.IntegerType(reference.ty.size, False) elif isinstance(reference, arch.Intrinsic): - assert expr.reference.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] - expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.data_type == type_info.TypeKind.TYPE_INT) + assert expr.reference.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) elif isinstance(reference, arch.Constant): - expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(reference.size), reference.signed) + expr.ty = type_info.IntegerType(reference.size, reference.signed) else: assert False, "Unhandled reference" @@ -312,7 +313,7 @@ def _(self, expr: behav.IndexedReference, context): assert isinstance(expr.reference, arch.Memory) ty = type_info.TypeKind.TYPE_UINT # TODO: Memory class should keep track of dtype, not only size? assert ty in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] - single_mem_acc_size = expr.reference.size + single_mem_acc_size = expr.reference.ty.size ## Simple eval check for ranged access. # Little-endian interpretation: @@ -329,7 +330,7 @@ def _(self, expr: behav.IndexedReference, context): ty_ = type_info.IntegerType(size, ty == type_info.TypeKind.TYPE_INT) - expr.inferred_type = ty_ + expr.ty = ty_ return expr @@ -337,7 +338,7 @@ 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 @@ -345,23 +346,23 @@ def _(self, expr: behav.TypeConv, context): assert expr.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] ty.signed = expr.data_type == type_info.TypeKind.TYPE_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 == type_info.TypeKind.TYPE_VOID: + if expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_VOID: signed = None else: - assert expr.ref_or_name.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] - signed = expr.ref_or_name.data_type == type_info.TypeKind.TYPE_INT - width = arch.get_const_or_val(expr.ref_or_name.size) - expr.inferred_type = type_info.IntegerType(arch.get_const_or_val(width), signed) + assert expr.ref_or_name.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + signed = expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_INT + width = arch.get_const_or_val(expr.ref_or_name.ty.size) + expr.ty = type_info.IntegerType(arch.get_const_or_val(width), signed) expr.args = [self.generate(stmt, context) for stmt in expr.args] return expr @@ -381,7 +382,7 @@ def _(self, expr: behav.Group, context): return expr.expr # type inference - expr.inferred_type = expr.expr.inferred_type + expr.ty = expr.expr.ty return expr diff --git a/m2isar/transforms/validate_behav/visitor.py b/m2isar/transforms/validate_behav/visitor.py index 4b9c5439..840df19e 100644 --- a/m2isar/transforms/validate_behav/visitor.py +++ b/m2isar/transforms/validate_behav/visitor.py @@ -43,13 +43,13 @@ 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 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.Literal) and expr.right.value == 0: @@ -57,8 +57,8 @@ def _(self, expr: behav.BinaryOperation, context): 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.Literal) 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 @@ -80,12 +80,12 @@ def _(self, expr: behav.Literal, context): 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): From f606f808efd7bd64c4c0ec933cebb47214c38f42 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Wed, 29 Apr 2026 13:26:13 +0200 Subject: [PATCH 06/33] [MetaModel] Move etiss attribute into ETISS backend --- m2isar/backends/etiss/architecture_writer.py | 8 +++-- .../backends/etiss/instruction_generator.py | 7 +++-- .../backends/etiss/instruction_transform.py | 19 ++++++------ m2isar/backends/etiss/instruction_utils.py | 5 +--- m2isar/metamodel/arch.py | 14 +-------- m2isar/metamodel/type_info.py | 29 ------------------- 6 files changed, 20 insertions(+), 62 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index cd56f913..ff4464d2 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -13,6 +13,8 @@ from mako.template import Template + +from .instruction_utils import actual_size from ... import M2TypeError from ...metamodel import arch, behav, type_info from . import BlockEndType @@ -40,10 +42,10 @@ 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.ty.actual_size} *{reg.name}{array_txt}") - regs.append(f"etiss_uint{reg.ty.actual_size} ins_{reg.name}{array_txt}") + regs.append(f"etiss_uint{actual_size(reg.ty.size)} *{reg.name}{array_txt}") + regs.append(f"etiss_uint{actual_size(reg.ty.size)} ins_{reg.name}{array_txt}") else: - regs.append(f"etiss_uint{reg.ty.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')) diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index 701a7070..ea6bc5a8 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -15,13 +15,14 @@ from ...metamodel import arch, behav, type_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.ty.kind]}{arg.ty.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 @@ -44,7 +45,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo return_type = instruction_utils.data_type_map[fn_def.ty.kind] if fn_def.ty.size: - return_type += f'{fn_def.ty.actual_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, @@ -105,7 +106,7 @@ 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].ty.actual_size + 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 diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index f8d6624e..6114cbe8 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -21,7 +21,7 @@ from . import CodeInfoTracker, replacements from .instruction_utils import (FN_VAL_REPL, MEM_VAL_REPL, CodePartsContainer, CodeString, FnID, MemID, StaticType, - TransformerContext, data_type_map) + 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)) @@ -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(target.size) > 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, @@ -574,14 +574,13 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): # 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 == type_info.TypeKind.TYPE_INT and expr_str.actual_size != expr_str.size: - target_size = expr.actual_size + if expr.data_type == type_info.TypeKind.TYPE_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,7 +589,7 @@ 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 == type_info.TypeKind.TYPE_INT, expr_str.regs_affected, line_infos=[expr.line_info] + expr_str.line_infos) c.mem_ids = expr_str.mem_ids diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index 5b9c9986..513c85f6 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -31,7 +31,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") @@ -64,9 +64,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): diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 986c2ab8..0cbe39f4 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -77,20 +77,8 @@ 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 diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 38107f73..9bd1100a 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -40,15 +40,6 @@ def __init__(self, size: int, signed: bool, ptr: Any=None): self.ptr = ptr super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) - @property - def actual_size(self): - """Returns the resolved size value rounded to the nearest multiple of 8.""" - - if self.size is None: - return None - - temp = 1 << (self.size - 1).bit_length() - return temp if temp >= 8 else 8 class FloatType: @@ -81,16 +72,6 @@ class MemoryType: def __init__(self, size: int): self.size = size - @property - def actual_size(self): - """Returns the resolved size value rounded to the nearest multiple of 8.""" - - if self.size is None: - return None - - temp = 1 << (self.size - 1).bit_length() - return temp if temp >= 8 else 8 - class FunctionAttribute(Enum): ETISS_STATICFN = auto() @@ -110,13 +91,3 @@ class FunctionType(): def __init__(self, size: int, kind: TypeKind): self.size = size self.kind = kind - - @property - def actual_size(self): - """Returns the resolved size value rounded to the nearest multiple of 8.""" - - if self.size is None: - return None - - temp = 1 << (self.size - 1).bit_length() - return temp if temp >= 8 else 8 \ No newline at end of file From 2ed0c73e49c71e89cd93b71d86dc3aa7447b0835 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 30 Apr 2026 08:05:54 +0200 Subject: [PATCH 07/33] [MetaModel] Attributes into type_info --- m2isar/backends/etiss/architecture_writer.py | 2 +- m2isar/backends/etiss/instruction_generator.py | 10 +++++----- m2isar/backends/etiss/instruction_transform.py | 12 ++++++------ m2isar/backends/etiss/instruction_utils.py | 2 +- .../coredsl2/architecture_model_builder.py | 2 +- m2isar/frontends/coredsl2/parser.py | 6 +++--- m2isar/metamodel/arch.py | 16 ++-------------- m2isar/metamodel/type_info.py | 12 ++++++++++++ 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index ff4464d2..228d6301 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -193,7 +193,7 @@ 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), type_info.TypeKind.TYPE_UINT) - error_instr = arch.Instruction(f"trap_entry {bitsize}", {arch.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None) + error_instr = arch.Instruction(f"trap_entry {bitsize}", {type_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)]) diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index ea6bc5a8..1df32052 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -156,11 +156,11 @@ def generate_instruction_callback(core: arch.CoreDef, instr_def: arch.Instructio 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 ((type_info.InstrAttribute.NO_CONT in instr_def.attributes + and type_info.InstrAttribute.COND not in instr_def.attributes and block_end_on == BlockEndType.UNCOND) or ( - arch.InstrAttribute.NO_CONT in instr_def.attributes + type_info.InstrAttribute.NO_CONT in instr_def.attributes and block_end_on == BlockEndType.ALL) ): @@ -231,8 +231,8 @@ def gen_rand_suffix(length: int = 8): 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 type_info.InstrAttribute.ENABLE in instr_def.attributes: + cond = instr_def.attributes[type_info.InstrAttribute.ENABLE] new_op = behav.Operation([ behav.Conditional( [cond[0]], diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 6114cbe8..6c3537dd 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -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 + type_info.InstrAttribute.NO_CONT in context.attributes, + type_info.InstrAttribute.COND in context.attributes, + type_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 type_info.InstrAttribute.NO_CONT in context.attributes and type_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 type_info.InstrAttribute.NO_CONT in context.attributes: return_conditions.clear() - if arch.InstrAttribute.FLUSH in context.attributes: + if type_info.InstrAttribute.FLUSH in context.attributes: container.initial_required = 'cp.code() += "cpu->exception = ETISS_RETURNCODE_RELOADBLOCKS;\\n";\n' + container.initial_required return_conditions.clear() diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index 513c85f6..5c504657 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -136,7 +136,7 @@ class TransformerContext: """ 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]", + fields: "dict[str, arch.BitFieldDescr]", attributes: "list[type_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 diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index e700a6ea..c7f9b118 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -515,7 +515,7 @@ def visitAttribute(self, ctx: CoreDSL2Parser.AttributeContext): name = ctx.name.text # read attribute from enums - attr = arch.InstrAttribute._member_map_.get(name.upper()) or \ + attr = type_info.InstrAttribute._member_map_.get(name.upper()) or \ type_info.MemoryAttribute._member_map_.get(name.upper()) or \ type_info.FunctionAttribute._member_map_.get(name.upper()) diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 59ebe8c9..edcef633 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -240,15 +240,15 @@ 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 type_info.InstrAttribute.ENABLE in instr_def.attributes: + enable_attr = instr_def.attributes[type_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) if enable is not None: assert isinstance(enable, bool) - instr_def.attributes.pop(arch.InstrAttribute.ENABLE) + instr_def.attributes.pop(type_info.InstrAttribute.ENABLE) if not enable: continue diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 0cbe39f4..1cf3bbfa 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -88,13 +88,13 @@ class Constant(SizedRefOrConst): _value: Union[int, "Constant", "BaseNode"] """The value this object holds. Can be an int, another constant or a statically resolvable BaseNode.""" - attributes: "dict[ConstAttribute, list[BaseNode]]" + attributes: "dict[type_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, "Constant", "BaseNode"], attributes: "dict[type_info.ConstAttribute, list[BaseNode]]", size=None, signed=False): self._value = value self.attributes = attributes if attributes else {} self.signed = signed @@ -190,18 +190,6 @@ def length(self): def __str__(self) -> str: return f', len {self.length}: {self.upper_base}:{self.lower_base}' -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 FnParam(Named): """A function parameter.""" diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 9bd1100a..1fb47e19 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -91,3 +91,15 @@ class FunctionType(): def __init__(self, size: int, kind: TypeKind): self.size = size self.kind = kind + +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() From 3e4c72b27aabf83c9d19279b3383122224fdd87b Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 30 Apr 2026 09:37:06 +0200 Subject: [PATCH 08/33] [MetaModel] StaticType into TypeInfo --- .../backends/etiss/instruction_transform.py | 60 +++++++-------- m2isar/backends/etiss/instruction_utils.py | 3 +- .../coredsl2/behavior_model_builder.py | 3 +- m2isar/metamodel/arch.py | 14 ++-- m2isar/metamodel/type_info.py | 22 +++++- m2isar/metamodel/utils/__init__.py | 18 ----- m2isar/metamodel/utils/expr_preprocessor.py | 5 +- m2isar/metamodel/utils/scalar_staticness.py | 75 +++++++++---------- 8 files changed, 96 insertions(+), 104 deletions(-) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 6c3537dd..9cd187a3 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -20,8 +20,8 @@ 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, actual_size) + CodeString, FnID, MemID, TransformerContext, + data_type_map, actual_size) # pylint: disable=unused-argument @@ -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", type_info.StaticType.READ, None, None, line_infos=expr.line_info)] + post = [CodeString("} // block", type_info.StaticType.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", type_info.StaticType.NONE, None, None)) + post.insert(0, CodeString("} // block", type_info.StaticType.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;", type_info.StaticType.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;", type_info.StaticType.RW, None, None, line_infos=expr.line_info) @generate.register def _(self, expr: behav.ScalarDefinition, context: TransformerContext): @@ -182,11 +182,11 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): a variable instantiation.""" if context.static_scalars: if context.ignore_static: - static = StaticType.RW + static = type_info.StaticType.RW else: static = expr.scalar.static else: - static = StaticType.NONE + static = type_info.StaticType.NONE actual_size = 1 << (expr.scalar.ty.size - 1).bit_length() actual_size = max(actual_size, 8) @@ -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 = type_info.StaticType.READ if fn.static and all(arg.static != type_info.StaticType.NONE for arg in fn_args) else type_info.StaticType.NONE # convert singular static arguments if not static: @@ -254,8 +254,8 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): cond = "if (cpu->return_pending) " if fn.throws == type_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", type_info.StaticType.READ, None, None), CodeString("{ // procedure", type_info.StaticType.NONE, None, None)] + post = [CodeString("} // procedure", type_info.StaticType.NONE, None, None), CodeString("} // procedure", type_info.StaticType.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 = type_info.StaticType.READ if fn.static and all(arg.static != type_info.StaticType.NONE for arg in fn_args) else type_info.StaticType.NONE # convert singular static arguments if not static: @@ -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 & type_info.StaticType.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 & type_info.StaticType.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 & type_info.StaticType.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 & type_info.StaticType.READ): target.code = Template(target.code).safe_substitute(replacements.rename_write) # keep track of affected and dependent registers @@ -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 = type_info.StaticType.NONE not in [x.static for x in (cond, then_expr, else_expr)] # convert singular static sub-components if not static: @@ -603,14 +603,14 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # extract referred object referred_var = expr.reference - static = StaticType.NONE + static = type_info.StaticType.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 = type_info.StaticType.READ # check which type of reference has to be generated if isinstance(referred_var, arch.Memory): @@ -626,7 +626,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # function argument signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = StaticType.READ + static = type_info.StaticType.READ elif isinstance(referred_var, arch.Scalar): assert isinstance(referred_var.ty, type_info.PrimitiveType) @@ -638,13 +638,13 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): elif isinstance(referred_var, arch.Constant): signed = referred_var.value < 0 size = context.native_size - static = StaticType.READ + static = type_info.StaticType.READ name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = StaticType.RW + static = type_info.StaticType.RW elif isinstance(referred_var, arch.Intrinsic): if context.ignore_static: @@ -652,7 +652,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = StaticType.READ + static = type_info.StaticType.READ if referred_var == context.intrinsics["__encoding_size"]: name = str(context.instr_size // 8) @@ -661,7 +661,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): raise TypeError("wrong type") if context.ignore_static: - static = StaticType.RW + static = type_info.StaticType.RW return CodeString(name, static, size, signed, line_infos=expr.line_info) @@ -687,9 +687,9 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): index.code = context.make_static(index.code, index.signed) if context.ignore_static: - static = StaticType.RW + static = type_info.StaticType.RW else: - static = StaticType.NONE + static = type_info.StaticType.NONE if type_info.MemoryAttribute.IS_MAIN_MEM in referred_mem.attributes: # generate memory access if main memory is accessed @@ -728,7 +728,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 = type_info.StaticType.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,7 +791,7 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): @generate.register def _(self, expr: behav.Literal, context: TransformerContext): if expr.ty.kind is type_info.TypeKind.TYPE_STR: - return CodeString(f'"{expr.value}"', StaticType.READ, None, False, line_infos=expr.line_info) + return CodeString(f'"{expr.value}"', type_info.StaticType.READ, None, False, line_infos=expr.line_info) # old number NumberLiteral elif expr.ty.kind == type_info.TypeKind.TYPE_NONE: diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index 5c504657..ba29d3c6 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -15,7 +15,6 @@ from ... import M2ValueError from ...metamodel import arch, type_info from ...metamodel.code_info import LineInfo -from ...metamodel.utils import StaticType from . import replacements data_type_map = { @@ -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 = type_info.StaticType(static) self.size = size self.signed = signed self.mem_ids = [] diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index d7c5f766..2d9abe5f 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -15,7 +15,6 @@ from ...metamodel import arch, behav, type_info, intrinsics 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 @@ -127,7 +126,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # instantiate a scalar and its definition - s = arch.Scalar(name, type_.kind, type_.size, None, StaticType.NONE) + s = arch.Scalar(name, type_.kind, type_.size, None, type_info.StaticType.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 1cf3bbfa..1641898f 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -216,15 +216,15 @@ class Scalar(Named): """A scalar variable object, used mainly in behavior descriptions.""" ty: Union[type_info.PrimitiveType] - static: bool + static: type_info.StaticType value: int def __init__(self, name, kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], size : int, - value: int, - static: bool, # Compile Time information + value: int, # Compile Time information + static: type_info.StaticType, storage=None ): self.ty = type_info.PrimitiveType(kind, size) if isinstance(kind, type_info.TypeKind) else kind @@ -243,7 +243,7 @@ class Array(Named): """A variable object for an Array, used mainly in behavior descriptions.""" ty: type_info.ArrayType - static: bool + static: type_info.StaticType values: list[int] def __init__(self, @@ -252,7 +252,7 @@ def __init__(self, size : int, length : int, values: list[int], - static: bool, # Compile Time information + static: type_info.StaticType, # Compile Time information storage=None ): # Explcit casting for now allowed??? @@ -435,7 +435,7 @@ class Function(Named): ext_name: str scalars: "dict[str, Scalar]" throws: bool - static: bool + static: type_info.StaticType def __init__(self, name, attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]", return_len, kind: type_info.TypeKind, args: "list[FnParam]", operation: "Operation", extern: bool=False, function_info: "FunctionInfo"=None): @@ -461,7 +461,7 @@ def __init__(self, name, attributes: "dict[type_info.FunctionAttribute, list[Bas self.args[arg_name] = arg self.operation = operation if operation is not None else Operation([]) - self.static = False + self.static = type_info.StaticType.NONE self.extern = extern super().__init__(name) diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 1fb47e19..1a4c6ce1 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -11,7 +11,8 @@ anything but the functional behavior of functions and instructions. """ -from enum import Enum, IntEnum, auto +from dataclasses import dataclass +from enum import Enum, IntEnum, auto, IntFlag from typing import Any, Union @@ -20,7 +21,7 @@ class TypeKind(Enum): TYPE_VOID = auto() TYPE_UINT = auto() TYPE_INT = auto() - TYPE_BOOL = auto() + # TYPE_BOOL = auto() Expressed as unsigned<1> TYPE_FLOAT = auto() TYPE_STR = auto() TYPE_ARRAY = auto() @@ -40,8 +41,6 @@ def __init__(self, size: int, signed: bool, ptr: Any=None): self.ptr = ptr super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) - - class FloatType: def __init__(self, exponent: int, mantissa: int, size: int): self.exponent = exponent @@ -103,3 +102,18 @@ class InstrAttribute(Enum): SIM_EXIT = auto() ENABLE = auto() ETISS_ERROR_INSTRUCTION = 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 \ No newline at end of file diff --git a/m2isar/metamodel/utils/__init__.py b/m2isar/metamodel/utils/__init__.py index 2f042c90..e2e3ffc4 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 25ec3973..ef432a5d 100644 --- a/m2isar/metamodel/utils/expr_preprocessor.py +++ b/m2isar/metamodel/utils/expr_preprocessor.py @@ -15,7 +15,6 @@ from ... import M2ValueError from .. import arch, type_info -from . import ScalarStaticnessContext from .expr_simplifier import ExprSimplifierVisitor from .function_staticness import FunctionStaticnessVisitor from .function_throws import FunctionThrowsVisitor @@ -50,7 +49,7 @@ def process_functions(core: arch.CoreDef): throws = function_throws_visitor.generate(fn_def.operation, None) fn_def.throws = throws or type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes - context = ScalarStaticnessContext() + context = type_info.ScalarStaticnessContext() logger.debug("examining scalar staticness for fn %s", fn_name) scalar_staticness_visitor.generate(fn_def.operation, context) @@ -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 = type_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/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index 78ffaff9..3cac60c1 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, type_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: type_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 type_info.StaticType.NONE return min(valid) @generate.register - def _(self, expr: behav.BinaryOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.BinaryOperation, context: type_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: type_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) left = self.generate(expr.left, context) right = self.generate(expr.right, context) @@ -63,38 +62,38 @@ 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: type_info.ScalarStaticnessContext): left = self.generate(expr.left, context) right = self.generate(expr.right, context) return min(left, right) @generate.register - def _(self, expr: behav.Literal, context: ScalarStaticnessContext): - return StaticType.READ + def _(self, expr: behav.Literal, context: type_info.ScalarStaticnessContext): + return type_info.StaticType.READ @generate.register - def _(self, expr: behav.ScalarDefinition, context: ScalarStaticnessContext): + def _(self, expr: behav.ScalarDefinition, context: type_info.ScalarStaticnessContext): scalar = cast(Any, expr.scalar) - scalar.static = StaticType.RW - return StaticType.RW + scalar.static = type_info.StaticType.RW + return type_info.StaticType.RW @generate.register def _(self, expr: behav.Break, context): - return StaticType.READ + return type_info.StaticType.READ @generate.register - def _(self, expr: behav.Assignment, context: ScalarStaticnessContext): + def _(self, expr: behav.Assignment, context: type_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 != type_info.StaticType.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 != type_info.StaticType.NONE: + expr_static = type_info.StaticType.RW else: - expr_static = StaticType.NONE + expr_static = type_info.StaticType.NONE if isinstance(expr.target, behav.NamedReference) and isinstance(expr.target.reference, arch.Scalar): target_ref = cast(Any, expr.target.reference) @@ -105,19 +104,19 @@ def _(self, expr: behav.Assignment, context: ScalarStaticnessContext): target_scalar.static &= expr_static @generate.register - def _(self, expr: behav.Conditional, context: ScalarStaticnessContext): + def _(self, expr: behav.Conditional, context: type_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: type_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: type_info.ScalarStaticnessContext): cond = self.generate(expr.cond, context) then_expr = self.generate(expr.then_expr, context) else_expr = self.generate(expr.else_expr, context) @@ -125,62 +124,62 @@ 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: type_info.ScalarStaticnessContext): if expr.expr is not None: return self.generate(expr.expr, context) - return StaticType.RW + return type_info.StaticType.RW @generate.register - def _(self, expr: behav.UnaryOperation, context: ScalarStaticnessContext): + def _(self, expr: behav.UnaryOperation, context: type_info.ScalarStaticnessContext): right = self.generate(expr.right, context) return right @generate.register - def _(self, expr: behav.NamedReference, context: ScalarStaticnessContext): + def _(self, expr: behav.NamedReference, context: type_info.ScalarStaticnessContext): if isinstance(expr.reference, arch.Scalar): return expr.reference.static static_map = { - arch.Memory: StaticType.NONE, - arch.BitFieldDescr: StaticType.READ, - arch.Constant: StaticType.READ, - arch.FnParam: StaticType.READ + arch.Memory: type_info.StaticType.NONE, + arch.BitFieldDescr: type_info.StaticType.READ, + arch.Constant: type_info.StaticType.READ, + arch.FnParam: type_info.StaticType.READ } - return static_map.get(type(expr.reference), StaticType.NONE) + return static_map.get(type(expr.reference), type_info.StaticType.NONE) @generate.register - def _(self, expr: behav.IndexedReference, context: ScalarStaticnessContext): + def _(self, expr: behav.IndexedReference, context: type_info.ScalarStaticnessContext): self.generate(expr.index, context) - return StaticType.NONE + return type_info.StaticType.NONE @generate.register - def _(self, expr: behav.TypeConv, context: ScalarStaticnessContext): + def _(self, expr: behav.TypeConv, context: type_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: type_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(type_info.StaticType.READ if is_static else type_info.StaticType.NONE) return min(args) @generate.register - def _(self, expr: behav.ProcedureCall, context: ScalarStaticnessContext): + def _(self, expr: behav.ProcedureCall, context: type_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(type_info.StaticType.READ if is_static else type_info.StaticType.NONE) return min(args) @generate.register - def _(self, expr: behav.Group, context: ScalarStaticnessContext): + def _(self, expr: behav.Group, context: type_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) return expr_result From 8c3f7881ca3d880b375a0cc94f4a04a12f66a4d3 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 13:12:55 +0200 Subject: [PATCH 09/33] [MetaModel] Move attributes into own attribute func --- m2isar/backends/etiss/architecture_writer.py | 12 +-- .../backends/etiss/instruction_generator.py | 16 ++-- .../backends/etiss/instruction_transform.py | 86 +++++++++---------- m2isar/backends/etiss/instruction_utils.py | 12 +-- .../coredsl2/architecture_model_builder.py | 14 +-- .../coredsl2/behavior_model_builder.py | 6 +- m2isar/frontends/coredsl2/parser.py | 8 +- m2isar/metamodel/arch.py | 45 +++++----- m2isar/metamodel/attribute_info.py | 52 +++++++++++ m2isar/metamodel/type_info.py | 53 +----------- m2isar/metamodel/utils/expr_preprocessor.py | 16 ++-- m2isar/metamodel/utils/function_throws.py | 40 ++++----- m2isar/metamodel/utils/scalar_staticness.py | 74 ++++++++-------- 13 files changed, 217 insertions(+), 217 deletions(-) create mode 100644 m2isar/metamodel/attribute_info.py diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 228d6301..3274a848 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -16,7 +16,7 @@ from .instruction_utils import actual_size from ... import M2TypeError -from ...metamodel import arch, behav, type_info +from ...metamodel import arch, behav, type_info, attribute_info from . import BlockEndType from .instruction_generator import (generate_fields, generate_instruction_callback) @@ -28,7 +28,7 @@ def write_child_reg_def(reg: arch.Memory, regs: "list[str]"): """Recursively generate register declarations""" logger.debug("processing register %s", reg) - if type_info.MemoryAttribute.IS_PC in reg.attributes or type_info.MemoryAttribute.IS_MAIN_MEM in reg.attributes: + if attribute_info.MemoryAttribute.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 @@ -179,12 +179,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 type_info.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 type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break @@ -193,7 +193,7 @@ 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), type_info.TypeKind.TYPE_UINT) - error_instr = arch.Instruction(f"trap_entry {bitsize}", {type_info.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None) + 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)]) @@ -209,7 +209,7 @@ 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[type_info.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN][0] + 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 1df32052..2b65883a 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -12,7 +12,7 @@ from mako.template import Template -from ...metamodel import arch, behav, type_info +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 @@ -67,7 +67,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 type_info.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) @@ -156,11 +156,11 @@ def generate_instruction_callback(core: arch.CoreDef, instr_def: arch.Instructio core.functions, enc_idx, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, False) # force a block end if necessary - if ((type_info.InstrAttribute.NO_CONT in instr_def.attributes - and type_info.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 ( - type_info.InstrAttribute.NO_CONT in instr_def.attributes + attribute_info.InstrAttribute.NO_CONT in instr_def.attributes and block_end_on == BlockEndType.ALL) ): @@ -197,7 +197,7 @@ def generate_instructions(core: arch.CoreDef, static_scalars: bool, block_end_on error_fn = None for fn in core.functions.values(): - if type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: error_fn = fn break @@ -231,8 +231,8 @@ def gen_rand_suffix(length: int = 8): code_string = f'{code:#0{int(enc_idx/4)}x}' mask_string = f'{mask:#0{int(enc_idx/4)}x}' - if type_info.InstrAttribute.ENABLE in instr_def.attributes: - cond = instr_def.attributes[type_info.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]], diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 9cd187a3..54ad5bfe 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -15,7 +15,7 @@ from functools import singledispatchmethod from ... import M2NameError, M2SyntaxError, M2ValueError, flatten -from ...metamodel import arch, behav, type_info +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 @@ -117,22 +117,22 @@ def _(self, expr: behav.Operation, context: TransformerContext): return_conditions = [] return_needed = any(( context.generates_exception, - type_info.InstrAttribute.NO_CONT in context.attributes, - type_info.InstrAttribute.COND in context.attributes, - type_info.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 type_info.InstrAttribute.NO_CONT in context.attributes and type_info.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 type_info.InstrAttribute.NO_CONT in context.attributes: + elif attribute_info.InstrAttribute.NO_CONT in context.attributes: return_conditions.clear() - if type_info.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 type_info.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", type_info.StaticType.READ, None, None, line_infos=expr.line_info)] - post = [CodeString("} // block", type_info.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", type_info.StaticType.NONE, None, None)) - post.insert(0, CodeString("} // block", type_info.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;", type_info.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;", type_info.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,11 +182,11 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): a variable instantiation.""" if context.static_scalars: if context.ignore_static: - static = type_info.StaticType.RW + static = attribute_info.StaticAttribute.RW else: static = expr.scalar.static else: - static = type_info.StaticType.NONE + static = attribute_info.StaticAttribute.NONE actual_size = 1 << (expr.scalar.ty.size - 1).bit_length() actual_size = max(actual_size, 8) @@ -214,7 +214,7 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): # determine if procedure call is entirely static - static = type_info.StaticType.READ if fn.static and all(arg.static != type_info.StaticType.NONE for arg in fn_args) else type_info.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 type_info.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,10 +237,10 @@ def _(self, expr: behav.ProcedureCall, context: TransformerContext): # add special behavior if this function is an exception entry point exc_code = "" - if type_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: + if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes: context.generates_exception = True - if type_info.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.ty.size is not None: @@ -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 == type_info.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", type_info.StaticType.READ, None, None), CodeString("{ // procedure", type_info.StaticType.NONE, None, None)] - post = [CodeString("} // procedure", type_info.StaticType.NONE, None, None), CodeString("} // procedure", type_info.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 = type_info.StaticType.READ if fn.static and all(arg.static != type_info.StaticType.NONE for arg in fn_args) else type_info.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,7 +288,7 @@ 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 type_info.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 @@ -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 & type_info.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 & type_info.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 & type_info.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 & type_info.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 @@ -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 = type_info.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: @@ -603,14 +603,14 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # extract referred object referred_var = expr.reference - static = type_info.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 = type_info.StaticType.READ + static = attribute_info.StaticAttribute.READ # check which type of reference has to be generated if isinstance(referred_var, arch.Memory): @@ -626,7 +626,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # function argument signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = type_info.StaticType.READ + static = attribute_info.StaticAttribute.READ elif isinstance(referred_var, arch.Scalar): assert isinstance(referred_var.ty, type_info.PrimitiveType) @@ -638,13 +638,13 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): elif isinstance(referred_var, arch.Constant): signed = referred_var.value < 0 size = context.native_size - static = type_info.StaticType.READ + static = attribute_info.StaticAttribute.READ name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = type_info.StaticType.RW + static = attribute_info.StaticAttribute.RW elif isinstance(referred_var, arch.Intrinsic): if context.ignore_static: @@ -652,7 +652,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT size = referred_var.ty.size - static = type_info.StaticType.READ + static = attribute_info.StaticAttribute.READ if referred_var == context.intrinsics["__encoding_size"]: name = str(context.instr_size // 8) @@ -661,7 +661,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): raise TypeError("wrong type") if context.ignore_static: - static = type_info.StaticType.RW + static = attribute_info.StaticAttribute.RW return CodeString(name, static, size, signed, line_infos=expr.line_info) @@ -687,11 +687,11 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): index.code = context.make_static(index.code, index.signed) if context.ignore_static: - static = type_info.StaticType.RW + static = attribute_info.StaticAttribute.RW else: - static = type_info.StaticType.NONE + static = attribute_info.StaticAttribute.NONE - if type_info.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.ty.size c = CodeString(f'{MEM_VAL_REPL}{context.mem_var_count}', static, size, False, line_infos=[expr.line_info] + index.line_infos) @@ -715,7 +715,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 type_info.MemoryAttribute.IS_MAIN_REG in referred_mem.attributes: + if attribute_info.MemoryAttribute.IS_MAIN_REG in referred_mem.attributes: c.regs_affected.add(index_code) return c @@ -728,7 +728,7 @@ def _(self, expr: behav.SliceOperation, context: TransformerContext): left = self.generate(expr.left, context) right = self.generate(expr.right, context) - static = type_info.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,7 +791,7 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): @generate.register def _(self, expr: behav.Literal, context: TransformerContext): if expr.ty.kind is type_info.TypeKind.TYPE_STR: - return CodeString(f'"{expr.value}"', type_info.StaticType.READ, None, False, line_infos=expr.line_info) + 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.TYPE_NONE: diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index ba29d3c6..f8f98221 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -13,7 +13,7 @@ from string import Template from ... import M2ValueError -from ...metamodel import arch, type_info +from ...metamodel import arch, type_info, attribute_info from ...metamodel.code_info import LineInfo from . import replacements @@ -47,7 +47,7 @@ class CodeString: def __init__(self, code, static, size, signed, regs_affected=None, line_infos=[]): self.code = code - self.static = type_info.StaticType(static) + self.static = attribute_info.StaticAttribute(static) self.size = size self.signed = signed self.mem_ids = [] @@ -135,7 +135,7 @@ class TransformerContext: """ 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[type_info.InstrAttribute]", functions: "dict[str, arch.Function]", + 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 @@ -161,7 +161,7 @@ def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, a self.pc_mem = None for _, mem_descr in chain(self.memories.items(), self.memory_aliases.items()): - if type_info.MemoryAttribute.IS_PC in mem_descr.attributes: + if attribute_info.MemoryAttribute.IS_PC in mem_descr.attributes: self.pc_mem = mem_descr break @@ -169,9 +169,9 @@ def __init__(self, constants: "dict[str, arch.Constant]", memories: "dict[str, a self.mem_raise_fn: arch.Function = None for fn_name, fn_def in self.functions.items(): - if type_info.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 type_info.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 diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index c7f9b118..df40aacf 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, type_info +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 @@ -197,8 +197,8 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex # decode attributes attributes = dict([self.visit(obj) for obj in ctx.attributes]) - if type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in attributes: - attributes[type_info.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_) @@ -391,7 +391,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if init is not None: m._initval[None] = exprInterpretVisitor.generate(init, None) - if type_info.MemoryAttribute.IS_MAIN_REG in attributes: + if attribute_info.MemoryAttribute.IS_MAIN_REG in attributes: self._main_reg_file = m self._memories[name] = m @@ -515,9 +515,9 @@ def visitAttribute(self, ctx: CoreDSL2Parser.AttributeContext): name = ctx.name.text # read attribute from enums - attr = type_info.InstrAttribute._member_map_.get(name.upper()) or \ - type_info.MemoryAttribute._member_map_.get(name.upper()) or \ - type_info.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()) # 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 2d9abe5f..0dbca58f 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING from ... import M2NameError, M2SyntaxError, M2TypeError, flatten -from ...metamodel import arch, behav, type_info, intrinsics +from ...metamodel import arch, behav, type_info, intrinsics, attribute_info from ...metamodel.code_info import (BranchEntryInfoFactory, BranchInfo, LineInfoFactory, LineInfoPlacement) from .parser_gen import CoreDSL2Parser, CoreDSL2Visitor @@ -90,7 +90,7 @@ def visitMethod_call(self, ctx: "CoreDSL2Parser.Method_callContext"): if ref is None: raise M2NameError(f"function \"{name}\" is not defined") - if type_info.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 @@ -126,7 +126,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # instantiate a scalar and its definition - s = arch.Scalar(name, type_.kind, type_.size, None, type_info.StaticType.NONE) + s = arch.Scalar(name, type_.kind, type_.size, None, attribute_info.StaticAttribute.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index edcef633..6b105568 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, type_info +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 @@ -240,15 +240,15 @@ def main(): sys.exit(1) instr_def.attributes[attr_name] = ops - if type_info.InstrAttribute.ENABLE in instr_def.attributes: - enable_attr = instr_def.attributes[type_info.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) if enable is not None: assert isinstance(enable, bool) - instr_def.attributes.pop(type_info.InstrAttribute.ENABLE) + instr_def.attributes.pop(attribute_info.InstrAttribute.ENABLE) if not enable: continue diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 1641898f..e764c662 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -14,10 +14,9 @@ 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 +from m2isar.metamodel import type_info, attribute_info from .. import M2TypeError from .behav import BaseNode, Operation, Literal @@ -216,7 +215,7 @@ class Scalar(Named): """A scalar variable object, used mainly in behavior descriptions.""" ty: Union[type_info.PrimitiveType] - static: type_info.StaticType + static: attribute_info.StaticAttribute value: int def __init__(self, @@ -224,7 +223,7 @@ def __init__(self, kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], size : int, value: int, # Compile Time information - static: type_info.StaticType, + static: attribute_info.StaticAttribute, storage=None ): self.ty = type_info.PrimitiveType(kind, size) if isinstance(kind, type_info.TypeKind) else kind @@ -243,7 +242,7 @@ class Array(Named): """A variable object for an Array, used mainly in behavior descriptions.""" ty: type_info.ArrayType - static: type_info.StaticType + static: attribute_info.StaticAttribute values: list[int] def __init__(self, @@ -252,7 +251,7 @@ def __init__(self, size : int, length : int, values: list[int], - static: type_info.StaticType, # Compile Time information + static: attribute_info.StaticAttribute, # Compile Time information storage=None ): # Explcit casting for now allowed??? @@ -286,12 +285,12 @@ class Memory(Named): ty : type_info.MemoryType range: RangeSpec - attributes: "dict[type_info.MemoryAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]" children: "list[Memory]" parent: Union['Memory', None] _initval: "dict[int, Union[int, Constant, BaseNode]]" - def __init__(self, name, range_: RangeSpec, size, attributes: "dict[type_info.MemoryAttribute, list[BaseNode]]"): + def __init__(self, name, range_: RangeSpec, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.ty = type_info.MemoryType(size) self.attributes = attributes if attributes else {} self.range = range_ @@ -317,12 +316,12 @@ def data_range(self): @property def is_pc(self): """Return true if this memory is tagged as being the program counter.""" - return type_info.MemoryAttribute.IS_PC in self.attributes + return attribute_info.MemoryAttribute.IS_PC in self.attributes @property def is_main_mem(self): """Return true if this memory is tagged as being the main memory array.""" - return type_info.MemoryAttribute.IS_MAIN_MEM in self.attributes + return attribute_info.MemoryAttribute.IS_MAIN_MEM in self.attributes @dataclasses.dataclass class BitVal: @@ -426,7 +425,7 @@ def __str__(self) -> str: class Function(Named): """A class representing a function.""" - attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.FunctionAttribute, list[BaseNode]]" ty: type_info.FunctionType args: "list[FnParam]" operation: "Operation" @@ -435,9 +434,9 @@ class Function(Named): ext_name: str scalars: "dict[str, Scalar]" throws: bool - static: type_info.StaticType + static: attribute_info.StaticAttribute - def __init__(self, name, attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]", return_len, kind: type_info.TypeKind, 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 = "" @@ -461,7 +460,7 @@ def __init__(self, name, attributes: "dict[type_info.FunctionAttribute, list[Bas self.args[arg_name] = arg self.operation = operation if operation is not None else Operation([]) - self.static = type_info.StaticType.NONE + self.static = attribute_info.StaticAttribute.NONE self.extern = extern super().__init__(name) @@ -490,7 +489,7 @@ def extract_memory_alias(memories: "list[Memory]"): return parents, aliases class AlwaysBlock(Named): - attributes: "dict[type_info.FunctionAttribute, list[BaseNode]]" + attributes: "dict[attribute_info.FunctionAttribute, list[BaseNode]]" operation: "Operation" def __init__(self, name: str, attributes, operation): @@ -548,19 +547,19 @@ 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 type_info.MemoryAttribute.IS_MAIN_REG in mem.attributes: + if attribute_info.MemoryAttribute.IS_MAIN_REG in mem.attributes: self.main_reg_file = mem - elif type_info.MemoryAttribute.IS_PC in mem.attributes: + elif attribute_info.MemoryAttribute.IS_PC in mem.attributes: self.pc_memory = mem - elif type_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: + elif attribute_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: self.main_memory = mem - elif type_info.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 type_info.MemoryAttribute.ETISS_IS_PROCNO in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_PROCNO in mem.attributes: self.procno_memory = mem - elif type_info.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 type_info.MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: + elif attribute_info.MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: self.irq_pending_memory = mem super().__init__(name) diff --git a/m2isar/metamodel/attribute_info.py b/m2isar/metamodel/attribute_info.py new file mode 100644 index 00000000..468b8fb8 --- /dev/null +++ b/m2isar/metamodel/attribute_info.py @@ -0,0 +1,52 @@ + +from enum import Enum, IntEnum, auto, IntFlag +from dataclasses import dataclass + +class MemoryAttribute(Enum): + IS_PC = auto() + IS_MAIN_MEM = auto() + IS_MAIN_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 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 + READ = auto() + WRITE = auto() + 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/type_info.py b/m2isar/metamodel/type_info.py index 1a4c6ce1..fad87bca 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -11,8 +11,7 @@ anything but the functional behavior of functions and instructions. """ -from dataclasses import dataclass -from enum import Enum, IntEnum, auto, IntFlag +from enum import Enum, auto from typing import Any, Union @@ -53,18 +52,6 @@ def __init__(self, element_type : Union[PrimitiveType, FloatType], length: int): self.length = length # allow shaped later or TYPE_ARRAY in element_type? -class MemoryAttribute(Enum): - IS_PC = auto() - IS_MAIN_MEM = auto() - IS_MAIN_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 MemoryType: size : int @@ -72,17 +59,6 @@ def __init__(self, size: int): self.size = size -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 FunctionType(): size : int kind : TypeKind @@ -90,30 +66,3 @@ class FunctionType(): def __init__(self, size: int, kind: TypeKind): self.size = size self.kind = kind - -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 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 \ No newline at end of file diff --git a/m2isar/metamodel/utils/expr_preprocessor.py b/m2isar/metamodel/utils/expr_preprocessor.py index ef432a5d..84330504 100644 --- a/m2isar/metamodel/utils/expr_preprocessor.py +++ b/m2isar/metamodel/utils/expr_preprocessor.py @@ -14,7 +14,7 @@ from itertools import chain from ... import M2ValueError -from .. import arch, type_info +from .. import arch, attribute_info from .expr_simplifier import ExprSimplifierVisitor from .function_staticness import FunctionStaticnessVisitor from .function_throws import FunctionThrowsVisitor @@ -47,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 type_info.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 = type_info.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 type_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes and type_info.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 (type_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or type_info.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 type_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn_def.attributes: - if type_info.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: @@ -84,6 +84,6 @@ def process_instructions(core: arch.CoreDef): throws = function_throws_visitor.generate(instr_def.operation, None) instr_def.throws = throws - context = type_info.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/function_throws.py b/m2isar/metamodel/utils/function_throws.py index f7573b72..2f891ae4 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, type_info +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, type_info.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, type_info.FunctionThrows.NO) + return reduce(or_, stmts, attribute_info.FunctionThrows.NO) @generate.register def _(self, expr: behav.BinaryOperation, context): @@ -66,15 +66,15 @@ def _(self, expr: behav.ConcatOperation, context): @generate.register def _(self, expr: behav.Literal, context): - return type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.ScalarDefinition, context): - return type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Break, context): - return type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Assignment, context): @@ -90,7 +90,7 @@ def _(self, expr: behav.Conditional, context): conds.extend(stmts) - return type_info.FunctionThrows.MAYBE if reduce(or_, conds) else type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.MAYBE if reduce(or_, conds) else attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.Loop, context): @@ -113,7 +113,7 @@ def _(self, expr: behav.Return, context): if expr.expr is not None: return self.generate(expr.expr, context) - return type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.UnaryOperation, context): @@ -123,15 +123,15 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Memory) and type_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: - return type_info.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 type_info.FunctionThrows.NO + return attribute_info.FunctionThrows.NO @generate.register def _(self, expr: behav.IndexedReference, context): - if isinstance(expr.reference, arch.Memory) and type_info.MemoryAttribute.ETISS_CAN_FAIL in expr.reference.attributes: - return type_info.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) @@ -144,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", type_info.FunctionThrows.NO) - args.append(throws if isinstance(throws, type_info.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", type_info.FunctionThrows.NO) - args.append(throws if isinstance(throws, type_info.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) @@ -164,8 +164,8 @@ def _(self, expr: behav.Group, context): return expr_result -def cast_to_throws(throws: Any) -> type_info.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 type_info.FunctionThrows.YES if throws else type_info.FunctionThrows.NO - return type_info.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 3cac60c1..565f003b 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -14,7 +14,7 @@ from functools import singledispatchmethod from typing import Any, cast -from ...metamodel import arch, behav, type_info +from ...metamodel import arch, behav, attribute_info from .ExprVisitor import ExprVisitor # pylint: disable=unused-argument @@ -27,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: type_info.ScalarStaticnessContext): + def _(self, expr: behav.Operation, context: attribute_info.ScalarStaticnessContext): statements = [] for stmt in expr.statements: temp = self.generate(stmt, context) @@ -43,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 type_info.StaticType.NONE + return attribute_info.StaticAttribute.NONE return min(valid) @generate.register - def _(self, expr: behav.BinaryOperation, context: type_info.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: type_info.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) @@ -62,38 +62,38 @@ def _(self, expr: behav.SliceOperation, context: type_info.ScalarStaticnessConte return min(expr_result, left, right) @generate.register - def _(self, expr: behav.ConcatOperation, context: type_info.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.Literal, context: type_info.ScalarStaticnessContext): - return type_info.StaticType.READ + def _(self, expr: behav.Literal, context: attribute_info.ScalarStaticnessContext): + return attribute_info.StaticAttribute.READ @generate.register - def _(self, expr: behav.ScalarDefinition, context: type_info.ScalarStaticnessContext): + def _(self, expr: behav.ScalarDefinition, context: attribute_info.ScalarStaticnessContext): scalar = cast(Any, expr.scalar) - scalar.static = type_info.StaticType.RW - return type_info.StaticType.RW + scalar.static = attribute_info.StaticAttribute.RW + return attribute_info.StaticAttribute.RW @generate.register def _(self, expr: behav.Break, context): - return type_info.StaticType.READ + return attribute_info.StaticAttribute.READ @generate.register - def _(self, expr: behav.Assignment, context: type_info.ScalarStaticnessContext): + def _(self, expr: behav.Assignment, context: attribute_info.ScalarStaticnessContext): self.generate(expr.target, context) - if context.context_is_static != type_info.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 != type_info.StaticType.NONE: - expr_static = type_info.StaticType.RW + if expr_static != attribute_info.StaticAttribute.NONE: + expr_static = attribute_info.StaticAttribute.RW else: - expr_static = type_info.StaticType.NONE + expr_static = attribute_info.StaticAttribute.NONE if isinstance(expr.target, behav.NamedReference) and isinstance(expr.target.reference, arch.Scalar): target_ref = cast(Any, expr.target.reference) @@ -104,19 +104,19 @@ def _(self, expr: behav.Assignment, context: type_info.ScalarStaticnessContext): target_scalar.static &= expr_static @generate.register - def _(self, expr: behav.Conditional, context: type_info.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: type_info.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: type_info.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) @@ -124,62 +124,62 @@ def _(self, expr: behav.Ternary, context: type_info.ScalarStaticnessContext): return min(cond, then_expr, else_expr) @generate.register - def _(self, expr: behav.Return, context: type_info.ScalarStaticnessContext): + def _(self, expr: behav.Return, context: attribute_info.ScalarStaticnessContext): if expr.expr is not None: return self.generate(expr.expr, context) - return type_info.StaticType.RW + return attribute_info.StaticAttribute.RW @generate.register - def _(self, expr: behav.UnaryOperation, context: type_info.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: type_info.ScalarStaticnessContext): + def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticnessContext): if isinstance(expr.reference, arch.Scalar): return expr.reference.static static_map = { - arch.Memory: type_info.StaticType.NONE, - arch.BitFieldDescr: type_info.StaticType.READ, - arch.Constant: type_info.StaticType.READ, - arch.FnParam: type_info.StaticType.READ + arch.Memory: attribute_info.StaticAttribute.NONE, + arch.BitFieldDescr: attribute_info.StaticAttribute.READ, + arch.Constant: attribute_info.StaticAttribute.READ, + arch.FnParam: attribute_info.StaticAttribute.READ } - return static_map.get(type(expr.reference), type_info.StaticType.NONE) + return static_map.get(type(expr.reference), attribute_info.StaticAttribute.NONE) @generate.register - def _(self, expr: behav.IndexedReference, context: type_info.ScalarStaticnessContext): + def _(self, expr: behav.IndexedReference, context: attribute_info.ScalarStaticnessContext): self.generate(expr.index, context) - return type_info.StaticType.NONE + return attribute_info.StaticAttribute.NONE @generate.register - def _(self, expr: behav.TypeConv, context: type_info.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: type_info.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(type_info.StaticType.READ if is_static else type_info.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: type_info.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(type_info.StaticType.READ if is_static else type_info.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: type_info.ScalarStaticnessContext): + def _(self, expr: behav.Group, context: attribute_info.ScalarStaticnessContext): expr_result = self.generate(expr.expr, context) return expr_result From c4e50992feb3fe8b0a2f3d6feac5b34a0e373403 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 13:19:39 +0200 Subject: [PATCH 10/33] [Metamodel] Use properties for expressive assertions --- m2isar/metamodel/type_info.py | 18 ++++++++++++++++++ m2isar/transforms/infer_types/visitor.py | 16 ++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index fad87bca..b36bcd0e 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -20,11 +20,29 @@ class TypeKind(Enum): TYPE_VOID = auto() TYPE_UINT = auto() TYPE_INT = auto() + TYPE_CHAR = auto() # TYPE_BOOL = auto() Expressed as unsigned<1> TYPE_FLOAT = auto() TYPE_STR = auto() TYPE_ARRAY = auto() + @property + def is_int(self): + return self in (TypeKind.TYPE_INT, TypeKind.TYPE_UINT) + + @property + def is_numeric(self): + return self in (TypeKind.TYPE_INT, TypeKind.TYPE_UINT, TypeKind.TYPE_FLOAT) + + @property + def is_void(self): + return self == TypeKind.TYPE_VOID + + @property + def is_scalar(self): + return self not in (TypeKind.TYPE_ARRAY, TypeKind.TYPE_VOID, TypeKind.TYPE_NONE) + + class PrimitiveType: def __init__(self, kind: TypeKind, size: int): self.kind = kind diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index d21e4951..a705cdcf 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -181,7 +181,7 @@ def _(self, expr: behav.ConcatOperation, context): def _(self, expr: behav.Literal, context): # type inference assert(expr.ty.size is not None) - assert(expr.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT]) + assert(expr.ty.kind.is_int) signed = True if expr.ty.kind == type_info.TypeKind.TYPE_INT else False #or arch.get_const_or_val(expr.value) < 0 else False expr.ty = type_info.IntegerType(expr.ty.size, signed) @@ -192,7 +192,7 @@ def _(self, expr: behav.ScalarDefinition, context): # type inference assert isinstance(expr.scalar.ty, type_info.PrimitiveType) assert expr.scalar.ty.size is not None - assert expr.scalar.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert expr.scalar.ty.kind.is_int expr.ty = expr.scalar.ty return expr @@ -281,7 +281,7 @@ def _(self, expr: behav.NamedReference, context): # type inference # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): - assert expr.reference.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert expr.reference.ty.kind.is_int ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) expr.ty = ty @@ -289,14 +289,14 @@ def _(self, expr: behav.NamedReference, context): assert isinstance(reference.ty, type_info.PrimitiveType) dt = reference.ty.kind sz = reference.ty.size - assert dt in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert dt.is_int signed = dt == type_info.TypeKind.TYPE_INT ty = type_info.IntegerType(sz, signed) expr.ty = ty elif isinstance(reference, arch.Memory): expr.ty = type_info.IntegerType(reference.ty.size, False) elif isinstance(reference, arch.Intrinsic): - assert expr.reference.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert expr.reference.ty.kind.is_int expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) elif isinstance(reference, arch.Constant): expr.ty = type_info.IntegerType(reference.size, reference.signed) @@ -312,7 +312,7 @@ def _(self, expr: behav.IndexedReference, context): # type inference assert isinstance(expr.reference, arch.Memory) ty = type_info.TypeKind.TYPE_UINT # TODO: Memory class should keep track of dtype, not only size? - assert ty in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert ty.is_int single_mem_acc_size = expr.reference.ty.size ## Simple eval check for ranged access. @@ -343,7 +343,7 @@ def _(self, expr: behav.TypeConv, context): logger.warning("Type conv needs inferred type. Skipping...") return expr assert isinstance(ty, type_info.IntegerType) - assert expr.data_type in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert expr.data_type.is_int ty.signed = expr.data_type == type_info.TypeKind.TYPE_INT if expr.size is not None: ty.size = expr.size @@ -359,7 +359,7 @@ def _(self, expr: behav.Callable, context): if expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_VOID: signed = None else: - assert expr.ref_or_name.ty.kind in [type_info.TypeKind.TYPE_UINT, type_info.TypeKind.TYPE_INT] + assert expr.ref_or_name.ty.kind.is_int signed = expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_INT width = arch.get_const_or_val(expr.ref_or_name.ty.size) expr.ty = type_info.IntegerType(arch.get_const_or_val(width), signed) From 4df2be8f5ffb909f7511be72859d6c3e3b3a24c8 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 13:46:26 +0200 Subject: [PATCH 11/33] [Metmodel] Warning for deprecated IntegerType Constr --- m2isar/metamodel/type_info.py | 8 +++++++- m2isar/transforms/infer_types/visitor.py | 9 +++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index b36bcd0e..abd8d357 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -15,6 +15,7 @@ from typing import Any, Union +logger = logging.getLogger("type_info_logger") class TypeKind(Enum): TYPE_NONE = auto() # NumberLiteral with no type, e.g. 0 or 1 TYPE_VOID = auto() @@ -56,7 +57,12 @@ def __init__(self, kind: TypeKind): class IntegerType(PrimitiveType): def __init__(self, size: int, signed: bool, ptr: Any=None): self.ptr = ptr - super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) + if type(signed) is not bool: + assert signed.is_int, "IntegerType must be of int kind" + super().__init__(signed, size) + else: + logger.warning("Deprecated IntegerType Constructor please use new TypeKind/instead of signed!!!!") + super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) class FloatType: def __init__(self, exponent: int, mantissa: int, size: int): diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index a705cdcf..c1fd9c1f 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -356,13 +356,10 @@ def _(self, expr: behav.TypeConv, context): @generate.register def _(self, expr: behav.Callable, context): if isinstance(expr.ref_or_name, arch.Function): - if expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_VOID: - signed = None - else: + if not(expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_VOID): assert expr.ref_or_name.ty.kind.is_int - signed = expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_INT - width = arch.get_const_or_val(expr.ref_or_name.ty.size) - expr.ty = type_info.IntegerType(arch.get_const_or_val(width), signed) + width = arch.get_const_or_val(expr.ref_or_name.ty.size) + expr.ty = type_info.IntegerType(arch.get_const_or_val(width), expr.ref_or_name.ty.kind) expr.args = [self.generate(stmt, context) for stmt in expr.args] return expr From 30bce2306b728a99a7b315832f4eb1826252c028 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 13:56:55 +0200 Subject: [PATCH 12/33] [Metamodel] Typing remove redundant TypeKind enums --- m2isar/backends/etiss/architecture_writer.py | 2 +- .../backends/etiss/instruction_generator.py | 4 +-- .../backends/etiss/instruction_transform.py | 26 ++++++++--------- m2isar/backends/etiss/instruction_utils.py | 6 ++-- m2isar/backends/isa_manual/writer.py | 6 ++-- .../coredsl2/architecture_model_builder.py | 12 ++++---- .../coredsl2/behavior_model_builder.py | 14 +++++----- m2isar/frontends/coredsl2/parser.py | 2 +- m2isar/metamodel/__init__.py | 2 +- m2isar/metamodel/arch.py | 6 ++-- m2isar/metamodel/behav.py | 2 +- m2isar/metamodel/type_info.py | 28 +++++++++---------- m2isar/metamodel/utils/expr_simplifier.py | 14 +++++----- m2isar/transforms/infer_types/visitor.py | 20 ++++++------- 14 files changed, 72 insertions(+), 72 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 3274a848..7c1e4326 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -192,7 +192,7 @@ 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), type_info.TypeKind.TYPE_UINT) + 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([ diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index 2b65883a..67eaa57c 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -132,7 +132,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.ty.kind == type_info.TypeKind.TYPE_INT 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};' @@ -238,7 +238,7 @@ def gen_rand_suffix(length: int = 8): [cond[0]], [ instr_def.operation.statements, - behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.TypeKind.TYPE_NONE)]) + 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 54ad5bfe..a858aead 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -195,7 +195,7 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): f'{data_type_map[expr.scalar.ty.kind]}{actual_size} {expr.scalar.name}', static, expr.scalar.ty.size, - expr.scalar.ty.kind == type_info.TypeKind.TYPE_INT, + expr.scalar.ty.kind == type_info.TypeKind.INT, line_infos=expr.line_info, ) @@ -292,7 +292,7 @@ def _(self, expr: behav.FunctionCall, context: TransformerContext): arg_str = ', '.join(arch_args + [arg.code for arg in fn_args]) # keep track of signedness of function return value - signed = fn.ty.kind == type_info.TypeKind.TYPE_INT + 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])) @@ -569,7 +569,7 @@ 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 = type_info.TypeKind.TYPE_INT if expr_str.signed else type_info.TypeKind.TYPE_UINT + 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: @@ -579,7 +579,7 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): code_str = expr_str.code # sign extension for non-2^N datatypes - if expr.data_type == type_info.TypeKind.TYPE_INT and actual_size(expr_str.size) != expr_str.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): @@ -591,7 +591,7 @@ def _(self, expr: behav.TypeConv, context: TransformerContext): else: 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 == type_info.TypeKind.TYPE_INT, 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 @@ -624,13 +624,13 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): elif isinstance(referred_var, arch.BitFieldDescr): # function argument - signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + signed = referred_var.ty.kind == type_info.TypeKind.INT size = referred_var.ty.size static = attribute_info.StaticAttribute.READ elif isinstance(referred_var, arch.Scalar): assert isinstance(referred_var.ty, type_info.PrimitiveType) - signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + signed = referred_var.ty.kind == type_info.TypeKind.INT size = referred_var.ty.size if context.static_scalars: static = referred_var.static @@ -642,7 +642,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): name = f'{referred_var.value}' elif isinstance(referred_var, arch.FnParam): - signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + signed = referred_var.ty.kind == type_info.TypeKind.INT size = referred_var.ty.size static = attribute_info.StaticAttribute.RW @@ -650,7 +650,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): if context.ignore_static: raise TypeError("intrinsic not allowed in function") - signed = referred_var.ty.kind == type_info.TypeKind.TYPE_INT + signed = referred_var.ty.kind == type_info.TypeKind.INT size = referred_var.ty.size static = attribute_info.StaticAttribute.READ @@ -790,11 +790,11 @@ def _(self, expr: behav.ConcatOperation, context: TransformerContext): @generate.register def _(self, expr: behav.Literal, context: TransformerContext): - if expr.ty.kind is type_info.TypeKind.TYPE_STR: + 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.TYPE_NONE: + elif expr.ty.kind == type_info.TypeKind.NONE: lit = int(expr.value) size = min(lit.bit_length(), 64) sign = lit < 0 @@ -807,13 +807,13 @@ def _(self, expr: behav.Literal, context: TransformerContext): return CodeString(str(twocomp_lit) + postfix, True, size, sign, line_infos=expr.line_info) # IntLiteral else: - assert(expr.ty.kind in [type_info.TypeKind.TYPE_INT, type_info.TypeKind.TYPE_UINT]) + 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.TYPE_INT: + 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: diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index f8f98221..252dae79 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -18,9 +18,9 @@ from . import replacements data_type_map = { - type_info.TypeKind.TYPE_INT: 'etiss_int', - type_info.TypeKind.TYPE_UINT: 'etiss_uint', - type_info.TypeKind.TYPE_VOID: 'void' + type_info.TypeKind.INT: 'etiss_int', + type_info.TypeKind.UINT: 'etiss_uint', + type_info.TypeKind.VOID: 'void' } diff --git a/m2isar/backends/isa_manual/writer.py b/m2isar/backends/isa_manual/writer.py index 1fd2a986..9c48c60d 100644 --- a/m2isar/backends/isa_manual/writer.py +++ b/m2isar/backends/isa_manual/writer.py @@ -145,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 == type_info.TypeKind.TYPE_UINT: + if data_type == type_info.TypeKind.UINT: self.write("unsigned") - elif data_type == type_info.TypeKind.TYPE_INT: + elif data_type == type_info.TypeKind.INT: self.write("signed") - elif data_type == type_info.TypeKind.TYPE_VOID: + elif data_type == type_info.TypeKind.VOID: self.write("void") else: raise NotImplementedError(f"Unsupported type: {data_type}") diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index df40aacf..bd1fba8c 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -61,7 +61,7 @@ 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, type_info.TypeKind.TYPE_UINT) + 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.""" @@ -213,7 +213,7 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex params = [params] return_size = None - data_type = type_info.TypeKind.TYPE_VOID + data_type = type_info.TypeKind.VOID if isinstance(type_, type_info.IntegerType): return_size = type_.size @@ -281,7 +281,7 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): else: width = value.bit_length() - kind = type_info.TypeKind.TYPE_UINT if value >=0 else type_info.TypeKind.TYPE_INT + 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): @@ -356,7 +356,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init is not None: init = self.visit(decl.init) - signed = False if type_.kind == type_info.TypeKind.TYPE_INT else False + signed = False if type_.kind == type_info.TypeKind.INT else False c = arch.Constant(name, init, [], type_.size, signed) @@ -437,7 +437,7 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type.""" - return type_info.PrimitiveType(type_info.TypeKind.TYPE_VOID, 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>).""" @@ -489,7 +489,7 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): value = SHORTHANDS[ctx.children[0].symbol.text] - return behav.Literal(value, type_info.TypeKind.TYPE_NONE) + return behav.Literal(value, type_info.TypeKind.NONE) def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionContext): """Generate an assignment. """ diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 0dbca58f..0ba7e09a 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -351,7 +351,7 @@ def visitInteger_constant(self, ctx: CoreDSL2Parser.Integer_constantContext): value = int(text, 0) width = value.bit_length() - kind = type_info.TypeKind.TYPE_INT if value <= 0 else type_info.TypeKind.TYPE_UINT + 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)) @@ -362,7 +362,7 @@ def visitCharacter_constant(self, ctx: CoreDSL2Parser.Character_constantContext) value = min(ord(text.replace("'", "")), 255) - return behav.Literal(value, type_info.TypeKind.TYPE_UINT, size=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 @@ -370,14 +370,14 @@ def visitString_constant(self, ctx: CoreDSL2Parser.String_constantContext): assert text[0] == '"' and text[-1] == '"' text = text[1:-1] - return behav.Literal(text, type_info.TypeKind.TYPE_STR, 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.Literal(BOOLCONST[text], type_info.TypeKind.TYPE_UINT, size=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.""" @@ -390,7 +390,7 @@ def visitCast_expression(self, ctx: CoreDSL2Parser.Cast_expressionContext): if ctx.sign: sign = self.visit(ctx.sign) - kind = type_info.TypeKind.TYPE_INT if sign else type_info.TypeKind.TYPE_UINT + kind = type_info.TypeKind.INT if sign else type_info.TypeKind.UINT size = None 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)) @@ -428,7 +428,7 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type specifier.""" - return type_info.PrimitiveType(type_info.TypeKind.TYPE_VOID, 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>.""" @@ -443,4 +443,4 @@ def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext) def visitInteger_shorthand(self, ctx: CoreDSL2Parser.Integer_shorthandContext): """Lookup a shorthand type specifier.""" - return behav.Literal(SHORTHANDS[ctx.children[0].symbol.text], type_info.TypeKind.TYPE_NONE, 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/parser.py b/m2isar/frontends/coredsl2/parser.py index 6b105568..2176a8f9 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -273,7 +273,7 @@ def main(): behav.BinaryOperation( behav.NamedReference(core_def.pc_memory), behav.Operator("+"), - behav.Literal(int(instr_def.size/8), type_info.TypeKind.TYPE_UINT) + behav.Literal(int(instr_def.size/8), type_info.TypeKind.UINT) ) ) diff --git a/m2isar/metamodel/__init__.py b/m2isar/metamodel/__init__.py index 02e6061c..cc40100a 100644 --- a/m2isar/metamodel/__init__.py +++ b/m2isar/metamodel/__init__.py @@ -73,7 +73,7 @@ def patch_model(module): param.annotation.generate = fn intrinsic_defs = [ - arch.Intrinsic("__encoding_size", 16, type_info.TypeKind.TYPE_UINT), + 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 e764c662..e2534632 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -198,7 +198,7 @@ class FnParam(Named): """The array width of this parameter.""" def __init__(self, name, size, kind: type_info.TypeKind, width=1): - self.ty = type_info.IntegerType(size, True if kind == type_info.TypeKind.TYPE_INT else False) + self.ty = type_info.IntegerType(size, True if kind == type_info.TypeKind.INT else False) self._width = width super().__init__(name) @@ -344,7 +344,7 @@ def __init__(self, name, _range: RangeSpec, kind: type_info.TypeKind): self.range = _range self.ty = type_info.BitFieldType(kind) if not kind: - self.ty = type_info.BitFieldType(type_info.TypeKind.TYPE_UINT) + self.ty = type_info.BitFieldType(type_info.TypeKind.UINT) super().__init__(name) @@ -359,7 +359,7 @@ class BitFieldDescr(Named): 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.IntegerType(get_const_or_val(size), True if kind == type_info.TypeKind.TYPE_INT else False) + self.ty = type_info.IntegerType(get_const_or_val(size), True if kind == type_info.TypeKind.INT else False) super().__init__(name) diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index d298e058..c901607e 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -95,7 +95,7 @@ def __init__(self, left: BaseNode, right: BaseNode, line_info=None) -> None: class Literal(BaseNode): - def __init__(self, value:int, kind : TypeKind = TypeKind.TYPE_INT, size=None, base=10, line_info=None): + def __init__(self, value:int, kind : TypeKind = TypeKind.INT, size=None, base=10, line_info=None): super().__init__(line_info) self._value : Union[int, str] = value diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index abd8d357..56052c68 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -17,31 +17,31 @@ logger = logging.getLogger("type_info_logger") class TypeKind(Enum): - TYPE_NONE = auto() # NumberLiteral with no type, e.g. 0 or 1 - TYPE_VOID = auto() - TYPE_UINT = auto() - TYPE_INT = auto() - TYPE_CHAR = auto() - # TYPE_BOOL = auto() Expressed as unsigned<1> - TYPE_FLOAT = auto() - TYPE_STR = auto() - TYPE_ARRAY = auto() + 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() + ARRAY = auto() @property def is_int(self): - return self in (TypeKind.TYPE_INT, TypeKind.TYPE_UINT) + return self in (TypeKind.INT, TypeKind.UINT) @property def is_numeric(self): - return self in (TypeKind.TYPE_INT, TypeKind.TYPE_UINT, TypeKind.TYPE_FLOAT) + return self in (TypeKind.INT, TypeKind.UINT, TypeKind.FLOAT) @property def is_void(self): - return self == TypeKind.TYPE_VOID + return self == TypeKind.VOID @property def is_scalar(self): - return self not in (TypeKind.TYPE_ARRAY, TypeKind.TYPE_VOID, TypeKind.TYPE_NONE) + return self not in (TypeKind.ARRAY, TypeKind.VOID, TypeKind.NONE) class PrimitiveType: @@ -62,7 +62,7 @@ def __init__(self, size: int, signed: bool, ptr: Any=None): super().__init__(signed, size) else: logger.warning("Deprecated IntegerType Constructor please use new TypeKind/instead of signed!!!!") - super().__init__(TypeKind.TYPE_INT if signed else TypeKind.TYPE_UINT, size) + super().__init__(TypeKind.INT if signed else TypeKind.UINT, size) class FloatType: def __init__(self, exponent: int, mantissa: int, size: int): diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index f3992e0b..fcdda8c1 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -71,10 +71,10 @@ def _(self, expr: behav.BinaryOperation, context): 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}")) - if res < 0 or expr.left.ty.kind == type_info.TypeKind.TYPE_INT or expr.right.ty.kind == type_info.TypeKind.TYPE_INT: - kind = type_info.TypeKind.TYPE_INT + 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.TYPE_UINT + 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 == "&&": @@ -204,7 +204,7 @@ def _(self, expr: behav.UnaryOperation, context): # pylint: disable=eval-used res: int = eval(f"{expr.op.value}{expr.right.value}") if res < 0: - kind = type_info.TypeKind.TYPE_INT + kind = type_info.TypeKind.INT else: kind = expr.right.ty.kind return behav.Literal(res, kind, max(expr.right.ty.size, res.bit_length())) @@ -215,9 +215,9 @@ def _(self, expr: behav.UnaryOperation, context): def _(self, expr: behav.NamedReference, context): if isinstance(expr.reference, arch.Constant): if expr.reference.signed: - kind = type_info.TypeKind.TYPE_INT + kind = type_info.TypeKind.INT else: - kind = type_info.TypeKind.TYPE_UINT + kind = type_info.TypeKind.UINT return behav.Literal(expr.reference.value, kind, arch.get_const_or_val(expr.reference.size)) return expr @@ -239,7 +239,7 @@ def _(self, expr: behav.TypeConv, context): assert size is not None expr.expr.bit_size = size assert expr.expr.bit_size is not None - expr.expr.signed = expr.data_type == type_info.TypeKind.TYPE_INT + expr.expr.signed = expr.data_type == type_info.TypeKind.INT return expr.expr return expr diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index c1fd9c1f..bca92c3a 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -68,8 +68,8 @@ def _(self, expr: behav.BinaryOperation, context): assert isinstance(expr.right.ty, type_info.IntegerType) 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.TYPE_INT else False - s2 = True if expr.right.ty.kind == type_info.TypeKind.TYPE_INT else False + 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 @@ -182,7 +182,7 @@ def _(self, expr: behav.Literal, context): # type inference assert(expr.ty.size is not None) assert(expr.ty.kind.is_int) - signed = True if expr.ty.kind == type_info.TypeKind.TYPE_INT else False #or arch.get_const_or_val(expr.value) < 0 else False + signed = True if expr.ty.kind == type_info.TypeKind.INT else False #or arch.get_const_or_val(expr.value) < 0 else False expr.ty = type_info.IntegerType(expr.ty.size, signed) return expr @@ -282,7 +282,7 @@ def _(self, expr: behav.NamedReference, context): # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): assert expr.reference.ty.kind.is_int - ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) + ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.INT) expr.ty = ty elif isinstance(reference, arch.Scalar): @@ -290,14 +290,14 @@ def _(self, expr: behav.NamedReference, context): dt = reference.ty.kind sz = reference.ty.size assert dt.is_int - signed = dt == type_info.TypeKind.TYPE_INT + signed = dt == type_info.TypeKind.INT ty = type_info.IntegerType(sz, signed) expr.ty = ty elif isinstance(reference, arch.Memory): expr.ty = type_info.IntegerType(reference.ty.size, False) elif isinstance(reference, arch.Intrinsic): assert expr.reference.ty.kind.is_int - expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.TYPE_INT) + expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.INT) elif isinstance(reference, arch.Constant): expr.ty = type_info.IntegerType(reference.size, reference.signed) else: @@ -311,7 +311,7 @@ def _(self, expr: behav.IndexedReference, context): # type inference assert isinstance(expr.reference, arch.Memory) - ty = type_info.TypeKind.TYPE_UINT # TODO: Memory class should keep track of dtype, not only size? + ty = type_info.TypeKind.UINT # TODO: Memory class should keep track of dtype, not only size? assert ty.is_int single_mem_acc_size = expr.reference.ty.size @@ -328,7 +328,7 @@ def _(self, expr: behav.IndexedReference, context): assert(lhs_offset >= rhs_offset) size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size - ty_ = type_info.IntegerType(size, ty == type_info.TypeKind.TYPE_INT) + ty_ = type_info.IntegerType(size, ty == type_info.TypeKind.INT) expr.ty = ty_ @@ -344,7 +344,7 @@ def _(self, expr: behav.TypeConv, context): return expr assert isinstance(ty, type_info.IntegerType) assert expr.data_type.is_int - ty.signed = expr.data_type == type_info.TypeKind.TYPE_INT + ty.signed = expr.data_type == type_info.TypeKind.INT if expr.size is not None: ty.size = expr.size @@ -356,7 +356,7 @@ def _(self, expr: behav.TypeConv, context): @generate.register def _(self, expr: behav.Callable, context): if isinstance(expr.ref_or_name, arch.Function): - if not(expr.ref_or_name.ty.kind == type_info.TypeKind.TYPE_VOID): + 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.IntegerType(arch.get_const_or_val(width), expr.ref_or_name.ty.kind) From e8ac700e356cd3d13bcbe8bbea6254aacff50819 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 14:15:30 +0200 Subject: [PATCH 13/33] [MetaModel] Avoid Deprecated IntegerType constructor --- .../coredsl2/architecture_model_builder.py | 6 ++-- .../coredsl2/behavior_model_builder.py | 6 ++-- m2isar/metamodel/arch.py | 4 +-- m2isar/metamodel/type_info.py | 11 +++--- m2isar/transforms/infer_types/visitor.py | 36 ++++++++++--------- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index bd1fba8c..066a5cdc 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -433,7 +433,9 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): else: raise M2TypeError("width has wrong type") - return type_info.IntegerType(width, signed) + kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT + + return type_info.IntegerType(width, kind) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type.""" @@ -441,7 +443,7 @@ def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool (alias for unsigned<1>).""" - return type_info.IntegerType(1, False) + return type_info.IntegerType(1, type_info.TypeKind.UINT) def visitBinary_expression(self, ctx: CoreDSL2Parser.Binary_expressionContext): """Generate a binary expression.""" diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 0ba7e09a..697aad3f 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -423,7 +423,9 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): else: raise M2TypeError("width has wrong type") - return type_info.IntegerType(width, signed) + kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT + + return type_info.IntegerType(width, kind) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type specifier.""" @@ -433,7 +435,7 @@ def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool type specifier. Aliases to unsigned<1>.""" - return type_info.IntegerType(1, False) + return type_info.IntegerType(1, type_info.TypeKind.UINT) def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext): """Generate integer signedness.""" diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index e2534632..6b572323 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -198,7 +198,7 @@ class FnParam(Named): """The array width of this parameter.""" def __init__(self, name, size, kind: type_info.TypeKind, width=1): - self.ty = type_info.IntegerType(size, True if kind == type_info.TypeKind.INT else False) + self.ty = type_info.IntegerType(size, kind) self._width = width super().__init__(name) @@ -359,7 +359,7 @@ class BitFieldDescr(Named): 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.IntegerType(get_const_or_val(size), True if kind == type_info.TypeKind.INT else False) + self.ty = type_info.IntegerType(get_const_or_val(size), kind) super().__init__(name) diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 56052c68..4201bf41 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -53,16 +53,15 @@ class BitFieldType(): def __init__(self, kind: TypeKind): self.kind = kind -#removed get_const_or_val class IntegerType(PrimitiveType): - def __init__(self, size: int, signed: bool, ptr: Any=None): + def __init__(self, size: int, signed_or_kind: Union[bool, TypeKind], ptr: Any=None): self.ptr = ptr - if type(signed) is not bool: - assert signed.is_int, "IntegerType must be of int kind" - super().__init__(signed, size) + if type(signed_or_kind) is not bool: + assert signed_or_kind.is_int, "IntegerType must be of int kind" + super().__init__(signed_or_kind, size) else: logger.warning("Deprecated IntegerType Constructor please use new TypeKind/instead of signed!!!!") - super().__init__(TypeKind.INT if signed else TypeKind.UINT, size) + super().__init__(TypeKind.INT if signed_or_kind else TypeKind.UINT, size) class FloatType: def __init__(self, exponent: int, mantissa: int, size: int): diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index bca92c3a..d2f82e3a 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -118,12 +118,14 @@ def _(self, expr: behav.BinaryOperation, context): elif expr.op.value in [">>", "<<"]: wr = w1 sr = s1 - expr.ty = type_info.IntegerType(wr, sr) + + kind = type_info.TypeKind.INT if sr else type_info.TypeKind.UINT + expr.ty = type_info.IntegerType(wr, kind) else: if expr.op.value in ["||", "&&"]: - expr.ty = type_info.IntegerType(1, False) # unsigned<1> / bool + expr.ty = type_info.IntegerType(1, type_info.TypeKind.UINT) # unsigned<1> / bool elif expr.op.value in ["<", ">", "==", "!=", ">=", "<="]: - expr.ty = type_info.IntegerType(1, False) # unsigned<1> / bool + expr.ty = type_info.IntegerType(1, type_info.TypeKind.UINT) # unsigned<1> / bool assert expr.ty is not None return expr @@ -170,7 +172,7 @@ def _(self, expr: behav.ConcatOperation, context): return expr 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.IntegerType(size, False) + ty = type_info.IntegerType(size, type_info.TypeKind.UINT) expr.ty = ty return expr @@ -182,8 +184,7 @@ def _(self, expr: behav.Literal, context): # type inference assert(expr.ty.size is not None) assert(expr.ty.kind.is_int) - signed = True if expr.ty.kind == type_info.TypeKind.INT else False #or arch.get_const_or_val(expr.value) < 0 else False - expr.ty = type_info.IntegerType(expr.ty.size, signed) + expr.ty = type_info.IntegerType(expr.ty.size, expr.ty.kind) return expr @@ -246,7 +247,7 @@ def _(self, expr: behav.Ternary, context): wt = arch.get_const_or_val(then_ty.size) we = arch.get_const_or_val(else_ty.size) wr = max(wt, we) - expr.ty = type_info.IntegerType(wr, True) + expr.ty = type_info.IntegerType(wr, type_info.TypeKind.INT) return expr @@ -263,11 +264,11 @@ def _(self, expr: behav.UnaryOperation, context): if expr.right.ty: w1 = expr.right.ty.size if expr.op.value == "-": - ty = type_info.IntegerType(w1 + 1, True) + ty = type_info.IntegerType(w1 + 1, type_info.TypeKind.INT) elif expr.op.value == "~": - ty = type_info.IntegerType(w1, True) + ty = type_info.IntegerType(w1, type_info.TypeKind.INT) elif expr.op.value == "!": - ty = type_info.IntegerType(1, False) + ty = type_info.IntegerType(1, type_info.TypeKind.UINT) else: ty = None expr.ty = ty @@ -282,7 +283,7 @@ def _(self, expr: behav.NamedReference, context): # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): assert expr.reference.ty.kind.is_int - ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.INT) + ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) expr.ty = ty elif isinstance(reference, arch.Scalar): @@ -290,16 +291,17 @@ def _(self, expr: behav.NamedReference, context): dt = reference.ty.kind sz = reference.ty.size assert dt.is_int - signed = dt == type_info.TypeKind.INT - ty = type_info.IntegerType(sz, signed) + ty = type_info.IntegerType(sz, dt) expr.ty = ty elif isinstance(reference, arch.Memory): - expr.ty = type_info.IntegerType(reference.ty.size, False) + expr.ty = type_info.IntegerType(reference.ty.size, type_info.TypeKind.UINT) elif isinstance(reference, arch.Intrinsic): assert expr.reference.ty.kind.is_int - expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind == type_info.TypeKind.INT) + expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) elif isinstance(reference, arch.Constant): - expr.ty = type_info.IntegerType(reference.size, reference.signed) + kind = type_info.TypeKind.INT if reference.signed else type_info.TypeKind.UINT + + expr.ty = type_info.IntegerType(reference.size, kind) else: assert False, "Unhandled reference" @@ -328,7 +330,7 @@ def _(self, expr: behav.IndexedReference, context): assert(lhs_offset >= rhs_offset) size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size - ty_ = type_info.IntegerType(size, ty == type_info.TypeKind.INT) + ty_ = type_info.IntegerType(size, ty) expr.ty = ty_ From 9a47542c4523fed0819de3d5dafa01862a27b6e5 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Thu, 30 Apr 2026 14:22:01 +0200 Subject: [PATCH 14/33] [MetaModel] Lit = 0 needs to be signed (Warning in GCC) --- m2isar/backends/etiss/instruction_transform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index a858aead..1421a20d 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -797,7 +797,7 @@ def _(self, expr: behav.Literal, context: TransformerContext): elif expr.ty.kind == type_info.TypeKind.NONE: lit = int(expr.value) size = min(lit.bit_length(), 64) - sign = lit < 0 + sign = lit <= 0 # TODO: Look in diff. U is sometimes there for negative vals!!! @@ -813,7 +813,7 @@ def _(self, expr: behav.Literal, context: TransformerContext): size = lit.bit_length() size = min(expr.ty.size, 128) - if expr.value < 0 or expr.ty.kind == type_info.TypeKind.INT: + 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: From 1627b739736500f2ba9f25382c671c2b982784f7 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 30 Apr 2026 23:14:55 +0200 Subject: [PATCH 15/33] [MetaModel] Abstract scalar class in more abstract Symbol Also resolve small merge issues --- .../backends/etiss/instruction_transform.py | 2 +- .../templates/etiss_arch_specific_h.mako | 20 ++--- m2isar/backends/etiss/virtualstruct_utils.py | 2 +- .../coredsl2/architecture_model_builder.py | 6 +- .../coredsl2/behavior_model_builder.py | 2 +- m2isar/metamodel/arch.py | 73 +++++-------------- m2isar/metamodel/attribute_info.py | 9 ++- m2isar/metamodel/type_info.py | 4 +- m2isar/metamodel/utils/function_staticness.py | 4 +- m2isar/metamodel/utils/scalar_staticness.py | 4 +- m2isar/transforms/infer_types/visitor.py | 2 +- 11 files changed, 49 insertions(+), 79 deletions(-) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 88610a56..af2f26f2 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -628,7 +628,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): size = referred_var.ty.size static = attribute_info.StaticAttribute.READ - elif isinstance(referred_var, arch.Scalar): + elif isinstance(referred_var, arch.Symbol): assert isinstance(referred_var.ty, type_info.PrimitiveType) signed = referred_var.ty.kind == type_info.TypeKind.INT size = referred_var.ty.size diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index a13cf831..189c164f 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -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.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.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.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.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.range.length // 32) // (vector_reg.ty.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.range.length // 32) // (vector_reg.ty.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.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.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.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.size}) val); // clang-format on } }; diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 82a4492d..6f0a8884 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.attribute_info import MemoryAttribute 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"} diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 1ee77372..085d8b8b 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -399,11 +399,11 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if attribute_info.MemoryAttribute.IS_MAIN_REG in attributes: self._main_reg_file = m - if arch.MemoryAttribute.IS_FLOAT_REG in attributes: + if attribute_info.MemoryAttribute.IS_FLOAT_REG in attributes: self._float_reg_file = m - if arch.MemoryAttribute.IS_VECTOR_REG in attributes: + if attribute_info.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 diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 697aad3f..c1930a68 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -126,7 +126,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # instantiate a scalar and its definition - s = arch.Scalar(name, type_.kind, type_.size, None, attribute_info.StaticAttribute.NONE) + s = arch.Symbol(name, type_, None, attribute_info.StaticAttribute.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 7fa582d0..cf4183f3 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -87,13 +87,13 @@ class Constant(SizedRefOrConst): _value: Union[int, "Constant", "BaseNode"] """The value this object holds. Can be an int, another constant or a statically resolvable BaseNode.""" - attributes: "dict[type_info.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[type_info.ConstAttribute, list[BaseNode]]", size=None, signed=False): + def __init__(self, name, value: Union[int, "Constant", "BaseNode"], attributes: "dict[attribute_info.ConstAttribute, list[BaseNode]]", size=None, signed=False): self._value = value self.attributes = attributes if attributes else {} self.signed = signed @@ -211,60 +211,27 @@ def width(self): def __str__(self) -> str: return f'{super().__str__()}, type={self.ty}' -class Scalar(Named): - """A scalar variable object, used mainly in behavior descriptions.""" - ty: Union[type_info.PrimitiveType] - static: attribute_info.StaticAttribute - value: int - - def __init__(self, - name, - kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], - size : int, - value: int, # Compile Time information - static: attribute_info.StaticAttribute, - storage=None - ): - self.ty = type_info.PrimitiveType(kind, size) if isinstance(kind, type_info.TypeKind) else kind - - self.static = static - - # optional: only for constants/literals - self.value = value - - # optional: backend info (register, memory, etc.) - self.storage = storage - super().__init__(name) - - -class Array(Named): - """A variable object for an Array, used mainly in behavior descriptions.""" - - ty: type_info.ArrayType - static: attribute_info.StaticAttribute - values: list[int] - - def __init__(self, - name, - kind : Union[type_info.TypeKind, type_info.IntegerType, type_info.FloatType], - size : int, - length : int, - values: list[int], - static: attribute_info.StaticAttribute, # Compile Time information - storage=None - ): - # Explcit casting for now allowed??? - self.ty = type_info.ArrayType(type_info.PrimitiveType(kind, size), length) if isinstance(kind, type_info.TypeKind) else kind +class Symbol(Named): + def __init__( + self, + name: str, + type: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType], + static: attribute_info.StaticAttribute = attribute_info.StaticAttribute.RW, + value=None, # Compile Time information + storage=None, + ): + assert isinstance(type, (type_info.PrimitiveType, type_info.IntegerType)) + assert type.kind.is_scalar + self.ty = type + self.static = static - self.static = static - - # optional: only for constants/literals - self.values = values + # optional: only for constants/literals + self.value = value # optional: backend info (register, memory, etc.) - self.storage = storage - super().__init__(name) + self.storage = storage + super().__init__(name) class Intrinsic(Named): @@ -591,4 +558,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 index 468b8fb8..5838d95b 100644 --- a/m2isar/metamodel/attribute_info.py +++ b/m2isar/metamodel/attribute_info.py @@ -6,6 +6,9 @@ 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() @@ -40,9 +43,9 @@ class InstrAttribute(Enum): class StaticAttribute(IntFlag): """Describes the staticness of a Scalar or Function""" - NONE = 0 - READ = auto() - WRITE = auto() + NONE = 0 #no access at all + READ = auto() # read access only + WRITE = auto() # write access only RW = READ | WRITE @dataclass diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 4201bf41..b900b655 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -13,7 +13,7 @@ from enum import Enum, auto from typing import Any, Union - +import logging logger = logging.getLogger("type_info_logger") class TypeKind(Enum): @@ -41,7 +41,7 @@ def is_void(self): @property def is_scalar(self): - return self not in (TypeKind.ARRAY, TypeKind.VOID, TypeKind.NONE) + return self in (TypeKind.INT, TypeKind.UINT, TypeKind.CHAR, TypeKind.FLOAT) class PrimitiveType: diff --git a/m2isar/metamodel/utils/function_staticness.py b/m2isar/metamodel/utils/function_staticness.py index db2a0f0d..ca61c92f 100644 --- a/m2isar/metamodel/utils/function_staticness.py +++ b/m2isar/metamodel/utils/function_staticness.py @@ -120,7 +120,7 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Scalar): + if isinstance(expr.reference, arch.Symbol): return expr.reference.static static_map = { @@ -128,7 +128,7 @@ def _(self, expr: behav.NamedReference, context): arch.BitFieldDescr: True, arch.Constant: True, arch.FnParam: True, - arch.Scalar: True, + arch.Symbol: True, arch.Intrinsic: False } diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index 565f003b..8271535b 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -95,7 +95,7 @@ def _(self, expr: behav.Assignment, context: attribute_info.ScalarStaticnessCont else: 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.Symbol): target_ref = cast(Any, expr.target.reference) target_ref.static &= expr_static @@ -138,7 +138,7 @@ def _(self, expr: behav.UnaryOperation, context: attribute_info.ScalarStaticness @generate.register def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticnessContext): - if isinstance(expr.reference, arch.Scalar): + if isinstance(expr.reference, arch.Symbol): return expr.reference.static static_map = { diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index d2f82e3a..e8e5f532 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -286,7 +286,7 @@ def _(self, expr: behav.NamedReference, context): ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) expr.ty = ty - elif isinstance(reference, arch.Scalar): + elif isinstance(reference, arch.Symbol): assert isinstance(reference.ty, type_info.PrimitiveType) dt = reference.ty.kind sz = reference.ty.size From 7bec7bb99def59fe556b662bb5a4c6f78a37c0f0 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Fri, 1 May 2026 23:05:34 +0200 Subject: [PATCH 16/33] [MetaModel] WIP abstract Symbol class for ARCH Common Subclass for - Alias - RegisterBanks - Memory - BitFieldDescr - Variables --- .../backends/etiss/instruction_transform.py | 4 +- .../coredsl2/behavior_model_builder.py | 2 +- m2isar/metamodel/arch.py | 150 ++++++++++++++---- m2isar/metamodel/attribute_info.py | 7 + m2isar/metamodel/type_info.py | 8 +- m2isar/metamodel/utils/function_staticness.py | 6 +- m2isar/metamodel/utils/scalar_staticness.py | 8 +- m2isar/transforms/infer_types/visitor.py | 2 +- 8 files changed, 139 insertions(+), 48 deletions(-) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index af2f26f2..d656b83c 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -628,12 +628,12 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): size = referred_var.ty.size static = attribute_info.StaticAttribute.READ - elif isinstance(referred_var, arch.Symbol): + 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): signed = referred_var.value < 0 diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index c1930a68..52c0d4d1 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -126,7 +126,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # instantiate a scalar and its definition - s = arch.Symbol(name, type_, None, attribute_info.StaticAttribute.NONE) + s = arch.Variable(name, type_, attribute_info.StaticAttribute.NONE) self._scalars[name] = s sd = behav.ScalarDefinition(s) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index cf4183f3..c94afcb9 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -213,58 +213,138 @@ def __str__(self) -> str: 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, - type: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType], + ty: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType, type_info.MemoryType, 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 - storage=None, ): - assert isinstance(type, (type_info.PrimitiveType, type_info.IntegerType)) - assert type.kind.is_scalar - self.ty = type - self.static = static + assert isinstance(ty, (type_info.PrimitiveType, type_info.IntegerType)) + assert ty.kind.is_scalar # optional: only for constants/literals self.value = value - # optional: backend info (register, memory, etc.) - self.storage = storage - super().__init__(name) - + super().__init__(name, ty, attributes={"static": static}) -class Intrinsic(Named): +class Intrinsic(Symbol): value: int - ty: type_info.PrimitiveType def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind, value: int = None): - self.ty = type_info.PrimitiveType(kind, get_const_or_val(size)) self.value = value - super().__init__(name) + super().__init__(name, type_info.PrimitiveType(kind, get_const_or_val(size))) + + +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.""" + range: RangeSpec + children: "list[Memory]" + _initval: "dict[int, Union[int, Constant, BaseNode]]" + + def __init__(self, name, range_: RangeSpec, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): + self.range = range_ + self.children = [] + self.parent = None + self._initval = {} + super().__init__(name, type_info.MemoryType(size), attributes) + + def initval(self, idx=None): + """Return the initial value for the given index.""" + + 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.""" + + if self.range.upper is None or self.range.lower is None: + return None + + return RangeSpec(self.range.upper - self.range.lower, 0) -class Memory(Named): + @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 + + +# TODO: decide later if you wanna keep lhs information : +# unsigned& S0 = X[8]; vs +# Intention: alias S0 <- X[8]; + +#TODO IndexedReference without right re-defined??? +class RegisterRef: + def __init__(self, reference, index): + self.reference = reference + self.index = index + + + def resolve(self, context): + bank = context[self.reference] + return bank[self.index] + + +class Alias(Symbol): + """A class representing a register. A register is a single element of a register bank, + which is used to represent registers in the architectural part of an M2-ISA-R model.""" + target: Union[RegisterRef, Symbol] + initval = 0, + + def __init__(self, name, initval: int, target: RegisterBank, attributes: dict = {}): + self.target = target + self.initval = initval + assert isinstance(target.ty, type_info.PrimitiveType) + super().__init__(name, type_info.PointerType(target.ty), attributes) + + def resolve(self, context): + target = self.target + + if isinstance(target, RegisterRef): + return target.resolve(context) + + while isinstance(target, Alias): + target = target.target + + return target + + +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. """ - ty : type_info.MemoryType range: RangeSpec - attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]" children: "list[Memory]" parent: Union['Memory', None] _initval: "dict[int, Union[int, Constant, BaseNode]]" def __init__(self, name, range_: RangeSpec, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): - self.ty = type_info.MemoryType(size) - self.attributes = attributes if attributes else {} self.range = range_ self.children = [] self.parent = None self._initval = {} - super().__init__(name) + super().__init__(name, type_info.MemoryType(size), attributes) def initval(self, idx=None): """Return the initial value for the given index.""" @@ -290,16 +370,8 @@ 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 -@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 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. """ @@ -309,11 +381,10 @@ class BitField(Named): def __init__(self, name, _range: RangeSpec, kind: type_info.TypeKind): self.range = _range - self.ty = type_info.BitFieldType(kind) 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.kind}' @@ -321,6 +392,15 @@ def __str__(self) -> str: def __repr__(self): return self.__str__() +@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. @@ -333,7 +413,7 @@ def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind): 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 @@ -341,13 +421,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 = "" @@ -399,7 +479,7 @@ class Function(Named): extern: bool ext_name: str - scalars: "dict[str, Scalar]" + scalars: "dict[str, Symbol]" throws: bool static: attribute_info.StaticAttribute diff --git a/m2isar/metamodel/attribute_info.py b/m2isar/metamodel/attribute_info.py index 5838d95b..658c176f 100644 --- a/m2isar/metamodel/attribute_info.py +++ b/m2isar/metamodel/attribute_info.py @@ -16,6 +16,13 @@ class MemoryAttribute(Enum): ETISS_IS_IRQ_PENDING = auto() ETISS_IS_PROCNO = auto() +class RegisterAttribute(Enum): + IS_PC = auto() + IS_GPR_REG = auto() + IS_FLOAT_REG = auto() + IS_VECTOR_REG = auto() + IS_CSR_REG = auto() + class FunctionAttribute(Enum): ETISS_STATICFN = auto() ETISS_NEEDS_ARCH = auto() diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index b900b655..41f9886f 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -11,6 +11,7 @@ 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 @@ -25,7 +26,6 @@ class TypeKind(Enum): # BOOL = auto() Expressed as unsigned<1> FLOAT = auto() STR = auto() - ARRAY = auto() @property def is_int(self): @@ -60,7 +60,7 @@ def __init__(self, size: int, signed_or_kind: Union[bool, TypeKind], ptr: Any=No assert signed_or_kind.is_int, "IntegerType must be of int kind" super().__init__(signed_or_kind, size) else: - logger.warning("Deprecated IntegerType Constructor please use new TypeKind/instead of signed!!!!") + logger.warning("Deprecated IntegerType please use new PrimitiveType instead !!!") super().__init__(TypeKind.INT if signed_or_kind else TypeKind.UINT, size) class FloatType: @@ -74,6 +74,10 @@ def __init__(self, element_type : Union[PrimitiveType, FloatType], length: int): self.element_kind : Union[PrimitiveType, FloatType] = element_type self.length = length # allow shaped later or TYPE_ARRAY in element_type? +@dataclass +class PointerType: + ty: PrimitiveType + class MemoryType: size : int diff --git a/m2isar/metamodel/utils/function_staticness.py b/m2isar/metamodel/utils/function_staticness.py index ca61c92f..557e21f9 100644 --- a/m2isar/metamodel/utils/function_staticness.py +++ b/m2isar/metamodel/utils/function_staticness.py @@ -120,15 +120,15 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Symbol): - 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.FnParam: True, - arch.Symbol: True, + arch.Variable: True, arch.Intrinsic: False } diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index 8271535b..fcaf26d8 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -95,9 +95,9 @@ def _(self, expr: behav.Assignment, context: attribute_info.ScalarStaticnessCont else: expr_static = attribute_info.StaticAttribute.NONE - if isinstance(expr.target, behav.NamedReference) and isinstance(expr.target.reference, arch.Symbol): + 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) @@ -138,8 +138,8 @@ def _(self, expr: behav.UnaryOperation, context: attribute_info.ScalarStaticness @generate.register def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticnessContext): - if isinstance(expr.reference, arch.Symbol): - return expr.reference.static + if isinstance(expr.reference, arch.Variable): + return expr.reference.attributes.get("static") static_map = { arch.Memory: attribute_info.StaticAttribute.NONE, diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index e8e5f532..2c7620cd 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -286,7 +286,7 @@ def _(self, expr: behav.NamedReference, context): ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) expr.ty = ty - elif isinstance(reference, arch.Symbol): + elif isinstance(reference, arch.Variable): assert isinstance(reference.ty, type_info.PrimitiveType) dt = reference.ty.kind sz = reference.ty.size From d2852499f2ba15da81eb56a8a768a4b5965886ef Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Mon, 4 May 2026 09:40:57 +0200 Subject: [PATCH 17/33] [MetaModel] Bugfix use attributes[static] for Symbols --- m2isar/metamodel/utils/scalar_staticness.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index fcaf26d8..bf305468 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -76,7 +76,7 @@ def _(self, expr: behav.Literal, context: attribute_info.ScalarStaticnessContext @generate.register def _(self, expr: behav.ScalarDefinition, context: attribute_info.ScalarStaticnessContext): scalar = cast(Any, expr.scalar) - scalar.static = attribute_info.StaticAttribute.RW + scalar.attributes["static"] = attribute_info.StaticAttribute.RW return attribute_info.StaticAttribute.RW @generate.register @@ -101,7 +101,7 @@ def _(self, expr: behav.Assignment, context: attribute_info.ScalarStaticnessCont 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: attribute_info.ScalarStaticnessContext): From e73670e38244f8631cd6567ed9d7e9ca2c366c2c Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Mon, 4 May 2026 09:42:16 +0200 Subject: [PATCH 18/33] [MetaModel] Get Rid of IntegerType by PointerType --- .../backends/etiss/instruction_transform.py | 7 +++- .../coredsl2/architecture_model_builder.py | 17 ++++---- .../coredsl2/behavior_model_builder.py | 6 +-- m2isar/metamodel/arch.py | 14 +++---- m2isar/metamodel/behav.py | 4 +- m2isar/metamodel/type_info.py | 9 ----- m2isar/transforms/infer_types/visitor.py | 40 +++++++++---------- 7 files changed, 47 insertions(+), 50 deletions(-) diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index d656b83c..32b066c4 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -184,7 +184,7 @@ def _(self, expr: behav.ScalarDefinition, context: TransformerContext): if context.ignore_static: static = attribute_info.StaticAttribute.RW else: - static = expr.scalar.static + static = expr.scalar.attributes["static"] else: static = attribute_info.StaticAttribute.NONE @@ -673,6 +673,7 @@ 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 @@ -686,6 +687,10 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): 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 = attribute_info.StaticAttribute.RW else: diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 085d8b8b..c9bcc085 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -221,7 +221,7 @@ def visitFunction_definition(self, ctx: CoreDSL2Parser.Function_definitionContex return_size = None data_type = type_info.TypeKind.VOID - if isinstance(type_, type_info.IntegerType): + if isinstance(type_, type_info.PrimitiveType): return_size = type_.size data_type = type_.kind @@ -300,7 +300,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # extract data type type_ = self.visit(ctx.type_) - assert isinstance(type_, type_info.IntegerType) + assert isinstance(type_, (type_info.PrimitiveType, type_info.PointerType)) # extract list of contained declarations for the given type decls: "list[CoreDSL2Parser.DeclaratorContext]" = ctx.declarations @@ -312,7 +312,7 @@ 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: raise M2DuplicateError(f"memory {name} already defined") @@ -341,7 +341,7 @@ 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_.size, attributes) + m = arch.Memory(name, range_spec, type_.ty.size, attributes) m.parent = reference m.parent.children.append(m) @@ -362,7 +362,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if decl.init is not None: init = self.visit(decl.init) - signed = False if type_.kind == type_info.TypeKind.INT else False + signed = True if type_.kind == type_info.TypeKind.INT else False c = arch.Constant(name, init, [], type_.size, signed) @@ -414,7 +414,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): @@ -447,7 +448,7 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT - return type_info.IntegerType(width, kind) + return type_info.PrimitiveType(kind, width) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type.""" @@ -455,7 +456,7 @@ def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool (alias for unsigned<1>).""" - return type_info.IntegerType(1, type_info.TypeKind.UINT) + return type_info.PrimitiveType(type_info.TypeKind.UINT, 1) def visitBinary_expression(self, ctx: CoreDSL2Parser.Binary_expressionContext): """Generate a binary expression.""" diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 52c0d4d1..1c3df621 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -400,7 +400,7 @@ 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_) return type_ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): @@ -425,7 +425,7 @@ def visitInteger_type(self, ctx: CoreDSL2Parser.Integer_typeContext): kind = type_info.TypeKind.INT if signed else type_info.TypeKind.UINT - return type_info.IntegerType(width, kind) + return type_info.PrimitiveType(kind, width) def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): """Generate a void type specifier.""" @@ -435,7 +435,7 @@ def visitVoid_type(self, ctx: CoreDSL2Parser.Void_typeContext): def visitBool_type(self, ctx: CoreDSL2Parser.Bool_typeContext): """Generate a bool type specifier. Aliases to unsigned<1>.""" - return type_info.IntegerType(1, type_info.TypeKind.UINT) + return type_info.PrimitiveType(type_info.TypeKind.UINT, 1) def visitInteger_signedness(self, ctx: CoreDSL2Parser.Integer_signednessContext): """Generate integer signedness.""" diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index c94afcb9..da7c7834 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -193,12 +193,12 @@ def __str__(self) -> str: class FnParam(Named): """A function parameter.""" - ty: type_info.IntegerType + ty: type_info.PrimitiveType _width: Union[int, "Constant", "BaseNode"] """The array width of this parameter.""" def __init__(self, name, size, kind: type_info.TypeKind, width=1): - self.ty = type_info.IntegerType(size, kind) + self.ty = type_info.PrimitiveType(kind, size) self._width = width super().__init__(name) @@ -237,7 +237,7 @@ def __init__( static: attribute_info.StaticAttribute = attribute_info.StaticAttribute.RW, value=None, # Compile Time information ): - assert isinstance(ty, (type_info.PrimitiveType, type_info.IntegerType)) + assert isinstance(ty, (type_info.PrimitiveType, type_info.PrimitiveType)) assert ty.kind.is_scalar # optional: only for constants/literals @@ -288,9 +288,6 @@ def is_gpr(self): return attribute_info.RegisterAttribute.IS_GPR_REG in self.attributes -# TODO: decide later if you wanna keep lhs information : -# unsigned& S0 = X[8]; vs -# Intention: alias S0 <- X[8]; #TODO IndexedReference without right re-defined??? class RegisterRef: @@ -304,6 +301,9 @@ def resolve(self, context): return bank[self.index] +# 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 a register. A register is a single element of a register bank, which is used to represent registers in the architectural part of an M2-ISA-R model.""" @@ -406,7 +406,7 @@ class BitFieldDescr(Named): 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.IntegerType(get_const_or_val(size), kind) + self.ty = type_info.PrimitiveType(kind, get_const_or_val(size)) super().__init__(name) diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index c901607e..4b3cc7cb 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -26,7 +26,7 @@ if TYPE_CHECKING: from .arch import (BitFieldDescr, Constant, FnParam, Function, Intrinsic, - Memory, Scalar) + Memory, Variable) from .code_info import LineInfo # pylint: disable=abstract-method @@ -166,7 +166,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 diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 41f9886f..9b27b2ca 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -53,15 +53,6 @@ class BitFieldType(): def __init__(self, kind: TypeKind): self.kind = kind -class IntegerType(PrimitiveType): - def __init__(self, size: int, signed_or_kind: Union[bool, TypeKind], ptr: Any=None): - self.ptr = ptr - if type(signed_or_kind) is not bool: - assert signed_or_kind.is_int, "IntegerType must be of int kind" - super().__init__(signed_or_kind, size) - else: - logger.warning("Deprecated IntegerType please use new PrimitiveType instead !!!") - super().__init__(TypeKind.INT if signed_or_kind else TypeKind.UINT, size) class FloatType: def __init__(self, exponent: int, mantissa: int, size: int): diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index 2c7620cd..b5525b31 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -64,8 +64,8 @@ def _(self, expr: behav.BinaryOperation, context): logger.warning("Slice Operation needs inferred type. Skipping...") expr.ty = None return expr - assert isinstance(expr.left.ty, type_info.IntegerType) - assert isinstance(expr.right.ty, type_info.IntegerType) + 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 @@ -120,12 +120,12 @@ def _(self, expr: behav.BinaryOperation, context): sr = s1 kind = type_info.TypeKind.INT if sr else type_info.TypeKind.UINT - expr.ty = type_info.IntegerType(wr, kind) + expr.ty = type_info.PrimitiveType(kind, wr) else: if expr.op.value in ["||", "&&"]: - expr.ty = type_info.IntegerType(1, type_info.TypeKind.UINT) # unsigned<1> / bool + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) # unsigned<1> / bool elif expr.op.value in ["<", ">", "==", "!=", ">=", "<="]: - expr.ty = type_info.IntegerType(1, type_info.TypeKind.UINT) # unsigned<1> / bool + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) # unsigned<1> / bool assert expr.ty is not None return expr @@ -141,7 +141,7 @@ def _(self, expr: behav.SliceOperation, context): if expr.expr.ty is None: logger.warning("Slice Operation needs inferred type. Skipping...") return expr - assert isinstance(expr.expr.ty, type_info.IntegerType) + 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.Literal): @@ -172,7 +172,7 @@ def _(self, expr: behav.ConcatOperation, context): return expr 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.IntegerType(size, type_info.TypeKind.UINT) + ty = type_info.PrimitiveType(type_info.TypeKind.UINT, size) expr.ty = ty return expr @@ -184,7 +184,7 @@ def _(self, expr: behav.Literal, context): # type inference assert(expr.ty.size is not None) assert(expr.ty.kind.is_int) - expr.ty = type_info.IntegerType(expr.ty.size, expr.ty.kind) + expr.ty = type_info.PrimitiveType(expr.ty.kind, expr.ty.size) return expr @@ -247,7 +247,7 @@ def _(self, expr: behav.Ternary, context): wt = arch.get_const_or_val(then_ty.size) we = arch.get_const_or_val(else_ty.size) wr = max(wt, we) - expr.ty = type_info.IntegerType(wr, type_info.TypeKind.INT) + expr.ty = type_info.PrimitiveType(type_info.TypeKind.INT, wr) return expr @@ -264,11 +264,11 @@ def _(self, expr: behav.UnaryOperation, context): if expr.right.ty: w1 = expr.right.ty.size if expr.op.value == "-": - ty = type_info.IntegerType(w1 + 1, type_info.TypeKind.INT) + ty = type_info.PrimitiveType(type_info.TypeKind.INT, w1 + 1) elif expr.op.value == "~": - ty = type_info.IntegerType(w1, type_info.TypeKind.INT) + ty = type_info.PrimitiveType(type_info.TypeKind.INT, w1) elif expr.op.value == "!": - ty = type_info.IntegerType(1, type_info.TypeKind.UINT) + ty = type_info.PrimitiveType(type_info.TypeKind.UINT, 1) else: ty = None expr.ty = ty @@ -283,7 +283,7 @@ def _(self, expr: behav.NamedReference, context): # expr.infered_type = ? if isinstance(reference, arch.BitFieldDescr): assert expr.reference.ty.kind.is_int - ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) + ty = type_info.PrimitiveType(reference.ty.kind, reference.ty.size) expr.ty = ty elif isinstance(reference, arch.Variable): @@ -291,17 +291,17 @@ def _(self, expr: behav.NamedReference, context): dt = reference.ty.kind sz = reference.ty.size assert dt.is_int - ty = type_info.IntegerType(sz, dt) + ty = type_info.PrimitiveType(dt, sz) expr.ty = ty elif isinstance(reference, arch.Memory): - expr.ty = type_info.IntegerType(reference.ty.size, type_info.TypeKind.UINT) + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, reference.ty.size) elif isinstance(reference, arch.Intrinsic): assert expr.reference.ty.kind.is_int - expr.ty = type_info.IntegerType(reference.ty.size, reference.ty.kind) + expr.ty = type_info.PrimitiveType(reference.ty.kind, reference.ty.size) elif isinstance(reference, arch.Constant): kind = type_info.TypeKind.INT if reference.signed else type_info.TypeKind.UINT - expr.ty = type_info.IntegerType(reference.size, kind) + expr.ty = type_info.PrimitiveType(kind, reference.size) else: assert False, "Unhandled reference" @@ -330,7 +330,7 @@ def _(self, expr: behav.IndexedReference, context): assert(lhs_offset >= rhs_offset) size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size - ty_ = type_info.IntegerType(size, ty) + ty_ = type_info.PrimitiveType(ty, size) expr.ty = ty_ @@ -344,7 +344,7 @@ def _(self, expr: behav.TypeConv, context): if ty is None: logger.warning("Type conv needs inferred type. Skipping...") return expr - assert isinstance(ty, type_info.IntegerType) + 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: @@ -361,7 +361,7 @@ def _(self, expr: behav.Callable, context): 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.IntegerType(arch.get_const_or_val(width), expr.ref_or_name.ty.kind) + 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 From dd0366e7fed3e7d2b1a5a75d3a184ef73375c216 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Mon, 11 May 2026 09:39:47 +0200 Subject: [PATCH 19/33] [MetaModel] WIP split arch.Memory in RegisterBanks/Alias/MEM --- .../coredsl2/architecture_model_builder.py | 128 ++++++++++--- .../coredsl2/behavior_model_builder.py | 29 ++- m2isar/frontends/coredsl2/parser.py | 46 ++++- m2isar/metamodel/arch.py | 170 +++++++++++------- m2isar/metamodel/attribute_info.py | 7 +- m2isar/metamodel/behav.py | 10 +- m2isar/metamodel/type_info.py | 7 - m2isar/metamodel/utils/scalar_staticness.py | 3 + m2isar/transforms/infer_types/visitor.py | 35 +++- 9 files changed, 302 insertions(+), 133 deletions(-) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index c9bcc085..a8705e52 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -31,13 +31,15 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): _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__() @@ -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() @@ -91,6 +95,7 @@ def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): constants = {} memories = {} + register_banks = {} functions = {} # instructions = {} instructions = [] @@ -101,6 +106,10 @@ def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): constants[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, constants, memories, register_banks, functions, instructions) if name in self._instruction_sets: raise M2DuplicateError(f"instruction set \"{name}\" already defined") @@ -143,8 +152,8 @@ def visitCore_def(self, ctx: CoreDSL2Parser.Core_defContext): 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._register_banks, self._register_aliases, self._functions, + self._instructions, self._instr_classes, intrinsics) return c @@ -312,9 +321,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): name = decl.name.text # generate a register alias - if isinstance(type_, type_info.PointerType): + 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 @@ -341,14 +350,32 @@ 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_.ty.size, attributes) - m.parent = reference - m.parent.children.append(m) + + alias = arch.Alias(name, init, reference, range_spec, type_, attributes) + + # # 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]; + # m = arch.Alias(name, ini) + + 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: @@ -370,9 +397,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): ret_decls.append(c) # register and extern declaration: "Memory" object in M2-ISA-R - elif "register" in storage or "extern" in storage: - if name in self._memories: - raise M2DuplicateError(f"memory {name} already defined") + elif "register" in storage: + if name in self._register_banks: + raise M2DuplicateError(f"register bank {name} already defined") size = [1] init = None @@ -390,22 +417,64 @@ 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_.size, attributes) - - # attach init value to memory object + # TODO: Constant might be 1 as well and makes a RegisterBank to Register ... [1] should be illegal anyways? + if isinstance(size[0], (arch.Constant, 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 Constants 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.MemoryAttribute.IS_MAIN_REG in attributes: + if attribute_info.RegisterAttribute.IS_MAIN_REG in attributes: self._main_reg_file = m - if attribute_info.MemoryAttribute.IS_FLOAT_REG in attributes: + if attribute_info.RegisterAttribute.IS_FLOAT_REG in attributes: self._float_reg_file = m - if attribute_info.MemoryAttribute.IS_VECTOR_REG in attributes: + if attribute_info.RegisterAttribute.IS_VECTOR_REG in attributes: self._vector_reg_file = m - if attribute_info.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": + if attribute_info.RegisterAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": self._csr_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") + + 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]) + + range_spec = arch.RangeSpec(size[0]) + m = arch.Memory(name, range_spec, type_.kind, type_.size, attributes) + + # attach init value to memory object + if init is not None: + m._initval[None] = exprInterpretVisitor.generate(init, None) + self._memories[name] = m ret_decls.append(m) @@ -488,7 +557,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._constants.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) @@ -520,7 +590,6 @@ def visitAssignment_expression(self, ctx: CoreDSL2Parser.Assignment_expressionCo 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) @@ -532,7 +601,8 @@ def visitAttribute(self, ctx: CoreDSL2Parser.AttributeContext): # read attribute from enums 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()) + 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 1c3df621..1c610707 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -9,7 +9,7 @@ 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, type_info, intrinsics, attribute_info @@ -31,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, constants: "dict[str, arch.Constant]", 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._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 @@ -275,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. """ @@ -284,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.""" @@ -299,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, @@ -328,6 +337,8 @@ def visitReference_expression(self, ctx: CoreDSL2Parser.Reference_expressionCont self._constants.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: diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 2176a8f9..cf3f9d7d 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -117,7 +117,7 @@ def main(): const_def._value = const_def.value for mem_def in itertools.chain(core_def.memories.values(), core_def.memory_aliases.values()): - mem_def.ty.size = arch.get_const_or_val(mem_def.ty.size) + mem_def.ty.size = arch.get_const_or_val(mem_def.ty.size) if isinstance(mem_def.ty, type_info.PrimitiveType) else arch.get_const_or_val(mem_def.ty.ty.size) mem_def._size = mem_def.ty.size mem_def.range._lower_base = mem_def.range.lower_base mem_def.range._upper_base = mem_def.range.upper_base @@ -126,8 +126,9 @@ 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) + {}, {}, {}, core_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: @@ -136,6 +137,35 @@ 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_kind.size = arch.get_const_or_val(reg_def.ty.element_kind.size) + reg_def.ty.length = arch.get_const_or_val(reg_def.ty.length) + elif isinstance(reg_def.ty, type_info.PrimitiveType): + assert(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!!!! + if isinstance(reg_def.ty.ty, type_info.ArrayType): + reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.element_kind.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.constants, {}, {},{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") @@ -157,7 +187,7 @@ def main(): 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) + 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: @@ -167,7 +197,7 @@ 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) + 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: @@ -197,7 +227,7 @@ def main(): 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) + 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: @@ -207,7 +237,7 @@ 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) + core_def.register_banks, core_def.register_aliases, {}, core_def.functions, warned_fns) try: op = behav_builder.visit(block_def.operation) @@ -232,7 +262,7 @@ def main(): 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) + 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: @@ -253,7 +283,7 @@ def main(): continue behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, - instr_def.fields, core_def.functions, warned_fns) + core_def.register_banks, core_def.register_aliases, instr_def.fields, core_def.functions, warned_fns) try: op = behav_builder.visit(instr_def.operation) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index da7c7834..e7fc51c3 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -19,7 +19,7 @@ from m2isar.metamodel import type_info, attribute_info from .. import M2TypeError -from .behav import BaseNode, Operation, Literal +from .behav import BaseNode, IndexedReference, Operation, Literal if TYPE_CHECKING: from .code_info import FunctionInfo @@ -211,14 +211,18 @@ def width(self): def __str__(self) -> str: 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.MemoryType, type_info.BitFieldType, type_info.PointerType], + ty: Union[type_info.PrimitiveType, type_info.FloatType, type_info.ArrayType, type_info.BitFieldType, type_info.PointerType], attributes : dict = {} ): self.ty = ty @@ -257,30 +261,22 @@ def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind, value: int 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.""" - range: RangeSpec children: "list[Memory]" _initval: "dict[int, Union[int, Constant, BaseNode]]" - def __init__(self, name, range_: RangeSpec, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): - self.range = range_ + def __init__(self, name, nr_ele: Union[int, Constant], kind: type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.children = [] - self.parent = None self._initval = {} - super().__init__(name, type_info.MemoryType(size), attributes) + ty = type_info.ArrayType(type_info.PrimitiveType(kind, size), nr_ele) + super().__init__(name, ty, attributes) + + # TODO: Implement this def initval(self, idx=None): """Return the initial value for the given index.""" 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.""" - - 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 is_gpr(self): @@ -288,44 +284,31 @@ def is_gpr(self): return attribute_info.RegisterAttribute.IS_GPR_REG in self.attributes +# 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, Constant, BaseNode]]" -#TODO IndexedReference without right re-defined??? -class RegisterRef: - def __init__(self, reference, index): - self.reference = reference - self.index = index - - - def resolve(self, context): - bank = context[self.reference] - return bank[self.index] - - -# 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 a register. A register is a single element of a register bank, - which is used to represent registers in the architectural part of an M2-ISA-R model.""" - target: Union[RegisterRef, Symbol] - initval = 0, + 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) - def __init__(self, name, initval: int, target: RegisterBank, attributes: dict = {}): - self.target = target - self.initval = initval - assert isinstance(target.ty, type_info.PrimitiveType) - super().__init__(name, type_info.PointerType(target.ty), attributes) + super().__init__(name, ty, attributes) - def resolve(self, context): - target = self.target + # TODO: Implement this + def initval(self): + """Return the initial value for the given index.""" - if isinstance(target, RegisterRef): - return target.resolve(context) + return get_const_or_val(self._initval) - while isinstance(target, Alias): - target = target.target - return target +#Idea extern [const volatile]<- atleast store it +#class Port -> raise ... class Memory(Symbol): @@ -339,12 +322,13 @@ class Memory(Symbol): parent: Union['Memory', None] _initval: "dict[int, Union[int, Constant, BaseNode]]" - def __init__(self, name, range_: RangeSpec, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): + def __init__(self, name, range_: RangeSpec, kind : type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.range = range_ self.children = [] self.parent = None self._initval = {} - super().__init__(name, type_info.MemoryType(size), attributes) + assert kind.is_numeric + super().__init__(name, type_info.PrimitiveType(kind, size), attributes) def initval(self, idx=None): """Return the initial value for the given index.""" @@ -370,6 +354,41 @@ 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 +# 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 a register. A register is a single element of a register bank, + which is used to represent registers in the architectural part of an M2-ISA-R model.""" + parent: Union[Memory, RegisterBank] + initval = 0 + + def __init__(self, name, init_val, parent: Union[Memory, RegisterBank], range: RangeSpec, type: type_info.PointerType, attributes: dict = {}): + self.parent = parent + self.initval = init_val + 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) + + def resolve(self, context): + parent = self.parent + + if isinstance(parent, RegisterBank): + return parent.resolve(context) + + while isinstance(parent, Alias): + parent = parent.parent + + return parent + + +# ============================================================ +# END +# ARCHITECTURE used within the functional +# behavior of instructions and functions. +# =========================================================== + class BitField(Symbol): """A class representing an operand in an instruction encoding. Can be split @@ -535,6 +554,21 @@ 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[attribute_info.FunctionAttribute, list[BaseNode]]" operation: "Operation" @@ -551,11 +585,12 @@ class InstructionSet(Named): """ 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]"): + register_banks: "dict[str, RegisterBank]", functions: "dict[str, Function]", instructions: "dict[tuple[int, int], Instruction]"): self.extension = extension self.constants = constants 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 @@ -565,14 +600,17 @@ 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]"): + 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.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 @@ -597,17 +635,7 @@ 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 attribute_info.MemoryAttribute.IS_MAIN_REG in mem.attributes: - self.main_reg_file = mem - elif attribute_info.MemoryAttribute.IS_FLOAT_REG in mem.attributes: - self.float_reg_file = mem - elif attribute_info.MemoryAttribute.IS_VECTOR_REG in mem.attributes: - self.vector_reg_file = mem - elif attribute_info.MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name.upper() == "CSR": - self.csr_reg_file = mem - elif attribute_info.MemoryAttribute.IS_PC in mem.attributes: - self.pc_memory = mem - elif attribute_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: + if attribute_info.MemoryAttribute.IS_MAIN_MEM in mem.attributes: self.main_memory = mem elif attribute_info.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN in mem.attributes: self.global_irq_en_memory = mem @@ -618,6 +646,20 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan elif attribute_info.MemoryAttribute.ETISS_IS_IRQ_PENDING in mem.attributes: self.irq_pending_memory = 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_CSR_REG in regs.attributes or regs.name.upper() == "CSR": + self.csr_reg_file = regs + elif attribute_info.RegisterAttribute.IS_PC in regs.attributes: + self.pc_memory = regs + + super().__init__(name) @property diff --git a/m2isar/metamodel/attribute_info.py b/m2isar/metamodel/attribute_info.py index 658c176f..e774cc4d 100644 --- a/m2isar/metamodel/attribute_info.py +++ b/m2isar/metamodel/attribute_info.py @@ -3,12 +3,7 @@ from dataclasses import dataclass 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() @@ -18,7 +13,7 @@ class MemoryAttribute(Enum): class RegisterAttribute(Enum): IS_PC = auto() - IS_GPR_REG = auto() + IS_MAIN_REG = auto() IS_FLOAT_REG = auto() IS_VECTOR_REG = auto() IS_CSR_REG = auto() diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index 4b3cc7cb..d750026e 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -26,7 +26,7 @@ if TYPE_CHECKING: from .arch import (BitFieldDescr, Constant, FnParam, Function, Intrinsic, - Memory, Variable) + Memory, Variable, RegisterBank) from .code_info import LineInfo # pylint: disable=abstract-method @@ -189,17 +189,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, Constant or FnParam.""" - def __init__(self, reference: Union["Memory", "BitFieldDescr", "Scalar", "Constant", "FnParam", "Intrinsic"], line_info=None): + def __init__(self, reference: Union["Memory", "BitFieldDescr", "Variable", "Constant", "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 diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 9b27b2ca..48c53d30 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -70,13 +70,6 @@ class PointerType: ty: PrimitiveType -class MemoryType: - size : int - - def __init__(self, size: int): - self.size = size - - class FunctionType(): size : int kind : TypeKind diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index bf305468..15823890 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -143,6 +143,9 @@ def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticness static_map = { 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.Constant: attribute_info.StaticAttribute.READ, arch.FnParam: attribute_info.StaticAttribute.READ diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index b5525b31..a63d501e 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -141,7 +141,7 @@ def _(self, expr: behav.SliceOperation, context): if expr.expr.ty is None: logger.warning("Slice Operation needs inferred type. Skipping...") return expr - assert isinstance(expr.expr.ty, type_info.PrimitiveType) + assert isinstance(expr.expr.ty, (type_info.PrimitiveType, type_info.ArrayType)) ty = expr.expr.ty # For non-static slices, we cann not infer the type! if not isinstance(expr.left, behav.Literal): @@ -154,9 +154,17 @@ def _(self, expr: behav.SliceOperation, context): rval = expr.right.value width = lval - rval + 1 if lval > rval else rval - lval + 1 ty_ = copy(ty) - ty_.size = width - expr.ty = ty_ + if isinstance(ty_, type_info.PrimitiveType): + ty_.size = width + elif isinstance(ty_, type_info.ArrayType): + if width == 1: # Array -> PrimitiveTpye + ty_ = ty_.element_kind + else: # Array Slice + ty_.length = width + else: + raise f"Type Slicing not supported for type {ty_}" + expr.ty = ty_ return expr @@ -295,6 +303,20 @@ def _(self, expr: behav.NamedReference, context): expr.ty = ty elif isinstance(reference, arch.Memory): expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, reference.ty.size) + elif isinstance(reference, arch.RegisterBank): + assert(isinstance(reference.ty, type_info.ArrayType)) #propagate type from element + expr.ty = reference.ty + elif isinstance(reference, arch.Register): + assert(isinstance(reference.ty, type_info.PrimitiveType)) #propagate type from element + expr.ty = reference.ty + elif isinstance(reference, arch.Alias): # propagate type from aliased mem or reg bank + if isinstance(reference.parent, arch.Memory): + expr.ty = self.generate(behav.NamedReference(reference.parent), context) + expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, reference.parent.ty.size) + elif isinstance(reference.parent, arch.RegisterBank): + expr.ty = reference.parent.ty.element_kind + else: + raise NotImplementedError(f"Alias parent type {type(reference.parent)} not supported for type inference") elif isinstance(reference, arch.Intrinsic): assert expr.reference.ty.kind.is_int expr.ty = type_info.PrimitiveType(reference.ty.kind, reference.ty.size) @@ -312,10 +334,13 @@ def _(self, expr: behav.IndexedReference, context): expr.index = self.generate(expr.index, context) # type inference - assert isinstance(expr.reference, arch.Memory) ty = type_info.TypeKind.UINT # TODO: Memory class should keep track of dtype, not only size? assert ty.is_int - single_mem_acc_size = expr.reference.ty.size + assert isinstance(expr.reference, (arch.Memory, arch.RegisterBank)) + if (isinstance(expr.reference, arch.Memory)): + single_mem_acc_size = expr.reference.ty.size + else: + single_mem_acc_size = expr.reference.ty.element_kind.size ## Simple eval check for ranged access. # Little-endian interpretation: From a8334e77e6a56c0e99ca87cee55bf2c59b51ccbc Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 14 May 2026 18:40:16 +0200 Subject: [PATCH 20/33] [MetaModel] WIP Architecture Classes bugfix for infer+simplify --- .../coredsl2/architecture_model_builder.py | 13 ++-- m2isar/frontends/coredsl2/parser.py | 25 ++++--- m2isar/metamodel/arch.py | 68 ++++++++--------- m2isar/metamodel/attribute_info.py | 2 +- m2isar/metamodel/utils/expr_simplifier.py | 12 ++- m2isar/transforms/infer_types/visitor.py | 74 ++++++++----------- 6 files changed, 95 insertions(+), 99 deletions(-) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index a8705e52..6c73f4f1 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -351,8 +351,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # instantiate M2-ISA-R object, keep track of parent - child relations - alias = arch.Alias(name, init, reference, range_spec, type_, attributes) - + alias = arch.Alias(name, reference, range_spec, init, 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]]; @@ -363,7 +363,6 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # unsigned& vreg0[16] = V[0][7:0] && V[1][7:0]; # or even more complex: # unsigned& vreg0.5[4] = V[0][3:0]; - # m = arch.Alias(name, ini) alias.parent.children.append(alias) @@ -443,8 +442,6 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): self._float_reg_file = m if attribute_info.RegisterAttribute.IS_VECTOR_REG in attributes: self._vector_reg_file = m - if attribute_info.RegisterAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": - self._csr_reg_file = m self._register_banks[name] = m ret_decls.append(m) @@ -468,13 +465,15 @@ 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_.kind, type_.size, 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 attribute_info.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": + self._csr_reg_file = m + self._memories[name] = m ret_decls.append(m) diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index cf3f9d7d..7b0f1f49 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -117,11 +117,18 @@ def main(): const_def._value = const_def.value for mem_def in itertools.chain(core_def.memories.values(), core_def.memory_aliases.values()): - mem_def.ty.size = arch.get_const_or_val(mem_def.ty.size) if isinstance(mem_def.ty, type_info.PrimitiveType) else arch.get_const_or_val(mem_def.ty.ty.size) - mem_def._size = mem_def.ty.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_kind.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_kind.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: @@ -142,14 +149,14 @@ def main(): reg_def.ty.element_kind.size = arch.get_const_or_val(reg_def.ty.element_kind.size) reg_def.ty.length = arch.get_const_or_val(reg_def.ty.length) elif isinstance(reg_def.ty, type_info.PrimitiveType): - assert(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!!!! - if isinstance(reg_def.ty.ty, type_info.ArrayType): - reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.element_kind.size) - else: - reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.size) + pass + # if isinstance(reg_def.ty.ty, type_info.ArrayType): + # reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.element_kind.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 = [] diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index e7fc51c3..4d467315 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -319,69 +319,65 @@ class Memory(Symbol): range: RangeSpec children: "list[Memory]" - parent: Union['Memory', None] + parent: "Union['Memory', None]" _initval: "dict[int, Union[int, Constant, BaseNode]]" - def __init__(self, name, range_: RangeSpec, kind : type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): - 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 = {} + self.parent = None # Just Legacy assert kind.is_numeric - super().__init__(name, type_info.PrimitiveType(kind, size), attributes) + super().__init__(name, type_info.ArrayType(type_info.PrimitiveType(kind, size), length), attributes) - def initval(self, idx=None): - """Return the initial value for the given index.""" - - 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.""" - - 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 is_pc(self): - """Return true if this memory is tagged as being the program counter.""" - return attribute_info.MemoryAttribute.IS_PC in self.attributes @property 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 + # 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 a register. A register is a single element of a register bank, - which is used to represent registers in the architectural part of an M2-ISA-R model.""" + """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 - def __init__(self, name, init_val, parent: Union[Memory, RegisterBank], range: RangeSpec, type: type_info.PointerType, attributes: dict = {}): + def __init__(self, name, parent: Union[Memory, RegisterBank], range: RangeSpec, init_val, type: type_info.PointerType, attributes: dict = {}): self.parent = parent - self.initval = init_val self.range = range + self.initval = init_val self.ty = type assert isinstance(parent.ty, (type_info.ArrayType, type_info.PrimitiveType)) super().__init__(name, type_info.PointerType(parent.ty), attributes) - def resolve(self, context): - parent = self.parent - if isinstance(parent, RegisterBank): - return parent.resolve(context) + @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 + """ - while isinstance(parent, Alias): - parent = parent.parent + if self.range.upper is None: + return None - return parent + if self.range.lower is None: + return self.range.upper + return self.range.upper - self.range.lower + 1 # ============================================================ # END @@ -645,6 +641,8 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.irq_en_memory = mem 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()): @@ -654,8 +652,6 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan 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_CSR_REG in regs.attributes or regs.name.upper() == "CSR": - self.csr_reg_file = regs elif attribute_info.RegisterAttribute.IS_PC in regs.attributes: self.pc_memory = regs diff --git a/m2isar/metamodel/attribute_info.py b/m2isar/metamodel/attribute_info.py index e774cc4d..c9d26e47 100644 --- a/m2isar/metamodel/attribute_info.py +++ b/m2isar/metamodel/attribute_info.py @@ -4,6 +4,7 @@ class MemoryAttribute(Enum): IS_MAIN_MEM = auto() + IS_CSR_REG = auto() DELETE = auto() ETISS_CAN_FAIL = auto() ETISS_IS_GLOBAL_IRQ_EN = auto() @@ -16,7 +17,6 @@ class RegisterAttribute(Enum): IS_MAIN_REG = auto() IS_FLOAT_REG = auto() IS_VECTOR_REG = auto() - IS_CSR_REG = auto() class FunctionAttribute(Enum): ETISS_STATICFN = auto() diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index fcdda8c1..220582ae 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -65,8 +65,12 @@ def _(self, expr: behav.BinaryOperation, context): expr.left.ty.size = arch.get_const_or_val(expr.right.reference.ty.size) if isinstance(expr.right, behav.Literal) and isinstance(expr.left, (behav.NamedReference, behav.IndexedReference)): - if expr.right.ty.size < arch.get_const_or_val(expr.left.reference.ty.size): - expr.right.ty.size = arch.get_const_or_val(expr.left.reference.ty.size) + if isinstance(expr.left.ty, type_info.ArrayType): + if expr.right.ty.size < arch.get_const_or_val(expr.left.reference.ty.element_kind.size): + expr.right.ty.size = arch.get_const_or_val(expr.left.reference.ty.element_kind.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.Literal) and isinstance(expr.right, behav.Literal): # pylint: disable=eval-used @@ -134,8 +138,8 @@ def _(self, expr: behav.Assignment, context): expr.expr = self.generate(expr.expr, context) 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.reference.ty.size): - expr.expr.ty.size = arch.get_const_or_val(expr.target.reference.ty.size) + 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 diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index a63d501e..2d08dcf1 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -141,7 +141,7 @@ def _(self, expr: behav.SliceOperation, context): if expr.expr.ty is None: logger.warning("Slice Operation needs inferred type. Skipping...") return expr - assert isinstance(expr.expr.ty, (type_info.PrimitiveType, type_info.ArrayType)) + 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.Literal): @@ -192,8 +192,8 @@ def _(self, expr: behav.Literal, context): # type inference assert(expr.ty.size is not None) assert(expr.ty.kind.is_int) - expr.ty = type_info.PrimitiveType(expr.ty.kind, expr.ty.size) + expr.ty = type_info.PrimitiveType(expr.ty.kind, expr.ty.size) return expr @generate.register @@ -287,43 +287,31 @@ 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.ty.kind.is_int - ty = type_info.PrimitiveType(reference.ty.kind, reference.ty.size) - expr.ty = ty - - elif isinstance(reference, arch.Variable): - assert isinstance(reference.ty, type_info.PrimitiveType) - dt = reference.ty.kind - sz = reference.ty.size - assert dt.is_int - ty = type_info.PrimitiveType(dt, sz) - expr.ty = ty - elif isinstance(reference, arch.Memory): - expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, reference.ty.size) - elif isinstance(reference, arch.RegisterBank): - assert(isinstance(reference.ty, type_info.ArrayType)) #propagate type from element + assert reference.ty.kind.is_int expr.ty = reference.ty - elif isinstance(reference, arch.Register): - assert(isinstance(reference.ty, type_info.PrimitiveType)) #propagate type from element + elif isinstance(reference, (arch.Variable, arch.Memory, arch.RegisterBank, arch.Register)): + assert isinstance(reference.ty, (type_info.PrimitiveType, type_info.ArrayType)) expr.ty = reference.ty elif isinstance(reference, arch.Alias): # propagate type from aliased mem or reg bank - if isinstance(reference.parent, arch.Memory): - expr.ty = self.generate(behav.NamedReference(reference.parent), context) - expr.ty = type_info.PrimitiveType(type_info.TypeKind.UINT, reference.parent.ty.size) - elif isinstance(reference.parent, arch.RegisterBank): + 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_kind + 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.ty.kind.is_int - expr.ty = type_info.PrimitiveType(reference.ty.kind, reference.ty.size) + assert reference.ty.kind.is_int and isinstance(reference.ty, type_info.PrimitiveType) + expr.ty = reference.ty elif isinstance(reference, arch.Constant): 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" @@ -333,31 +321,33 @@ def _(self, expr: behav.NamedReference, context): def _(self, expr: behav.IndexedReference, context): expr.index = self.generate(expr.index, context) - # type inference - ty = type_info.TypeKind.UINT # TODO: Memory class should keep track of dtype, not only size? - assert ty.is_int assert isinstance(expr.reference, (arch.Memory, arch.RegisterBank)) - if (isinstance(expr.reference, arch.Memory)): - single_mem_acc_size = expr.reference.ty.size - else: - single_mem_acc_size = expr.reference.ty.element_kind.size + # 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 expr.reference.ty.element_kind.kind.is_int + single_mem_acc_size = expr.reference.ty.element_kind.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_ = type_info.PrimitiveType(ty, size) + ty_ = type_info.PrimitiveType(expr.reference.ty.element_kind.kind, size) expr.ty = ty_ + assert expr.ty is not None return expr From 2121a50109fac0e3d13527e0b61ab024bfb2eabd Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 14 May 2026 18:40:46 +0200 Subject: [PATCH 21/33] [Transform] Type Inferene for Funcs as well --- m2isar/transforms/infer_types/transform.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/m2isar/transforms/infer_types/transform.py b/m2isar/transforms/infer_types/transform.py index 92b5034e..0d5a879b 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(): From dac6ea059b66529b50173d49343c1acad9659db5 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 14 May 2026 19:00:35 +0200 Subject: [PATCH 22/33] [Backend] MetaModel Viewer update for Alias/Memory --- m2isar/backends/viewer/treegen.py | 15 +++++++++++++++ m2isar/backends/viewer/viewer.py | 21 +++++++++++++++++---- m2isar/metamodel/type_info.py | 15 ++++++++++++--- m2isar/transforms/infer_types/visitor.py | 2 +- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/m2isar/backends/viewer/treegen.py b/m2isar/backends/viewer/treegen.py index 8afeb28c..d988f02d 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,15 +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.Literal, context: "TreeGenContext"): context.tree.insert(context.parent, tk.END, text="Number 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"): @@ -169,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 @@ -191,18 +200,21 @@ 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.tree.insert(context.parent, tk.END, text="Type ", values=(expr.reference.ty,)) # If LHS is a complex expression => RHS also an expression # TODO: Little Endian only so far supported @@ -230,6 +242,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 @@ -243,6 +257,7 @@ def type_conv(self, expr: behav.TypeConv, context: "TreeGenContext"): self.generate(expr.expr, 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 ce29d51a..47bb3deb 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) @@ -115,12 +115,25 @@ def main(): # 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.ty.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_kind.kind}), {arch.get_const_or_val(mem_def.ty.element_kind.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.ty.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 = reg_def.ty.length if isinstance(reg_def.ty, type_info.ArrayType) else 1 + kind = reg_def.ty.element_kind.kind if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.kind + size = reg_def.ty.element_kind.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,)) diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 48c53d30..7ffca295 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -48,27 +48,33 @@ 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_kind : 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_kind}, {self.length}>" @dataclass class PointerType: - ty: PrimitiveType - + ty: Union[PrimitiveType, FloatType, ArrayType] + def __str__(self): + return f"POINTER<{self.ty}>" class FunctionType(): size : int @@ -77,3 +83,6 @@ class FunctionType(): 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/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index 2d08dcf1..02a18d64 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -157,7 +157,7 @@ def _(self, expr: behav.SliceOperation, context): if isinstance(ty_, type_info.PrimitiveType): ty_.size = width elif isinstance(ty_, type_info.ArrayType): - if width == 1: # Array -> PrimitiveTpye + if width == 1: # Array -> PrimitiveType ty_ = ty_.element_kind else: # Array Slice ty_.length = width From 68a21ee16d18f03eaf2ef790952a5c2b9f2cd7fc Mon Sep 17 00:00:00 2001 From: jokap11 Date: Thu, 14 May 2026 19:08:13 +0200 Subject: [PATCH 23/33] [Metamodel] Remove addition Type info for Type Conv --- m2isar/backends/viewer/treegen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/m2isar/backends/viewer/treegen.py b/m2isar/backends/viewer/treegen.py index d988f02d..aaa417aa 100644 --- a/m2isar/backends/viewer/treegen.py +++ b/m2isar/backends/viewer/treegen.py @@ -257,7 +257,6 @@ def type_conv(self, expr: behav.TypeConv, context: "TreeGenContext"): self.generate(expr.expr, context) context.pop() - context.tree.insert(context.parent, tk.END, text="Type ", values=(expr.ty,)) context.pop() @generate.register From 8e192b0f6b2506c43644055e3cf95ae4bffebaeb Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Tue, 26 May 2026 14:40:46 +0200 Subject: [PATCH 24/33] [Frontend] CoreDSL 2 add properties for regs/mem --- m2isar/metamodel/arch.py | 82 ++++++++++++++---------- m2isar/transforms/infer_types/visitor.py | 7 +- 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 4d467315..1a880f3b 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -259,53 +259,65 @@ def __init__(self, name, size: ValOrConst, kind: type_info.TypeKind, value: int 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, Constant, BaseNode]]" + """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, Constant, BaseNode]]" - def __init__(self, name, nr_ele: Union[int, Constant], 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) + def __init__(self, name, nr_ele: Union[int, Constant], 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) - super().__init__(name, ty, attributes) + super().__init__(name, ty, attributes) - # TODO: Implement this - def initval(self, idx=None): - """Return the initial value for the given index.""" - - return get_const_or_val(self._initval[idx]) + # TODO: Implement this + def initval(self, idx=None): + """Return the initial value for the given index.""" + return get_const_or_val(self._initval[idx]) + @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 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 + @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 # 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, Constant, BaseNode]]" + """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, Constant, BaseNode]]" - 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) + 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) - super().__init__(name, ty, attributes) + super().__init__(name, ty, attributes) - # TODO: Implement this - def initval(self): - """Return the initial value for the given index.""" + # TODO: Implement this + def initval(self): + """Return the initial value for the given index.""" - return get_const_or_val(self._initval) + return get_const_or_val(self._initval) + @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 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 #Idea extern [const volatile]<- atleast store it #class Port -> raise ... @@ -343,12 +355,12 @@ 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 + _initval = 0 def __init__(self, name, parent: Union[Memory, RegisterBank], range: RangeSpec, init_val, type: type_info.PointerType, attributes: dict = {}): self.parent = parent self.range = range - self.initval = init_val + self._initval = init_val self.ty = type assert isinstance(parent.ty, (type_info.ArrayType, type_info.PrimitiveType)) super().__init__(name, type_info.PointerType(parent.ty), attributes) diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index 02a18d64..6c89769a 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -160,7 +160,7 @@ def _(self, expr: behav.SliceOperation, context): if width == 1: # Array -> PrimitiveType ty_ = ty_.element_kind else: # Array Slice - ty_.length = width + ty_.length = arch.get_const_or_val(width) else: raise f"Type Slicing not supported for type {ty_}" @@ -292,6 +292,11 @@ def _(self, expr: behav.NamedReference, context): 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) From 0cd5136aa06ee195bcab3a3f344909850f199b9a Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Tue, 26 May 2026 14:41:20 +0200 Subject: [PATCH 25/33] [FrontEnd] CoreDSL bugfix of Alias init + Dict instead of Set --- m2isar/frontends/coredsl2/architecture_model_builder.py | 2 +- m2isar/frontends/coredsl2/parser.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 6c73f4f1..716143e4 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -351,7 +351,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # instantiate M2-ISA-R object, keep track of parent - child relations - alias = arch.Alias(name, reference, range_spec, init, type_, attributes) + alias = arch.Alias(name, reference, range_spec, None, type_, attributes) self._memory_aliases[name] = alias # # TODO: Decide if alias can have a range ??? # Might make sense to do this for vreg0...vreg31 -> diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 7b0f1f49..179a0280 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -163,8 +163,8 @@ def main(): for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, {}, {},{core_def.register_banks}, - {core_def.register_aliases}, {}, core_def.functions, warned_fns) + behav_builder = BehaviorModelBuilder(core_def.constants, {}, {},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: From 5c3bd660d1a9bfbcf73fa4716184e5cf87af2c46 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Tue, 26 May 2026 14:41:57 +0200 Subject: [PATCH 26/33] [Backend] Make Viewer Ref type better readable --- m2isar/backends/viewer/treegen.py | 3 ++- m2isar/backends/viewer/viewer.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/m2isar/backends/viewer/treegen.py b/m2isar/backends/viewer/treegen.py index aaa417aa..f3d864e1 100644 --- a/m2isar/backends/viewer/treegen.py +++ b/m2isar/backends/viewer/treegen.py @@ -213,8 +213,9 @@ def named_reference(self, expr: behav.NamedReference, context: "TreeGenContext") 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 diff --git a/m2isar/backends/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index 47bb3deb..271df262 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -125,7 +125,7 @@ def main(): # 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 = reg_def.ty.length if isinstance(reg_def.ty, type_info.ArrayType) else 1 + 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_kind.kind if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.kind size = reg_def.ty.element_kind.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}",)) From 56de585b6c1bb0419d3a4f9cc8755cd0fc3f0170 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Tue, 26 May 2026 14:44:41 +0200 Subject: [PATCH 27/33] [Backend] ETISS refactor with new Register/Memory/Alias classes --- m2isar/backends/etiss/architecture_writer.py | 103 +++++++++++++----- .../backends/etiss/instruction_generator.py | 9 +- .../backends/etiss/instruction_transform.py | 38 ++++++- m2isar/backends/etiss/instruction_utils.py | 15 ++- .../etiss/templates/etiss_arch_cpp.mako | 32 +++--- .../etiss/templates/etiss_arch_gdbcore.mako | 14 +-- .../templates/etiss_arch_specific_cpp.mako | 14 +-- .../templates/etiss_arch_specific_h.mako | 32 +++--- .../etiss/templates/etiss_arch_struct.mako | 3 + m2isar/backends/etiss/virtualstruct_utils.py | 34 +++--- m2isar/backends/etiss/writer.py | 2 +- 11 files changed, 197 insertions(+), 99 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index ff2fae82..2ff90698 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -10,6 +10,8 @@ import logging import pathlib +from itertools import chain +from typing import Union from mako.template import Template @@ -24,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 attribute_info.MemoryAttribute.IS_PC in reg.attributes or attribute_info.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) @@ -42,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{actual_size(reg.ty.size)} *{reg.name}{array_txt}") - regs.append(f"etiss_uint{actual_size(reg.ty.size)} ins_{reg.name}{array_txt}") + if isinstance(reg.ty, type_info.ArrayType): + size = actual_size(reg.ty.element_kind.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{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: @@ -79,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 @@ -93,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.""" @@ -117,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[]' @@ -144,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: @@ -168,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_kind.size = arch.get_const_or_val(core.main_reg_file.ty.element_kind.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_kind.size = arch.get_const_or_val(core.float_reg_file.ty.element_kind.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_kind.size = arch.get_const_or_val(core.vector_reg_file.ty.element_kind.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_kind.size = arch.get_const_or_val(core.csr_reg_file.ty.element_kind.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: diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index 67eaa57c..ec91e27e 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -48,8 +48,9 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo 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.constants, 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) @@ -152,8 +153,8 @@ def generate_instruction_callback(core: arch.CoreDef, instr_def: arch.Instructio 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.constants, 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 ((attribute_info.InstrAttribute.NO_CONT in instr_def.attributes diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 32b066c4..141d348a 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -353,7 +353,7 @@ 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 actual_size(target.size) > target.size: + if actual_size(arch.get_const_or_val(target.size)) > arch.get_const_or_val(target.size): if target.signed: shift = actual_size(target.size) - target.size expr_str.code = f'(((etiss_int{actual_size(target.size)})({expr_str.code})) << {shift}) >> {shift}' @@ -622,6 +622,35 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): size = referred_var.ty.size context.used_arch_data = True + elif isinstance(referred_var, arch.RegisterBank): + # architecture constant + 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_kind.size) + context.used_arch_data = True + + elif isinstance(referred_var, arch.Register): + # architecture constant + 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 + # 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.ty.kind == type_info.TypeKind.INT @@ -680,7 +709,10 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): if isinstance(referred_mem, arch.Memory): context.used_arch_data = True - size = referred_mem.ty.size + if isinstance(referred_mem, type_info.PrimitiveType): + size = referred_mem.ty.size + else: + size = referred_mem.ty.element_kind.size # convert static index expression index_code = index.code @@ -722,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 attribute_info.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 diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index 252dae79..a1899ecf 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -134,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[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): + def __init__(self, constants: "dict[str, arch.Constant]", 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.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 @@ -160,9 +163,9 @@ 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 attribute_info.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 diff --git a/m2isar/backends/etiss/templates/etiss_arch_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_cpp.mako index 0d047ec6..dcfa5d75 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 < ${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 < ${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 < ${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 69582d47..c2c70b89 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 1c15d1bb..2ad2e2cb 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -191,8 +191,8 @@ 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 "&" %>\ +<% en_ref = "&" %>\ +<% pending_ref = "&" %>\ std::vector vec; std::vector mask; @@ -201,10 +201,10 @@ etiss::InterruptVector *${core_name}Arch::createInterruptVector(ETISS_CPU *cpu) 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 189c164f..efd2380c 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.ty.size / 8)} + ${(int(main_reg.ty.length)* int(main_reg.ty.element_kind.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.ty.size / 8)} + ${(int(main_reg.ty.length)* int(main_reg.ty.element_kind.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.ty.size}) val; + *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.ty.element_kind.size}) val; % else: - ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.ty.size}) val; + ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.ty.element_kind.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.ty.size / 8)} + ${(int(float_reg.ty.length)* int(float_reg.ty.element_kind.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.ty.size / 8)} + ${(int(float_reg.ty.length)* int(float_reg.ty.element_kind.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.ty.size}) val; + *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.ty.element_kind.size}) val; % else: - ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.ty.size}) val; + ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.ty.element_kind.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.ty.size // 8))} + ${int((vector_reg.ty.length // 32) // (vector_reg.ty.element_kind.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.ty.size // 8))} + ${int((vector_reg.ty.length // 32) // (vector_reg.ty.element_kind.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.ty.size / 8)} + ${int(csr_reg.ty.element_kind.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.ty.size / 8)} + ${int(csr_reg.ty.element_kind.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.ty.size}) gprid_); + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.ty.element_kind.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.ty.size}) val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.ty.element_kind.size}) val); // clang-format on } }; @@ -280,7 +280,7 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field "instructionPointer", "instructionPointer", R|W, - ${int(main_reg.ty.size / 8)} + ${int(main_reg.ty.element_kind.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.ty.size}) val; + ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.ty.element_kind.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 94ce4a02..f2c10f04 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 6f0a8884..e321dc54 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.attribute_info 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 8624e0a6..b742d732 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 From f7deed338f4dcc61f170f70551c163fbad4fdc4c Mon Sep 17 00:00:00 2001 From: jokap11 Date: Tue, 26 May 2026 15:54:34 +0200 Subject: [PATCH 28/33] [MetaModel] Remove init from Alias declaration --- m2isar/frontends/coredsl2/architecture_model_builder.py | 2 +- m2isar/metamodel/arch.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 716143e4..c3322169 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -351,7 +351,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # instantiate M2-ISA-R object, keep track of parent - child relations - alias = arch.Alias(name, reference, range_spec, None, type_, attributes) + 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 -> diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 1a880f3b..d4fd0845 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -357,10 +357,9 @@ class Alias(Symbol): parent: Union[Memory, RegisterBank] _initval = 0 - def __init__(self, name, parent: Union[Memory, RegisterBank], range: RangeSpec, init_val, type: type_info.PointerType, attributes: dict = {}): + def __init__(self, name, parent: Union[Memory, RegisterBank], range: RangeSpec, type: type_info.PointerType, attributes: dict = {}): self.parent = parent self.range = range - self._initval = init_val self.ty = type assert isinstance(parent.ty, (type_info.ArrayType, type_info.PrimitiveType)) super().__init__(name, type_info.PointerType(parent.ty), attributes) From 56bc5b45d21e68cfc972f9a54e926d6f933d628d Mon Sep 17 00:00:00 2001 From: jokap11 Date: Tue, 26 May 2026 15:56:42 +0200 Subject: [PATCH 29/33] [MetaModel] Constrain Types of Literals --- m2isar/metamodel/behav.py | 9 +++++---- m2isar/metamodel/type_info.py | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index d750026e..6767eb9f 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -20,7 +20,7 @@ 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 @@ -95,14 +95,15 @@ def __init__(self, left: BaseNode, right: BaseNode, line_info=None) -> None: class Literal(BaseNode): - def __init__(self, value:int, kind : TypeKind = TypeKind.INT, size=None, base=10, line_info=None): + def __init__(self, value:int, kind: TypeKind = TypeKind.INT, size=None, base: Optional[int]=10, line_info=None): super().__init__(line_info) - self._value : Union[int, str] = value + assert kind.is_literal + self._value: Union[int, str] = value self.ty = PrimitiveType(kind, size) # assigned during type checking #Optional type information (not always given) - self.base: int = base # 2, 10, 16 + self.base: Optional[int] = base # 2, 10, 16 def __repr__(self): return f"Literal(value={self.value}, type={self.ty}, base={self.base})" diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index 7ffca295..a7183dc8 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -43,6 +43,10 @@ def is_void(self): 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): From 27ba05a557e77ed0294de47780199d57467945d6 Mon Sep 17 00:00:00 2001 From: jokap11 Date: Tue, 26 May 2026 15:57:06 +0200 Subject: [PATCH 30/33] [Backend] Etiss eval constants at a certain time --- m2isar/backends/etiss/templates/etiss_arch_cpp.mako | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_cpp.mako index dcfa5d75..8cab0bb2 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_cpp.mako @@ -82,7 +82,7 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) % for reg in ptr_regs: % if isinstance(reg.ty, type_info.ArrayType): - for (int i = 0; i < ${reg.ty.length}; ++i) + 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]; @@ -97,7 +97,7 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) % if not isinstance(reg, (arch.Memory, arch.Alias)): % if not reg.is_pc: % if isinstance(reg.ty, type_info.ArrayType): - for (int i = 0; i < ${reg.ty.length}; ++i) + for (int i = 0; i < ${arch.get_const_or_val(reg.ty.length)}; ++i) { ${core_name.lower()}cpu->${reg.name}[i] = 0; } @@ -111,7 +111,7 @@ void ${core_name}Arch::resetCPU(ETISS_CPU *cpu, etiss::uint64 *startpointer) % for reg, parent in alias_regs.items(): <% ref = "&" %>\ % if isinstance(reg.ty, type_info.ArrayType): - for (int i = 0; i < ${reg.ty.length}; ++i) + for (int i = 0; i < ${arch.get_const_or_val(reg.ty.length)}; ++i) { ${core_name.lower()}cpu->${parent.name}[i] = ${ref}${core_name.lower()}cpu->${reg.name}[i]; } From 9cfa338e1c23afc40826688110c7d063c1e79404 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Fri, 29 May 2026 15:10:22 +0200 Subject: [PATCH 31/33] [MetaModel] Switch Constant with more precise Parameter term --- .../backends/etiss/instruction_generator.py | 10 ++-- .../backends/etiss/instruction_transform.py | 10 ++-- m2isar/backends/etiss/instruction_utils.py | 6 +-- m2isar/backends/isa_manual/visitor.py | 2 +- m2isar/backends/viewer/viewer.py | 6 +-- .../coredsl2/architecture_model_builder.py | 30 ++++++------ .../coredsl2/behavior_model_builder.py | 6 +-- m2isar/frontends/coredsl2/expr_interpreter.py | 2 +- m2isar/frontends/coredsl2/parser.py | 26 +++++----- m2isar/metamodel/arch.py | 48 +++++++++---------- m2isar/metamodel/behav.py | 6 +-- m2isar/metamodel/utils/expr_simplifier.py | 4 +- m2isar/metamodel/utils/function_staticness.py | 2 +- m2isar/metamodel/utils/scalar_staticness.py | 2 +- m2isar/transforms/infer_types/visitor.py | 4 +- 15 files changed, 82 insertions(+), 82 deletions(-) diff --git a/m2isar/backends/etiss/instruction_generator.py b/m2isar/backends/etiss/instruction_generator.py index ec91e27e..c170d120 100644 --- a/m2isar/backends/etiss/instruction_generator.py +++ b/m2isar/backends/etiss/instruction_generator.py @@ -34,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(): @@ -48,7 +48,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo 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, core.register_banks, + 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) @@ -148,12 +148,12 @@ 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, core.register_banks, core.register_aliases, instr_def.fields, instr_def.attributes, + 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 @@ -226,7 +226,7 @@ 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}' diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 141d348a..17ca5a75 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -614,7 +614,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): # 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}" @@ -623,7 +623,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): context.used_arch_data = True elif isinstance(referred_var, arch.RegisterBank): - # architecture constant + # architecture constant parameter if not static: ref = "*" if len(referred_var.children) > 0 else "" name = f"{ref}{replacements.default_prefix}{name}" @@ -632,7 +632,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): context.used_arch_data = True elif isinstance(referred_var, arch.Register): - # architecture constant + # architecture constant parameter if not static: ref = "*" if len(referred_var.children) > 0 else "" name = f"{ref}{replacements.default_prefix}{name}" @@ -641,7 +641,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): context.used_arch_data = True elif isinstance(referred_var, arch.Alias): - # architecture constant + # architecture constant parameter # Limitation: Alias does not haven children if not static: ref = "" @@ -664,7 +664,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): if context.static_scalars: 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 = attribute_info.StaticAttribute.READ diff --git a/m2isar/backends/etiss/instruction_utils.py b/m2isar/backends/etiss/instruction_utils.py index a1899ecf..e6dd7969 100644 --- a/m2isar/backends/etiss/instruction_utils.py +++ b/m2isar/backends/etiss/instruction_utils.py @@ -134,12 +134,12 @@ 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.Alias]", + 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 @@ -209,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/isa_manual/visitor.py b/m2isar/backends/isa_manual/visitor.py index 7b6a1412..0e4dbab3 100644 --- a/m2isar/backends/isa_manual/visitor.py +++ b/m2isar/backends/isa_manual/visitor.py @@ -130,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/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index 271df262..19aef75a 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -107,9 +107,9 @@ 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 diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index c3322169..a3db027e 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -24,7 +24,7 @@ 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]" @@ -43,7 +43,7 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): def __init__(self): super().__init__() - self._constants = {} + self._parameters = {} # self._instructions = {} self._instructions = [] self._functions = {} @@ -93,7 +93,7 @@ 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 = {} @@ -102,8 +102,8 @@ def visitInstruction_set(self, ctx: CoreDSL2Parser.Instruction_setContext): # 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)): @@ -123,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, register_banks, 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") @@ -151,7 +151,7 @@ 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._parameters, self._memories, self._memory_aliases, self._register_banks, self._register_aliases, self._functions, self._instructions, self._instr_classes, intrinsics) @@ -380,8 +380,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): 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 @@ -390,9 +390,9 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): signed = True if type_.kind == type_info.TypeKind.INT else False - c = arch.Constant(name, init, [], type_.size, signed) + 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 @@ -417,7 +417,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): 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.Constant, behav.NamedReference)): + 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: @@ -430,7 +430,7 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): # Unset Case: =1 m = arch.Register(name, type_.kind, type_.size, attributes) else: - raise NotImplementedError("Only Constants and Int allowed as dimension for register size") + 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: @@ -556,7 +556,7 @@ 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") @@ -584,7 +584,7 @@ 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): diff --git a/m2isar/frontends/coredsl2/behavior_model_builder.py b/m2isar/frontends/coredsl2/behavior_model_builder.py index 1c610707..e6d40430 100644 --- a/m2isar/frontends/coredsl2/behavior_model_builder.py +++ b/m2isar/frontends/coredsl2/behavior_model_builder.py @@ -31,13 +31,13 @@ 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.Alias]", + 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 @@ -334,7 +334,7 @@ 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 \ diff --git a/m2isar/frontends/coredsl2/expr_interpreter.py b/m2isar/frontends/coredsl2/expr_interpreter.py index ffda8cd0..be869785 100644 --- a/m2isar/frontends/coredsl2/expr_interpreter.py +++ b/m2isar/frontends/coredsl2/expr_interpreter.py @@ -31,7 +31,7 @@ def _(self, expr: behav.Literal, context): @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 179a0280..30c56461 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -27,7 +27,7 @@ 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? @@ -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,7 +113,7 @@ 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()): @@ -134,7 +134,7 @@ def main(): for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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) @@ -163,7 +163,7 @@ def main(): for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, {}, {},core_def.register_banks, + 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) @@ -193,7 +193,7 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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) @@ -203,7 +203,7 @@ def main(): fn_def.attributes[attr_name] = ops - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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): @@ -233,7 +233,7 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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) @@ -243,7 +243,7 @@ def main(): block_def.attributes[attr_name] = ops - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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: @@ -268,7 +268,7 @@ def main(): ops = [] for attr_op in attr_ops: try: - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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) @@ -282,14 +282,14 @@ def main(): 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(attribute_info.InstrAttribute.ENABLE) if not enable: continue - behav_builder = BehaviorModelBuilder(core_def.constants, core_def.memories, core_def.memory_aliases, + 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: diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index d4fd0845..ac80198c 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -26,7 +26,7 @@ exprInterpretVisitor = ExprInterpreterVisitor() def get_const_or_val(arg) -> int: - if isinstance(arg, Constant): + if isinstance(arg, Parameter): return arg.value if isinstance(arg, Literal): @@ -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): @@ -79,12 +79,12 @@ def size(self) -> int: def __str__(self) -> str: 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[attribute_info.ConstAttribute, list[BaseNode]]" @@ -93,7 +93,7 @@ class Constant(SizedRefOrConst): signed: bool """The signedness of this constant.""" - def __init__(self, name, value: Union[int, "Constant", "BaseNode"], attributes: "dict[attribute_info.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 @@ -117,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): @@ -194,7 +194,7 @@ class FnParam(Named): """A function parameter.""" ty: type_info.PrimitiveType - _width: Union[int, "Constant", "BaseNode"] + _width: Union[int, "Parameter", "BaseNode"] """The array width of this parameter.""" def __init__(self, name, size, kind: type_info.TypeKind, width=1): @@ -244,7 +244,7 @@ def __init__( assert isinstance(ty, (type_info.PrimitiveType, type_info.PrimitiveType)) assert ty.kind.is_scalar - # optional: only for constants/literals + # optional: only for parameters/literals self.value = value super().__init__(name, ty, attributes={"static": static}) @@ -262,9 +262,9 @@ 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, Constant, BaseNode]]" + _initval: "dict[int, Union[int, Parameter, BaseNode]]" - def __init__(self, name, nr_ele: Union[int, Constant], kind: type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): + 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) @@ -294,7 +294,7 @@ class Register(Symbol): 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, Constant, BaseNode]]" + _initval: "dict[Union[int, Parameter, BaseNode]]" def __init__(self, name, kind: type_info.TypeKind, size, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.children = [] @@ -332,7 +332,7 @@ class Memory(Symbol): range: RangeSpec children: "list[Memory]" parent: "Union['Memory', None]" - _initval: "dict[int, Union[int, Constant, BaseNode]]" + _initval: "dict[int, Union[int, Parameter, BaseNode]]" def __init__(self, name, kind : type_info.TypeKind, size, length, attributes: "dict[attribute_info.MemoryAttribute, list[BaseNode]]"): self.children = [] @@ -587,15 +587,15 @@ 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]", + 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 @@ -606,14 +606,14 @@ 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]", + 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 diff --git a/m2isar/metamodel/behav.py b/m2isar/metamodel/behav.py index 6767eb9f..faf7e7ab 100644 --- a/m2isar/metamodel/behav.py +++ b/m2isar/metamodel/behav.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: - from .arch import (BitFieldDescr, Constant, FnParam, Function, Intrinsic, + from .arch import (BitFieldDescr, Parameter, FnParam, Function, Intrinsic, Memory, Variable, RegisterBank) from .code_info import LineInfo @@ -190,9 +190,9 @@ 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, Variable, Constant or FnParam.""" + """A named reference to a :class:`arch.Memory`, BitFieldDescr, Variable, Parameter or FnParam.""" - def __init__(self, reference: Union["Memory", "BitFieldDescr", "Variable", "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 diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index 220582ae..d4355389 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -9,7 +9,7 @@ """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 +* 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.Literal` @@ -217,7 +217,7 @@ def _(self, expr: behav.UnaryOperation, context): @generate.register def _(self, expr: behav.NamedReference, context): - if isinstance(expr.reference, arch.Constant): + if isinstance(expr.reference, arch.Parameter): if expr.reference.signed: kind = type_info.TypeKind.INT else: diff --git a/m2isar/metamodel/utils/function_staticness.py b/m2isar/metamodel/utils/function_staticness.py index 557e21f9..6744946b 100644 --- a/m2isar/metamodel/utils/function_staticness.py +++ b/m2isar/metamodel/utils/function_staticness.py @@ -126,7 +126,7 @@ def _(self, expr: behav.NamedReference, context): static_map = { arch.Memory: False, arch.BitFieldDescr: True, - arch.Constant: True, + arch.Parameter: True, arch.FnParam: True, arch.Variable: True, arch.Intrinsic: False diff --git a/m2isar/metamodel/utils/scalar_staticness.py b/m2isar/metamodel/utils/scalar_staticness.py index 15823890..f1959793 100644 --- a/m2isar/metamodel/utils/scalar_staticness.py +++ b/m2isar/metamodel/utils/scalar_staticness.py @@ -147,7 +147,7 @@ def _(self, expr: behav.NamedReference, context: attribute_info.ScalarStaticness arch.Register: attribute_info.StaticAttribute.NONE, arch.Alias: attribute_info.StaticAttribute.NONE, arch.BitFieldDescr: attribute_info.StaticAttribute.READ, - arch.Constant: attribute_info.StaticAttribute.READ, + arch.Parameter: attribute_info.StaticAttribute.READ, arch.FnParam: attribute_info.StaticAttribute.READ } diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index 6c89769a..ac1450df 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -9,7 +9,7 @@ """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 +* 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.Literal` @@ -312,7 +312,7 @@ def _(self, expr: behav.NamedReference, context): elif isinstance(reference, arch.Intrinsic): assert reference.ty.kind.is_int and isinstance(reference.ty, type_info.PrimitiveType) expr.ty = reference.ty - elif isinstance(reference, arch.Constant): + 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): From ca607fdf27e1b0b870af9c71d003dc15a35ff5b2 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Fri, 29 May 2026 15:47:33 +0200 Subject: [PATCH 32/33] [MetaModel] Rename ArrayType Subtype from kind to type --- m2isar/backends/etiss/architecture_writer.py | 10 +++++----- m2isar/backends/etiss/instruction_transform.py | 4 ++-- m2isar/backends/viewer/viewer.py | 6 +++--- m2isar/frontends/coredsl2/parser.py | 8 ++++---- m2isar/metamodel/arch.py | 9 +++++++-- m2isar/metamodel/type_info.py | 4 ++-- m2isar/metamodel/utils/expr_simplifier.py | 4 ++-- m2isar/transforms/infer_types/visitor.py | 10 +++++----- 8 files changed, 30 insertions(+), 25 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 2ff90698..73893637 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -52,7 +52,7 @@ def write_child_reg_def(reg: Union[arch.Memory, arch.RegisterBank, arch.Register # 1) array of pointers, used for actual access # 2) array of actual data type, for every index which is not aliased if isinstance(reg.ty, type_info.ArrayType): - size = actual_size(reg.ty.element_kind.size) + size = actual_size(reg.ty.element_type.size) elif isinstance(reg.ty, type_info.PrimitiveType): size = actual_size(reg.ty.size) else: @@ -202,16 +202,16 @@ 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_kind.size = arch.get_const_or_val(core.main_reg_file.ty.element_kind.size) + 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_kind.size = arch.get_const_or_val(core.float_reg_file.ty.element_kind.size) + 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_kind.size = arch.get_const_or_val(core.vector_reg_file.ty.element_kind.size) + 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_kind.size = arch.get_const_or_val(core.csr_reg_file.ty.element_kind.size) + 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( diff --git a/m2isar/backends/etiss/instruction_transform.py b/m2isar/backends/etiss/instruction_transform.py index 17ca5a75..0fc3e515 100644 --- a/m2isar/backends/etiss/instruction_transform.py +++ b/m2isar/backends/etiss/instruction_transform.py @@ -628,7 +628,7 @@ def _(self, expr: behav.NamedReference, context: TransformerContext): 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_kind.size) + size = (referred_var.ty.length)*(referred_var.ty.element_type.size) context.used_arch_data = True elif isinstance(referred_var, arch.Register): @@ -712,7 +712,7 @@ def _(self, expr: behav.IndexedReference, context: TransformerContext): if isinstance(referred_mem, type_info.PrimitiveType): size = referred_mem.ty.size else: - size = referred_mem.ty.element_kind.size + size = referred_mem.ty.element_type.size # convert static index expression index_code = index.code diff --git a/m2isar/backends/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index 19aef75a..3aea4294 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -115,7 +115,7 @@ def main(): # 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"{arch.get_const_or_val(mem_def.ty.length)}:0 ({mem_def.ty.element_kind.kind}), {arch.get_const_or_val(mem_def.ty.element_kind.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 mem_alias_id = tree.insert(core_id, tk.END, text="Memory Aliases") @@ -126,8 +126,8 @@ def main(): 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_kind.kind if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.kind - size = reg_def.ty.element_kind.size if isinstance(reg_def.ty, type_info.ArrayType) else reg_def.ty.size + 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 diff --git a/m2isar/frontends/coredsl2/parser.py b/m2isar/frontends/coredsl2/parser.py index 30c56461..2282dbb6 100644 --- a/m2isar/frontends/coredsl2/parser.py +++ b/m2isar/frontends/coredsl2/parser.py @@ -118,13 +118,13 @@ def main(): for mem_def in itertools.chain(core_def.memories.values(), core_def.memory_aliases.values()): if isinstance(mem_def.ty, type_info.ArrayType): - size = arch.get_const_or_val(mem_def.ty.element_kind.size) + 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_kind.size) + 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) @@ -146,7 +146,7 @@ def main(): 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_kind.size = arch.get_const_or_val(reg_def.ty.element_kind.size) + 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) @@ -154,7 +154,7 @@ def main(): 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_kind.size) + # 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) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index ac80198c..c723a969 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -241,8 +241,13 @@ def __init__( static: attribute_info.StaticAttribute = attribute_info.StaticAttribute.RW, value=None, # Compile Time information ): - assert isinstance(ty, (type_info.PrimitiveType, type_info.PrimitiveType)) - assert ty.kind.is_scalar + 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 diff --git a/m2isar/metamodel/type_info.py b/m2isar/metamodel/type_info.py index a7183dc8..94342f4c 100644 --- a/m2isar/metamodel/type_info.py +++ b/m2isar/metamodel/type_info.py @@ -69,10 +69,10 @@ def __str__(self): return f"FLOAT" + return f"ARRAY<{self.element_type}, {self.length}>" @dataclass class PointerType: diff --git a/m2isar/metamodel/utils/expr_simplifier.py b/m2isar/metamodel/utils/expr_simplifier.py index d4355389..aa18252b 100644 --- a/m2isar/metamodel/utils/expr_simplifier.py +++ b/m2isar/metamodel/utils/expr_simplifier.py @@ -66,8 +66,8 @@ def _(self, expr: behav.BinaryOperation, context): 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_kind.size): - expr.right.ty.size = arch.get_const_or_val(expr.left.reference.ty.element_kind.size) + 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) diff --git a/m2isar/transforms/infer_types/visitor.py b/m2isar/transforms/infer_types/visitor.py index ac1450df..3bb5e18d 100644 --- a/m2isar/transforms/infer_types/visitor.py +++ b/m2isar/transforms/infer_types/visitor.py @@ -158,7 +158,7 @@ def _(self, expr: behav.SliceOperation, context): ty_.size = width elif isinstance(ty_, type_info.ArrayType): if width == 1: # Array -> PrimitiveType - ty_ = ty_.element_kind + ty_ = ty_.element_type else: # Array Slice ty_.length = arch.get_const_or_val(width) else: @@ -302,7 +302,7 @@ def _(self, expr: behav.NamedReference, context): 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_kind + expr.ty = reference.parent.ty.element_type assert (expr.ty is not None) elif isinstance(reference.parent, arch.Register): expr.ty = reference.parent.ty @@ -333,8 +333,8 @@ def _(self, expr: behav.IndexedReference, context): # expr.right = self.generate(expr.right, context) # type inference - assert expr.reference.ty.element_kind.kind.is_int - single_mem_acc_size = expr.reference.ty.element_kind.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: @@ -349,7 +349,7 @@ def _(self, expr: behav.IndexedReference, context): assert(lhs_offset >= rhs_offset) size = (lhs_offset - rhs_offset + 1)*single_mem_acc_size - ty_ = type_info.PrimitiveType(expr.reference.ty.element_kind.kind, size) + ty_ = type_info.PrimitiveType(expr.reference.ty.element_type.kind, size) expr.ty = ty_ assert expr.ty is not None From 7acca3213bf8a7efae5f80eb2b1ebe75a50314f1 Mon Sep 17 00:00:00 2001 From: Johannes Kappes Date: Sun, 31 May 2026 17:20:13 +0200 Subject: [PATCH 33/33] [Backend] ETISS change element_kind with type --- .../templates/etiss_arch_specific_cpp.mako | 8 ++--- .../templates/etiss_arch_specific_h.mako | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 2ad2e2cb..70d0217d 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -201,10 +201,10 @@ etiss::InterruptVector *${core_name}Arch::createInterruptVector(ETISS_CPU *cpu) 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 } @@ -217,7 +217,7 @@ void ${core_name}Arch::deleteInterruptVector(etiss::InterruptVector *vec, ETISS_ etiss::InterruptEnable *${core_name}Arch::createInterruptEnable(ETISS_CPU *cpu) { <% ref = "&" %>\ - return new etiss::MappedInterruptEnable(${ref}((${core_name} *)cpu)->${global_irq_en_reg.name}, ${global_irq_en_mask}); + 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 efd2380c..0e99ca02 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.ty.length)* int(main_reg.ty.element_kind.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.ty.length)* int(main_reg.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.length)* int(float_reg.ty.element_kind.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.ty.length)* int(float_reg.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.length // 32) // (vector_reg.ty.element_kind.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.ty.length // 32) // (vector_reg.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.element_kind.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.ty.element_kind.size}) val; + ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.ty.element_type.size}) val; // clang-format on } };