IncludeGuardian created this revision. IncludeGuardian added a reviewer: clang. IncludeGuardian added a project: clang. Herald added a project: All. IncludeGuardian requested review of this revision. Herald added a subscriber: cfe-commits.
The multiple-include optimization allows Clang to avoid opening a files when they contain `#pragma once` or a proper include guard. Both GCC and Microsoft Visual Studio allow null directives outside of the `#ifndef`/`#endif` pair without disabling this multiple-include optimization. GCC documents this behavior here https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html. > There must be no directives outside the controlling directive pair, > but the null directive (a line containing nothing other than a > single '#' and possibly whitespace) is permitted. However, Clang disables the multiple-include optimization when encountering the null directive. In particular, this slows down preprocessing of most projects that depend on boost as many boost libraries depend on the boost preprocessor library, which contains null directives outside the include guard on **every** header file. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D147928 Files: clang/include/clang/Lex/MultipleIncludeOpt.h clang/lib/Lex/PPDirectives.cpp clang/test/Preprocessor/multiple-inclusion-opt.cpp clang/test/Preprocessor/multiple-inclusion-opt.h Index: clang/test/Preprocessor/multiple-inclusion-opt.h =================================================================== --- /dev/null +++ clang/test/Preprocessor/multiple-inclusion-opt.h @@ -0,0 +1,18 @@ +# // null directive and comments before include guard + +#ifndef MULTIPLE_INCLUSION_OPT + +int foo(); + +// The position of the define should not matter +#define MULTIPLE_INCLUSION_OPT + +int bar(); + +#endif + +# +# +/* Two null directives + and a multiline comment + after the #endif */ Index: clang/test/Preprocessor/multiple-inclusion-opt.cpp =================================================================== --- /dev/null +++ clang/test/Preprocessor/multiple-inclusion-opt.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -E -P -H %s 2>&1 | grep "multiple-inclusion-opt.h" | count 1 + +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" Index: clang/lib/Lex/PPDirectives.cpp =================================================================== --- clang/lib/Lex/PPDirectives.cpp +++ clang/lib/Lex/PPDirectives.cpp @@ -1177,6 +1177,12 @@ switch (Result.getKind()) { case tok::eod: + // Allow the null directive to appear outside of the include guard and still + // keep multiple-include optimization. So if we haven't recorded any tokens + // yet we can reset MIOpt to forget about the null directive. + if (!ReadAnyTokensBeforeDirective) { + CurPPLexer->MIOpt.ResetReadToken(); + } return; // null directive. case tok::code_completion: setCodeCompletionReached(); Index: clang/include/clang/Lex/MultipleIncludeOpt.h =================================================================== --- clang/include/clang/Lex/MultipleIncludeOpt.h +++ clang/include/clang/Lex/MultipleIncludeOpt.h @@ -108,6 +108,12 @@ ImmediatelyAfterTopLevelIfndef = false; } + /// ResetReadToken - reset whether we have read any tokens. Called when + /// encountering tokens outside of the include guard that have no effect if + /// the file in question is is included multiple times (e.g. the null + /// directive). + void ResetReadToken() { ReadAnyTokens = false; } + /// ExpandedMacro - When a macro is expanded with this lexer as the current /// buffer, this method is called to disable the MIOpt if needed. void ExpandedMacro() { DidMacroExpansion = true; }
Index: clang/test/Preprocessor/multiple-inclusion-opt.h =================================================================== --- /dev/null +++ clang/test/Preprocessor/multiple-inclusion-opt.h @@ -0,0 +1,18 @@ +# // null directive and comments before include guard + +#ifndef MULTIPLE_INCLUSION_OPT + +int foo(); + +// The position of the define should not matter +#define MULTIPLE_INCLUSION_OPT + +int bar(); + +#endif + +# +# +/* Two null directives + and a multiline comment + after the #endif */ Index: clang/test/Preprocessor/multiple-inclusion-opt.cpp =================================================================== --- /dev/null +++ clang/test/Preprocessor/multiple-inclusion-opt.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -E -P -H %s 2>&1 | grep "multiple-inclusion-opt.h" | count 1 + +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" +#include "multiple-inclusion-opt.h" Index: clang/lib/Lex/PPDirectives.cpp =================================================================== --- clang/lib/Lex/PPDirectives.cpp +++ clang/lib/Lex/PPDirectives.cpp @@ -1177,6 +1177,12 @@ switch (Result.getKind()) { case tok::eod: + // Allow the null directive to appear outside of the include guard and still + // keep multiple-include optimization. So if we haven't recorded any tokens + // yet we can reset MIOpt to forget about the null directive. + if (!ReadAnyTokensBeforeDirective) { + CurPPLexer->MIOpt.ResetReadToken(); + } return; // null directive. case tok::code_completion: setCodeCompletionReached(); Index: clang/include/clang/Lex/MultipleIncludeOpt.h =================================================================== --- clang/include/clang/Lex/MultipleIncludeOpt.h +++ clang/include/clang/Lex/MultipleIncludeOpt.h @@ -108,6 +108,12 @@ ImmediatelyAfterTopLevelIfndef = false; } + /// ResetReadToken - reset whether we have read any tokens. Called when + /// encountering tokens outside of the include guard that have no effect if + /// the file in question is is included multiple times (e.g. the null + /// directive). + void ResetReadToken() { ReadAnyTokens = false; } + /// ExpandedMacro - When a macro is expanded with this lexer as the current /// buffer, this method is called to disable the MIOpt if needed. void ExpandedMacro() { DidMacroExpansion = true; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits