diff --git a/RELEASENOTES.docu b/RELEASENOTES.docu index beda591a..29e0cf0c 100644 --- a/RELEASENOTES.docu +++ b/RELEASENOTES.docu @@ -1795,4 +1795,11 @@ extern typedef struct { } my_type_t; Multiple `extern` declarations for the same identifier are now allowed as long as they all declare the same type for the identifier . + Added the warning WBIGUNROLL + which is emitted when a #select or #foreach statement is + compiled to a large unrolled loop. + This warning is only enabled by default with Simics API version 8 or + above. With version 7 and below it must be explicitly enabledby passing + either --no-compat=suppress_WBIGUNROLL or + --warn=WBIGUNROLL to DMLC. diff --git a/lib/1.2/dml-builtins.dml b/lib/1.2/dml-builtins.dml index f2511de6..5a84327d 100644 --- a/lib/1.2/dml-builtins.dml +++ b/lib/1.2/dml-builtins.dml @@ -215,6 +215,7 @@ template device { parameter _compat_io_memory auto; parameter _compat_shared_logs_on_device auto; parameter _compat_suppress_WLOGMIXUP auto; + parameter _compat_suppress_WBIGUNROLL auto; parameter _compat_legacy_attributes auto; parameter _compat_lenient_typechecking auto; parameter _compat_dml12_inline auto; diff --git a/lib/1.4/dml-builtins.dml b/lib/1.4/dml-builtins.dml index fc1af1bf..53a77309 100644 --- a/lib/1.4/dml-builtins.dml +++ b/lib/1.4/dml-builtins.dml @@ -561,6 +561,7 @@ template device { param _compat_io_memory auto; param _compat_shared_logs_on_device auto; param _compat_suppress_WLOGMIXUP auto; + param _compat_suppress_WBIGUNROLL auto; param _compat_legacy_attributes auto; param _compat_lenient_typechecking auto; param _compat_dml12_inline auto; diff --git a/py/dml/codegen.py b/py/dml/codegen.py index 861ebcc2..8b05a624 100644 --- a/py/dml/codegen.py +++ b/py/dml/codegen.py @@ -3031,12 +3031,19 @@ def stmt_select(stmt, location, scope): else_dead = True break + iterations = len(clauses) if else_dead: (last_cond, last_stmt) = clauses.pop(-1) assert last_cond.constant and last_cond.value if_chain = last_stmt else: if_chain = codegen_statement(else_ast, location, scope) + if iterations > WBIGUNROLL.limit and isinstance(lst, List): + report(WBIGUNROLL(stmt.site, + '#'*(stmt.site.dml_version() != (1, 2)) + + 'select', + iterations)) + for (cond, stmt) in reversed(clauses): if_chain = mkIf(cond.site, cond, stmt, if_chain) return [if_chain] @@ -3162,6 +3169,10 @@ def foreach_constant_list(site, itername, lst, statement, location, scope): stmt) spec.append(mkCompound(site, decls + [stmt])) + if len(spec) > WBIGUNROLL.limit and isinstance(lst, List): + report(WBIGUNROLL(site, + '#'*(site.dml_version() != (1, 2)) + 'foreach', + len(spec))) return [mkUnrolledLoop(site, spec, context.label if context.used else None)] diff --git a/py/dml/compat.py b/py/dml/compat.py index b8945d55..8a264474 100644 --- a/py/dml/compat.py +++ b/py/dml/compat.py @@ -198,6 +198,30 @@ class suppress_WLOGMIXUP(CompatFeature): short = "Suppress the warning 'WLOGMIXUP' by default" last_api_version = api_6 +@feature +class suppress_WBIGUNROLL(CompatFeature): + '''This compatibility feature makes it so the warning `WBIGUNROLL` is + suppressed by default. `WBIGUNROLL` warns about `#foreach` and `#select` + statements that compile to large unrolled loops — for more + information, see the documentation of `WBIGUNROLL` in the + [Messages](messages.html) section. + + `WBIGUNROLL` is suppressed by default below Simics API version 8 in order + to avoid overwhelming users with warnings. Addressing occurences of large + unrolled loops should be done before or as part of migration to Simics API + version 8. + + Passing `--no-compat=suppress_WBUGUNROLL` to DMLC has almost the same effect + as passing `--warn=WBIGUNROLL`; either will cause DMLC to report the warning + even when the Simics API version in use is below 8. The only difference + between these two options is that if `--no-compat=suppress_WBIGUNROLL` is + used (and `--warn=WBIGUNROLL` is not), then `WBIGUNROLL` may still be + explicitly suppressed via `--no-warn=WBIGUNROLL`. In contrast, + `--warn=WBIGUNROLL` doesn't allow for `WBIGUNROLL` to be suppressed at + all.''' + short = "Suppress the warning 'WBIGUNROLL' by default" + last_api_version = api_7 + @feature class legacy_attributes(CompatFeature): '''This compatibility feature makes DMLC register all attributes using the diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py index c1c3b39b..8ce72098 100644 --- a/py/dml/dmlc.py +++ b/py/dml/dmlc.py @@ -599,6 +599,8 @@ def main(argv): if compat.suppress_WLOGMIXUP in dml.globals.enabled_compat: ignore_warning('WLOGMIXUP') + if compat.suppress_WBIGUNROLL in dml.globals.enabled_compat: + ignore_warning('WBIGUNROLL') for w in options.disabled_warnings: if not is_warning_tag(w): diff --git a/py/dml/messages.py b/py/dml/messages.py index 8d7b9cc2..c8e69b6b 100644 --- a/py/dml/messages.py +++ b/py/dml/messages.py @@ -2291,6 +2291,32 @@ class WHOOKSEND(DMLWarning): + "Declarations section in the DML 1.4 reference manual for " + "information about the differences between 'send' and 'send_now'") +class WBIGUNROLL(DMLWarning): + limit = 64 + __doc__ = f""" + A `#select` or `#foreach` statement was specified which caused DMLC to + study the body and generate duplicated C code for it a large number of + times (more than {limit} times.) This can dramatically increase both DMLC + and GCC compile times and code size, if not crash DMLC outright. + + To address this, you have two options: + * Ensure most iterations can be entirely eliminated at compile-time by + DMLC. For `#select`, this can be done by ensuring that the `where` check + will be a constant value (e.g. a constant equality) for most if not all + items of the specified list. For `#foreach`, encase the body in an `#if` + check that is false for most items of the specified list. + * Don't use `#select` or `#foreach`. Represent the specified compile-time + list instead as a `session` array or an `extern`ed C array, and iterate + through it using traditional `for` loops. For more information, see [the + various answers to this Stack Overflow question.](https://stackoverflow.com/questions/75073681/cannot-use-variable-index-in-a-constant-list) + + This warning is only enabled by default with Simics API version 8 or above + (due to the compatibility feature `suppress_WBIGUNROLL`.) + """ + fmt = ("This '%s' statement compiles to an unrolled loop where the loop " + + "body is studied and generated %s times. This may dramatically " + + "increase both DMLC and GCC compilation times, if not crash DMLC " + + "outright!") class PSHA1(PortingMessage): """The `port-dml` script requires that the DML file has not been diff --git a/test/1.2/legacy/T_suppress_WBIGUNROLL_disabled.dml b/test/1.2/legacy/T_suppress_WBIGUNROLL_disabled.dml new file mode 100644 index 00000000..5048af67 --- /dev/null +++ b/test/1.2/legacy/T_suppress_WBIGUNROLL_disabled.dml @@ -0,0 +1,15 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.2; + +device test; + +/// COMPILE-ONLY +/// NO-CC + +/// DMLC-FLAG --no-compat=suppress_WBIGUNROLL + +/// SCAN-FOR-TAGS suppress_WBIGUNROLL.dml +import "suppress_WBIGUNROLL.dml"; diff --git a/test/1.2/legacy/T_suppress_WBIGUNROLL_enabled.dml b/test/1.2/legacy/T_suppress_WBIGUNROLL_enabled.dml new file mode 100644 index 00000000..9f65ffbe --- /dev/null +++ b/test/1.2/legacy/T_suppress_WBIGUNROLL_enabled.dml @@ -0,0 +1,14 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.2; + +device test; + +/// COMPILE-ONLY +/// NO-CC + +/// DMLC-FLAG --simics-api=7 + +import "suppress_WBIGUNROLL.dml"; diff --git a/test/1.2/legacy/suppress_WBIGUNROLL.dml b/test/1.2/legacy/suppress_WBIGUNROLL.dml new file mode 100644 index 00000000..acfa4aa8 --- /dev/null +++ b/test/1.2/legacy/suppress_WBIGUNROLL.dml @@ -0,0 +1,80 @@ +/* + © 2024 Intel Corporation + SPDX-License-Identifier: MPL-2.0 +*/ +dml 1.2; + +// expectations in this file are selectively enabled using SCAN-FOR-TAGS + +data int zero = 0; + +parameter zeroes_64 = [$zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero]; + +// Sixtyfourth zero is constant +parameter zeroes_65 = [$zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, $zero, + $zero, $zero, $zero, $zero, $zero, $zero, $zero, 0, + $zero]; + +bank b { + register regs[i in 0..64] size 4 @ undefined; +} + +method init { + local int nonconstant; + + // no warning + foreach x in ($zeroes_64) assert true; + // The else branch is not considered an iteration + select x in ($zeroes_64) where (x == nonconstant) { + assert true; + } else assert true; + + /// WARNING WBIGUNROLL + foreach x in ($zeroes_65) assert true; + /// WARNING WBIGUNROLL + select x in ($zeroes_65) where (x == nonconstant) { + assert true; + } else assert true; + + // no warning + foreach x in ($zeroes_65) { + // As sixtyfourth bit is constant 0, DML 1.2 semantics eliminates this + // if, and subsequently causes that iteration to be omitted from + // codegen entirely; reducing the total count to 64 + if (x != 0) assert true; + } + + // As sixtyfourth bit is constant 0, select can cut short at it, reducing + // the total number of iterations to 64 + select x in ($zeroes_65) where (x == 0) { + assert true; + } else assert true; + + // As sixtyfourth bit is constant 0, select can omit the check and thus + // iteration for it, reducing the total number of iterations to 64 + select x in ($zeroes_65) where (x != 0) { + assert true; + } else assert true; + + + // WBIGUNROLL is only reported for compile-time lists defined via list + // syntax, not the object lists generated by DMLC + // no warning + foreach x in ($b.unmapped_registers) assert true; + select x in ($b.unmapped_registers) where (x == nonconstant) { + assert true; + } else assert true; +}