diff --git a/MODULEINFO b/MODULEINFO
index 90295af9c..ef8c00bab 100644
--- a/MODULEINFO
+++ b/MODULEINFO
@@ -43,6 +43,7 @@ Make: dmlc
$(HOST)/bin/dml/python/dml/c_backend.py
$(HOST)/bin/dml/python/dml/g_backend.py
$(HOST)/bin/dml/python/dml/codegen.py
+ $(HOST)/bin/dml/python/dml/compat.py
$(HOST)/bin/dml/python/dml/crep.py
$(HOST)/bin/dml/python/dml/ctree.py
$(HOST)/bin/dml/python/dml/dmllex.py
diff --git a/Makefile b/Makefile
index a4079e7eb..97326e80a 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,7 @@ PYFILES := dml/__init__.py \
dml/c_backend.py \
dml/g_backend.py \
dml/codegen.py \
+ dml/compat.py \
dml/crep.py \
dml/ctree.py \
dml/template.py \
@@ -210,9 +211,11 @@ DOC_FILES_14 := $(wildcard $(DOC_SRC_DIR_14)/*.md) $(DOC_SRC_DIR_14)/toc.json
DOC_DEST_14 := $(SIMICS_PROJECT)/$(HOST_TYPE)/doc/html/dml-1.4-reference-manual
DOC_MARKER_14 := $(DOC_DEST_14)/filelist.json
-GENERATED_MD_FILES_14 = $(addprefix generated-md-1.4/,grammar.md messages.md changes-auto.md dml-builtins.md utility.md)
+GENERATED_MD_FILES_14 = $(addprefix generated-md-1.4/,grammar.md messages.md changes-auto.md dml-builtins.md utility.md deprecations-auto.md)
generated-md-1.4/changes-auto.md: $(SRC_BASE)/$(TARGET)/porting_to_md.py $(DMLC_BIN) | generated-md-1.4
$(PYTHON) $< $(PYTHONPATH) $@
+generated-md-1.4/deprecations-auto.md: $(SRC_BASE)/$(TARGET)/deprecations_to_md.py $(SRC_BASE)/$(TARGET)/doc/1.4/deprecations-header.md $(DMLC_BIN) | generated-md-1.4
+ $(PYTHON) $< $(PYTHONPATH) $(word 2,$^) $@
generated-md-1.4/dml-builtins.md generated-md-1.4/utility.md: generated-md-1.4/%.md: $(DMLC_DIR)/lib/1.4/%.dml
$(PYTHON) $(DMLC_DIR)/dmlcomments_to_md.py $< $@
DOC_FILES_14 += $(GENERATED_MD_FILES_14)
diff --git a/RELEASENOTES.docu b/RELEASENOTES.docu
index f671560ed..eb5b8b098 100644
--- a/RELEASENOTES.docu
+++ b/RELEASENOTES.docu
@@ -1730,4 +1730,11 @@ extern typedef struct { } my_type_t;
This warning is only enabled by default with Simics API version 7 or
above. With version 6 and below it must be explicitly enabled by passing
--warn=WLOGMIXUP to DMLC.
+
--no-compat=_tag_
flag, which disables the
+feature represented by _`tag`_. The available tags are listed in the next
+section. The tag also controls the name of a global boolean parameter that the
+DML program may use to check whether the feature is available. The parameter's
+name is the tag name preceded by `_compat_`.
+
+## List of deprecated features
diff --git a/doc/1.4/toc.json b/doc/1.4/toc.json
index 63c5da40b..ac9ac02d8 100644
--- a/doc/1.4/toc.json
+++ b/doc/1.4/toc.json
@@ -16,6 +16,9 @@
{"file": "messages.md",
"numbering": true,
"appendix": true},
+ {"file": "deprecations-auto.md",
+ "numbering": true,
+ "appendix": true},
{"file": "changes.md",
"numbering": true,
"appendix": true,
diff --git a/generate_env.py b/generate_env.py
index 417b3dfed..2a94b60b6 100644
--- a/generate_env.py
+++ b/generate_env.py
@@ -1,7 +1,6 @@
# © 2021-2023 Intel Corporation
# SPDX-License-Identifier: MPL-2.0
-import os
import sys
from pathlib import Path
@@ -13,7 +12,7 @@ def generate_env(out):
def is_windows():
return {is_windows()}
def api_versions():
- return {api_versions()}
+ return {repr(api_versions())}
def default_api_version():
return {repr(default_api_version())}
''')
diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml
index c7766e97b..b269bf15c 100644
--- a/lib/1.2/dml-builtins.dml
+++ b/lib/1.2/dml-builtins.dml
@@ -206,6 +206,16 @@ template device {
parameter simics_api_version auto;
+ parameter _compat_port_proxy_ifaces auto;
+ parameter _compat_port_proxy_attrs auto;
+ parameter _compat_port_obj_param auto;
+ parameter _compat_io_memory auto;
+ parameter _compat_dml12_inline auto;
+ parameter _compat_dml12_not auto;
+ parameter _compat_dml12_goto auto;
+ parameter _compat_dml12_misc auto;
+ parameter _compat_dml12_int auto;
+
// automatic parameters
parameter obj auto;
parameter logobj = $obj;
diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml
index 4725dfa23..e921a3594 100644
--- a/lib/1.4/dml-builtins.dml
+++ b/lib/1.4/dml-builtins.dml
@@ -539,9 +539,16 @@ template device {
param _is_simics_object = true;
param simics_api_version auto;
- param _api_5_or_older = simics_api_version == "5"
- || simics_api_version == "4.8";
- param _api_6_or_older = simics_api_version == "6" || _api_5_or_older;
+
+ param _compat_port_proxy_ifaces auto;
+ param _compat_port_proxy_attrs auto;
+ param _compat_port_obj_param auto;
+ param _compat_io_memory auto;
+ param _compat_dml12_inline auto;
+ param _compat_dml12_not auto;
+ param _compat_dml12_goto auto;
+ param _compat_dml12_misc auto;
+ param _compat_dml12_int auto;
// automatic parameters
param obj auto;
@@ -554,7 +561,7 @@ template device {
param be_bitorder default _be_bitorder;
param log_group = undefined;
- param use_io_memory default _api_6_or_older;
+ param use_io_memory default _compat_io_memory;
// this was available in DML 1.2; defensively reserved in 1.4
param banks = undefined;
// this carried semantics in DML 1.2; deprecated in 1.4
@@ -1443,7 +1450,7 @@ template port is object {
param _is_simics_object = true;
// limitations for ports are not recognized
param limitations = undefined;
- param obj = dev._api_5_or_older #? dev.obj #: _port_obj();
+ param obj = dev._compat_port_obj_param #? dev.obj #: _port_obj();
session conf_object_t *_cached_port_obj;
shared method _port_obj() -> (conf_object_t *) {
@@ -1775,7 +1782,7 @@ template bank is (object, shown_desc) {
// compatibility: referencing 'obj' in a bank method must evaluate to
// dev.obj in code that can compile on both 5 and 6
// TODO: we should probably make obj an immutable session variable
- param obj = dev._api_5_or_older #? dev.obj #: _bank_obj();
+ param obj = dev._compat_port_obj_param #? dev.obj #: _bank_obj();
param partial : bool;
param overlapping : bool;
param _le_byte_order : bool;
diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py
index 3b7cd38af..343306eba 100644
--- a/py/dml/c_backend.py
+++ b/py/dml/c_backend.py
@@ -13,7 +13,7 @@
from pathlib import Path
from . import objects, logging, crep, output, ctree, serialize, structure
-from . import traits
+from . import traits, compat
import dml.globals
from .structure import get_attr_name, port_class_ident, need_port_proxy_attrs
from .logging import *
@@ -886,36 +886,37 @@ def generate_implement(code, device, impl):
# Legacy interface ports are only added for ports and banks that were
# available in Simics 5, i.e. zero or one dimensional direct
# descendants of the device object
- if (port.local_dimensions() == 0 and port.parent.parent is None
- and port.objtype != 'subdevice'):
- code.out("static const %s = %s;\n" % (
- ifacetype.declaration('port_iface'),
- interface_block(device, ifacestruct, methods, ())))
- code.out('SIM_register_port_interface'
- '(class, "%s", &port_iface, "%s", %s);\n'
- % (impl.name, crep.cname(port), desc))
- elif (port.local_dimensions() == 1 and port.parent.parent is None
- and port.objtype != 'subdevice'):
- [arrlen] = port.arraylens()
- code.out("static const %s%s = %s;\n" %
- (ifacetype.declaration("ifaces"),
- "[%s]" % (arrlen,),
- "{%s\n}" % ",\n".join(
- "%s" % interface_block(device, ifacestruct,
- methods, (i, ))
- for i in range(arrlen))))
- code.out("interface_array_t iface_vect = VNULL;\n")
- idxvar = "i0"
- code.out("for (int %s = 0; %s < %d; %s++)\n" %
- (idxvar, idxvar, arrlen, idxvar),
- postindent = 1)
- access = "[%s]" % idxvar
- code.out("VADD(iface_vect, &ifaces%s);\n" % access,
- postindent = -1)
- code.out('VT_register_port_array_interface'
- '(class, "%s", &iface_vect, "%s", %s);\n'
- % (impl.name, crep.cname(port), desc))
- code.out('VFREE(iface_vect);\n')
+ if (port.parent is dml.globals.device
+ and port.objtype in {'port', 'bank'}
+ and compat.port_proxy_ifaces in dml.globals.enabled_compat):
+ if port.local_dimensions() == 0:
+ code.out("static const %s = %s;\n" % (
+ ifacetype.declaration('port_iface'),
+ interface_block(device, ifacestruct, methods, ())))
+ code.out('SIM_register_port_interface'
+ '(class, "%s", &port_iface, "%s", %s);\n'
+ % (impl.name, crep.cname(port), desc))
+ elif port.local_dimensions() == 1:
+ [arrlen] = port.arraylens()
+ code.out("static const %s%s = %s;\n" %
+ (ifacetype.declaration("ifaces"),
+ "[%s]" % (arrlen,),
+ "{%s\n}" % ",\n".join(
+ "%s" % interface_block(device, ifacestruct,
+ methods, (i, ))
+ for i in range(arrlen))))
+ code.out("interface_array_t iface_vect = VNULL;\n")
+ idxvar = "i0"
+ code.out("for (int %s = 0; %s < %d; %s++)\n" %
+ (idxvar, idxvar, arrlen, idxvar),
+ postindent = 1)
+ access = "[%s]" % idxvar
+ code.out("VADD(iface_vect, &ifaces%s);\n" % access,
+ postindent = -1)
+ code.out('VT_register_port_array_interface'
+ '(class, "%s", &iface_vect, "%s", %s);\n'
+ % (impl.name, crep.cname(port), desc))
+ code.out('VFREE(iface_vect);\n')
code.out("}\n", preindent = -1)
def port_prefix(port):
@@ -1544,7 +1545,7 @@ def generate_alloc(device):
out('}\n\n', preindent = -1)
def generate_initialize(device):
- if dml.globals.api_version <= '6':
+ if dml.globals.api_version <= compat.api_6:
start_function_definition(
('void %s_pre_del_notify(conf_object_t *_subscriber,'
' conf_object_t *_notifier, lang_void *_data)') % crep.cname(device))
@@ -1589,7 +1590,7 @@ def generate_initialize(device):
codegen_inline_byname(device, (), '_init', [], [], device.site).toc()
reset_line_directive()
- if dml.globals.api_version <= '6':
+ if dml.globals.api_version <= compat.api_6:
out('SIM_add_notifier(_obj, Sim_Notify_Object_Delete, _obj, '
+ crep.cname(device) + '_pre_del_notify, NULL);\n')
@@ -1909,7 +1910,7 @@ def generate_init_data_objs(device):
for (start, end) in [('A', 'Z'), ('a', 'z'), ('0', '9'), ('_', '_')]
for i in range(ord(start), ord(end) + 1))
def init_function_name(device, outprefix):
- if dml.globals.api_version in {'4.8'}:
+ if dml.globals.api_version <= compat.api_4_8:
return 'initialize_' + crep.cname(device)
return '_initialize_' + ''.join(
(ch if ch in ident_chars else '_') for ch in outprefix)
@@ -1936,7 +1937,7 @@ def generate_init(device, initcode, outprefix):
out('.alloc = '+crep.cname(device)+'_alloc,\n')
out('.init = '+crep.cname(device)+'_init,\n')
out('.finalize = '+crep.cname(device)+'_finalize,\n')
- if dml.globals.api_version >= '7':
+ if dml.globals.api_version >= compat.api_7:
out('.deinit = '+crep.cname(device)+'_deinit,\n')
out('.dealloc = '+crep.cname(device)+'_dealloc,\n')
out('.description = '+doc.read()+',\n')
@@ -3228,12 +3229,10 @@ def generate_cfile(device, footers,
full_module):
global c_file
- if dml.globals.api_version == 'internal':
- api_define = ''
- else:
- sym = 'SIMICS_%s_API' % dml.globals.api_version.replace('.', '_')
- api_define = '#ifndef %s\n#define %s\n#endif\n' % (
- sym, sym)
+ sym = 'SIMICS_%s_API' % ('4_8' if dml.globals.api_version == compat.api_4_8
+ else dml.globals.api_version.str)
+ api_define = '#ifndef %s\n#define %s\n#endif\n' % (
+ sym, sym)
c_top = '\n'.join([
'/*',
@@ -3416,7 +3415,7 @@ def generate_cfile_body(device, footers, full_module, filename_prefix):
if full_module:
# caught as error earlier on
- assert dml.globals.api_version in ['4.8']
+ assert dml.globals.api_version == compat.api_4_8
out('\n')
out('EXPORTED void\n')
out('init_local(void)\n')
diff --git a/py/dml/codegen.py b/py/dml/codegen.py
index 5674999f0..a952b3b07 100644
--- a/py/dml/codegen.py
+++ b/py/dml/codegen.py
@@ -5,13 +5,14 @@
from abc import ABC, abstractmethod, abstractproperty
import operator
import contextlib
+
from functools import reduce
import itertools
import os
import math
from . import objects, crep, ctree, ast, int_register, logging, serialize
-from . import dmlparse, output
+from . import dmlparse, output, compat
from .logging import *
from .expr import *
from .ctree import *
@@ -1079,7 +1080,7 @@ def subast_has_dollar(expr_ast):
@expression_dispatcher
def expr_unop(tree, location, scope):
[op, rh_ast] = tree.args
- if (dml.globals.compat_dml12
+ if (compat.dml12_misc in dml.globals.enabled_compat
and op == 'sizeof' and rh_ast.kind == 'variable_dml12'):
var = rh_ast.args[0]
if var in typedefs and scope.lookup(var) is None:
@@ -1120,7 +1121,7 @@ def expr_unop(tree, location, scope):
return ctree.AddressOfMethod(tree.site, func)
raise rh.exc()
if op == '!':
- if dml.globals.compat_dml12 and dml.globals.api_version <= "5":
+ if compat.dml12_not in dml.globals.enabled_compat:
t = rh.ctype()
if isinstance(safe_realtype(t), TInt) and subast_has_dollar(rh_ast):
# A previous bug caused DMLC to permit expressions on
@@ -1142,7 +1143,8 @@ def expr_unop(tree, location, scope):
elif op == 'post++': return mkPostInc(tree.site, rh)
elif op == 'post--': return mkPostDec(tree.site, rh)
elif op == 'sizeof':
- if not dml.globals.compat_dml12 and not isinstance(rh, ctree.LValue):
+ if (compat.dml12_misc not in dml.globals.enabled_compat
+ and not isinstance(rh, ctree.LValue)):
raise ERVAL(rh.site, 'sizeof')
return codegen_sizeof(tree.site, rh)
elif op == 'defined': return mkBoolConstant(tree.site, True)
@@ -1318,9 +1320,9 @@ def expr_cast(tree, location, scope):
for (site, _) in struct_defs:
report(EANONSTRUCT(site, "'cast' expression"))
- if (dml.globals.compat_dml12 and dml.globals.api_version <= "6"
+ if (compat.dml12_misc in dml.globals.enabled_compat
and isinstance(expr, InterfaceMethodRef)):
- # Workaround for bug 24144
+ # Workaround for SIMICS-9868
return mkLit(tree.site, "%s->%s" % (
expr.node_expr.read(), expr.method_name), type)
@@ -1524,18 +1526,17 @@ def eval_type(asttype, site, location, scope, extern=False, typename=None,
etype = TInt(width, False, members)
elif tag == 'typeof':
expr = codegen_expression_maybe_nonvalue(info, location, scope)
- if (not dml.globals.compat_dml12
- and not isinstance(expr, ctree.LValue)
- # for compatibility with dml-builtins, using 1.2
- and not isinstance(expr, ctree.RegisterWithFields)):
- raise ERVAL(expr.site, 'typeof')
if isinstance(expr, NonValue):
+ # for compatibility with dml-builtins, using 1.2
if isinstance(expr, (ctree.NoallocNodeRef,
ctree.RegisterWithFields,
ctree.IncompleteNodeRefWithStorage)):
etype = expr.node_type
else:
raise expr.exc()
+ elif (not isinstance(expr, ctree.LValue)
+ and compat.dml12_misc not in dml.globals.enabled_compat):
+ raise ERVAL(expr.site, 'typeof')
else:
etype = expr.ctype().clone()
if not etype:
@@ -1979,7 +1980,8 @@ def stmt_local(stmt, location, scope):
def convert_decl(decl_ast):
(name, asttype) = decl_ast.args
- if dml.globals.dml_version == (1, 2) and not dml.globals.compat_dml12:
+ if (dml.globals.dml_version == (1, 2)
+ and compat.dml12_misc not in dml.globals.enabled_compat):
check_varname(stmt.site, name)
(struct_decls, etype) = eval_type(asttype, stmt.site, location, scope)
stmts.extend(mkStructDefinition(site, t) for (site, t) in struct_decls)
@@ -2543,7 +2545,7 @@ def stmt_assert(stmt, location, scope):
@statement_dispatcher
def stmt_goto(stmt, location, scope):
[label] = stmt.args
- if not dml.globals.compat_dml12:
+ if compat.dml12_goto not in dml.globals.enabled_compat:
report(ESYNTAX(stmt.site, 'goto', 'goto statement not allowed'))
return [mkGoto(stmt.site, label)]
@@ -3043,7 +3045,8 @@ def stmt_select(stmt, location, scope):
if_chain = mkIf(cond.site, cond, stmt, if_chain)
return [if_chain]
raise lst.exc()
- elif dml.globals.compat_dml12 and isinstance(lst.ctype(), TVector):
+ elif (compat.dml12_misc in dml.globals.enabled_compat
+ and isinstance(lst.ctype(), TVector)):
itervar = lookup_var(stmt.site, scope, itername)
if not itervar:
raise EIDENT(stmt.site, itername)
@@ -3315,18 +3318,20 @@ def common_inline(site, method, indices, inargs, outargs):
return mkNull(site)
if dml.globals.debuggable:
- if method.fully_typed and (not dml.globals.compat_dml12
- or all(not arg.constant for arg in inargs)):
+ if method.fully_typed and (
+ compat.dml12_inline not in dml.globals.enabled_compat
+ or all(not arg.constant for arg in inargs)):
# call method instead of inlining it
func = method_instance(method)
else:
# create a specialized method instance based on parameter
# types, and call that
- intypes = tuple(arg if ((ptype is None or dml.globals.compat_dml12)
- and (arg.constant or undefined(arg)))
- else methfunc_param(ptype, arg)
- for ((pname, ptype), arg)
- in zip(method.inp, inargs))
+ intypes = tuple(
+ arg if ((ptype is None
+ or compat.dml12_inline in dml.globals.enabled_compat)
+ and (arg.constant or undefined(arg)))
+ else methfunc_param(ptype, arg)
+ for ((pname, ptype), arg) in zip(method.inp, inargs))
outtypes = tuple(methfunc_param(ptype, arg)
for ((pname, ptype), arg)
in zip(method.outp, outargs))
@@ -3546,8 +3551,9 @@ def codegen_inline(site, meth_node, indices, inargs, outargs,
if inhibit_copyin or undefined(arg):
param_scope.add(ExpressionSymbol(parmname, arg, arg.site))
- elif arg.constant and (parmtype is None
- or dml.globals.compat_dml12):
+ elif arg.constant and (
+ parmtype is None
+ or compat.dml12_inline in dml.globals.enabled_compat):
# Constants must be passed directly to
# provide constant folding. Other values are stored in a
# local variable to improve type checking and variable
@@ -3761,7 +3767,8 @@ def codegen_method_func(func):
method.site)
inline_scope = MethodParamScope(global_scope)
for (name, e) in func.inp:
- if dml.globals.dml_version == (1, 2) and not dml.globals.compat_dml12:
+ if dml.globals.dml_version == (1, 2) and (
+ compat.dml12_misc not in dml.globals.enabled_compat):
check_varname(method.site, name)
if isinstance(e, Expression):
inlined_arg = (
@@ -3969,7 +3976,7 @@ def codegen_call(site, meth_node, indices, inargs, outargs):
require_fully_typed(site, meth_node)
func = method_instance(meth_node)
- if dml.globals.compat_dml12:
+ if compat.dml12_misc in dml.globals.enabled_compat:
# For backward compatibility. See bug 21367.
inargs = [mkCast(site, arg, TPtr(TNamed('char')))
if isinstance(arg, StringConstant) else arg
diff --git a/py/dml/compat.py b/py/dml/compat.py
new file mode 100644
index 000000000..eeae2aab7
--- /dev/null
+++ b/py/dml/compat.py
@@ -0,0 +1,225 @@
+# © 2023 Intel Corporation
+# SPDX-License-Identifier: MPL-2.0
+
+import abc
+from dataclasses import dataclass
+
+
+@dataclass(order=True, frozen=True)
+class API:
+ ordinal: int
+ str: str
+
+
+api_4_8 = API(4, "4.8")
+api_5 = API(5, "5")
+api_6 = API(6, "6")
+api_7 = API(7, "7")
+
+
+# All API versions known to the DML implementation. Note that the set
+# of APIs accessible to the end-user is limited to what the associated
+# Simics version supports.
+apis = {api.str: api
+ for api in [api_4_8, api_5, api_6, api_7]}
+
+
+class CompatFeature(abc.ABC):
+ def tag(self) -> str:
+ return self.__class__.__name__
+
+ @abc.abstractproperty
+ def __doc__(self): pass
+
+ @abc.abstractproperty
+ def short(self) -> str: pass
+
+ @abc.abstractproperty
+ def last_api_version(self) -> API: pass
+
+
+# tag -> feature
+features: dict[str, CompatFeature] = {}
+
+
+def feature(cls: type[CompatFeature]):
+ assert issubclass(cls, CompatFeature)
+ singleton = cls()
+ features[singleton.tag()] = singleton
+ return singleton
+
+
+@feature
+class port_proxy_ifaces(CompatFeature):
+ '''Version 5 and earlier of Simics relied on interface ports (as
+ registered by the `SIM_register_port_interface` API function) for
+ exposing the interfaces of ports and banks. In newer versions of
+ Simics, interfaces are instead exposed on separate configuration
+ objects. When this feature is enabled, old-style interface ports
+ are created as proxies to the interfaces on the respective port
+ objects. Such proxies are not created for all banks and ports;
+ e.g., banks inside groups were not allowed in Simics 5, so such
+ banks do not need proxies for backward compatibility.
+ '''
+ short = "Don't generate proxy port interfaces for banks and ports"
+ last_api_version = api_7
+
+
+@feature
+class port_proxy_attrs(CompatFeature):
+ r'''In Simics 5, configuration attributes for `connect`,
+ `attribute` and `register` objects inside banks and ports were
+ registered on the device object, named like
+ bankname\_attrname
. Such proxy
+ attributes are only created When this feature is enabled.
+ Proxy attributes are not created for all banks and ports, in the
+ same manner as documented in the `port_proxy_ifaces` feature.
+ '''
+ short = ("Don't generate top-level proxy attributes"
+ + " for attributes in banks and ports")
+ last_api_version = api_7
+
+
+@feature
+class io_memory(CompatFeature):
+ '''The `transaction` interface was introduced in 6, and will
+ eventually replace the `io_memory` interface. When this feature is
+ enabled, the top-level parameter `use_io_memory` defaults to
+ `true`, causing `bank` objects to implement `io_memory` instead of
+ `transaction` by default.'''
+ short = 'Use the io_memory interface by default in banks'
+ last_api_version = api_6
+
+
+@feature
+class port_obj_param(CompatFeature):
+ '''This compatibility feature changes the value of the `obj`
+ parameter in `bank` and `port` objects: Before Simics 6 there were
+ no dedicated port objects, so this parameter did not exist and if
+ you wrote `obj` inside a bank, this would resolve to
+ `dev.obj`. This feature preserves this legacy behaviour by making
+ the `obj` parameter of banks and ports resolves to `dev.obj`
+ rather than the port object.
+ '''
+ short = "bank.obj and port.obj resolve to dev.obj"
+ last_api_version = api_5
+
+
+@feature
+class dml12_inline(CompatFeature):
+ '''When using `inline` to inline a method in a DML 1.2 device,
+ constant parameters passed in typed arguments are inlined as
+ constants when this feature is enabled. This can improve
+ compilation time in some cases, but has some unintuitive semantic
+ implications.
+ '''
+ short = "Don't inline method arguments with a declared type in DML 1.2"
+ last_api_version = api_6
+
+
+# separate class only because last_api_version differs
+@feature
+class dml12_not(CompatFeature):
+ '''DML 1.2-specific: the operand of the `!` operator is not
+ type-checked; in particular, negation expressions on the form
+ `!$reg`, where `reg` is a register, are permitted'''
+ short = "Disallow ! operator on register references in DML 1.2"
+ last_api_version = api_5
+
+
+@feature
+class dml12_misc(CompatFeature):
+ '''This compatibility feature preserves a number of minor language quirks
+ that were originally in DML 1.2, but were cleaned up in
+ DML 1.4. When this feature is enabled, DML 1.2 will permit the following:
+
+ * `sizeof(typename)` (see `WSIZEOFTYPE`)
+
+ * the `typeof` operator on an expression that isn't an lvalue
+
+ * `select` statements over `vect` types
+
+ * Passing a string literal in a (non-`const`) `char *` method argument
+
+ * Using the character `-` in the `c_name` parameter of `interface` objects
+
+ * Using the `c_name` parameter to override interface type in
+ `implement` objects
+
+ * `loggroup` identifiers are accessible under the same name in
+ generated C code
+
+ * Applying the `&` operator on something that isn't an lvalue
+ (typically gives broken C code)
+
+ * `extern` statements that do not specify a type (`extern foo;`)
+
+ * Anonymous banks (`bank { ... }`)
+
+ * Unused templates may instantiate non-existing templates
+
+ * The same symbol may be used both for a top-level object (`$`
+ scope) and a top-level symbol (non-`$` scope, e.g. `extern`,
+ `constant` or `loggroup`)
+
+ '''
+ short = "Disable various DML 1.2 quirks"
+ last_api_version = api_6
+
+
+@feature
+class dml12_goto(CompatFeature):
+ '''The `goto` statement is deprecated; this compatibility feature
+ preserves it. Most `goto` based control structures can be reworked by
+ changing the `goto` into a `throw`, and its label into a `catch`
+ block; since this is sometimes nontrivial, it can be useful to disable
+ the `goto` statement separately.
+ '''
+ short = "Disable the goto statement in DML 1.2"
+ last_api_version = api_6
+
+
+@feature
+class dml12_int(CompatFeature):
+ '''This compatibility feature affects many semantic details of
+ integer arithmetic. When this feature is enabled, DMLC translates
+ most integer operations directly into C, without compensating for
+ DML-specifics, like the support for odd-sized
+ uintNN types; this can sometimes have unexpected
+ consequences. The most immediate effect of disabling this feature
+ is that DMLC will report errors on statements like `assert 0;` and
+ `while (1) { ... }`, which need to change into `assert false;` and
+ `while (true) { ... }`, respectively. Other effects include:
+
+ * Integers of non-standard sizes are represented as a native C
+ type, e.g. `uint5` is represented as `uint8`, allowing it to
+ store numbers too large to fit in 5 bits. With modern DML
+ semantics, arithmetic is done on 64-bit integers and bits are
+ truncated if casting or storing in a smaller type.
+
+ Old code sometimes relies on this feature by comparing variables
+ of type `int1` to the value `1`. In DML 1.4, the only values of
+ type `int1` are `0` and `-1`, so such code should be rewritten
+ to use the `uint1` type. It can be a good idea to grep for
+ `[^a-z_]int1[^0-9]` and review if `uint1` is a better choice.
+
+ * Some operations have undefined behaviour in C, which is
+ inherited by traditional DML 1.2. In modern DML this is
+ well-defined, e.g., an unconditional critical error on negative
+ shift or division by zero, and truncation on too large shift
+ operands or signed shift overflow.
+
+ * Comparison operators `<`, `<=`, `==`, `>=`, `>` inherit C
+ semantics in traditional DML, whereas in modern DML they are
+ compared as integers. This sometimes makes a difference when
+ comparing signed and unsigned numbers; in particular, `-1 !=
+ 0xffffffffffffffff` consistently in modern DML, whereas with
+ compatibility semantics, they are consiered different only if
+ both are constant.
+
+ The `dml12_int` feature only applies to DML 1.2 files; if a DML
+ 1.4 file is imported from a DML 1.2 file, then modern DML
+ semantics is still used for operations in that file.
+ '''
+ short = "Use legacy integer semantics in DML 1.2"
+ last_api_version = api_6
diff --git a/py/dml/crep.py b/py/dml/crep.py
index 4d3a26dd8..8214c8dff 100644
--- a/py/dml/crep.py
+++ b/py/dml/crep.py
@@ -10,6 +10,7 @@
from .logging import *
from .expr_util import *
from .messages import *
+from . import compat
__all__ = (
'cname',
@@ -73,7 +74,8 @@ def cname(node):
elif node.objtype == 'interface':
# this is weird... kept for compatibility
name = param_str(node, 'c_name').replace('-', '_')
- if name != node.name and not dml.globals.compat_dml12:
+ if name != node.name and (
+ compat.dml12_misc not in dml.globals.enabled_compat):
report(WDEPRECATED(param_expr_site(node, 'c_name'),
'parameter c_name'))
return name
@@ -176,7 +178,7 @@ def node_storage_type_dml12(node, site):
else:
return None
elif node.objtype == 'implement':
- if dml.globals.compat_dml12:
+ if compat.dml12_misc in dml.globals.enabled_compat:
typename = param_str(node, 'c_type')
t = TNamed(typename)
t.declaration_site = node.site
@@ -226,7 +228,7 @@ def conf_object(site, node, indices):
cref_portobj(node, indices[:node.dimensions]))
def cloggroup(name):
- if dml.globals.compat_dml12:
+ if compat.dml12_misc in dml.globals.enabled_compat:
return name
else:
return '_dml_loggroup_' + name
diff --git a/py/dml/ctree.py b/py/dml/ctree.py
index 5d9fad091..04878f579 100644
--- a/py/dml/ctree.py
+++ b/py/dml/ctree.py
@@ -22,6 +22,7 @@
from .expr_util import *
from .slotsmeta import auto_init
from . import dmlparse, output
+from . import compat
import dml.globals
# set from codegen.py
codegen_call_expr = None
@@ -1151,8 +1152,13 @@ def make(cls, site, lh, rh):
lhtype = lh.ctype()
rhtype = rh.ctype()
- if isinstance(lhtype, TUnknown) or isinstance(rhtype, TUnknown):
- return cls(site, lh, rh)
+ if (dml.globals.dml_version == (1, 2)
+ and (isinstance(lhtype, TUnknown) or isinstance(rhtype, TUnknown))):
+ # urgh, some classes take an extra constructor arg
+ if issubclass(cls, (ArithBinOp, BitBinOp, BitShift)):
+ return cls(site, lh, rh, TUnknown)
+ else:
+ return cls(site, lh, rh)
return cls.make_simple(site, lh, rh)
@@ -2455,7 +2461,8 @@ def make_simple(cls, site, rh):
TFunction([TPtr(TNamed('conf_object_t')),
TPtr(TVoid())],
TVoid())))
- if not dml.globals.compat_dml12 and not isinstance(rh, LValue):
+ if (compat.dml12_misc not in dml.globals.enabled_compat
+ and not isinstance(rh, LValue)):
raise ERVAL(rh.site, '&')
return AddressOf(site, rh)
@@ -2509,7 +2516,7 @@ class Not(UnaryOp):
@staticmethod
def make_simple(site, rh):
- if not dml.globals.compat_dml12:
+ if compat.dml12_misc not in dml.globals.enabled_compat:
rh = as_bool(rh)
if rh.constant:
return mkBoolConstant(site, not rh.value)
@@ -3788,7 +3795,8 @@ def lookup_component(site, base, indices, name, only_local):
indices = indices[:-base.local_dimensions()]
return lookup_component(site, base.parent, indices, name, False)
- if dml.globals.compat_dml12 and not base.parent:
+ if (compat.dml12_misc in dml.globals.enabled_compat
+ and not base.parent):
# Last resort is to look for components in anonymous banks
for bank in base.get_components('bank'):
if not bank.name:
diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py
index cd6b29da4..5821ff55a 100644
--- a/py/dml/dmlc.py
+++ b/py/dml/dmlc.py
@@ -12,6 +12,7 @@
from . import serialize
from . import dmlparse
from . import output
+from . import compat
import dml.c_backend
import dml.info_backend
@@ -282,6 +283,28 @@ def print_help(self):
for tag in by_ignored[True]:
print(f' {tag}')
+class CompatHelpAction(HelpAction):
+ def print_help(self):
+ print('''\
+Tags accepted by --no-compat. Each of these represents a deprecated
+compatibility feature that will be unavailable in all API versions
+newer than a particular version. The --no-compat=TAG flag disables a
+feature also when an older API version is used. This allows migration
+to a new API version in smaller steps, and can also allow disabling
+features that are scheduled for removal in a future API version.''')
+ by_version = {}
+ for feature in compat.features.values():
+ if (feature.last_api_version.str in api_versions()
+ or (feature.last_api_version
+ > compat.apis[default_api_version()])):
+ by_version.setdefault(feature.last_api_version, []).append(
+ feature)
+ for (api, features) in sorted(by_version.items()):
+ print(f' Features available with --simics-api={api.str}'
+ ' or older:')
+ for feature in sorted(features, key=lambda f: f.tag()):
+ print(f' {feature.tag():20s} {feature.short}')
+
def main(argv):
# DML files must be utf8, but are generally opened without specifying
# the 'encoding' arg. This works only if utf8_mode is enabled.
@@ -385,16 +408,11 @@ def main(argv):
parser.add_argument('--werror', action='store_true',
help='Turn all warnings into errors')
- #