C/C++ projects can benefit from using precompiled headers to improvecompile time. GCC addedsupport for precompiled headers in 2003 (version 3.4), andthe current documentation can be found at https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html.
This article focuses on Clang precompiled headers (PCH). Let's beginwith an example.
1 | cat > a1.cc <<'eof' |
We compile b.hh
using -c
, just like wewould compile a non-header file. Clang parses the file, performssemantic analysis, and writes the precompiled header (as a serializedAST file) into b.hh.pch
.
When compiling a.cc
, we use -include-pch
asa prefix header. This means that the translation unit will get twob.h
copies: one from b.hh.pch
and one from thetextual b.hh
. The same applies to a1.cc
. Toavoid a redefinition of 'fb'
error, b.hh
should have a header guard or use #pragma once
.
Now, let's examine the steps in detail.
Given a header file as input, Clang determines the input type aseither c-header
(.h
) orc++-header
(.hh
/.hpp
/.hxx
) based on the fileextension.
For compilation actions, either clang
orclang++
can be used. If we treat .h
as a C++header, we need to specify -xc++-header
(e.g.,clang -c -xc++-header b.h -o b.h.pcm
). (It's worth notingthat the behavior of clang++ -c a.h
is deprecated. Otherthan that, the only significant difference between clang
and clang++
is the linking process, specifically whetherthe C++ standard library is linked.)
When the input type is c-header
orc++-header
, Clang Driver selects the -emit-pch
frontend action. (Note:c++-user-header
/c++-system-header
are used forC++ modules and have different functionality.)
Conventionally, the extension used for Clang precompiled headers is.pch
(similar to MSVC). However, to match GCC, when the-o
option is omitted, the default output file isinput_file + ".gch"
(seeDriver::GetNamedOutputPath
).
The frontend parses the file, performs semantic analysis, and writesthe precompiled header (as a serialized AST file) (seePCHGenerator
). For the serialized format, refer to Precompiled Headerand Modules Internals.
-include-pch b.hh.pch
(PreprocessorOptions::ImplicitPCHInclude
) loads theprecompiled header b.hh.pch
as a prefix header.
We can also write -include b.hh
, and Clang will probeb.hh.pch
/b.hh.gch
and use the file if present.This is a behavior ported from GCC.
-include-pch
may specify a directory. Clang will searchfor a suitable precompiled header in the directory (seeASTReader::isAcceptableASTFile
). The directory may containprecompiled headers for different compiler options. This is anotherbehavior ported from GCC.
1 | echo 'extern int X;' > d.hh |
1 | % clang++ -c -DX=z -include d.hh e.cc |
When we generate and use a precompiled header with different compileroptions, the behavior will be a combination of those options.Consequently, the behavior of -include b.hh
may differdepending on the presence ofb.hh.pch
/b.hh.gch
.
To identify this common pitfall, Clang performs PCH validation (seePCHValidator
) to check for inconsistent options, similar tohow MSVC handles it. The validated options include those that can affectAST generation, such as language options (-std=
), targetoptions (-triple
), file system options, header searchoptions, and preprocessor options.
Modules employ the same validation mechanism, but PCH validation isstricter (!AllowCompatibleConfigurationMismatch
). Thismeans thatCOMPATIBLE_LANGOPT
/COMPATIBLE_ENUM_LANGOPT
/COMPATIBLE_VALUE_LANGOPT
options (e.g., whether the built-in macro __OPTIMIZE__
isdefined) must match as well.
If one side of the precompiled header and the user code are compiledwith the -D
option, the other side should either use thesame -D
option or omit it entirely.
1 | clang -c -xc++-header b.h -o b.pch -DB=1 |
In order to achieve better performance, it is possible to makecertain compromises on properties such as language standardconformance.
-fpch-instantiate-templates
-fpch-instantiate-templates
allows pending template instantiations to be performed in the PCH file.This means that these instantiations do not need to be repeated in everytranslation unit that includes the PCH file. This optimization cansignificantly improve the speed of certain projects. However, the optionchanges the instantiation points of certain function templates, which isnon-conforming. Nevertheless, the altered behavior is generally harmlessin most cases.
1 |
|
1 | % clang++ -c -xc++-header a.cc -o a.pch |
Modular code generation was initially implemented. It waslater extended to support precompiled headers by https://reviews.llvm.org/D69778. To utilize thisfeature, you can specify -Xclang -fmodules-codegen
as acommand-line option or use the driver option-fpch-codegen
.
When generating a serialized AST file for PCH or modules, Clangidentifies non-always-inline functions that do not depend on templateparameters and have linkages other than GVA_Internal
orGVA_AvailableExternally
. These functions are thenserialized (see ASTWriter::ModularCodegenDecls
).
In an importer that encounters such a definition, the linkage isadjusted to GVA_AvailableExternally
. This allows fordiscarding of the definition if it is not required foroptimizations.
Let's consider an example using the files a.cc
,a1.cc
, and b.h
from the initial exampleprovided at the beginning of this article. 1
2
3
4
5
6
7echo 'module b { header "b.h" }' > module.modulemap
clang -c -Xclang -emit-module -fmodules -xc++ module.modulemap -fmodule-name=b -o b.pcm -Xclang -fmodules-codegen
clang -c b.pcm
clang -c -fmodules -fno-implicit-module-maps -fmodule-file=b.pcm a.cc
clang -c -fmodules -fno-implicit-module-maps -fmodule-file=b.pcm a1.cc
clang++ a.o a1.o b.o -o a
Both a.cc
and a1.cc
includeb.h
and obtain an inline definition of fb
. Ina regular build, the fb
definition hasGVA_DiscardableODR
linkage and is compiled twice intoa.o
and a1.o
. These duplicate definitions arethen deduplicated by the linker, following COMDAT semantics.
In a modular code generation build, fb
is assignedGVA_StrongODR
linkage in b.pcm
and is emittedinto b.o
. The copies of fb
ina.cc
and a1.cc
are adjusted toGVA_AvailableExternally
. They are used for optimizations bycallers but are not emitted otherwise. In a -O0
build, theGVA_AvailableExternally
definitions are simply discarded.Regardless, both the code generator and the linker have reduced work,resulting in decreased build time.
However, there are two primary differences in behavior.
First, if b.h
contains a GVA_StrongExternal
definition, a regular build will encounter a linker error due to aduplicate symbol. However, in the prebuilt modules build using-fmodules-codegen
, this error does not occur.
Second, in a regular build, if fb
is unused, notranslation unit will contain its COMDAT definition. On the other hand,in the prebuilt modules build using -fmodules-codegen
, wecompile the prebuilt module b.pcm
into b.o
andlink b.o
into the executable, always resulting in adefinition of fb
, even when using -O1
orabove. To discard fb
, linker garbage collection can beleveraged by using-ffunction-sections -Wl,--gc-sections
.
If b.h
contains an inline variable with an initializerinvolving a side effect (e.g.,inline int vb = puts("vb"), 1;
), the modular codegeneration build will always observe the side effect. In contrast, aregular build may not observe the side effect if, for example, thecontaining header is not included in any translation unit.
Nevertheless, these behavior differences are almost always benign,and the speedup gained in build time may outweigh the downsides.
Note: Clang exhibits a similiar behavior when compiling moduleinterface units and module partitions for strong definitions.
Modular code generation has been extended to support PCH in Clang 11.We specify -fpch-codegen
to pass-fmodules-codegen
to the frontend. 1
2
3
4
5clang++ -c -xc++-header -fpch-codegen b.h -o b.pch
clang++ -c b.pch -o b.o
clang++ -c -include-pch b.pch a.cc
clang++ -c -include-pch b.pch a1.cc
clang++ a.o a1.o b.o -o a
When using -fpch-codegen
, compared to thenon--fpch-codegen
usage of PCH, it is necessary to compilethe PCH file b.pch
into b.o
and linkb.o
into the executable. If b.o
is not linked,a linker error for an undefined fb()
will occur.
-fpch-debuginfo
-fpch-debuginfo
serves a similar purpose as-fpch-codegen
, but specifically for debug informationdescriptions of types.
Here is an example of using MSVC-style precompiled headers withclang-cl (a CL-style driver mode)
1 | clang-cl /c /Ycb.hh a.cc |
Let's consider the initial example provided at the beginning of thisarticle.
The /Ycb.hh
command instructs the Clang Driver toperform two frontend actions. First, Clang parses the base source filea.cc
up to and including #include "b.hh"
,performs semantic analysis, and writes the precompiled header intob.pch
. It replaces the header file extension with.pch
, unlike GCC. Second, Clang compiles a.cc
using -include-pch b.pch
, but it skips preprocessing tokensup to and including #include "b.hh"
(seePreprocessor::SkippingUntilPCHThroughHeader
).
The /Yub.hh
command is similar to the second frontendaction of /Ycb.hh
. It compiles a1.cc
using-include-pch b.pch
, but it also skips preprocessing tokensup to and including #include "b.hh"
.
Internally, /Ycb.hh
and /Yub.hh
instructthe driver to pass -pch-through-header=b.hh
to thefrontend. This helps Clang detect common pitfalls by examining whetherthe source file contains the #include "b.hh"
directive.
It is also possible to use /Yc
and /Yu
without specifying a filename. In this case, the precompiled headerregion is determined by #pragma hdrstop
or the end of thesource file. For more details, refer to /Yc(Create Precompiled Header File).
Additionally, when using clang-cl /Yc
, the cc1 option-building-pch-with-obj
is passed to the frontend toserialize dllexport
declarations.
TODO: PCH signature and linker
TODO
-fno-pch-timestamp
TODO