Skip to content

[Preprocessor] Do not expand macros if the input is already preprocessed #137665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

jmmartinez
Copy link
Contributor

@jmmartinez jmmartinez commented Apr 28, 2025

Preprocessing the preprocessor output again interacts poorly with some flag combinations when we perform a separate preprocessing stage. In our case, -no-integrated-cpp -dD triggered this issue; but I guess that other flags could also trigger problems (-save-temps instead of -no-integrated-cpp).

Full context (which is quite weird I'll admit):

  • To cache OpenCL kernel compilation results, we use the -no-integrated-cpp for the driver to generate a separate preprocessing command (clang -E) before the rest of the compilation.
  • Some OpenCL C language features are implemented as macro definitions (in opencl-c-base.h). The semantic analysis queries the preprocessor to check if these are defined or not, for example, when we checks if a builtin is available when using -fdeclare-opencl-builtins.
  • To preserve these #define directives, on the preprocessor's output, we use -dD. However, other #define directives are also maintained besides OpenCL ones; which triggers the issue shown in this PR.

A better fix for our particular case could have been to move the language features implemented as macros into some sort of a flag to be used together with -fdeclare-opencl-builtins.
But I also thought that not preprocessing preprocessor outputs seemed like something desirable. I hope to work on this on a follow up.

@jmmartinez jmmartinez requested a review from lamb-j April 28, 2025 16:33
@jmmartinez jmmartinez self-assigned this Apr 28, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Apr 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 28, 2025

@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: Juan Manuel Martinez Caamaño (jmmartinez)

Changes

This is a draft while I'm trying to figure out what's left to do.

This has issues with the test Modules/initializers.cpp.
The function CompilerInstance::createModuleFromSource creates a FrontendInput that "is preprocessed" from the preprocessed source string that is passed as argument (the module contents). However, this does not seem preprocessed (at least for this test).

InputKind::ModuleMap, /*Preprocessed*/true));

I'm not familiar with modules and I'm not sure if it's this patch, or the test that has issues.

Other failures are fixed with #137623


Full diff: https://github.com/llvm/llvm-project/pull/137665.diff

3 Files Affected:

  • (modified) clang/include/clang/Lex/Preprocessor.h (+5)
  • (modified) clang/lib/Frontend/InitPreprocessor.cpp (+7)
  • (added) clang/test/Preprocessor/preprocess-cpp-output.c (+10)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index f2dfd3a349b8b..63774e48a468b 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -1831,6 +1831,11 @@ class Preprocessor {
     MacroExpansionInDirectivesOverride = true;
   }
 
+  void SetDisableMacroExpansion() {
+    DisableMacroExpansion = true;
+    MacroExpansionInDirectivesOverride = false;
+  }
+
   /// Peeks ahead N tokens and returns that token without consuming any
   /// tokens.
   ///
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 1f297f228fc1b..6693cfb469f82 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1556,6 +1556,13 @@ void clang::InitializePreprocessor(Preprocessor &PP,
                                    const PCHContainerReader &PCHContainerRdr,
                                    const FrontendOptions &FEOpts,
                                    const CodeGenOptions &CodeGenOpts) {
+
+  if (all_of(FEOpts.Inputs,
+             [](const FrontendInputFile &FI) { return FI.isPreprocessed(); })) {
+    PP.SetDisableMacroExpansion();
+    return;
+  }
+
   const LangOptions &LangOpts = PP.getLangOpts();
   std::string PredefineBuffer;
   PredefineBuffer.reserve(4080);
diff --git a/clang/test/Preprocessor/preprocess-cpp-output.c b/clang/test/Preprocessor/preprocess-cpp-output.c
new file mode 100644
index 0000000000000..2c180601e30ac
--- /dev/null
+++ b/clang/test/Preprocessor/preprocess-cpp-output.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -E -x c %s | FileCheck %s --check-prefixes=EXPANDED
+// RUN: %clang_cc1 -E -x cpp-output %s | FileCheck %s --check-prefixes=NOT-EXPANDED
+
+// EXPANDED: void __attribute__((__attribute__((always_inline)))) foo()
+// NOT-EXPANDED: void __attribute__((always_inline)) foo()
+
+#define always_inline __attribute__((always_inline))
+void __attribute__((always_inline)) foo() {
+    return 4;
+}

@jmmartinez jmmartinez requested a review from yxsamliu April 29, 2025 15:16
@shafik shafik requested review from cor3ntin and tahonermann April 29, 2025 17:19
@Bigcheese
Copy link
Contributor

module maps don't get preprocessed, so I don't think it matters what that is set to. For normal module.modulemap files Preprocessed is false. I think it's fine to remove the true and see if anything breaks, the preprocessor just skips over that section anyway.

@lamb-j
Copy link
Contributor

lamb-j commented May 9, 2025

module maps don't get preprocessed, so I don't think it matters what that is set to. For normal module.modulemap files Preprocessed is false. I think it's fine to remove the true and see if anything breaks, the preprocessor just skips over that section anyway.

Thanks for the pointer. @Bigcheese who should we solicit an approving review from? I'm not familiar enough with the implications of the change to feel confident approving

@jmmartinez
Copy link
Contributor Author

module maps don't get preprocessed, so I don't think it matters what that is set to. For normal module.modulemap files Preprocessed is false. I think it's fine to remove the true and see if anything breaks, the preprocessor just skips over that section anyway.

Currently a single test breaks: Modules/initializers.cpp which has preprocessor directives in the module source.

I guess then that this is an issue with the test. I'll try to propose a fix to the test then.

The module contents should not contain preprocessor directives. The
contents should be already preprocessed.

Duplicate the modules instead to propose 2 versions: one with the
namespace ns and one without.
@jmmartinez jmmartinez force-pushed the draft/do-not-preprocess-cpp-output branch from 4ed49c6 to 09c37ac Compare May 13, 2025 13:50
@llvmbot llvmbot added the clang:modules C++20 modules and Clang Header Modules label May 13, 2025
@jmmartinez
Copy link
Contributor Author

I fixed the Modules/initializers.cpp test by moving the preprocessor directives outside of the module contents (the module contents should be already preprocessed and not contain preproc directives).

@cor3ntin
Copy link
Contributor

This seems reasonable. Can you update the description to explain the motivation for the change, though?
Is it a bug you are trying to fix, or a specific scenario you think should be optimized? Thanks

@jmmartinez
Copy link
Contributor Author

This seems reasonable. Can you update the description to explain the motivation for the change, though? Is it a bug you are trying to fix, or a specific scenario you think should be optimized? Thanks

I've updated the description with an explanation of the situation that triggered the issue for us. But in the wider scheme, I think that no preprocessing the preprocessor's output is the actual desired behavior.

Thanks!

@jmmartinez
Copy link
Contributor Author

Ping !

Comment on lines 1561 to 1566

if (all_of(FEOpts.Inputs,
[](const FrontendInputFile &FI) { return FI.isPreprocessed(); })) {
PP.SetDisableMacroExpansion();
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you consider doing that on a per-file basis in FrontendAction::BeginSourceFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it makes more sense there. I've updated it to match.

@cor3ntin
Copy link
Contributor

Ping !

Sorry, I missed that you replied last week

@jmmartinez jmmartinez requested a review from cor3ntin May 23, 2025 06:45
Comment on lines +87 to +88
if (CurrentInput.isPreprocessed())
CI.getPreprocessor().SetDisableMacroExpansion();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should (save and) restore the previous state in EndSourceFileAction,
Did you check if any subclass of FrontendAction would need to be modified?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants