From 56e5f7209f72e3dd89219bf775a15fff0c71b8b3 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 13 Sep 2023 15:58:02 +0200 Subject: [PATCH 01/20] Remove --illegal-attrs flag From df6f8da784cf977670185602d8c72f5437b919b5 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 13 Sep 2023 15:56:02 +0200 Subject: [PATCH 02/20] Add --deprecate=PORT_PROXY_IFACES --- MODULEINFO | 1 + Makefile | 1 + lib/1.2/dml-builtins.dml | 2 + lib/1.4/dml-builtins.dml | 2 + py/dml/c_backend.py | 64 ++++++++++--------- py/dml/deprecations.py | 48 ++++++++++++++ py/dml/dmlc.py | 42 ++++++++++++ py/dml/globals.py | 2 + py/dml/structure.py | 5 +- .../legacy/T_port_proxy_ifaces_disabled.dml | 16 +++++ .../legacy/T_port_proxy_ifaces_enabled.dml | 15 +++++ test/1.4/legacy/port_proxy_ifaces.dml | 23 +++++++ 12 files changed, 189 insertions(+), 32 deletions(-) create mode 100644 py/dml/deprecations.py create mode 100644 test/1.4/legacy/T_port_proxy_ifaces_disabled.dml create mode 100644 test/1.4/legacy/T_port_proxy_ifaces_enabled.dml create mode 100644 test/1.4/legacy/port_proxy_ifaces.dml diff --git a/MODULEINFO b/MODULEINFO index 90295af9c..b824b208f 100644 --- a/MODULEINFO +++ b/MODULEINFO @@ -45,6 +45,7 @@ Make: dmlc $(HOST)/bin/dml/python/dml/codegen.py $(HOST)/bin/dml/python/dml/crep.py $(HOST)/bin/dml/python/dml/ctree.py + $(HOST)/bin/dml/python/dml/deprecations.py $(HOST)/bin/dml/python/dml/dmllex.py $(HOST)/bin/dml/python/dml/dmllex12.py $(HOST)/bin/dml/python/dml/dmllex14.py diff --git a/Makefile b/Makefile index a4079e7eb..b3d59d039 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ PYFILES := dml/__init__.py \ dml/topsort.py \ dml/traits.py \ dml/types.py \ + dml/deprecations.py \ dml/dmllex.py \ dml/dmllex12.py \ dml/dmllex14.py \ diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index c7766e97b..8e3fb2b94 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -206,6 +206,8 @@ template device { parameter simics_api_version auto; + parameter _deprecate_port_proxy_ifaces 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..1cf7402d7 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -543,6 +543,8 @@ template device { || simics_api_version == "4.8"; param _api_6_or_older = simics_api_version == "6" || _api_5_or_older; + param _deprecate_port_proxy_ifaces auto; + // automatic parameters param obj auto; param classname default name; diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 3b7cd38af..5970ea976 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, deprecations import dml.globals from .structure import get_attr_name, port_class_ident, need_port_proxy_attrs from .logging import * @@ -886,36 +886,38 @@ 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 (deprecations.port_proxy_ifaces + not in dml.globals.enabled_deprecations)): + 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): diff --git a/py/dml/deprecations.py b/py/dml/deprecations.py new file mode 100644 index 000000000..53a9ca45f --- /dev/null +++ b/py/dml/deprecations.py @@ -0,0 +1,48 @@ +# © 2023 Intel Corporation +# SPDX-License-Identifier: MPL-2.0 + +import abc + +from . import env + + +class DeprecatedFeature(abc.ABC): + def tag(self): + return self.__class__.__name__ + + @abc.abstractproperty + def __doc__(self): pass + + @abc.abstractproperty + def short(self): pass + + @abc.abstractproperty + def last_api_version(self): pass + + +# API version -> tag -> deprecation +deprecations: dict[str, dict[str, DeprecatedFeature]] = { + api: {} for api in env.api_versions()} + + +def deprecation(cls: type[DeprecatedFeature]): + assert issubclass(cls, DeprecatedFeature) + singleton = cls() + deprecations[cls.last_api_version][singleton.tag()] = singleton + return singleton + + +@deprecation +class port_proxy_ifaces(DeprecatedFeature): + '''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 = "6" diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index cd6b29da4..8b776a716 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 deprecations import dml.c_backend import dml.info_backend @@ -282,6 +283,20 @@ def print_help(self): for tag in by_ignored[True]: print(f' {tag}') +class DeprecateHelpAction(HelpAction): + def print_help(self): + print('''\ +Tags accepted by --deprecate. Each of these represents a deprecated +compatibility feature that will be unavailable in all API versions +newer than a particular version. The --deprecate=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 early access +to deprecations in a yet unreleased API version.''') + for (version, deps) in deprecations.deprecations.items(): + print(f' Features available with --simics-api={version} or older:') + for (tag, dep) in deps.items(): + print(f' {tag:20s} {dep.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. @@ -438,6 +453,19 @@ def main(argv): # # + #
--deprecate=TAG
+ #
+ parser.add_argument( + '--deprecate', action='append', default=[], + help='Disable a compatibility feature') + + parser.add_argument( + '--help-deprecate', action=DeprecateHelpAction, + help='List the available tags for --deprecate') + + # + # + # Legacy: write deps to stdout, and assume it's redirected to .dmldep # Should be removed in 6 parser.add_argument( @@ -560,6 +588,20 @@ def main(argv): dml.globals.api_version = options.simics_api + for api in api_versions()[api_versions().index(options.simics_api):]: + features = {tag for (tag, dep) in deprecations.deprecations.items() + if dep.last_api_version in apis} + for flag in options.deprecate: + for tag in flag.split(','): + if any(tag in tags for tags in deprecations.deprecations.values()): + features.discard(tag) + else: + options.error(f'invalid tag {tag} for --deprecate.' + ' Try --help-deprecate.') + dml.globals.enabled_deprecations = { + dep for deps in deprecations.deprecations.values() + for (tag, dep) in deps.items() + if tag not in features} inputfilename = options.input_filename diff --git a/py/dml/globals.py b/py/dml/globals.py index 8fd5592c1..ab7cc6110 100644 --- a/py/dml/globals.py +++ b/py/dml/globals.py @@ -58,6 +58,8 @@ # types.TypeSequence -> codegen.TypeSequenceInfo type_sequence_infos = {} +enabled_deprecations = set() + # 1.4 style integer operations in 1.2, --strict-dml12-int strict_int_flag = None def compat_dml12_int(site): diff --git a/py/dml/structure.py b/py/dml/structure.py index 81364662d..32b0fb397 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -31,6 +31,7 @@ from .reginfo import explode_registers from . import dmlparse from .set import Set +from . import deprecations __all__ = ( 'mkglobals', 'mkdev' @@ -1030,7 +1031,9 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['NULL'] = NullParamExpr(site) autoparams['simics_api_version'] = SimpleParamExpr( mkStringConstant(site, dml.globals.api_version)) - + for (tag, dep) in deprecations.deprecations.items(): + autoparams[f'_deprecate_{tag}'] = SimpleParamExpr( + mkBoolConstant(site, dep in dml.globals.enabled_deprecations)) dml.globals.device = obj elif obj.objtype == 'bank': diff --git a/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml b/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml new file mode 100644 index 000000000..3c2fac8e8 --- /dev/null +++ b/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml @@ -0,0 +1,16 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// DMLC-FLAG --simics-api=6 +/// DMLC-FLAG --deprecate=port_proxy_ifaces + +import "port_proxy_ifaces.dml"; + +method init() { + test(true); +} diff --git a/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml b/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml new file mode 100644 index 000000000..a2b9ff0e0 --- /dev/null +++ b/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml @@ -0,0 +1,15 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// DMLC-FLAG --simics-api=6 + +import "port_proxy_ifaces.dml"; + +method init() { + test(false); +} diff --git a/test/1.4/legacy/port_proxy_ifaces.dml b/test/1.4/legacy/port_proxy_ifaces.dml new file mode 100644 index 000000000..6994dbba7 --- /dev/null +++ b/test/1.4/legacy/port_proxy_ifaces.dml @@ -0,0 +1,23 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +import "simics/devs/signal.dml"; + +saved bool raised = false; + +port p { + implement signal { + method signal_raise() { + raised = true; + } + } +} + +method test(bool proxies_deprecated) { + assert _deprecate_port_proxy_ifaces == proxies_deprecated; + assert (SIM_c_get_port_interface(dev.obj, "signal", "p") == NULL) + == proxies_deprecated; +} From 11c44c06b557a7cfa2c4727bebbebe625d7bc732 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 12 Sep 2023 14:34:15 +0200 Subject: [PATCH 03/20] Add PORT_PROXY_ATTRS deprecation --- lib/1.2/dml-builtins.dml | 1 + lib/1.4/dml-builtins.dml | 1 + py/dml/deprecations.py | 15 +++++++++++++++ py/dml/structure.py | 4 +++- test/1.4/legacy/T_port_proxy_attrs_disabled.dml | 16 ++++++++++++++++ test/1.4/legacy/T_port_proxy_attrs_enabled.dml | 15 +++++++++++++++ test/1.4/legacy/port_proxy_attrs.dml | 17 +++++++++++++++++ 7 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/1.4/legacy/T_port_proxy_attrs_disabled.dml create mode 100644 test/1.4/legacy/T_port_proxy_attrs_enabled.dml create mode 100644 test/1.4/legacy/port_proxy_attrs.dml diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index 8e3fb2b94..c53fd1c9f 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -207,6 +207,7 @@ template device { parameter simics_api_version auto; parameter _deprecate_port_proxy_ifaces auto; + parameter _deprecate_port_proxy_attrs auto; // automatic parameters parameter obj auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index 1cf7402d7..6676df126 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -544,6 +544,7 @@ template device { param _api_6_or_older = simics_api_version == "6" || _api_5_or_older; param _deprecate_port_proxy_ifaces auto; + param _deprecate_port_proxy_attrs auto; // automatic parameters param obj auto; diff --git a/py/dml/deprecations.py b/py/dml/deprecations.py index 53a9ca45f..86d51aeab 100644 --- a/py/dml/deprecations.py +++ b/py/dml/deprecations.py @@ -46,3 +46,18 @@ class port_proxy_ifaces(DeprecatedFeature): ''' short = "Don't generate proxy port interfaces for banks and ports" last_api_version = "6" + + +@deprecation +class port_proxy_attrs(DeprecatedFeature): + 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 deprecation is not enabled. + Proxy attributes are not created for all banks and ports, in the + same manner as documented in the `port_proxy_ifaces` deprecation. + ''' + short = ("Don't generate top-level proxy attributes" + + " for attributes in banks and ports") + last_api_version = "6" diff --git a/py/dml/structure.py b/py/dml/structure.py index 32b0fb397..7f9151465 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -2585,7 +2585,9 @@ def need_port_proxy_attrs(port): assert port.objtype in {'port', 'bank', 'subdevice'} return (port.objtype in {'port', 'bank'} and port.dimensions <= 1 - and port.parent is dml.globals.device) + and port.parent is dml.globals.device + and (deprecations.port_proxy_attrs + not in dml.globals.enabled_deprecations)) class ConfAttrParentObjectProxyInfoParamExpr(objects.ParamExpr): '''The _parent_obj_proxy_info parameter of a attribute, register, or diff --git a/test/1.4/legacy/T_port_proxy_attrs_disabled.dml b/test/1.4/legacy/T_port_proxy_attrs_disabled.dml new file mode 100644 index 000000000..cb7624b9c --- /dev/null +++ b/test/1.4/legacy/T_port_proxy_attrs_disabled.dml @@ -0,0 +1,16 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// DMLC-FLAG --simics-api=6 +/// DMLC-FLAG --deprecate=port_proxy_attrs + +import "port_proxy_attrs.dml"; + +method init() { + test(true); +} diff --git a/test/1.4/legacy/T_port_proxy_attrs_enabled.dml b/test/1.4/legacy/T_port_proxy_attrs_enabled.dml new file mode 100644 index 000000000..6a95f751d --- /dev/null +++ b/test/1.4/legacy/T_port_proxy_attrs_enabled.dml @@ -0,0 +1,15 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// DMLC-FLAG --simics-api=6 + +import "port_proxy_attrs.dml"; + +method init() { + test(false); +} diff --git a/test/1.4/legacy/port_proxy_attrs.dml b/test/1.4/legacy/port_proxy_attrs.dml new file mode 100644 index 000000000..f5a36d597 --- /dev/null +++ b/test/1.4/legacy/port_proxy_attrs.dml @@ -0,0 +1,17 @@ +/* + © 2023 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +import "simics/simulator/conf-object.dml"; + +port p { + attribute a is uint64_attr; +} + +method test(bool proxies_deprecated) { + assert _deprecate_port_proxy_attrs == proxies_deprecated; + assert SIM_class_has_attribute(SIM_object_class(dev.obj), "p_a") + == !proxies_deprecated; +} From 4313dcb6a78b43fa0efc80a57d6018e1b0dc9091 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 13 Sep 2023 15:56:02 +0200 Subject: [PATCH 04/20] Add documentation for --deprecation --- Makefile | 5 ++- deprecations_to_md.py | 24 ++++++++++++ doc/1.4/deprecations-header.md | 70 ++++++++++++++++++++++++++++++++++ doc/1.4/toc.json | 3 ++ 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 deprecations_to_md.py create mode 100644 doc/1.4/deprecations-header.md diff --git a/Makefile b/Makefile index b3d59d039..3cd9f41be 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,6 @@ PYFILES := dml/__init__.py \ dml/topsort.py \ dml/traits.py \ dml/types.py \ - dml/deprecations.py \ dml/dmllex.py \ dml/dmllex12.py \ dml/dmllex14.py \ @@ -211,9 +210,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/deprecations_to_md.py b/deprecations_to_md.py new file mode 100644 index 000000000..02e072e4a --- /dev/null +++ b/deprecations_to_md.py @@ -0,0 +1,24 @@ +# © 2023 Intel Corporation +# SPDX-License-Identifier: MPL-2.0 + +import sys +from pathlib import Path +[path_to_dml, header, outfile] = sys.argv[1:] +sys.path.append(path_to_dml) + +from dml.deprecations import deprecations +from dml.env import api_versions + +with open(outfile, 'w') as f: + f.write(Path(header).read_text()) + for (ver, deps) in deprecations.items(): + if deps and ver in api_versions(): + f.write(f"### Features available up to --simics-api={ver}\n") + f.write("
\n") + for d in deps.values(): + assert d.__doc__ + f.write(f"
{d.tag()}
\n") + doc = '\n'.join(line[4:] if line.startswith(' ') else line + for line in d.__doc__.strip().splitlines()) + f.write(f"
\n\n{doc}\n
\n") + f.write("
\n") diff --git a/doc/1.4/deprecations-header.md b/doc/1.4/deprecations-header.md new file mode 100644 index 000000000..1fed1475c --- /dev/null +++ b/doc/1.4/deprecations-header.md @@ -0,0 +1,70 @@ + + +# Managing deprecated language features + +As the DML language evolves, we sometimes need to change the language in +incompatible ways, which requires DML users to migrate their code. This +appendix describes the mechanisms we provide to make this migration process +smooth for users with large DML code bases. + +In DML, deprecations can come in many forms. Deprecations in the form of +removed or renamed symbols in libraries are rather easy to manage, since they +give clear compile errors that often are straightforward to fix. A slightly +harder type of deprecation is when some language construct or API function +adjusts its semantics; this can make the model behave differently without +signalling error messages. A third kind of deprecation is when DML changes how +compiled models appear in Simics, typically to adjust changes in the Simics +API. Such changes add another dimension because they typically affect the +end-users of the DML models, rather than the authors of the models. Thus, as an +author of a model you may need to synchronize your migration of such features +with your end-users, to ease their transition to a new major version. + +## Deprecation mechanisms + +The simplest deprecation mechanism is Simics API versions: Each deprecated DML +feature is associated with a Simics API version, and each Simics version +supports a number of such API versions. Features reach end-of-life when moving +to a new Simics major version, the features belonging to a previous Simics API +version are dropped. Since Simics is currently the primary distribution channel +for DML, this deprecation scheme is used for DML features as well. + +This scheme allows users with a large code base to smoothly migrate from one +Simics major version, N, to the next, N+1: +* First, while still using version N, make sure all Simics modules are updated + to use API version N. Modules can be migrated one by one. +* Second, update the Simics version to N+1. This should normally have no + effect on DML, but may come with other challenges. +* Third, update modules to API N+1, one by one. Simics version N+1 will always + offers full support for API N, so there is no rush to update, but changing + the API version early makes sure deprecated features are not introduced in + new code. + +In addition to the API version, DML offers some compiler flags for selectively +disabling deprecated features that are normally part of the used API. This has +some uses, in particular: +* During migration to a new API version, disabling one deprecated feature at a + time can allow a smoother, more gradual, migration. +* If a legacy feature is still fully supported in the latest API version, then + it cannot be disabled by selecting an API version, so selectively disabling + it is the only way to turn it off. There are reasons to do this, e.g.: + * Disabling a feature before it is deprecated guarantees that it is not + relied upon in new code, which eases later migration. + * Avoiding a feature that has a newer replacement makes the code base + cleaner and more consistent. + * Some legacy features can also bloat models, by exposing features in a + redundant manner. This can also have a negative impact on performance. + +## Controlling deprecation on the DML command-line +DMLC provides a command-line flag `--api-version` to specify the API version to +be used for a model. When building with the standard Simics build system, this is controlled by the `SIMICS_API_VERSION` variable in `make`, or the `SIMICS_API`/`MODULE_SIMICS_API` variable in `CMake`. + +DMLC also provides the --deprecate=_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 `_deprecate_`. + +## 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, From 18ad914bfe4f24615550342c35d7413e00adf940 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Sat, 23 Sep 2023 00:45:27 +0200 Subject: [PATCH 05/20] Convert --strict-dml12 to three deprecation tags --- lib/1.2/dml-builtins.dml | 3 ++ lib/1.4/dml-builtins.dml | 3 ++ py/dml/codegen.py | 39 ++++++++++++++---------- py/dml/crep.py | 8 +++-- py/dml/ctree.py | 9 ++++-- py/dml/deprecations.py | 64 ++++++++++++++++++++++++++++++++++++++++ py/dml/dmlc.py | 19 +++++++----- py/dml/globals.py | 3 -- py/dml/structure.py | 21 ++++++++----- py/dml/template.py | 5 +++- py/dml/toplevel.py | 8 +++-- py/dml/types.py | 8 +++-- 12 files changed, 143 insertions(+), 47 deletions(-) diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index c53fd1c9f..6e2c391c6 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -208,6 +208,9 @@ template device { parameter _deprecate_port_proxy_ifaces auto; parameter _deprecate_port_proxy_attrs auto; + parameter _compat_dml12_inline auto; + parameter _compat_dml12_not auto; + parameter _compat_dml12_misc auto; // automatic parameters parameter obj auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index 6676df126..eb1ef2fa7 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -545,6 +545,9 @@ template device { param _deprecate_port_proxy_ifaces auto; param _deprecate_port_proxy_attrs auto; + param _compat_dml12_inline auto; + param _compat_dml12_not auto; + param _compat_dml12_misc auto; // automatic parameters param obj auto; diff --git a/py/dml/codegen.py b/py/dml/codegen.py index 5674999f0..861e3f008 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -11,7 +11,7 @@ import math from . import objects, crep, ctree, ast, int_register, logging, serialize -from . import dmlparse, output +from . import dmlparse, output, deprecations from .logging import * from .expr import * from .ctree import * @@ -1079,7 +1079,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 (deprecations.dml12_misc not in dml.globals.enabled_deprecations 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 +1120,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 deprecations.dml12_not not in dml.globals.enabled_deprecations: 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 +1142,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 (deprecations.dml12_misc in dml.globals.enabled_deprecations + 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 +1319,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 (deprecations.dml12_misc not in dml.globals.enabled_deprecations 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,7 +1525,7 @@ 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 + if (deprecations.dml12_misc in dml.globals.enabled_deprecations and not isinstance(expr, ctree.LValue) # for compatibility with dml-builtins, using 1.2 and not isinstance(expr, ctree.RegisterWithFields)): @@ -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 deprecations.dml12_misc in dml.globals.enabled_deprecations): 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 deprecations.dml12_misc in dml.globals.enabled_deprecations: 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 (deprecations.dml12_misc not in dml.globals.enabled_deprecations + and isinstance(lst.ctype(), TVector)): itervar = lookup_var(stmt.site, scope, itername) if not itervar: raise EIDENT(stmt.site, itername) @@ -3315,14 +3318,16 @@ 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 ( + deprecations.dml12_inline in dml.globals.enabled_deprecations + 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) + intypes = tuple(arg if ((ptype is None or deprecations.dml12_inline + not in dml.globals.enabled_deprecations) and (arg.constant or undefined(arg))) else methfunc_param(ptype, arg) for ((pname, ptype), arg) @@ -3547,7 +3552,8 @@ 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): + or deprecations.dml12_inline + not in dml.globals.enabled_deprecations): # 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 ( + deprecations.dml12_misc in dml.globals.enabled_deprecations): 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 deprecations.dml12_misc not in dml.globals.enabled_deprecations: # For backward compatibility. See bug 21367. inargs = [mkCast(site, arg, TPtr(TNamed('char'))) if isinstance(arg, StringConstant) else arg diff --git a/py/dml/crep.py b/py/dml/crep.py index 4d3a26dd8..f178b0bc0 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 deprecations __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 ( + deprecations.dml12_misc in dml.globals.enabled_deprecations): 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 deprecations.dml12_misc not in dml.globals.enabled_deprecations: 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 deprecations.dml12_misc not in dml.globals.enabled_deprecations: return name else: return '_dml_loggroup_' + name diff --git a/py/dml/ctree.py b/py/dml/ctree.py index 5d9fad091..7700822b0 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 deprecations import dml.globals # set from codegen.py codegen_call_expr = None @@ -2455,7 +2456,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 (deprecations.dml12_misc in dml.globals.enabled_deprecations + and not isinstance(rh, LValue)): raise ERVAL(rh.site, '&') return AddressOf(site, rh) @@ -2509,7 +2511,7 @@ class Not(UnaryOp): @staticmethod def make_simple(site, rh): - if not dml.globals.compat_dml12: + if deprecations.dml12_misc in dml.globals.enabled_deprecations: rh = as_bool(rh) if rh.constant: return mkBoolConstant(site, not rh.value) @@ -3788,7 +3790,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 (deprecations.dml12_misc not in dml.globals.enabled_deprecations + 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/deprecations.py b/py/dml/deprecations.py index 86d51aeab..af1a2fbef 100644 --- a/py/dml/deprecations.py +++ b/py/dml/deprecations.py @@ -61,3 +61,67 @@ class port_proxy_attrs(DeprecatedFeature): short = ("Don't generate top-level proxy attributes" + " for attributes in banks and ports") last_api_version = "6" + + +@deprecation +class dml12_inline(DeprecatedFeature): + '''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 = "6" + + +# separate class only because last_api_version differs +@deprecation +class dml12_not(DeprecatedFeature): + '''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 = "5" + + +@deprecation +class dml12_misc(DeprecatedFeature): + '''A number of minor language quirks in DML 1.2 were cleaned up in + DML 1.4; enabling this deprecation switches to the DML 1.4 + semantics for these. This includes: + + * `sizeof(typename)` is permitted with a warning message (`WSIZEOFTYPE`) + + * the `typeof` operator on an expression that isn't an lvalue + + * the `goto` statement + + * `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 = "6" diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index 8b776a716..2ef552911 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -404,12 +404,11 @@ def main(argv): #
Report errors for some constructs that will be forbidden in # future versions of the DML language
parser.add_argument('--strict-dml12', action='store_true', - help='Report errors for some constructs that will be' - + ' forbidden in future versions of the DML language') + help='Alias for --strict-int' + ' --deprecate=dml12_inline,dml12_not,dml12_misc') parser.add_argument('--strict-int', action='store_true', - help='Use DML 1.4 style integer arithmetic semantics' - + ' when compiling DML 1.2 files. Implied by' - + ' --strict-dml12.') + help='Use DML 1.4 style integer arithmetic semantics' + + ' when compiling DML 1.2 files.') #
--coverity
#
Adds Synopsys® Coverity® analysis annotations to suppress common @@ -589,8 +588,8 @@ def main(argv): dml.globals.api_version = options.simics_api for api in api_versions()[api_versions().index(options.simics_api):]: - features = {tag for (tag, dep) in deprecations.deprecations.items() - if dep.last_api_version in apis} + features = {tag for (tag, dep) in deprecations.deprecations[api].items() + if dep.last_api_version in api_versions()} for flag in options.deprecate: for tag in flag.split(','): if any(tag in tags for tags in deprecations.deprecations.values()): @@ -598,6 +597,10 @@ def main(argv): else: options.error(f'invalid tag {tag} for --deprecate.' ' Try --help-deprecate.') + if options.strict_dml12: + features.discard('dml12_include') + features.discard('dml12_not') + features.discard('dml12_misc') dml.globals.enabled_deprecations = { dep for deps in deprecations.deprecations.values() for (tag, dep) in deps.items() @@ -644,7 +647,7 @@ def main(argv): dml.globals.serialized_traits = serialize.SerializedTraits() (dml_version, devname, headers, footers, global_defs, top_tpl, imported) = toplevel.parse_main_file( - inputfilename, options.import_path, options.strict_dml12) + inputfilename, options.import_path) logtime("parsing") if dml_version != (1, 2): diff --git a/py/dml/globals.py b/py/dml/globals.py index ab7cc6110..b3569a333 100644 --- a/py/dml/globals.py +++ b/py/dml/globals.py @@ -66,9 +66,6 @@ def compat_dml12_int(site): # if site is None, guess DML 1.4 return not strict_int_flag and site and site.dml_version() == (1, 2) -# True if compiling DML 1.2 without --strict. Set after parsing. -compat_dml12 = None - debuggable = False coverity = False diff --git a/py/dml/structure.py b/py/dml/structure.py index 7f9151465..f5cc9ae74 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -82,7 +82,8 @@ def mkglobals(stmts): for name in by_name: clash = by_name[name] - if len(clash) > 1 and dml.globals.compat_dml12: + if len(clash) > 1 and (deprecations.dml12_misc + not in dml.globals.enabled_deprecations): # DML 1.2 permits multiple redundant 'extern foo;' # declarations; drop these for stmt in redundant_externs(clash): @@ -155,7 +156,8 @@ def mkglobals(stmts): if typ is None: # guaranteed by grammar assert dml.globals.dml_version == (1, 2) - if (not dml.globals.compat_dml12 + if (deprecations.dml12_misc + in dml.globals.enabled_deprecations and not site.filename().endswith('simics-api.dml')): report(EEXTERN(stmt.site)) typ = TUnknown() @@ -500,7 +502,7 @@ def add_templates(obj_specs, each_stmts): while i < len(queue): (site, tpl) = queue[i] i += 1 - if (dml.globals.compat_dml12 + if (deprecations.dml12_misc not in dml.globals.enabled_deprecations and tpl.name in dml.globals.missing_templates): report(ENTMPL(site, tpl.name)) continue @@ -942,7 +944,8 @@ def create_object(site, ident, objtype, parent, assert not arraylen_asts return objects.Device(ident, site) elif objtype == 'bank': - if ident is None and not dml.globals.compat_dml12: + if (ident is None + and deprecations.dml12_misc in dml.globals.enabled_deprecations): report(ESYNTAX(site, 'bank', 'anonymous banks are not allowed')) return objects.Bank(ident, site, parent, array_lens, index_vars) elif objtype == 'group': @@ -1031,9 +1034,11 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['NULL'] = NullParamExpr(site) autoparams['simics_api_version'] = SimpleParamExpr( mkStringConstant(site, dml.globals.api_version)) - for (tag, dep) in deprecations.deprecations.items(): - autoparams[f'_deprecate_{tag}'] = SimpleParamExpr( - mkBoolConstant(site, dep in dml.globals.enabled_deprecations)) + for deps in deprecations.deprecations.values(): + for (tag, dep) in deps.items(): + autoparams[f'_deprecate_{tag}'] = SimpleParamExpr( + mkBoolConstant( + site, dep in dml.globals.enabled_deprecations)) dml.globals.device = obj elif obj.objtype == 'bank': @@ -1898,7 +1903,7 @@ def mkobj2(obj, obj_specs, params, each_stmts): else: report(e) if (dml.globals.dml_version != (1, 2) - or not dml.globals.compat_dml12): + or deprecations.dml12_misc in dml.globals.enabled_deprecations): # TODO: this should be handled cleaner in the case of pure # 1.4 code for p in obj.get_components(): diff --git a/py/dml/template.py b/py/dml/template.py index 6f291c145..e3427ccc3 100644 --- a/py/dml/template.py +++ b/py/dml/template.py @@ -5,6 +5,7 @@ import os from . import ast, logging +from . import deprecations from .logging import * from .messages import * from .set import Set @@ -305,7 +306,9 @@ def process_templates(template_decls): # fallback: add missing templates and retry for missing in all_missing: site = references[missing].site - if missing not in uncond_refs or dml.globals.compat_dml12: + if (missing not in uncond_refs + or (deprecations.dml12_misc + not in dml.globals.enabled_deprecations)): # delay error until template instantiation dml.globals.missing_templates.add(missing) else: diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index e5b180406..6c2f93045 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -14,6 +14,7 @@ from ply import lex, yacc from . import objects, logging, codegen, ctree, ast +from . import deprecations from . import symtab from .messages import * from .logging import * @@ -347,7 +348,7 @@ def exists(filename): return True return os.path.exists(filename) -def parse_main_file(inputfilename, explicit_import_path, strict): +def parse_main_file(inputfilename, explicit_import_path): if not exists(inputfilename): raise ENOFILE(SimpleSite(f"{inputfilename}:0")) (kind, site, name, stmts) = parse_dmlast_or_dml(inputfilename) @@ -357,7 +358,10 @@ def parse_main_file(inputfilename, explicit_import_path, strict): version = site.dml_version() dml.globals.dml_version = version version_str = fmt_version(version) - dml.globals.compat_dml12 = version == (1, 2) and not strict + if version != (1, 2): + dml.globals.enabled_deprecations.update([ + deprecations.dml12_inline, deprecations.dml12_not, + deprecations.dml12_misc]) implicit_imports = [ ast.import_(site, "dml-builtins.dml"), diff --git a/py/dml/types.py b/py/dml/types.py index 481b751e2..c6bd2f147 100644 --- a/py/dml/types.py +++ b/py/dml/types.py @@ -56,6 +56,7 @@ from .output import out from .messages import * from .logging import * +from . import deprecations import dml .globals import abc @@ -552,7 +553,8 @@ def canstore(self, other): other = realtype(other) if other.is_int: trunc = (other.bits > self.bits) - if dml.globals.compat_dml12 and isinstance(other, TBool): + if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + and isinstance(other, TBool)): return (False, False, constviol) return (True, trunc, constviol) if other.is_float and not self.is_bitfields: @@ -737,7 +739,7 @@ def sizeof(self): return None return self.size.value * elt_size def cmp(self, other): - if dml.globals.compat_dml12: + if deprecations.dml12_misc not in dml.globals.enabled_deprecations: if isinstance(other, (TArray, TPtr)): return self.base.cmp(other.base) elif isinstance(other, (TPtr, TArray)): @@ -768,7 +770,7 @@ def key(self): def describe(self): return 'pointer to %s' % (self.base.describe()) def cmp(self, other): - if dml.globals.compat_dml12: + if deprecations.dml12_misc not in dml.globals.enabled_deprecations: if isinstance(other, TPtr): # Can only compare for voidness or equality if self.base.void or other.base.void: From 2a90ee1f668a6045afbe0ff475859ec3fca9fa9b Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Sat, 23 Sep 2023 22:51:28 +0200 Subject: [PATCH 06/20] Use integers to represent API versions This is nice because it allows comparisons like "simics_api < 7" to work also in Simics 10. We use the integer 4 to represent the API "4.8" --- deprecations_to_md.py | 6 ++++-- generate_env.py | 6 +++--- py/dml/c_backend.py | 20 +++++++++----------- py/dml/deprecations.py | 12 ++++++------ py/dml/dmlc.py | 16 +++++++++------- py/dml/structure.py | 6 ++++-- py/dml/toplevel.py | 4 ++-- py/dml/types.py | 4 ++-- 8 files changed, 39 insertions(+), 35 deletions(-) diff --git a/deprecations_to_md.py b/deprecations_to_md.py index 02e072e4a..14be739b7 100644 --- a/deprecations_to_md.py +++ b/deprecations_to_md.py @@ -9,11 +9,13 @@ from dml.deprecations import deprecations from dml.env import api_versions +api_map = {v: k for (k, v) in api_versions().items()} with open(outfile, 'w') as f: f.write(Path(header).read_text()) for (ver, deps) in deprecations.items(): - if deps and ver in api_versions(): - f.write(f"### Features available up to --simics-api={ver}\n") + if deps and ver in api_map: + f.write( + f"### Features available up to --simics-api={api_map[ver]}\n") f.write("
\n") for d in deps.values(): assert d.__doc__ diff --git a/generate_env.py b/generate_env.py index 417b3dfed..68303baa0 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 @@ -9,13 +8,14 @@ from simicsutils.internal import api_versions, default_api_version def generate_env(out): + api_versions_dict = {v: 4 if v == '4.8' else int(v) for v in api_versions()} Path(out).write_text(f'''\ def is_windows(): return {is_windows()} def api_versions(): - return {api_versions()} + return {api_versions_dict} def default_api_version(): - return {repr(default_api_version())} + return {api_versions_dict[default_api_version()]} ''') if __name__ == '__main__': diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 5970ea976..3c8ed1cd7 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1546,7 +1546,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 <= 6: start_function_definition( ('void %s_pre_del_notify(conf_object_t *_subscriber,' ' conf_object_t *_notifier, lang_void *_data)') % crep.cname(device)) @@ -1591,7 +1591,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 <= 6: out('SIM_add_notifier(_obj, Sim_Notify_Object_Delete, _obj, ' + crep.cname(device) + '_pre_del_notify, NULL);\n') @@ -1911,7 +1911,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 <= 4: return 'initialize_' + crep.cname(device) return '_initialize_' + ''.join( (ch if ch in ident_chars else '_') for ch in outprefix) @@ -1938,7 +1938,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 >= 7: out('.deinit = '+crep.cname(device)+'_deinit,\n') out('.dealloc = '+crep.cname(device)+'_dealloc,\n') out('.description = '+doc.read()+',\n') @@ -3230,12 +3230,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 == 4 + else dml.globals.api_version) + api_define = '#ifndef %s\n#define %s\n#endif\n' % ( + sym, sym) c_top = '\n'.join([ '/*', @@ -3418,7 +3416,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 == 4 out('\n') out('EXPORTED void\n') out('init_local(void)\n') diff --git a/py/dml/deprecations.py b/py/dml/deprecations.py index af1a2fbef..63eed634e 100644 --- a/py/dml/deprecations.py +++ b/py/dml/deprecations.py @@ -22,7 +22,7 @@ def last_api_version(self): pass # API version -> tag -> deprecation deprecations: dict[str, dict[str, DeprecatedFeature]] = { - api: {} for api in env.api_versions()} + api: {} for api in env.api_versions().values()} def deprecation(cls: type[DeprecatedFeature]): @@ -45,7 +45,7 @@ class port_proxy_ifaces(DeprecatedFeature): banks do not need proxies for backward compatibility. ''' short = "Don't generate proxy port interfaces for banks and ports" - last_api_version = "6" + last_api_version = 6 @deprecation @@ -60,7 +60,7 @@ class port_proxy_attrs(DeprecatedFeature): ''' short = ("Don't generate top-level proxy attributes" + " for attributes in banks and ports") - last_api_version = "6" + last_api_version = 6 @deprecation @@ -72,7 +72,7 @@ class dml12_inline(DeprecatedFeature): implications. ''' short = "Don't inline method arguments with a declared type in DML 1.2" - last_api_version = "6" + last_api_version = 6 # separate class only because last_api_version differs @@ -82,7 +82,7 @@ class dml12_not(DeprecatedFeature): 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 = "5" + last_api_version = 5 @deprecation @@ -124,4 +124,4 @@ class dml12_misc(DeprecatedFeature): ''' short = "Disable various DML 1.2 quirks" - last_api_version = "6" + last_api_version = 6 diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index 2ef552911..84efd1f16 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -437,7 +437,7 @@ def main(argv): parser.add_argument( '--simics-api', action='store', metavar='VERSION', - default=default_api_version(), + default=str(default_api_version()), help=('specify Simics API version (default %s)' % default_api_version())) @@ -519,7 +519,8 @@ def main(argv): else: defs[name] = value - if options.simics_api not in api_versions(): + api_map = api_versions() + if options.simics_api not in api_map: prerr("dmlc: the version '%s' is not a valid API version" % ( options.simics_api)) sys.exit(1) @@ -585,15 +586,16 @@ def main(argv): + "The DMLC developers WILL NOT respect their use. " + "NEVER enable this flag for any kind of production code!!!***") - dml.globals.api_version = options.simics_api + dml.globals.api_version = api_map[options.simics_api] - for api in api_versions()[api_versions().index(options.simics_api):]: - features = {tag for (tag, dep) in deprecations.deprecations[api].items() - if dep.last_api_version in api_versions()} + features = {tag: dep for api in api_map.values() + if api <= dml.globals.api_version + for (tag, dep) in deprecations.deprecations[api].items()} for flag in options.deprecate: for tag in flag.split(','): if any(tag in tags for tags in deprecations.deprecations.values()): - features.discard(tag) + if tag in features: + del features[tag] else: options.error(f'invalid tag {tag} for --deprecate.' ' Try --help-deprecate.') diff --git a/py/dml/structure.py b/py/dml/structure.py index f5cc9ae74..a8e49982e 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -16,6 +16,7 @@ from . import topsort from . import slotsmeta from . import ctree +from . import env from . import serialize from .logging import * from .codegen import * @@ -1032,8 +1033,9 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['banks'] = UninitializedParamExpr(site, 'banks') else: autoparams['NULL'] = NullParamExpr(site) + api_map = {i: s for (s, i) in env.api_versions().items()} autoparams['simics_api_version'] = SimpleParamExpr( - mkStringConstant(site, dml.globals.api_version)) + mkStringConstant(site, api_map[dml.globals.api_version])) for deps in deprecations.deprecations.values(): for (tag, dep) in deps.items(): autoparams[f'_deprecate_{tag}'] = SimpleParamExpr( @@ -1883,7 +1885,7 @@ def mkobj2(obj, obj_specs, params, each_stmts): param.get_expr(zero_index * param.dimensions) except DMLError as e: if (dml.globals.dml_version == (1, 2) - and dml.globals.api_version <= '5' + and dml.globals.api_version <= 5 and isinstance(e, EREF)): # We forgive some errors in unused parameters, to # avoid the annoyance caused by hard errors from code diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 6c2f93045..0a2270429 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -383,10 +383,10 @@ def parse_main_file(inputfilename, explicit_import_path): for path in [os.path.join(orig_path, version_str), orig_path]] - if version == (1, 2) and dml.globals.api_version not in ( + if version == (1, 2) and dml.globals.api_version not in { # we may want to add "8" if we want to postpone the # deprecation of DML 1.2 - "4.8", "5", "6", "7", "internal"): + 4, 5, 6, 7}: raise ESIMAPI(site, fmt_version(version), dml.globals.api_version) diff --git a/py/dml/types.py b/py/dml/types.py index c6bd2f147..5d2a3ede2 100644 --- a/py/dml/types.py +++ b/py/dml/types.py @@ -1233,9 +1233,9 @@ def parse_type(typename): return TBool() elif typename == 'void': return TVoid() - elif typename == 'integer_t' and dml.globals.api_version < '7': + elif typename == 'integer_t' and dml.globals.api_version < 7: return TInt(64, True) - elif typename == 'uinteger_t' and dml.globals.api_version < '7': + elif typename == 'uinteger_t' and dml.globals.api_version < 7: return TInt(64, False) else: return TNamed(typename) From 7ff5957859d41120719c4f36e63217030c56e42a Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Sun, 24 Sep 2023 14:39:36 +0200 Subject: [PATCH 07/20] Change --deprecate to --no-compat This also included some renames and change in polarities; in particular, all deprecated features are now disabled by default, as required by some unit tests. --- MODULEINFO | 2 +- Makefile | 1 + deprecations_to_md.py | 4 +- doc/1.4/deprecations-header.md | 8 ++- lib/1.2/dml-builtins.dml | 4 +- lib/1.4/dml-builtins.dml | 4 +- py/dml/c_backend.py | 5 +- py/dml/codegen.py | 42 +++++++------- py/dml/{deprecations.py => compat.py} | 44 +++++++-------- py/dml/crep.py | 8 +-- py/dml/ctree.py | 8 +-- py/dml/dmlc.py | 55 ++++++++++--------- py/dml/globals.py | 2 +- py/dml/structure.py | 23 ++++---- py/dml/template.py | 5 +- py/dml/toplevel.py | 8 +-- py/dml/types.py | 8 +-- .../legacy/T_port_proxy_attrs_disabled.dml | 4 +- .../1.4/legacy/T_port_proxy_attrs_enabled.dml | 2 +- .../legacy/T_port_proxy_ifaces_disabled.dml | 4 +- .../legacy/T_port_proxy_ifaces_enabled.dml | 2 +- test/1.4/legacy/port_proxy_attrs.dml | 6 +- test/1.4/legacy/port_proxy_ifaces.dml | 6 +- 23 files changed, 127 insertions(+), 128 deletions(-) rename py/dml/{deprecations.py => compat.py} (77%) diff --git a/MODULEINFO b/MODULEINFO index b824b208f..ef8c00bab 100644 --- a/MODULEINFO +++ b/MODULEINFO @@ -43,9 +43,9 @@ 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/deprecations.py $(HOST)/bin/dml/python/dml/dmllex.py $(HOST)/bin/dml/python/dml/dmllex12.py $(HOST)/bin/dml/python/dml/dmllex14.py diff --git a/Makefile b/Makefile index 3cd9f41be..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 \ diff --git a/deprecations_to_md.py b/deprecations_to_md.py index 14be739b7..cfcaa08f0 100644 --- a/deprecations_to_md.py +++ b/deprecations_to_md.py @@ -6,13 +6,13 @@ [path_to_dml, header, outfile] = sys.argv[1:] sys.path.append(path_to_dml) -from dml.deprecations import deprecations +from dml.compat import features from dml.env import api_versions api_map = {v: k for (k, v) in api_versions().items()} with open(outfile, 'w') as f: f.write(Path(header).read_text()) - for (ver, deps) in deprecations.items(): + for (ver, deps) in features.items(): if deps and ver in api_map: f.write( f"### Features available up to --simics-api={api_map[ver]}\n") diff --git a/doc/1.4/deprecations-header.md b/doc/1.4/deprecations-header.md index 1fed1475c..391ef5bf0 100644 --- a/doc/1.4/deprecations-header.md +++ b/doc/1.4/deprecations-header.md @@ -59,12 +59,14 @@ some uses, in particular: ## Controlling deprecation on the DML command-line DMLC provides a command-line flag `--api-version` to specify the API version to -be used for a model. When building with the standard Simics build system, this is controlled by the `SIMICS_API_VERSION` variable in `make`, or the `SIMICS_API`/`MODULE_SIMICS_API` variable in `CMake`. +be used for a model. When building with the standard Simics build system, this +is controlled by the `SIMICS_API_VERSION` variable in `make`, or the +`SIMICS_API`/`MODULE_SIMICS_API` variable in `CMake`. -DMLC also provides the --deprecate=_tag_ flag, which disables the +DMLC also provides the --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 `_deprecate_`. +name is the tag name preceded by `_compat_`. ## List of deprecated features diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index 6e2c391c6..c653f3f03 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -206,8 +206,8 @@ template device { parameter simics_api_version auto; - parameter _deprecate_port_proxy_ifaces auto; - parameter _deprecate_port_proxy_attrs auto; + parameter _compat_port_proxy_ifaces auto; + parameter _compat_port_proxy_attrs auto; parameter _compat_dml12_inline auto; parameter _compat_dml12_not auto; parameter _compat_dml12_misc auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index eb1ef2fa7..d011d5e07 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -543,8 +543,8 @@ template device { || simics_api_version == "4.8"; param _api_6_or_older = simics_api_version == "6" || _api_5_or_older; - param _deprecate_port_proxy_ifaces auto; - param _deprecate_port_proxy_attrs auto; + param _compat_port_proxy_ifaces auto; + param _compat_port_proxy_attrs auto; param _compat_dml12_inline auto; param _compat_dml12_not auto; param _compat_dml12_misc auto; diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 3c8ed1cd7..8e65d11a4 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, deprecations +from . import traits, compat import dml.globals from .structure import get_attr_name, port_class_ident, need_port_proxy_attrs from .logging import * @@ -888,8 +888,7 @@ def generate_implement(code, device, impl): # descendants of the device object if (port.parent is dml.globals.device and port.objtype in {'port', 'bank'} - and (deprecations.port_proxy_ifaces - not in dml.globals.enabled_deprecations)): + 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'), diff --git a/py/dml/codegen.py b/py/dml/codegen.py index 861e3f008..16ce839f7 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -11,7 +11,7 @@ import math from . import objects, crep, ctree, ast, int_register, logging, serialize -from . import dmlparse, output, deprecations +from . import dmlparse, output, compat from .logging import * from .expr import * from .ctree import * @@ -1079,7 +1079,7 @@ def subast_has_dollar(expr_ast): @expression_dispatcher def expr_unop(tree, location, scope): [op, rh_ast] = tree.args - if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + 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 +1120,7 @@ def expr_unop(tree, location, scope): return ctree.AddressOfMethod(tree.site, func) raise rh.exc() if op == '!': - if deprecations.dml12_not not in dml.globals.enabled_deprecations: + 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 +1142,7 @@ 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 (deprecations.dml12_misc in dml.globals.enabled_deprecations + 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) @@ -1319,7 +1319,7 @@ def expr_cast(tree, location, scope): for (site, _) in struct_defs: report(EANONSTRUCT(site, "'cast' expression")) - if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + if (compat.dml12_misc in dml.globals.enabled_compat and isinstance(expr, InterfaceMethodRef)): # Workaround for SIMICS-9868 return mkLit(tree.site, "%s->%s" % ( @@ -1525,7 +1525,7 @@ 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 (deprecations.dml12_misc in dml.globals.enabled_deprecations + if (compat.dml12_misc not in dml.globals.enabled_compat and not isinstance(expr, ctree.LValue) # for compatibility with dml-builtins, using 1.2 and not isinstance(expr, ctree.RegisterWithFields)): @@ -1981,7 +1981,7 @@ def stmt_local(stmt, location, scope): def convert_decl(decl_ast): (name, asttype) = decl_ast.args if (dml.globals.dml_version == (1, 2) - and deprecations.dml12_misc in dml.globals.enabled_deprecations): + 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) @@ -2545,7 +2545,7 @@ def stmt_assert(stmt, location, scope): @statement_dispatcher def stmt_goto(stmt, location, scope): [label] = stmt.args - if deprecations.dml12_misc in dml.globals.enabled_deprecations: + if compat.dml12_misc not in dml.globals.enabled_compat: report(ESYNTAX(stmt.site, 'goto', 'goto statement not allowed')) return [mkGoto(stmt.site, label)] @@ -3045,7 +3045,7 @@ def stmt_select(stmt, location, scope): if_chain = mkIf(cond.site, cond, stmt, if_chain) return [if_chain] raise lst.exc() - elif (deprecations.dml12_misc not in dml.globals.enabled_deprecations + elif (compat.dml12_misc in dml.globals.enabled_compat and isinstance(lst.ctype(), TVector)): itervar = lookup_var(stmt.site, scope, itername) if not itervar: @@ -3319,19 +3319,19 @@ def common_inline(site, method, indices, inargs, outargs): if dml.globals.debuggable: if method.fully_typed and ( - deprecations.dml12_inline in dml.globals.enabled_deprecations + 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 deprecations.dml12_inline - not in dml.globals.enabled_deprecations) - 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)) @@ -3551,9 +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 deprecations.dml12_inline - not in dml.globals.enabled_deprecations): + 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 @@ -3768,7 +3768,7 @@ def codegen_method_func(func): inline_scope = MethodParamScope(global_scope) for (name, e) in func.inp: if dml.globals.dml_version == (1, 2) and ( - deprecations.dml12_misc in dml.globals.enabled_deprecations): + compat.dml12_misc not in dml.globals.enabled_compat): check_varname(method.site, name) if isinstance(e, Expression): inlined_arg = ( @@ -3976,7 +3976,7 @@ def codegen_call(site, meth_node, indices, inargs, outargs): require_fully_typed(site, meth_node) func = method_instance(meth_node) - if deprecations.dml12_misc not in dml.globals.enabled_deprecations: + 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/deprecations.py b/py/dml/compat.py similarity index 77% rename from py/dml/deprecations.py rename to py/dml/compat.py index 63eed634e..32837e832 100644 --- a/py/dml/deprecations.py +++ b/py/dml/compat.py @@ -6,7 +6,7 @@ from . import env -class DeprecatedFeature(abc.ABC): +class CompatFeature(abc.ABC): def tag(self): return self.__class__.__name__ @@ -20,20 +20,20 @@ def short(self): pass def last_api_version(self): pass -# API version -> tag -> deprecation -deprecations: dict[str, dict[str, DeprecatedFeature]] = { +# API version -> tag -> feature +features: dict[str, dict[str, CompatFeature]] = { api: {} for api in env.api_versions().values()} -def deprecation(cls: type[DeprecatedFeature]): - assert issubclass(cls, DeprecatedFeature) +def feature(cls: type[CompatFeature]): + assert issubclass(cls, CompatFeature) singleton = cls() - deprecations[cls.last_api_version][singleton.tag()] = singleton + features[cls.last_api_version][singleton.tag()] = singleton return singleton -@deprecation -class port_proxy_ifaces(DeprecatedFeature): +@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 @@ -48,23 +48,23 @@ class port_proxy_ifaces(DeprecatedFeature): last_api_version = 6 -@deprecation -class port_proxy_attrs(DeprecatedFeature): +@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 deprecation is not enabled. + 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` deprecation. + 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 = 6 -@deprecation -class dml12_inline(DeprecatedFeature): +@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 @@ -76,8 +76,8 @@ class dml12_inline(DeprecatedFeature): # separate class only because last_api_version differs -@deprecation -class dml12_not(DeprecatedFeature): +@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''' @@ -85,13 +85,13 @@ class dml12_not(DeprecatedFeature): last_api_version = 5 -@deprecation -class dml12_misc(DeprecatedFeature): - '''A number of minor language quirks in DML 1.2 were cleaned up in - DML 1.4; enabling this deprecation switches to the DML 1.4 - semantics for these. This includes: +@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)` is permitted with a warning message (`WSIZEOFTYPE`) + * `sizeof(typename)` (see `WSIZEOFTYPE`) * the `typeof` operator on an expression that isn't an lvalue diff --git a/py/dml/crep.py b/py/dml/crep.py index f178b0bc0..8214c8dff 100644 --- a/py/dml/crep.py +++ b/py/dml/crep.py @@ -10,7 +10,7 @@ from .logging import * from .expr_util import * from .messages import * -from . import deprecations +from . import compat __all__ = ( 'cname', @@ -75,7 +75,7 @@ def cname(node): # this is weird... kept for compatibility name = param_str(node, 'c_name').replace('-', '_') if name != node.name and ( - deprecations.dml12_misc in dml.globals.enabled_deprecations): + compat.dml12_misc not in dml.globals.enabled_compat): report(WDEPRECATED(param_expr_site(node, 'c_name'), 'parameter c_name')) return name @@ -178,7 +178,7 @@ def node_storage_type_dml12(node, site): else: return None elif node.objtype == 'implement': - if deprecations.dml12_misc not in dml.globals.enabled_deprecations: + if compat.dml12_misc in dml.globals.enabled_compat: typename = param_str(node, 'c_type') t = TNamed(typename) t.declaration_site = node.site @@ -228,7 +228,7 @@ def conf_object(site, node, indices): cref_portobj(node, indices[:node.dimensions])) def cloggroup(name): - if deprecations.dml12_misc not in dml.globals.enabled_deprecations: + 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 7700822b0..7b4fd635a 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -22,7 +22,7 @@ from .expr_util import * from .slotsmeta import auto_init from . import dmlparse, output -from . import deprecations +from . import compat import dml.globals # set from codegen.py codegen_call_expr = None @@ -2456,7 +2456,7 @@ def make_simple(cls, site, rh): TFunction([TPtr(TNamed('conf_object_t')), TPtr(TVoid())], TVoid()))) - if (deprecations.dml12_misc in dml.globals.enabled_deprecations + if (compat.dml12_misc not in dml.globals.enabled_compat and not isinstance(rh, LValue)): raise ERVAL(rh.site, '&') return AddressOf(site, rh) @@ -2511,7 +2511,7 @@ class Not(UnaryOp): @staticmethod def make_simple(site, rh): - if deprecations.dml12_misc in dml.globals.enabled_deprecations: + if compat.dml12_misc not in dml.globals.enabled_compat: rh = as_bool(rh) if rh.constant: return mkBoolConstant(site, not rh.value) @@ -3790,7 +3790,7 @@ def lookup_component(site, base, indices, name, only_local): indices = indices[:-base.local_dimensions()] return lookup_component(site, base.parent, indices, name, False) - if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + 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'): diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index 84efd1f16..da08a9359 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -12,7 +12,7 @@ from . import serialize from . import dmlparse from . import output -from . import deprecations +from . import compat import dml.c_backend import dml.info_backend @@ -283,17 +283,19 @@ def print_help(self): for tag in by_ignored[True]: print(f' {tag}') -class DeprecateHelpAction(HelpAction): +class CompatHelpAction(HelpAction): def print_help(self): print('''\ -Tags accepted by --deprecate. Each of these represents a deprecated +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 --deprecate=TAG flag disables a +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 early access -to deprecations in a yet unreleased API version.''') - for (version, deps) in deprecations.deprecations.items(): - print(f' Features available with --simics-api={version} or older:') +to a new API version in smaller steps, and can also allow disabling +features that are scheduled for removal in a future API version.''') + ver_map = {i: s for (s, i) in api_versions().items()} + for (version, deps) in compat.features.items(): + print(f' Features available with --simics-api={ver_map[version]}' + ' or older:') for (tag, dep) in deps.items(): print(f' {tag:20s} {dep.short}') @@ -405,7 +407,7 @@ def main(argv): # future versions of the DML language
parser.add_argument('--strict-dml12', action='store_true', help='Alias for --strict-int' - ' --deprecate=dml12_inline,dml12_not,dml12_misc') + ' --no-compat=dml12_inline,dml12_not,dml12_misc') parser.add_argument('--strict-int', action='store_true', help='Use DML 1.4 style integer arithmetic semantics' + ' when compiling DML 1.2 files.') @@ -452,15 +454,15 @@ def main(argv): # # - #
--deprecate=TAG
- #
+ #
--no-compat=TAG
+ #
Disable a compatibility feature
parser.add_argument( - '--deprecate', action='append', default=[], + '--no-compat', action='append', default=[], help='Disable a compatibility feature') parser.add_argument( - '--help-deprecate', action=DeprecateHelpAction, - help='List the available tags for --deprecate') + '--help-no-compat', action=CompatHelpAction, + help='List the available tags for --no-compat') # # @@ -589,24 +591,23 @@ def main(argv): dml.globals.api_version = api_map[options.simics_api] features = {tag: dep for api in api_map.values() - if api <= dml.globals.api_version - for (tag, dep) in deprecations.deprecations[api].items()} - for flag in options.deprecate: + if api >= dml.globals.api_version + for (tag, dep) in compat.features[api].items()} + for flag in options.no_compat: for tag in flag.split(','): - if any(tag in tags for tags in deprecations.deprecations.values()): + if any(tag in tags for tags in compat.features.values()): if tag in features: del features[tag] else: - options.error(f'invalid tag {tag} for --deprecate.' - ' Try --help-deprecate.') + options.error(f'invalid tag {tag} for --no-compat.' + ' Try --help-no-compat.') if options.strict_dml12: - features.discard('dml12_include') - features.discard('dml12_not') - features.discard('dml12_misc') - dml.globals.enabled_deprecations = { - dep for deps in deprecations.deprecations.values() - for (tag, dep) in deps.items() - if tag not in features} + for feature in [compat.dml12_inline, compat.dml12_not, + compat.dml12_misc]: + tag = feature.tag() + if tag in features: + del features[tag] + dml.globals.enabled_compat = set(features.values()) inputfilename = options.input_filename diff --git a/py/dml/globals.py b/py/dml/globals.py index b3569a333..1105f3fe1 100644 --- a/py/dml/globals.py +++ b/py/dml/globals.py @@ -58,7 +58,7 @@ # types.TypeSequence -> codegen.TypeSequenceInfo type_sequence_infos = {} -enabled_deprecations = set() +enabled_compat = set() # 1.4 style integer operations in 1.2, --strict-dml12-int strict_int_flag = None diff --git a/py/dml/structure.py b/py/dml/structure.py index a8e49982e..e2ce7890d 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -32,7 +32,7 @@ from .reginfo import explode_registers from . import dmlparse from .set import Set -from . import deprecations +from . import compat __all__ = ( 'mkglobals', 'mkdev' @@ -83,8 +83,7 @@ def mkglobals(stmts): for name in by_name: clash = by_name[name] - if len(clash) > 1 and (deprecations.dml12_misc - not in dml.globals.enabled_deprecations): + if len(clash) > 1 and compat.dml12_misc in dml.globals.enabled_compat: # DML 1.2 permits multiple redundant 'extern foo;' # declarations; drop these for stmt in redundant_externs(clash): @@ -157,8 +156,7 @@ def mkglobals(stmts): if typ is None: # guaranteed by grammar assert dml.globals.dml_version == (1, 2) - if (deprecations.dml12_misc - in dml.globals.enabled_deprecations + if (compat.dml12_misc not in dml.globals.enabled_compat and not site.filename().endswith('simics-api.dml')): report(EEXTERN(stmt.site)) typ = TUnknown() @@ -503,7 +501,7 @@ def add_templates(obj_specs, each_stmts): while i < len(queue): (site, tpl) = queue[i] i += 1 - if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + if (compat.dml12_misc in dml.globals.enabled_compat and tpl.name in dml.globals.missing_templates): report(ENTMPL(site, tpl.name)) continue @@ -946,7 +944,7 @@ def create_object(site, ident, objtype, parent, return objects.Device(ident, site) elif objtype == 'bank': if (ident is None - and deprecations.dml12_misc in dml.globals.enabled_deprecations): + and compat.dml12_misc not in dml.globals.enabled_compat): report(ESYNTAX(site, 'bank', 'anonymous banks are not allowed')) return objects.Bank(ident, site, parent, array_lens, index_vars) elif objtype == 'group': @@ -1036,11 +1034,11 @@ def make_autoparams(obj, index_vars, index_var_sites): api_map = {i: s for (s, i) in env.api_versions().items()} autoparams['simics_api_version'] = SimpleParamExpr( mkStringConstant(site, api_map[dml.globals.api_version])) - for deps in deprecations.deprecations.values(): + for deps in compat.features.values(): for (tag, dep) in deps.items(): - autoparams[f'_deprecate_{tag}'] = SimpleParamExpr( + autoparams[f'_compat_{tag}'] = SimpleParamExpr( mkBoolConstant( - site, dep in dml.globals.enabled_deprecations)) + site, dep in dml.globals.enabled_compat)) dml.globals.device = obj elif obj.objtype == 'bank': @@ -1905,7 +1903,7 @@ def mkobj2(obj, obj_specs, params, each_stmts): else: report(e) if (dml.globals.dml_version != (1, 2) - or deprecations.dml12_misc in dml.globals.enabled_deprecations): + or compat.dml12_misc not in dml.globals.enabled_compat): # TODO: this should be handled cleaner in the case of pure # 1.4 code for p in obj.get_components(): @@ -2593,8 +2591,7 @@ def need_port_proxy_attrs(port): return (port.objtype in {'port', 'bank'} and port.dimensions <= 1 and port.parent is dml.globals.device - and (deprecations.port_proxy_attrs - not in dml.globals.enabled_deprecations)) + and compat.port_proxy_attrs in dml.globals.enabled_compat) class ConfAttrParentObjectProxyInfoParamExpr(objects.ParamExpr): '''The _parent_obj_proxy_info parameter of a attribute, register, or diff --git a/py/dml/template.py b/py/dml/template.py index e3427ccc3..13b04a1c7 100644 --- a/py/dml/template.py +++ b/py/dml/template.py @@ -5,7 +5,7 @@ import os from . import ast, logging -from . import deprecations +from . import compat from .logging import * from .messages import * from .set import Set @@ -307,8 +307,7 @@ def process_templates(template_decls): for missing in all_missing: site = references[missing].site if (missing not in uncond_refs - or (deprecations.dml12_misc - not in dml.globals.enabled_deprecations)): + or compat.dml12_misc in dml.globals.enabled_compat): # delay error until template instantiation dml.globals.missing_templates.add(missing) else: diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 0a2270429..5bf9d829a 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -14,7 +14,7 @@ from ply import lex, yacc from . import objects, logging, codegen, ctree, ast -from . import deprecations +from . import compat from . import symtab from .messages import * from .logging import * @@ -359,9 +359,9 @@ def parse_main_file(inputfilename, explicit_import_path): dml.globals.dml_version = version version_str = fmt_version(version) if version != (1, 2): - dml.globals.enabled_deprecations.update([ - deprecations.dml12_inline, deprecations.dml12_not, - deprecations.dml12_misc]) + for feature in [compat.dml12_inline, compat.dml12_not, + compat.dml12_misc]: + dml.globals.enabled_compat.discard(feature) implicit_imports = [ ast.import_(site, "dml-builtins.dml"), diff --git a/py/dml/types.py b/py/dml/types.py index 5d2a3ede2..240b08dca 100644 --- a/py/dml/types.py +++ b/py/dml/types.py @@ -56,7 +56,7 @@ from .output import out from .messages import * from .logging import * -from . import deprecations +from . import compat import dml .globals import abc @@ -553,7 +553,7 @@ def canstore(self, other): other = realtype(other) if other.is_int: trunc = (other.bits > self.bits) - if (deprecations.dml12_misc not in dml.globals.enabled_deprecations + if (compat.dml12_misc in dml.globals.enabled_compat and isinstance(other, TBool)): return (False, False, constviol) return (True, trunc, constviol) @@ -739,7 +739,7 @@ def sizeof(self): return None return self.size.value * elt_size def cmp(self, other): - if deprecations.dml12_misc not in dml.globals.enabled_deprecations: + if compat.dml12_misc in dml.globals.enabled_compat: if isinstance(other, (TArray, TPtr)): return self.base.cmp(other.base) elif isinstance(other, (TPtr, TArray)): @@ -770,7 +770,7 @@ def key(self): def describe(self): return 'pointer to %s' % (self.base.describe()) def cmp(self, other): - if deprecations.dml12_misc not in dml.globals.enabled_deprecations: + if compat.dml12_misc in dml.globals.enabled_compat: if isinstance(other, TPtr): # Can only compare for voidness or equality if self.base.void or other.base.void: diff --git a/test/1.4/legacy/T_port_proxy_attrs_disabled.dml b/test/1.4/legacy/T_port_proxy_attrs_disabled.dml index cb7624b9c..ee9d4a4f9 100644 --- a/test/1.4/legacy/T_port_proxy_attrs_disabled.dml +++ b/test/1.4/legacy/T_port_proxy_attrs_disabled.dml @@ -7,10 +7,10 @@ dml 1.4; device test; /// DMLC-FLAG --simics-api=6 -/// DMLC-FLAG --deprecate=port_proxy_attrs +/// DMLC-FLAG --no-compat=port_proxy_attrs import "port_proxy_attrs.dml"; method init() { - test(true); + test(false); } diff --git a/test/1.4/legacy/T_port_proxy_attrs_enabled.dml b/test/1.4/legacy/T_port_proxy_attrs_enabled.dml index 6a95f751d..7dd7c2c14 100644 --- a/test/1.4/legacy/T_port_proxy_attrs_enabled.dml +++ b/test/1.4/legacy/T_port_proxy_attrs_enabled.dml @@ -11,5 +11,5 @@ device test; import "port_proxy_attrs.dml"; method init() { - test(false); + test(true); } diff --git a/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml b/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml index 3c2fac8e8..7691d5b88 100644 --- a/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml +++ b/test/1.4/legacy/T_port_proxy_ifaces_disabled.dml @@ -7,10 +7,10 @@ dml 1.4; device test; /// DMLC-FLAG --simics-api=6 -/// DMLC-FLAG --deprecate=port_proxy_ifaces +/// DMLC-FLAG --no-compat=port_proxy_ifaces import "port_proxy_ifaces.dml"; method init() { - test(true); + test(false); } diff --git a/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml b/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml index a2b9ff0e0..b7a04ec9e 100644 --- a/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml +++ b/test/1.4/legacy/T_port_proxy_ifaces_enabled.dml @@ -11,5 +11,5 @@ device test; import "port_proxy_ifaces.dml"; method init() { - test(false); + test(true); } diff --git a/test/1.4/legacy/port_proxy_attrs.dml b/test/1.4/legacy/port_proxy_attrs.dml index f5a36d597..4aaf814de 100644 --- a/test/1.4/legacy/port_proxy_attrs.dml +++ b/test/1.4/legacy/port_proxy_attrs.dml @@ -10,8 +10,8 @@ port p { attribute a is uint64_attr; } -method test(bool proxies_deprecated) { - assert _deprecate_port_proxy_attrs == proxies_deprecated; +method test(bool proxies_enabled) { + assert _compat_port_proxy_attrs == proxies_enabled; assert SIM_class_has_attribute(SIM_object_class(dev.obj), "p_a") - == !proxies_deprecated; + == proxies_enabled; } diff --git a/test/1.4/legacy/port_proxy_ifaces.dml b/test/1.4/legacy/port_proxy_ifaces.dml index 6994dbba7..c31ea754e 100644 --- a/test/1.4/legacy/port_proxy_ifaces.dml +++ b/test/1.4/legacy/port_proxy_ifaces.dml @@ -16,8 +16,8 @@ port p { } } -method test(bool proxies_deprecated) { - assert _deprecate_port_proxy_ifaces == proxies_deprecated; +method test(bool proxies_enabled) { + assert _compat_port_proxy_ifaces == proxies_enabled; assert (SIM_c_get_port_interface(dev.obj, "signal", "p") == NULL) - == proxies_deprecated; + == !proxies_enabled; } From 82499b05f59403102cfa3b3501194a58c8b3e5ed Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 12:02:50 +0200 Subject: [PATCH 08/20] Refactor: represent API versions with a dedicated class Also flatten the compat.features dict and use string-only API representation in env.py --- deprecations_to_md.py | 36 +++++++++++++++++++++--------------- generate_env.py | 5 ++--- py/dml/c_backend.py | 14 +++++++------- py/dml/compat.py | 43 ++++++++++++++++++++++++++++++------------- py/dml/dmlc.py | 43 +++++++++++++++++++++++-------------------- py/dml/structure.py | 13 +++++-------- py/dml/toplevel.py | 10 ++++------ py/dml/types.py | 4 ++-- 8 files changed, 94 insertions(+), 74 deletions(-) diff --git a/deprecations_to_md.py b/deprecations_to_md.py index cfcaa08f0..fc8669dd5 100644 --- a/deprecations_to_md.py +++ b/deprecations_to_md.py @@ -6,21 +6,27 @@ [path_to_dml, header, outfile] = sys.argv[1:] sys.path.append(path_to_dml) -from dml.compat import features -from dml.env import api_versions +from dml import compat +from dml.env import api_versions, default_api_version + +by_version = {} +for feature in compat.features.values(): + if (feature.last_api_version in api_versions() + # don't document features that are unconditionally disabled in + # this Simics version + or feature.last_api_version > compat.apis[default_api_version()]): + by_version.setdefault(feature.last_api_version, []).append(feature) -api_map = {v: k for (k, v) in api_versions().items()} with open(outfile, 'w') as f: f.write(Path(header).read_text()) - for (ver, deps) in features.items(): - if deps and ver in api_map: - f.write( - f"### Features available up to --simics-api={api_map[ver]}\n") - f.write("
\n") - for d in deps.values(): - assert d.__doc__ - f.write(f"
{d.tag()}
\n") - doc = '\n'.join(line[4:] if line.startswith(' ') else line - for line in d.__doc__.strip().splitlines()) - f.write(f"
\n\n{doc}\n
\n") - f.write("
\n") + for (ver, features) in sorted(by_version.items()): + f.write( + f"### Features available up to --simics-api={ver.str}\n") + f.write("
\n") + for feature in sorted(compat.features.values(), key=lambda f: f.tag()): + assert feature.__doc__ + f.write(f"
{feature.tag()}
\n") + doc = '\n'.join(line[4:] if line.startswith(' ') else line + for line in feature.__doc__.strip().splitlines()) + f.write(f"
\n\n{doc}\n
\n") + f.write("
\n") diff --git a/generate_env.py b/generate_env.py index 68303baa0..2a94b60b6 100644 --- a/generate_env.py +++ b/generate_env.py @@ -8,14 +8,13 @@ from simicsutils.internal import api_versions, default_api_version def generate_env(out): - api_versions_dict = {v: 4 if v == '4.8' else int(v) for v in api_versions()} Path(out).write_text(f'''\ def is_windows(): return {is_windows()} def api_versions(): - return {api_versions_dict} + return {repr(api_versions())} def default_api_version(): - return {api_versions_dict[default_api_version()]} + return {repr(default_api_version())} ''') if __name__ == '__main__': diff --git a/py/dml/c_backend.py b/py/dml/c_backend.py index 8e65d11a4..343306eba 100644 --- a/py/dml/c_backend.py +++ b/py/dml/c_backend.py @@ -1545,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)) @@ -1590,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') @@ -1910,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 <= 4: + 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) @@ -1937,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') @@ -3229,8 +3229,8 @@ def generate_cfile(device, footers, full_module): global c_file - sym = 'SIMICS_%s_API' % ('4_8' if dml.globals.api_version == 4 - else dml.globals.api_version) + 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) @@ -3415,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 == 4 + 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/compat.py b/py/dml/compat.py index 32837e832..f4e2c52c8 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -2,33 +2,50 @@ # SPDX-License-Identifier: MPL-2.0 import abc +from dataclasses import dataclass -from . import env + +@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): + def tag(self) -> str: return self.__class__.__name__ @abc.abstractproperty def __doc__(self): pass @abc.abstractproperty - def short(self): pass + def short(self) -> str: pass @abc.abstractproperty - def last_api_version(self): pass + def last_api_version(self) -> API: pass -# API version -> tag -> feature -features: dict[str, dict[str, CompatFeature]] = { - api: {} for api in env.api_versions().values()} +# tag -> feature +features: dict[str, CompatFeature] = {} def feature(cls: type[CompatFeature]): assert issubclass(cls, CompatFeature) singleton = cls() - features[cls.last_api_version][singleton.tag()] = singleton + features[singleton.tag()] = singleton return singleton @@ -45,7 +62,7 @@ class port_proxy_ifaces(CompatFeature): banks do not need proxies for backward compatibility. ''' short = "Don't generate proxy port interfaces for banks and ports" - last_api_version = 6 + last_api_version = api_6 @feature @@ -60,7 +77,7 @@ class port_proxy_attrs(CompatFeature): ''' short = ("Don't generate top-level proxy attributes" + " for attributes in banks and ports") - last_api_version = 6 + last_api_version = api_6 @feature @@ -72,7 +89,7 @@ class dml12_inline(CompatFeature): implications. ''' short = "Don't inline method arguments with a declared type in DML 1.2" - last_api_version = 6 + last_api_version = api_6 # separate class only because last_api_version differs @@ -82,7 +99,7 @@ class dml12_not(CompatFeature): 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 = 5 + last_api_version = api_5 @feature @@ -124,4 +141,4 @@ class dml12_misc(CompatFeature): ''' short = "Disable various DML 1.2 quirks" - last_api_version = 6 + last_api_version = api_6 diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index da08a9359..04868f394 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -292,12 +292,18 @@ def print_help(self): 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.''') - ver_map = {i: s for (s, i) in api_versions().items()} - for (version, deps) in compat.features.items(): - print(f' Features available with --simics-api={ver_map[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 (tag, dep) in deps.items(): - print(f' {tag:20s} {dep.short}') + 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 @@ -521,16 +527,14 @@ def main(argv): else: defs[name] = value - api_map = api_versions() - if options.simics_api not in api_map: - prerr("dmlc: the version '%s' is not a valid API version" % ( - options.simics_api)) - sys.exit(1) + api = dml.globals.api_version = compat.apis.get(options.simics_api) + if api is None: + parser.error(f"dmlc: the version '{options.simics_api}'" + " is not a valid API version") - if options.full_module and options.simics_api not in ['4.8']: - prerr("dmlc: the -m option is only valid together with --api=4.8" - " or older") - sys.exit(1) + if options.full_module and api != compat.api_4_8: + parser.error("dmlc: the -m option is only valid together with --api=4.8" + " or older") # This warning is disabled by default below Simics 7 due to sheer # prominence of the issue it warns about in existing code. @@ -538,7 +542,7 @@ def main(argv): # to handle the bugs as part of migration, instead of suddenly # overwhelming them with a truly massive amount of warnings in an # intermediate release. - if options.simics_api in {'4.8', '5', '6'}: + if api <= compat.api_6: ignore_warning('WLOGMIXUP') for w in options.disabled_warnings: @@ -588,14 +592,13 @@ def main(argv): + "The DMLC developers WILL NOT respect their use. " + "NEVER enable this flag for any kind of production code!!!***") - dml.globals.api_version = api_map[options.simics_api] + features = {tag: feature + for (tag, feature) in compat.features.items() + if feature.last_api_version >= dml.globals.api_version} - features = {tag: dep for api in api_map.values() - if api >= dml.globals.api_version - for (tag, dep) in compat.features[api].items()} for flag in options.no_compat: for tag in flag.split(','): - if any(tag in tags for tags in compat.features.values()): + if tag in compat.features: if tag in features: del features[tag] else: diff --git a/py/dml/structure.py b/py/dml/structure.py index e2ce7890d..94cd44dcb 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -1031,14 +1031,11 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['banks'] = UninitializedParamExpr(site, 'banks') else: autoparams['NULL'] = NullParamExpr(site) - api_map = {i: s for (s, i) in env.api_versions().items()} autoparams['simics_api_version'] = SimpleParamExpr( - mkStringConstant(site, api_map[dml.globals.api_version])) - for deps in compat.features.values(): - for (tag, dep) in deps.items(): - autoparams[f'_compat_{tag}'] = SimpleParamExpr( - mkBoolConstant( - site, dep in dml.globals.enabled_compat)) + mkStringConstant(site, dml.globals.api_version.str)) + for (tag, feature) in compat.features.items(): + autoparams[f'_compat_{tag}'] = SimpleParamExpr( + mkBoolConstant(site, feature in dml.globals.enabled_compat)) dml.globals.device = obj elif obj.objtype == 'bank': @@ -1883,7 +1880,7 @@ def mkobj2(obj, obj_specs, params, each_stmts): param.get_expr(zero_index * param.dimensions) except DMLError as e: if (dml.globals.dml_version == (1, 2) - and dml.globals.api_version <= 5 + and dml.globals.api_version <= compat.api_5 and isinstance(e, EREF)): # We forgive some errors in unused parameters, to # avoid the annoyance caused by hard errors from code diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 5bf9d829a..bbc20efca 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -383,12 +383,10 @@ def parse_main_file(inputfilename, explicit_import_path): for path in [os.path.join(orig_path, version_str), orig_path]] - if version == (1, 2) and dml.globals.api_version not in { - # we may want to add "8" if we want to postpone the - # deprecation of DML 1.2 - 4, 5, 6, 7}: - raise ESIMAPI(site, fmt_version(version), - dml.globals.api_version) + # we may want to bump last version to 8 if we want to postpone the + # deprecation of DML 1.2 + if version == (1, 2) and dml.globals.api_version > compat.api_7: + raise ESIMAPI(site, fmt_version(version), dml.globals.api_version.str) # Map normalized, absolute path of an imported file, to list of # seen spellings. One spelling is a string in an import statement which diff --git a/py/dml/types.py b/py/dml/types.py index 240b08dca..21b44817b 100644 --- a/py/dml/types.py +++ b/py/dml/types.py @@ -1233,9 +1233,9 @@ def parse_type(typename): return TBool() elif typename == 'void': return TVoid() - elif typename == 'integer_t' and dml.globals.api_version < 7: + elif typename == 'integer_t' and dml.globals.api_version < compat.api_7: return TInt(64, True) - elif typename == 'uinteger_t' and dml.globals.api_version < 7: + elif typename == 'uinteger_t' and dml.globals.api_version < compat.api_7: return TInt(64, False) else: return TNamed(typename) From 6ed691030c2c4ef62237ae4eefe05ff7710615f1 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 26 Sep 2023 18:41:11 +0200 Subject: [PATCH 09/20] Validate that dml-builtins declares all autoparams --- py/dml/structure.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/py/dml/structure.py b/py/dml/structure.py index 94cd44dcb..f359b0a98 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -1021,8 +1021,6 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['index'] = SimpleParamExpr(mkUndefined(site)) else: autoparams['indices'] = IndexListParamExpr(site, index_params) - autoparams['_be_bitorder'] = SimpleParamExpr( - mkBoolConstant(site, site.bitorder() == 'be')) if obj.objtype == 'device': with crep.DeviceInstanceContext(): @@ -1031,6 +1029,8 @@ def make_autoparams(obj, index_vars, index_var_sites): autoparams['banks'] = UninitializedParamExpr(site, 'banks') else: autoparams['NULL'] = NullParamExpr(site) + autoparams['_be_bitorder'] = SimpleParamExpr( + mkBoolConstant(site, site.bitorder() == 'be')) autoparams['simics_api_version'] = SimpleParamExpr( mkStringConstant(site, dml.globals.api_version.str)) for (tag, feature) in compat.features.items(): @@ -1081,7 +1081,8 @@ def make_autoparams(obj, index_vars, index_var_sites): # Add common automatic parameters autoparams['qname'] = QNameParamExpr(obj, 'device') - autoparams['_static_qname'] = StaticQNameParamExpr(obj, 'device') + if dml.globals.dml_version != (1, 2): + autoparams['_static_qname'] = StaticQNameParamExpr(obj, 'device') if obj.parent: autoparams['parent'] = ParentParamExpr(obj) @@ -1134,7 +1135,8 @@ def create_parameters(obj, obj_specs, index_vars, index_sites): parameters.setdefault(name, []).append((obj_spec.rank, s)) autoparams = make_autoparams(obj, index_vars, index_sites) - + for name in autoparams: + assert name in parameters, name return [mkparam(obj, autoparams, merge_parameters(parameters[name], obj_specs)) for name in sorted(parameters)] From 4c0c05e62e0ad02013ee3541b14efe9c2483f228 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 12:04:50 +0200 Subject: [PATCH 10/20] Use compat system for --strict-int --- lib/1.2/dml-builtins.dml | 1 + lib/1.4/dml-builtins.dml | 1 + py/dml/compat.py | 46 ++++++++++++++++++++++++++++++++++++++++ py/dml/dmlc.py | 20 ++++++++--------- py/dml/globals.py | 8 ++++--- py/dml/types.py | 2 +- 6 files changed, 64 insertions(+), 14 deletions(-) diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index c653f3f03..fa58c5a31 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -211,6 +211,7 @@ template device { parameter _compat_dml12_inline auto; parameter _compat_dml12_not auto; parameter _compat_dml12_misc auto; + parameter _compat_dml12_int auto; // automatic parameters parameter obj auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index d011d5e07..5c7586910 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -548,6 +548,7 @@ template device { param _compat_dml12_inline auto; param _compat_dml12_not auto; param _compat_dml12_misc auto; + param _compat_dml12_int auto; // automatic parameters param obj auto; diff --git a/py/dml/compat.py b/py/dml/compat.py index f4e2c52c8..810afb7c8 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -142,3 +142,49 @@ class dml12_misc(CompatFeature): ''' short = "Disable various DML 1.2 quirks" 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/dmlc.py b/py/dml/dmlc.py index 04868f394..0483b996f 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -408,15 +408,11 @@ def main(argv): parser.add_argument('--werror', action='store_true', help='Turn all warnings into errors') - #
--strict
- #
Report errors for some constructs that will be forbidden in - # future versions of the DML language
parser.add_argument('--strict-dml12', action='store_true', - help='Alias for --strict-int' - ' --no-compat=dml12_inline,dml12_not,dml12_misc') + help='Alias for --no-compat=dml12_inline' + ',dml12_not,dml12_misc,dml12_int') parser.add_argument('--strict-int', action='store_true', - help='Use DML 1.4 style integer arithmetic semantics' - + ' when compiling DML 1.2 files.') + help='Alias for --no-compat=dml12_int') #
--coverity
#
Adds Synopsys® Coverity® analysis annotations to suppress common @@ -604,9 +600,15 @@ def main(argv): else: options.error(f'invalid tag {tag} for --no-compat.' ' Try --help-no-compat.') + + if options.strict_int: + tag = compat.dml12_int.tag() + if tag in features: + del features[tag] + if options.strict_dml12: for feature in [compat.dml12_inline, compat.dml12_not, - compat.dml12_misc]: + compat.dml12_misc, compat.dml12_int]: tag = feature.tag() if tag in features: del features[tag] @@ -659,8 +661,6 @@ def main(argv): if dml_version != (1, 2): logging.show_porting = False - dml.globals.strict_int_flag = options.strict_dml12 or options.strict_int - if 'DMLC_DUMP_INPUT_FILES' in os.environ: dump_input_files(outputbase, dict( imported, **{inputfilename: [os.path.basename(inputfilename)]})) diff --git a/py/dml/globals.py b/py/dml/globals.py index 1105f3fe1..6c31beb86 100644 --- a/py/dml/globals.py +++ b/py/dml/globals.py @@ -3,6 +3,8 @@ # Global variables +from . import compat + # name -> Template instance. An object declaration in a file are # represented implicitly as a template, named @filename.dml. templates = {} @@ -60,11 +62,11 @@ enabled_compat = set() -# 1.4 style integer operations in 1.2, --strict-dml12-int -strict_int_flag = None +# 1.4 style integer operations in 1.2 def compat_dml12_int(site): # if site is None, guess DML 1.4 - return not strict_int_flag and site and site.dml_version() == (1, 2) + return (compat.dml12_int in enabled_compat + and site and site.dml_version() == (1, 2)) debuggable = False diff --git a/py/dml/types.py b/py/dml/types.py index 21b44817b..a329b2e58 100644 --- a/py/dml/types.py +++ b/py/dml/types.py @@ -483,7 +483,7 @@ def cmp(self, other): if isinstance(self, TSize) != isinstance(other, TSize): return NotImplemented if (dml.globals.dml_version == (1, 2) - and not dml.globals.strict_int_flag): + and compat.dml12_int in dml.globals.enabled_compat): # Ignore signedness return 0 if self.bits == other.bits else NotImplemented else: From 2dad4301f2de3028127bcd2cd2870bccc03afdaa Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 26 Sep 2023 18:53:10 +0200 Subject: [PATCH 11/20] Use compat to control the default of use_io_memory param --- lib/1.2/dml-builtins.dml | 1 + lib/1.4/dml-builtins.dml | 4 ++-- py/dml/compat.py | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index fa58c5a31..1e1ea1115 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -208,6 +208,7 @@ template device { parameter _compat_port_proxy_ifaces auto; parameter _compat_port_proxy_attrs auto; + parameter _compat_io_memory auto; parameter _compat_dml12_inline auto; parameter _compat_dml12_not auto; parameter _compat_dml12_misc auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index 5c7586910..e5a01e2ba 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -541,10 +541,10 @@ template device { 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_io_memory auto; param _compat_dml12_inline auto; param _compat_dml12_not auto; param _compat_dml12_misc auto; @@ -561,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 diff --git a/py/dml/compat.py b/py/dml/compat.py index 810afb7c8..22038271d 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -80,6 +80,17 @@ class port_proxy_attrs(CompatFeature): last_api_version = api_6 +@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 dml12_inline(CompatFeature): '''When using `inline` to inline a method in a DML 1.2 device, From 20336a662ca6e13641b62c708f12585b7eff270d Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Tue, 26 Sep 2023 18:54:14 +0200 Subject: [PATCH 12/20] Use compat system for the obj param of ports/banks --- lib/1.2/dml-builtins.dml | 1 + lib/1.4/dml-builtins.dml | 7 +++---- py/dml/compat.py | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index 1e1ea1115..22d1252cf 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -208,6 +208,7 @@ template device { 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; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index e5a01e2ba..4f5086c21 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -539,11 +539,10 @@ 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 _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; @@ -1450,7 +1449,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 *) { @@ -1782,7 +1781,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/compat.py b/py/dml/compat.py index 22038271d..f6bb02a7c 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -91,6 +91,20 @@ class io_memory(CompatFeature): 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, From d2867f0073d0bca915b436de7a4a1c96d5a84ca3 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 09:40:41 +0200 Subject: [PATCH 13/20] Repair odd ICE Triggered by `VPOP + VPOP` when compiling with --no-compat=dml12_int, or by 1.2/errors/EAPPLY when enabling --no-compat=dml12_int,dml12_misc --- py/dml/ctree.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/py/dml/ctree.py b/py/dml/ctree.py index 7b4fd635a..04878f579 100644 --- a/py/dml/ctree.py +++ b/py/dml/ctree.py @@ -1152,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) From 782a81f6b50cead79c9467dd21aff300e089d4e9 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 09:44:20 +0200 Subject: [PATCH 14/20] Avoid errors on `typeof $this` in arrays of registers or fields Passing --no-compat=dml12_misc triggers this in many tests. --- py/dml/codegen.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/py/dml/codegen.py b/py/dml/codegen.py index 16ce839f7..2a3adf1df 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -5,6 +5,7 @@ from abc import ABC, abstractmethod, abstractproperty import operator import contextlib + from functools import reduce import itertools import os @@ -1525,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 (compat.dml12_misc not in dml.globals.enabled_compat - 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: From d06d43e120eb8a0f84040de78564e906c9949c8e Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 09:45:24 +0200 Subject: [PATCH 15/20] Avoid overflow with --no-compat=dml12_int --- test/1.2/syntax/T_int.dml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/1.2/syntax/T_int.dml b/test/1.2/syntax/T_int.dml index c9125a4e9..ac9e1a5ad 100644 --- a/test/1.2/syntax/T_int.dml +++ b/test/1.2/syntax/T_int.dml @@ -10,8 +10,8 @@ method test -> (bool ok) { local int i = 123_456; local int b1 = 0b_1010_0101; local int b2 = 0b1_01__00___101; - local int x1 = 0x_dead_beef; - local int x2 = 0xd_ea__db___eef; + local uint32 x1 = 0x_dead_beef; + local uint32 x2 = 0xd_ea__db___eef; ok = (i == 123456 && b1 == 0b10100101 && b2 == 0b10100101 && x1 == 0xdeadbeef && x2 == 0xdeadbeef); From 91e2424a9a01ea841bfc749b88d78f167dfa298e Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 14:55:50 +0200 Subject: [PATCH 16/20] Add rudimentary tests for help flags --- test/tests.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/tests.py b/test/tests.py index cc0fe7d02..908f2a676 100644 --- a/test/tests.py +++ b/test/tests.py @@ -936,6 +936,12 @@ def test(self): assert (dir / (self.shortname + '.c')).is_file() all_tests = [] +def subtest(*args, **kwargs): + def register(cls): + all_tests.append(cls(*args, **kwargs)) + return cls + return register + # First, some special cases class ErrorTest(CTestCase): @@ -1102,6 +1108,21 @@ def test(self): all_tests.append(DebuggableCheck('debuggable-check')) +@subtest('--help-no-compat') +@subtest('--help-warn') +@subtest('--help') +class HelpTest(BaseTestCase): + '''Check that some DMLC flag works''' + __slots__ = () + def test(self): + cmd = main_dmlc + [self.fullname] + self.pr(f"Running: {' '.join(cmd)}") + ret = subprocess.run(cmd, capture_output=True, text=True) + self.pr(ret.stderr) + self.pr(ret.stdout) + if ret.returncode: + raise TestFail('dmlc returncode {ret.returncode}') + class DmlDepBase(CTestCase): '''Base class for DML dependency test cases.''' __slots__ = () From ae88ac5cce0b300491665dfbf6db90a15c9593fc Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 14:56:09 +0200 Subject: [PATCH 17/20] Add release note for --no-compat flag --- RELEASENOTES.docu | 7 +++++++ 1 file changed, 7 insertions(+) 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. + Added a flag --no-compat + to selectively disable compatibility features. In + particular, --no-compat=port_proxy_ifaces disables + generation of interface trampolines, which can speed up + compilation of devices with huge arrays of ports. The + option --help-no-compat lists all features that can be + disabled. From bc55035bcc5170bbe7def38abc91d0012b753ddd Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 28 Sep 2023 12:25:39 +0200 Subject: [PATCH 18/20] Avoid incorrect ENAMECOLL with dml12_misc --- py/dml/structure.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/py/dml/structure.py b/py/dml/structure.py index f359b0a98..59d7d61a6 100644 --- a/py/dml/structure.py +++ b/py/dml/structure.py @@ -1907,7 +1907,13 @@ def mkobj2(obj, obj_specs, params, each_stmts): # 1.4 code for p in obj.get_components(): sym = global_scope.lookup(p.name) - if sym: + if sym and ( + # hacky workaround for the ExpressionSymbol + # implicitly added above. Needed when importing 1.4 + # code from 1.2 with --no-compat=dml12_misc + dml.globals.dml_version != (1, 2) + or p.site.dml_version == (1, 2) + or p.site != sym.site): report(ENAMECOLL(p.site, sym.site, p.name)) # At this point, methods and subobjs are created and we can From 18febee9902dbd535ef16a125e86b920a5ca3585 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Thu, 5 Oct 2023 08:27:43 +0200 Subject: [PATCH 19/20] Extract goto to a separate compat feature --- lib/1.2/dml-builtins.dml | 1 + lib/1.4/dml-builtins.dml | 1 + py/dml/codegen.py | 2 +- py/dml/compat.py | 14 ++++++++++++-- py/dml/dmlc.py | 5 +++-- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index 22d1252cf..b269bf15c 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -212,6 +212,7 @@ template device { 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; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index 4f5086c21..e921a3594 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -546,6 +546,7 @@ template device { 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; diff --git a/py/dml/codegen.py b/py/dml/codegen.py index 2a3adf1df..a952b3b07 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -2545,7 +2545,7 @@ def stmt_assert(stmt, location, scope): @statement_dispatcher def stmt_goto(stmt, location, scope): [label] = stmt.args - if compat.dml12_misc not in dml.globals.enabled_compat: + if compat.dml12_goto not in dml.globals.enabled_compat: report(ESYNTAX(stmt.site, 'goto', 'goto statement not allowed')) return [mkGoto(stmt.site, label)] diff --git a/py/dml/compat.py b/py/dml/compat.py index f6bb02a7c..956ef8a22 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -137,8 +137,6 @@ class dml12_misc(CompatFeature): * the `typeof` operator on an expression that isn't an lvalue - * the `goto` statement - * `select` statements over `vect` types * Passing a string literal in a (non-`const`) `char *` method argument @@ -169,6 +167,18 @@ class dml12_misc(CompatFeature): 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 diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index 0483b996f..5821ff55a 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -410,7 +410,7 @@ def main(argv): parser.add_argument('--strict-dml12', action='store_true', help='Alias for --no-compat=dml12_inline' - ',dml12_not,dml12_misc,dml12_int') + ',dml12_not,dml12_goto,dml12_misc,dml12_int') parser.add_argument('--strict-int', action='store_true', help='Alias for --no-compat=dml12_int') @@ -608,7 +608,8 @@ def main(argv): if options.strict_dml12: for feature in [compat.dml12_inline, compat.dml12_not, - compat.dml12_misc, compat.dml12_int]: + compat.dml12_goto, compat.dml12_misc, + compat.dml12_int]: tag = feature.tag() if tag in features: del features[tag] From 00193279df9c5640c20c4f07b7722a9f52d0bc72 Mon Sep 17 00:00:00 2001 From: Erik Carstensen Date: Wed, 27 Sep 2023 09:48:23 +0200 Subject: [PATCH 20/20] Temporarily bump last API of port proxy flags to 7 Deprecating requires updating lots of tests and components; better to push that to a separate PR. --- py/dml/compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/dml/compat.py b/py/dml/compat.py index 956ef8a22..eeae2aab7 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -62,7 +62,7 @@ class port_proxy_ifaces(CompatFeature): banks do not need proxies for backward compatibility. ''' short = "Don't generate proxy port interfaces for banks and ports" - last_api_version = api_6 + last_api_version = api_7 @feature @@ -77,7 +77,7 @@ class port_proxy_attrs(CompatFeature): ''' short = ("Don't generate top-level proxy attributes" + " for attributes in banks and ports") - last_api_version = api_6 + last_api_version = api_7 @feature