[clang] [C++20] [Modules] Bring Decls Hash to BMI for C++20 Module units (PR #71627)

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 7 20:23:33 PST 2023


https://github.com/ChuanqiXu9 created https://github.com/llvm/llvm-project/pull/71627

Close https://github.com/llvm/llvm-project/issues/71618

This contains https://github.com/llvm/llvm-project/pull/71622 and so that it is not easy to review this until we have stacked reviews. So the main purpose of current patch now is to have a feeling about what we want to do.

The motivating example is:

```
// a.cppm
export module a;
export int a() {
    return 43;
}

// use.cc
import a;
int use() {
    return a();
}
```


After we change the implementation of a() from return 43; to return 44;, we can avoid recompiling use.cc to use.o since the interface doesn't change.

To be continued

>From 3f98ce16010147f0d4fd5e74ac484844ae7ea78d Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Wed, 8 Nov 2023 09:55:07 +0800
Subject: [PATCH 1/3] [C++20] [Modules] Introduce thin BMI

Close https://github.com/llvm/llvm-project/issues/71034

This patch introduces thin BMI, which doesn't contain the definitions of
functions and variables if its definitions won't contribute to the ABI.

Testing is a big part of the patch. We want to make sure the thin BMI
contains the same behavior with the existing and relatively stable
fatBMI. This is pretty helpful for further reduction.

For user interfeaces, this patch introduces `-fthinBMI-output=`
arguments to specify the position of thin BMI. This should be used when
compiling a single module unit.

The design is helpful to use thin BMI in two phase compilations too.
With thin BMI, In two phase compilations, we'll generate 2 BMIs, one
thin BMI for being used by consumers, one fat BMI for compiling itself
to object files. Maybe it sounds confusing to have 2 BMIs for one module
unit. But only the thin BMI will be the BMI we're talking about
generally and the fat BMI is only visible by the module unit itself.

With one phase compilation, we may find the behavior of
`-fthinBMI-output=` is pretty similar with `-fmodule-output=`, except
one generating thin BMI and the other generating fat BMI. The design
here is based on 2 things:
(1) The serialization of C++ is pretty complex. We can't be sure we're handling
every detail correctly in the every beginning.
(2) The fat BMI is relatively widely used and relatively stable.
So it looks not good to replace the fat BMI immediately with thin BMI.

But, of course, in the end of the day, we want the consumers to use the
thin BMI only. When that day comes, the `-fmodule-output=` will be an alias to
`-fthinBMI-output=`.

Another design choice is to reuse `-fmodule-output=` and introduce a flag
`-femit-thin-BMI`. Then `-femit-thin-BMI -fmodule-output=` will have the same effect
with `-fthinBMI-output=` now.
The flag `-femit-thin-BMI` should be opt-in now and opt-off later
and finally deprecated.

The roadmap for thin BMI in my mind is:

(1) In clang18, release thin BMI and mark it as experimental. Also
encourage users and build systems to try this new mode.
(2) In clang19 or clang20 (based on the issue feedbacks), remove the
experimental mark for thin BMI and mark fat BMI as deprecated to be
used by consumers.
(3) In clang21 or clang22, error out if we found the users are trying to
import a fat BMI.
---
 .../clang/Basic/DiagnosticDriverKinds.td      |  2 +
 clang/include/clang/Driver/Options.td         |  9 +++-
 .../include/clang/Frontend/FrontendActions.h  | 18 ++++++-
 .../include/clang/Frontend/FrontendOptions.h  |  9 +++-
 clang/include/clang/Serialization/ASTWriter.h | 30 ++++++++++-
 clang/lib/Driver/Driver.cpp                   |  7 +++
 clang/lib/Driver/ToolChains/Clang.cpp         |  2 +
 clang/lib/Frontend/CompilerInvocation.cpp     |  2 +
 clang/lib/Frontend/FrontendActions.cpp        | 49 +++++++++++++++--
 .../ExecuteCompilerInvocation.cpp             |  2 +
 clang/lib/Serialization/ASTWriter.cpp         | 32 ++++++-----
 clang/lib/Serialization/ASTWriterDecl.cpp     | 53 ++++++++++++++++---
 clang/lib/Serialization/GeneratePCH.cpp       | 36 ++++++++++++-
 clang/test/CXX/basic/basic.link/p10-ex2.cpp   |  3 +-
 .../p4-friend-in-reachable-class.cpp          |  6 ++-
 clang/test/Driver/thinBMI-output.cppm         | 29 ++++++++++
 .../test/Modules/InheritDefaultArguments.cppm |  3 ++
 clang/test/Modules/Reachability-Private.cpp   | 10 ++++
 .../Modules/Reachability-func-default-arg.cpp |  3 ++
 clang/test/Modules/Reachability-func-ret.cpp  |  3 ++
 .../Reachability-template-default-arg.cpp     |  3 ++
 .../Reachability-template-instantiation.cpp   |  4 ++
 .../Modules/Reachability-using-templates.cpp  |  3 ++
 clang/test/Modules/Reachability-using.cpp     |  3 ++
 clang/test/Modules/concept.cppm               |  4 ++
 clang/test/Modules/concept_differ.cppm        |  5 ++
 clang/test/Modules/ctor.arg.dep.cppm          |  4 ++
 clang/test/Modules/cxx20-10-1-ex1.cpp         | 13 +++++
 clang/test/Modules/cxx20-10-1-ex2.cpp         | 36 ++++++++++---
 clang/test/Modules/cxx20-10-2-ex2.cpp         | 12 +++++
 clang/test/Modules/cxx20-10-2-ex5.cpp         | 12 +++++
 clang/test/Modules/cxx20-10-3-ex1.cpp         | 14 +++++
 clang/test/Modules/cxx20-10-3-ex2.cpp         | 10 ++++
 clang/test/Modules/cxx20-10-5-ex1.cpp         | 12 +++++
 .../Modules/cxx20-import-diagnostics-a.cpp    | 39 ++++++++++++++
 .../Modules/cxx20-import-diagnostics-b.cpp    | 25 +++++++++
 .../Modules/cxx20-module-file-info-macros.cpp |  3 ++
 clang/test/Modules/deduction-guide.cppm       |  3 ++
 clang/test/Modules/deduction-guide2.cppm      |  3 ++
 clang/test/Modules/deduction-guide3.cppm      |  3 ++
 clang/test/Modules/derived_class.cpp          |  3 ++
 ...duplicated-module-file-eq-module-name.cppm |  4 ++
 clang/test/Modules/enum-class.cppm            |  3 ++
 .../explicitly-specialized-template.cpp       |  3 ++
 .../test/Modules/export-language-linkage.cppm |  5 ++
 clang/test/Modules/ftime-trace.cppm           |  9 ++++
 .../inconsistent-deduction-guide-linkage.cppm |  6 +++
 clang/test/Modules/inconsistent-export.cppm   | 13 +++++
 clang/test/Modules/inherited_arg.cppm         | 11 ++++
 .../Modules/instantiation-argdep-lookup.cppm  |  3 ++
 clang/test/Modules/lambdas.cppm               | 15 ++++++
 .../Modules/merge-concepts-cxx-modules.cpp    | 12 +++++
 .../Modules/merge-constrained-friends.cpp     |  3 ++
 clang/test/Modules/merge-lambdas.cppm         |  4 ++
 .../Modules/merge-requires-with-lambdas.cppm  | 19 +++++++
 .../merge-var-template-spec-cxx-modules.cppm  |  5 ++
 clang/test/Modules/mismatch-diagnostics.cpp   | 11 ++++
 .../module-init-duplicated-import.cppm        | 11 ++++
 clang/test/Modules/named-modules-adl-2.cppm   |  4 ++
 clang/test/Modules/named-modules-adl-3.cppm   | 17 ++++++
 clang/test/Modules/named-modules-adl.cppm     |  3 ++
 .../Modules/no-duplicate-codegen-in-GMF.cppm  |  8 +++
 clang/test/Modules/pair-unambiguous-ctor.cppm |  9 ++++
 .../test/Modules/partial_specialization.cppm  |  3 ++
 .../test/Modules/placement-new-reachable.cpp  |  3 ++
 clang/test/Modules/polluted-operator.cppm     |  3 ++
 clang/test/Modules/pr54457.cppm               |  3 ++
 clang/test/Modules/pr56916.cppm               | 12 +++++
 clang/test/Modules/pr58532.cppm               |  6 +++
 clang/test/Modules/pr58716.cppm               |  2 +-
 clang/test/Modules/pr59719.cppm               |  3 ++
 clang/test/Modules/pr59780.cppm               |  8 +++
 clang/test/Modules/pr59999.cppm               | 13 +++++
 clang/test/Modules/pr60036.cppm               | 14 +++++
 clang/test/Modules/pr60085.cppm               | 17 ++++++
 clang/test/Modules/pr60275.cppm               |  7 ++-
 clang/test/Modules/pr60486.cppm               |  3 ++
 clang/test/Modules/pr60693.cppm               |  4 ++
 clang/test/Modules/pr60775.cppm               | 13 +++++
 clang/test/Modules/pr60890.cppm               |  6 +++
 clang/test/Modules/pr61065.cppm               | 13 +++++
 clang/test/Modules/pr61065_2.cppm             | 15 ++++++
 clang/test/Modules/pr61067.cppm               | 14 +++++
 clang/test/Modules/pr61317.cppm               |  9 ++++
 clang/test/Modules/pr61783.cppm               |  8 +++
 clang/test/Modules/pr61892.cppm               | 40 +++++++-------
 clang/test/Modules/pr62158.cppm               |  9 ++++
 clang/test/Modules/pr62359.cppm               | 16 ++++++
 clang/test/Modules/pr62589.cppm               |  3 ++
 clang/test/Modules/pr62705.cppm               |  8 +++
 clang/test/Modules/pr62796.cppm               |  4 ++
 clang/test/Modules/pr62943.cppm               | 12 +++++
 clang/test/Modules/pr63544.cppm               | 12 +++++
 clang/test/Modules/pr63595.cppm               | 10 ++++
 clang/test/Modules/pr67627.cppm               |  4 ++
 clang/test/Modules/pr67893.cppm               | 12 +++++
 clang/test/Modules/predefined.cpp             |  3 ++
 clang/test/Modules/preferred_name.cppm        | 10 ++++
 clang/test/Modules/redefinition-merges.cppm   |  6 +++
 .../redundant-template-default-arg.cpp        |  3 ++
 .../redundant-template-default-arg2.cpp       |  3 ++
 .../redundant-template-default-arg3.cpp       |  3 ++
 clang/test/Modules/search-partitions.cpp      | 16 ++++++
 ...unction-definition-for-template-class.cppm | 12 +++++
 .../template-function-specialization.cpp      |  5 +-
 clang/test/Modules/template-lambdas.cppm      | 15 ++++++
 clang/test/Modules/template-pack.cppm         |  3 ++
 .../Modules/template_default_argument.cpp     |  3 ++
 clang/unittests/Sema/SemaNoloadLookupTest.cpp |  9 ++--
 .../Serialization/ForceCheckFileInputTest.cpp | 10 ++--
 .../Serialization/NoCommentsTest.cpp          |  9 ++--
 .../Serialization/VarDeclConstantInitTest.cpp | 13 +++--
 112 files changed, 1049 insertions(+), 83 deletions(-)
 create mode 100644 clang/test/Driver/thinBMI-output.cppm

diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 676f1a62b49dd0d..aad67a9f4c7da01 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -158,6 +158,8 @@ def err_drv_invalid_output_with_multiple_archs : Error<
 def err_drv_no_input_files : Error<"no input files">;
 def err_drv_output_argument_with_multiple_files : Error<
   "cannot specify -o when generating multiple output files">;
+def err_drv_thin_bmi_output_argument_with_multiple_files : Error <
+  "cannot specify -fthinBMI-output when generating multiple module files">;
 def err_drv_out_file_argument_with_multiple_sources : Error<
   "cannot specify '%0%1' when compiling multiple source files">;
 def err_no_external_assembler : Error<
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 36052511203f65c..12785f280183e03 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2915,6 +2915,11 @@ def fmodule_output : Flag<["-"], "fmodule-output">, Flags<[NoXarchOption]>,
   Visibility<[ClangOption, CC1Option]>,
   HelpText<"Save intermediate module file results when compiling a standard C++ module unit.">;
 
+def fthinBMI_output_EQ : Joined<["-"], "fthinBMI-output=">, Group<f_Group>,
+  HelpText<"Specify the output path for the thin BMI for C++20 Named modules">,
+  Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
+  MarshallingInfoString<FrontendOpts<"ThinBMIPath">>;
+
 def fmodules_prune_interval : Joined<["-"], "fmodules-prune-interval=">, Group<i_Group>,
   Visibility<[ClangOption, CC1Option]>, MetaVarName<"<seconds>">,
   HelpText<"Specify the interval (in seconds) between attempts to prune the module cache">,
@@ -7223,7 +7228,9 @@ def ast_view : Flag<["-"], "ast-view">,
 def emit_module : Flag<["-"], "emit-module">,
   HelpText<"Generate pre-compiled module file from a module map">;
 def emit_module_interface : Flag<["-"], "emit-module-interface">,
-  HelpText<"Generate pre-compiled module file from a C++ module interface">;
+  HelpText<"Generate pre-compiled module file from a standard C++ module interface unit">;
+def emit_thin_module_interface : Flag<["-"], "emit-thin-module-interface">,
+  HelpText<"Generate reduced prebuilt module interface from a standard C++ module interface unit">;
 def emit_header_unit : Flag<["-"], "emit-header-unit">,
   HelpText<"Generate C++20 header units from header files">;
 def emit_pch : Flag<["-"], "emit-pch">,
diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h
index 3940e00eeb8dba7..c419deb80034fae 100644
--- a/clang/include/clang/Frontend/FrontendActions.h
+++ b/clang/include/clang/Frontend/FrontendActions.h
@@ -118,6 +118,9 @@ class GenerateModuleAction : public ASTFrontendAction {
   CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0;
 
 protected:
+  std::vector<std::unique_ptr<ASTConsumer>>
+  CreateMultiplexConsumer(CompilerInstance &CI, StringRef InFile);
+
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef InFile) override;
 
@@ -147,14 +150,27 @@ class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
   CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
 };
 
+/// Generates fatBMI (which contains full information to generate the object
+/// files) for C++20 Named Modules. Also generates the thin BMI (only contains
+/// necessary information for importers) if `-fthinBMI-output=`.
 class GenerateModuleInterfaceAction : public GenerateModuleAction {
-private:
+protected:
   bool BeginSourceFileAction(CompilerInstance &CI) override;
 
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override;
+
   std::unique_ptr<raw_pwrite_stream>
   CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
 };
 
+/// Only generates the thin BMI. This action is mainly used by tests.
+class GenerateThinModuleInterfaceAction : public GenerateModuleInterfaceAction {
+private:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) override;
+};
+
 class GenerateHeaderUnitAction : public GenerateModuleAction {
 
 private:
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 53a8681cfdbba04..719d4ca81336116 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -85,9 +85,13 @@ enum ActionKind {
   /// Generate pre-compiled module from a module map.
   GenerateModule,
 
-  /// Generate pre-compiled module from a C++ module interface file.
+  /// Generate pre-compiled module from a standard C++ module interface unit.
   GenerateModuleInterface,
 
+  /// Generate reduced module interface for a standard C++ module interface
+  /// unit.
+  GenerateThinModuleInterface,
+
   /// Generate a C++20 header unit module from a header file.
   GenerateHeaderUnit,
 
@@ -549,6 +553,9 @@ class FrontendOptions {
   /// Path which stores the output files for -ftime-trace
   std::string TimeTracePath;
 
+  /// Path to the thin BMI for -fthinbmi-output=
+  std::string ThinBMIPath;
+
 public:
   FrontendOptions()
       : DisableFree(false), RelocatablePCH(false), ShowHelp(false),
diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index 3019bbc2ddc9cc7..3ccb35aa72f9b01 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -166,6 +166,10 @@ class ASTWriter : public ASTDeserializationListener,
   /// Indicates that the AST contained compiler errors.
   bool ASTHasCompilerErrors = false;
 
+  /// Indicates that we're going to generate the reduced BMI for C++20
+  /// named modules.
+  bool GeneratingThinBMI = false;
+
   /// Mapping from input file entries to the index into the
   /// offset table where information about that input file is stored.
   llvm::DenseMap<const FileEntry *, uint32_t> InputFileIDs;
@@ -582,7 +586,8 @@ class ASTWriter : public ASTDeserializationListener,
   ASTWriter(llvm::BitstreamWriter &Stream, SmallVectorImpl<char> &Buffer,
             InMemoryModuleCache &ModuleCache,
             ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
-            bool IncludeTimestamps = true, bool BuildingImplicitModule = false);
+            bool IncludeTimestamps = true, bool BuildingImplicitModule = false,
+            bool GeneratingThinBMI = false);
   ~ASTWriter() override;
 
   ASTContext &getASTContext() const {
@@ -813,6 +818,13 @@ class PCHGenerator : public SemaConsumer {
   const ASTWriter &getWriter() const { return Writer; }
   SmallVectorImpl<char> &getPCH() const { return Buffer->Data; }
 
+  bool isComplete() const { return Buffer->IsComplete; }
+  PCHBuffer *getBufferPtr() { return Buffer.get(); }
+  StringRef getOutputFile() const { return OutputFile; }
+  DiagnosticsEngine &getDiagnostics() const {
+    return SemaPtr->getDiagnostics();
+  }
+
 public:
   PCHGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
                StringRef OutputFile, StringRef isysroot,
@@ -820,7 +832,8 @@ class PCHGenerator : public SemaConsumer {
                ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
                bool AllowASTWithErrors = false, bool IncludeTimestamps = true,
                bool BuildingImplicitModule = false,
-               bool ShouldCacheASTInMemory = false);
+               bool ShouldCacheASTInMemory = false,
+               bool GeneratingThinBMI = false);
   ~PCHGenerator() override;
 
   void InitializeSema(Sema &S) override { SemaPtr = &S; }
@@ -830,6 +843,19 @@ class PCHGenerator : public SemaConsumer {
   bool hasEmittedPCH() const { return Buffer->IsComplete; }
 };
 
+class ThinBMIGenerator : public PCHGenerator {
+public:
+  ThinBMIGenerator(const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
+                   StringRef OutputFile, std::shared_ptr<PCHBuffer> Buffer,
+                   bool IncludeTimestamps);
+
+  void HandleTranslationUnit(ASTContext &Ctx) override;
+};
+
+/// If the definition may impact the ABI. If yes, we're allowed to eliminate
+/// the definition of D in thin BMI.
+bool MayDefAffectABI(const Decl *D);
+
 /// A simple helper class to pack several bits in order into (a) 32 bit
 /// integer(s).
 class BitsPacker {
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 6f5ff8141032677..76def412d12552c 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4086,6 +4086,13 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
     }
   }
 
+  // Diagnose misuse of -fthinBMI-output. It should be an error if we specify
+  // -fthinBMI-output with multiple precompilation jobs. Here we didn't check if
+  // there are multiple module units in the inputs.
+  if (C.getArgs().getLastArg(options::OPT_fthinBMI_output_EQ) &&
+      Inputs.size() > 1)
+    Diag(clang::diag::err_drv_thin_bmi_output_argument_with_multiple_files);
+
   handleArguments(C, Args, Inputs, Actions);
 
   bool UseNewOffloadingDriver =
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 22f992166ded6c0..5bb945be78dcb41 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3940,6 +3940,8 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D,
   Args.ClaimAllArgs(options::OPT_fmodule_output);
   Args.ClaimAllArgs(options::OPT_fmodule_output_EQ);
 
+  Args.AddLastArg(CmdArgs, options::OPT_fthinBMI_output_EQ);
+
   return HaveModules;
 }
 
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 637c6a35af6532b..b6245d0dddb1052 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2554,6 +2554,7 @@ static const auto &getFrontendActionTable() {
 
       {frontend::GenerateModule, OPT_emit_module},
       {frontend::GenerateModuleInterface, OPT_emit_module_interface},
+      {frontend::GenerateThinModuleInterface, OPT_emit_thin_module_interface},
       {frontend::GenerateHeaderUnit, OPT_emit_header_unit},
       {frontend::GeneratePCH, OPT_emit_pch},
       {frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs},
@@ -4236,6 +4237,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
   case frontend::FixIt:
   case frontend::GenerateModule:
   case frontend::GenerateModuleInterface:
+  case frontend::GenerateThinModuleInterface:
   case frontend::GenerateHeaderUnit:
   case frontend::GeneratePCH:
   case frontend::GenerateInterfaceStubs:
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 2afcf1cf9f68c81..1aaebd160201692 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -184,12 +184,12 @@ bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) {
   return true;
 }
 
-std::unique_ptr<ASTConsumer>
-GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
-                                        StringRef InFile) {
+std::vector<std::unique_ptr<ASTConsumer>>
+GenerateModuleAction::CreateMultiplexConsumer(CompilerInstance &CI,
+                                              StringRef InFile) {
   std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
   if (!OS)
-    return nullptr;
+    return {};
 
   std::string OutputFile = CI.getFrontendOpts().OutputFile;
   std::string Sysroot;
@@ -210,6 +210,17 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
       +CI.getFrontendOpts().BuildingImplicitModule));
   Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator(
       CI, std::string(InFile), OutputFile, std::move(OS), Buffer));
