Skip to content

Commit

Permalink
[Doc] Update documentation for no-transitive-change (#96453)
Browse files Browse the repository at this point in the history
(Some backgrounds, not required to read:
https://discourse.llvm.org/t/rfc-c-20-modules-introduce-thin-bmi-and-decls-hash/74755)

This is the document part for the no-transitive-change
(#86912,
#92083,
#92085,
#92511) to provide the ability
for build system to skip some unnecessary recompilations.

See the patch for examples.
  • Loading branch information
ChuanqiXu9 authored Jul 19, 2024
1 parent 55a7be5 commit 6f710fe
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
7 changes: 7 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ here. Generic improvements to Clang as a whole or to its underlying
infrastructure are described first, followed by language-specific
sections with improvements to Clang's support for those languages.

- Implemented improvements to BMIs for C++20 Modules that can reduce
the number of rebuilds during incremental recompilation. We are seeking
feedback from Build System authors and other interested users, especially
when you feel Clang changes the BMI and misses an opportunity to avoid
recompilations or causes correctness issues. See StandardCPlusPlusModules
`StandardCPlusPlusModules <StandardCPlusPlusModules.html>`_ for more details.

- The ``\par`` documentation comment command now supports an optional
argument, which denotes the header of the paragraph started by
an instance of the ``\par`` command comment. The implementation
Expand Down
143 changes: 143 additions & 0 deletions clang/docs/StandardCPlusPlusModules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,149 @@ in the future. The expected roadmap for Reduced BMIs as of Clang 19.x is:
comes, the term BMI will refer to the Reduced BMI and the Full BMI will only
be meaningful to build systems which elect to support two-phase compilation.

Experimental Non-Cascading Changes
----------------------------------

This section is primarily for build system vendors. For end compiler users,
if you don't want to read it all, this is helpful to reduce recompilations.
We encourage build system vendors and end users try this out and bring feedback.

Before Clang 19, a change in BMI of any (transitive) dependency would cause the
outputs of the BMI to change. Starting with Clang 19, changes to non-direct
dependencies should not directly affect the output BMI, unless they affect the
results of the compilations. We expect that there are many more opportunities
for this optimization than we currently have realized and would appreaciate
feedback about missed optimization opportunities. For example,

.. code-block:: c++

// m-partA.cppm
export module m:partA;

// m-partB.cppm
export module m:partB;
export int getB() { return 44; }

// m.cppm
export module m;
export import :partA;
export import :partB;

// useBOnly.cppm
export module useBOnly;
import m;
export int B() {
return getB();
}

// Use.cc
import useBOnly;
int get() {
return B();
}

To compile the project (for brevity, some commands are omitted.):

.. code-block:: console
$ clang++ -std=c++20 m-partA.cppm --precompile -o m-partA.pcm
$ clang++ -std=c++20 m-partB.cppm --precompile -o m-partB.pcm
$ clang++ -std=c++20 m.cppm --precompile -o m.pcm -fprebuilt-module-path=.
$ clang++ -std=c++20 useBOnly.cppm --precompile -o useBOnly.pcm -fprebuilt-module-path=.
$ md5sum useBOnly.pcm
07656bf4a6908626795729295f9608da useBOnly.pcm
If the interface of ``m-partA.cppm`` is changed to:

.. code-block:: c++

// m-partA.v1.cppm
export module m:partA;
export int getA() { return 43; }

and the BMI for ``useBOnly`` is recompiled as in:

.. code-block:: console
$ clang++ -std=c++20 m-partA.cppm --precompile -o m-partA.pcm
$ clang++ -std=c++20 m-partB.cppm --precompile -o m-partB.pcm
$ clang++ -std=c++20 m.cppm --precompile -o m.pcm -fprebuilt-module-path=.
$ clang++ -std=c++20 useBOnly.cppm --precompile -o useBOnly.pcm -fprebuilt-module-path=.
$ md5sum useBOnly.pcm
07656bf4a6908626795729295f9608da useBOnly.pcm
then the contents of ``useBOnly.pcm`` remain unchanged.
Consequently, if the build system only bases recompilation decisions on directly imported modules,
it becomes possible to skip the recompilation of ``Use.cc``.
It should be fine because the altered interfaces do not affect ``Use.cc`` in any way;
the changes do not cascade.

When ``Clang`` generates a BMI, it records the hash values of all potentially contributory BMIs
for the BMI being produced. This ensures that build systems are not required to consider
transitively imported modules when deciding whether to recompile.

What is considered to be a potential contributory BMIs is currently unspecified.
However, it is a severe bug for a BMI to remain unchanged following an observable change
that affects its consumers.

Build systems may utilize this optimization by doing an update-if-changed operation to the BMI
that is consumed from the BMI that is output by the compiler.

We encourage build systems to add an experimental mode that
reuses the cached BMI when **direct** dependencies did not change,
even if **transitive** dependencies did change.

Given there are potential compiler bugs, we recommend that build systems
support this feature as a configurable option so that users
can go back to the transitive change mode safely at any time.

Interactions with Reduced BMI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

With reduced BMI, non-cascading changes can be more powerful. For example,

.. code-block:: c++

// A.cppm
export module A;
export int a() { return 44; }

// B.cppm
export module B;
import A;
export int b() { return a(); }

.. code-block:: console
$ clang++ -std=c++20 A.cppm -c -fmodule-output=A.pcm -fexperimental-modules-reduced-bmi -o A.o
$ clang++ -std=c++20 B.cppm -c -fmodule-output=B.pcm -fexperimental-modules-reduced-bmi -o B.o -fmodule-file=A=A.pcm
$ md5sum B.pcm
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
And let's change the implementation for ``A.cppm`` into:

.. code-block:: c++

export module A;
int a_impl() { return 99; }
export int a() { return a_impl(); }

and recompile the example:

.. code-block:: console
$ clang++ -std=c++20 A.cppm -c -fmodule-output=A.pcm -fexperimental-modules-reduced-bmi -o A.o
$ clang++ -std=c++20 B.cppm -c -fmodule-output=B.pcm -fexperimental-modules-reduced-bmi -o B.o -fmodule-file=A=A.pcm
$ md5sum B.pcm
6c2bd452ca32ab418bf35cd141b060b9 B.pcm
We should find the contents of ``B.pcm`` remains the same. In this case, the build system is
allowed to skip recompilations of TUs which solely and directly depend on module ``B``.

This only happens with a reduced BMI. With reduced BMIs, we won't record the function body
of ``int b()`` in the BMI for ``B`` so that the module ``A`` doesn't contribute to the BMI of ``B``
and we have less dependencies.

Performance Tips
----------------

Expand Down

0 comments on commit 6f710fe

Please sign in to comment.