Keep multiple-include optimization for null directives
authorElliot Goodrich <elliotgoodrich@gmail.com>
Thu, 27 Apr 2023 17:43:18 +0000 (13:43 -0400)
committerAaron Ballman <aaron@aaronballman.com>
Thu, 27 Apr 2023 17:44:11 +0000 (13:44 -0400)
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.

Differential Revision: https://reviews.llvm.org/D147928

clang/docs/ReleaseNotes.rst
clang/include/clang/Lex/MultipleIncludeOpt.h
clang/lib/Lex/PPDirectives.cpp
clang/test/Preprocessor/multiple-inclusion-opt.cpp [new file with mode: 0644]
clang/test/Preprocessor/multiple-inclusion-opt.h [new file with mode: 0644]

index 85eb445..71b197a 100644 (file)
@@ -157,6 +157,8 @@ Non-comprehensive list of changes in this release
 - A new builtin type trait ``__is_trivially_equaltiy_comparable`` has been added,
   which checks whether comparing two instances of a type is equivalent to
   ``memcmp(&lhs, &rhs, sizeof(T)) == 0``.
+- Clang now ignores null directives outside of the include guard when deciding
+  whether a file can be enabled for the multiple-include optimization.
 
 New Compiler Flags
 ------------------
index 7ceb7e5..8e57022 100644 (file)
@@ -108,6 +108,12 @@ public:
     ImmediatelyAfterTopLevelIfndef = false;
   }
 
+  /// SetReadToken - Set whether the value of 'ReadAnyTokens'.  Called to
+  /// override 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 SetReadToken(bool Value) { ReadAnyTokens = Value; }
+
   /// 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 3132d57..7e4ba98 100644 (file)
@@ -1177,6 +1177,10 @@ void Preprocessor::HandleDirective(Token &Result) {
 
   switch (Result.getKind()) {
   case tok::eod:
+    // Ignore the null directive with regards to the multiple-include
+    // optimization, i.e. allow the null directive to appear outside of the
+    // include guard and still enable the multiple-include optimization.
+    CurPPLexer->MIOpt.SetReadToken(ReadAnyTokensBeforeDirective);
     return;   // null directive.
   case tok::code_completion:
     setCodeCompletionReached();
diff --git a/clang/test/Preprocessor/multiple-inclusion-opt.cpp b/clang/test/Preprocessor/multiple-inclusion-opt.cpp
new file mode 100644 (file)
index 0000000..0da2033
--- /dev/null
@@ -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"
diff --git a/clang/test/Preprocessor/multiple-inclusion-opt.h b/clang/test/Preprocessor/multiple-inclusion-opt.h
new file mode 100644 (file)
index 0000000..d346c9d
--- /dev/null
@@ -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 */