+  return std::move(Consumers);
+}
+
+std::unique_ptr<ASTConsumer>
+GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
+                                        StringRef InFile) {
+  std::vector<std::unique_ptr<ASTConsumer>> Consumers =
+      CreateMultiplexConsumer(CI, InFile);
+  if (Consumers.empty())
+    return nullptr;
+
   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
 }
 
@@ -264,6 +275,35 @@ GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI,
   return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
 }
 
+static std::unique_ptr<ASTConsumer>
+CreateThinBMIGenerator(CompilerInstance &CI, StringRef OutputFile) {
+  auto Buffer = std::make_shared<PCHBuffer>();
+  return std::make_unique<ThinBMIGenerator>(
+      CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Buffer,
+      /*IncludeTimestamps=*/+CI.getFrontendOpts().IncludeTimestamps);
+}
+
+std::unique_ptr<ASTConsumer>
+GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
+                                                 StringRef InFile) {
+  std::vector<std::unique_ptr<ASTConsumer>> Consumers =
+      CreateMultiplexConsumer(CI, InFile);
+  if (Consumers.empty())
+    return nullptr;
+
+  if (!CI.getFrontendOpts().ThinBMIPath.empty())
+    Consumers.push_back(
+        CreateThinBMIGenerator(CI, CI.getFrontendOpts().ThinBMIPath));
+
+  return std::make_unique<MultiplexConsumer>(std::move(Consumers));
+}
+
+std::unique_ptr<ASTConsumer>
+GenerateThinModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
+                                                     StringRef InFile) {
+  return CreateThinBMIGenerator(CI, CI.getFrontendOpts().OutputFile);
+}
+
 bool GenerateHeaderUnitAction::BeginSourceFileAction(CompilerInstance &CI) {
   if (!CI.getLangOpts().CPlusPlusModules) {
     CI.getDiagnostics().Report(diag::err_module_interface_requires_cpp_modules);
@@ -830,7 +870,6 @@ void DumpModuleInfoAction::ExecuteAction() {
 
   const LangOptions &LO = getCurrentASTUnit().getLangOpts();
   if (LO.CPlusPlusModules && !LO.CurrentModule.empty()) {
-
     ASTReader *R = getCurrentASTUnit().getASTReader().get();
     unsigned SubModuleCount = R->getTotalNumSubmodules();
     serialization::ModuleFile &MF = R->getModuleManager().getPrimaryModule();
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index b280a1359d2f272..59f7f955db50971 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -65,6 +65,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
     return std::make_unique<GenerateModuleFromModuleMapAction>();
   case GenerateModuleInterface:
     return std::make_unique<GenerateModuleInterfaceAction>();
+  case GenerateThinModuleInterface:
+    return std::make_unique<GenerateThinModuleInterfaceAction>();
   case GenerateHeaderUnit:
     return std::make_unique<GenerateHeaderUnitAction>();
   case GeneratePCH:            return std::make_unique<GeneratePCHAction>();
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 0d347d86cb2eb44..ee8ba8a6874b57a 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -4593,10 +4593,12 @@ ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream,
                      SmallVectorImpl<char> &Buffer,
                      InMemoryModuleCache &ModuleCache,
                      ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
-                     bool IncludeTimestamps, bool BuildingImplicitModule)
+                     bool IncludeTimestamps, bool BuildingImplicitModule,
+                     bool GeneratingThinBMI)
     : Stream(Stream), Buffer(Buffer), ModuleCache(ModuleCache),
       IncludeTimestamps(IncludeTimestamps),
-      BuildingImplicitModule(BuildingImplicitModule) {
+      BuildingImplicitModule(BuildingImplicitModule),
+      GeneratingThinBMI(GeneratingThinBMI) {
   for (const auto &Ext : Extensions) {
     if (auto Writer = Ext->createExtensionWriter(*this))
       ModuleFileExtensionWriters.push_back(std::move(Writer));
@@ -5403,18 +5405,20 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
 
     // Add a trailing update record, if any. These must go last because we
     // lazily load their attached statement.
-    if (HasUpdatedBody) {
-      const auto *Def = cast<FunctionDecl>(D);
-      Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
-      Record.push_back(Def->isInlined());
-      Record.AddSourceLocation(Def->getInnerLocStart());
-      Record.AddFunctionDefinition(Def);
-    } else if (HasAddedVarDefinition) {
-      const auto *VD = cast<VarDecl>(D);
-      Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
-      Record.push_back(VD->isInline());
-      Record.push_back(VD->isInlineSpecified());
-      Record.AddVarDeclInit(VD);
+    if (!GeneratingThinBMI || MayDefAffectABI(D)) {
+      if (HasUpdatedBody) {
+        const auto *Def = cast<FunctionDecl>(D);
+        Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
+        Record.push_back(Def->isInlined());
+        Record.AddSourceLocation(Def->getInnerLocStart());
+        Record.AddFunctionDefinition(Def);
+      } else if (HasAddedVarDefinition) {
+        const auto *VD = cast<VarDecl>(D);
+        Record.push_back(UPD_CXX_ADDED_VAR_DEFINITION);
+        Record.push_back(VD->isInline());
+        Record.push_back(VD->isInlineSpecified());
+        Record.AddVarDeclInit(VD);
+      }
     }
 
     OffsetsRecord.push_back(GetDeclRef(D));
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 763b610288f1182..00a04ee2fa730d1 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/OpenMPClause.h"
 #include "clang/AST/PrettyDeclStackTrace.h"
 #include "clang/Basic/SourceManager.h"
@@ -40,11 +41,14 @@ namespace clang {
     serialization::DeclCode Code;
     unsigned AbbrevToUse;
 
+    bool GeneratingThinBMI = false;
+
   public:
     ASTDeclWriter(ASTWriter &Writer, ASTContext &Context,
-                  ASTWriter::RecordDataImpl &Record)
+                  ASTWriter::RecordDataImpl &Record, bool GeneratingThinBMI)
         : Writer(Writer), Context(Context), Record(Writer, Record),
-          Code((serialization::DeclCode)0), AbbrevToUse(0) {}
+          Code((serialization::DeclCode)0), AbbrevToUse(0),
+          GeneratingThinBMI(GeneratingThinBMI) {}
 
     uint64_t Emit(Decl *D) {
       if (!Code)
@@ -270,6 +274,35 @@ namespace clang {
   };
 }
 
+bool clang::MayDefAffectABI(const Decl *D) {
+  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+    if (FD->isInlined() || FD->isConstexpr())
+      return true;
+
+    // Non-user-provided functions get emitted as weak definitions with every
+    // use, no matter whether they've been explicitly instantiated etc.
+    if (!FD->isUserProvided())
+      return true;
+    
+    if (FD->isDependentContext())
+      return true;
+
+    if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
+      return true;
+  }
+
+  if (auto *VD = dyn_cast<VarDecl>(D)) {
+    if (!VD->getDeclContext()->getRedeclContext()->isFileContext() ||
+        VD->isInline() || VD->isConstexpr() || isa<ParmVarDecl>(VD))
+      return true;
+
+    if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
+      return true;
+  }
+
+  return false;
+}
+
 void ASTDeclWriter::Visit(Decl *D) {
   DeclVisitor<ASTDeclWriter>::Visit(D);
 
@@ -285,9 +318,12 @@ void ASTDeclWriter::Visit(Decl *D) {
   // have been written. We want it last because we will not read it back when
   // retrieving it from the AST, we'll just lazily set the offset.
   if (auto *FD = dyn_cast<FunctionDecl>(D)) {
-    Record.push_back(FD->doesThisDeclarationHaveABody());
-    if (FD->doesThisDeclarationHaveABody())
-      Record.AddFunctionDefinition(FD);
+    if (!GeneratingThinBMI || MayDefAffectABI(FD)) {
+      Record.push_back(FD->doesThisDeclarationHaveABody());
+      if (FD->doesThisDeclarationHaveABody())
+        Record.AddFunctionDefinition(FD);
+    } else
+      Record.push_back(0);
   }
 
   // Similar to FunctionDecls, handle VarDecl's initializer here and write it
@@ -295,7 +331,10 @@ void ASTDeclWriter::Visit(Decl *D) {
   // we have finished recursive deserialization, because it can recursively
   // refer back to the variable.
   if (auto *VD = dyn_cast<VarDecl>(D)) {
-    Record.AddVarDeclInit(VD);
+    if (!GeneratingThinBMI || MayDefAffectABI(VD))
+      Record.AddVarDeclInit(VD);
+    else
+      Record.push_back(0);
   }
 
   // And similarly for FieldDecls. We already serialized whether there is a
@@ -2486,7 +2525,7 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
   assert(ID >= FirstDeclID && "invalid decl ID");
 
   RecordData Record;
-  ASTDeclWriter W(*this, Context, Record);
+  ASTDeclWriter W(*this, Context, Record, GeneratingThinBMI);
 
   // Build a record for this declaration
   W.Visit(D);
diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp
index cf8084333811f13..a51047a148c93f0 100644
--- a/clang/lib/Serialization/GeneratePCH.cpp
+++ b/clang/lib/Serialization/GeneratePCH.cpp
@@ -12,9 +12,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTContext.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/SemaConsumer.h"
+#include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/ASTWriter.h"
 #include "llvm/Bitstream/BitstreamWriter.h"
 
@@ -25,11 +27,12 @@ PCHGenerator::PCHGenerator(
     StringRef OutputFile, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer,
     ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
     bool AllowASTWithErrors, bool IncludeTimestamps,
-    bool BuildingImplicitModule, bool ShouldCacheASTInMemory)
+    bool BuildingImplicitModule, bool ShouldCacheASTInMemory,
+    bool GeneratingThinBMI)
     : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()),
       SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data),
       Writer(Stream, this->Buffer->Data, ModuleCache, Extensions,
-             IncludeTimestamps, BuildingImplicitModule),
+             IncludeTimestamps, BuildingImplicitModule, GeneratingThinBMI),
       AllowASTWithErrors(AllowASTWithErrors),
       ShouldCacheASTInMemory(ShouldCacheASTInMemory) {
   this->Buffer->IsComplete = false;
@@ -78,3 +81,32 @@ ASTMutationListener *PCHGenerator::GetASTMutationListener() {
 ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() {
   return &Writer;
 }
+
+ThinBMIGenerator::ThinBMIGenerator(
+    const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
+    StringRef OutputFile, std::shared_ptr<PCHBuffer> Buffer,
+    bool IncludeTimestamps)
+    : PCHGenerator(
+          PP, ModuleCache, OutputFile, llvm::StringRef(), Buffer,
+          /*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
+          /*AllowASTWithErrors*/ false, /*IncludeTimestamps=*/IncludeTimestamps,
+          /*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false,
+          /*GeneratingThinBMI=*/true) {}
+
+void ThinBMIGenerator::HandleTranslationUnit(ASTContext &Ctx) {
+  PCHGenerator::HandleTranslationUnit(Ctx);
+
+  if (!isComplete())
+    return;
+
+  std::error_code EC;
+  auto OS = std::make_unique<llvm::raw_fd_ostream>(getOutputFile(), EC);
+  if (EC) {
+    getDiagnostics().Report(diag::err_fe_unable_to_open_output)
+        << getOutputFile() << EC.message() << "\n";
+    return;
+  }
+
+  *OS << getBufferPtr()->Data;
+  OS->flush();
+}
diff --git a/clang/test/CXX/basic/basic.link/p10-ex2.cpp b/clang/test/CXX/basic/basic.link/p10-ex2.cpp
index 95fdb56f78d6250..98daf12cefc64be 100644
--- a/clang/test/CXX/basic/basic.link/p10-ex2.cpp
+++ b/clang/test/CXX/basic/basic.link/p10-ex2.cpp
@@ -4,8 +4,9 @@
 // RUN: cd %t
 //
 // RUN: %clang_cc1 -std=c++20 M.cpp -fsyntax-only -DTEST_INTERFACE -verify
-// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm
+// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm -fthinBMI-output=M.thin.pcm
 // RUN: %clang_cc1 -std=c++20 useM.cpp -fsyntax-only -fmodule-file=M=M.pcm -verify
+// RUN: %clang_cc1 -std=c++20 useM.cpp -fsyntax-only -fmodule-file=M=M.thin.pcm -verify
 
 //--- decls.h
 int f(); // #1, attached to the global module
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp
index 638057cbd681f06..3a46a218bef03bb 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4-friend-in-reachable-class.cpp
@@ -7,8 +7,10 @@
 // RUN: mkdir %t
 // RUN: split-file %s %t
 //
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Friend-in-reachable-class.cppm -o %t/X.pcm
-// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Friend-in-reachable-class.cppm \
+// RUN:   -fthinBMI-output=%t/X.thin.pcm -o %t/X.pcm
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=X=%t/X.pcm %t/Use.cpp -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=X=%t/X.thin.pcm %t/Use.cpp -verify -fsyntax-only
 //
 //--- Friend-in-reachable-class.cppm
 module;
diff --git a/clang/test/Driver/thinBMI-output.cppm b/clang/test/Driver/thinBMI-output.cppm
new file mode 100644
index 000000000000000..e3268dcf6c15987
--- /dev/null
+++ b/clang/test/Driver/thinBMI-output.cppm
@@ -0,0 +1,29 @@
+// It is annoying to handle different slash direction
+// in Windows and Linux. So we disable the test on Windows
+// here.
+// REQUIRES: !system-windows
+// On AIX, the default output for `-c` may be `.s` instead of `.o`,
+// which makes the test fail. So disable the test on AIX.
+// REQUIRES: !system-aix
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang -std=c++20 %t/Hello.cppm -fthinBMI-output=%t/Hello.pcm -c -o %t/Hello.o \
+// RUN:   -### 2>&1 | FileCheck %t/Hello.cppm
+// RUN: %clang -std=c++20 %t/Hello.cppm -fthinBMI-output=%t/Hello.pcm --precompile \
+// RUN:   -o %t/Hello.pcm -### 2>&1 | FileCheck %t/Hello.cppm
+//
+// Tests that we can't use `-fthinBMI-output=` with multiple input files
+// RUN: not %clang -std=c++20 %t/Hello.cppm %t/a.cppm -fthinBMI-output=%t/Hello.pcm  \
+// RUN:     -o %t/a.out -### 2>&1 | FileCheck %t/a.cppm
+
+//--- Hello.cppm
+export module Hello;
+
+// CHECK: "-emit-module-interface"{{.*}}"-fthinBMI-output={{[a-zA-Z0-9./-]*}}Hello.pcm"
+
+//--- a.cppm
+export module a;
+// CHECK: cannot specify -fthinBMI-output when generating multiple module files
diff --git a/clang/test/Modules/InheritDefaultArguments.cppm b/clang/test/Modules/InheritDefaultArguments.cppm
index 0afb46319ff8508..1bf7c3210bf61d7 100644
--- a/clang/test/Modules/InheritDefaultArguments.cppm
+++ b/clang/test/Modules/InheritDefaultArguments.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cppm -verify -fsyntax-only
 
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cppm -verify -fsyntax-only
+
 //--- foo.h
 template <typename T, typename U = int>
 class Templ;
diff --git a/clang/test/Modules/Reachability-Private.cpp b/clang/test/Modules/Reachability-Private.cpp
index 9a7c3ba231f179c..e29906ccf743c6b 100644
--- a/clang/test/Modules/Reachability-Private.cpp
+++ b/clang/test/Modules/Reachability-Private.cpp
@@ -9,6 +9,16 @@
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp \
 // RUN: -DTEST_BADINLINE -verify -fsyntax-only
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/Private.cppm -emit-thin-module-interface \
+// RUN: -o %t/Private.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp \
+// RUN: -DTEST_BADINLINE -verify -fsyntax-only
+
 //--- Private.cppm
 export module Private;
 #ifdef TEST_BADINLINE
diff --git a/clang/test/Modules/Reachability-func-default-arg.cpp b/clang/test/Modules/Reachability-func-default-arg.cpp
index 0d6d8655d53293e..6bf0bcadc7e76ea 100644
--- a/clang/test/Modules/Reachability-func-default-arg.cpp
+++ b/clang/test/Modules/Reachability-func-default-arg.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/func_default_arg.cppm -emit-module-interface -o %t/func_default_arg.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
+// RUN: %clang_cc1 -std=c++20 %t/func_default_arg.cppm -emit-thin-module-interface -o %t/func_default_arg.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 //
 //--- func_default_arg.cppm
 export module func_default_arg;
diff --git a/clang/test/Modules/Reachability-func-ret.cpp b/clang/test/Modules/Reachability-func-ret.cpp
index ca5bbc68d759f93..4bc2e0c5ec29471 100644
--- a/clang/test/Modules/Reachability-func-ret.cpp
+++ b/clang/test/Modules/Reachability-func-ret.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/func_ret.cppm -emit-module-interface -o %t/func_ret.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
+// RUN: %clang_cc1 -std=c++20 %t/func_ret.cppm -emit-thin-module-interface -o %t/func_ret.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 //
 //--- func_ret.cppm
 export module func_ret;
diff --git a/clang/test/Modules/Reachability-template-default-arg.cpp b/clang/test/Modules/Reachability-template-default-arg.cpp
index 6fb109e41fcf0a0..3e1bcd1888ef80d 100644
--- a/clang/test/Modules/Reachability-template-default-arg.cpp
+++ b/clang/test/Modules/Reachability-template-default-arg.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/template_default_arg.cppm -emit-module-interface -o %t/template_default_arg.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/template_default_arg.cppm -emit-thin-module-interface -o %t/template_default_arg.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
 //--- template_default_arg.cppm
 export module template_default_arg;
diff --git a/clang/test/Modules/Reachability-template-instantiation.cpp b/clang/test/Modules/Reachability-template-instantiation.cpp
index 2170c7b92a370aa..3dd2268a23433aa 100644
--- a/clang/test/Modules/Reachability-template-instantiation.cpp
+++ b/clang/test/Modules/Reachability-template-instantiation.cpp
@@ -5,6 +5,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-module-interface -o %t/Templ.pcm
 // RUN: %clang_cc1 -std=c++20 %t/Use.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/Use.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cpp -verify -fsyntax-only
+
+// RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-thin-module-interface -o %t/Templ.pcm
+// RUN: %clang_cc1 -std=c++20 %t/Use.cppm -fprebuilt-module-path=%t -emit-thin-module-interface -o %t/Use.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cpp -verify -fsyntax-only
 //
 //--- Templ.h
 #ifndef TEMPL_H
diff --git a/clang/test/Modules/Reachability-using-templates.cpp b/clang/test/Modules/Reachability-using-templates.cpp
index f530e15bd4d2ba4..5dc5b48cbaf8543 100644
--- a/clang/test/Modules/Reachability-using-templates.cpp
+++ b/clang/test/Modules/Reachability-using-templates.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/mod.templates.cppm -emit-module-interface -o %t/mod.templates.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/mod.templates.cppm -emit-thin-module-interface -o %t/mod.templates.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
 //--- mod.templates.cppm
 export module mod.templates;
diff --git a/clang/test/Modules/Reachability-using.cpp b/clang/test/Modules/Reachability-using.cpp
index 642b97dd8432c3b..eaa7ed852ed1479 100644
--- a/clang/test/Modules/Reachability-using.cpp
+++ b/clang/test/Modules/Reachability-using.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/mod.cppm -emit-module-interface -o %t/mod.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/mod.cppm -emit-thin-module-interface -o %t/mod.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
 //--- mod.cppm
 export module mod;
diff --git a/clang/test/Modules/concept.cppm b/clang/test/Modules/concept.cppm
index 0e85a46411a5448..a41ce72ed9e26b3 100644
--- a/clang/test/Modules/concept.cppm
+++ b/clang/test/Modules/concept.cppm
@@ -5,6 +5,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t -DDIFFERENT %t/B.cppm -verify
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/B.cppm -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.full.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t -DDIFFERENT %t/B.cppm -verify
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/B.cppm -verify
 
 //--- foo.h
 #ifndef FOO_H
diff --git a/clang/test/Modules/concept_differ.cppm b/clang/test/Modules/concept_differ.cppm
index ccb29d26e53d134..7341af8f39984bb 100644
--- a/clang/test/Modules/concept_differ.cppm
+++ b/clang/test/Modules/concept_differ.cppm
@@ -5,6 +5,11 @@
 // RUN: %clang_cc1 -x c++ -std=c++20 %t/A.cppm -I%t -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -x c++ -std=c++20 %t/B.cppm -I%t -emit-module-interface -o %t/B.pcm
 // RUN: %clang_cc1 -x c++ -std=c++20 -fprebuilt-module-path=%t %t/foo.cpp -verify
+//
+// RUN: rm %t/A.pcm %t/B.pcm
+// RUN: %clang_cc1 -x c++ -std=c++20 %t/A.cppm -I%t -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -x c++ -std=c++20 %t/B.cppm -I%t -emit-thin-module-interface -o %t/B.pcm
+// RUN: %clang_cc1 -x c++ -std=c++20 -fprebuilt-module-path=%t %t/foo.cpp -verify
 
 //--- foo.h
 template <class T>
diff --git a/clang/test/Modules/ctor.arg.dep.cppm b/clang/test/Modules/ctor.arg.dep.cppm
index 0e5b1a694f6a5e1..28ab704e5628d7c 100644
--- a/clang/test/Modules/ctor.arg.dep.cppm
+++ b/clang/test/Modules/ctor.arg.dep.cppm
@@ -5,6 +5,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 //
+// RUN: rm %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+//
 //--- foo.h
 
 namespace ns {
diff --git a/clang/test/Modules/cxx20-10-1-ex1.cpp b/clang/test/Modules/cxx20-10-1-ex1.cpp
index b9a5e8023d035f3..772555b25b80a16 100644
--- a/clang/test/Modules/cxx20-10-1-ex1.cpp
+++ b/clang/test/Modules/cxx20-10-1-ex1.cpp
@@ -17,6 +17,19 @@
 // RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex1-tu4.cpp \
 // RUN:  -fmodule-file=%t/A.pcm -o %t/ex1.o
 
+// RUN: rm %t/A_Internals.pcm %t/A_Foo.pcm %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex1-tu1.cpp \
+// RUN:  -o %t/A_Internals.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex1-tu2.cpp \
+// RUN:  -fmodule-file=%t/A_Internals.pcm -o %t/A_Foo.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex1-tu3.cpp \
+// RUN:  -fmodule-file=%t/A_Foo.pcm -o %t/A.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex1-tu4.cpp \
+// RUN:  -fmodule-file=%t/A.pcm -o %t/ex1.o
+
 // expected-no-diagnostics
 
 //--- std10-1-ex1-tu1.cpp
diff --git a/clang/test/Modules/cxx20-10-1-ex2.cpp b/clang/test/Modules/cxx20-10-1-ex2.cpp
index 8b908d5fa2eda61..f273655783eec40 100644
--- a/clang/test/Modules/cxx20-10-1-ex2.cpp
+++ b/clang/test/Modules/cxx20-10-1-ex2.cpp
@@ -5,26 +5,50 @@
 
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu1.cpp \
 // RUN:  -o %t/B_Y.pcm
-
+//
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu2.cpp \
 // RUN:     -fmodule-file=B:Y=%t/B_Y.pcm -o %t/B.pcm
-
+//
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu3.cpp \
 // RUN:     -o %t/B_X1.pcm -verify
-
+//
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu4.cpp \
 // RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/B_X2.pcm
-
+//
 // RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex2-tu5.cpp \
 // RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/b_tu5.o
-
+//
 // RUN: %clang_cc1 -std=c++20 -S %t/std10-1-ex2-tu6.cpp \
 // RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/b_tu6.s -verify
-
+//
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-1-ex2-tu7.cpp \
 // RUN:     -fmodule-file=B:X2=%t/B_X2.pcm -fmodule-file=B=%t/B.pcm \
 // RUN:     -fmodule-file=B:Y=%t/B_Y.pcm   -o %t/B_X3.pcm -verify
 
+// Test again with thin BMI.
+// RUN: rm %t/B_X2.pcm %t/B.pcm %t/B_Y.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex2-tu1.cpp \
+// RUN:  -o %t/B_Y.pcm
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex2-tu2.cpp \
+// RUN:     -fmodule-file=B:Y=%t/B_Y.pcm -o %t/B.pcm
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex2-tu3.cpp \
+// RUN:     -o %t/B_X1.pcm -verify
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex2-tu4.cpp \
+// RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/B_X2.pcm
+//
+// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std10-1-ex2-tu5.cpp \
+// RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/b_tu5.o
+//
+// RUN: %clang_cc1 -std=c++20 -S %t/std10-1-ex2-tu6.cpp \
+// RUN:     -fmodule-file=B=%t/B.pcm -fmodule-file=B:Y=%t/B_Y.pcm  -o %t/b_tu6.s -verify
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-1-ex2-tu7.cpp \
+// RUN:     -fmodule-file=B:X2=%t/B_X2.pcm -fmodule-file=B=%t/B.pcm \
+// RUN:     -fmodule-file=B:Y=%t/B_Y.pcm   -o %t/B_X3.pcm -verify
+
 //--- std10-1-ex2-tu1.cpp
 module B:Y;
 int y();
diff --git a/clang/test/Modules/cxx20-10-2-ex2.cpp b/clang/test/Modules/cxx20-10-2-ex2.cpp
index bc66d6a2ec1a92e..21fae454d5105b7 100644
--- a/clang/test/Modules/cxx20-10-2-ex2.cpp
+++ b/clang/test/Modules/cxx20-10-2-ex2.cpp
@@ -14,6 +14,18 @@
 // RUN: -fmodule-file=%t/std-10-2-ex2-c.pcm -fmodule-file=X=%t/X.pcm \
 // RUN: -pedantic-errors -verify -o  %t/M.pcm
 
+// Test again with thin BMI.
+// RUN: %clang_cc1 -std=c++20 -emit-header-unit -I %t \
+// RUN: -xc++-user-header std-10-2-ex2-c.h -o %t/std-10-2-ex2-c.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std-10-2-ex2-tu1.cpp \
+// RUN:  -o  %t/X.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std-10-2-ex2-tu2.cpp \
+// RUN: -fmodule-file=%t/std-10-2-ex2-c.pcm -fmodule-file=X=%t/X.pcm \
+// RUN: -pedantic-errors -verify -o  %t/M.pcm
+
+
 //--- std-10-2-ex2-b.h
 int f();
 
diff --git a/clang/test/Modules/cxx20-10-2-ex5.cpp b/clang/test/Modules/cxx20-10-2-ex5.cpp
index 49c5934c8f2172d..c3555cf8f67eb05 100644
--- a/clang/test/Modules/cxx20-10-2-ex5.cpp
+++ b/clang/test/Modules/cxx20-10-2-ex5.cpp
@@ -13,6 +13,18 @@
 // RUN: %clang_cc1 -std=c++20 -emit-obj %t/std-10-2-ex5-tu3.cpp \
 // RUN:  -fmodule-file=M=%t/M.pcm -verify -o %t/main.o
 
+// Test again with thin BMI.
+// RUN: rm %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std-10-2-ex5-tu1.cpp \
+// RUN:  -o  %t/M.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std-10-2-ex5-tu2.cpp \
+// RUN:  -fmodule-file=M=%t/M.pcm -o  %t/tu-2.o
+
+// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std-10-2-ex5-tu3.cpp \
+// RUN:  -fmodule-file=M=%t/M.pcm -verify -o %t/main.o
+
+
 //--- std-10-2-ex5-tu1.cpp
 export module M;
 export struct X {
diff --git a/clang/test/Modules/cxx20-10-3-ex1.cpp b/clang/test/Modules/cxx20-10-3-ex1.cpp
index 5d6e2554f753b07..c87e6d084d10c37 100644
--- a/clang/test/Modules/cxx20-10-3-ex1.cpp
+++ b/clang/test/Modules/cxx20-10-3-ex1.cpp
@@ -14,6 +14,20 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-3-ex1-tu4.cpp \
 // RUN:  -fmodule-file=M:Part=%t/M_Part.pcm -o %t/M.pcm
 
+// Test again with thin BMI.
+// RUN: rm %t/M_PartImpl.pcm %t/M.pcm %t/M_Part.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex1-tu1.cpp \
+// RUN:  -o %t/M_PartImpl.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex1-tu2.cpp \
+// RUN:  -fmodule-file=M:PartImpl=%t/M_PartImpl.pcm -o %t/M.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex1-tu3.cpp \
+// RUN:  -o %t/M_Part.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex1-tu4.cpp \
+// RUN:  -fmodule-file=M:Part=%t/M_Part.pcm -o %t/M.pcm
+
 //--- std10-3-ex1-tu1.cpp
 module M:PartImpl;
 
diff --git a/clang/test/Modules/cxx20-10-3-ex2.cpp b/clang/test/Modules/cxx20-10-3-ex2.cpp
index b1d6d669c0a0e60..9367114181038e7 100644
--- a/clang/test/Modules/cxx20-10-3-ex2.cpp
+++ b/clang/test/Modules/cxx20-10-3-ex2.cpp
@@ -11,6 +11,16 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std10-3-ex2-tu3.cpp \
 // RUN:  -o %t/M.pcm -verify
 
+// Test again with thin BMI.
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex2-tu1.cpp \
+// RUN:  -o %t/M.pcm
+
+// RUN: %clang_cc1 -std=c++20 -S %t/std10-3-ex2-tu2.cpp \
+// RUN:  -fmodule-file=M=%t/M.pcm -o %t/tu_8.s -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/std10-3-ex2-tu3.cpp \
+// RUN:  -o %t/M.pcm -verify
+
 //--- std10-3-ex2-tu1.cpp
 export module M;
 
diff --git a/clang/test/Modules/cxx20-10-5-ex1.cpp b/clang/test/Modules/cxx20-10-5-ex1.cpp
index a83162c5c150171..2eb27d2c93c549a 100644
--- a/clang/test/Modules/cxx20-10-5-ex1.cpp
+++ b/clang/test/Modules/cxx20-10-5-ex1.cpp
@@ -11,6 +11,18 @@
 // RUN: %clang_cc1 -std=c++20 std-10-5-ex1-use.cpp  -fmodule-file=A=A.pcm \
 // RUN:    -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm A.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface std-10-5-ex1-interface.cpp \
+// RUN: -DBAD_FWD_DECL  -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface std-10-5-ex1-interface.cpp \
+// RUN: -o A.pcm
+
+// RUN: %clang_cc1 -std=c++20 std-10-5-ex1-use.cpp  -fmodule-file=A=A.pcm \
+// RUN:    -fsyntax-only -verify
+
+
 //--- std-10-5-ex1-interface.cpp
 
 export module A;
diff --git a/clang/test/Modules/cxx20-import-diagnostics-a.cpp b/clang/test/Modules/cxx20-import-diagnostics-a.cpp
index a5cf44ed82d5ff3..fb4d5d917a9e3b8 100644
--- a/clang/test/Modules/cxx20-import-diagnostics-a.cpp
+++ b/clang/test/Modules/cxx20-import-diagnostics-a.cpp
@@ -36,6 +36,45 @@
 // RUN: %clang_cc1 -std=c++20 -emit-obj %t/import-diags-tu11.cpp \
 // RUN:  -fmodule-file=C=%t/C.pcm  -o %t/impl.o
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu1.cpp \
+// RUN:  -o %t/B.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu2.cpp \
+// RUN:  -o %t/C.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu3.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -fmodule-file=C=%t/C.pcm -o %t/AOK1.pcm
+
+// RUN: %clang_cc1 -std=c++20 -S %t/import-diags-tu4.cpp \
+// RUN:  -fmodule-file=AOK1=%t/AOK1.pcm -fmodule-file=B=%t/B.pcm \
+// RUN:  -fmodule-file=C=%t/C.pcm -o %t/tu_3.s -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu5.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -fmodule-file=C=%t/C.pcm -o %t/BC.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -S %t/import-diags-tu6.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -fmodule-file=C=%t/C.pcm -o %t/tu_5.s -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu7.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -o %t/D.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu8.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -o %t/D.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -S %t/import-diags-tu9.cpp \
+// RUN:  -fmodule-file=B=%t/B.pcm -o %t/tu_8.s -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/import-diags-tu10.cpp \
+// RUN:  -o %t/B.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-obj %t/import-diags-tu11.cpp \
+// RUN:  -fmodule-file=C=%t/C.pcm  -o %t/impl.o
+
 // Test diagnostics for incorrect module import sequences.
 
 //--- import-diags-tu1.cpp
diff --git a/clang/test/Modules/cxx20-import-diagnostics-b.cpp b/clang/test/Modules/cxx20-import-diagnostics-b.cpp
index 7d432633552a25a..baba79c4384f669 100644
--- a/clang/test/Modules/cxx20-import-diagnostics-b.cpp
+++ b/clang/test/Modules/cxx20-import-diagnostics-b.cpp
@@ -22,6 +22,31 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface  %t/g.cpp \
 // RUN: -fmodule-file=a=%t/a.pcm -o %t/g.pcm -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/a.cpp -o %t/a.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/c.cpp \
+// RUN: -fmodule-file=a=%t/a.pcm -o %t/c.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/d.cpp \
+// RUN: -fmodule-file=a=%t/a.pcm -o %t/d.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/e.cpp \
+// RUN: -fmodule-file=a=%t/a.pcm -o %t/e.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/a-part.cpp \
+// RUN: -o %t/a-part.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/f.cpp \
+// RUN: -fmodule-file=a=%t/a.pcm -o %t/f.pcm -verify
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface  %t/g.cpp \
+// RUN: -fmodule-file=a=%t/a.pcm -o %t/g.pcm -verify
+
 //--- a.cpp
 export module a;
 
diff --git a/clang/test/Modules/cxx20-module-file-info-macros.cpp b/clang/test/Modules/cxx20-module-file-info-macros.cpp
index bc7df1c9f50b597..caa7fe49a89db1c 100644
--- a/clang/test/Modules/cxx20-module-file-info-macros.cpp
+++ b/clang/test/Modules/cxx20-module-file-info-macros.cpp
@@ -17,6 +17,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/named_module.cppm -emit-module-interface -o %t/M.pcm
 // RUN: %clang_cc1 -module-file-info %t/M.pcm | FileCheck %t/named_module.cppm
 
+// RUN: %clang_cc1 -std=c++20 %t/named_module.cppm -emit-thin-module-interface -o %t/M.pcm
+// RUN: %clang_cc1 -module-file-info %t/M.pcm | FileCheck %t/named_module.cppm
+
 //--- foo.h
 #pragma once
 #define FOO
diff --git a/clang/test/Modules/deduction-guide.cppm b/clang/test/Modules/deduction-guide.cppm
index 9c959a71365dac9..01ffc990bb06754 100644
--- a/clang/test/Modules/deduction-guide.cppm
+++ b/clang/test/Modules/deduction-guide.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-module-interface -o %t/Templ.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 
+// RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-thin-module-interface -o %t/Templ.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
 //--- foo.h
 template <typename T>
 class Templ {
diff --git a/clang/test/Modules/deduction-guide2.cppm b/clang/test/Modules/deduction-guide2.cppm
index a163c3656831019..c5ae73fe7bbc080 100644
--- a/clang/test/Modules/deduction-guide2.cppm
+++ b/clang/test/Modules/deduction-guide2.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-module-interface -o %t/Templ.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 
+// RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-thin-module-interface -o %t/Templ.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
 //--- Templ.cppm
 export module Templ;
 export template <typename T>
diff --git a/clang/test/Modules/deduction-guide3.cppm b/clang/test/Modules/deduction-guide3.cppm
index 8fa08a0625d7c89..38a817a9e33238b 100644
--- a/clang/test/Modules/deduction-guide3.cppm
+++ b/clang/test/Modules/deduction-guide3.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-module-interface -o %t/Templ.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 
+// RUN: %clang_cc1 -std=c++20 %t/Templ.cppm -emit-thin-module-interface -o %t/Templ.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
 //--- Templ.cppm
 export module Templ;
 template <typename T>
diff --git a/clang/test/Modules/derived_class.cpp b/clang/test/Modules/derived_class.cpp
index ee9e0ae4637ec72..c016d5b0477f4df 100644
--- a/clang/test/Modules/derived_class.cpp
+++ b/clang/test/Modules/derived_class.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-module-interface -o %t/foo.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -emit-thin-module-interface -o %t/foo.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
 //--- bar.h
 struct bar_base {
diff --git a/clang/test/Modules/duplicated-module-file-eq-module-name.cppm b/clang/test/Modules/duplicated-module-file-eq-module-name.cppm
index e86dbe2b941ef88..1d84a1ce3f6dc0a 100644
--- a/clang/test/Modules/duplicated-module-file-eq-module-name.cppm
+++ b/clang/test/Modules/duplicated-module-file-eq-module-name.cppm
@@ -8,6 +8,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 %t/u.cpp -fmodule-file=a=%t/unexist.pcm \
 // RUN:      -fmodule-file=a=%t/a.pcm -verify -fsyntax-only
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/u.cpp -fmodule-file=a=%t/unexist.pcm \
+// RUN:      -fmodule-file=a=%t/a.pcm -verify -fsyntax-only
 
 //--- a.cppm
 export module a;
diff --git a/clang/test/Modules/enum-class.cppm b/clang/test/Modules/enum-class.cppm
index 01ae8c0d8814da5..0c4f2079a3c5f6b 100644
--- a/clang/test/Modules/enum-class.cppm
+++ b/clang/test/Modules/enum-class.cppm
@@ -6,6 +6,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 
 //--- foo.h
 enum class foo {
diff --git a/clang/test/Modules/explicitly-specialized-template.cpp b/clang/test/Modules/explicitly-specialized-template.cpp
index 89677254ea739ac..0ccd8aa771f1847 100644
--- a/clang/test/Modules/explicitly-specialized-template.cpp
+++ b/clang/test/Modules/explicitly-specialized-template.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/X.cppm -emit-module-interface -o %t/X.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
+// RUN: %clang_cc1 -std=c++20 %t/X.cppm -emit-thin-module-interface -o %t/X.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+//
 //--- foo.h
 #ifndef FOO_H
 #define FOO_H
diff --git a/clang/test/Modules/export-language-linkage.cppm b/clang/test/Modules/export-language-linkage.cppm
index 331a620aaad0613..4927eb18b1523da 100644
--- a/clang/test/Modules/export-language-linkage.cppm
+++ b/clang/test/Modules/export-language-linkage.cppm
@@ -7,6 +7,11 @@
 // RUN: %clang_cc1 -std=c++20 %t/c.cppm -fsyntax-only -verify
 // RUN: %clang_cc1 -module-file-info %t/a.pcm | FileCheck %t/a.cppm
 
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -fsyntax-only -verify
+// RUN: %clang_cc1 -module-file-info %t/a.pcm | FileCheck %t/a.cppm
+
 //--- a.cppm
 export module a;
 export extern "C++" int foo() { return 43; }
diff --git a/clang/test/Modules/ftime-trace.cppm b/clang/test/Modules/ftime-trace.cppm
index 48cd4113ec7826c..f8c8f7dfb0b2baf 100644
--- a/clang/test/Modules/ftime-trace.cppm
+++ b/clang/test/Modules/ftime-trace.cppm
@@ -9,5 +9,14 @@
 // RUN: %clang_cc1 -std=c++20 %t/a.pcm -ftime-trace=%t/a.json -o -
 // RUN: ls %t | grep "a.json"
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/a.pcm -ftime-trace=%t/a.json -o -
+// RUN: ls %t | grep "a.json"
+
 //--- a.cppm
 export module a;
diff --git a/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm b/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm
index abcbec07f97de06..17740cf1f5fd270 100644
--- a/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm
+++ b/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm
@@ -8,6 +8,12 @@
 // RUN: %clang_cc1 -std=c++20 %t/D.cppm -I%t -emit-module-interface -o %t/D.pcm
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/D-part.cppm -I%t -fprebuilt-module-path=%t -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -I%t -emit-thin-module-interface -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/A.cppm -I%t -fprebuilt-module-path=%t -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/D.cppm -I%t -emit-thin-module-interface -o %t/D.pcm
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/D-part.cppm -I%t -fprebuilt-module-path=%t -verify
+
 //--- A.cppm
 module;
 export module baz:A;
diff --git a/clang/test/Modules/inconsistent-export.cppm b/clang/test/Modules/inconsistent-export.cppm
index 5e94d2b37b75785..14689407b45d594 100644
--- a/clang/test/Modules/inconsistent-export.cppm
+++ b/clang/test/Modules/inconsistent-export.cppm
@@ -9,6 +9,19 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 %t/use.cppm -fprebuilt-module-path=%t -emit-obj
 
+// Test again with thin BMI.
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/m-a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-thin-module-interface -o %t/m-b.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/m.cppm -emit-thin-module-interface -o %t/m.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/use.cppm -fprebuilt-module-path=%t -emit-obj
+
+
 //--- a.cppm
 export module m:a;
 namespace n {
diff --git a/clang/test/Modules/inherited_arg.cppm b/clang/test/Modules/inherited_arg.cppm
index eb66b70cdce3360..d638882e77980a1 100644
--- a/clang/test/Modules/inherited_arg.cppm
+++ b/clang/test/Modules/inherited_arg.cppm
@@ -7,6 +7,17 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A-B.cppm -I%t -emit-thin-module-interface -o %t/A-B.pcm
+// RUN: %clang_cc1 -std=c++20 %t/A-C.cppm -I%t -emit-thin-module-interface -o %t/A-C.pcm
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
+
 //--- foo.h
 template <typename U, typename T>
 class pair {};
diff --git a/clang/test/Modules/instantiation-argdep-lookup.cppm b/clang/test/Modules/instantiation-argdep-lookup.cppm
index fc9009a5bc13d5a..7d8ff9899cff0b0 100644
--- a/clang/test/Modules/instantiation-argdep-lookup.cppm
+++ b/clang/test/Modules/instantiation-argdep-lookup.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
 //
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+//
 //--- foo.h
 
 namespace ns {
diff --git a/clang/test/Modules/lambdas.cppm b/clang/test/Modules/lambdas.cppm
index 7f00cf6f8682ac4..4d15b1c7c3993a7 100644
--- a/clang/test/Modules/lambdas.cppm
+++ b/clang/test/Modules/lambdas.cppm
@@ -11,6 +11,21 @@
 // RUN:    -o %t/lambdas2.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
 // RUN:    -verify -DUSE_LAMBDA2
+//
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/lambdas.cppm -emit-thin-module-interface \
+// RUN:    -o %t/lambdas.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
+// RUN:    -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/lambdas2.cppm -emit-thin-module-interface \
+// RUN:    -o %t/lambdas2.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
+// RUN:    -verify -DUSE_LAMBDA2
 
 //--- lambdas.h
 auto l1 = []() constexpr -> int {
diff --git a/clang/test/Modules/merge-concepts-cxx-modules.cpp b/clang/test/Modules/merge-concepts-cxx-modules.cpp
index 3d4f8435531a88c..94a66a4bb0e9dfa 100644
--- a/clang/test/Modules/merge-concepts-cxx-modules.cpp
+++ b/clang/test/Modules/merge-concepts-cxx-modules.cpp
@@ -8,6 +8,18 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/conflicting.cppm -o %t/conflicting.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cppm -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/same_as.cppm -o %t/same_as.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -fprebuilt-module-path=%t %t/concepts.cppm -o %t/concepts.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -fprebuilt-module-path=%t %t/format.cppm -o %t/format.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/conflicting.cppm -o %t/conflicting.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cppm -fsyntax-only -verify
+
+
 //--- same_as.cppm
 export module same_as;
 export template <class T, class U>
diff --git a/clang/test/Modules/merge-constrained-friends.cpp b/clang/test/Modules/merge-constrained-friends.cpp
index 8f0e9ed83cf2962..ba482355f1c132b 100644
--- a/clang/test/Modules/merge-constrained-friends.cpp
+++ b/clang/test/Modules/merge-constrained-friends.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++23 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++23 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++23 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++23 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- A.cppm
 module;
 export module A;
diff --git a/clang/test/Modules/merge-lambdas.cppm b/clang/test/Modules/merge-lambdas.cppm
index a1d04ab4e234dd3..2225d50f4005f2d 100644
--- a/clang/test/Modules/merge-lambdas.cppm
+++ b/clang/test/Modules/merge-lambdas.cppm
@@ -6,6 +6,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -o %t/B.pcm
 // RUN: %clang_cc1 -std=c++20 %t/use.cppm -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-thin-module-interface -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 %t/use.cppm -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- lambda.h
 inline auto cmp = [](auto l, auto r) {
   return l < r;
diff --git a/clang/test/Modules/merge-requires-with-lambdas.cppm b/clang/test/Modules/merge-requires-with-lambdas.cppm
index 5767492047684b9..038b8e2f42d5d54 100644
--- a/clang/test/Modules/merge-requires-with-lambdas.cppm
+++ b/clang/test/Modules/merge-requires-with-lambdas.cppm
@@ -17,6 +17,25 @@
 // RUN: %clang_cc1 -std=c++20 %t/A3.cppm -emit-module-interface -o %t/A3.pcm
 // RUN: %clang_cc1 -std=c++20 %t/TestA3.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/A0.cppm -emit-thin-module-interface -o %t/A0.pcm
+// RUN: %clang_cc1 -std=c++20 %t/TestA.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/A1.cppm -emit-thin-module-interface -o %t/A1.pcm
+// RUN: %clang_cc1 -std=c++20 %t/TestA1.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/A2.cppm -emit-thin-module-interface -o %t/A2.pcm
+// RUN: %clang_cc1 -std=c++20 %t/TestA2.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/A3.cppm -emit-thin-module-interface -o %t/A3.pcm
+// RUN: %clang_cc1 -std=c++20 %t/TestA3.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
+
 //--- A.h
 template <class _Tp>
 concept A = requires(const _Tp& __t) { []<class __Up>(const __Up&) {}(__t); };
diff --git a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm b/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
index a451bfe7804d332..8fd225c894ac9f5 100644
--- a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
+++ b/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
@@ -7,6 +7,11 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/reexport2.cppm -o %t/reexport2.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cppm -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/var_def.cppm -o %t/var_def.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -fprebuilt-module-path=%t %t/reexport1.cppm -o %t/reexport1.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -fprebuilt-module-path=%t %t/reexport2.cppm -o %t/reexport2.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cppm -fsyntax-only -verify
+
 //--- use.cppm
 import reexport1;
 import reexport2;
diff --git a/clang/test/Modules/mismatch-diagnostics.cpp b/clang/test/Modules/mismatch-diagnostics.cpp
index f8ce987cfba5727..741ecf15680b05a 100644
--- a/clang/test/Modules/mismatch-diagnostics.cpp
+++ b/clang/test/Modules/mismatch-diagnostics.cpp
@@ -13,6 +13,17 @@
 // RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH \
 // RUN:     %t/use.cpp 2>&1 | FileCheck %s
 
+// Test again with thin BMI.
+// RUN: %clang_cc1 -triple %itanium_abi_triple                          \
+// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules       \
+// RUN:     -emit-thin-module-interface -pthread -DBUILD_MODULE              \
+// RUN:     %t/mismatching_module.cppm -o                               \
+// RUN:     %t/prebuilt_modules/mismatching_module.pcm
+//
+// RUN: not %clang_cc1 -triple %itanium_abi_triple -std=c++20           \
+// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH \
+// RUN:     %t/use.cpp 2>&1 | FileCheck %s
+
 //--- mismatching_module.cppm
 export module mismatching_module;
 
diff --git a/clang/test/Modules/module-init-duplicated-import.cppm b/clang/test/Modules/module-init-duplicated-import.cppm
index de0ce1962f10082..5a5632699ed2609 100644
--- a/clang/test/Modules/module-init-duplicated-import.cppm
+++ b/clang/test/Modules/module-init-duplicated-import.cppm
@@ -9,6 +9,17 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm  \
 // RUN:     -S -emit-llvm -o - | FileCheck %t/m.cppm
 
+// Test again with thin BMI.
+// Note that we can't use thin BMI here for m.cppm since it is required
+// to generate the backend code.
+// RUN: rm %t/a.pcm %t/m.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/a.cppm \
+// RUN:      -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.cppm \
+// RUN:      -emit-module-interface -fmodule-file=a=%t/a.pcm -o %t/m.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm  \
+// RUN:     -S -emit-llvm -o - | FileCheck %t/m.cppm
+
 //--- a.cppm
 export module a;
 export struct A {
diff --git a/clang/test/Modules/named-modules-adl-2.cppm b/clang/test/Modules/named-modules-adl-2.cppm
index 655acfcd93f69ab..e1e009199d398a2 100644
--- a/clang/test/Modules/named-modules-adl-2.cppm
+++ b/clang/test/Modules/named-modules-adl-2.cppm
@@ -6,6 +6,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -emit-module-interface -o %t/b.pcm
 // RUN: %clang_cc1 -std=c++20 %t/c.cppm -fmodule-file=a=%t/a.pcm -fmodule-file=b=%t/b.pcm -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -emit-thin-module-interface -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -fmodule-file=a=%t/a.pcm -fmodule-file=b=%t/b.pcm -fsyntax-only -verify
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/named-modules-adl-3.cppm b/clang/test/Modules/named-modules-adl-3.cppm
index 2fc2962c926b1b6..37119e3cc742f4a 100644
--- a/clang/test/Modules/named-modules-adl-3.cppm
+++ b/clang/test/Modules/named-modules-adl-3.cppm
@@ -14,6 +14,23 @@
 // RUN: %clang_cc1 -std=c++20 -DEXPORT_OPERATOR %t/c.cppm -fmodule-file=a=%t/a.pcm \
 // RUN:     -fmodule-file=b=%t/b.pcm -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm  -emit-thin-module-interface \
+// RUN:     -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -fmodule-file=a=%t/a.pcm -fmodule-file=b=%t/b.pcm \
+// RUN:     -fsyntax-only -verify
+//
+// RUN: %clang_cc1 -std=c++20 -DEXPORT_OPERATOR %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -DEXPORT_OPERATOR %t/b.cppm -fmodule-file=a=%t/a.pcm  \
+// RUN:     -emit-thin-module-interface -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 -DEXPORT_OPERATOR %t/c.cppm -fmodule-file=a=%t/a.pcm \
+// RUN:     -fmodule-file=b=%t/b.pcm -fsyntax-only -verify
+
 //--- foo.h
 namespace n {
 
diff --git a/clang/test/Modules/named-modules-adl.cppm b/clang/test/Modules/named-modules-adl.cppm
index d5133ef367265a8..655a57374bef54b 100644
--- a/clang/test/Modules/named-modules-adl.cppm
+++ b/clang/test/Modules/named-modules-adl.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
+
 //--- a.h
 namespace n {
 
diff --git a/clang/test/Modules/no-duplicate-codegen-in-GMF.cppm b/clang/test/Modules/no-duplicate-codegen-in-GMF.cppm
index e4d06415fabd453..b088288591bbef4 100644
--- a/clang/test/Modules/no-duplicate-codegen-in-GMF.cppm
+++ b/clang/test/Modules/no-duplicate-codegen-in-GMF.cppm
@@ -9,6 +9,14 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/B.pcm -S -emit-llvm -o - | FileCheck %t/B.cppm
 
+// Test again with thin BMI. Note that we need to generate fat BMI for B.cppm
+// since it is required to generate backend codes.
+// RUN: rm %t/A.pcm %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/B.cppm -emit-module-interface -o %t/B.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/B.pcm -S -emit-llvm -o - | FileCheck %t/B.cppm
+
 //--- foo.h
 
 template <class T>
diff --git a/clang/test/Modules/pair-unambiguous-ctor.cppm b/clang/test/Modules/pair-unambiguous-ctor.cppm
index eb242244260cbdf..3d5269904042df2 100644
--- a/clang/test/Modules/pair-unambiguous-ctor.cppm
+++ b/clang/test/Modules/pair-unambiguous-ctor.cppm
@@ -10,6 +10,15 @@
 // RUN: %clang_cc1 -std=c++20 %t/algorithm.cppm -I%t -emit-module-interface -o %t/std-algorithm.pcm
 // RUN: %clang_cc1 -std=c++20 %t/Use.cppm -I%t -fprebuilt-module-path=%t -emit-module-interface -verify -o %t/Use.pcm
 
+// Test again with thin BMI.
+// RUN: rm -fr %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/string.cppm -I%t -emit-thin-module-interface -o %t/std-string.pcm
+// RUN: %clang_cc1 -std=c++20 %t/algorithm.cppm -I%t -emit-thin-module-interface -o %t/std-algorithm.pcm
+// RUN: %clang_cc1 -std=c++20 %t/Use.cppm -I%t -fprebuilt-module-path=%t -emit-thin-module-interface -verify -o %t/Use.pcm
+
 //--- Use.cppm
 // expected-no-diagnostics
 module;
diff --git a/clang/test/Modules/partial_specialization.cppm b/clang/test/Modules/partial_specialization.cppm
index 3a01857172112e1..10e766a6bb834a9 100644
--- a/clang/test/Modules/partial_specialization.cppm
+++ b/clang/test/Modules/partial_specialization.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/A.cppm -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/A.cppm -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+//
 //--- foo.h
 template<typename T, typename U>
 inline constexpr bool IsSame = false;
diff --git a/clang/test/Modules/placement-new-reachable.cpp b/clang/test/Modules/placement-new-reachable.cpp
index 29263173d78f458..c6f6e264acf45a4 100644
--- a/clang/test/Modules/placement-new-reachable.cpp
+++ b/clang/test/Modules/placement-new-reachable.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- placement.h
 namespace std {
   using size_t = decltype(sizeof(0));
diff --git a/clang/test/Modules/polluted-operator.cppm b/clang/test/Modules/polluted-operator.cppm
index b24464aa6ad21ef..fa92a8fb1a684e7 100644
--- a/clang/test/Modules/polluted-operator.cppm
+++ b/clang/test/Modules/polluted-operator.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -emit-module-interface -o %t/b.pcm -verify
 
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -emit-thin-module-interface -o %t/b.pcm -verify
+
 //--- foo.h
 
 namespace std
diff --git a/clang/test/Modules/pr54457.cppm b/clang/test/Modules/pr54457.cppm
index ed67ec1065376ef..8a195dbe4958677 100644
--- a/clang/test/Modules/pr54457.cppm
+++ b/clang/test/Modules/pr54457.cppm
@@ -9,6 +9,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-module-interface -o %t/C.pcm
 // RUN: %clang_cc1 -std=c++20 %t/UseC.cppm -fprebuilt-module-path=%t -verify -S -o -
 
+// RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-thin-module-interface -o %t/C.pcm
+// RUN: %clang_cc1 -std=c++20 %t/UseC.cppm -fprebuilt-module-path=%t -verify -S -o -
+
 //--- A.cppm
 // expected-no-diagnostics
 export module A;
diff --git a/clang/test/Modules/pr56916.cppm b/clang/test/Modules/pr56916.cppm
index a435b06d5cf1523..98299af7c894129 100644
--- a/clang/test/Modules/pr56916.cppm
+++ b/clang/test/Modules/pr56916.cppm
@@ -8,6 +8,18 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fsyntax-only -fprebuilt-module-path=%t -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/M-A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-thin-module-interface -o %t/M-B.pcm
+// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-thin-module-interface -o %t/M.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fsyntax-only -fprebuilt-module-path=%t -verify
+
+
 //--- foo.h
 template <typename T>
 class Templ {
diff --git a/clang/test/Modules/pr58532.cppm b/clang/test/Modules/pr58532.cppm
index cf530b4ac2ccce5..cc584914ef3ca46 100644
--- a/clang/test/Modules/pr58532.cppm
+++ b/clang/test/Modules/pr58532.cppm
@@ -7,6 +7,12 @@
 // RUN: %clang_cc1 -std=c++20 %t/implementation.cpp -fmodule-file=m=%t/m.pcm \
 // RUN:     -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: %clang_cc1 -std=c++20 %t/interface.cppm -emit-thin-module-interface \
+// RUN:     -o %t/m.pcm
+// RUN: %clang_cc1 -std=c++20 %t/implementation.cpp -fmodule-file=m=%t/m.pcm \
+// RUN:     -fsyntax-only -verify
+
 //--- invisible.h
 #pragma once // This breaks things.
 const int kInvisibleSymbol = 0;
diff --git a/clang/test/Modules/pr58716.cppm b/clang/test/Modules/pr58716.cppm
index 3f97fca7d5e8a32..177802fe3afcb89 100644
--- a/clang/test/Modules/pr58716.cppm
+++ b/clang/test/Modules/pr58716.cppm
@@ -8,7 +8,7 @@
 //
 // RUN: %clang_cc1 -triple=x86_64-linux-gnu -std=c++20 -emit-module-interface %t/m.cppm -o %t/m.pcm
 // RUN: %clang_cc1 -triple=x86_64-linux-gnu -std=c++20 %t/m.pcm -S -emit-llvm -o - | FileCheck %t/m.cppm
-//
+
 //--- m.cppm
 module;
 #include "fail.h"
diff --git a/clang/test/Modules/pr59719.cppm b/clang/test/Modules/pr59719.cppm
index 5aea8992a0ca85b..f1ef69dc04d248f 100644
--- a/clang/test/Modules/pr59719.cppm
+++ b/clang/test/Modules/pr59719.cppm
@@ -7,6 +7,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/data.cppm -emit-module-interface -o %t/data.pcm
 // RUN: %clang_cc1 -std=c++20 %t/main.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/data.cppm -emit-thin-module-interface -o %t/data.pcm
+// RUN: %clang_cc1 -std=c++20 %t/main.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- foo.h
 namespace std {
 
diff --git a/clang/test/Modules/pr59780.cppm b/clang/test/Modules/pr59780.cppm
index d4bbd52c13f1a4a..211ed67fccf6aa7 100644
--- a/clang/test/Modules/pr59780.cppm
+++ b/clang/test/Modules/pr59780.cppm
@@ -9,6 +9,14 @@
 // RUN:     -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/use.cpp
 // RUN: %clang_cc1 -std=c++20 %t/a.pcm -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/a.cppm
 
+// Test again with thin BMI.
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -triple %itanium_abi_triple -emit-module-interface \
+// RUN:     -fthinBMI-output=%t/a.pcm -o %t/a.full.pcm
+// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -S \
+// RUN:     -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/use.cpp
+// RUN: %clang_cc1 -std=c++20 %t/a.full.pcm -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/a.cppm
+
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr59999.cppm b/clang/test/Modules/pr59999.cppm
index 23710de9fe1c559..da59da4b7c1405e 100644
--- a/clang/test/Modules/pr59999.cppm
+++ b/clang/test/Modules/pr59999.cppm
@@ -11,6 +11,19 @@
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Object.pcm \
 // RUN:     -fmodule-file=Module=%t/Module.pcm -S -emit-llvm -o - | FileCheck %t/Object.cppm
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Module.cppm \
+// RUN:     -emit-thin-module-interface -o %t/Module.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Object.cppm \
+// RUN:     -fmodule-file=Module=%t/Module.pcm -emit-module-interface -o %t/Object.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/Object.pcm \
+// RUN:     -fmodule-file=Module=%t/Module.pcm -S -emit-llvm -o - | FileCheck %t/Object.cppm
+
+
 //--- Module.cppm
 export module Module;
 
diff --git a/clang/test/Modules/pr60036.cppm b/clang/test/Modules/pr60036.cppm
index 297132cfde60bdb..02f6e4fb01cfd79 100644
--- a/clang/test/Modules/pr60036.cppm
+++ b/clang/test/Modules/pr60036.cppm
@@ -24,6 +24,20 @@
 // RUN:		-fmodule-file=c=%t/c.pcm -fmodule-file=d=%t/d.pcm -fmodule-file=e=%t/e.pcm \
 // RUN:		-fmodule-file=f=%t/f.pcm -verify -fsyntax-only 
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/c.pcm
+// RUN: %clang_cc1 -std=c++20 %t/d.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/d.pcm
+// RUN: %clang_cc1 -std=c++20 %t/e.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/e.pcm
+// RUN: %clang_cc1 -std=c++20 %t/f.cppm -emit-thin-module-interface -fprebuilt-module-path=%t -o %t/f.pcm
+// RUN: %clang_cc1 -std=c++20 %t/g.cppm -fprebuilt-module-path=%t -verify -fsyntax-only 
+
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr60085.cppm b/clang/test/Modules/pr60085.cppm
index fba601206404717..abe7bab3073edee 100644
--- a/clang/test/Modules/pr60085.cppm
+++ b/clang/test/Modules/pr60085.cppm
@@ -27,6 +27,23 @@
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.pcm \
 // RUN:     -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/a.cppm
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/d.cppm \
+// RUN:     -emit-thin-module-interface -o %t/d.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/c.cppm \
+// RUN:     -emit-thin-module-interface -o %t/c.pcm -fmodule-file=%t/d.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cppm \
+// RUN:     -emit-thin-module-interface -o %t/b.pcm -fmodule-file=%t/d.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.cppm \
+// RUN:     -emit-module-interface -o %t/a.pcm -fmodule-file=%t/d.pcm \
+// RUN:     -fmodule-file=%t/c.pcm -fmodule-file=%t/b.pcm 
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.pcm \
+// RUN:     -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/a.cppm
+
 //--- d.cppm
 export module d;
 
diff --git a/clang/test/Modules/pr60275.cppm b/clang/test/Modules/pr60275.cppm
index 57b31c6952bea94..48a199782ec93a2 100644
--- a/clang/test/Modules/pr60275.cppm
+++ b/clang/test/Modules/pr60275.cppm
@@ -5,7 +5,12 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -emit-module-interface %t/a.cppm -o %t/a.pcm
-// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cpp -fmodule-file=%t/a.pcm -emit-llvm -o - | FileCheck %t/b.cpp
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cpp -fmodule-file=a=%t/a.pcm -emit-llvm -o - | FileCheck %t/b.cpp
+
+// Test again with thin BMI
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cpp -fmodule-file=a=%t/a.pcm -emit-llvm -o - | FileCheck %t/b.cpp
+
 //--- foo.h
 
 consteval void global() {}
diff --git a/clang/test/Modules/pr60486.cppm b/clang/test/Modules/pr60486.cppm
index 13802a4917e6e7f..13d37771f2ce8ad 100644
--- a/clang/test/Modules/pr60486.cppm
+++ b/clang/test/Modules/pr60486.cppm
@@ -7,6 +7,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm %t/b.cppm -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm %t/b.cppm -fsyntax-only -verify
+
 //--- foo.h
 template<typename = void>
 struct s {
diff --git a/clang/test/Modules/pr60693.cppm b/clang/test/Modules/pr60693.cppm
index c50791083a5beae..08e3e4ab39ec3e4 100644
--- a/clang/test/Modules/pr60693.cppm
+++ b/clang/test/Modules/pr60693.cppm
@@ -7,6 +7,10 @@
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.cppm -emit-module-interface -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -fmodule-file=a=%t/a.pcm %t/c.cpp -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/c.cpp
 
+// Test again with thin BMI
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -fmodule-file=a=%t/a.pcm %t/c.cpp -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/c.cpp
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr60775.cppm b/clang/test/Modules/pr60775.cppm
index 4db027ba3600a98..2830b80a2101158 100644
--- a/clang/test/Modules/pr60775.cppm
+++ b/clang/test/Modules/pr60775.cppm
@@ -12,6 +12,19 @@
 // RUN: %clang_cc1 -std=c++20 %t/f.cppm -emit-module-interface -fmodule-file=c=%t/c.pcm -o %t/f.pcm
 // RUN: %clang_cc1 -std=c++20 %t/g.cpp -fmodule-file=f=%t/f.pcm -fmodule-file=c=%t/c.pcm  -verify -fsyntax-only
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -I%t -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cpp -fmodule-file=a=%t/a.pcm -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -I%t -emit-thin-module-interface -o %t/c.pcm
+// RUN: %clang_cc1 -std=c++20 %t/d.cppm -emit-thin-module-interface -fmodule-file=c=%t/c.pcm -o %t/d.pcm
+// RUN: %clang_cc1 -std=c++20 %t/e.cpp -fmodule-file=d=%t/d.pcm -fmodule-file=c=%t/c.pcm -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/f.cppm -emit-thin-module-interface -fmodule-file=c=%t/c.pcm -o %t/f.pcm
+// RUN: %clang_cc1 -std=c++20 %t/g.cpp -fmodule-file=f=%t/f.pcm -fmodule-file=c=%t/c.pcm  -verify -fsyntax-only
+
 //--- initializer_list.h
 namespace std {
   typedef decltype(sizeof(int)) size_t;
diff --git a/clang/test/Modules/pr60890.cppm b/clang/test/Modules/pr60890.cppm
index 2560bec5b43351a..9d3381e4de00015 100644
--- a/clang/test/Modules/pr60890.cppm
+++ b/clang/test/Modules/pr60890.cppm
@@ -9,6 +9,12 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/c.cppm -fprebuilt-module-path=%t -o %t/c.pcm
 // RUN: %clang_cc1 -std=c++20 %t/d.cpp -fprebuilt-module-path=%t -S -emit-llvm -o -
 
+// Test again with thin BMI
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/b.cppm -fprebuilt-module-path=%t -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/c.cppm -fprebuilt-module-path=%t -o %t/c.pcm
+// RUN: %clang_cc1 -std=c++20 %t/d.cpp -fprebuilt-module-path=%t -S -emit-llvm -o -
+
 //--- a.cppm
 export module a;
  
diff --git a/clang/test/Modules/pr61065.cppm b/clang/test/Modules/pr61065.cppm
index cf6fcdda78cd446..3437aaa33f4a6ad 100644
--- a/clang/test/Modules/pr61065.cppm
+++ b/clang/test/Modules/pr61065.cppm
@@ -10,6 +10,19 @@
 // DISABLED:     -fprebuilt-module-path=%t
 // DISABLED: %clang_cc1 -std=c++20 %t/d.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-thin-module-interface -o %t/b.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// DISABLED: %clang_cc1 -std=c++20 %t/c.cppm -emit-thin-module-interface -o %t/c.pcm \
+// DISABLED:     -fprebuilt-module-path=%t
+// DISABLED: %clang_cc1 -std=c++20 %t/d.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
+
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr61065_2.cppm b/clang/test/Modules/pr61065_2.cppm
index 10cc1a06b7e450a..9a815f8edf3e057 100644
--- a/clang/test/Modules/pr61065_2.cppm
+++ b/clang/test/Modules/pr61065_2.cppm
@@ -11,6 +11,21 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 %t/e.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-thin-module-interface -o %t/b.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -emit-thin-module-interface -o %t/c.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/d.cppm -emit-thin-module-interface -o %t/d.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/e.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
+
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr61067.cppm b/clang/test/Modules/pr61067.cppm
index 8469a1db1f1c8d0..9842b0a4f15f323 100644
--- a/clang/test/Modules/pr61067.cppm
+++ b/clang/test/Modules/pr61067.cppm
@@ -12,6 +12,20 @@
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/c.cpp -fmodule-file=a=%t/a.pcm \
 // RUN:     -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/c.cpp
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/a.cppm \
+// RUN:     -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.cppm \
+// RUN:     -emit-module-interface -fmodule-file=a=%t/a.pcm -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/b.pcm -S \
+// RUN:     -emit-llvm -disable-llvm-passes -o - | FileCheck %t/b.cppm
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple %t/c.cpp -fmodule-file=a=%t/a.pcm \
+// RUN:     -S -emit-llvm -disable-llvm-passes -o - | FileCheck %t/c.cpp
+
 //--- a.cppm
 export module a;
 
diff --git a/clang/test/Modules/pr61317.cppm b/clang/test/Modules/pr61317.cppm
index 4b54d26dc5a63bb..7b7ec80a6b953fa 100644
--- a/clang/test/Modules/pr61317.cppm
+++ b/clang/test/Modules/pr61317.cppm
@@ -8,6 +8,15 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-thin-module-interface -o %t/B.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- foo.h
 #ifndef _FOO
 #define _FOO
diff --git a/clang/test/Modules/pr61783.cppm b/clang/test/Modules/pr61783.cppm
index 9cf773b0b282baa..4555787f591735a 100644
--- a/clang/test/Modules/pr61783.cppm
+++ b/clang/test/Modules/pr61783.cppm
@@ -9,6 +9,14 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/user.cpp -fmodule-file=mod=%t/mod.pcm \
 // RUN:     -S -emit-llvm -o - | FileCheck %t/user.cpp
 
+// Test again with thin BMI
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/mod.cppm -emit-thin-module-interface \
+// RUN:     -o %t/mod.pcm
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/mod.pcm -S -emit-llvm -o - | \
+// RUN:     FileCheck %t/mod.cppm
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions %t/user.cpp -fmodule-file=mod=%t/mod.pcm \
+// RUN:     -S -emit-llvm -o - | FileCheck %t/user.cpp
+
 //--- mod.cppm
 module;
 
diff --git a/clang/test/Modules/pr61892.cppm b/clang/test/Modules/pr61892.cppm
index 99d02f36b2b54b3..99722543366a6ad 100644
--- a/clang/test/Modules/pr61892.cppm
+++ b/clang/test/Modules/pr61892.cppm
@@ -2,11 +2,25 @@
 // RUN: mkdir -p %t
 // RUN: split-file %s %t
 //
+// RUNX: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
+// RUNX:     -emit-module-interface %t/a.cppm -o %t/a.pcm
+// RUNX: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
+// RUNX:     %t/b.cpp -fmodule-file=a=%t/a.pcm -disable-llvm-passes \
+// RUNX:     -emit-llvm -o - | FileCheck %t/b.cpp
+// RUNX: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
+// RUNX:     %t/c.cpp -fmodule-file=a=%t/a.pcm -disable-llvm-passes \
+// RUNX:     -emit-llvm -o - | FileCheck %t/c.cpp
+
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
-// RUN:     -emit-module-interface %t/a.cppm -o %t/a.pcm
-// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
-// RUN:     %t/b.cpp -fmodule-file=a=%t/a.pcm -disable-llvm-passes \
-// RUN:     -emit-llvm -o - | FileCheck %t/b.cpp
+// RUN:     -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUNX: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
+// RUNX:     %t/b.cpp -fmodule-file=a=%t/a.pcm -disable-llvm-passes \
+// RUNX:     -emit-llvm -o - | FileCheck %t/b.cpp
 // RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple \
 // RUN:     %t/c.cpp -fmodule-file=a=%t/a.pcm -disable-llvm-passes \
 // RUN:     -emit-llvm -o - | FileCheck %t/c.cpp
@@ -23,20 +37,10 @@ struct integer {
 export template<typename>
 int a = static_cast<int>(integer());
 
-struct s {
-    ~s();
-    operator int() const;
-};
-
-export template<typename>
-auto d = s();
-
 int aa() {
-	return a<void> + d<void>;
+	return a<void>;
 }
 
-int dynamic_func();
-export inline int dynamic_var = dynamic_func();
 
 //--- b.cpp
 import a;
@@ -53,13 +57,9 @@ void b() {}
 //--- c.cpp
 import a;
 int c() {
-    return a<void> + d<void> + dynamic_var;
+    return a<void>;
 }
 
 // The used variables are generated normally
 // CHECK-DAG: @_ZW1a1aIvE =
-// CHECK-DAG: @_ZW1a1dIvE =
-// CHECK-DAG: @_ZW1a11dynamic_var = linkonce_odr
 // CHECK-DAG: @_ZGVW1a1aIvE =
-// CHECk-DAG: @_ZGVW1a1dIvE =
-// CHECK-DAG: @_ZGVW1a11dynamic_var = linkonce_odr
diff --git a/clang/test/Modules/pr62158.cppm b/clang/test/Modules/pr62158.cppm
index 7a0761df7715807..54acd8622dc94e0 100644
--- a/clang/test/Modules/pr62158.cppm
+++ b/clang/test/Modules/pr62158.cppm
@@ -6,6 +6,15 @@
 // RUN: %clang_cc1 -std=c++20 %t/main.cpp -fmodule-file=lib=%t/lib.pcm \
 // RUN:     -verify -fsyntax-only
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/lib.cppm -o %t/lib.pcm
+// RUN: %clang_cc1 -std=c++20 %t/main.cpp -fmodule-file=lib=%t/lib.pcm \
+// RUN:     -verify -fsyntax-only
+
 //--- header.h
 namespace lib::inline __1 {
 template <class>
diff --git a/clang/test/Modules/pr62359.cppm b/clang/test/Modules/pr62359.cppm
index 4632457e57f189e..82f790182909591 100644
--- a/clang/test/Modules/pr62359.cppm
+++ b/clang/test/Modules/pr62359.cppm
@@ -12,6 +12,22 @@
 // RUN: %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only -verify
 // RUN: %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only -verify
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/Hello.cppm -o %t/Hello.pcm
+// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN:     2>&1 | FileCheck %t/use.cpp
+// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN:     2>&1 | FileCheck %t/use2.cpp
+//
+// RUN: %clang_cc1 -std=c++20 -fopenmp -emit-thin-module-interface %t/Hello.cppm -o %t/Hello.pcm
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only -verify
+
+
 //--- Hello.cppm
 export module hello;
 export void hello() {
diff --git a/clang/test/Modules/pr62589.cppm b/clang/test/Modules/pr62589.cppm
index 4164c3405ac0e3b..98296575188fba9 100644
--- a/clang/test/Modules/pr62589.cppm
+++ b/clang/test/Modules/pr62589.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++23 -emit-module-interface %t/a.cppm -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++23 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++23 -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++23 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
+
 //--- foo.h
 class TypeA {};
 
diff --git a/clang/test/Modules/pr62705.cppm b/clang/test/Modules/pr62705.cppm
index a09bdf2563e84b2..587d5bc3942f275 100644
--- a/clang/test/Modules/pr62705.cppm
+++ b/clang/test/Modules/pr62705.cppm
@@ -10,6 +10,14 @@
 // RUN: %clang_cc1 %t/b.pcm -std=c++20 -triple %itanium_abi_triple \
 // RUN:     -emit-llvm -o - | FileCheck %t/b.cppm
 
+// RUN: %clang_cc1 %t/a.cppm -std=c++20 -triple %itanium_abi_triple \
+// RUN:     -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 %t/b.cppm -std=c++20 -triple %itanium_abi_triple \
+// RUN:     -emit-module-interface -o %t/b.pcm \
+// RUN:     -fmodule-file=a=%t/a.pcm
+// RUN: %clang_cc1 %t/b.pcm -std=c++20 -triple %itanium_abi_triple \
+// RUN:     -emit-llvm -o - | FileCheck %t/b.cppm
+
 //--- foo.h
 namespace n {
 
diff --git a/clang/test/Modules/pr62796.cppm b/clang/test/Modules/pr62796.cppm
index f96e54bc6adedec..0efd558c97c1e2d 100644
--- a/clang/test/Modules/pr62796.cppm
+++ b/clang/test/Modules/pr62796.cppm
@@ -6,6 +6,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fmodule-file=Fibonacci.Cache=%t/Cache.pcm \
 // RUN:     -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/Cache.cppm -o %t/Cache.pcm
+// RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fmodule-file=Fibonacci.Cache=%t/Cache.pcm \
+// RUN:     -fsyntax-only -verify
+
 //--- Cache.cppm
 export module Fibonacci.Cache;
 
diff --git a/clang/test/Modules/pr62943.cppm b/clang/test/Modules/pr62943.cppm
index 27868b78220f5c9..6da25d1e6a0663c 100644
--- a/clang/test/Modules/pr62943.cppm
+++ b/clang/test/Modules/pr62943.cppm
@@ -9,6 +9,18 @@
 // RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t \
 // RUN:     -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -emit-thin-module-interface -o %t/b.pcm
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -emit-thin-module-interface \
+// RUN:     -fprebuilt-module-path=%t -o %t/c.pcm
+// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t \
+// RUN:     -fsyntax-only -verify
+
 //--- foo.h
 #ifndef FOO_H
 #define FOO_H
diff --git a/clang/test/Modules/pr63544.cppm b/clang/test/Modules/pr63544.cppm
index 16224cfd0109491..16b9cd9580d0457 100644
--- a/clang/test/Modules/pr63544.cppm
+++ b/clang/test/Modules/pr63544.cppm
@@ -8,6 +8,18 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++23 %t/pr63544.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++23 %t/a.cppm -emit-thin-module-interface -o %t/m-a.pcm
+// RUN: %clang_cc1 -std=c++23 %t/b.cppm -emit-thin-module-interface -o %t/m-b.pcm
+// RUN: %clang_cc1 -std=c++23 %t/m.cppm -emit-thin-module-interface -o %t/m.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++23 %t/pr63544.cpp -fprebuilt-module-path=%t -fsyntax-only -verify
+
+
 //--- foo.h
 
 namespace std {
diff --git a/clang/test/Modules/pr63595.cppm b/clang/test/Modules/pr63595.cppm
index 13a5f84a3e71f22..604553ab2ad1698 100644
--- a/clang/test/Modules/pr63595.cppm
+++ b/clang/test/Modules/pr63595.cppm
@@ -6,6 +6,16 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface -I%t %t/module2.cppm -o %t/module2.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/merge.cpp -verify -fsyntax-only
 
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -I%t %t/module1.cppm -o %t/module1.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface -I%t %t/module2.cppm -o %t/module2.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/merge.cpp -verify -fsyntax-only
+
+
 //--- header.h
 namespace NS {
 template <int I>
diff --git a/clang/test/Modules/pr67627.cppm b/clang/test/Modules/pr67627.cppm
index 3d4410229080a98..c59ca8ed9033140 100644
--- a/clang/test/Modules/pr67627.cppm
+++ b/clang/test/Modules/pr67627.cppm
@@ -5,6 +5,10 @@
 // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
 // RUN: %clang_cc1 -std=c++20 %t/B.cppm -fmodule-file=A=%t/A.pcm -fsyntax-only -verify
 
+// RUN: rm %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -fmodule-file=A=%t/A.pcm -fsyntax-only -verify
+
 //--- A.cppm
 export module A;
 
diff --git a/clang/test/Modules/pr67893.cppm b/clang/test/Modules/pr67893.cppm
index 7d4e4c1dc5d843c..1ecf367a3217b4c 100644
--- a/clang/test/Modules/pr67893.cppm
+++ b/clang/test/Modules/pr67893.cppm
@@ -9,6 +9,18 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm  \
 // RUN:     -S -emit-llvm -o - | FileCheck %t/m.cppm
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/a.cppm \
+// RUN:      -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.cppm \
+// RUN:      -emit-thin-module-interface -fmodule-file=a=%t/a.pcm -o %t/m.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/m.pcm  \
+// RUN:     -S -emit-llvm -o - | FileCheck %t/m.cppm
+
 //--- a.cppm
 export module a;
 export struct A {
diff --git a/clang/test/Modules/predefined.cpp b/clang/test/Modules/predefined.cpp
index fbe0c4e23ca59cf..5d755adff76aa48 100644
--- a/clang/test/Modules/predefined.cpp
+++ b/clang/test/Modules/predefined.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -x c++ -std=c++20 -emit-module-interface a.h -o a.pcm -fms-extensions -verify
 // RUN: %clang_cc1 -std=c++20 a.cpp -fmodule-file=A=a.pcm -fms-extensions -fsyntax-only -verify
 
+// RUN: %clang_cc1 -x c++ -std=c++20 -emit-thin-module-interface a.h -o a.pcm -fms-extensions -verify
+// RUN: %clang_cc1 -std=c++20 a.cpp -fmodule-file=A=a.pcm -fms-extensions -fsyntax-only -verify
+
 //--- a.h
 
 // expected-no-diagnostics
diff --git a/clang/test/Modules/preferred_name.cppm b/clang/test/Modules/preferred_name.cppm
index 46ad96cb1abc33c..734c0d0b15b4c5e 100644
--- a/clang/test/Modules/preferred_name.cppm
+++ b/clang/test/Modules/preferred_name.cppm
@@ -8,6 +8,16 @@
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cppm -verify -fsyntax-only
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use1.cpp -verify -fsyntax-only
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use2.cpp -verify -fsyntax-only
+
+// Test again with thin BMI.
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-thin-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use.cppm -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use1.cpp -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/Use2.cpp -verify -fsyntax-only
 //
 //--- foo.h
 template<class _CharT>
diff --git a/clang/test/Modules/redefinition-merges.cppm b/clang/test/Modules/redefinition-merges.cppm
index 9ab4006f985fa94..71f49c31689ddb3 100644
--- a/clang/test/Modules/redefinition-merges.cppm
+++ b/clang/test/Modules/redefinition-merges.cppm
@@ -12,6 +12,12 @@
 // RUN: %clang_cc1 -std=c++20 -I%t %t/M.cppm -emit-module-interface -o %t/M.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use1.cpp -verify -fsyntax-only
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use2.cpp -verify -fsyntax-only
+
+// / Test again with thin BMI.
+// RUN: %clang_cc1 -std=c++20 -I%t %t/M.cppm -emit-thin-module-interface -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use1.cpp -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use2.cpp -verify -fsyntax-only
+
 //
 //--- foo.h
 #ifndef FOO
diff --git a/clang/test/Modules/redundant-template-default-arg.cpp b/clang/test/Modules/redundant-template-default-arg.cpp
index 6807b45e513954c..2ed47336ebc819b 100644
--- a/clang/test/Modules/redundant-template-default-arg.cpp
+++ b/clang/test/Modules/redundant-template-default-arg.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1  -std=c++20 %t/foo.cppm -I%t -emit-module-interface -o %t/foo.pcm
 // RUN: %clang_cc1  -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -I%t -fsyntax-only -verify
 
+// RUN: %clang_cc1  -std=c++20 %t/foo.cppm -I%t -emit-thin-module-interface -o %t/foo.pcm
+// RUN: %clang_cc1  -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -I%t -fsyntax-only -verify
+
 //--- foo.h
 template <typename T>
 T u;
diff --git a/clang/test/Modules/redundant-template-default-arg2.cpp b/clang/test/Modules/redundant-template-default-arg2.cpp
index 41deb112cfa6eae..7ceece1ac977531 100644
--- a/clang/test/Modules/redundant-template-default-arg2.cpp
+++ b/clang/test/Modules/redundant-template-default-arg2.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 %t/foo.cppm -I%t -emit-module-interface -o %t/foo.pcm
 // RUN: %clang_cc1 -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 %t/foo.cppm -I%t -emit-thin-module-interface -o %t/foo.pcm
+// RUN: %clang_cc1 -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -fsyntax-only -verify
+
 //--- foo.cppm
 export module foo;
 export template <typename T = int>
diff --git a/clang/test/Modules/redundant-template-default-arg3.cpp b/clang/test/Modules/redundant-template-default-arg3.cpp
index 8bb222ac91ffce1..71414e526d2c8a3 100644
--- a/clang/test/Modules/redundant-template-default-arg3.cpp
+++ b/clang/test/Modules/redundant-template-default-arg3.cpp
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1  -std=c++20 %t/foo.cppm -I%t -emit-module-interface -o %t/foo.pcm
 // RUN: %clang_cc1  -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -I%t/. -fsyntax-only -verify
 
+// RUN: %clang_cc1  -std=c++20 %t/foo.cppm -I%t -emit-thin-module-interface -o %t/foo.pcm
+// RUN: %clang_cc1  -fprebuilt-module-path=%t -std=c++20 %t/use.cpp -I%t/. -fsyntax-only -verify
+
 //--- foo.h
 template <typename T = int>
 T v;
diff --git a/clang/test/Modules/search-partitions.cpp b/clang/test/Modules/search-partitions.cpp
index 571160def7e9b7b..8b69e5b0a9ae9df 100644
--- a/clang/test/Modules/search-partitions.cpp
+++ b/clang/test/Modules/search-partitions.cpp
@@ -14,6 +14,22 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/moduleA.cpp \
 // RUN:  -fprebuilt-module-path=%t
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/partition1.cpp \
+// RUN:  -o %t/A-Part1.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/partition2.cpp \
+// RUN:  -o %t/A-Part2.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/partition3.cpp \
+// RUN:  -o %t/A-Part3.pcm
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/moduleA.cpp -fprebuilt-module-path=%t 
+
 // expected-no-diagnostics
 
 //--- partition1.cpp
diff --git a/clang/test/Modules/seperated-member-function-definition-for-template-class.cppm b/clang/test/Modules/seperated-member-function-definition-for-template-class.cppm
index e32da39d9df1af4..658c73cc78e843f 100644
--- a/clang/test/Modules/seperated-member-function-definition-for-template-class.cppm
+++ b/clang/test/Modules/seperated-member-function-definition-for-template-class.cppm
@@ -12,6 +12,18 @@
 // RUN:     -fprebuilt-module-path=%t
 // RUN: %clang_cc1 -std=c++20 %t/use.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/base.cppm -emit-thin-module-interface -o %t/package-base.pcm
+// RUN: %clang_cc1 -std=c++20 %t/child.cppm -emit-thin-module-interface -o %t/package-child.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/package.cppm -emit-thin-module-interface -o %t/package.pcm \
+// RUN:     -fprebuilt-module-path=%t
+// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fsyntax-only -verify -fprebuilt-module-path=%t
+
 //--- base.cppm
 export module package:base;
 
diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp
index 3eac92e7edb94c9..204c8bad5e45415 100644
--- a/clang/test/Modules/template-function-specialization.cpp
+++ b/clang/test/Modules/template-function-specialization.cpp
@@ -4,7 +4,10 @@
 //
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/foo.cppm -o %t/foo.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
-//
+
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/foo.cppm -o %t/foo.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only
+
 //--- foo.cppm
 module;
 # 3 __FILE__ 1 // use the next physical line number here (and below)
diff --git a/clang/test/Modules/template-lambdas.cppm b/clang/test/Modules/template-lambdas.cppm
index 69117a1a04fc7b6..4f223e456774125 100644
--- a/clang/test/Modules/template-lambdas.cppm
+++ b/clang/test/Modules/template-lambdas.cppm
@@ -12,6 +12,21 @@
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
 // RUN:    -verify -DUSE_LAMBDA2
 
+// Test again with thin BMI
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/template_lambdas.cppm -emit-thin-module-interface \
+// RUN:    -o %t/lambdas.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
+// RUN:    -verify
+//
+// RUN: %clang_cc1 -std=c++20 %t/template_lambdas2.cppm -emit-thin-module-interface \
+// RUN:    -o %t/lambdas2.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \
+// RUN:    -verify -DUSE_LAMBDA2
+
 //--- lambdas.h
 auto l1 = []<int I>() constexpr -> int {
     return I;
diff --git a/clang/test/Modules/template-pack.cppm b/clang/test/Modules/template-pack.cppm
index eca17f31f015e5e..e46901a9ee405a8 100644
--- a/clang/test/Modules/template-pack.cppm
+++ b/clang/test/Modules/template-pack.cppm
@@ -5,6 +5,9 @@
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm
 // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -fsyntax-only -verify
 
+// RUN: %clang_cc1 -std=c++20 -emit-thin-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -fsyntax-only -verify
+
 //--- foo.h
 
 namespace std
diff --git a/clang/test/Modules/template_default_argument.cpp b/clang/test/Modules/template_default_argument.cpp
index 5a7d1c04cf18175..00af7ca9687c692 100644
--- a/clang/test/Modules/template_default_argument.cpp
+++ b/clang/test/Modules/template_default_argument.cpp
@@ -4,6 +4,9 @@
 //
 // RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-module-interface -o %t/B.pcm
 // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
+
+// RUN: %clang_cc1 -std=c++20 %t/B.cppm -emit-thin-module-interface -o %t/B.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only -verify
 //
 //--- templ.h
 template <typename T, typename U = T>
diff --git a/clang/unittests/Sema/SemaNoloadLookupTest.cpp b/clang/unittests/Sema/SemaNoloadLookupTest.cpp
index b24c72cba407f3a..aa259cda4a84c2c 100644
--- a/clang/unittests/Sema/SemaNoloadLookupTest.cpp
+++ b/clang/unittests/Sema/SemaNoloadLookupTest.cpp
@@ -64,7 +64,7 @@ class NoloadLookupTest : public ::testing::Test {
     CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
 
     std::string CacheBMIPath =
-        llvm::Twine(TestDir + "/" + ModuleName + " .pcm").str();
+        llvm::Twine(TestDir + "/" + ModuleName + ".pcm").str();
     std::string PrebuiltModulePath =
         "-fprebuilt-module-path=" + TestDir.str().str();
     const char *Args[] = {"clang++",
@@ -75,9 +75,7 @@ class NoloadLookupTest : public ::testing::Test {
                           TestDir.c_str(),
                           "-I",
                           TestDir.c_str(),
-                          FileName.c_str(),
-                          "-o",
-                          CacheBMIPath.c_str()};
+                          FileName.c_str()};
     std::shared_ptr<CompilerInvocation> Invocation =
         createInvocation(Args, CIOpts);
     EXPECT_TRUE(Invocation);
@@ -85,7 +83,8 @@ class NoloadLookupTest : public ::testing::Test {
     CompilerInstance Instance;
     Instance.setDiagnostics(Diags.get());
     Instance.setInvocation(Invocation);
-    GenerateModuleInterfaceAction Action;
+    Instance.getFrontendOpts().OutputFile = CacheBMIPath;
+    GenerateThinModuleInterfaceAction Action;
     EXPECT_TRUE(Instance.ExecuteAction(Action));
     EXPECT_FALSE(Diags->hasErrorOccurred());
 
diff --git a/clang/unittests/Serialization/ForceCheckFileInputTest.cpp b/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
index ed0daa43436eb6d..15a0aa78d2169eb 100644
--- a/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
+++ b/clang/unittests/Serialization/ForceCheckFileInputTest.cpp
@@ -69,9 +69,9 @@ export int aa = 43;
     CIOpts.Diags = Diags;
     CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
 
-    const char *Args[] = {
-        "clang++",       "-std=c++20", "--precompile", "-working-directory",
-        TestDir.c_str(), "a.cppm",     "-o",           BMIPath.c_str()};
+    const char *Args[] = {"clang++",       "-std=c++20",
+                          "--precompile",  "-working-directory",
+                          TestDir.c_str(), "a.cppm"};
     std::shared_ptr<CompilerInvocation> Invocation =
         createInvocation(Args, CIOpts);
     EXPECT_TRUE(Invocation);
@@ -88,6 +88,8 @@ export int aa = 43;
     Instance.setDiagnostics(Diags.get());
     Instance.setInvocation(Invocation);
 
+    Instance.getFrontendOpts().OutputFile = BMIPath;
+
     if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
             Instance.getInvocation(), Instance.getDiagnostics(), CIOpts.VFS))
       CIOpts.VFS = VFSWithRemapping;
@@ -95,7 +97,7 @@ export int aa = 43;
 
     Instance.getHeaderSearchOpts().ValidateASTInputFilesContent = true;
 
-    GenerateModuleInterfaceAction Action;
+    GenerateThinModuleInterfaceAction Action;
     EXPECT_TRUE(Instance.ExecuteAction(Action));
     EXPECT_FALSE(Diags->hasErrorOccurred());
   }
diff --git a/clang/unittests/Serialization/NoCommentsTest.cpp b/clang/unittests/Serialization/NoCommentsTest.cpp
index 2632a6337807acf..6a5b9251303d3b7 100644
--- a/clang/unittests/Serialization/NoCommentsTest.cpp
+++ b/clang/unittests/Serialization/NoCommentsTest.cpp
@@ -90,9 +90,9 @@ void foo() {}
   CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
 
   std::string CacheBMIPath = llvm::Twine(TestDir + "/Comments.pcm").str();
-  const char *Args[] = {
-      "clang++",       "-std=c++20",    "--precompile", "-working-directory",
-      TestDir.c_str(), "Comments.cppm", "-o",           CacheBMIPath.c_str()};
+  const char *Args[] = {"clang++",       "-std=c++20",
+                        "--precompile",  "-working-directory",
+                        TestDir.c_str(), "Comments.cppm"};
   std::shared_ptr<CompilerInvocation> Invocation =
       createInvocation(Args, CIOpts);
   ASSERT_TRUE(Invocation);
@@ -100,7 +100,8 @@ void foo() {}
   CompilerInstance Instance;
   Instance.setDiagnostics(Diags.get());
   Instance.setInvocation(Invocation);
-  GenerateModuleInterfaceAction Action;
+  Instance.getFrontendOpts().OutputFile = CacheBMIPath;
+  GenerateThinModuleInterfaceAction Action;
   ASSERT_TRUE(Instance.ExecuteAction(Action));
   ASSERT_FALSE(Diags->hasErrorOccurred());
 
diff --git a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
index 86ae929e7f17e43..a6db2b0ac9068fa 100644
--- a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
+++ b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp
@@ -96,10 +96,9 @@ export namespace Fibonacci
   CIOpts.Diags = Diags;
   CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
 
-  std::string CacheBMIPath = llvm::Twine(TestDir + "/Cached.pcm").str();
-  const char *Args[] = {
-      "clang++",       "-std=c++20",  "--precompile", "-working-directory",
-      TestDir.c_str(), "Cached.cppm", "-o",           CacheBMIPath.c_str()};
+  const char *Args[] = {"clang++",       "-std=c++20",
+                        "--precompile",  "-working-directory",
+                        TestDir.c_str(), "Cached.cppm"};
   std::shared_ptr<CompilerInvocation> Invocation =
       createInvocation(Args, CIOpts);
   ASSERT_TRUE(Invocation);
@@ -107,7 +106,11 @@ export namespace Fibonacci
   CompilerInstance Instance;
   Instance.setDiagnostics(Diags.get());
   Instance.setInvocation(Invocation);
-  GenerateModuleInterfaceAction Action;
+
+  std::string CacheBMIPath = llvm::Twine(TestDir + "/Cached.pcm").str();
+  Instance.getFrontendOpts().OutputFile = CacheBMIPath;
+
+  GenerateThinModuleInterfaceAction Action;
   ASSERT_TRUE(Instance.ExecuteAction(Action));
   ASSERT_FALSE(Diags->hasErrorOccurred());
 

>From 387f312012ed04cb579e04c1f729e9cdaa0e4d1f Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Wed, 8 Nov 2023 10:51:55 +0800
Subject: [PATCH 2/3] fmt

---
 clang/lib/Serialization/ASTWriterDecl.cpp | 2 +-
 clang/lib/Serialization/GeneratePCH.cpp   | 9 +++++----
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 00a04ee2fa730d1..ca7794b8c5f9773 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -283,7 +283,7 @@ bool clang::MayDefAffectABI(const Decl *D) {
     // use, no matter whether they've been explicitly instantiated etc.
     if (!FD->isUserProvided())
       return true;
-    
+
     if (FD->isDependentContext())
       return true;
 
diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp
index a51047a148c93f0..4bceb986a1e586f 100644
--- a/clang/lib/Serialization/GeneratePCH.cpp
+++ b/clang/lib/Serialization/GeneratePCH.cpp
@@ -82,10 +82,11 @@ ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() {
   return &Writer;
 }
 
-ThinBMIGenerator::ThinBMIGenerator(
-    const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
-    StringRef OutputFile, std::shared_ptr<PCHBuffer> Buffer,
-    bool IncludeTimestamps)
+ThinBMIGenerator::ThinBMIGenerator(const Preprocessor &PP,
+                                   InMemoryModuleCache &ModuleCache,
+                                   StringRef OutputFile,
+                                   std::shared_ptr<PCHBuffer> Buffer,
+                                   bool IncludeTimestamps)
     : PCHGenerator(
           PP, ModuleCache, OutputFile, llvm::StringRef(), Buffer,
           /*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(),

>From 561b8a1ac2a94761a9bf190c6ad2b8785ce9e072 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Thu, 2 Nov 2023 18:36:57 +0800
Subject: [PATCH 3/3] [C++20] [Modules] Introduce Decls Hash in BMI

---
 clang/include/clang/AST/ODRHash.h             |   3 +
 clang/include/clang/Driver/Options.td         |   3 +
 .../include/clang/Frontend/FrontendActions.h  |  34 +-
 .../include/clang/Frontend/FrontendOptions.h  |   3 +
 .../include/clang/Serialization/ASTBitCodes.h |   3 +
 clang/include/clang/Serialization/ASTReader.h |  10 +
 clang/include/clang/Serialization/ASTWriter.h |   5 +
 clang/lib/AST/ODRHash.cpp                     |   2 +
 clang/lib/Driver/Driver.cpp                   |   7 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |   5 +-
 clang/lib/Frontend/CompilerInvocation.cpp     |   2 +
 clang/lib/Frontend/FrontendActions.cpp        |  49 +-
 .../ExecuteCompilerInvocation.cpp             |   2 +
 clang/lib/Serialization/ASTReader.cpp         |  75 ++
 clang/lib/Serialization/ASTWriter.cpp         |  10 +
 clang/lib/Serialization/ASTWriterDecl.cpp     | 121 +++
 clang/test/Modules/cxx20-module-file-info.cpp |   3 +
 .../decls-hash-get-bmi-decls-hash.cppm        |  35 +
 .../Modules/decls-hash-module-file-info.cppm  |  35 +
 .../Serialization/BMIDeclsHashTest.cpp        | 952 ++++++++++++++++++
 clang/unittests/Serialization/CMakeLists.txt  |   1 +
 21 files changed, 1340 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/Modules/decls-hash-get-bmi-decls-hash.cppm
 create mode 100644 clang/test/Modules/decls-hash-module-file-info.cppm
 create mode 100644 clang/unittests/Serialization/BMIDeclsHashTest.cpp

diff --git a/clang/include/clang/AST/ODRHash.h b/clang/include/clang/AST/ODRHash.h
index cedf644520fc320..5f5d8f99402edce 100644
--- a/clang/include/clang/AST/ODRHash.h
+++ b/clang/include/clang/AST/ODRHash.h
@@ -101,6 +101,9 @@ class ODRHash {
   // Save booleans until the end to lower the size of data to process.
   void AddBoolean(bool value);
 
+  // Add intergeers to ID.
+  void AddInteger(unsigned value);
+
   static bool isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent);
 
 private:
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 12785f280183e03..1294b3ae1be29e9 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5093,6 +5093,9 @@ def muclibc : Flag<["-"], "muclibc">, Group<m_libc_Group>, Flags<[HelpHidden]>;
 def module_file_info : Flag<["-"], "module-file-info">, Flags<[]>,
   Visibility<[ClangOption, CC1Option]>, Group<Action_Group>,
   HelpText<"Provide information about a particular module file">;
+def get_bmi_decls_hash : Flag<["-"], "get-bmi-decls-hash">, Flags<[]>,
+  Visibility<[ClangOption, CC1Option]>, Group<Action_Group>,
+  HelpText<"Get the BMI Decls hash value for a particular module file">;
 def mthumb : Flag<["-"], "mthumb">, Group<m_Group>;
 def mtune_EQ : Joined<["-"], "mtune=">, Group<m_Group>,
   HelpText<"Only supported on AArch64, PowerPC, RISC-V, SystemZ, and X86">;
diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h
index c419deb80034fae..d4ff4f8f6c57d91 100644
--- a/clang/include/clang/Frontend/FrontendActions.h
+++ b/clang/include/clang/Frontend/FrontendActions.h
@@ -190,9 +190,8 @@ class SyntaxOnlyAction : public ASTFrontendAction {
   bool hasCodeCompletionSupport() const override { return true; }
 };
 
-/// Dump information about the given module file, to be used for
-/// basic debugging and discovery.
-class DumpModuleInfoAction : public ASTFrontendAction {
+// Base action for dumping module informations.
+class DumpModuleInfoActionBase : public ASTFrontendAction {
   // Allow other tools (ex lldb) to direct output for their use.
   std::shared_ptr<llvm::raw_ostream> OutputStream;
 
@@ -200,11 +199,12 @@ class DumpModuleInfoAction : public ASTFrontendAction {
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef InFile) override;
   bool BeginInvocation(CompilerInstance &CI) override;
-  void ExecuteAction() override;
+  // Setup the output file.
+  llvm::raw_ostream &getOutputStream();
 
 public:
-  DumpModuleInfoAction() = default;
-  explicit DumpModuleInfoAction(std::shared_ptr<llvm::raw_ostream> Out)
+  DumpModuleInfoActionBase() = default;
+  explicit DumpModuleInfoActionBase(std::shared_ptr<llvm::raw_ostream> Out)
       : OutputStream(Out) {}
   bool hasPCHSupport() const override { return false; }
   bool hasASTFileSupport() const override { return true; }
@@ -212,6 +212,28 @@ class DumpModuleInfoAction : public ASTFrontendAction {
   bool hasCodeCompletionSupport() const override { return false; }
 };
 
+/// Dump information about the given module file, to be used for
+/// basic debugging and discovery.
+class DumpModuleInfoAction : public DumpModuleInfoActionBase {
+  void ExecuteAction() override;
+
+public:
+  DumpModuleInfoAction() = default;
+  explicit DumpModuleInfoAction(std::shared_ptr<llvm::raw_ostream> Out)
+      : DumpModuleInfoActionBase(Out) {}
+};
+
+/// Get the modules decl hash value action. The information is contained by
+/// DumpModuleInfoAction too. But this should be much faster.
+class GetModuleDeclsHashAction : public DumpModuleInfoActionBase {
+  void ExecuteAction() override;
+
+public:
+  GetModuleDeclsHashAction() = default;
+  explicit GetModuleDeclsHashAction(std::shared_ptr<llvm::raw_ostream> Out)
+      : DumpModuleInfoActionBase(Out) {}
+};
+
 class VerifyPCHAction : public ASTFrontendAction {
 protected:
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index 719d4ca81336116..a4a23f3ca03f5be 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -107,6 +107,9 @@ enum ActionKind {
   /// Dump information about a module file.
   ModuleFileInfo,
 
+  /// Get BMI Decls Hash about a module file.
+  GetBMIDeclsHash,
+
   /// Load and verify that a PCH file is usable.
   VerifyPCH,
 
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 5c32fbc079c9a65..72a93bbb2ebd801 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -695,6 +695,9 @@ enum ASTRecordTypes {
   /// Record code for an unterminated \#pragma clang assume_nonnull begin
   /// recorded in a preamble.
   PP_ASSUME_NONNULL_LOC = 67,
+
+  /// Record code for the decls hash in the thin BMI.
+  BMI_DECLS_HASH = 68,
 };
 
 /// Record types used within a source manager block.
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 7eefdca6815cdad..a46ed6a95aae96f 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -937,6 +937,9 @@ class ASTReader
   /// Sema tracks these to emit deferred diags.
   llvm::SmallSetVector<serialization::DeclID, 4> DeclsToCheckForDeferredDiags;
 
+  /// The hash value of read C++20 thin BMI.
+  std::optional<uint64_t> ReadedBMIDeclsHash;
+
 private:
   struct ImportedSubmodule {
     serialization::SubmoduleID ID;
@@ -1794,6 +1797,13 @@ class ASTReader
                                   StringRef ExistingModuleCachePath,
                                   bool RequireStrictOptionMatches = false);
 
+  static std::optional<uint64_t> getBMIHash(StringRef Filename,
+                                            FileManager &FileMgr);
+
+  std::optional<uint64_t> getReadedBMIDeclsHash() const {
+    return ReadedBMIDeclsHash;
+  }
+
   /// Returns the suggested contents of the predefines buffer,
   /// which contains a (typically-empty) subset of the predefines
   /// build prior to including the precompiled header.
diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index 3ccb35aa72f9b01..5dfeaf9d6c62ba0 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -170,6 +170,11 @@ class ASTWriter : public ASTDeserializationListener,
   /// named modules.
   bool GeneratingThinBMI = false;
 
+  /// The hash for recorded decls for C++20 named modules. The parts of decls
+  /// which not affecting the ABI may not be recorded. e.g.,
+  /// the function body of a non-inline function.
+  llvm::hash_code BMIDeclsHash = 0;
+
   /// Mapping from input file entries to the index into the
   /// offset table where information about that input file is stored.
   llvm::DenseMap<const FileEntry *, uint32_t> InputFileIDs;
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index aea1a93ae1fa828..ace24eb4d29d852 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -1249,3 +1249,5 @@ void ODRHash::AddQualType(QualType T) {
 void ODRHash::AddBoolean(bool Value) {
   Bools.push_back(Value);
 }
+
+void ODRHash::AddInteger(unsigned Value) { ID.AddInteger(Value); }
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 76def412d12552c..42bd241f1ff70fe 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -354,6 +354,7 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL,
   } else if ((PhaseArg = DAL.getLastArg(options::OPT_fsyntax_only)) ||
              (PhaseArg = DAL.getLastArg(options::OPT_print_supported_cpus)) ||
              (PhaseArg = DAL.getLastArg(options::OPT_module_file_info)) ||
+             (PhaseArg = DAL.getLastArg(options::OPT_get_bmi_decls_hash)) ||
              (PhaseArg = DAL.getLastArg(options::OPT_verify_pch)) ||
              (PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) ||
              (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) ||
@@ -4736,7 +4737,8 @@ Action *Driver::ConstructPhaseAction(
       return C.MakeAction<MigrateJobAction>(Input, types::TY_Remap);
     if (Args.hasArg(options::OPT_emit_ast))
       return C.MakeAction<CompileJobAction>(Input, types::TY_AST);
-    if (Args.hasArg(options::OPT_module_file_info))
+    if (Args.hasArg(options::OPT_module_file_info) ||
+        Args.hasArg(options::OPT_get_bmi_decls_hash))
       return C.MakeAction<CompileJobAction>(Input, types::TY_ModuleFile);
     if (Args.hasArg(options::OPT_verify_pch))
       return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing);
@@ -5818,7 +5820,8 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
   }
 
   if (JA.getType() == types::TY_ModuleFile &&
-      C.getArgs().getLastArg(options::OPT_module_file_info)) {
+      (C.getArgs().getLastArg(options::OPT_module_file_info) ||
+       C.getArgs().getLastArg(options::OPT_get_bmi_decls_hash))) {
     return "-";
   }
 
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 5bb945be78dcb41..c091a94ea0d7805 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4960,7 +4960,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     } else if (JA.getType() == types::TY_AST) {
       CmdArgs.push_back("-emit-pch");
     } else if (JA.getType() == types::TY_ModuleFile) {
-      CmdArgs.push_back("-module-file-info");
+      if (Args.hasArg(options::OPT_get_bmi_decls_hash))
+        CmdArgs.push_back("-get-bmi-decls-hash");
+      else
+        CmdArgs.push_back("-module-file-info");
     } else if (JA.getType() == types::TY_RewrittenObjC) {
       CmdArgs.push_back("-rewrite-objc");
       rewriteKind = RK_NonFragile;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b6245d0dddb1052..177b40a98578947 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2561,6 +2561,7 @@ static const auto &getFrontendActionTable() {
       {frontend::InitOnly, OPT_init_only},
       {frontend::ParseSyntaxOnly, OPT_fsyntax_only},
       {frontend::ModuleFileInfo, OPT_module_file_info},
+      {frontend::GetBMIDeclsHash, OPT_get_bmi_decls_hash},
       {frontend::VerifyPCH, OPT_verify_pch},
       {frontend::PrintPreamble, OPT_print_preamble},
       {frontend::PrintPreprocessedInput, OPT_E},
@@ -4243,6 +4244,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
   case frontend::GenerateInterfaceStubs:
   case frontend::ParseSyntaxOnly:
   case frontend::ModuleFileInfo:
+  case frontend::GetBMIDeclsHash:
   case frontend::VerifyPCH:
   case frontend::PluginAction:
   case frontend::RewriteObjC:
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 1aaebd160201692..06b15079887c774 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -328,8 +328,8 @@ SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
 }
 
 std::unique_ptr<ASTConsumer>
-DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI,
-                                        StringRef InFile) {
+DumpModuleInfoActionBase::CreateASTConsumer(CompilerInstance &CI,
+                                            StringRef InFile) {
   return std::make_unique<ASTConsumer>();
 }
 
@@ -809,13 +809,23 @@ namespace {
   };
 }
 
-bool DumpModuleInfoAction::BeginInvocation(CompilerInstance &CI) {
+bool DumpModuleInfoActionBase::BeginInvocation(CompilerInstance &CI) {
   // The Object file reader also supports raw ast files and there is no point in
   // being strict about the module file format in -module-file-info mode.
   CI.getHeaderSearchOpts().ModuleFormat = "obj";
   return true;
 }
 
+llvm::raw_ostream &DumpModuleInfoActionBase::getOutputStream() {
+  StringRef OutputFileName = getCompilerInstance().getFrontendOpts().OutputFile;
+  if (!OutputFileName.empty() && OutputFileName != "-") {
+    std::error_code EC;
+    OutputStream.reset(new llvm::raw_fd_ostream(
+        OutputFileName.str(), EC, llvm::sys::fs::OF_TextWithCRLF));
+  }
+  return OutputStream ? *OutputStream : llvm::outs();
+}
+
 static StringRef ModuleKindName(Module::ModuleKind MK) {
   switch (MK) {
   case Module::ModuleMapModule:
@@ -842,15 +852,9 @@ static StringRef ModuleKindName(Module::ModuleKind MK) {
 
 void DumpModuleInfoAction::ExecuteAction() {
   assert(isCurrentFileAST() && "dumping non-AST?");
-  // Set up the output file.
+
   CompilerInstance &CI = getCompilerInstance();
-  StringRef OutputFileName = CI.getFrontendOpts().OutputFile;
-  if (!OutputFileName.empty() && OutputFileName != "-") {
-    std::error_code EC;
-    OutputStream.reset(new llvm::raw_fd_ostream(
-        OutputFileName.str(), EC, llvm::sys::fs::OF_TextWithCRLF));
-  }
-  llvm::raw_ostream &Out = OutputStream ? *OutputStream : llvm::outs();
+  llvm::raw_ostream &Out = getOutputStream();
 
   Out << "Information for module file '" << getCurrentFile() << "':\n";
   auto &FileMgr = CI.getFileManager();
@@ -875,6 +879,12 @@ void DumpModuleInfoAction::ExecuteAction() {
     serialization::ModuleFile &MF = R->getModuleManager().getPrimaryModule();
     Out << "  ====== C++20 Module structure ======\n";
 
+    std::optional<uint64_t> DeclsHash = R->getReadedBMIDeclsHash();
+    if (DeclsHash) {
+      Out << "  Decls Hash: ";
+      Out.write_hex(*DeclsHash) << "\n";
+    }
+
     if (MF.ModuleName != LO.CurrentModule)
       Out << "  Mismatched module names : " << MF.ModuleName << " and "
           << LO.CurrentModule << "\n";
@@ -963,6 +973,23 @@ void DumpModuleInfoAction::ExecuteAction() {
       HSOpts.ModulesValidateDiagnosticOptions);
 }
 
+void GetModuleDeclsHashAction::ExecuteAction() {
+  llvm::raw_ostream &Out = getOutputStream();
+
+  if (!isCurrentFileAST()) {
+    Out << "We should only trying to get Decls hash from a module file.\n";
+    return;
+  }
+
+  ASTReader *R = getCurrentASTUnit().getASTReader().get();
+  std::optional<uint64_t> DeclsHash = R->getReadedBMIDeclsHash();
+  if (DeclsHash) {
+    Out << "Decls Hash: ";
+    Out.write_hex(*DeclsHash) << "\n";
+  } else
+    Out << "Failed to read Decls Hash.\n";
+}
+
 //===----------------------------------------------------------------------===//
 // Preprocessor Actions
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 59f7f955db50971..1f594d093fcb6d0 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -75,6 +75,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
   case InitOnly:               return std::make_unique<InitOnlyAction>();
   case ParseSyntaxOnly:        return std::make_unique<SyntaxOnlyAction>();
   case ModuleFileInfo:         return std::make_unique<DumpModuleInfoAction>();
+  case GetBMIDeclsHash:
+    return std::make_unique<GetModuleDeclsHashAction>();
   case VerifyPCH:              return std::make_unique<VerifyPCHAction>();
   case TemplightDump:          return std::make_unique<TemplightDumpAction>();
 
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index e91d1c65e0f6a7e..4d458d419f48a44 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3385,6 +3385,13 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
       break;
     }
 
+    case BMI_DECLS_HASH:
+      if (!ReadedBMIDeclsHash)
+        ReadedBMIDeclsHash = Record[0];
+      else
+        ReadedBMIDeclsHash = llvm::hash_combine(ReadedBMIDeclsHash, Record[0]);
+      break;
+
     case TU_UPDATE_LEXICAL: {
       DeclContext *TU = ContextObj->getTranslationUnitDecl();
       LexicalContents Contents(
@@ -5681,6 +5688,74 @@ bool ASTReader::isAcceptableASTFile(StringRef Filename, FileManager &FileMgr,
                                   /*ValidateDiagnosticOptions=*/true);
 }
 
+std::optional<uint64_t> ASTReader::getBMIHash(StringRef Filename,
+                                              FileManager &FileMgr) {
+  // Open the AST file.
+  std::unique_ptr<llvm::MemoryBuffer> OwnedBuffer;
+
+  // FIXME: This allows use of the VFS; we do not allow use of the
+  // VFS when actually loading a module.
+  auto BufferOrErr = FileMgr.getBufferForFile(Filename);
+  if (!BufferOrErr)
+    return std::nullopt;
+
+  OwnedBuffer = std::move(*BufferOrErr);
+  llvm::MemoryBuffer *Buffer = OwnedBuffer.get();
+
+  // Initialize the stream
+  // FIXME: Should we consider other format?
+  PCHContainerOperations PCHOperations;
+  StringRef Bytes = PCHOperations.getRawReader().ExtractPCH(*Buffer);
+  BitstreamCursor Stream(Bytes);
+
+  // Sniff for the signature.
+  if (llvm::Error Err = doesntStartWithASTFileMagic(Stream)) {
+    consumeError(std::move(Err)); // FIXME this drops errors on the floor.
+    return std::nullopt;
+  }
+
+  if (SkipCursorToBlock(Stream, AST_BLOCK_ID))
+    return std::nullopt;
+
+  while (true) {
+    Expected<llvm::BitstreamEntry> MaybeEntry = Stream.advance();
+    if (!MaybeEntry) {
+      consumeError(MaybeEntry.takeError());
+      return std::nullopt;
+    }
+    llvm::BitstreamEntry Entry = MaybeEntry.get();
+
+    switch (Entry.Kind) {
+    case llvm::BitstreamEntry::Error:
+    case llvm::BitstreamEntry::EndBlock:
+      return std::nullopt;
+
+    case llvm::BitstreamEntry::Record: {
+      RecordData Record;
+      StringRef Blob;
+      Expected<uint64_t> MaybeRecCode =
+          Stream.readRecord(Entry.ID, Record, &Blob);
+      if (!MaybeRecCode) {
+        consumeError(MaybeRecCode.takeError());
+        return std::nullopt;
+      }
+      if (MaybeRecCode.get() == BMI_DECLS_HASH)
+        return Record[0];
+
+      continue;
+    }
+
+    case llvm::BitstreamEntry::SubBlock:
+      if (llvm::Error Err = Stream.SkipBlock())
+        consumeError(std::move(Err));
+
+      continue;
+    }
+  }
+
+  return std::nullopt;
+}
+
 llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F,
                                           unsigned ClientLoadCapabilities) {
   // Enter the submodule block.
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index ee8ba8a6874b57a..cb919650499e0d9 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -826,6 +826,7 @@ void ASTWriter::WriteBlockInfoBlock() {
 
   // AST Top-Level Block.
   BLOCK(AST_BLOCK);
+  RECORD(BMI_DECLS_HASH);
   RECORD(TYPE_OFFSET);
   RECORD(DECL_OFFSET);
   RECORD(IDENTIFIER_OFFSET);
@@ -5093,6 +5094,15 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
 
   DoneWritingDeclsAndTypes = true;
 
+  if (isWritingStdCXXNamedModules()) {
+    if (Chain && Chain->getReadedBMIDeclsHash())
+      BMIDeclsHash =
+          llvm::hash_combine(BMIDeclsHash, *Chain->getReadedBMIDeclsHash());
+
+    RecordData Record = {BMIDeclsHash};
+    Stream.EmitRecord(BMI_DECLS_HASH, Record);
+  }
+
   // These things can only be done once we've written out decls and types.
   WriteTypeDeclOffsets();
   if (!DeclUpdatesOffsetsRecord.empty())
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index ca7794b8c5f9773..02f6b18edff0f60 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2509,6 +2509,124 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
   return Context.DeclMustBeEmitted(D);
 }
 
+/// Get the hash value of D for the part which contributes to the
+/// BMI (built module interface).
+///
+/// Return std::nullopt in case we are confident the Declaration won't
+/// contribute to the BMI completely.
+///
+/// Note that, for the safety consideration, we can/should return the hash
+/// value if we are not confident that D may contribute to the interface or
+/// not.
+static std::optional<unsigned> getBMICompatibleHash(Decl *D) {
+  ODRHash Hasher;
+
+  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+    if (!MayDefAffectABI(FD)) {
+      // For non-exported non-inline function, we can skip recording its hash
+      // value in the BMI.
+      if (FD->isInvisibleOutsideTheOwningModule())
+        return std::nullopt;
+
+      // For exported non-inline function, it should be fine enough to record
+      // the signature only.
+      Hasher.AddFunctionDecl(FD, /*SkipBody=*/true);
+      return Hasher.CalculateHash();
+    }
+
+    // For inlined functions, actually there are cases the BMI should keep the
+    // same after we touch the inlined functions, e.g.,
+    //
+    // export module a;
+    // inline int f() { return 43; }
+    // export int a() { return f(); }
+    //
+    // Since all the uses of `f()` is in an non-inline function, it should be
+    // fine to keep the module interface unchanged if we change the definition
+    // of `f()`.
+    //
+    // However, to make the general cases work, we have to perform a context
+    // sensitive reachable analysis to get whether the inline funciton may be
+    // used by an exported inline function transitively. Maybe it is not so hard
+    // or maybe there are simple cases we can cover easily, but we leave the
+    // oppotunities to the future.
+
+    // Otherwise we can try to use the cached ODR Hash.
+    return FD->getODRHash();
+  }
+
+  if (auto *VD = dyn_cast<VarDecl>(D)) {
+    if (!MayDefAffectABI(VD) && VD->isInvisibleOutsideTheOwningModule())
+      return std::nullopt;
+
+    Hasher.AddDecl(VD);
+    Hasher.AddQualType(VD->getType());
+    // ODRHash::AddDecl won't add the definition for variables by default.
+    if (MayDefAffectABI(VD) && VD->hasInit())
+      Hasher.AddStmt(VD->getInit());
+
+    return Hasher.CalculateHash();
+  }
+
+  if (auto *CXXRD = dyn_cast<CXXRecordDecl>(D)) {
+    if (!CXXRD->hasDefinition())
+      return std::nullopt;
+
+    // We can't filter the case if the class is not exported since the
+    // non-exported class may be reachable to users:
+    //
+    //  export module a;
+    //  class A{};
+    //  export A a() { ... }
+    //
+    // The users of 'a' can get `A` by:
+    //
+    //  auto v = a();
+
+    // We can't use CXXRecordDecl::getODRHash since it'll use the ODR
+    // hash for all member functions and all member variables.
+    //
+    // The member function and field variables will be recorded seperately.
+    //
+    // TODO: We should try to merge these logics to ODRHash and StmtProfiler.
+    Hasher.AddDecl(CXXRD);
+    Hasher.AddInteger(CXXRD->getNumBases());
+    auto Bases = CXXRD->bases();
+    for (const auto &Base : Bases) {
+      Hasher.AddQualType(Base.getTypeSourceInfo()->getType());
+      Hasher.AddBoolean(Base.isVirtual());
+      Hasher.AddInteger(Base.getAccessSpecifierAsWritten());
+    }
+
+    return Hasher.CalculateHash();
+  }
+
+  if (auto *ED = dyn_cast<EnumDecl>(D)) {
+    if (ED->isInvisibleOutsideTheOwningModule())
+      return std::nullopt;
+
+    // Try to use the cached ODR Hash Value.
+    return ED->getODRHash();
+  }
+
+  if (auto *FD = dyn_cast<FieldDecl>(D)) {
+    Hasher.AddDecl(D);
+    Hasher.AddSubDecl(FD);
+    Hasher.CalculateHash();
+  }
+
+  Hasher.AddDecl(D);
+  return Hasher.CalculateHash();
+}
+
+static void conditionaly_hash_combine(llvm::hash_code &HashValue, Decl *D) {
+  std::optional<unsigned> ODRHashD = getBMICompatibleHash(D);
+  if (!ODRHashD)
+    return;
+
+  HashValue = llvm::hash_combine(HashValue, *ODRHashD);
+}
+
 void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
   PrettyDeclStackTraceEntry CrashInfo(Context, D, SourceLocation(),
                                       "serializing");
@@ -2530,6 +2648,9 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
   // Build a record for this declaration
   W.Visit(D);
 
+  if (isWritingStdCXXNamedModules())
+    conditionaly_hash_combine(BMIDeclsHash, D);
+
   // Emit this declaration to the bitstream.
   uint64_t Offset = W.Emit(D);
 
diff --git a/clang/test/Modules/cxx20-module-file-info.cpp b/clang/test/Modules/cxx20-module-file-info.cpp
index 99a215645e8fe76..6ee8ada7d6b8fc4 100644
--- a/clang/test/Modules/cxx20-module-file-info.cpp
+++ b/clang/test/Modules/cxx20-module-file-info.cpp
@@ -30,6 +30,7 @@ export module A;
 void a();
 
 // CHECK-A: ====== C++20
+// CHECK-A-NEXT: Decls Hash
 // CHECK-A-NEXT: Interface Unit 'A' is the Primary Module at index #1
 
 //--- mod-info-tu2.cpp
@@ -38,6 +39,7 @@ export module B;
 void b();
 
 // CHECK-B: ====== C++20
+// CHECK-B-NEXT: Decls Hash
 // CHECK-B-NEXT: Interface Unit 'B' is the Primary Module at index #1
 
 //--- mod-info-tu3.cpp
@@ -55,6 +57,7 @@ export void say(const char *);
 void foo() {}
 
 // CHECK-FOO: ====== C++20
+// CHECK-FOO-NEXT:  Decls Hash
 // CHECK-FOO-NEXT:  Interface Unit 'Foo' is the Primary Module at index #3
 // CHECK-FOO-NEXT:   Sub Modules:
 // CHECK-FOO-NEXT:    Global Module Fragment '<global>' is at index #4
diff --git a/clang/test/Modules/decls-hash-get-bmi-decls-hash.cppm b/clang/test/Modules/decls-hash-get-bmi-decls-hash.cppm
new file mode 100644
index 000000000000000..5bc253acce5ffe7
--- /dev/null
+++ b/clang/test/Modules/decls-hash-get-bmi-decls-hash.cppm
@@ -0,0 +1,35 @@
+// Test that -module-file-info can print the decls hash for thin BMI.
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/a.v1.cppm -emit-thin-module-interface -o %t/a.v1.pcm
+//
+// RUN: %clang_cc1 -get-bmi-decls-hash %t/a.pcm > %t/a.pcm.hash
+// RUN: %clang_cc1 -get-bmi-decls-hash %t/a.v1.pcm > %t/a.v1.pcm.hash
+//
+// RUN: diff %t/a.pcm.hash %t/a.v1.pcm.hash
+
+//--- a.cppm
+export module a;
+export int v = 43;
+export int a() {
+    return 43;
+}
+
+unsigned int non_exported() {
+    return v;
+}
+
+//--- a.v1.cppm
+export module a;
+export int v = 45;
+export int a() {
+    return 44;
+}
+
+unsigned int non_exported() {
+    return v + 43;
+}
diff --git a/clang/test/Modules/decls-hash-module-file-info.cppm b/clang/test/Modules/decls-hash-module-file-info.cppm
new file mode 100644
index 000000000000000..f9a2fa5155ccc35
--- /dev/null
+++ b/clang/test/Modules/decls-hash-module-file-info.cppm
@@ -0,0 +1,35 @@
+// Test that -module-file-info can print the decls hash for thin BMI.
+//
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-thin-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/a.v1.cppm -emit-thin-module-interface -o %t/a.v1.pcm
+//
+// RUN: %clang_cc1 -module-file-info %t/a.pcm | grep "Decls Hash:" > %t/a.pcm.hash
+// RUN: %clang_cc1 -module-file-info %t/a.v1.pcm | grep "Decls Hash:" > %t/a.v1.pcm.hash
+//
+// RUN: diff %t/a.pcm.hash %t/a.v1.pcm.hash
+
+//--- a.cppm
+export module a;
+export int v = 43;
+export int a() {
+    return 43;
+}
+
+unsigned int non_exported() {
+    return v;
+}
+
+//--- a.v1.cppm
+export module a;
+export int v = 45;
+export int a() {
+    return 44;
+}
+
+unsigned int non_exported() {
+    return v + 43;
+}
diff --git a/clang/unittests/Serialization/BMIDeclsHashTest.cpp b/clang/unittests/Serialization/BMIDeclsHashTest.cpp
new file mode 100644
index 000000000000000..9b6333cedcd3862
--- /dev/null
+++ b/clang/unittests/Serialization/BMIDeclsHashTest.cpp
@@ -0,0 +1,952 @@
+//===- unittests/Serialization/ThinBMIDeclsHashTest.cpp - CI tests --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+class ThinBMIDeclsHashTest : public ::testing::Test {
+  void SetUp() override {
+    ASSERT_FALSE(sys::fs::createUniqueDirectory("modules-test", TestDir));
+  }
+
+  void TearDown() override { sys::fs::remove_directories(TestDir); }
+
+  using PathType = llvm::SmallString<256>;
+
+  PathType TestDir;
+
+public:
+  FileManager FMgr = FileManager{FileSystemOptions()};
+
+  void addFile(StringRef Path, StringRef Contents) {
+    ASSERT_TRUE(sys::path::is_absolute(Path));
+    ASSERT_TRUE(Path.startswith(TestDir));
+
+    std::error_code EC;
+    llvm::raw_fd_ostream OS(Path, EC);
+    ASSERT_FALSE(EC);
+    OS << Contents;
+  }
+
+  PathType getUniquePathInTestDir(StringRef Suffix) {
+    PathType Pattern("%%-%%-%%-%%-%%-%%");
+    Pattern.append(Suffix);
+    llvm::sys::fs::createUniquePath(Pattern, Pattern, /*MakeAbsolute=*/false);
+
+    PathType Result(TestDir);
+    llvm::sys::path::append(Result, Pattern);
+    return Result;
+  }
+
+  // Map from module name to BMI path.
+  using ModuleMapTy = llvm::StringMap<std::string>;
+
+  std::string GenerateModuleInterface(StringRef Contents,
+                                      StringRef AdditionalArgs = StringRef()) {
+    IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+        CompilerInstance::createDiagnostics(new DiagnosticOptions());
+    CreateInvocationOptions CIOpts;
+    CIOpts.Diags = Diags;
+    CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
+
+    PathType InterfaceSourceName = getUniquePathInTestDir(".cppm");
+    addFile(InterfaceSourceName, Contents);
+
+    const char *Args[] = {"clang++",
+                          "-std=c++20",
+                          "--precompile",
+                          AdditionalArgs.data(),
+                          "-working-directory",
+                          TestDir.c_str(),
+                          "-I",
+                          TestDir.c_str(),
+                          InterfaceSourceName.c_str()};
+    std::shared_ptr<CompilerInvocation> Invocation =
+        createInvocation(Args, CIOpts);
+    EXPECT_TRUE(Invocation);
+
+    CompilerInstance Instance;
+    Instance.setDiagnostics(Diags.get());
+    Instance.setInvocation(Invocation);
+
+    PathType CacheBMIPath = getUniquePathInTestDir(".pcm");
+    Instance.getFrontendOpts().OutputFile = (std::string)CacheBMIPath;
+    GenerateThinModuleInterfaceAction Action;
+    EXPECT_TRUE(Instance.ExecuteAction(Action));
+    EXPECT_FALSE(Diags->hasErrorOccurred());
+
+    return (std::string)CacheBMIPath;
+  }
+
+  std::optional<uint64_t> getBMIHash(StringRef BMIPath) {
+    return ASTReader::getBMIHash(BMIPath, FMgr);
+  }
+
+  bool CompareBMIHash(StringRef Contents1, StringRef Contents2) {
+    auto BMIPath1 = GenerateModuleInterface(Contents1);
+    auto BMIPath2 = GenerateModuleInterface(Contents2);
+
+    std::optional<uint64_t> Hash1 = getBMIHash(BMIPath1);
+    EXPECT_TRUE(Hash1);
+    std::optional<uint64_t> Hash2 = getBMIHash(BMIPath2);
+    EXPECT_TRUE(Hash2);
+
+    return *Hash1 == *Hash2;
+  }
+};
+
+// Test that:
+// - the BMI hash won't change if we only touched the body of a non-inline
+// function.
+// - the BMI hash will change if we changed the interface of exported non-inline
+// function.
+// - the BMI hash will change if we add or delete exported functions.
+// - the BMI hash won't change if we changed, add and delete non-exported and
+// non-inline
+//   function.
+TEST_F(ThinBMIDeclsHashTest, BasicNonInlineFuncTest) {
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                             R"cpp(
+export module a;
+export int a() {
+  return 44;
+}
+  )cpp"));
+
+  // The interface should change if we change the function name.
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export int b() {
+  return 43;
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export char a() {
+  return 44;
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export int a(int v = 43) {
+  return 44 + v;
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a(int v = 44) {
+  return v;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export int a(int v = 43) {
+  return v;
+}
+  )cpp"));
+
+  // Test that the comment won't change the interface.
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                             R"cpp(
+export module a;
+// New comments here.
+export int a() {
+  return 43;
+}
+  )cpp"));
+
+  // Test that adding new exported functions may change the interface.
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+
+export int a2() {
+  return a() + 43;
+}
+  )cpp"));
+
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                             R"cpp(
+export module a;
+
+int non_exported() { return 49; }
+
+export int a() {
+  return non_exported();
+}
+  )cpp"));
+
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                             R"cpp(
+export module a;
+
+int non_exported() { return 99; }
+
+export int a() {
+  return non_exported();
+}
+  )cpp"));
+}
+
+// Tests that:
+// - The interface shouldn't change if we changed the definition of the
+// non-inline variables.
+// - The interface shouldn change if we change the interface (type and name) of
+// the exported
+//   non-inline variables.
+// - The interface shouldn't change if we change, add or remove non-exported and
+// non-inline
+//   variables.
+TEST_F(ThinBMIDeclsHashTest, BasicNonInlineVarTest) {
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                             R"cpp(
+export module a;
+export int a = 45;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export short a = 43;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export double a = 43.0;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export int aa = 43;
+  )cpp"));
+
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                             R"cpp(
+export module a;
+int a_def();
+export int a = a_def();
+  )cpp"));
+
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                             R"cpp(
+export module a;
+int a_def();
+int a_non_exported = a_def();
+export int a = a_non_exported;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export int a = 43;
+export int another_exported = 44;
+  )cpp"));
+}
+
+TEST_F(ThinBMIDeclsHashTest, OrderingTests) {
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export int v = 43;
+export int a() {
+  return 43;
+}
+  )cpp",
+                             R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+export int v = 43;
+  )cpp"));
+}
+
+// Tests that the inerface will change every time we touched, added or delete
+// an inline function.
+TEST_F(ThinBMIDeclsHashTest, InlineFunctionTests) {
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export inline int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export inline int a() {
+  return 44;
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export inline int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export inline short a() {
+  return 43;
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export inline int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export inline int a(int v = 44) {
+  return 43;
+}
+  )cpp"));
+
+  // Note that the following cases **can** be fine if the interface didn't
+  // change. But we choose to change the interface according to our current
+  // implementation strategies to change the interface every time for every
+  // change in inline functions.
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+inline int inl_a() {
+  return 44;
+}
+export int a() {
+  return inl_a();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+inline int inl_a() {
+  return 45;
+}
+export int a() {
+  return inl_a();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+inline int inl_a() {
+  return 44;
+}
+export int a() {
+  return inl_a();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+inline int inl_a() {
+  return 44;
+}
+export int a() {
+  return inl_a();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+inline int inl_a() {
+  return 44;
+}
+inline int new_unused_inline() {
+  return 43;
+}
+export int a() {
+  return inl_a();
+}
+  )cpp"));
+
+  /// Testing implicitly inline function.
+
+  // Note that the following case **can** be fine if the interface didn't
+  // change. But we choose to change the interface according to our current
+  // implementation strategies to change the interface every time for every
+  // change in inline functions.
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+module;
+class A {
+public:
+  int get() { return 43; }
+};
+export module a;
+export int a() {
+  A a;
+  return a.get();
+}
+  )cpp",
+                              R"cpp(
+module;
+class A {
+public:
+  int get() { return 44; }
+};
+export module a;
+export int a() {
+  A a;
+  return a.get();
+}
+  )cpp"));
+}
+
+// Tests that the inerface will change every time we touched, added or delete
+// an inline variables.
+TEST_F(ThinBMIDeclsHashTest, InlineVarTests) {
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export inline int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export inline int a = 45;
+  )cpp"));
+
+  // Note that the following case **can** be fine if the interface didn't
+  // change. But we choose to change the interface according to our current
+  // implementation strategies to change the interface every time for every
+  // change in inline functions.
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+inline int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+inline int a = 45;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export int a = 43;
+inline int v = 49;
+  )cpp"));
+}
+
+TEST_F(ThinBMIDeclsHashTest, ClassTests) {
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+export class A {
+public:
+  int get() { return 43; }
+};
+  )cpp",
+                             R"cpp(
+export module a;
+export class A {
+public:
+  int get() { return 44; }
+};
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export class A {
+public:
+  int get() { return 43; }
+};
+  )cpp",
+                              R"cpp(
+export module a;
+export class A {
+public:
+  int get() { return 43; }
+
+  int get_2() { return 44;}
+};
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export class A {
+public:
+  int get() { return 43; }
+};
+  )cpp",
+                              R"cpp(
+export module a;
+export class A {
+  int a = 43;
+public:
+  int get() { return a; }
+};
+  )cpp"));
+
+  /// Testing the use of different classes in inline functions.
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 43; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp",
+                             R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 44; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp"));
+
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 43; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp",
+                             R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 43; }
+  int get_2() { return 44; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 43; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+class A {
+  int a = 43;
+public:
+  int get() { return a; }
+};
+export inline int func() {
+  A a;
+  return a.get();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+public:
+  int get() { return 43; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+class A {
+  int a = 43;
+public:
+  int get() { return a; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+  int a = 44;
+public:
+  int get() { return a; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+class A {
+  int a = 43;
+public:
+  int get() { return a; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class A {
+  int a = 44;
+public:
+  int get() { return a; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp",
+                              R"cpp(
+export module a;
+class A {
+  short a = 44;
+public:
+  int get() { return a; }
+};
+export inline A func() {
+  return A();
+}
+  )cpp"));
+
+  // Testing different bases
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export class A {};
+  )cpp",
+                              R"cpp(
+export module a;
+class Base1 {};
+export class A : public Base1 {};
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class Base1 {};
+export class A : public Base1 {};
+  )cpp",
+                              R"cpp(
+export module a;
+class Base1 {};
+export class A : protected Base1 {};
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class Base1 {};
+class Base2 {};
+export class A : public Base1 {};
+  )cpp",
+                              R"cpp(
+export module a;
+class Base1 {};
+class Base2 {};
+export class A : public Base1, public Base2 {};
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+class Base1 {};
+class Base2 {};
+export class A : public Base1, public Base2 {};
+  )cpp",
+                              R"cpp(
+export module a;
+class Base1 {};
+class Base2 {};
+export class A : virtual public Base1, virtual public Base2 {};
+  )cpp"));
+}
+
+TEST_F(ThinBMIDeclsHashTest, ExportUsingTests) {
+  EXPECT_TRUE(CompareBMIHash(R"cpp(
+module;
+int a() { return 43; }
+export module a;
+export using ::a;
+  )cpp",
+                             R"cpp(
+module;
+int a() { return 44; }
+export module a;
+export using ::a;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+module;
+inline int a() { return 43; }
+export module a;
+export using ::a;
+  )cpp",
+                              R"cpp(
+module;
+inline int a() { return 44; }
+export module a;
+export using ::a;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+module;
+class A {
+public:
+  int get() { return 43; }
+};
+export module a;
+export using ::A;
+  )cpp",
+                              R"cpp(
+module;
+class A {
+public:
+  int get() { return 44; }
+};
+export module a;
+export using ::A;
+  )cpp"));
+}
+
+TEST_F(ThinBMIDeclsHashTest, ConstExprTests) {
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export constexpr int a = 43;
+  )cpp",
+                              R"cpp(
+export module a;
+export constexpr int a = 44;
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export constexpr int a() {
+  return 44;
+}
+  )cpp",
+                              R"cpp(
+export module a;
+export constexpr int a() {
+  return 45;
+}
+  )cpp"));
+}
+
+TEST_F(ThinBMIDeclsHashTest, TemplateTests) {
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export template <class C>
+int get(C c) { return 43 + c; }
+  )cpp",
+                              R"cpp(
+export module a;
+export template <class C>
+int get(C c) { return 44 + c; }
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+template <class C>
+int get(C c) { return 43 + c; }
+export int a() {
+  return get<int>(43);
+}
+  )cpp",
+                              R"cpp(
+export module a;
+template <class C>
+int get(C c) { return 44 + c; }
+export int a() {
+  return get<int>(43);
+}
+  )cpp"));
+
+  EXPECT_FALSE(CompareBMIHash(R"cpp(
+export module a;
+export template<class T>
+class Templ {
+public:
+  // get is a non-template function inside a template class.
+  int get() { return 43; }
+};
+  )cpp",
+                              R"cpp(
+export module a;
+export template<class T>
+class Templ {
+public:
+  int get() { return 44; }
+};
+  )cpp"));
+}
+
+static std::string get_module_map_flag(StringRef ModuleName,
+                                       StringRef BMIPath) {
+  return std::string("-fmodule-file=") + ModuleName.str() + "=" + BMIPath.str();
+}
+
+// Test that if the decls hash of any import module changes, change
+// the decls hash of the current module.
+TEST_F(ThinBMIDeclsHashTest, ImportTests) {
+  auto BMIPathA = GenerateModuleInterface(R"cpp(
+export module a;
+export int a() {
+  return 43;
+}
+  )cpp");
+
+  auto BMIPathB = GenerateModuleInterface(R"cpp(
+export module b;
+import a;
+  )cpp",
+                                          get_module_map_flag("a", BMIPathA));
+
+  std::optional<uint64_t> Hash1 = getBMIHash(BMIPathB);
+  EXPECT_TRUE(Hash1);
+
+  // Test that if the decls hash of the imported doesn't change,
+  // the decls hash of the current module shouldn't change.
+  BMIPathA = GenerateModuleInterface(R"cpp(
+export module a;
+export int a() {
+  return 44;
+}
+  )cpp");
+
+  BMIPathB = GenerateModuleInterface(R"cpp(
+export module b;
+import a;
+  )cpp",
+                                     get_module_map_flag("a", BMIPathA));
+
+  std::optional<uint64_t> Hash2 = getBMIHash(BMIPathB);
+  EXPECT_TRUE(Hash2);
+
+  EXPECT_EQ(*Hash1, *Hash2);
+
+  // Test that if the decls hash of the imported changes,
+  // the decls hash of the current module shouldn't change.
+  BMIPathA = GenerateModuleInterface(R"cpp(
+export module a;
+export long long a(int x = 43) {
+  return 43;
+}
+  )cpp");
+
+  BMIPathB = GenerateModuleInterface(R"cpp(
+export module b;
+import a;
+  )cpp",
+                                     get_module_map_flag("a", BMIPathA));
+
+  std::optional<uint64_t> Hash3 = getBMIHash(BMIPathB);
+  EXPECT_TRUE(Hash2);
+
+  EXPECT_NE(*Hash1, *Hash3);
+}
+
+} // namespace
diff --git a/clang/unittests/Serialization/CMakeLists.txt b/clang/unittests/Serialization/CMakeLists.txt
index 10d7de970c643da..a34e0b4c5d7dc1e 100644
--- a/clang/unittests/Serialization/CMakeLists.txt
+++ b/clang/unittests/Serialization/CMakeLists.txt
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_clang_unittest(SerializationTests
+  BMIDeclsHashTest.cpp
   ForceCheckFileInputTest.cpp
   InMemoryModuleCacheTest.cpp
   ModuleCacheTest.cpp



More information about the cfe-commits mailing list