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

Reply via email to