`6.3.1 from High Integrity C++ <http://www.codingstandard.com/rule/6-3-1-ensure-that-the-labels-for-a-jump-statement-or-a-switch-condition-appear-later-in-the-same-or-an-enclosing-block/>`_.
`6.3.1 from High Integrity C++ <http://www.codingstandard.com/rule/6-3-1-ensure-that-the-labels-for-a-jump-statement-or-a-switch-condition-appear-later-in-the-same-or-an-enclosing-block/>`_.
-For more information on why to avoid programming
+For more information on why to avoid programming
with ``goto`` you can read the famous paper `A Case against the GO TO Statement. <https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF>`_.
The check diagnoses ``goto`` for backward jumps in every language mode. These
with ``goto`` you can read the famous paper `A Case against the GO TO Statement. <https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF>`_.
The check diagnoses ``goto`` for backward jumps in every language mode. These
-This check implements the type-based semantics of ``gsl::owner<T*>``, which allows
-static analysis on code, that uses raw pointers to handle resources like
+This check implements the type-based semantics of ``gsl::owner<T*>``, which allows
+static analysis on code, that uses raw pointers to handle resources like
dynamic memory, but won't introduce RAII concepts.
The relevant sections in the `C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md>`_ are I.11, C.33, R.3 and GSL.Views
dynamic memory, but won't introduce RAII concepts.
The relevant sections in the `C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md>`_ are I.11, C.33, R.3 and GSL.Views
@@ -20,7+20,7 @@ the `Guideline Support Library <https://github.com/isocpp/CppCoreGuidelines/blob
All checks are purely type based and not (yet) flow sensitive.
The following examples will demonstrate the correct and incorrect initializations
All checks are purely type based and not (yet) flow sensitive.
The following examples will demonstrate the correct and incorrect initializations
-of owners, assignment is handled the same way. Note that both ``new`` and
+of owners, assignment is handled the same way. Note that both ``new`` and
``malloc()``-like resource functions are considered to produce resources.
.. code-block:: c++
``malloc()``-like resource functions are considered to produce resources.
Rule `6.3.1 High Integrity C++ <http://www.codingstandard.com/rule/6-3-1-ensure-that-the-labels-for-a-jump-statement-or-a-switch-condition-appear-later-in-the-same-or-an-enclosing-block/>`_
Rule `6.3.1 High Integrity C++ <http://www.codingstandard.com/rule/6-3-1-ensure-that-the-labels-for-a-jump-statement-or-a-switch-condition-appear-later-in-the-same-or-an-enclosing-block/>`_
-requires that ``goto`` only skips parts of a block and is not used for other
+requires that ``goto`` only skips parts of a block and is not used for other
reasons.
Both coding guidelines implement the same exception to the usage of ``goto``.
reasons.
Both coding guidelines implement the same exception to the usage of ``goto``.
@@ -9,7+9,7 @@ The `rule 6.1.2 <http://www.codingstandard.com/rule/6-1-2-explicitly-cover-all-p
and `rule 6.1.4 <http://www.codingstandard.com/rule/6-1-4-ensure-that-a-switch-statement-has-at-least-two-case-labels-distinct-from-the-default-label/>`_
of the High Integrity C++ Coding Standard are enforced.
and `rule 6.1.4 <http://www.codingstandard.com/rule/6-1-4-ensure-that-a-switch-statement-has-at-least-two-case-labels-distinct-from-the-default-label/>`_
of the High Integrity C++ Coding Standard are enforced.
-``if-else if`` chains that miss a final ``else`` branch might lead to unexpected
+``if-else if`` chains that miss a final ``else`` branch might lead to unexpected
program execution and be the result of a logical error.
If the missing ``else`` branch is intended you can leave it empty with a clarifying
comment.
program execution and be the result of a logical error.
If the missing ``else`` branch is intended you can leave it empty with a clarifying
comment.
@@ -20,10+20,10 @@ This warning can be noisy on some code bases, so it is disabled by default.
void f1() {
int i = determineTheNumber();
void f1() {
int i = determineTheNumber();
- if(i > 0) {
- // Some Calculation
- } else if (i < 0) {
- // Precondition violated or something else.
+ if(i > 0) {
+ // Some Calculation
+ } else if (i < 0) {
+ // Precondition violated or something else.
}
// ...
}
}
// ...
}
@@ -72,16+72,16 @@ Degenerated ``switch`` statements without any labels are caught as well.
}
// Should rather be the following:
}
// Should rather be the following:
- if (i == 1) {
- // do something here
+ if (i == 1) {
+ // do something here
}
}
- else {
- // do something here
+ else {
+ // do something here
}
.. code-block:: c++
}
.. code-block:: c++
-
+
// A completely degenerated switch will be diagnosed.
int i = 42;
switch(i) {}
// A completely degenerated switch will be diagnosed.
@@ -25,7+25,7 @@ Both of these examples will be replaced with:
The second example will also receive a warning that ``randomFunc`` is no longer supported in the same way as before so if the user wants the same functionality, the user will need to change the implementation of the ``randomFunc``.
The second example will also receive a warning that ``randomFunc`` is no longer supported in the same way as before so if the user wants the same functionality, the user will need to change the implementation of the ``randomFunc``.
-One thing to be aware of here is that ``std::random_device`` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere.
+One thing to be aware of here is that ``std::random_device`` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere.
Another thing is that the seeding quality of the suggested fix is quite poor: ``std::mt19937`` has an internal state of 624 32-bit integers, but is only seeded with a single integer. So if you require
higher quality randomness, you should consider seeding better, for example:
Another thing is that the seeding quality of the suggested fix is quite poor: ``std::mt19937`` has an internal state of 624 32-bit integers, but is only seeded with a single integer. So if you require
higher quality randomness, you should consider seeding better, for example:
FileName "(file)" StringRef The name of the file being included, as written in the source code.
RecoveryPath (path) SmallVectorImpl<char> If this client indicates that it can recover from this missing file, the client should set this as an additional header search patch.
FileName "(file)" StringRef The name of the file being included, as written in the source code.
RecoveryPath (path) SmallVectorImpl<char> If this client indicates that it can recover from this missing file, the client should set this as an additional header search patch.
@@ -228,7+228,7 @@ InclusionDirective is called when an inclusion directive of any kind (#include</
In the above interleaved layout, each virtual table's offset-to-top and RTTI are always adjacent, which shows that the layout has the first property.
For the second property, let us look at f2 as an example. In the interleaved layout,
-there are two entries for f2: B::f2 and D::f2. The distance between &B::f2
+there are two entries for f2: B::f2 and D::f2. The distance between &B::f2
and its address point D::offset-to-top (the entry immediately after &B::rtti) is 5 entry-length, so is the distance between &D::f2 and C::offset-to-top (the entry immediately after &D::rtti).
Forward-Edge CFI for Indirect Function Calls
and its address point D::offset-to-top (the entry immediately after &B::rtti) is 5 entry-length, so is the distance between &D::f2 and C::offset-to-top (the entry immediately after &D::rtti).
@@ -33,12+33,12 @@ The ``#include`` mechanism provided by the C preprocessor is a very poor way to
code into headers.
* **Fragility**: ``#include`` directives are treated as textual
code into headers.
* **Fragility**: ``#include`` directives are treated as textual
- inclusion by the preprocessor, and are therefore subject to any
- active macro definitions at the time of inclusion. If any of the
- active macro definitions happens to collide with a name in the
- library, it can break the library API or cause compilation failures
- in the library header itself. For an extreme example,
- ``#define std "The C++ Standard"`` and then include a standard
+ inclusion by the preprocessor, and are therefore subject to any
+ active macro definitions at the time of inclusion. If any of the
+ active macro definitions happens to collide with a name in the
+ library, it can break the library API or cause compilation failures
+ in the library header itself. For an extreme example,
+ ``#define std "The C++ Standard"`` and then include a standard
library header: the result is a horrific cascade of failures in the
C++ Standard Library's implementation. More subtle real-world
problems occur when the headers for two different libraries interact
library header: the result is a horrific cascade of failures in the
C++ Standard Library's implementation. More subtle real-world
problems occur when the headers for two different libraries interact
@@ -158,7+158,7 @@ Module maps are specified as separate files (each named ``module.modulemap``) al
.. note::
To actually see any benefits from modules, one first has to introduce module maps for the underlying C standard library and the libraries and headers on which it depends. The section `Modularizing a Platform`_ describes the steps one must take to write these module maps.
.. note::
To actually see any benefits from modules, one first has to introduce module maps for the underlying C standard library and the libraries and headers on which it depends. The section `Modularizing a Platform`_ describes the steps one must take to write these module maps.
-
+
One can use module maps without modules to check the integrity of the use of header files. To do this, use the ``-fimplicit-module-maps`` option instead of the ``-fmodules`` option, or use ``-fmodule-map-file=`` option to explicitly specify the module map files to load.
Compilation model
One can use module maps without modules to check the integrity of the use of header files. To do this, use the ``-fimplicit-module-maps`` option instead of the ``-fmodules`` option, or use ``-fmodule-map-file=`` option to explicitly specify the module map files to load.
* ``<stdio.h>`` defines a macro ``getc`` (and exports its ``#define``)
* ``<cstdio>`` imports the ``<stdio.h>`` module and undefines the macro (and exports its ``#undef``)
* ``<stdio.h>`` defines a macro ``getc`` (and exports its ``#define``)
* ``<cstdio>`` imports the ``<stdio.h>`` module and undefines the macro (and exports its ``#undef``)
-
+
The ``#undef`` overrides the ``#define``, and a source file that imports both modules *in any order* will not see ``getc`` defined as a macro.
Module Map Language
The ``#undef`` overrides the ``#define``, and a source file that imports both modules *in any order* will not see ``getc`` defined as a macro.
Module Map Language
@@ -447,7+447,7 @@ As an example, the module map file for the C standard library might look a bit l
// ...more headers follow...
}
// ...more headers follow...
}
-Here, the top-level module ``std`` encompasses the whole C standard library. It has a number of submodules containing different parts of the standard library: ``complex`` for complex numbers, ``ctype`` for character types, etc. Each submodule lists one of more headers that provide the contents for that submodule. Finally, the ``export *`` command specifies that anything included by that submodule will be automatically re-exported.
+Here, the top-level module ``std`` encompasses the whole C standard library. It has a number of submodules containing different parts of the standard library: ``complex`` for complex numbers, ``ctype`` for character types, etc. Each submodule lists one of more headers that provide the contents for that submodule. Finally, the ``export *`` command specifies that anything included by that submodule will be automatically re-exported.
Lexical structure
-----------------
Lexical structure
-----------------
@@ -646,7+646,7 @@ A header with the ``umbrella`` specifier is called an umbrella header. An umbrel
.. note::
Any headers not included by the umbrella header should have
.. note::
Any headers not included by the umbrella header should have
- explicit ``header`` declarations. Use the
+ explicit ``header`` declarations. Use the
``-Wincomplete-umbrella`` warning option to ask Clang to complain
about headers not covered by the umbrella header or the module map.
``-Wincomplete-umbrella`` warning option to ask Clang to complain
about headers not covered by the umbrella header or the module map.
@@ -691,7+691,7 @@ An umbrella directory declaration specifies that all of the headers in the speci
*umbrella-dir-declaration*:
``umbrella`` *string-literal*
*umbrella-dir-declaration*:
``umbrella`` *string-literal*
-
+
The *string-literal* refers to a directory. When the module is built, all of the header files in that directory (and its subdirectories) are included in the module.
An *umbrella-dir-declaration* shall not refer to the same directory as the location of an umbrella *header-declaration*. In other words, only a single kind of umbrella can be specified for a given directory.
The *string-literal* refers to a directory. When the module is built, all of the header files in that directory (and its subdirectories) are included in the module.
An *umbrella-dir-declaration* shall not refer to the same directory as the location of an umbrella *header-declaration*. In other words, only a single kind of umbrella can be specified for a given directory.
@@ -719,7+719,7 @@ A *submodule-declaration* that is an *inferred-submodule-declaration* describes
@@ -729,9+729,9 @@ For each header included by the umbrella header or in the umbrella directory tha
* Have the same name as the header (without the file extension)
* Have the ``explicit`` specifier, if the *inferred-submodule-declaration* has the ``explicit`` specifier
* Have the same name as the header (without the file extension)
* Have the ``explicit`` specifier, if the *inferred-submodule-declaration* has the ``explicit`` specifier
-* Have the ``framework`` specifier, if the
+* Have the ``framework`` specifier, if the
*inferred-submodule-declaration* has the ``framework`` specifier
*inferred-submodule-declaration* has the ``framework`` specifier
-* Have the attributes specified by the \ *inferred-submodule-declaration*
+* Have the attributes specified by the \ *inferred-submodule-declaration*
* Contain a single *header-declaration* naming that header
* Contain a single *export-declaration* ``export *``, if the \ *inferred-submodule-declaration* contains the \ *inferred-submodule-member* ``export *``
* Contain a single *header-declaration* naming that header
* Contain a single *export-declaration* ``export *``, if the \ *inferred-submodule-declaration* contains the \ *inferred-submodule-member* ``export *``
@@ -914,11+914,11 @@ Each *identifier* in the *config-macro-list* specifies the name of a macro. The
A *config-macros-declaration* shall only be present on a top-level module, i.e., a module that is not nested within an enclosing module.
A *config-macros-declaration* shall only be present on a top-level module, i.e., a module that is not nested within an enclosing module.
-The ``exhaustive`` attribute specifies that the list of macros in the *config-macros-declaration* is exhaustive, meaning that no other macro definition is intended to have an effect on the API of that module.
+The ``exhaustive`` attribute specifies that the list of macros in the *config-macros-declaration* is exhaustive, meaning that no other macro definition is intended to have an effect on the API of that module.
.. note::
.. note::
- The ``exhaustive`` attribute implies that any macro definitions
+ The ``exhaustive`` attribute implies that any macro definitions
for macros not listed as configuration macros should be ignored
completely when building the module. As an optimization, the
compiler could reduce the number of unique module variants by not
for macros not listed as configuration macros should be ignored
completely when building the module. As an optimization, the
compiler could reduce the number of unique module variants by not
@@ -1062,7+1062,7 @@ When writing a private module as part of a *framework*, it's recommended that:
Modularizing a Platform
=======================
Modularizing a Platform
=======================
-To get any benefit out of modules, one needs to introduce module maps for software libraries starting at the bottom of the stack. This typically means introducing a module map covering the operating system's headers and the C standard library headers (in ``/usr/include``, for a Unix system).
+To get any benefit out of modules, one needs to introduce module maps for software libraries starting at the bottom of the stack. This typically means introducing a module map covering the operating system's headers and the C standard library headers (in ``/usr/include``, for a Unix system).
The module maps will be written using the `module map language`_, which provides the tools necessary to describe the mapping between headers and modules. Because the set of headers differs from one system to the next, the module map will likely have to be somewhat customized for, e.g., a particular distribution and version of the operating system. Moreover, the system headers themselves may require some modification, if they exhibit any anti-patterns that break modules. Such common patterns are described below.
The module maps will be written using the `module map language`_, which provides the tools necessary to describe the mapping between headers and modules. Because the set of headers differs from one system to the next, the module map will likely have to be somewhat customized for, e.g., a particular distribution and version of the operating system. Moreover, the system headers themselves may require some modification, if they exhibit any anti-patterns that break modules. Such common patterns are described below.
@@ -27,7+27,7 @@ Explicit cast from nullable to nonnul:
anotherTakesNonNull(bar); // would be great to warn here, but not necessary(*)
Because bar corresponds to the same symbol all the time it is not easy to implement the checker that way the cast only suppress the first call but not the second. For this reason in the first implementation after a contradictory cast happens, I will treat bar as nullable unspecified, this way all of the warnings will be suppressed. Treating the symbol as nullable unspecified also has an advantage that in case the takesNonNull function body is being inlined, the will be no warning, when the symbol is dereferenced. In case I have time after the initial version I might spend additional time to try to find a more sophisticated solution, in which we would produce the second warning (*).
anotherTakesNonNull(bar); // would be great to warn here, but not necessary(*)
Because bar corresponds to the same symbol all the time it is not easy to implement the checker that way the cast only suppress the first call but not the second. For this reason in the first implementation after a contradictory cast happens, I will treat bar as nullable unspecified, this way all of the warnings will be suppressed. Treating the symbol as nullable unspecified also has an advantage that in case the takesNonNull function body is being inlined, the will be no warning, when the symbol is dereferenced. In case I have time after the initial version I might spend additional time to try to find a more sophisticated solution, in which we would produce the second warning (*).
-
+
**2) nonnull**
* Dereferencing a nonnull, or sending message to it is ok.
**2) nonnull**
* Dereferencing a nonnull, or sending message to it is ok.
@@ -77,11+77,11 @@ A symbol may need to be treated differently inside an inlined body. For example,
id obj = getNonnull();
takesNullable(obj);
takesNonnull(obj);
id obj = getNonnull();
takesNullable(obj);
takesNonnull(obj);
-
+
void takesNullable(nullable id obj) {
obj->ivar // we should assume obj is nullable and warn here
}
void takesNullable(nullable id obj) {
obj->ivar // we should assume obj is nullable and warn here
}
-
+
With no special treatment, when the takesNullable is inlined the analyzer will not warn when the obj symbol is dereferenced. One solution for this is to reanalyze takesNullable as a top level function to get possible violations. The alternative method, deducing nullability information from the arguments after inlining is not robust enough (for example there might be more parameters with different nullability, but in the given path the two parameters might end up being the same symbol or there can be nested functions that take different view of the nullability of the same symbol). So the symbol will remain nonnull to avoid false positives but the functions that takes nullable parameters will be analyzed separately as well without inlining.
Annotations on multi level pointers
With no special treatment, when the takesNullable is inlined the analyzer will not warn when the obj symbol is dereferenced. One solution for this is to reanalyze takesNullable as a top level function to get possible violations. The alternative method, deducing nullability information from the arguments after inlining is not robust enough (for example there might be more parameters with different nullability, but in the given path the two parameters might end up being the same symbol or there can be nested functions that take different view of the nullability of the same symbol). So the symbol will remain nonnull to avoid false positives but the functions that takes nullable parameters will be analyzed separately as well without inlining.
@@ -54,7+54,7 @@ A "little endian" layout has the least significant byte first (lowest in memory
.. figure:: ARM-BE-ldr.png
:align: right
.. figure:: ARM-BE-ldr.png
:align: right
-
+
Big endian vector load using ``LDR``.
Big endian vector load using ``LDR``.
@@ -82,7+82,7 @@ Because ``LD1 == LDR + REV`` and similarly ``LDR == LD1 + REV`` (on a big endian
.. container:: clearer
Note that throughout this section we only mention loads. Stores have exactly the same problems as their associated loads, so have been skipped for brevity.
.. container:: clearer
Note that throughout this section we only mention loads. Stores have exactly the same problems as their associated loads, so have been skipped for brevity.
- 1. Predicate ``LDR`` and ``STR`` instructions so that they are never allowed to be selected to generate vector loads and stores. The exception is one-lane vectors [1]_ - these by definition cannot have lane ordering problems so are fine to use ``LDR``/``STR``.
+ 1. Predicate ``LDR`` and ``STR`` instructions so that they are never allowed to be selected to generate vector loads and stores. The exception is one-lane vectors [1]_ - these by definition cannot have lane ordering problems so are fine to use ``LDR``/``STR``.
2. Create code generation patterns for bitconverts that create ``REV`` instructions.
2. Create code generation patterns for bitconverts that create ``REV`` instructions.
@@ -191,7+191,7 @@ For the previous example, this would be::
LD1 v0.4s, [x]
LD1 v0.4s, [x]
- REV64 v0.4s, v0.4s // There is no REV128 instruction, so it must be synthesizedcd
+ REV64 v0.4s, v0.4s // There is no REV128 instruction, so it must be synthesizedcd
EXT v0.16b, v0.16b, v0.16b, #8 // with a REV64 then an EXT to swap the two 64-bit elements.
REV64 v0.2d, v0.2d
EXT v0.16b, v0.16b, v0.16b, #8 // with a REV64 then an EXT to swap the two 64-bit elements.
REV64 v0.2d, v0.2d
@@ -202,4+202,3 @@ For the previous example, this would be::
It turns out that these ``REV`` pairs can, in almost all cases, be squashed together into a single ``REV``. For the example above, a ``REV128 4s`` + ``REV128 2d`` is actually a ``REV64 4s``, as shown in the figure on the right.
.. [1] One lane vectors may seem useless as a concept but they serve to distinguish between values held in general purpose registers and values held in NEON/VFP registers. For example, an ``i64`` would live in an ``x`` register, but ``<1 x i64>`` would live in a ``d`` register.
It turns out that these ``REV`` pairs can, in almost all cases, be squashed together into a single ``REV``. For the example above, a ``REV128 4s`` + ``REV128 2d`` is actually a ``REV64 4s``, as shown in the figure on the right.
.. [1] One lane vectors may seem useless as a concept but they serve to distinguish between values held in general purpose registers and values held in NEON/VFP registers. For example, an ``i64`` would live in an ``x`` register, but ``<1 x i64>`` would live in a ``d`` register.
<suspend> // returns a coroutine handle on first suspend
for(;;) {
print(n++);
<suspend> // returns a coroutine handle on first suspend
- }
- }
+ }
+ }
This coroutine calls some function `print` with value `n` as an argument and
This coroutine calls some function `print` with value `n` as an argument and
-suspends execution. Every time this coroutine resumes, it calls `print` again with an argument one bigger than the last time. This coroutine never completes by itself and must be destroyed explicitly. If we use this coroutine with
-a `main` shown in the previous section. It will call `print` with values 4, 5
+suspends execution. Every time this coroutine resumes, it calls `print` again with an argument one bigger than the last time. This coroutine never completes by itself and must be destroyed explicitly. If we use this coroutine with
+a `main` shown in the previous section. It will call `print` with values 4, 5
and 6 after which the coroutine will be destroyed.
The LLVM IR for this coroutine looks like this:
and 6 after which the coroutine will be destroyed.
The LLVM IR for this coroutine looks like this:
@@ -309,28+309,28 @@ The LLVM IR for this coroutine looks like this:
}
The `entry` block establishes the coroutine frame. The `coro.size`_ intrinsic is
}
The `entry` block establishes the coroutine frame. The `coro.size`_ intrinsic is
-lowered to a constant representing the size required for the coroutine frame.
-The `coro.begin`_ intrinsic initializes the coroutine frame and returns the
-coroutine handle. The second parameter of `coro.begin` is given a block of memory
+lowered to a constant representing the size required for the coroutine frame.
+The `coro.begin`_ intrinsic initializes the coroutine frame and returns the
+coroutine handle. The second parameter of `coro.begin` is given a block of memory
to be used if the coroutine frame needs to be allocated dynamically.
The `coro.id`_ intrinsic serves as coroutine identity useful in cases when the
to be used if the coroutine frame needs to be allocated dynamically.
The `coro.id`_ intrinsic serves as coroutine identity useful in cases when the
-`coro.begin`_ intrinsic get duplicated by optimization passes such as
+`coro.begin`_ intrinsic get duplicated by optimization passes such as
jump-threading.
jump-threading.
-The `cleanup` block destroys the coroutine frame. The `coro.free`_ intrinsic,
+The `cleanup` block destroys the coroutine frame. The `coro.free`_ intrinsic,
given the coroutine handle, returns a pointer of the memory block to be freed or
given the coroutine handle, returns a pointer of the memory block to be freed or
-`null` if the coroutine frame was not allocated dynamically. The `cleanup`
+`null` if the coroutine frame was not allocated dynamically. The `cleanup`
block is entered when coroutine runs to completion by itself or destroyed via
call to the `coro.destroy`_ intrinsic.
block is entered when coroutine runs to completion by itself or destroyed via
call to the `coro.destroy`_ intrinsic.
-The `suspend` block contains code to be executed when coroutine runs to
-completion or suspended. The `coro.end`_ intrinsic marks the point where
-a coroutine needs to return control back to the caller if it is not an initial
-invocation of the coroutine.
+The `suspend` block contains code to be executed when coroutine runs to
+completion or suspended. The `coro.end`_ intrinsic marks the point where
+a coroutine needs to return control back to the caller if it is not an initial
+invocation of the coroutine.
-The `loop` blocks represents the body of the coroutine. The `coro.suspend`_
-intrinsic in combination with the following switch indicates what happens to
-control flow when a coroutine is suspended (default case), resumed (case 0) or
+The `loop` blocks represents the body of the coroutine. The `coro.suspend`_
+intrinsic in combination with the following switch indicates what happens to
+control flow when a coroutine is suspended (default case), resumed (case 0) or
@@ -27,12+27,12 @@ def-use dependency between them into larger nodes that contain multiple-
instructions.
As described in [1]_ the DDG uses graph abstraction to group nodes
instructions.
As described in [1]_ the DDG uses graph abstraction to group nodes
-that are part of a strongly connected component of the graph
+that are part of a strongly connected component of the graph
into special nodes called pi-blocks. pi-blocks represent cycles of data
dependency that prevent reordering transformations. Since any strongly
connected component of the graph is a maximal subgraph of all the nodes
that form a cycle, pi-blocks are at most one level deep. In other words,
into special nodes called pi-blocks. pi-blocks represent cycles of data
dependency that prevent reordering transformations. Since any strongly
connected component of the graph is a maximal subgraph of all the nodes
that form a cycle, pi-blocks are at most one level deep. In other words,
-no pi-blocks are nested inside another pi-block, resulting in a
+no pi-blocks are nested inside another pi-block, resulting in a
hierarchical representation that is at most one level deep.
hierarchical representation that is at most one level deep.
@@ -130,7+130,7 @@ The current implementation of DDG differs slightly from the dependence
graph described in [1]_ in the following ways:
1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the ``store`` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the ``store`` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through ``LoopAnalysis``.
graph described in [1]_ in the following ways:
1. The graph nodes in the paper represent three main program components, namely *assignment statements*, *for loop headers* and *while loop headers*. In this implementation, DDG nodes naturally represent LLVM IR instructions. An assignment statement in this implementation typically involves a node representing the ``store`` instruction along with a number of individual nodes computing the right-hand-side of the assignment that connect to the ``store`` node via a def-use edge. The loop header instructions are not represented as special nodes in this implementation because they have limited uses and can be easily identified, for example, through ``LoopAnalysis``.
- 2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
+ 2. The paper describes five types of dependency edges between nodes namely *loop dependency*, *flow-*, *anti-*, *output-*, and *input-* dependencies. In this implementation *memory* edges represent the *flow-*, *anti-*, *output-*, and *input-* dependencies. However, *loop dependencies* are not made explicit, because they mainly represent association between a loop structure and the program elements inside the loop and this association is fairly obvious in LLVM IR itself.
3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.
3. The paper describes two types of pi-blocks; *recurrences* whose bodies are SCCs and *IN* nodes whose bodies are not part of any SCC. In this implementation, pi-blocks are only created for *recurrences*. *IN* nodes remain as simple DDG nodes in the graph.