Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --deprecate option for proxy ports and interfaces #209

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MODULEINFO
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Make: dmlc
$(HOST)/bin/dml/python/dml/c_backend.py
$(HOST)/bin/dml/python/dml/g_backend.py
$(HOST)/bin/dml/python/dml/codegen.py
$(HOST)/bin/dml/python/dml/compat.py
$(HOST)/bin/dml/python/dml/crep.py
$(HOST)/bin/dml/python/dml/ctree.py
$(HOST)/bin/dml/python/dml/dmllex.py
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -210,9 +211,11 @@ DOC_FILES_14 := $(wildcard $(DOC_SRC_DIR_14)/*.md) $(DOC_SRC_DIR_14)/toc.json
DOC_DEST_14 := $(SIMICS_PROJECT)/$(HOST_TYPE)/doc/html/dml-1.4-reference-manual
DOC_MARKER_14 := $(DOC_DEST_14)/filelist.json

GENERATED_MD_FILES_14 = $(addprefix generated-md-1.4/,grammar.md messages.md changes-auto.md dml-builtins.md utility.md)
GENERATED_MD_FILES_14 = $(addprefix generated-md-1.4/,grammar.md messages.md changes-auto.md dml-builtins.md utility.md deprecations-auto.md)
generated-md-1.4/changes-auto.md: $(SRC_BASE)/$(TARGET)/porting_to_md.py $(DMLC_BIN) | generated-md-1.4
$(PYTHON) $< $(PYTHONPATH) $@
generated-md-1.4/deprecations-auto.md: $(SRC_BASE)/$(TARGET)/deprecations_to_md.py $(SRC_BASE)/$(TARGET)/doc/1.4/deprecations-header.md $(DMLC_BIN) | generated-md-1.4
$(PYTHON) $< $(PYTHONPATH) $(word 2,$^) $@
generated-md-1.4/dml-builtins.md generated-md-1.4/utility.md: generated-md-1.4/%.md: $(DMLC_DIR)/lib/1.4/%.dml
$(PYTHON) $(DMLC_DIR)/dmlcomments_to_md.py $< $@
DOC_FILES_14 += $(GENERATED_MD_FILES_14)
Expand Down
7 changes: 7 additions & 0 deletions RELEASENOTES.docu
Original file line number Diff line number Diff line change
Expand Up @@ -1730,4 +1730,11 @@ extern typedef struct { } my_type_t;</pre> </add-note></build-id>
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
<tt>--warn=WLOGMIXUP</tt> to DMLC.</add-note></build-id>
<build-id value="next"><add-note> Added a flag <tt>--no-compat</tt>
to selectively disable compatibility features. In
particular, <tt>--no-compat=port_proxy_ifaces</tt> disables
generation of interface trampolines, which can speed up
compilation of devices with huge arrays of ports. The
option <tt>--help-no-compat</tt> lists all features that can be
disabled.</add-note></build-id>
</rn>
32 changes: 32 additions & 0 deletions deprecations_to_md.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# © 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 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)

with open(outfile, 'w') as f:
f.write(Path(header).read_text())
for (ver, features) in sorted(by_version.items()):
f.write(
f"### Features available up to --simics-api={ver.str}\n")
f.write("<dl>\n")
for feature in sorted(compat.features.values(), key=lambda f: f.tag()):
assert feature.__doc__
f.write(f" <dt>{feature.tag()}</dt>\n")
doc = '\n'.join(line[4:] if line.startswith(' ') else line
for line in feature.__doc__.strip().splitlines())
f.write(f" <dd>\n\n{doc}\n</dd>\n")
f.write("</dl>\n")
72 changes: 72 additions & 0 deletions doc/1.4/deprecations-header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!--
© 2023 Intel Corporation
SPDX-License-Identifier: MPL-2.0
-->

# 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 <code>--no-compat=_tag_</code> flag, which disables the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, <code>the last word is _italicized_</code> works? Huh. In the ref manual we use <tt>the last work is <em>italicized</em></tt>. We should pick one for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree: I will not remember style at this level of detail so it will cost more than it gives. I can see two cases where markdown style matters:

  • we need to avoid markdown that relyies on corners that have non-local effect, such as an unescaped _underscore that works fine as long as no other underscore appears later in the same paragraph.
  • it's nice to ban other inconsistencies as long we can run a linter to enforce the rule.

feature represented by _`tag`_. The available tags are listed in the next
section. The tag also controls the name of a global boolean parameter that the
DML program may use to check whether the feature is available. The parameter's
name is the tag name preceded by `_compat_`.

## List of deprecated features
3 changes: 3 additions & 0 deletions doc/1.4/toc.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions generate_env.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# © 2021-2023 Intel Corporation
# SPDX-License-Identifier: MPL-2.0

import os
import sys
from pathlib import Path

Expand All @@ -13,7 +12,7 @@ def generate_env(out):
def is_windows():
return {is_windows()}
def api_versions():
return {api_versions()}
return {repr(api_versions())}
def default_api_version():
return {repr(default_api_version())}
''')
Expand Down
10 changes: 10 additions & 0 deletions lib/1.2/dml-builtins.dml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,16 @@ template device {

parameter simics_api_version auto;

parameter _compat_port_proxy_ifaces auto;
parameter _compat_port_proxy_attrs auto;
parameter _compat_port_obj_param auto;
parameter _compat_io_memory auto;
parameter _compat_dml12_inline auto;
parameter _compat_dml12_not auto;
parameter _compat_dml12_goto auto;
parameter _compat_dml12_misc auto;
parameter _compat_dml12_int auto;
lwaern-intel marked this conversation as resolved.
Show resolved Hide resolved

// automatic parameters
parameter obj auto;
parameter logobj = $obj;
Expand Down
19 changes: 13 additions & 6 deletions lib/1.4/dml-builtins.dml
Original file line number Diff line number Diff line change
Expand Up @@ -539,9 +539,16 @@ template device {
param _is_simics_object = true;

param simics_api_version auto;
param _api_5_or_older = simics_api_version == "5"
|| simics_api_version == "4.8";
param _api_6_or_older = simics_api_version == "6" || _api_5_or_older;

param _compat_port_proxy_ifaces auto;
param _compat_port_proxy_attrs auto;
param _compat_port_obj_param auto;
param _compat_io_memory auto;
param _compat_dml12_inline auto;
param _compat_dml12_not auto;
param _compat_dml12_goto auto;
param _compat_dml12_misc auto;
param _compat_dml12_int auto;

// automatic parameters
param obj auto;
Expand All @@ -554,7 +561,7 @@ template device {
param be_bitorder default _be_bitorder;
param log_group = undefined;

param use_io_memory default _api_6_or_older;
param use_io_memory default _compat_io_memory;
// this was available in DML 1.2; defensively reserved in 1.4
param banks = undefined;
// this carried semantics in DML 1.2; deprecated in 1.4
Expand Down Expand Up @@ -1443,7 +1450,7 @@ template port is object {
param _is_simics_object = true;
// limitations for ports are not recognized
param limitations = undefined;
param obj = dev._api_5_or_older #? dev.obj #: _port_obj();
param obj = dev._compat_port_obj_param #? dev.obj #: _port_obj();

session conf_object_t *_cached_port_obj;
shared method _port_obj() -> (conf_object_t *) {
Expand Down Expand Up @@ -1775,7 +1782,7 @@ template bank is (object, shown_desc) {
// compatibility: referencing 'obj' in a bank method must evaluate to
// dev.obj in code that can compile on both 5 and 6
// TODO: we should probably make obj an immutable session variable
param obj = dev._api_5_or_older #? dev.obj #: _bank_obj();
param obj = dev._compat_port_obj_param #? dev.obj #: _bank_obj();
param partial : bool;
param overlapping : bool;
param _le_byte_order : bool;
Expand Down
83 changes: 41 additions & 42 deletions py/dml/c_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pathlib import Path

from . import objects, logging, crep, output, ctree, serialize, structure
from . import traits
from . import traits, compat
import dml.globals
from .structure import get_attr_name, port_class_ident, need_port_proxy_attrs
from .logging import *
Expand Down Expand Up @@ -886,36 +886,37 @@ def generate_implement(code, device, impl):
# Legacy interface ports are only added for ports and banks that were
# available in Simics 5, i.e. zero or one dimensional direct
# descendants of the device object
if (port.local_dimensions() == 0 and port.parent.parent is None
and port.objtype != 'subdevice'):
code.out("static const %s = %s;\n" % (
ifacetype.declaration('port_iface'),
interface_block(device, ifacestruct, methods, ())))
code.out('SIM_register_port_interface'
'(class, "%s", &port_iface, "%s", %s);\n'
% (impl.name, crep.cname(port), desc))
elif (port.local_dimensions() == 1 and port.parent.parent is None
and port.objtype != 'subdevice'):
[arrlen] = port.arraylens()
code.out("static const %s%s = %s;\n" %
(ifacetype.declaration("ifaces"),
"[%s]" % (arrlen,),
"{%s\n}" % ",\n".join(
"%s" % interface_block(device, ifacestruct,
methods, (i, ))
for i in range(arrlen))))
code.out("interface_array_t iface_vect = VNULL;\n")
idxvar = "i0"
code.out("for (int %s = 0; %s < %d; %s++)\n" %
(idxvar, idxvar, arrlen, idxvar),
postindent = 1)
access = "[%s]" % idxvar
code.out("VADD(iface_vect, &ifaces%s);\n" % access,
postindent = -1)
code.out('VT_register_port_array_interface'
'(class, "%s", &iface_vect, "%s", %s);\n'
% (impl.name, crep.cname(port), desc))
code.out('VFREE(iface_vect);\n')
if (port.parent is dml.globals.device
and port.objtype in {'port', 'bank'}
and compat.port_proxy_ifaces in dml.globals.enabled_compat):
if port.local_dimensions() == 0:
code.out("static const %s = %s;\n" % (
ifacetype.declaration('port_iface'),
interface_block(device, ifacestruct, methods, ())))
code.out('SIM_register_port_interface'
'(class, "%s", &port_iface, "%s", %s);\n'
% (impl.name, crep.cname(port), desc))
elif port.local_dimensions() == 1:
[arrlen] = port.arraylens()
code.out("static const %s%s = %s;\n" %
(ifacetype.declaration("ifaces"),
"[%s]" % (arrlen,),
"{%s\n}" % ",\n".join(
"%s" % interface_block(device, ifacestruct,
methods, (i, ))
for i in range(arrlen))))
code.out("interface_array_t iface_vect = VNULL;\n")
idxvar = "i0"
code.out("for (int %s = 0; %s < %d; %s++)\n" %
(idxvar, idxvar, arrlen, idxvar),
postindent = 1)
access = "[%s]" % idxvar
code.out("VADD(iface_vect, &ifaces%s);\n" % access,
postindent = -1)
code.out('VT_register_port_array_interface'
'(class, "%s", &iface_vect, "%s", %s);\n'
% (impl.name, crep.cname(port), desc))
code.out('VFREE(iface_vect);\n')
code.out("}\n", preindent = -1)

def port_prefix(port):
Expand Down Expand Up @@ -1544,7 +1545,7 @@ def generate_alloc(device):
out('}\n\n', preindent = -1)

def generate_initialize(device):
if dml.globals.api_version <= '6':
if dml.globals.api_version <= compat.api_6:
start_function_definition(
('void %s_pre_del_notify(conf_object_t *_subscriber,'
' conf_object_t *_notifier, lang_void *_data)') % crep.cname(device))
Expand Down Expand Up @@ -1589,7 +1590,7 @@ def generate_initialize(device):
codegen_inline_byname(device, (), '_init', [], [], device.site).toc()

reset_line_directive()
if dml.globals.api_version <= '6':
if dml.globals.api_version <= compat.api_6:
out('SIM_add_notifier(_obj, Sim_Notify_Object_Delete, _obj, '
+ crep.cname(device) + '_pre_del_notify, NULL);\n')

Expand Down Expand Up @@ -1909,7 +1910,7 @@ def generate_init_data_objs(device):
for (start, end) in [('A', 'Z'), ('a', 'z'), ('0', '9'), ('_', '_')]
for i in range(ord(start), ord(end) + 1))
def init_function_name(device, outprefix):
if dml.globals.api_version in {'4.8'}:
if dml.globals.api_version <= compat.api_4_8:
return 'initialize_' + crep.cname(device)
return '_initialize_' + ''.join(
(ch if ch in ident_chars else '_') for ch in outprefix)
Expand All @@ -1936,7 +1937,7 @@ def generate_init(device, initcode, outprefix):
out('.alloc = '+crep.cname(device)+'_alloc,\n')
out('.init = '+crep.cname(device)+'_init,\n')
out('.finalize = '+crep.cname(device)+'_finalize,\n')
if dml.globals.api_version >= '7':
if dml.globals.api_version >= compat.api_7:
out('.deinit = '+crep.cname(device)+'_deinit,\n')
out('.dealloc = '+crep.cname(device)+'_dealloc,\n')
out('.description = '+doc.read()+',\n')
Expand Down Expand Up @@ -3228,12 +3229,10 @@ def generate_cfile(device, footers,
full_module):
global c_file

if dml.globals.api_version == 'internal':
api_define = ''
else:
sym = 'SIMICS_%s_API' % dml.globals.api_version.replace('.', '_')
api_define = '#ifndef %s\n#define %s\n#endif\n' % (
sym, sym)
sym = 'SIMICS_%s_API' % ('4_8' if dml.globals.api_version == compat.api_4_8
else dml.globals.api_version.str)
api_define = '#ifndef %s\n#define %s\n#endif\n' % (
sym, sym)

c_top = '\n'.join([
'/*',
Expand Down Expand Up @@ -3416,7 +3415,7 @@ def generate_cfile_body(device, footers, full_module, filename_prefix):

if full_module:
# caught as error earlier on
assert dml.globals.api_version in ['4.8']
assert dml.globals.api_version == compat.api_4_8
out('\n')
out('EXPORTED void\n')
out('init_local(void)\n')
Expand Down
Loading
Loading