# SPDX-License-Identifier: Apache-2.0
#
# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R
#
# Copyright (C) 2022
# Chair of Electrical Design Automation
# Technical University of Munich
import logging
from copy import copy
from functools import singledispatchmethod
from m2isar.metamodel import arch, behav
from ...metamodel.utils.ExprVisitor import ExprVisitor
[docs]
logger = logging.getLogger("validate_behav")
# pylint: disable=unused-argument
[docs]
class ValidateBehavVisitor(ExprVisitor):
"""Visitor to validate a metamodel."""
@singledispatchmethod
[docs]
def generate(self, expr: behav.BaseNode, context):
raise NotImplementedError(f"No visit method implemented for type {type(expr).__name__} in {type(self).__name__}")
@generate.register
[docs]
def _(self, expr: behav.Operation, context):
for stmt in expr.statements:
self.generate(stmt, context)
@generate.register
def _(self, expr: behav.Block, context):
for stmt in expr.statements:
self.generate(stmt, context)
@generate.register
def _(self, expr: behav.BinaryOperation, context):
self.generate(expr.left, 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:
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:
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:
pass
if isinstance(expr.right, behav.IntLiteral) and expr.right.value == 0:
pass
else:
context.emit_warning(f"Signed vs. unsigned comparison", "sign-compare", logger=logger, line_info=expr.line_info)
# TODO: also check possible range of non-literal rhs?
if op.value == "<<" and isinstance(expr.right, behav.IntLiteral) and expr.left.inferred_type.width <= expr.right.value:
context.emit_warning(f"Shift count overflow for << operation ({expr.left.inferred_type.width} vs. {expr.right.value})", "shift-overflow", logger=logger, line_info=expr.line_info)
@generate.register
def _(self, expr: behav.SliceOperation, context):
self.generate(expr.expr, context)
self.generate(expr.left, context)
self.generate(expr.right, context)
@generate.register
def _(self, expr: behav.ConcatOperation, context):
self.generate(expr.left, 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):
pass
@generate.register
def _(self, expr: behav.Assignment, context):
self.generate(expr.target, context)
self.generate(expr.expr, context)
assert expr.target.inferred_type is not None
assert expr.expr.inferred_type is not None
if expr.target.inferred_type.width < expr.expr.inferred_type.width:
context.emit_warning(f"Implicit truncation {expr.expr.inferred_type.width} -> {expr.target.inferred_type.width} found", "implicit-trunc", logger=logger, line_info=expr.line_info)
if expr.target.inferred_type.width > expr.expr.inferred_type.width:
context.emit_warning(f"Implicit extend {expr.expr.inferred_type.width} -> {expr.target.inferred_type.width} found", "implicit-extend", logger=logger, line_info=expr.line_info)
@generate.register
def _(self, expr: behav.Conditional, context):
for cond in expr.conds:
self.generate(cond, context)
for stmt in expr.stmts:
self.generate(stmt, context)
@generate.register
def _(self, expr: behav.Loop, context):
self.generate(expr.cond, context)
for stmt in expr.stmts:
self.generate(stmt, context)
@generate.register
def _(self, expr: behav.Ternary, context):
self.generate(expr.cond, context)
self.generate(expr.then_expr, context)
self.generate(expr.else_expr, context)
@generate.register
def _(self, expr: behav.Return, context):
if expr.expr is not None:
self.generate(expr.expr, context)
return expr
@generate.register
def _(self, expr: behav.UnaryOperation, context):
self.generate(expr.right, context)
return expr
@generate.register
def _(self, expr: behav.ScalarDefinition, context):
pass
@generate.register
def _(self, expr: behav.Break, context):
pass
@generate.register
def _(self, expr: behav.NamedReference, context):
pass
@generate.register
def _(self, expr: behav.IndexedReference, context):
self.generate(expr.index, context)
@generate.register
def _(self, expr: behav.TypeConv, context):
self.generate(expr.expr, context)
@generate.register
def _(self, expr: behav.Callable, context):
for arg in expr.args:
self.generate(arg, context)
@generate.register
def _(self, expr: behav.Callable, context):
for arg in expr.args:
self.generate(arg, context)
@generate.register
def _(self, expr: behav.Group, context):
self.generate(expr.expr, context)