diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cbb4542c..0e7eb896 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -204,3 +204,11 @@ (fixes SIMICS-22848). - `release 6 6349` - `release 7 7054` +- `note 6` DMLC no longer emits warnings saying + `top-level 'if' body with unsupported statements`. These warnings were + often triggered in common code, causing excessive polution in build logs. + The same rules as before apply to `#if` statements: Statements such as + `param` and `template` are forbidden inside `#if`, but a special exception + allows forbidden statements to appear specifically inside an `#if (dml_1_2)` + block. The warning message was meant to highlight this irregularity, but + caused more harm than good; error messages surrounding the special case have been improved instead. diff --git a/doc/1.4/language.md b/doc/1.4/language.md index f77bfff8..eb90874a 100644 --- a/doc/1.4/language.md +++ b/doc/1.4/language.md @@ -2318,8 +2318,8 @@ The *object declarations* are any number of declarations of objects, session variables, saved variables, methods, or other `#if` statements, but not parameters, `is` statements, or `in each` statements . When the conditional is `true` (or if it's the else branch of a false conditional), the object -declarations are treated as if they had appeared without any surrounding *#if*. -So the two following declarations are equivalent: +declarations are treated as if they had appeared without any surrounding `#if`. +Thus, the two following snippets are equivalent: ``` #if (true) { @@ -2329,12 +2329,34 @@ So the two following declarations are equivalent: } ``` -is equivalent to - ``` register R size 4; ``` +As a special exception, an `#if` statement that appears on top level is allowed +to contain any type of statement, as long as the condition doesn't reference +any identifiers other than `dml_1_2`, `true` and `false`. This is often useful +while migrating the devices of a system from DML 1.2 to DML 1.4, +as it allows conditional definitions of templates in common code used from both DML 1.2 and DML 1.4. For example, let's say an existing template `reset_to_seven` +is used in DML 1.2 code to set the reset value of a field to 7 in DML 1.2. +The parameters that control reset values have changed from DML 1.2 to DML 1.4, +and one way to handle this is to provide separate template definitions +depending on whether the device uses DML 1.2 or DML 1.4: +``` +#if (dml_1_2) { + template seven is field { + param hard_reset_value = 7; + param soft_reset_value = 7; + } +} #else { + template seven is field { + param init_val = 7; + } +} +``` +Later, when all related devices have been ported to DML 1.4, +the `dml_1_2` clause can be removed. + ## In Each Declarations In Each declarations are a convenient mechanism to apply a diff --git a/py/dml/dmlparse.py b/py/dml/dmlparse.py index a580ae74..76a6edb2 100644 --- a/py/dml/dmlparse.py +++ b/py/dml/dmlparse.py @@ -270,12 +270,12 @@ def toplevel_param(t): def toplevel_if(t): '''toplevel_if : hashif LPAREN expression RPAREN \ LBRACE device_statements RBRACE toplevel_else''' - if all(stmt.kind in allowed_in_hashif for stmt in t[6] + t[8]): - t[0] = ast.hashif(site(t), t[3], t[6], t[8]) + bad_stmts = [stmt for stmt in t[6] + t[8] + if stmt.kind not in allowed_in_hashif] + if bad_stmts: + t[0] = ast.toplevel_if(site(t), t[3], t[6], t[8], bad_stmts) else: - report(WEXPERIMENTAL(site(t), ("top-level 'if' body with unsupported " - + "statements"))) - t[0] = ast.toplevel_if(site(t), t[3], t[6], t[8]) + t[0] = ast.hashif(site(t), t[3], t[6], t[8]) @prod_dml14 def toplevel_else_no(t): diff --git a/py/dml/messages.py b/py/dml/messages.py index e4dc0000..92b8e913 100644 --- a/py/dml/messages.py +++ b/py/dml/messages.py @@ -829,6 +829,18 @@ class ECONDINEACH(DMLError): version = "1.4" fmt = "conditional 'in each' is not allowed" +class EBADCONDSTMT(DMLError): + """ + `#if` statements in object scope are only allowed to contain + certain kinds of declarations: objects, `method`, `session`, + `saved`, `#if`, or `error`. + + A special exception is that a `#if` on top scope may contain any + kind of statement as long as the `#if` condition doesn't reference + any identifiers other than `dml_1_2`, `true`, and `false`. + """ + fmt = "object of type %s not allowed inside `#if`" + # TODO: Consider re-wording the semantics of this error, allocate_type is only # relevant in 1.4 when imported from 1.2, and as per SIMICS-9393 this # error might not even be necessary diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py index 4a69e7f0..7af462a8 100644 --- a/py/dml/toplevel.py +++ b/py/dml/toplevel.py @@ -148,25 +148,29 @@ def scan_statements(filename, site, stmts): [text] = s.args footers.append(ctree.mkCText(s.site, text)) elif s.kind == 'toplevel_if': - [cond, tbranch, fbranch] = s.args + [cond, tbranch, fbranch, bad_stmts] = s.args scope = symtab.Symtab() bsite = SimpleSite('', dml_version=dml.globals.dml_version) # HACK Add constants to scope typically defined by dml-builtins, # which is not accessible here - def add_constant(name, expr): + constants = { + 'dml_1_2': ctree.BoolConstant( + bsite, dml.globals.dml_version == (1, 2)), + 'true': ctree.BoolConstant(bsite, True), + 'false': ctree.BoolConstant(bsite, False), + } + for (name, expr) in constants.items(): scope.add(ctree.ExpressionSymbol(name, expr, bsite)) - add_constant( - 'dml_1_2', - ctree.BoolConstant(bsite, dml.globals.dml_version == (1, 2))) - add_constant('true', ctree.BoolConstant(bsite, True)) - add_constant('false', ctree.BoolConstant(bsite, False)) try: expr = ctree.as_bool(codegen.codegen_expression( cond, None, scope)) if not expr.constant: raise ENCONST(expr.site, expr) + except EIDENT: + for stmt in bad_stmts: + report(EBADCONDSTMT(stmt.site, stmt.kind)) except DMLError as e: report(e) else: diff --git a/test/1.2/misc/T_import_dml14.dml b/test/1.2/misc/T_import_dml14.dml index 93051cde..36378a29 100644 --- a/test/1.2/misc/T_import_dml14.dml +++ b/test/1.2/misc/T_import_dml14.dml @@ -12,7 +12,6 @@ constant x = 0; parameter is_dml_12 = true; -/// WARNING WEXPERIMENTAL dml14.dml /// SCAN-FOR-TAGS dml14.dml import "dml14.dml"; diff --git a/test/1.2/misc/dml14.dml b/test/1.2/misc/dml14.dml index 5e2d8826..1ae19d08 100644 --- a/test/1.2/misc/dml14.dml +++ b/test/1.2/misc/dml14.dml @@ -401,7 +401,6 @@ bank testbank is (miss_pattern_bank, function_mapped_bank) { bank overridden_bank is (unified_hard_reset, unified_soft_reset); -/// WARNING WEXPERIMENTAL #if (dml_1_2) { import "io-memory.dml"; } #else { @@ -475,10 +474,8 @@ attribute woa is (write_only_attr) { param global_sym = 14; -/// WARNING WEXPERIMENTAL #if (!dml_1_2) { param cond = false; -/// WARNING WEXPERIMENTAL } #else #if (!dml_1_2) { error; } #else { diff --git a/test/1.4/errors/T_EBADCONDSTMT.dml b/test/1.4/errors/T_EBADCONDSTMT.dml new file mode 100644 index 00000000..1c8a4b7e --- /dev/null +++ b/test/1.4/errors/T_EBADCONDSTMT.dml @@ -0,0 +1,28 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +constant x = 1; + +#if (x == 1) { +/// ERROR EBADCONDSTMT +param p = 3; +/// ERROR EBADCONDSTMT +typedef int i_t; +} #else { +/// ERROR EBADCONDSTMT +template t { +} + +/// ERROR EBADCONDSTMT +extern typedef int i_t; +} + +#if (true) { +// no error +param p = 3; +} diff --git a/test/1.4/errors/T_ECONDINEACH.dml b/test/1.4/errors/T_ECONDINEACH.dml index ac6ad46c..1ac05735 100644 --- a/test/1.4/errors/T_ECONDINEACH.dml +++ b/test/1.4/errors/T_ECONDINEACH.dml @@ -22,7 +22,6 @@ port p { } } -/// WARNING WEXPERIMENTAL #if (false && true && dml_1_2) { // Experimental support for complex top-level conditionals allows in-each // to be used diff --git a/test/1.4/structure/T_top_hashif.dml b/test/1.4/structure/T_top_hashif.dml new file mode 100644 index 00000000..72ff9e02 --- /dev/null +++ b/test/1.4/structure/T_top_hashif.dml @@ -0,0 +1,51 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +device test; + +/// COMPILE-ONLY + +#if (true) { +extern typedef int a_t; +typedef int b_t; +param p = 1; +} #else { +extern void *x; +import "nonexisting.dml"; +param p = 2; +error; +} + +#if (p != 1) { error; } + +#if (false) { +typedef void *a_t; +extern typedef void *b_t; +error; +template t { error; } +} #else { +extern int x; +import "imported.dml"; +template t { param from_t = true; } +} + +#if (!imported) { error; } + +is t; +#if (!from_t) { error; } + +#if (dml_1_2 || false) { +error; +} #else { + #if (true) { + #if (!false) { + // nested top-level #ifs are permitted + param foo = true; + } + } +} + +#if (!foo) { error; } diff --git a/test/1.4/structure/imported.dml b/test/1.4/structure/imported.dml new file mode 100644 index 00000000..638c5458 --- /dev/null +++ b/test/1.4/structure/imported.dml @@ -0,0 +1,7 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.4; + +param imported = true;