C. Managing deprecated language features - intel/device-modeling-language GitHub Wiki
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, breaking changes can come in many forms. Breaking changes 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 breaking change 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 change 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.
The simplest deprecation mechanism is Simics API versions: Each breaking change is associated with a Simics API version, and each Simics version supports a number of such API versions. When moving to a new Simics major version, support for the oldest API version is dropped which means the corresponding changes become mandatory. Since Simics is currently the primary distribution channel for DML, this 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 offer full support for API N, so there is no rush to update, but changing the API version early is a way to make sure deprecated features are not introduced in new code.
DML supports a more fine-grained mechanism for managing breaking changes, where each individual change in a new API version can be enabled individually, without enabling the full API version. This has the following uses:
- During migration to a new API version, enabling one breaking change at a time across the whole system makes it easier to analyze errors, because you only know that all errors come from the same change.
- When a breaking change is first introduced, it will be disabled by default in
the latest API version, and only be enabled by default when the next API
version is introduced. Thus, until the next Simics major release, the
breaking change cannot be activated without explicitly enabling it. There are
reasons to adopt changes early, e.g.:
- It guarantees that newly written code does not rely on the old behaviour, which eases later migration.
- Avoiding a legacy construct that has a newer replacement makes the code base cleaner and more consistent.
- Some breaking changes remove redundant representations from models, thereby reducing bloat both in terms of usability and performance.
DMLC provides a command-line flag --api-version to specify the API version to
be used for a model. When building with the CMake based build system in Simics,
this is controlled by the SIMICS_API parameter of the simics_add_module
function.
DMLC also provides a flag --breaking-change=TAG, which enables
the breaking change represented by TAG. The valid tags are listed in the
next section.
Each breaking change has an associated tag, which is passed to the
--breaking-change flag. Each tag also comes with a top-level DML parameter,
which DML code can use to check if a change is enabled or not. A section with
title foo-bar corresponds to the DMLC flag --breaking-change=foo-bar, and
when that flag is passed, the global parameter _breaking_change_foo_bar to
true.
Each breaking change is also implicitly enabled when compiling with a Simics API version above a certain threshold. The breaking changes under the section for API n are useful when migrating to API n+1.
These changes are enabled automatically when compiling using
Simics API 7 or newer. With older Simics API versions, the
changes can be enabled individually by passing
--breaking-change=TAG
to the dmlc compiler.
- dml12-disable-inline-constants
-
When using
inlinein DML 1.2, constant arguments passed in typed parameters were inlined as constants, which had some unintuitive semantic implications. In DML 1.4, constants are only inlined in parameters declared usinginlineas quasi-type. When this change is enabled, DML 1.2 only inlines constants in untyped method parameters, causing its behaviour to closer resemble DML 1.4. - dml12-modern-int
-
Up to Simics 6, DML 1.2 used legacy integer semantics, translating most operations directly into C without compensating for DML-specifics like the support for odd-sized uintNN types. This can sometimes have unexpected consequences. When this change is enabled, modern DML 1.4 integer semantics is used.
The most immediate effect of enabling this change is that DMLC will report errors on statements like
assert 0;andwhile (1) { ... }, which need to change intoassert false;andwhile (true) { ... }, respectively. Other effects include:-
With legacy integer semantics, integers of non-standard sizes are represented as a native C type, e.g.
uint5is represented asuint8, allowing it to store numbers too large to fit in 5 bits. With modern integer 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
int1to the value1. In DML 1.4, the only values of typeint1are0and-1, so such code should be rewritten to use theuint1type. It can be a good idea to grep for[^a-z_]int1[^0-9]and review ifuint1is a better choice. -
With legacy semantics, some operations that have undefined behaviour in C effectively counts as undefined behaviour in DML as well, whereas modern semantics have well-defined semantics for all operations. For instance, negative shift or division by zero is handled with an unconditional critical error, and too large shift operands or signed shift overflow is handled by truncation.
-
With legacy integer semantics, comparison operators
<,<=,==,>=,>inherit C semantics, whereas with modern integer semantics they are compared mathematically as integers. This sometimes makes a difference when comparing signed and unsigned numbers; in particular,-1 != 0xffffffffffffffffconsistently with modern semantics, whereas with legacy semantics, they are consiered different only if both are constant.
-
- dml12-remove-goto
-
Up to Simics 6, the
gotostatement was allowed in DML 1.2. The statement is not allowed in DML 1.4. When this change is enabled,gotois disallowed also in DML 1.2. Mostgoto-based control structures can be rewritten usingthrowandcatchblocks. - dml12-remove-misc-quirks
-
DML 1.2 had several quirks that inadvertedly caused it to permit some strange patterns. In DML 1.4 this was cleaned up and some patterns were disallowed. When this change is enabled, the DML 1.4 behaviour applies also in DML 1.2.
Examples of forbidden patterns include:
-
sizeof(typename)(seeWSIZEOFTYPE) -
typeofon non-lvalue expressions -
selectstatements overvecttypes - Passing a string literal to
char *arguments (now requiresconst char *) - Using the character
-in thec_nameparameter ofinterfaceobjects - Overriding interface type via the
c_nameparameter inimplementobjects -
loggroupidentifiers accessible under same name in generated C - Applying
&to non-lvalues -
externstatements without type (extern foo;) - Anonymous banks (
bank { ... }) - Unused templates instantiating non-existing templates
- Using the same symbol for both top-level object (
$scope) and top-level symbol (non-$scope, e.g.extern,constantorloggroup)
-
- enable-WLOGMIXUP
-
Up to Simics 6, the warning
WLOGMIXUPwas suppressed by default to avoid overwhelming users, as the faulty pattern it reports was common. When this change is enabled,WLOGMIXUPis reported by default. Code using the faulty pattern should be fixed before enabling this change. - shared-logs-locally
-
Up to Simics 6, log statements inside shared methods always logged on the device object instead of the nearest enclosing configuration object. This was a bug that many scripts relied on. When this change is enabled, logs inside shared methods behave consistently with non-shared methods and log on the nearest enclosing configuration object.
- transaction-by-default
-
Up to Simics 6, the top-level parameter
use_io_memorydefaulted totrue, causingbankobjects to implementio_memoryinstead oftransactionby default. When this change is enabled, banks will implementtransactionby default, anduse_io_memorymust be set explicitly totrueif the old behavior is desired.
These changes are enabled automatically when compiling using
Simics API 8 or newer. With older Simics API versions, the
changes can be enabled individually by passing
--breaking-change=TAG
to the dmlc compiler.
- forbid-broken-conditional-is
-
Up to Simics 7, a bug prevented DMLC from reporting an error when instantiating a nonexistent template within an
#ifblock. For example, the following would compile without errors:#if (true) { group g { // should be an error, but silently ignored when this // feature is enabled is nonexisting_template; } }This change causes DMLC to report an error on such code.
- forbid-broken-unused-types
-
Up to Simics 7, a bug prevented DMLC from reporting certain errors on unused unused
extern-declared types. For example, the following would compile without errors:extern typedef struct { undefined_type_t member; } never_used_t;This change causes DMLC to report an error on such code.
- forbid-function-in-extern-struct
-
Up to Simics 7, a bug allowed omitting the
*in function pointer members ofextern typedef structdeclarations. For example, the following would compile without errors:extern typedef struct { void m(conf_object_t *); } my_interface_t;This change causes DMLC to report an error on such code. To fix it, use the standard C form:
extern typedef struct { void (*m)(conf_object_t *); } my_interface_t; - forbid-warning-statement
-
Up to Simics 7, the
_warningstatement was allowed, though rarely useful. When this change is enabled,_warningstatements are disallowed. - modern-attributes
-
Up to Simics 7, attributes were registered using the legacy
SIM_register_typed_attributeAPI, which supported the dictionary type ("D" in type strings). When this change is enabled, attributes are registered using the modernSIM_register_attributefamily, and the dictionary type becomes unsupported. Models using dictionary attributes must migrate to another representation, such as a list of two-element lists. E.g., a dict from integers to strings can be represented as an attribute of type[[is]*]instead ofD. The outer list is created usingSIM_alloc_attr_listinstead ofSIM_alloc_attr_dict, and items are added usingSIM_set_attr_list_item(&outer, i, SIM_make_attr_list(2, key, value))rather thanSIM_attr_dict_set_item(&outer, i, key, value). - range-check-method-indices
-
Up to Simics 7, methods defined under object arrays did not validate that indices used when calling the method were within bounds. When this change is enabled, indices are implicitly range checked. If enabling this change causes crashes, then that definitely signifies a bug in your model; a bug that would very likely result in memory corruption if the assertion were not to be made.
- remove-port-proxy-attrs
-
In Simics 5, configuration attributes for
connect,attributeandregisterobjects inside banks and ports were registered on the device object, named likebankname_attrname.When this change is enabled, attributes are only registered on the bank or port object itself. When the change is not yet enabled, ports and banks are also redundantly exposed through a proxy attribute on the device object.
When enabling this change, you can expect that some code that accesses some attributes directly will have to be updated. For instance, if your device has a bank
regswith a registerR, thenyour_dev.regs_R = 4711will give an error, and can be changed into:
your_dev.bank.regs.R = 4711Note
Proxy attributes are only ever created for constructs that were permitted in Simics 5. For instance, attributes under banks inside groups will not get a proxy attribute even when this change is not enabled.
- remove-port-proxy-ifaces
-
Version 5 and earlier of Simics relied on interface ports (as registered by the
SIM_register_port_interfaceAPI function) for exposing the interfaces of ports and banks. In newer versions of Simics, interfaces are instead exposed on separate configuration objects.When this change is enabled, ports and banks will only expose interfaces through the dedicated port object. When this change is not yet enabled, ports and banks are also redundantly exposed through an old-style interface port for compatibility.
When enabling this change, you can expect that initialization of connection attributes of other objects have to be updated. For instance, if your device has a port named
p, thenother_dev.target = [your_dev, "p"]will give an error, and can be changed into:
other_dev.target = your_dev.port.pNote
Interface ports are only ever created for constructs that were permitted in Simics 5. For instance, interfaces in banks inside groups will not get an interface port even when this change is not enabled.
- require-version-statement
-
Up to Simics 7, the version specification statement (
dml 1.4;) at the start of each file was optional, anddml 1.3;was permitted as a deprecated alias fordml 1.4;. When this change is enabled, the version statement becomes mandatory, anddml 1.3;is no longer accepted. - restrict-log-levels
-
Up to Simics 7, log levels for "warning", "error", and "critical" logs could be any integer between 1 and 5, even though it is only meaningful to provide 1 as the primary level and 5 as the subsequent level. When this change is enabled, the primary log level must be 1, and if the subsequent level is provided it must be 5; any other values will be rejected.
- strict-typechecking
-
Up to Simics 7, DMLC's type checking was very lenient compared to GCC, especially for method overrides and uses of
externmacros. When this change is enabled, type checking becomes more strict.The most common type of errors triggered by enabling this change are due to discrepencies between pointer types. In particular, implicitly discarding
const-qualification of a pointer's base type will never be tolerated, andvoidpointers are only considered equivalent with any other pointer type in the same contexts as C.Novel type errors from uses of C macros exposed to DML through
externdeclarations can often be resolved by changing the signature of theexterndeclaration to more accurately reflect the macro's effective type. - vect-needs-provisional
-
Up to Simics 7, the
vectsyntax was permitted without enabling thesimics_util_vectprovisional feature, issuing only a warning in DML 1.4. When this change is enabled, thevectsyntax is forbidden unless the provisional feature is explicitly enabled.