[clang] [Draft] Summary Based Analysis Prototype (PR #144224)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 14 07:05:01 PDT 2025


https://github.com/isuckatcs created https://github.com/llvm/llvm-project/pull/144224

This is the initial implementation of how I imagine summaries used in clang and the CSA. There are still rooms for improvement and the patch still serves as an RFC to discuss which direction to go further.

1.) Compile each TU to generate the summaries

```cpp
// main.cpp
int *x;

void foo();

int main() {
  x = nullptr;
  foo();
  return *x;
}
```
```cpp
// foo.cpp
void foo() {}
```
```
clang++ main.cpp foo.cpp --emit-summaries
```
2.) Give the path to the directory containing the summaries to a clang tool to use them
```
clang++ --analyze main.cpp --summaries-dir=.
```
```
main.cpp:8:10: warning: Dereference of null pointer (loaded from variable 'x') [core.NullDereference]
    8 |   return *x;
      |          ^~
```

The summaries for `main.cpp` and `foo.cpp` look like this:
```json
[
  {
    "id": "c:@F at main#",
    "attrs": {
      "function": []
    },
    "calls": [
      {
        "id": "c:@F at foo#"
      }
    ]
  }
]
```

```json
[
  {
    "id": "c:@F at foo#",
    "attrs": {
      "function": [
        "no_write_global"
      ]
    },
    "calls": []
  }
]
```

>From 7fba0addb97f9195eb307882254207f9b58072f2 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 22 May 2025 22:43:35 +0200
Subject: [PATCH 01/24] [clang][Driver] Add option to emit summaries

---
 clang/include/clang/Driver/Options.td         | 13 ++++++++++
 .../include/clang/Frontend/FrontendOptions.h  |  3 +++
 clang/lib/Driver/ToolChains/Clang.cpp         | 24 +++++++++++++++++++
 clang/lib/Frontend/FrontendAction.cpp         |  6 +++++
 4 files changed, 46 insertions(+)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 22261621df092..f0c7b277e68e2 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5941,6 +5941,15 @@ def save_temps : Flag<["-", "--"], "save-temps">, Flags<[NoXarchOption]>,
   Visibility<[ClangOption, FlangOption, FC1Option]>,
   Alias<save_temps_EQ>, AliasArgs<["cwd"]>,
   HelpText<"Alias for --save-temps=cwd">;
+def emit_summaries_EQ : Joined<["-", "--"], "emit-summaries=">, Flags<[NoXarchOption]>,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Save summaries about the different functions. <arg> can be set to 'cwd' for "
+  "current working directory, or 'obj' which will save temporary files in the "
+  "same directory as the final output file">;
+def emit_summaries : Flag<["-", "--"], "emit-summaries">, Flags<[NoXarchOption]>,
+  Visibility<[ClangOption]>,
+  Alias<emit_summaries_EQ>, AliasArgs<["cwd"]>,
+  HelpText<"Alias for --emit-summaries=cwd">;
 def save_stats_EQ : Joined<["-", "--"], "save-stats=">, Flags<[NoXarchOption]>,
   HelpText<"Save llvm statistics.">;
 def save_stats : Flag<["-", "--"], "save-stats">, Flags<[NoXarchOption]>,
@@ -8148,6 +8157,10 @@ defm emit_llvm_uselists : BoolOption<"", "emit-llvm-uselists",
   NegFlag<SetFalse, [], [ClangOption], "Don't preserve">,
   BothFlags<[], [ClangOption], " order of LLVM use-lists when serializing">>;
 
+def summary_file : Joined<["-"], "summary-file=">,
+  HelpText<"Filename to write summaries about function definitions to">,
+  MarshallingInfoString<FrontendOpts<"SummaryFile">>;
+
 def print_stats : Flag<["-"], "print-stats">,
   HelpText<"Print performance metrics and statistics">,
   MarshallingInfoFlag<FrontendOpts<"ShowStats">>;
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index c919a53ae089e..af2451c1bde8e 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -534,6 +534,9 @@ class FrontendOptions {
   /// minimization hints.
   std::string DumpMinimizationHintsPath;
 
+  /// Filename to write summaries about function definitions to.
+  std::string SummaryFile;
+
 public:
   FrontendOptions()
       : DisableFree(false), RelocatablePCH(false), ShowHelp(false),
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 2fb6cf8ea2bdc..676a4e34ddb27 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5470,6 +5470,30 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   if (Args.getLastArg(options::OPT_save_temps_EQ))
     Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
 
+  // FIXME: This needs to be cleaned up and needs proper error handling as well.
+  if (const Arg *A = Args.getLastArg(options::OPT_emit_summaries_EQ)) {
+    llvm::SmallString<10> input;
+    for (const auto &II : Inputs) {
+      if (!II.isFilename())
+        continue;
+
+      input = II.getFilename();
+      break;
+    }
+
+    if (!input.empty()) {
+      if (A->containsValue("cwd")) {
+        llvm::SmallString<10> filename = llvm::sys::path::filename(input);
+        llvm::sys::path::replace_extension(filename, "json");
+
+        CmdArgs.push_back(
+            Args.MakeArgString(Twine("-summary-file=") + filename));
+      } else if (A->containsValue("obj")) {
+        // FIXME: implement
+      }
+    }
+  }
+
   auto *MemProfArg = Args.getLastArg(options::OPT_fmemory_profile,
                                      options::OPT_fmemory_profile_EQ,
                                      options::OPT_fno_memory_profile);
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 54a2e3eb297f5..868a1f70abd43 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -1253,6 +1253,12 @@ void FrontendAction::EndSourceFile() {
   // Finalize the action.
   EndSourceFileAction();
 
+  if (CI.hasSema() && !CI.getFrontendOpts().SummaryFile.empty()) {
+    std::error_code EC;
+    llvm::raw_fd_ostream(CI.getFrontendOpts().SummaryFile, EC,
+                         llvm::sys::fs::CD_CreateAlways);
+  }
+
   // Sema references the ast consumer, so reset sema first.
   //
   // FIXME: There is more per-file stuff we could just drop here?

>From 3d7d23ad7815ef4527f000651f4321348b200873 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 7 Jun 2025 21:38:36 +0200
Subject: [PATCH 02/24] [clang][Summary] add the summarizer skeleton

---
 .../include/clang/Frontend/CompilerInstance.h | 21 ++++++++++++++++++-
 clang/include/clang/Sema/Sema.h               |  6 +++++-
 clang/include/clang/Sema/SemaSummarizer.h     | 19 +++++++++++++++++
 clang/include/clang/Sema/SummaryConsumer.h    |  8 +++++++
 clang/lib/Frontend/ChainedIncludesSource.cpp  |  2 +-
 clang/lib/Frontend/CompilerInstance.cpp       | 11 ++++++++--
 clang/lib/Frontend/FrontendAction.cpp         | 10 ++++++++-
 clang/lib/Frontend/FrontendActions.cpp        |  2 +-
 clang/lib/Sema/CMakeLists.txt                 |  1 +
 clang/lib/Sema/Sema.cpp                       |  7 ++++++-
 clang/lib/Sema/SemaDecl.cpp                   |  4 ++++
 clang/lib/Sema/SemaSummarizer.cpp             |  8 +++++++
 clang/lib/Testing/TestAST.cpp                 |  2 +-
 clang/unittests/CodeGen/TestCompiler.h        |  2 +-
 .../unittests/Frontend/CodeGenActionTest.cpp  |  2 +-
 .../unittests/Sema/ExternalSemaSourceTest.cpp |  2 +-
 clang/unittests/Sema/SemaLookupTest.cpp       |  2 +-
 17 files changed, 96 insertions(+), 13 deletions(-)
 create mode 100644 clang/include/clang/Sema/SemaSummarizer.h
 create mode 100644 clang/include/clang/Sema/SummaryConsumer.h
 create mode 100644 clang/lib/Sema/SemaSummarizer.cpp

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 5f25a932c5052..f296e6d042a54 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -48,6 +48,7 @@ class ModuleFile;
 }
 
 class CodeCompleteConsumer;
+class SummaryConsumer;
 class DiagnosticsEngine;
 class DiagnosticConsumer;
 class FileManager;
@@ -121,6 +122,9 @@ class CompilerInstance : public ModuleLoader {
   /// The code completion consumer.
   std::unique_ptr<CodeCompleteConsumer> CompletionConsumer;
 
+  /// The summary consumer.
+  std::unique_ptr<SummaryConsumer> TheSummaryConsumer;
+
   /// The semantic analysis object.
   std::unique_ptr<Sema> TheSema;
 
@@ -607,6 +611,18 @@ class CompilerInstance : public ModuleLoader {
     return *CompletionConsumer;
   }
 
+  /// @}
+  /// @name Summary
+  /// @{
+
+  bool hasSummaryConsumer() const { return (bool)TheSummaryConsumer; }
+
+  SummaryConsumer &getSummaryConsumer() const {
+    assert(TheSummaryConsumer &&
+           "Compiler instance has no code summary consumer!");
+    return *TheSummaryConsumer;
+  }
+
   /// setCodeCompletionConsumer - Replace the current code completion consumer;
   /// the compiler instance takes ownership of \p Value.
   void setCodeCompletionConsumer(CodeCompleteConsumer *Value);
@@ -736,9 +752,12 @@ class CompilerInstance : public ModuleLoader {
       Preprocessor &PP, StringRef Filename, unsigned Line, unsigned Column,
       const CodeCompleteOptions &Opts, raw_ostream &OS);
 
+  void createSummaryConsumer();
+
   /// Create the Sema object to be used for parsing.
   void createSema(TranslationUnitKind TUKind,
-                  CodeCompleteConsumer *CompletionConsumer);
+                  CodeCompleteConsumer *CompletionConsumer,
+                  SummaryConsumer *SummaryConsumer);
 
   /// Create the frontend timer and replace any existing one with it.
   void createFrontendTimer();
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fe93df94438cb..9cb2158ab4ff1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -125,6 +125,7 @@ class CXXBasePath;
 class CXXBasePaths;
 class CXXFieldCollector;
 class CodeCompleteConsumer;
+class SummaryConsumer;
 enum class ComparisonCategoryType : unsigned char;
 class ConstraintSatisfaction;
 class DarwinSDKInfo;
@@ -159,6 +160,7 @@ class SemaARM;
 class SemaAVR;
 class SemaBPF;
 class SemaCodeCompletion;
+class SemaSummarizer;
 class SemaCUDA;
 class SemaDirectX;
 class SemaHLSL;
@@ -883,7 +885,8 @@ class Sema final : public SemaBase {
 public:
   Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
        TranslationUnitKind TUKind = TU_Complete,
-       CodeCompleteConsumer *CompletionConsumer = nullptr);
+       CodeCompleteConsumer *CompletionConsumer = nullptr,
+       SummaryConsumer *SummaryConsumer = nullptr);
   ~Sema();
 
   /// Perform initialization that occurs after the parser has been
@@ -1564,6 +1567,7 @@ class Sema final : public SemaBase {
   std::unique_ptr<SemaAVR> AVRPtr;
   std::unique_ptr<SemaBPF> BPFPtr;
   std::unique_ptr<SemaCodeCompletion> CodeCompletionPtr;
+  std::unique_ptr<SemaSummarizer> SummarizerPtr;
   std::unique_ptr<SemaCUDA> CUDAPtr;
   std::unique_ptr<SemaDirectX> DirectXPtr;
   std::unique_ptr<SemaHLSL> HLSLPtr;
diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
new file mode 100644
index 0000000000000..3661e443bb2de
--- /dev/null
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -0,0 +1,19 @@
+#ifndef LLVM_CLANG_SEMA_SEMASUMMARIZER_H
+#define LLVM_CLANG_SEMA_SEMASUMMARIZER_H
+
+#include "clang/Sema/SemaBase.h"
+#include "clang/Sema/SummaryConsumer.h"
+
+namespace clang {
+class SemaSummarizer : public SemaBase {
+public:
+  SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer)
+      : SemaBase(S), SummaryConsumer(SummaryConsumer) {}
+
+  SummaryConsumer *SummaryConsumer;
+
+  void SummarizeFunctionBody(FunctionDecl *FD) const;
+};
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_SEMASUMMARIZE_H
diff --git a/clang/include/clang/Sema/SummaryConsumer.h b/clang/include/clang/Sema/SummaryConsumer.h
new file mode 100644
index 0000000000000..8aa2713b46c65
--- /dev/null
+++ b/clang/include/clang/Sema/SummaryConsumer.h
@@ -0,0 +1,8 @@
+#ifndef LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
+#define LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
+
+namespace clang {
+class SummaryConsumer {};
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
diff --git a/clang/lib/Frontend/ChainedIncludesSource.cpp b/clang/lib/Frontend/ChainedIncludesSource.cpp
index 95b0ed248d545..437f5387375f7 100644
--- a/clang/lib/Frontend/ChainedIncludesSource.cpp
+++ b/clang/lib/Frontend/ChainedIncludesSource.cpp
@@ -142,7 +142,7 @@ IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource(
     Clang->getASTContext().setASTMutationListener(
                                             consumer->GetASTMutationListener());
     Clang->setASTConsumer(std::move(consumer));
-    Clang->createSema(TU_Prefix, nullptr);
+    Clang->createSema(TU_Prefix, nullptr, nullptr);
 
     if (firstInclude) {
       Preprocessor &PP = Clang->getPreprocessor();
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 503d36467653e..917a187f49fb5 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -37,6 +37,7 @@
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/ParsedAttr.h"
 #include "clang/Sema/Sema.h"
+#include "clang/Sema/SummaryConsumer.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
 #include "clang/Serialization/InMemoryModuleCache.h"
@@ -741,10 +742,16 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP,
   return new PrintingCodeCompleteConsumer(Opts, OS);
 }
 
+void CompilerInstance::createSummaryConsumer() {
+  TheSummaryConsumer.reset(
+      getFrontendOpts().SummaryFile.empty() ? nullptr : new SummaryConsumer());
+}
+
 void CompilerInstance::createSema(TranslationUnitKind TUKind,
-                                  CodeCompleteConsumer *CompletionConsumer) {
+                                  CodeCompleteConsumer *CompletionConsumer,
+                                  SummaryConsumer *SummaryConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
-                         TUKind, CompletionConsumer));
+                         TUKind, CompletionConsumer, SummaryConsumer));
 
   // Set up API notes.
   TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 868a1f70abd43..aeeab901b9ed7 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -1339,8 +1339,16 @@ void ASTFrontendAction::ExecuteAction() {
   if (CI.hasCodeCompletionConsumer())
     CompletionConsumer = &CI.getCodeCompletionConsumer();
 
+  CI.createSummaryConsumer();
+
+  // Use a code completion consumer?
+  SummaryConsumer *SummaryConsumer = nullptr;
+  if (CI.hasSummaryConsumer())
+    SummaryConsumer = &CI.getSummaryConsumer();
+
   if (!CI.hasSema())
-    CI.createSema(getTranslationUnitKind(), CompletionConsumer);
+    CI.createSema(getTranslationUnitKind(), CompletionConsumer,
+                  SummaryConsumer);
 
   ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
            CI.getFrontendOpts().SkipFunctionBodies);
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 8c75e1a46da54..49f1420c75047 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -52,7 +52,7 @@ void EnsureSemaIsCreated(CompilerInstance &CI, FrontendAction &Action) {
 
   if (!CI.hasSema())
     CI.createSema(Action.getTranslationUnitKind(),
-                  GetCodeCompletionConsumer(CI));
+                  GetCodeCompletionConsumer(CI), nullptr);
 }
 } // namespace
 
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 4b87004e4b8ea..5237f20201fde 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -85,6 +85,7 @@ add_clang_library(clangSema
   SemaStmt.cpp
   SemaStmtAsm.cpp
   SemaStmtAttr.cpp
+  SemaSummarizer.cpp
   SemaSPIRV.cpp
   SemaSYCL.cpp
   SemaSwift.cpp
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 1901d19b14dfc..2410d513c299e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -64,6 +64,7 @@
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/Sema/SemaSPIRV.h"
 #include "clang/Sema/SemaSYCL.h"
+#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaSystemZ.h"
 #include "clang/Sema/SemaWasm.h"
@@ -248,7 +249,8 @@ const unsigned Sema::MaxAlignmentExponent;
 const uint64_t Sema::MaximumAlignment;
 
 Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
-           TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter)
+           TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter,
+           SummaryConsumer *SummaryConsumer)
     : SemaBase(*this), CollectStats(false), TUKind(TUKind),
       CurFPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
       Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
@@ -263,6 +265,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       BPFPtr(std::make_unique<SemaBPF>(*this)),
       CodeCompletionPtr(
           std::make_unique<SemaCodeCompletion>(*this, CodeCompleter)),
+      SummarizerPtr(SummaryConsumer ? std::make_unique<SemaSummarizer>(
+                                          *this, SummaryConsumer)
+                                    : nullptr),
       CUDAPtr(std::make_unique<SemaCUDA>(*this)),
       DirectXPtr(std::make_unique<SemaDirectX>(*this)),
       HLSLPtr(std::make_unique<SemaHLSL>(*this)),
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 814f81cb64cae..bb80fd2a46f58 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -55,6 +55,7 @@
 #include "clang/Sema/SemaPPC.h"
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/Sema/SemaSYCL.h"
+#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaWasm.h"
 #include "clang/Sema/Template.h"
@@ -16694,6 +16695,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
   if (FD && !FD->isDeleted())
     checkTypeSupport(FD->getType(), FD->getLocation(), FD);
 
+  if (FD && SummarizerPtr)
+    SummarizerPtr->SummarizeFunctionBody(FD);
+
   return dcl;
 }
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
new file mode 100644
index 0000000000000..ce1d514d3df9c
--- /dev/null
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -0,0 +1,8 @@
+#include "clang/Sema/SemaSummarizer.h"
+
+namespace clang {
+void SemaSummarizer::SummarizeFunctionBody(FunctionDecl *FD) const {
+  FD->dump();
+}
+
+} // namespace clang
\ No newline at end of file
diff --git a/clang/lib/Testing/TestAST.cpp b/clang/lib/Testing/TestAST.cpp
index 748f59b856e83..db0490689da53 100644
--- a/clang/lib/Testing/TestAST.cpp
+++ b/clang/lib/Testing/TestAST.cpp
@@ -69,7 +69,7 @@ void createMissingComponents(CompilerInstance &Clang) {
   if (!Clang.hasASTContext())
     Clang.createASTContext();
   if (!Clang.hasSema())
-    Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr);
+    Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr, nullptr);
 }
 
 } // namespace
diff --git a/clang/unittests/CodeGen/TestCompiler.h b/clang/unittests/CodeGen/TestCompiler.h
index a6fec7fb0945d..760fa340c3d74 100644
--- a/clang/unittests/CodeGen/TestCompiler.h
+++ b/clang/unittests/CodeGen/TestCompiler.h
@@ -69,7 +69,7 @@ struct TestCompiler {
 
     compiler.setASTConsumer(std::move(Consumer));
 
-    compiler.createSema(clang::TU_Prefix, nullptr);
+    compiler.createSema(clang::TU_Prefix, nullptr, nullptr);
 
     clang::SourceManager &sm = compiler.getSourceManager();
     sm.setMainFileID(sm.createFileID(
diff --git a/clang/unittests/Frontend/CodeGenActionTest.cpp b/clang/unittests/Frontend/CodeGenActionTest.cpp
index 90818b72cd6e6..e958ea1993a4a 100644
--- a/clang/unittests/Frontend/CodeGenActionTest.cpp
+++ b/clang/unittests/Frontend/CodeGenActionTest.cpp
@@ -37,7 +37,7 @@ class NullCodeGenAction : public CodeGenAction {
     if (!CI.hasPreprocessor())
       return;
     if (!CI.hasSema())
-      CI.createSema(getTranslationUnitKind(), nullptr);
+      CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
   }
 };
 
diff --git a/clang/unittests/Sema/ExternalSemaSourceTest.cpp b/clang/unittests/Sema/ExternalSemaSourceTest.cpp
index 2b271d4bf7825..d223a7135ee84 100644
--- a/clang/unittests/Sema/ExternalSemaSourceTest.cpp
+++ b/clang/unittests/Sema/ExternalSemaSourceTest.cpp
@@ -194,7 +194,7 @@ class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
   void ExecuteAction() override {
     CompilerInstance &CI = getCompilerInstance();
     ASSERT_FALSE(CI.hasSema());
-    CI.createSema(getTranslationUnitKind(), nullptr);
+    CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
     ASSERT_TRUE(CI.hasDiagnostics());
     DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
     DiagnosticConsumer *Client = Diagnostics.getClient();
diff --git a/clang/unittests/Sema/SemaLookupTest.cpp b/clang/unittests/Sema/SemaLookupTest.cpp
index d97b571f6a37c..96c27945421f9 100644
--- a/clang/unittests/Sema/SemaLookupTest.cpp
+++ b/clang/unittests/Sema/SemaLookupTest.cpp
@@ -22,7 +22,7 @@ class LookupAction : public ASTFrontendAction {
   void ExecuteAction() override {
     CompilerInstance &CI = getCompilerInstance();
     ASSERT_FALSE(CI.hasSema());
-    CI.createSema(getTranslationUnitKind(), nullptr);
+    CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
     ASSERT_TRUE(CI.hasSema());
     Sema &S = CI.getSema();
     ParseAST(S);

>From 2592f08b25c7394f7a765a4ca1ecb9ce69657560 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sun, 8 Jun 2025 00:21:20 +0200
Subject: [PATCH 03/24] [clang][Summary] implement summary base prototype

---
 clang/include/clang/Sema/SemaSummarizer.h |  2 +-
 clang/lib/Sema/SemaSummarizer.cpp         | 57 +++++++++++++++++++++--
 2 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 3661e443bb2de..45e97529a2f1f 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -12,7 +12,7 @@ class SemaSummarizer : public SemaBase {
 
   SummaryConsumer *SummaryConsumer;
 
-  void SummarizeFunctionBody(FunctionDecl *FD) const;
+  void SummarizeFunctionBody(const FunctionDecl *FD);
 };
 } // namespace clang
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index ce1d514d3df9c..65ac0a7e4ec36 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -1,8 +1,59 @@
 #include "clang/Sema/SemaSummarizer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Index/USRGeneration.h"
+#include <set>
 
 namespace clang {
-void SemaSummarizer::SummarizeFunctionBody(FunctionDecl *FD) const {
-  FD->dump();
+namespace {
+class FunctionSummary {
+  SmallVector<char> ID;
+  std::vector<std::string> FunctionAttrs;
+  std::set<SmallVector<char>> Calls;
+
+public:
+  void addCall(const clang::FunctionDecl *FD) {
+    SmallVector<char> Call;
+    index::generateUSRForDecl(FD, Call);
+    Calls.emplace(Call);
+  }
+
+  FunctionSummary(const clang::FunctionDecl *FD) {
+    index::generateUSRForDecl(FD, ID);
+  }
+};
+
+class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
+  FunctionSummary *Summary;
+
+public:
+  CallCollector(FunctionSummary &Summary) : Summary(&Summary) {}
+
+  virtual void
+  run(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+    if (!Call)
+      return;
+
+    const auto *Callee = llvm::dyn_cast<FunctionDecl>(Call->getCalleeDecl());
+    Summary->addCall(Callee);
+  }
+};
+
+void CollectCalledFunctions(const FunctionDecl *FD, FunctionSummary &Summary) {
+  using namespace ast_matchers;
+  MatchFinder Finder;
+  CallCollector CC(Summary);
+
+  Finder.addMatcher(functionDecl(forEachDescendant(callExpr().bind("call"))),
+                    &CC);
+  Finder.match(*FD, FD->getASTContext());
+}
+
+} // namespace
+
+void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
+  FunctionSummary Summary(FD);
+  CollectCalledFunctions(FD, Summary);
 }
 
-} // namespace clang
\ No newline at end of file
+} // namespace clang

>From 43c1b90334a7abc7caf636f6bc15ff4a3194be13 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sun, 8 Jun 2025 14:53:27 +0200
Subject: [PATCH 04/24] [clang][Summary] implement summary inference prototype

---
 clang/include/clang/Sema/SemaSummarizer.h   | 22 ++++-
 clang/include/clang/Sema/SummaryAttribute.h | 54 ++++++++++++
 clang/lib/Sema/SemaSummarizer.cpp           | 95 +++++++++++++++------
 3 files changed, 141 insertions(+), 30 deletions(-)
 create mode 100644 clang/include/clang/Sema/SummaryAttribute.h

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 45e97529a2f1f..71ec68cf947ba 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -2,15 +2,31 @@
 #define LLVM_CLANG_SEMA_SEMASUMMARIZER_H
 
 #include "clang/Sema/SemaBase.h"
+#include "clang/Sema/SummaryAttribute.h"
 #include "clang/Sema/SummaryConsumer.h"
+#include <set>
 
 namespace clang {
+class FunctionSummary {
+  SmallVector<char> ID;
+  std::set<SummaryAttribute> FunctionAttrs;
+  std::set<SmallVector<char>> Calls;
+
+public:
+  FunctionSummary(const clang::FunctionDecl *FD);
+
+  void addAttribute(SummaryAttribute Attr) { FunctionAttrs.emplace(Attr); }
+  bool hasAttribute(SummaryAttribute Attr) { return FunctionAttrs.count(Attr); }
+
+  void addCall(const clang::FunctionDecl *FD);
+};
+
 class SemaSummarizer : public SemaBase {
 public:
-  SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer)
-      : SemaBase(S), SummaryConsumer(SummaryConsumer) {}
+  SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer);
 
-  SummaryConsumer *SummaryConsumer;
+  std::vector<std::unique_ptr<SummaryAttributeManager>> Attributes;
+  SummaryConsumer *TheSummaryConsumer;
 
   void SummarizeFunctionBody(const FunctionDecl *FD);
 };
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
new file mode 100644
index 0000000000000..ed8a8125d22f1
--- /dev/null
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -0,0 +1,54 @@
+#ifndef LLVM_CLANG_SEMA_SEMASUMMARYATTRIBUTE_H
+#define LLVM_CLANG_SEMA_SEMASUMMARYATTRIBUTE_H
+
+#include "clang/AST/Decl.h"
+#include <string>
+
+namespace clang {
+enum SummaryAttribute {
+  NO_WRITE_GLOBAL,
+};
+
+class FunctionSummary;
+
+class SummaryAttributeManager {
+  inline static std::unordered_map<SummaryAttribute, std::string> AttrToStr;
+
+protected:
+  const SummaryAttribute Attr;
+  const char *Str;
+
+public:
+  SummaryAttributeManager(SummaryAttribute Attr, const char *Str)
+      : Attr(Attr), Str(Str) {
+    assert(AttrToStr.count(Attr) == 0 && "attribute already registered");
+    for (auto &&[attr, str] : AttrToStr)
+      assert(str != Str && "attribute representation is already used");
+
+    AttrToStr[Attr] = Str;
+  }
+  virtual ~SummaryAttributeManager() = default;
+
+  virtual bool predicate(const FunctionDecl *FD) = 0;
+
+  // FIXME: This should receive all the parsed summaries as well.
+  virtual bool merge(FunctionSummary &Summary) = 0;
+
+  virtual std::string serialize() const { return Str; };
+  virtual std::optional<SummaryAttribute> parse(std::string_view Input) const {
+    if (Str == Input)
+      return Attr;
+
+    return std::nullopt;
+  };
+
+  std::optional<SummaryAttribute> infer(const FunctionDecl *FD) {
+    if (predicate(FD))
+      return Attr;
+
+    return std::nullopt;
+  };
+};
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_SEMASUMMARYATTRIBUTEH
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 65ac0a7e4ec36..1a7dd7ba26f99 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -1,31 +1,14 @@
 #include "clang/Sema/SemaSummarizer.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Index/USRGeneration.h"
+#include "clang/Sema/SummaryConsumer.h"
 #include <set>
 
 namespace clang {
 namespace {
-class FunctionSummary {
-  SmallVector<char> ID;
-  std::vector<std::string> FunctionAttrs;
-  std::set<SmallVector<char>> Calls;
-
-public:
-  void addCall(const clang::FunctionDecl *FD) {
-    SmallVector<char> Call;
-    index::generateUSRForDecl(FD, Call);
-    Calls.emplace(Call);
-  }
-
-  FunctionSummary(const clang::FunctionDecl *FD) {
-    index::generateUSRForDecl(FD, ID);
-  }
-};
-
 class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
   FunctionSummary *Summary;
 
-public:
   CallCollector(FunctionSummary &Summary) : Summary(&Summary) {}
 
   virtual void
@@ -37,23 +20,81 @@ class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
     const auto *Callee = llvm::dyn_cast<FunctionDecl>(Call->getCalleeDecl());
     Summary->addCall(Callee);
   }
+
+public:
+  static void CollectCalledFunctions(const FunctionDecl *FD,
+                                     FunctionSummary &Summary) {
+    using namespace ast_matchers;
+    MatchFinder Finder;
+    CallCollector CC(Summary);
+
+    Finder.addMatcher(functionDecl(forEachDescendant(callExpr().bind("call"))),
+                      &CC);
+    Finder.match(*FD, FD->getASTContext());
+  }
 };
 
-void CollectCalledFunctions(const FunctionDecl *FD, FunctionSummary &Summary) {
-  using namespace ast_matchers;
-  MatchFinder Finder;
-  CallCollector CC(Summary);
+class NoWriteGlobalAttrManager : public SummaryAttributeManager {
+  class Callback : public ast_matchers::MatchFinder::MatchCallback {
+  public:
+    bool WriteGlobal = false;
 
-  Finder.addMatcher(functionDecl(forEachDescendant(callExpr().bind("call"))),
-                    &CC);
-  Finder.match(*FD, FD->getASTContext());
-}
+    void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
+      const auto *Assignment =
+          Result.Nodes.getNodeAs<BinaryOperator>("assignment");
+      if (!Assignment)
+        return;
+
+      WriteGlobal = true;
+    };
+  };
 
+public:
+  NoWriteGlobalAttrManager()
+      : SummaryAttributeManager(NO_WRITE_GLOBAL, "no_write_global") {}
+
+  bool predicate(const FunctionDecl *FD) override {
+    using namespace ast_matchers;
+    MatchFinder Finder;
+    Callback CB;
+
+    Finder.addMatcher(
+        functionDecl(forEachDescendant(
+            binaryOperator(isAssignmentOperator(),
+                           hasLHS(declRefExpr(to(varDecl(hasGlobalStorage())))))
+                .bind("assignment"))),
+        &CB);
+    Finder.match(*FD, FD->getASTContext());
+    return !CB.WriteGlobal;
+  };
+
+  bool merge(FunctionSummary &Summary) override { return true; };
+};
 } // namespace
 
+void FunctionSummary::addCall(const clang::FunctionDecl *FD) {
+  SmallVector<char> Call;
+  index::generateUSRForDecl(FD, Call);
+  Calls.emplace(Call);
+}
+
+FunctionSummary::FunctionSummary(const clang::FunctionDecl *FD) {
+  index::generateUSRForDecl(FD, ID);
+}
+
+SemaSummarizer::SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer)
+    : SemaBase(S), TheSummaryConsumer(SummaryConsumer) {
+  Attributes.emplace_back(std::make_unique<NoWriteGlobalAttrManager>());
+}
+
 void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
   FunctionSummary Summary(FD);
-  CollectCalledFunctions(FD, Summary);
+  CallCollector::CollectCalledFunctions(FD, Summary);
+
+  for (auto &&Attr : Attributes) {
+    if (const auto &InferredAttr = Attr->infer(FD))
+      Summary.addAttribute(*InferredAttr);
+  }
 }
 
 } // namespace clang

>From f1ea491e6f2cf7c6d21f25b611cd33cd6607567b Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sun, 8 Jun 2025 17:08:55 +0200
Subject: [PATCH 05/24] [clang][Summary] summary printing prototype

---
 clang/include/clang/Sema/SemaSummarizer.h   | 10 ++++++-
 clang/include/clang/Sema/SummaryAttribute.h |  3 ++-
 clang/include/clang/Sema/SummaryConsumer.h  | 30 ++++++++++++++++++++-
 clang/lib/Frontend/CompilerInstance.cpp     | 12 +++++++--
 clang/lib/Frontend/FrontendAction.cpp       |  6 -----
 clang/lib/Sema/CMakeLists.txt               |  1 +
 clang/lib/Sema/Sema.cpp                     |  5 ++++
 clang/lib/Sema/SemaSummarizer.cpp           | 13 +++++++++
 clang/lib/Sema/SummaryConsumer.cpp          | 24 +++++++++++++++++
 9 files changed, 93 insertions(+), 11 deletions(-)
 create mode 100644 clang/lib/Sema/SummaryConsumer.cpp

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 71ec68cf947ba..ef40599d982a1 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -15,8 +15,12 @@ class FunctionSummary {
 public:
   FunctionSummary(const clang::FunctionDecl *FD);
 
+  SmallVector<char> getID() const { return ID; }
+  const std::set<SummaryAttribute> &getFunctionAttrs() const { return FunctionAttrs; }
+  const std::set<SmallVector<char>> &getCalls() const { return Calls; }
+
   void addAttribute(SummaryAttribute Attr) { FunctionAttrs.emplace(Attr); }
-  bool hasAttribute(SummaryAttribute Attr) { return FunctionAttrs.count(Attr); }
+  bool hasAttribute(SummaryAttribute Attr) const { return FunctionAttrs.count(Attr); }
 
   void addCall(const clang::FunctionDecl *FD);
 };
@@ -28,7 +32,11 @@ class SemaSummarizer : public SemaBase {
   std::vector<std::unique_ptr<SummaryAttributeManager>> Attributes;
   SummaryConsumer *TheSummaryConsumer;
 
+  void ActOnStartOfSourceFile();
+  void ActOnEndOfSourceFile();
+  
   void SummarizeFunctionBody(const FunctionDecl *FD);
+
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index ed8a8125d22f1..2d2fd1e4ff623 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -34,7 +34,8 @@ class SummaryAttributeManager {
   // FIXME: This should receive all the parsed summaries as well.
   virtual bool merge(FunctionSummary &Summary) = 0;
 
-  virtual std::string serialize() const { return Str; };
+  // FIXME: bad design
+  static std::string serialize(SummaryAttribute Attr) { return AttrToStr[Attr]; };
   virtual std::optional<SummaryAttribute> parse(std::string_view Input) const {
     if (Str == Input)
       return Attr;
diff --git a/clang/include/clang/Sema/SummaryConsumer.h b/clang/include/clang/Sema/SummaryConsumer.h
index 8aa2713b46c65..c2698288ce721 100644
--- a/clang/include/clang/Sema/SummaryConsumer.h
+++ b/clang/include/clang/Sema/SummaryConsumer.h
@@ -1,8 +1,36 @@
 #ifndef LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
 #define LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
 
+#include "clang/Basic/LLVM.h"
+#include "llvm/Support/JSON.h"
 namespace clang {
-class SummaryConsumer {};
+class FunctionSummary;
+
+class SummaryConsumer {
+public:
+    virtual ~SummaryConsumer() = default;
+
+    virtual void ProcessStartOfSourceFile() {};
+    virtual void ProcessFunctionSummary(const FunctionSummary&) {};
+    virtual void ProcessEndOfSourceFile() {};
+};
+
+class PrintingSummaryConsumer : public SummaryConsumer {
+public:
+    PrintingSummaryConsumer(raw_ostream &OS)
+      : SummaryConsumer() {}
+};
+
+class JSONPrintingSummaryConsumer : public PrintingSummaryConsumer {
+    llvm::json::OStream JOS;
+
+public:
+    JSONPrintingSummaryConsumer(raw_ostream &OS) : PrintingSummaryConsumer(OS), JOS(OS, 2) {}
+
+    void ProcessStartOfSourceFile() override { JOS.arrayBegin(); };
+    void ProcessFunctionSummary(const FunctionSummary&) override;
+    void ProcessEndOfSourceFile() override { JOS.arrayEnd(); };
+};
 } // namespace clang
 
 #endif // LLVM_CLANG_SEMA_SUMMARYCONSUMER_H
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 917a187f49fb5..c90b8884f5731 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -743,8 +743,16 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP,
 }
 
 void CompilerInstance::createSummaryConsumer() {
-  TheSummaryConsumer.reset(
-      getFrontendOpts().SummaryFile.empty() ? nullptr : new SummaryConsumer());
+  const std::string& SummaryFile = getFrontendOpts().SummaryFile;
+  if(SummaryFile.empty())
+    return;
+
+  std::error_code EC;
+  // FIXME: this being static is a design error
+  static llvm::raw_fd_ostream SummaryOS(SummaryFile, EC, llvm::sys::fs::CD_CreateAlways);
+
+  if(!EC)
+    TheSummaryConsumer.reset(new JSONPrintingSummaryConsumer(SummaryOS));
 }
 
 void CompilerInstance::createSema(TranslationUnitKind TUKind,
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index aeeab901b9ed7..35b0c0373533a 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -1253,12 +1253,6 @@ void FrontendAction::EndSourceFile() {
   // Finalize the action.
   EndSourceFileAction();
 
-  if (CI.hasSema() && !CI.getFrontendOpts().SummaryFile.empty()) {
-    std::error_code EC;
-    llvm::raw_fd_ostream(CI.getFrontendOpts().SummaryFile, EC,
-                         llvm::sys::fs::CD_CreateAlways);
-  }
-
   // Sema references the ast consumer, so reset sema first.
   //
   // FIXME: There is more per-file stuff we could just drop here?
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 5237f20201fde..d6297016c015f 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -99,6 +99,7 @@ add_clang_library(clangSema
   SemaType.cpp
   SemaWasm.cpp
   SemaX86.cpp
+  SummaryConsumer.cpp
   TypeLocBuilder.cpp
 
   DEPENDS
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 2410d513c299e..ae9f8c00081b3 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1147,6 +1147,9 @@ void Sema::ActOnStartOfTranslationUnit() {
   if (getLangOpts().CPlusPlusModules &&
       getLangOpts().getCompilingModule() == LangOptions::CMK_HeaderUnit)
     HandleStartOfHeaderUnit();
+  
+  if(SummarizerPtr)
+    SummarizerPtr->ActOnStartOfSourceFile();
 }
 
 void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
@@ -1222,6 +1225,8 @@ void Sema::ActOnEndOfTranslationUnit() {
   assert(DelayedDiagnostics.getCurrentPool() == nullptr
          && "reached end of translation unit with a pool attached?");
 
+  if(SummarizerPtr)
+    SummarizerPtr->ActOnEndOfSourceFile();
   // If code completion is enabled, don't perform any end-of-translation-unit
   // work.
   if (PP.isCodeCompletionEnabled())
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 1a7dd7ba26f99..9fbb940ec3fbb 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -87,6 +87,16 @@ SemaSummarizer::SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer)
   Attributes.emplace_back(std::make_unique<NoWriteGlobalAttrManager>());
 }
 
+void SemaSummarizer::ActOnStartOfSourceFile() {
+  if(TheSummaryConsumer)
+    TheSummaryConsumer->ProcessStartOfSourceFile();
+}
+
+void SemaSummarizer::ActOnEndOfSourceFile() {
+  if(TheSummaryConsumer)
+    TheSummaryConsumer->ProcessEndOfSourceFile();
+}
+
 void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
   FunctionSummary Summary(FD);
   CallCollector::CollectCalledFunctions(FD, Summary);
@@ -95,6 +105,9 @@ void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
     if (const auto &InferredAttr = Attr->infer(FD))
       Summary.addAttribute(*InferredAttr);
   }
+
+  if(TheSummaryConsumer)
+    TheSummaryConsumer->ProcessFunctionSummary(Summary);
 }
 
 } // namespace clang
diff --git a/clang/lib/Sema/SummaryConsumer.cpp b/clang/lib/Sema/SummaryConsumer.cpp
new file mode 100644
index 0000000000000..45aeaee502ed9
--- /dev/null
+++ b/clang/lib/Sema/SummaryConsumer.cpp
@@ -0,0 +1,24 @@
+#include "clang/Sema/SummaryConsumer.h"
+#include "clang/Sema/SemaSummarizer.h"
+
+namespace clang {
+void JSONPrintingSummaryConsumer::ProcessFunctionSummary(const FunctionSummary &Summary) {
+  JOS.object([&]{
+    JOS.attribute("id", llvm::json::Value(Summary.getID()));
+    JOS.attributeObject("attrs", [&]{
+      JOS.attributeArray("function", [&]{
+        for(auto &&Attr : Summary.getFunctionAttrs()) {
+          JOS.value(llvm::json::Value(SummaryAttributeManager::serialize(Attr)));
+        }
+      });
+    });
+    JOS.attributeArray("calls", [&]{
+      for(auto &&Call : Summary.getCalls()) {
+        JOS.object([&]{
+          JOS.attribute("id", llvm::json::Value(Call));
+        });
+      }
+    });
+  });
+}
+} // namespace clang
\ No newline at end of file

>From 6fb640433209596d293316ed07dab58e014acf2a Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sun, 8 Jun 2025 22:26:29 +0200
Subject: [PATCH 06/24] [clang][Driver][Summary] add a flag to specify the
 directory to parse the summaries from

---
 clang/include/clang/Driver/Options.td         |  4 +++
 .../include/clang/Frontend/FrontendOptions.h  |  3 ++
 clang/lib/Driver/ToolChains/Clang.cpp         |  3 ++
 clang/lib/Frontend/FrontendAction.cpp         | 29 +++++++++++++++++++
 4 files changed, 39 insertions(+)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index f0c7b277e68e2..c66f098c459c2 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -5941,6 +5941,10 @@ def save_temps : Flag<["-", "--"], "save-temps">, Flags<[NoXarchOption]>,
   Visibility<[ClangOption, FlangOption, FC1Option]>,
   Alias<save_temps_EQ>, AliasArgs<["cwd"]>,
   HelpText<"Alias for --save-temps=cwd">;
+def summaries_dir_EQ : Joined<["-", "--"], "summaries-dir=">, Flags<[NoXarchOption]>,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Read summaries about different functions from this directory">,
+  MarshallingInfoString<FrontendOpts<"SummaryDirPath">>;
 def emit_summaries_EQ : Joined<["-", "--"], "emit-summaries=">, Flags<[NoXarchOption]>,
   Visibility<[ClangOption, CC1Option]>,
   HelpText<"Save summaries about the different functions. <arg> can be set to 'cwd' for "
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index af2451c1bde8e..20c60f823d5f3 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -537,6 +537,9 @@ class FrontendOptions {
   /// Filename to write summaries about function definitions to.
   std::string SummaryFile;
 
+  /// The directory used to load summary files.
+  std::string SummaryDirPath;
+
 public:
   FrontendOptions()
       : DisableFree(false), RelocatablePCH(false), ShowHelp(false),
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 676a4e34ddb27..48b19615ab08f 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5470,6 +5470,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   if (Args.getLastArg(options::OPT_save_temps_EQ))
     Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);
 
+  if (Args.getLastArg(options::OPT_summaries_dir_EQ))
+    Args.AddLastArg(CmdArgs, options::OPT_summaries_dir_EQ);
+
   // FIXME: This needs to be cleaned up and needs proper error handling as well.
   if (const Arg *A = Args.getLastArg(options::OPT_emit_summaries_EQ)) {
     llvm::SmallString<10> input;
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 35b0c0373533a..9cbebbabd8529 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -46,7 +46,9 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
+#include <fstream>
 #include <memory>
+#include <sstream>
 #include <system_error>
 using namespace clang;
 
@@ -962,6 +964,33 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     }
   }
 
+  // FIXME: lookup dirs recursively
+  if (!CI.getFrontendOpts().SummaryDirPath.empty()) {
+    FileManager &FileMgr = CI.getFileManager();
+
+    StringRef SummaryDirPath = CI.getFrontendOpts().SummaryDirPath;
+    if (auto SummaryDir = FileMgr.getOptionalDirectoryRef(SummaryDirPath)) {
+      std::error_code EC;
+      SmallString<128> DirNative;
+      llvm::sys::path::native(SummaryDir->getName(), DirNative);
+
+      llvm::vfs::FileSystem &FS = FileMgr.getVirtualFileSystem();
+      for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC),
+                                         DirEnd;
+           Dir != DirEnd && !EC; Dir.increment(EC)) {
+        if (llvm::sys::path::extension(Dir->path()) == ".json") {
+          std::ifstream t(Dir->path().str());
+          std::stringstream buffer;
+          buffer << t.rdbuf();
+
+          auto JSON = llvm::json::parse(buffer.str());
+          if (JSON)
+            JSON->dump();
+        }
+      }
+    }
+  }
+
   // Set up the preprocessor if needed. When parsing model files the
   // preprocessor of the original source is reused.
   if (!isModelParsingAction())

>From a45d2c0cedab95d5b0db1dba11218f97e86f64b9 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Mon, 9 Jun 2025 23:45:47 +0200
Subject: [PATCH 07/24] [clang][Summary] implement parsing the summaries

---
 .../include/clang/Frontend/CompilerInstance.h |  14 +++
 clang/include/clang/Sema/Sema.h               |   2 +
 clang/include/clang/Sema/SemaSummarizer.h     |  29 ++++-
 clang/include/clang/Sema/SummaryAttribute.h   |  35 ++----
 clang/include/clang/Sema/SummaryConsumer.h    |  11 +-
 clang/lib/Frontend/CompilerInstance.cpp       |   9 +-
 clang/lib/Frontend/FrontendAction.cpp         |  23 ++--
 clang/lib/Sema/CMakeLists.txt                 |   1 +
 clang/lib/Sema/Sema.cpp                       |   4 +-
 clang/lib/Sema/SemaDecl.cpp                   |   2 +-
 clang/lib/Sema/SemaSummarizer.cpp             | 105 +++++++++++++++---
 clang/lib/Sema/SummaryAttribute.cpp           |  19 ++++
 clang/lib/Sema/SummaryConsumer.cpp            |  18 +--
 13 files changed, 191 insertions(+), 81 deletions(-)
 create mode 100644 clang/lib/Sema/SummaryAttribute.cpp

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index f296e6d042a54..f8f13bbc998d9 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -57,6 +57,7 @@ class Module;
 class ModuleCache;
 class Preprocessor;
 class Sema;
+class SummaryManager;
 class SourceManager;
 class TargetInfo;
 enum class DisableValidationForModuleKind;
@@ -124,6 +125,9 @@ class CompilerInstance : public ModuleLoader {
 
   /// The summary consumer.
   std::unique_ptr<SummaryConsumer> TheSummaryConsumer;
+  
+  /// The summary manager object.
+  std::unique_ptr<SummaryManager> TheSummaryManager;
 
   /// The semantic analysis object.
   std::unique_ptr<Sema> TheSema;
@@ -520,6 +524,15 @@ class CompilerInstance : public ModuleLoader {
   /// setASTContext - Replace the current AST context.
   void setASTContext(ASTContext *Value);
 
+  bool hasSummaryManager() {
+    return TheSummaryManager != nullptr;
+  }
+
+  SummaryManager &getSummaryManager() {
+    assert(TheSummaryManager && "Compiler instance has no summary manager!");
+    return *TheSummaryManager;
+  }
+
   /// Replace the current Sema; the compiler instance takes ownership
   /// of S.
   void setSema(Sema *S);
@@ -753,6 +766,7 @@ class CompilerInstance : public ModuleLoader {
       const CodeCompleteOptions &Opts, raw_ostream &OS);
 
   void createSummaryConsumer();
+  void createSummaryManager();
 
   /// Create the Sema object to be used for parsing.
   void createSema(TranslationUnitKind TUKind,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9cb2158ab4ff1..b36fce47fb792 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -125,6 +125,7 @@ class CXXBasePath;
 class CXXBasePaths;
 class CXXFieldCollector;
 class CodeCompleteConsumer;
+class SummaryManager;
 class SummaryConsumer;
 enum class ComparisonCategoryType : unsigned char;
 class ConstraintSatisfaction;
@@ -886,6 +887,7 @@ class Sema final : public SemaBase {
   Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
        TranslationUnitKind TUKind = TU_Complete,
        CodeCompleteConsumer *CompletionConsumer = nullptr,
+       SummaryManager *SummaryManager = nullptr,
        SummaryConsumer *SummaryConsumer = nullptr);
   ~Sema();
 
diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index ef40599d982a1..38e3565af0a95 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -13,6 +13,7 @@ class FunctionSummary {
   std::set<SmallVector<char>> Calls;
 
 public:
+  FunctionSummary(SmallVector<char> ID, std::set<SummaryAttribute> FunctionAttrs, std::set<SmallVector<char>> Calls);
   FunctionSummary(const clang::FunctionDecl *FD);
 
   SmallVector<char> getID() const { return ID; }
@@ -25,18 +26,36 @@ class FunctionSummary {
   void addCall(const clang::FunctionDecl *FD);
 };
 
-class SemaSummarizer : public SemaBase {
+class SummaryManager {
+  std::map<std::string, const FunctionSummary *> IDToSummary;
+  std::vector<std::unique_ptr<FunctionSummary>> FunctionSummaries;
+  
+  std::map<SummaryAttribute, const SummaryAttributeDescription *> AttrToDescription;
+  std::vector<std::unique_ptr<SummaryAttributeDescription>> AttributeDescriptions;
+
 public:
-  SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer);
+  SummaryManager();
+
+  FunctionSummary SummarizeFunctionBody(const FunctionDecl *FD);
+  
+  void SerializeSummary(llvm::json::OStream &, const FunctionSummary &) const;
+  void ParseSummaryFromJSON(StringRef path);
+
+  void ReduceSummaries();
+};
 
-  std::vector<std::unique_ptr<SummaryAttributeManager>> Attributes;
+// FIXME: Is this class needed?
+class SemaSummarizer : public SemaBase {
+public:
+  SummaryManager *TheSummaryManager;
   SummaryConsumer *TheSummaryConsumer;
 
+  SemaSummarizer(Sema &S, SummaryManager &SummaryManager, SummaryConsumer *SummaryConsumer) 
+    : SemaBase(S), TheSummaryManager(&SummaryManager), TheSummaryConsumer(SummaryConsumer) {};
+
   void ActOnStartOfSourceFile();
   void ActOnEndOfSourceFile();
-  
   void SummarizeFunctionBody(const FunctionDecl *FD);
-
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index 2d2fd1e4ff623..66d1d11334ac5 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -11,44 +11,25 @@ enum SummaryAttribute {
 
 class FunctionSummary;
 
-class SummaryAttributeManager {
-  inline static std::unordered_map<SummaryAttribute, std::string> AttrToStr;
-
+class SummaryAttributeDescription {
 protected:
   const SummaryAttribute Attr;
-  const char *Str;
+  std::string_view Serialzed;
 
 public:
-  SummaryAttributeManager(SummaryAttribute Attr, const char *Str)
-      : Attr(Attr), Str(Str) {
-    assert(AttrToStr.count(Attr) == 0 && "attribute already registered");
-    for (auto &&[attr, str] : AttrToStr)
-      assert(str != Str && "attribute representation is already used");
+  SummaryAttributeDescription(SummaryAttribute Attr, const char *Str) : Attr(Attr), Serialzed(Str) {}
+  virtual ~SummaryAttributeDescription() = default;
 
-    AttrToStr[Attr] = Str;
-  }
-  virtual ~SummaryAttributeManager() = default;
+  SummaryAttribute getAttribute() { return Attr; };
 
   virtual bool predicate(const FunctionDecl *FD) = 0;
+  std::optional<SummaryAttribute> infer(const FunctionDecl *FD);
 
   // FIXME: This should receive all the parsed summaries as well.
   virtual bool merge(FunctionSummary &Summary) = 0;
 
-  // FIXME: bad design
-  static std::string serialize(SummaryAttribute Attr) { return AttrToStr[Attr]; };
-  virtual std::optional<SummaryAttribute> parse(std::string_view Input) const {
-    if (Str == Input)
-      return Attr;
-
-    return std::nullopt;
-  };
-
-  std::optional<SummaryAttribute> infer(const FunctionDecl *FD) {
-    if (predicate(FD))
-      return Attr;
-
-    return std::nullopt;
-  };
+  virtual std::string serialize();
+  virtual std::optional<SummaryAttribute> parse(std::string_view input);
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Sema/SummaryConsumer.h b/clang/include/clang/Sema/SummaryConsumer.h
index c2698288ce721..85c48b3c1b939 100644
--- a/clang/include/clang/Sema/SummaryConsumer.h
+++ b/clang/include/clang/Sema/SummaryConsumer.h
@@ -5,9 +5,14 @@
 #include "llvm/Support/JSON.h"
 namespace clang {
 class FunctionSummary;
+class SummaryManager;
 
 class SummaryConsumer {
+protected:
+    const SummaryManager *TheSummaryManager;
+
 public:
+    SummaryConsumer(const SummaryManager &SummaryManager) : TheSummaryManager(&SummaryManager) {}
     virtual ~SummaryConsumer() = default;
 
     virtual void ProcessStartOfSourceFile() {};
@@ -17,15 +22,15 @@ class SummaryConsumer {
 
 class PrintingSummaryConsumer : public SummaryConsumer {
 public:
-    PrintingSummaryConsumer(raw_ostream &OS)
-      : SummaryConsumer() {}
+    PrintingSummaryConsumer(const SummaryManager &SummaryManager, raw_ostream &OS)
+      : SummaryConsumer(SummaryManager) {}
 };
 
 class JSONPrintingSummaryConsumer : public PrintingSummaryConsumer {
     llvm::json::OStream JOS;
 
 public:
-    JSONPrintingSummaryConsumer(raw_ostream &OS) : PrintingSummaryConsumer(OS), JOS(OS, 2) {}
+    JSONPrintingSummaryConsumer(const SummaryManager &SummaryManager, raw_ostream &OS) : PrintingSummaryConsumer(SummaryManager, OS), JOS(OS, 2) {}
 
     void ProcessStartOfSourceFile() override { JOS.arrayBegin(); };
     void ProcessFunctionSummary(const FunctionSummary&) override;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index c90b8884f5731..deed94e1f0b42 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -37,6 +37,7 @@
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/ParsedAttr.h"
 #include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SummaryConsumer.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
@@ -752,14 +753,18 @@ void CompilerInstance::createSummaryConsumer() {
   static llvm::raw_fd_ostream SummaryOS(SummaryFile, EC, llvm::sys::fs::CD_CreateAlways);
 
   if(!EC)
-    TheSummaryConsumer.reset(new JSONPrintingSummaryConsumer(SummaryOS));
+    TheSummaryConsumer.reset(new JSONPrintingSummaryConsumer(getSummaryManager(), SummaryOS));
+}
+
+void CompilerInstance::createSummaryManager() {
+  TheSummaryManager.reset(new SummaryManager());
 }
 
 void CompilerInstance::createSema(TranslationUnitKind TUKind,
                                   CodeCompleteConsumer *CompletionConsumer,
                                   SummaryConsumer *SummaryConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
-                         TUKind, CompletionConsumer, SummaryConsumer));
+                         TUKind, CompletionConsumer, hasSummaryManager() ? &getSummaryManager() : nullptr, SummaryConsumer));
 
   // Set up API notes.
   TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 9cbebbabd8529..58aac1b8cd1b1 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -35,6 +35,7 @@
 #include "clang/Parse/ParseAST.h"
 #include "clang/Sema/HLSLExternalSemaSource.h"
 #include "clang/Sema/MultiplexExternalSemaSource.h"
+#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Serialization/ASTDeserializationListener.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
@@ -46,9 +47,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
-#include <fstream>
 #include <memory>
-#include <sstream>
 #include <system_error>
 using namespace clang;
 
@@ -894,6 +893,10 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     }
   }
 
+  if(!CI.hasSummaryManager()) {
+    CI.createSummaryManager();
+  }
+
   // Set up embedding for any specified files. Do this before we load any
   // source files, including the primary module map for the compilation.
   for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) {
@@ -978,16 +981,11 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
       for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC),
                                          DirEnd;
            Dir != DirEnd && !EC; Dir.increment(EC)) {
-        if (llvm::sys::path::extension(Dir->path()) == ".json") {
-          std::ifstream t(Dir->path().str());
-          std::stringstream buffer;
-          buffer << t.rdbuf();
-
-          auto JSON = llvm::json::parse(buffer.str());
-          if (JSON)
-            JSON->dump();
-        }
+        if (llvm::sys::path::extension(Dir->path()) == ".json")
+          CI.getSummaryManager().ParseSummaryFromJSON(Dir->path());
       }
+
+      CI.getSummaryManager().ReduceSummaries();
     }
   }
 
@@ -1362,6 +1360,9 @@ void ASTFrontendAction::ExecuteAction() {
   if (CI.hasCodeCompletionConsumer())
     CompletionConsumer = &CI.getCodeCompletionConsumer();
 
+  if(!CI.hasSummaryManager()) {
+    CI.createSummaryManager();
+  }
   CI.createSummaryConsumer();
 
   // Use a code completion consumer?
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index d6297016c015f..bf8f73dc985db 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -99,6 +99,7 @@ add_clang_library(clangSema
   SemaType.cpp
   SemaWasm.cpp
   SemaX86.cpp
+  SummaryAttribute.cpp
   SummaryConsumer.cpp
   TypeLocBuilder.cpp
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ae9f8c00081b3..5b3f1a4dca36e 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -250,6 +250,7 @@ const uint64_t Sema::MaximumAlignment;
 
 Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
            TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter,
+           SummaryManager *SummaryManager,
            SummaryConsumer *SummaryConsumer)
     : SemaBase(*this), CollectStats(false), TUKind(TUKind),
       CurFPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
@@ -265,8 +266,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       BPFPtr(std::make_unique<SemaBPF>(*this)),
       CodeCompletionPtr(
           std::make_unique<SemaCodeCompletion>(*this, CodeCompleter)),
-      SummarizerPtr(SummaryConsumer ? std::make_unique<SemaSummarizer>(
-                                          *this, SummaryConsumer)
+      SummarizerPtr(SummaryManager ? std::make_unique<SemaSummarizer>(*this, *SummaryManager, SummaryConsumer)
                                     : nullptr),
       CUDAPtr(std::make_unique<SemaCUDA>(*this)),
       DirectXPtr(std::make_unique<SemaDirectX>(*this)),
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index bb80fd2a46f58..45f1523868f75 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16695,7 +16695,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
   if (FD && !FD->isDeleted())
     checkTypeSupport(FD->getType(), FD->getLocation(), FD);
 
-  if (FD && SummarizerPtr)
+  if (FD && SummarizerPtr && SummarizerPtr->TheSummaryConsumer)
     SummarizerPtr->SummarizeFunctionBody(FD);
 
   return dcl;
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 9fbb940ec3fbb..b4ad0d4a6db2f 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -2,7 +2,10 @@
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Index/USRGeneration.h"
 #include "clang/Sema/SummaryConsumer.h"
+#include "clang/Sema/SummaryAttribute.h"
 #include <set>
+#include <fstream>
+#include <sstream>
 
 namespace clang {
 namespace {
@@ -34,7 +37,7 @@ class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
   }
 };
 
-class NoWriteGlobalAttrManager : public SummaryAttributeManager {
+class NoWriteGlobalDescription : public SummaryAttributeDescription {
   class Callback : public ast_matchers::MatchFinder::MatchCallback {
   public:
     bool WriteGlobal = false;
@@ -50,8 +53,8 @@ class NoWriteGlobalAttrManager : public SummaryAttributeManager {
   };
 
 public:
-  NoWriteGlobalAttrManager()
-      : SummaryAttributeManager(NO_WRITE_GLOBAL, "no_write_global") {}
+  NoWriteGlobalDescription()
+      : SummaryAttributeDescription(NO_WRITE_GLOBAL, "no_write_global") {}
 
   bool predicate(const FunctionDecl *FD) override {
     using namespace ast_matchers;
@@ -78,13 +81,95 @@ void FunctionSummary::addCall(const clang::FunctionDecl *FD) {
   Calls.emplace(Call);
 }
 
+FunctionSummary::FunctionSummary(SmallVector<char> ID, std::set<SummaryAttribute> FunctionAttrs, std::set<SmallVector<char>> Calls) :
+  ID(std::move(ID)), FunctionAttrs(std::move(FunctionAttrs)), Calls(std::move(Calls)) {}
+
 FunctionSummary::FunctionSummary(const clang::FunctionDecl *FD) {
   index::generateUSRForDecl(FD, ID);
 }
 
-SemaSummarizer::SemaSummarizer(Sema &S, SummaryConsumer *SummaryConsumer)
-    : SemaBase(S), TheSummaryConsumer(SummaryConsumer) {
-  Attributes.emplace_back(std::make_unique<NoWriteGlobalAttrManager>());
+SummaryManager::SummaryManager() {
+  AttributeDescriptions.emplace_back(std::make_unique<NoWriteGlobalDescription>());
+
+  for(auto &&AttrDescr : AttributeDescriptions)
+    AttrToDescription[AttrDescr->getAttribute()] = AttrDescr.get();
+}
+
+FunctionSummary SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
+  auto Summary = std::make_unique<FunctionSummary>(FD);
+  CallCollector::CollectCalledFunctions(FD, *Summary);
+
+  for (auto &&AttrDesc : AttributeDescriptions) {
+    if (const auto &Attr = AttrDesc->infer(FD))
+      Summary->addAttribute(*Attr);
+  }
+
+  // FIXME: This is duplicated and hurts my eyes regardless
+  std::string key(Summary->getID().begin(), Summary->getID().size());
+  auto *SummaryPtr = FunctionSummaries.emplace_back(std::move(Summary)).get();
+  IDToSummary[key] = SummaryPtr;
+  return *SummaryPtr;
+}
+
+void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSummary &Summary) const {
+  JOS.object([&]{
+    JOS.attribute("id", llvm::json::Value(Summary.getID()));
+    JOS.attributeObject("attrs", [&]{
+      JOS.attributeArray("function", [&]{
+        for(auto &&Attr : Summary.getFunctionAttrs()) {
+          JOS.value(llvm::json::Value(AttributeDescriptions[Attr]->serialize()));
+        }
+      });
+    });
+    JOS.attributeArray("calls", [&]{
+      for(auto &&Call : Summary.getCalls()) {
+        JOS.object([&]{
+          JOS.attribute("id", llvm::json::Value(Call));
+        });
+      }
+    });
+  });
+}
+
+void SummaryManager::ParseSummaryFromJSON(StringRef path) {
+  std::ifstream t(path.str());
+  std::stringstream buffer;
+  buffer << t.rdbuf();
+
+  auto JSON = llvm::json::parse(buffer.str());
+  if (!JSON)
+    return;
+
+  llvm::json::Array *Summaries = JSON->getAsArray();
+  for(auto it = Summaries->begin(); it != Summaries->end(); ++it) {
+    llvm::json::Object *Summary = it->getAsObject();
+
+    SmallString<128> ID(*Summary->getString("id"));
+    std::set<SummaryAttribute> FunctionAttrs;
+    llvm::json::Array *FunctionAttributes = Summary->getObject("attrs")->getArray("function");
+    for(auto attrIt = FunctionAttributes->begin(); attrIt != FunctionAttributes->end(); ++attrIt) {
+      for(auto &&AttrDesc : AttributeDescriptions) {
+        if(auto Attr = AttrDesc->parse(*attrIt->getAsString()))
+          FunctionAttrs.emplace(*Attr);
+      }
+    }
+
+    std::set<SmallVector<char>> Calls;
+    llvm::json::Array *CallEntries = Summary->getArray("calls");
+    for(auto callIt = CallEntries->begin(); callIt != CallEntries->end(); ++callIt) {
+      auto *Obj = callIt->getAsObject();
+      Calls.emplace(SmallString<128>(*Obj->getString("id")));
+    }
+    
+    std::string key = ID.str().str();
+    auto ParsedSummary = std::make_unique<FunctionSummary>(std::move(ID), std::move(FunctionAttrs), std::move(Calls));
+    auto *ParsedSummaryPtr = FunctionSummaries.emplace_back(std::move(ParsedSummary)).get();
+    IDToSummary[key] = ParsedSummaryPtr;
+  }
+}
+
+void SummaryManager::ReduceSummaries() {
+  // FIXME: implement
 }
 
 void SemaSummarizer::ActOnStartOfSourceFile() {
@@ -98,13 +183,7 @@ void SemaSummarizer::ActOnEndOfSourceFile() {
 }
 
 void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
-  FunctionSummary Summary(FD);
-  CallCollector::CollectCalledFunctions(FD, Summary);
-
-  for (auto &&Attr : Attributes) {
-    if (const auto &InferredAttr = Attr->infer(FD))
-      Summary.addAttribute(*InferredAttr);
-  }
+  FunctionSummary Summary = TheSummaryManager->SummarizeFunctionBody(FD);
 
   if(TheSummaryConsumer)
     TheSummaryConsumer->ProcessFunctionSummary(Summary);
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
new file mode 100644
index 0000000000000..4affec7b4ffb7
--- /dev/null
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -0,0 +1,19 @@
+#include "clang/Sema/SummaryAttribute.h"
+
+namespace clang {
+std::string SummaryAttributeDescription::serialize() { return std::string(Serialzed); }
+
+std::optional<SummaryAttribute> SummaryAttributeDescription::parse(std::string_view input) {
+  if(input == Serialzed)
+    return Attr;
+
+  return std::nullopt;
+}
+
+std::optional<SummaryAttribute> SummaryAttributeDescription::infer(const FunctionDecl *FD) {
+  if (predicate(FD))
+    return Attr;
+
+  return std::nullopt;
+}
+} // namespace clang
\ No newline at end of file
diff --git a/clang/lib/Sema/SummaryConsumer.cpp b/clang/lib/Sema/SummaryConsumer.cpp
index 45aeaee502ed9..37646c1e07a7f 100644
--- a/clang/lib/Sema/SummaryConsumer.cpp
+++ b/clang/lib/Sema/SummaryConsumer.cpp
@@ -3,22 +3,6 @@
 
 namespace clang {
 void JSONPrintingSummaryConsumer::ProcessFunctionSummary(const FunctionSummary &Summary) {
-  JOS.object([&]{
-    JOS.attribute("id", llvm::json::Value(Summary.getID()));
-    JOS.attributeObject("attrs", [&]{
-      JOS.attributeArray("function", [&]{
-        for(auto &&Attr : Summary.getFunctionAttrs()) {
-          JOS.value(llvm::json::Value(SummaryAttributeManager::serialize(Attr)));
-        }
-      });
-    });
-    JOS.attributeArray("calls", [&]{
-      for(auto &&Call : Summary.getCalls()) {
-        JOS.object([&]{
-          JOS.attribute("id", llvm::json::Value(Call));
-        });
-      }
-    });
-  });
+  TheSummaryManager->SerializeSummary(JOS, Summary);
 }
 } // namespace clang
\ No newline at end of file

>From e6b21071a455f1aeae544dec93cba08ccf985008 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Tue, 10 Jun 2025 23:30:34 +0200
Subject: [PATCH 08/24] [clang][Summary] implement reduction prototype

---
 clang/include/clang/Sema/SemaSummarizer.h   |  6 +++
 clang/include/clang/Sema/SummaryAttribute.h |  3 +-
 clang/lib/Frontend/FrontendAction.cpp       | 14 +++++++
 clang/lib/Sema/SemaSummarizer.cpp           | 42 ++++++++++++++++++++-
 4 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 38e3565af0a95..7d2f8252dc957 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -20,6 +20,7 @@ class FunctionSummary {
   const std::set<SummaryAttribute> &getFunctionAttrs() const { return FunctionAttrs; }
   const std::set<SmallVector<char>> &getCalls() const { return Calls; }
 
+  void clearAttributes() { FunctionAttrs.clear(); }
   void addAttribute(SummaryAttribute Attr) { FunctionAttrs.emplace(Attr); }
   bool hasAttribute(SummaryAttribute Attr) const { return FunctionAttrs.count(Attr); }
 
@@ -42,6 +43,11 @@ class SummaryManager {
   void ParseSummaryFromJSON(StringRef path);
 
   void ReduceSummaries();
+
+  // FIXME: debug only, remove later
+  const std::vector<std::unique_ptr<FunctionSummary>> &getSummaries() {
+    return FunctionSummaries;
+  }
 };
 
 // FIXME: Is this class needed?
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index 66d1d11334ac5..beb735c00933c 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -25,8 +25,7 @@ class SummaryAttributeDescription {
   virtual bool predicate(const FunctionDecl *FD) = 0;
   std::optional<SummaryAttribute> infer(const FunctionDecl *FD);
 
-  // FIXME: This should receive all the parsed summaries as well.
-  virtual bool merge(FunctionSummary &Summary) = 0;
+  virtual bool merge(const FunctionSummary &Summary) const = 0;
 
   virtual std::string serialize();
   virtual std::optional<SummaryAttribute> parse(std::string_view input);
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 58aac1b8cd1b1..7af4a79f83eec 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -985,7 +985,21 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
           CI.getSummaryManager().ParseSummaryFromJSON(Dir->path());
       }
 
+      // FIXME: debug only, remove later
+      for (auto &&S : CI.getSummaryManager().getSummaries()) {
+        llvm::json::OStream Out(llvm::errs());
+        CI.getSummaryManager().SerializeSummary(Out, *S);
+      }
+      llvm::errs() << '\n';
+
       CI.getSummaryManager().ReduceSummaries();
+
+      // FIXME: debug only, remove later
+      for (auto &&S : CI.getSummaryManager().getSummaries()) {
+        llvm::json::OStream Out(llvm::errs());
+        CI.getSummaryManager().SerializeSummary(Out, *S);
+      }
+      llvm::errs() << '\n';
     }
   }
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index b4ad0d4a6db2f..569d272b36bb3 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -71,7 +71,9 @@ class NoWriteGlobalDescription : public SummaryAttributeDescription {
     return !CB.WriteGlobal;
   };
 
-  bool merge(FunctionSummary &Summary) override { return true; };
+  bool merge(const FunctionSummary &Summary) const override {
+    return Summary.getFunctionAttrs().count(Attr);
+  };
 };
 } // namespace
 
@@ -169,7 +171,43 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
 }
 
 void SummaryManager::ReduceSummaries() {
-  // FIXME: implement
+  bool changed = true;
+  while (changed) {
+    changed = false;
+
+    for (auto &&Function : FunctionSummaries) {
+      for (auto &&call : Function->getCalls()) {
+        // FIXME: This is duplicated and hurts my eyes regardless
+        std::string key(call.begin(), call.size());
+
+        // If we don't have a summary about a called function, we forget
+        // everything about the current one as well.
+        if (!IDToSummary.count(key)) {
+          changed = true;
+          Function->clearAttributes();
+          break;
+        }
+
+        const FunctionSummary *callSummary = IDToSummary.at(key);
+
+        std::set<SummaryAttribute> reducedAttrs;
+        for (auto &&attr : Function->getFunctionAttrs()) {
+          // FIXME: handle union style attributes...
+          if (AttrToDescription[attr]->merge(*callSummary))
+            reducedAttrs.emplace(attr);
+        }
+
+        if (reducedAttrs != Function->getFunctionAttrs()) {
+          Function->clearAttributes();
+
+          for (auto &&attr : reducedAttrs)
+            Function->addAttribute(attr);
+
+          changed = true;
+        }
+      }
+    }
+  }
 }
 
 void SemaSummarizer::ActOnStartOfSourceFile() {

>From da80bdbf6e7749fc9e81ec6bfdeb211b999d637d Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 12 Jun 2025 00:31:33 +0200
Subject: [PATCH 09/24] [clang][Summary] make `SummaryManager` own the
 attributes

---
 clang/include/clang/Sema/SemaSummarizer.h   |  22 +--
 clang/include/clang/Sema/SummaryAttribute.h |  38 +++--
 clang/lib/Sema/SemaSummarizer.cpp           | 168 ++++++++------------
 clang/lib/Sema/SummaryAttribute.cpp         |  33 ++--
 4 files changed, 130 insertions(+), 131 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 7d2f8252dc957..42f668009d2f2 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -9,35 +9,37 @@
 namespace clang {
 class FunctionSummary {
   SmallVector<char> ID;
-  std::set<SummaryAttribute> FunctionAttrs;
+  std::set<const SummaryAttribute *> FunctionAttrs;
   std::set<SmallVector<char>> Calls;
 
 public:
-  FunctionSummary(SmallVector<char> ID, std::set<SummaryAttribute> FunctionAttrs, std::set<SmallVector<char>> Calls);
+  FunctionSummary(SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs, std::set<SmallVector<char>> Calls);
   FunctionSummary(const clang::FunctionDecl *FD);
 
   SmallVector<char> getID() const { return ID; }
-  const std::set<SummaryAttribute> &getFunctionAttrs() const { return FunctionAttrs; }
+  const std::set<const SummaryAttribute *> &getFunctionAttrs() const { return FunctionAttrs; }
   const std::set<SmallVector<char>> &getCalls() const { return Calls; }
 
-  void clearAttributes() { FunctionAttrs.clear(); }
-  void addAttribute(SummaryAttribute Attr) { FunctionAttrs.emplace(Attr); }
-  bool hasAttribute(SummaryAttribute Attr) const { return FunctionAttrs.count(Attr); }
+  void replaceAttributes(std::set<const SummaryAttribute *> Attrs) { FunctionAttrs = std::move(Attrs); }
+  void addAttribute(const SummaryAttribute * Attr) { FunctionAttrs.emplace(Attr); }
 
   void addCall(const clang::FunctionDecl *FD);
 };
 
 class SummaryManager {
-  std::map<std::string, const FunctionSummary *> IDToSummary;
+  std::map<SmallVector<char>, const FunctionSummary *> IDToSummary;
   std::vector<std::unique_ptr<FunctionSummary>> FunctionSummaries;
   
-  std::map<SummaryAttribute, const SummaryAttributeDescription *> AttrToDescription;
-  std::vector<std::unique_ptr<SummaryAttributeDescription>> AttributeDescriptions;
+  std::map<SummaryAttributeKind, const SummaryAttribute *> KindToAttribute;
+  std::vector<std::unique_ptr<SummaryAttribute>> Attributes;
 
+  void SaveSummary(std::unique_ptr<FunctionSummary> Summary);
+  bool ReduceFunctionSummary(FunctionSummary &FunctionSummary);
 public:
   SummaryManager();
 
-  FunctionSummary SummarizeFunctionBody(const FunctionDecl *FD);
+  const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
+  void SummarizeFunctionBody(const FunctionDecl *FD);
   
   void SerializeSummary(llvm::json::OStream &, const FunctionSummary &) const;
   void ParseSummaryFromJSON(StringRef path);
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index beb735c00933c..bfe7ecf84e795 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -2,33 +2,47 @@
 #define LLVM_CLANG_SEMA_SEMASUMMARYATTRIBUTE_H
 
 #include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include <string>
 
 namespace clang {
-enum SummaryAttribute {
+enum SummaryAttributeKind {
   NO_WRITE_GLOBAL,
 };
 
 class FunctionSummary;
 
-class SummaryAttributeDescription {
-protected:
-  const SummaryAttribute Attr;
+class SummaryAttribute {
+  const SummaryAttributeKind Kind;
   std::string_view Serialzed;
 
 public:
-  SummaryAttributeDescription(SummaryAttribute Attr, const char *Str) : Attr(Attr), Serialzed(Str) {}
-  virtual ~SummaryAttributeDescription() = default;
+  SummaryAttribute(SummaryAttributeKind Attr, const char *Str) : Kind(Attr), Serialzed(Str) {}
+  virtual ~SummaryAttribute() = default;
+  
+  SummaryAttributeKind getKind() { return Kind; }
 
-  SummaryAttribute getAttribute() { return Attr; };
+  virtual bool infer(const FunctionDecl *FD) const = 0;
+  virtual bool merge(const FunctionSummary &Summary) const = 0;
 
-  virtual bool predicate(const FunctionDecl *FD) = 0;
-  std::optional<SummaryAttribute> infer(const FunctionDecl *FD);
+  virtual std::string serialize() const { return std::string(Serialzed); };
+  virtual bool parse(std::string_view input) const { return input == Serialzed; };
+};
 
-  virtual bool merge(const FunctionSummary &Summary) const = 0;
+class NoWriteGlobalDescription : public SummaryAttribute {
+  class Callback : public ast_matchers::MatchFinder::MatchCallback {
+  public:
+    bool WriteGlobal = false;
 
-  virtual std::string serialize();
-  virtual std::optional<SummaryAttribute> parse(std::string_view input);
+    void run(const ast_matchers::MatchFinder::MatchResult &Result) override final;
+  };
+
+public:
+  NoWriteGlobalDescription()
+  : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
+  
+  bool infer(const FunctionDecl *FD) const override final;
+  bool merge(const FunctionSummary &Summary) const override final;
 };
 } // namespace clang
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 569d272b36bb3..97b2d50bd7602 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -37,80 +37,54 @@ class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
   }
 };
 
-class NoWriteGlobalDescription : public SummaryAttributeDescription {
-  class Callback : public ast_matchers::MatchFinder::MatchCallback {
-  public:
-    bool WriteGlobal = false;
-
-    void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
-      const auto *Assignment =
-          Result.Nodes.getNodeAs<BinaryOperator>("assignment");
-      if (!Assignment)
-        return;
-
-      WriteGlobal = true;
-    };
-  };
-
-public:
-  NoWriteGlobalDescription()
-      : SummaryAttributeDescription(NO_WRITE_GLOBAL, "no_write_global") {}
-
-  bool predicate(const FunctionDecl *FD) override {
-    using namespace ast_matchers;
-    MatchFinder Finder;
-    Callback CB;
-
-    Finder.addMatcher(
-        functionDecl(forEachDescendant(
-            binaryOperator(isAssignmentOperator(),
-                           hasLHS(declRefExpr(to(varDecl(hasGlobalStorage())))))
-                .bind("assignment"))),
-        &CB);
-    Finder.match(*FD, FD->getASTContext());
-    return !CB.WriteGlobal;
-  };
-
-  bool merge(const FunctionSummary &Summary) const override {
-    return Summary.getFunctionAttrs().count(Attr);
-  };
-};
+SmallVector<char> GetUSR(const FunctionDecl *FD) {
+  SmallVector<char> USR;
+  index::generateUSRForDecl(FD, USR);
+  return USR;
+}
 } // namespace
 
 void FunctionSummary::addCall(const clang::FunctionDecl *FD) {
-  SmallVector<char> Call;
-  index::generateUSRForDecl(FD, Call);
-  Calls.emplace(Call);
+  Calls.emplace(GetUSR(FD));
 }
 
-FunctionSummary::FunctionSummary(SmallVector<char> ID, std::set<SummaryAttribute> FunctionAttrs, std::set<SmallVector<char>> Calls) :
+FunctionSummary::FunctionSummary(SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs, std::set<SmallVector<char>> Calls) :
   ID(std::move(ID)), FunctionAttrs(std::move(FunctionAttrs)), Calls(std::move(Calls)) {}
 
-FunctionSummary::FunctionSummary(const clang::FunctionDecl *FD) {
-  index::generateUSRForDecl(FD, ID);
-}
+FunctionSummary::FunctionSummary(const clang::FunctionDecl *FD) : ID(GetUSR(FD)) {}
 
 SummaryManager::SummaryManager() {
-  AttributeDescriptions.emplace_back(std::make_unique<NoWriteGlobalDescription>());
+  Attributes.emplace_back(std::make_unique<NoWriteGlobalDescription>());
+
+  for(auto &&Attr : Attributes) {
+    assert(KindToAttribute.count(Attr->getKind()) == 0 && "Attr already registered");
+    KindToAttribute[Attr->getKind()] = Attr.get();
+  }
+}
 
-  for(auto &&AttrDescr : AttributeDescriptions)
-    AttrToDescription[AttrDescr->getAttribute()] = AttrDescr.get();
+void SummaryManager::SaveSummary(std::unique_ptr<FunctionSummary> Summary) {
+  auto *SummaryPtr = FunctionSummaries.emplace_back(std::move(Summary)).get();
+  IDToSummary[SummaryPtr->getID()] = SummaryPtr;
 }
 
-FunctionSummary SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
+const FunctionSummary *SummaryManager::GetSummary(const FunctionDecl *FD) const { 
+  auto USR = GetUSR(FD);
+  if(!IDToSummary.count(USR))
+    return nullptr;
+
+  return IDToSummary.at(USR);
+}
+
+void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
   auto Summary = std::make_unique<FunctionSummary>(FD);
   CallCollector::CollectCalledFunctions(FD, *Summary);
 
-  for (auto &&AttrDesc : AttributeDescriptions) {
-    if (const auto &Attr = AttrDesc->infer(FD))
-      Summary->addAttribute(*Attr);
+  for (auto &&Attr : Attributes) {
+    if (Attr->infer(FD))
+      Summary->addAttribute(Attr.get());
   }
 
-  // FIXME: This is duplicated and hurts my eyes regardless
-  std::string key(Summary->getID().begin(), Summary->getID().size());
-  auto *SummaryPtr = FunctionSummaries.emplace_back(std::move(Summary)).get();
-  IDToSummary[key] = SummaryPtr;
-  return *SummaryPtr;
+  SaveSummary(std::move(Summary));
 }
 
 void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSummary &Summary) const {
@@ -119,7 +93,7 @@ void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSu
     JOS.attributeObject("attrs", [&]{
       JOS.attributeArray("function", [&]{
         for(auto &&Attr : Summary.getFunctionAttrs()) {
-          JOS.value(llvm::json::Value(AttributeDescriptions[Attr]->serialize()));
+          JOS.value(llvm::json::Value(Attr->serialize()));
         }
       });
     });
@@ -147,12 +121,12 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
     llvm::json::Object *Summary = it->getAsObject();
 
     SmallString<128> ID(*Summary->getString("id"));
-    std::set<SummaryAttribute> FunctionAttrs;
+    std::set<const SummaryAttribute *> FunctionAttrs;
     llvm::json::Array *FunctionAttributes = Summary->getObject("attrs")->getArray("function");
     for(auto attrIt = FunctionAttributes->begin(); attrIt != FunctionAttributes->end(); ++attrIt) {
-      for(auto &&AttrDesc : AttributeDescriptions) {
-        if(auto Attr = AttrDesc->parse(*attrIt->getAsString()))
-          FunctionAttrs.emplace(*Attr);
+      for(auto &&Attr : Attributes) {
+        if(Attr->parse(*attrIt->getAsString()))
+          FunctionAttrs.emplace(Attr.get());
       }
     }
 
@@ -163,51 +137,47 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
       Calls.emplace(SmallString<128>(*Obj->getString("id")));
     }
     
-    std::string key = ID.str().str();
-    auto ParsedSummary = std::make_unique<FunctionSummary>(std::move(ID), std::move(FunctionAttrs), std::move(Calls));
-    auto *ParsedSummaryPtr = FunctionSummaries.emplace_back(std::move(ParsedSummary)).get();
-    IDToSummary[key] = ParsedSummaryPtr;
+    SaveSummary(std::make_unique<FunctionSummary>(std::move(ID), std::move(FunctionAttrs), std::move(Calls)));
   }
 }
 
-void SummaryManager::ReduceSummaries() {
-  bool changed = true;
-  while (changed) {
-    changed = false;
-
-    for (auto &&Function : FunctionSummaries) {
-      for (auto &&call : Function->getCalls()) {
-        // FIXME: This is duplicated and hurts my eyes regardless
-        std::string key(call.begin(), call.size());
-
-        // If we don't have a summary about a called function, we forget
-        // everything about the current one as well.
-        if (!IDToSummary.count(key)) {
-          changed = true;
-          Function->clearAttributes();
-          break;
-        }
+bool SummaryManager::ReduceFunctionSummary(FunctionSummary &Function) {
+  bool changed = false;
 
-        const FunctionSummary *callSummary = IDToSummary.at(key);
+  for (auto &&call : Function.getCalls()) {
+    std::set<const SummaryAttribute *> reducedAttrs;
 
-        std::set<SummaryAttribute> reducedAttrs;
-        for (auto &&attr : Function->getFunctionAttrs()) {
-          // FIXME: handle union style attributes...
-          if (AttrToDescription[attr]->merge(*callSummary))
-            reducedAttrs.emplace(attr);
-        }
+    // If we don't have a summary about a called function, we forget
+    // everything about the current one as well.
+    if (!IDToSummary.count(call)) {
+      Function.replaceAttributes(std::move(reducedAttrs));
+      return true;
+    }
 
-        if (reducedAttrs != Function->getFunctionAttrs()) {
-          Function->clearAttributes();
+    const FunctionSummary *callSummary = IDToSummary[call];
 
-          for (auto &&attr : reducedAttrs)
-            Function->addAttribute(attr);
+    for (auto &&Attr : Function.getFunctionAttrs()) {
+      if (Attr->merge(*callSummary))
+        reducedAttrs.emplace(Attr);
+    }
 
-          changed = true;
-        }
-      }
+    if (reducedAttrs != Function.getFunctionAttrs()) {
+      Function.replaceAttributes(std::move(reducedAttrs));
+      changed = true;
     }
   }
+
+  return changed;
+}
+
+void SummaryManager::ReduceSummaries() {
+  bool changed = true;
+  while (changed) {
+    changed = false;
+
+    for (auto &&Function : FunctionSummaries)
+      changed |= ReduceFunctionSummary(*Function);
+  }
 }
 
 void SemaSummarizer::ActOnStartOfSourceFile() {
@@ -221,10 +191,10 @@ void SemaSummarizer::ActOnEndOfSourceFile() {
 }
 
 void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
-  FunctionSummary Summary = TheSummaryManager->SummarizeFunctionBody(FD);
+  TheSummaryManager->SummarizeFunctionBody(FD);
 
   if(TheSummaryConsumer)
-    TheSummaryConsumer->ProcessFunctionSummary(Summary);
+    TheSummaryConsumer->ProcessFunctionSummary(*TheSummaryManager->GetSummary(FD));
 }
 
 } // namespace clang
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index 4affec7b4ffb7..953cc38b2e7a1 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -1,19 +1,32 @@
 #include "clang/Sema/SummaryAttribute.h"
+#include "clang/Sema/SemaSummarizer.h"
 
 namespace clang {
-std::string SummaryAttributeDescription::serialize() { return std::string(Serialzed); }
+void NoWriteGlobalDescription::Callback::run(const ast_matchers::MatchFinder::MatchResult &Result) {
+  const auto *Assignment =
+      Result.Nodes.getNodeAs<BinaryOperator>("assignment");
+  if (!Assignment)
+    return;
 
-std::optional<SummaryAttribute> SummaryAttributeDescription::parse(std::string_view input) {
-  if(input == Serialzed)
-    return Attr;
-
-  return std::nullopt;
+  WriteGlobal = true;
 }
 
-std::optional<SummaryAttribute> SummaryAttributeDescription::infer(const FunctionDecl *FD) {
-  if (predicate(FD))
-    return Attr;
+bool NoWriteGlobalDescription::infer(const FunctionDecl *FD) const {
+  using namespace ast_matchers;
+  MatchFinder Finder;
+  Callback CB;
+
+  Finder.addMatcher(
+      functionDecl(forEachDescendant(
+          binaryOperator(isAssignmentOperator(),
+                         hasLHS(declRefExpr(to(varDecl(hasGlobalStorage())))))
+              .bind("assignment"))),
+      &CB);
+  Finder.match(*FD, FD->getASTContext());
+  return !CB.WriteGlobal;
+}
 
-  return std::nullopt;
+bool NoWriteGlobalDescription::merge(const FunctionSummary &Summary) const {
+  return Summary.getFunctionAttrs().count(this);
 }
 } // namespace clang
\ No newline at end of file

>From 4964c96b88d0a1a1b6b251a0489b2e82ceb60ebe Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 12 Jun 2025 01:04:13 +0200
Subject: [PATCH 10/24] [clang][Summary] only keep one constructor for
 `FunctionSummary`

---
 clang/include/clang/Sema/SemaSummarizer.h   | 27 ++++---
 clang/include/clang/Sema/SummaryAttribute.h | 16 ++--
 clang/lib/Sema/SemaSummarizer.cpp           | 87 ++++++++++-----------
 clang/lib/Sema/SummaryAttribute.cpp         |  8 +-
 4 files changed, 73 insertions(+), 65 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 42f668009d2f2..7fea668bae694 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -9,38 +9,45 @@
 namespace clang {
 class FunctionSummary {
   SmallVector<char> ID;
-  std::set<const SummaryAttribute *> FunctionAttrs;
+  std::set<const SummaryAttribute *> Attrs;
   std::set<SmallVector<char>> Calls;
 
 public:
-  FunctionSummary(SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs, std::set<SmallVector<char>> Calls);
-  FunctionSummary(const clang::FunctionDecl *FD);
+  FunctionSummary(SmallVector<char> ID,
+                  std::set<const SummaryAttribute *> Attrs,
+                  std::set<SmallVector<char>> Calls);
 
   SmallVector<char> getID() const { return ID; }
-  const std::set<const SummaryAttribute *> &getFunctionAttrs() const { return FunctionAttrs; }
+  const std::set<const SummaryAttribute *> &getAttributes() const {
+    return Attrs;
+  }
   const std::set<SmallVector<char>> &getCalls() const { return Calls; }
 
-  void replaceAttributes(std::set<const SummaryAttribute *> Attrs) { FunctionAttrs = std::move(Attrs); }
-  void addAttribute(const SummaryAttribute * Attr) { FunctionAttrs.emplace(Attr); }
+  void replaceAttributes(std::set<const SummaryAttribute *> Attrs) {
+    this->Attrs = std::move(Attrs);
+  }
 
-  void addCall(const clang::FunctionDecl *FD);
+  friend class SummaryManager;
 };
 
 class SummaryManager {
   std::map<SmallVector<char>, const FunctionSummary *> IDToSummary;
   std::vector<std::unique_ptr<FunctionSummary>> FunctionSummaries;
-  
+
   std::map<SummaryAttributeKind, const SummaryAttribute *> KindToAttribute;
   std::vector<std::unique_ptr<SummaryAttribute>> Attributes;
 
-  void SaveSummary(std::unique_ptr<FunctionSummary> Summary);
+  void CreateSummary(SmallVector<char> ID,
+                     std::set<const SummaryAttribute *> Attrs,
+                     std::set<SmallVector<char>> Calls);
   bool ReduceFunctionSummary(FunctionSummary &FunctionSummary);
+
 public:
   SummaryManager();
 
   const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
   void SummarizeFunctionBody(const FunctionDecl *FD);
-  
+
   void SerializeSummary(llvm::json::OStream &, const FunctionSummary &) const;
   void ParseSummaryFromJSON(StringRef path);
 
diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index bfe7ecf84e795..22067da3a4b81 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -17,16 +17,19 @@ class SummaryAttribute {
   std::string_view Serialzed;
 
 public:
-  SummaryAttribute(SummaryAttributeKind Attr, const char *Str) : Kind(Attr), Serialzed(Str) {}
+  SummaryAttribute(SummaryAttributeKind Attr, const char *Str)
+      : Kind(Attr), Serialzed(Str) {}
   virtual ~SummaryAttribute() = default;
-  
+
   SummaryAttributeKind getKind() { return Kind; }
 
   virtual bool infer(const FunctionDecl *FD) const = 0;
   virtual bool merge(const FunctionSummary &Summary) const = 0;
 
   virtual std::string serialize() const { return std::string(Serialzed); };
-  virtual bool parse(std::string_view input) const { return input == Serialzed; };
+  virtual bool parse(std::string_view input) const {
+    return input == Serialzed;
+  };
 };
 
 class NoWriteGlobalDescription : public SummaryAttribute {
@@ -34,13 +37,14 @@ class NoWriteGlobalDescription : public SummaryAttribute {
   public:
     bool WriteGlobal = false;
 
-    void run(const ast_matchers::MatchFinder::MatchResult &Result) override final;
+    void
+    run(const ast_matchers::MatchFinder::MatchResult &Result) override final;
   };
 
 public:
   NoWriteGlobalDescription()
-  : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
-  
+      : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
+
   bool infer(const FunctionDecl *FD) const override final;
   bool merge(const FunctionSummary &Summary) const override final;
 };
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 97b2d50bd7602..811c95028abb0 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -9,10 +9,14 @@
 
 namespace clang {
 namespace {
-class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
-  FunctionSummary *Summary;
+SmallVector<char> GetUSR(const FunctionDecl *FD) {
+  SmallVector<char> USR;
+  index::generateUSRForDecl(FD, USR);
+  return USR;
+}
 
-  CallCollector(FunctionSummary &Summary) : Summary(&Summary) {}
+class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
+  std::set<SmallVector<char>> Calls;
 
   virtual void
   run(const ast_matchers::MatchFinder::MatchResult &Result) override {
@@ -21,78 +25,70 @@ class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
       return;
 
     const auto *Callee = llvm::dyn_cast<FunctionDecl>(Call->getCalleeDecl());
-    Summary->addCall(Callee);
+    Calls.emplace(GetUSR(Callee));
   }
 
 public:
-  static void CollectCalledFunctions(const FunctionDecl *FD,
-                                     FunctionSummary &Summary) {
+  std::set<SmallVector<char>> collect(const FunctionDecl *FD) {
     using namespace ast_matchers;
     MatchFinder Finder;
-    CallCollector CC(Summary);
 
     Finder.addMatcher(functionDecl(forEachDescendant(callExpr().bind("call"))),
-                      &CC);
+                      this);
     Finder.match(*FD, FD->getASTContext());
+
+    return Calls;
   }
 };
-
-SmallVector<char> GetUSR(const FunctionDecl *FD) {
-  SmallVector<char> USR;
-  index::generateUSRForDecl(FD, USR);
-  return USR;
-}
 } // namespace
 
-void FunctionSummary::addCall(const clang::FunctionDecl *FD) {
-  Calls.emplace(GetUSR(FD));
-}
-
-FunctionSummary::FunctionSummary(SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs, std::set<SmallVector<char>> Calls) :
-  ID(std::move(ID)), FunctionAttrs(std::move(FunctionAttrs)), Calls(std::move(Calls)) {}
-
-FunctionSummary::FunctionSummary(const clang::FunctionDecl *FD) : ID(GetUSR(FD)) {}
+FunctionSummary::FunctionSummary(
+    SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs,
+    std::set<SmallVector<char>> Calls)
+    : ID(std::move(ID)), Attrs(std::move(FunctionAttrs)),
+      Calls(std::move(Calls)) {}
 
 SummaryManager::SummaryManager() {
   Attributes.emplace_back(std::make_unique<NoWriteGlobalDescription>());
 
-  for(auto &&Attr : Attributes) {
-    assert(KindToAttribute.count(Attr->getKind()) == 0 && "Attr already registered");
+  for (auto &&Attr : Attributes) {
+    assert(KindToAttribute.count(Attr->getKind()) == 0 &&
+           "Attr already registered");
     KindToAttribute[Attr->getKind()] = Attr.get();
   }
 }
 
-void SummaryManager::SaveSummary(std::unique_ptr<FunctionSummary> Summary) {
+void SummaryManager::CreateSummary(SmallVector<char> ID,
+                                   std::set<const SummaryAttribute *> Attrs,
+                                   std::set<SmallVector<char>> Calls) {
+  auto Summary = std::make_unique<FunctionSummary>(
+      std::move(ID), std::move(Attrs), std::move(Calls));
   auto *SummaryPtr = FunctionSummaries.emplace_back(std::move(Summary)).get();
   IDToSummary[SummaryPtr->getID()] = SummaryPtr;
 }
 
-const FunctionSummary *SummaryManager::GetSummary(const FunctionDecl *FD) const { 
+const FunctionSummary *
+SummaryManager::GetSummary(const FunctionDecl *FD) const {
   auto USR = GetUSR(FD);
-  if(!IDToSummary.count(USR))
-    return nullptr;
-
-  return IDToSummary.at(USR);
+  return IDToSummary.count(USR) ? IDToSummary.at(USR) : nullptr;
 }
 
 void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
-  auto Summary = std::make_unique<FunctionSummary>(FD);
-  CallCollector::CollectCalledFunctions(FD, *Summary);
-
+  std::set<const SummaryAttribute *> Attrs;
   for (auto &&Attr : Attributes) {
     if (Attr->infer(FD))
-      Summary->addAttribute(Attr.get());
+      Attrs.emplace(Attr.get());
   }
 
-  SaveSummary(std::move(Summary));
+  CreateSummary(GetUSR(FD), std::move(Attrs), CallCollector().collect(FD));
 }
 
 void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSummary &Summary) const {
   JOS.object([&]{
     JOS.attribute("id", llvm::json::Value(Summary.getID()));
-    JOS.attributeObject("attrs", [&]{
-      JOS.attributeArray("function", [&]{
-        for(auto &&Attr : Summary.getFunctionAttrs()) {
+    JOS.attributeObject("attrs", [&] {
+      JOS.attributeArray("function", [&] {
+        for (auto &&Attr : Summary.getAttributes()) {
           JOS.value(llvm::json::Value(Attr->serialize()));
         }
       });
@@ -124,8 +120,8 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
     std::set<const SummaryAttribute *> FunctionAttrs;
     llvm::json::Array *FunctionAttributes = Summary->getObject("attrs")->getArray("function");
     for(auto attrIt = FunctionAttributes->begin(); attrIt != FunctionAttributes->end(); ++attrIt) {
-      for(auto &&Attr : Attributes) {
-        if(Attr->parse(*attrIt->getAsString()))
+      for (auto &&Attr : Attributes) {
+        if (Attr->parse(*attrIt->getAsString()))
           FunctionAttrs.emplace(Attr.get());
       }
     }
@@ -136,8 +132,8 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
       auto *Obj = callIt->getAsObject();
       Calls.emplace(SmallString<128>(*Obj->getString("id")));
     }
-    
-    SaveSummary(std::make_unique<FunctionSummary>(std::move(ID), std::move(FunctionAttrs), std::move(Calls)));
+
+    CreateSummary(std::move(ID), std::move(FunctionAttrs), std::move(Calls));
   }
 }
 
@@ -156,12 +152,12 @@ bool SummaryManager::ReduceFunctionSummary(FunctionSummary &Function) {
 
     const FunctionSummary *callSummary = IDToSummary[call];
 
-    for (auto &&Attr : Function.getFunctionAttrs()) {
+    for (auto &&Attr : Function.getAttributes()) {
       if (Attr->merge(*callSummary))
         reducedAttrs.emplace(Attr);
     }
 
-    if (reducedAttrs != Function.getFunctionAttrs()) {
+    if (reducedAttrs != Function.getAttributes()) {
       Function.replaceAttributes(std::move(reducedAttrs));
       changed = true;
     }
@@ -194,7 +190,8 @@ void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
   TheSummaryManager->SummarizeFunctionBody(FD);
 
   if(TheSummaryConsumer)
-    TheSummaryConsumer->ProcessFunctionSummary(*TheSummaryManager->GetSummary(FD));
+    TheSummaryConsumer->ProcessFunctionSummary(
+        *TheSummaryManager->GetSummary(FD));
 }
 
 } // namespace clang
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index 953cc38b2e7a1..c1611ef90fae5 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -2,9 +2,9 @@
 #include "clang/Sema/SemaSummarizer.h"
 
 namespace clang {
-void NoWriteGlobalDescription::Callback::run(const ast_matchers::MatchFinder::MatchResult &Result) {
-  const auto *Assignment =
-      Result.Nodes.getNodeAs<BinaryOperator>("assignment");
+void NoWriteGlobalDescription::Callback::run(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  const auto *Assignment = Result.Nodes.getNodeAs<BinaryOperator>("assignment");
   if (!Assignment)
     return;
 
@@ -27,6 +27,6 @@ bool NoWriteGlobalDescription::infer(const FunctionDecl *FD) const {
 }
 
 bool NoWriteGlobalDescription::merge(const FunctionSummary &Summary) const {
-  return Summary.getFunctionAttrs().count(this);
+  return Summary.getAttributes().count(this);
 }
 } // namespace clang
\ No newline at end of file

>From d8c26d3d7a6bcf989748bcde25f6ef46c93f43eb Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 12 Jun 2025 01:13:23 +0200
Subject: [PATCH 11/24] [clang][Summary] the summary manager shouldn't be
 reading the JSON file

---
 clang/include/clang/Sema/SemaSummarizer.h |  2 +-
 clang/lib/Frontend/FrontendAction.cpp     | 15 +++++++++++--
 clang/lib/Sema/SemaSummarizer.cpp         | 26 +++++++----------------
 3 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 7fea668bae694..2c58591350b8e 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -49,7 +49,7 @@ class SummaryManager {
   void SummarizeFunctionBody(const FunctionDecl *FD);
 
   void SerializeSummary(llvm::json::OStream &, const FunctionSummary &) const;
-  void ParseSummaryFromJSON(StringRef path);
+  void ParseSummaryFromJSON(const llvm::json::Array &Summary);
 
   void ReduceSummaries();
 
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 7af4a79f83eec..b8732e58e7ffd 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -47,7 +47,9 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Timer.h"
 #include "llvm/Support/raw_ostream.h"
+#include <fstream>
 #include <memory>
+#include <sstream>
 #include <system_error>
 using namespace clang;
 
@@ -981,8 +983,17 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
       for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC),
                                          DirEnd;
            Dir != DirEnd && !EC; Dir.increment(EC)) {
-        if (llvm::sys::path::extension(Dir->path()) == ".json")
-          CI.getSummaryManager().ParseSummaryFromJSON(Dir->path());
+        if (llvm::sys::path::extension(Dir->path()) == ".json") {
+          std::ifstream t(Dir->path().str());
+          std::stringstream buffer;
+          buffer << t.rdbuf();
+
+          auto JSON = llvm::json::parse(buffer.str());
+          if (!JSON)
+            continue;
+
+          CI.getSummaryManager().ParseSummaryFromJSON(*JSON->getAsArray());
+        }
       }
 
       // FIXME: debug only, remove later
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 811c95028abb0..5cd25fd563b90 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -1,11 +1,9 @@
 #include "clang/Sema/SemaSummarizer.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Index/USRGeneration.h"
-#include "clang/Sema/SummaryConsumer.h"
 #include "clang/Sema/SummaryAttribute.h"
+#include "clang/Sema/SummaryConsumer.h"
 #include <set>
-#include <fstream>
-#include <sstream>
 
 namespace clang {
 namespace {
@@ -103,22 +101,14 @@ void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSu
   });
 }
 
-void SummaryManager::ParseSummaryFromJSON(StringRef path) {
-  std::ifstream t(path.str());
-  std::stringstream buffer;
-  buffer << t.rdbuf();
-
-  auto JSON = llvm::json::parse(buffer.str());
-  if (!JSON)
-    return;
-
-  llvm::json::Array *Summaries = JSON->getAsArray();
-  for(auto it = Summaries->begin(); it != Summaries->end(); ++it) {
-    llvm::json::Object *Summary = it->getAsObject();
+void SummaryManager::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
+  for (auto it = Summary.begin(); it != Summary.end(); ++it) {
+    const llvm::json::Object *FunctionSummary = it->getAsObject();
 
-    SmallString<128> ID(*Summary->getString("id"));
+    SmallString<128> ID(*FunctionSummary->getString("id"));
     std::set<const SummaryAttribute *> FunctionAttrs;
-    llvm::json::Array *FunctionAttributes = Summary->getObject("attrs")->getArray("function");
+    const llvm::json::Array *FunctionAttributes =
+        FunctionSummary->getObject("attrs")->getArray("function");
     for(auto attrIt = FunctionAttributes->begin(); attrIt != FunctionAttributes->end(); ++attrIt) {
       for (auto &&Attr : Attributes) {
         if (Attr->parse(*attrIt->getAsString()))
@@ -127,7 +117,7 @@ void SummaryManager::ParseSummaryFromJSON(StringRef path) {
     }
 
     std::set<SmallVector<char>> Calls;
-    llvm::json::Array *CallEntries = Summary->getArray("calls");
+    const llvm::json::Array *CallEntries = FunctionSummary->getArray("calls");
     for(auto callIt = CallEntries->begin(); callIt != CallEntries->end(); ++callIt) {
       auto *Obj = callIt->getAsObject();
       Calls.emplace(SmallString<128>(*Obj->getString("id")));

>From b5465c15d2e8bf8d9667894a775b96cdaa99c768 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 12 Jun 2025 01:17:07 +0200
Subject: [PATCH 12/24] [clang][Summary] the summary manager shouldn't contain
 the JSON summary consumer logic

---
 clang/include/clang/Sema/SemaSummarizer.h |  7 -------
 clang/lib/Frontend/FrontendAction.cpp     | 14 --------------
 clang/lib/Sema/SemaSummarizer.cpp         | 20 --------------------
 clang/lib/Sema/SummaryConsumer.cpp        | 16 +++++++++++++++-
 4 files changed, 15 insertions(+), 42 deletions(-)

diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SemaSummarizer.h
index 2c58591350b8e..7e0c5990e5de1 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SemaSummarizer.h
@@ -48,15 +48,8 @@ class SummaryManager {
   const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
   void SummarizeFunctionBody(const FunctionDecl *FD);
 
-  void SerializeSummary(llvm::json::OStream &, const FunctionSummary &) const;
   void ParseSummaryFromJSON(const llvm::json::Array &Summary);
-
   void ReduceSummaries();
-
-  // FIXME: debug only, remove later
-  const std::vector<std::unique_ptr<FunctionSummary>> &getSummaries() {
-    return FunctionSummaries;
-  }
 };
 
 // FIXME: Is this class needed?
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index b8732e58e7ffd..dfbc3e0d14e91 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -996,21 +996,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
         }
       }
 
-      // FIXME: debug only, remove later
-      for (auto &&S : CI.getSummaryManager().getSummaries()) {
-        llvm::json::OStream Out(llvm::errs());
-        CI.getSummaryManager().SerializeSummary(Out, *S);
-      }
-      llvm::errs() << '\n';
-
       CI.getSummaryManager().ReduceSummaries();
-
-      // FIXME: debug only, remove later
-      for (auto &&S : CI.getSummaryManager().getSummaries()) {
-        llvm::json::OStream Out(llvm::errs());
-        CI.getSummaryManager().SerializeSummary(Out, *S);
-      }
-      llvm::errs() << '\n';
     }
   }
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 5cd25fd563b90..615df8e78a90b 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -81,26 +81,6 @@ void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
   CreateSummary(GetUSR(FD), std::move(Attrs), CallCollector().collect(FD));
 }
 
-void SummaryManager::SerializeSummary(llvm::json::OStream &JOS, const FunctionSummary &Summary) const {
-  JOS.object([&]{
-    JOS.attribute("id", llvm::json::Value(Summary.getID()));
-    JOS.attributeObject("attrs", [&] {
-      JOS.attributeArray("function", [&] {
-        for (auto &&Attr : Summary.getAttributes()) {
-          JOS.value(llvm::json::Value(Attr->serialize()));
-        }
-      });
-    });
-    JOS.attributeArray("calls", [&]{
-      for(auto &&Call : Summary.getCalls()) {
-        JOS.object([&]{
-          JOS.attribute("id", llvm::json::Value(Call));
-        });
-      }
-    });
-  });
-}
-
 void SummaryManager::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
   for (auto it = Summary.begin(); it != Summary.end(); ++it) {
     const llvm::json::Object *FunctionSummary = it->getAsObject();
diff --git a/clang/lib/Sema/SummaryConsumer.cpp b/clang/lib/Sema/SummaryConsumer.cpp
index 37646c1e07a7f..b10bc827f94ab 100644
--- a/clang/lib/Sema/SummaryConsumer.cpp
+++ b/clang/lib/Sema/SummaryConsumer.cpp
@@ -3,6 +3,20 @@
 
 namespace clang {
 void JSONPrintingSummaryConsumer::ProcessFunctionSummary(const FunctionSummary &Summary) {
-  TheSummaryManager->SerializeSummary(JOS, Summary);
+  JOS.object([&] {
+    JOS.attribute("id", llvm::json::Value(Summary.getID()));
+    JOS.attributeObject("attrs", [&] {
+      JOS.attributeArray("function", [&] {
+        for (auto &&Attr : Summary.getAttributes()) {
+          JOS.value(llvm::json::Value(Attr->serialize()));
+        }
+      });
+    });
+    JOS.attributeArray("calls", [&] {
+      for (auto &&Call : Summary.getCalls()) {
+        JOS.object([&] { JOS.attribute("id", llvm::json::Value(Call)); });
+      }
+    });
+  });
 }
 } // namespace clang
\ No newline at end of file

>From 1754b30c1a54261223b827da001ba078815b79ea Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Thu, 12 Jun 2025 01:25:59 +0200
Subject: [PATCH 13/24] [clang][Summary] rename description to attr

---
 clang/include/clang/Sema/SummaryAttribute.h | 5 ++---
 clang/lib/Sema/SemaSummarizer.cpp           | 2 +-
 clang/lib/Sema/SummaryAttribute.cpp         | 6 +++---
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index 22067da3a4b81..759710fb052e6 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -32,7 +32,7 @@ class SummaryAttribute {
   };
 };
 
-class NoWriteGlobalDescription : public SummaryAttribute {
+class NoWriteGlobalAttr : public SummaryAttribute {
   class Callback : public ast_matchers::MatchFinder::MatchCallback {
   public:
     bool WriteGlobal = false;
@@ -42,8 +42,7 @@ class NoWriteGlobalDescription : public SummaryAttribute {
   };
 
 public:
-  NoWriteGlobalDescription()
-      : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
+  NoWriteGlobalAttr() : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
 
   bool infer(const FunctionDecl *FD) const override final;
   bool merge(const FunctionSummary &Summary) const override final;
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 615df8e78a90b..63091b6f7fefc 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -47,7 +47,7 @@ FunctionSummary::FunctionSummary(
       Calls(std::move(Calls)) {}
 
 SummaryManager::SummaryManager() {
-  Attributes.emplace_back(std::make_unique<NoWriteGlobalDescription>());
+  Attributes.emplace_back(std::make_unique<NoWriteGlobalAttr>());
 
   for (auto &&Attr : Attributes) {
     assert(KindToAttribute.count(Attr->getKind()) == 0 &&
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index c1611ef90fae5..48fda7077f4a3 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -2,7 +2,7 @@
 #include "clang/Sema/SemaSummarizer.h"
 
 namespace clang {
-void NoWriteGlobalDescription::Callback::run(
+void NoWriteGlobalAttr::Callback::run(
     const ast_matchers::MatchFinder::MatchResult &Result) {
   const auto *Assignment = Result.Nodes.getNodeAs<BinaryOperator>("assignment");
   if (!Assignment)
@@ -11,7 +11,7 @@ void NoWriteGlobalDescription::Callback::run(
   WriteGlobal = true;
 }
 
-bool NoWriteGlobalDescription::infer(const FunctionDecl *FD) const {
+bool NoWriteGlobalAttr::infer(const FunctionDecl *FD) const {
   using namespace ast_matchers;
   MatchFinder Finder;
   Callback CB;
@@ -26,7 +26,7 @@ bool NoWriteGlobalDescription::infer(const FunctionDecl *FD) const {
   return !CB.WriteGlobal;
 }
 
-bool NoWriteGlobalDescription::merge(const FunctionSummary &Summary) const {
+bool NoWriteGlobalAttr::merge(const FunctionSummary &Summary) const {
   return Summary.getAttributes().count(this);
 }
 } // namespace clang
\ No newline at end of file

>From 7e27c32c9a0c03a0fc995bb3710aac61494219ad Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Fri, 13 Jun 2025 00:21:50 +0200
Subject: [PATCH 14/24] [clang][Summary] more flexible merge logic

---
 clang/include/clang/Sema/SummaryAttribute.h | 6 ++++--
 clang/lib/Sema/SemaSummarizer.cpp           | 7 ++++---
 clang/lib/Sema/SummaryAttribute.cpp         | 6 ++++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index 759710fb052e6..0014a9878bacd 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -24,7 +24,8 @@ class SummaryAttribute {
   SummaryAttributeKind getKind() { return Kind; }
 
   virtual bool infer(const FunctionDecl *FD) const = 0;
-  virtual bool merge(const FunctionSummary &Summary) const = 0;
+  virtual bool merge(const FunctionSummary &Caller,
+                     const FunctionSummary &Callee) const = 0;
 
   virtual std::string serialize() const { return std::string(Serialzed); };
   virtual bool parse(std::string_view input) const {
@@ -45,7 +46,8 @@ class NoWriteGlobalAttr : public SummaryAttribute {
   NoWriteGlobalAttr() : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
 
   bool infer(const FunctionDecl *FD) const override final;
-  bool merge(const FunctionSummary &Summary) const override final;
+  bool merge(const FunctionSummary &Caller,
+             const FunctionSummary &Callee) const override final;
 };
 } // namespace clang
 
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SemaSummarizer.cpp
index 63091b6f7fefc..3b056fc53f8c9 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SemaSummarizer.cpp
@@ -73,6 +73,7 @@ SummaryManager::GetSummary(const FunctionDecl *FD) const {
 
 void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
   std::set<const SummaryAttribute *> Attrs;
+
   for (auto &&Attr : Attributes) {
     if (Attr->infer(FD))
       Attrs.emplace(Attr.get());
@@ -122,9 +123,9 @@ bool SummaryManager::ReduceFunctionSummary(FunctionSummary &Function) {
 
     const FunctionSummary *callSummary = IDToSummary[call];
 
-    for (auto &&Attr : Function.getAttributes()) {
-      if (Attr->merge(*callSummary))
-        reducedAttrs.emplace(Attr);
+    for (auto &&Attr : Attributes) {
+      if (Attr->merge(Function, *callSummary))
+        reducedAttrs.emplace(Attr.get());
     }
 
     if (reducedAttrs != Function.getAttributes()) {
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index 48fda7077f4a3..6f141dde68e52 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -26,7 +26,9 @@ bool NoWriteGlobalAttr::infer(const FunctionDecl *FD) const {
   return !CB.WriteGlobal;
 }
 
-bool NoWriteGlobalAttr::merge(const FunctionSummary &Summary) const {
-  return Summary.getAttributes().count(this);
+bool NoWriteGlobalAttr::merge(const FunctionSummary &Caller,
+                              const FunctionSummary &Callee) const {
+  return Caller.getAttributes().count(this) &&
+         Callee.getAttributes().count(this);
 }
 } // namespace clang
\ No newline at end of file

>From 4f8acb909effe739523a7342e5bf6dbb0ac75353 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 00:02:10 +0200
Subject: [PATCH 15/24] [clang][Summary] refactor summaries in the compiler
 instance and sema

---
 .../include/clang/Frontend/CompilerInstance.h | 41 ++++++++++---------
 clang/include/clang/Sema/Sema.h               |  8 ++--
 clang/include/clang/Sema/SummaryConsumer.h    | 27 ++++++------
 .../{SemaSummarizer.h => SummaryContext.h}    | 27 +++---------
 clang/lib/Frontend/CompilerInstance.cpp       | 19 +++++----
 clang/lib/Frontend/FrontendAction.cpp         | 14 +++----
 clang/lib/Sema/CMakeLists.txt                 |  2 +-
 clang/lib/Sema/Sema.cpp                       | 21 +++++-----
 clang/lib/Sema/SemaDecl.cpp                   |  8 ++--
 clang/lib/Sema/SummaryAttribute.cpp           |  2 +-
 clang/lib/Sema/SummaryConsumer.cpp            |  2 +-
 ...{SemaSummarizer.cpp => SummaryContext.cpp} | 35 ++++------------
 12 files changed, 89 insertions(+), 117 deletions(-)
 rename clang/include/clang/Sema/{SemaSummarizer.h => SummaryContext.h} (67%)
 rename clang/lib/Sema/{SemaSummarizer.cpp => SummaryContext.cpp} (82%)

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index f8f13bbc998d9..b107f15af9563 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -19,6 +19,7 @@
 #include "clang/Lex/DependencyDirectivesScanner.h"
 #include "clang/Lex/HeaderSearchOptions.h"
 #include "clang/Lex/ModuleLoader.h"
+#include "clang/Sema/SummaryContext.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -48,6 +49,7 @@ class ModuleFile;
 }
 
 class CodeCompleteConsumer;
+class SummaryContext;
 class SummaryConsumer;
 class DiagnosticsEngine;
 class DiagnosticConsumer;
@@ -57,7 +59,6 @@ class Module;
 class ModuleCache;
 class Preprocessor;
 class Sema;
-class SummaryManager;
 class SourceManager;
 class TargetInfo;
 enum class DisableValidationForModuleKind;
@@ -125,9 +126,12 @@ class CompilerInstance : public ModuleLoader {
 
   /// The summary consumer.
   std::unique_ptr<SummaryConsumer> TheSummaryConsumer;
-  
-  /// The summary manager object.
-  std::unique_ptr<SummaryManager> TheSummaryManager;
+
+  /// The summary context.
+  std::unique_ptr<SummaryContext> SummaryCtx;
+
+  /// The summary output file.
+  std::unique_ptr<llvm::raw_fd_ostream> SummaryOS;
 
   /// The semantic analysis object.
   std::unique_ptr<Sema> TheSema;
@@ -524,15 +528,6 @@ class CompilerInstance : public ModuleLoader {
   /// setASTContext - Replace the current AST context.
   void setASTContext(ASTContext *Value);
 
-  bool hasSummaryManager() {
-    return TheSummaryManager != nullptr;
-  }
-
-  SummaryManager &getSummaryManager() {
-    assert(TheSummaryManager && "Compiler instance has no summary manager!");
-    return *TheSummaryManager;
-  }
-
   /// Replace the current Sema; the compiler instance takes ownership
   /// of S.
   void setSema(Sema *S);
@@ -624,10 +619,23 @@ class CompilerInstance : public ModuleLoader {
     return *CompletionConsumer;
   }
 
+  /// setCodeCompletionConsumer - Replace the current code completion consumer;
+  /// the compiler instance takes ownership of \p Value.
+  void setCodeCompletionConsumer(CodeCompleteConsumer *Value);
+
   /// @}
   /// @name Summary
   /// @{
 
+  bool hasSummaryContext() { return (bool)SummaryCtx; }
+
+  SummaryContext &getSummaryContext() {
+    assert(SummaryCtx && "Compiler instance has no summary context!");
+    return *SummaryCtx;
+  }
+
+  void createSummaryContext() { SummaryCtx.reset(new SummaryContext()); }
+
   bool hasSummaryConsumer() const { return (bool)TheSummaryConsumer; }
 
   SummaryConsumer &getSummaryConsumer() const {
@@ -636,9 +644,7 @@ class CompilerInstance : public ModuleLoader {
     return *TheSummaryConsumer;
   }
 
-  /// setCodeCompletionConsumer - Replace the current code completion consumer;
-  /// the compiler instance takes ownership of \p Value.
-  void setCodeCompletionConsumer(CodeCompleteConsumer *Value);
+  void createSummaryConsumer();
 
   /// @}
   /// @name Frontend timer
@@ -765,9 +771,6 @@ class CompilerInstance : public ModuleLoader {
       Preprocessor &PP, StringRef Filename, unsigned Line, unsigned Column,
       const CodeCompleteOptions &Opts, raw_ostream &OS);
 
-  void createSummaryConsumer();
-  void createSummaryManager();
-
   /// Create the Sema object to be used for parsing.
   void createSema(TranslationUnitKind TUKind,
                   CodeCompleteConsumer *CompletionConsumer,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b36fce47fb792..1a4d2e99685e0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -125,7 +125,7 @@ class CXXBasePath;
 class CXXBasePaths;
 class CXXFieldCollector;
 class CodeCompleteConsumer;
-class SummaryManager;
+class SummaryContext;
 class SummaryConsumer;
 enum class ComparisonCategoryType : unsigned char;
 class ConstraintSatisfaction;
@@ -161,7 +161,6 @@ class SemaARM;
 class SemaAVR;
 class SemaBPF;
 class SemaCodeCompletion;
-class SemaSummarizer;
 class SemaCUDA;
 class SemaDirectX;
 class SemaHLSL;
@@ -887,7 +886,7 @@ class Sema final : public SemaBase {
   Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
        TranslationUnitKind TUKind = TU_Complete,
        CodeCompleteConsumer *CompletionConsumer = nullptr,
-       SummaryManager *SummaryManager = nullptr,
+       SummaryContext *SummaryCtx = nullptr,
        SummaryConsumer *SummaryConsumer = nullptr);
   ~Sema();
 
@@ -1265,6 +1264,8 @@ class Sema final : public SemaBase {
   DiagnosticsEngine &Diags;
   SourceManager &SourceMgr;
   api_notes::APINotesManager APINotes;
+  SummaryContext *SummaryCtx;
+  SummaryConsumer *SummaryCnsmr;
 
   /// A RAII object to enter scope of a compound statement.
   class CompoundScopeRAII {
@@ -1569,7 +1570,6 @@ class Sema final : public SemaBase {
   std::unique_ptr<SemaAVR> AVRPtr;
   std::unique_ptr<SemaBPF> BPFPtr;
   std::unique_ptr<SemaCodeCompletion> CodeCompletionPtr;
-  std::unique_ptr<SemaSummarizer> SummarizerPtr;
   std::unique_ptr<SemaCUDA> CUDAPtr;
   std::unique_ptr<SemaDirectX> DirectXPtr;
   std::unique_ptr<SemaHLSL> HLSLPtr;
diff --git a/clang/include/clang/Sema/SummaryConsumer.h b/clang/include/clang/Sema/SummaryConsumer.h
index 85c48b3c1b939..f8844ffb64e21 100644
--- a/clang/include/clang/Sema/SummaryConsumer.h
+++ b/clang/include/clang/Sema/SummaryConsumer.h
@@ -5,36 +5,37 @@
 #include "llvm/Support/JSON.h"
 namespace clang {
 class FunctionSummary;
-class SummaryManager;
+class SummaryContext;
 
 class SummaryConsumer {
 protected:
-    const SummaryManager *TheSummaryManager;
+  const SummaryContext *SummaryCtx;
 
 public:
-    SummaryConsumer(const SummaryManager &SummaryManager) : TheSummaryManager(&SummaryManager) {}
-    virtual ~SummaryConsumer() = default;
+  SummaryConsumer(const SummaryContext &SummaryCtx) : SummaryCtx(&SummaryCtx) {}
+  virtual ~SummaryConsumer() = default;
 
-    virtual void ProcessStartOfSourceFile() {};
-    virtual void ProcessFunctionSummary(const FunctionSummary&) {};
-    virtual void ProcessEndOfSourceFile() {};
+  virtual void ProcessStartOfSourceFile(){};
+  virtual void ProcessFunctionSummary(const FunctionSummary &){};
+  virtual void ProcessEndOfSourceFile(){};
 };
 
 class PrintingSummaryConsumer : public SummaryConsumer {
 public:
-    PrintingSummaryConsumer(const SummaryManager &SummaryManager, raw_ostream &OS)
-      : SummaryConsumer(SummaryManager) {}
+  PrintingSummaryConsumer(const SummaryContext &SummaryCtx, raw_ostream &OS)
+      : SummaryConsumer(SummaryCtx) {}
 };
 
 class JSONPrintingSummaryConsumer : public PrintingSummaryConsumer {
     llvm::json::OStream JOS;
 
 public:
-    JSONPrintingSummaryConsumer(const SummaryManager &SummaryManager, raw_ostream &OS) : PrintingSummaryConsumer(SummaryManager, OS), JOS(OS, 2) {}
+  JSONPrintingSummaryConsumer(const SummaryContext &SummaryCtx, raw_ostream &OS)
+      : PrintingSummaryConsumer(SummaryCtx, OS), JOS(OS, 2) {}
 
-    void ProcessStartOfSourceFile() override { JOS.arrayBegin(); };
-    void ProcessFunctionSummary(const FunctionSummary&) override;
-    void ProcessEndOfSourceFile() override { JOS.arrayEnd(); };
+  void ProcessStartOfSourceFile() override { JOS.arrayBegin(); };
+  void ProcessFunctionSummary(const FunctionSummary &) override;
+  void ProcessEndOfSourceFile() override { JOS.arrayEnd(); };
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Sema/SemaSummarizer.h b/clang/include/clang/Sema/SummaryContext.h
similarity index 67%
rename from clang/include/clang/Sema/SemaSummarizer.h
rename to clang/include/clang/Sema/SummaryContext.h
index 7e0c5990e5de1..67b1162d61763 100644
--- a/clang/include/clang/Sema/SemaSummarizer.h
+++ b/clang/include/clang/Sema/SummaryContext.h
@@ -1,7 +1,6 @@
-#ifndef LLVM_CLANG_SEMA_SEMASUMMARIZER_H
-#define LLVM_CLANG_SEMA_SEMASUMMARIZER_H
+#ifndef LLVM_CLANG_SEMA_SEMASUMMARYCONTEXT_H
+#define LLVM_CLANG_SEMA_SEMASUMMARYCONTEXT_H
 
-#include "clang/Sema/SemaBase.h"
 #include "clang/Sema/SummaryAttribute.h"
 #include "clang/Sema/SummaryConsumer.h"
 #include <set>
@@ -26,11 +25,9 @@ class FunctionSummary {
   void replaceAttributes(std::set<const SummaryAttribute *> Attrs) {
     this->Attrs = std::move(Attrs);
   }
-
-  friend class SummaryManager;
 };
 
-class SummaryManager {
+class SummaryContext {
   std::map<SmallVector<char>, const FunctionSummary *> IDToSummary;
   std::vector<std::unique_ptr<FunctionSummary>> FunctionSummaries;
 
@@ -43,7 +40,7 @@ class SummaryManager {
   bool ReduceFunctionSummary(FunctionSummary &FunctionSummary);
 
 public:
-  SummaryManager();
+  SummaryContext();
 
   const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
   void SummarizeFunctionBody(const FunctionDecl *FD);
@@ -51,20 +48,6 @@ class SummaryManager {
   void ParseSummaryFromJSON(const llvm::json::Array &Summary);
   void ReduceSummaries();
 };
-
-// FIXME: Is this class needed?
-class SemaSummarizer : public SemaBase {
-public:
-  SummaryManager *TheSummaryManager;
-  SummaryConsumer *TheSummaryConsumer;
-
-  SemaSummarizer(Sema &S, SummaryManager &SummaryManager, SummaryConsumer *SummaryConsumer) 
-    : SemaBase(S), TheSummaryManager(&SummaryManager), TheSummaryConsumer(SummaryConsumer) {};
-
-  void ActOnStartOfSourceFile();
-  void ActOnEndOfSourceFile();
-  void SummarizeFunctionBody(const FunctionDecl *FD);
-};
 } // namespace clang
 
-#endif // LLVM_CLANG_SEMA_SEMASUMMARIZE_H
+#endif // LLVM_CLANG_SEMA_SEMASUMMARYCONTEXTH
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index deed94e1f0b42..448d45b0f49db 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -37,7 +37,6 @@
 #include "clang/Sema/CodeCompleteConsumer.h"
 #include "clang/Sema/ParsedAttr.h"
 #include "clang/Sema/Sema.h"
-#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SummaryConsumer.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
@@ -749,22 +748,24 @@ void CompilerInstance::createSummaryConsumer() {
     return;
 
   std::error_code EC;
-  // FIXME: this being static is a design error
-  static llvm::raw_fd_ostream SummaryOS(SummaryFile, EC, llvm::sys::fs::CD_CreateAlways);
+  SummaryOS.reset(new llvm::raw_fd_ostream(SummaryFile, EC,
+                                           llvm::sys::fs::CD_CreateAlways));
 
-  if(!EC)
-    TheSummaryConsumer.reset(new JSONPrintingSummaryConsumer(getSummaryManager(), SummaryOS));
-}
+  if (EC) {
+    SummaryOS = nullptr;
+    return;
+  }
 
-void CompilerInstance::createSummaryManager() {
-  TheSummaryManager.reset(new SummaryManager());
+  TheSummaryConsumer.reset(
+      new JSONPrintingSummaryConsumer(getSummaryContext(), *SummaryOS));
 }
 
 void CompilerInstance::createSema(TranslationUnitKind TUKind,
                                   CodeCompleteConsumer *CompletionConsumer,
                                   SummaryConsumer *SummaryConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
-                         TUKind, CompletionConsumer, hasSummaryManager() ? &getSummaryManager() : nullptr, SummaryConsumer));
+                         TUKind, CompletionConsumer, &getSummaryContext(),
+                         SummaryConsumer));
 
   // Set up API notes.
   TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index dfbc3e0d14e91..2821327c6a824 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -35,7 +35,7 @@
 #include "clang/Parse/ParseAST.h"
 #include "clang/Sema/HLSLExternalSemaSource.h"
 #include "clang/Sema/MultiplexExternalSemaSource.h"
-#include "clang/Sema/SemaSummarizer.h"
+#include "clang/Sema/SummaryContext.h"
 #include "clang/Serialization/ASTDeserializationListener.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/Serialization/GlobalModuleIndex.h"
@@ -895,8 +895,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     }
   }
 
-  if(!CI.hasSummaryManager()) {
-    CI.createSummaryManager();
+  if (!CI.hasSummaryContext()) {
+    CI.createSummaryContext();
   }
 
   // Set up embedding for any specified files. Do this before we load any
@@ -992,11 +992,11 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
           if (!JSON)
             continue;
 
-          CI.getSummaryManager().ParseSummaryFromJSON(*JSON->getAsArray());
+          CI.getSummaryContext().ParseSummaryFromJSON(*JSON->getAsArray());
         }
       }
 
-      CI.getSummaryManager().ReduceSummaries();
+      CI.getSummaryContext().ReduceSummaries();
     }
   }
 
@@ -1371,8 +1371,8 @@ void ASTFrontendAction::ExecuteAction() {
   if (CI.hasCodeCompletionConsumer())
     CompletionConsumer = &CI.getCodeCompletionConsumer();
 
-  if(!CI.hasSummaryManager()) {
-    CI.createSummaryManager();
+  if (!CI.hasSummaryContext()) {
+    CI.createSummaryContext();
   }
   CI.createSummaryConsumer();
 
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index bf8f73dc985db..9d5a593813bd3 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -85,7 +85,6 @@ add_clang_library(clangSema
   SemaStmt.cpp
   SemaStmtAsm.cpp
   SemaStmtAttr.cpp
-  SemaSummarizer.cpp
   SemaSPIRV.cpp
   SemaSYCL.cpp
   SemaSwift.cpp
@@ -101,6 +100,7 @@ add_clang_library(clangSema
   SemaX86.cpp
   SummaryAttribute.cpp
   SummaryConsumer.cpp
+  SummaryContext.cpp
   TypeLocBuilder.cpp
 
   DEPENDS
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 5b3f1a4dca36e..2870871876701 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -64,11 +64,11 @@
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/Sema/SemaSPIRV.h"
 #include "clang/Sema/SemaSYCL.h"
-#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaSystemZ.h"
 #include "clang/Sema/SemaWasm.h"
 #include "clang/Sema/SemaX86.h"
+#include "clang/Sema/SummaryContext.h"
 #include "clang/Sema/TemplateDeduction.h"
 #include "clang/Sema/TemplateInstCallback.h"
 #include "clang/Sema/TypoCorrection.h"
@@ -250,12 +250,12 @@ const uint64_t Sema::MaximumAlignment;
 
 Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
            TranslationUnitKind TUKind, CodeCompleteConsumer *CodeCompleter,
-           SummaryManager *SummaryManager,
-           SummaryConsumer *SummaryConsumer)
+           SummaryContext *SummaryCtx, SummaryConsumer *SummaryConsumer)
     : SemaBase(*this), CollectStats(false), TUKind(TUKind),
       CurFPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
       Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
       SourceMgr(PP.getSourceManager()), APINotes(SourceMgr, LangOpts),
+      SummaryCtx(SummaryCtx), SummaryCnsmr(SummaryConsumer),
       AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr),
       LateTemplateParser(nullptr), LateTemplateParserCleanup(nullptr),
       OpaqueParser(nullptr), CurContext(nullptr), ExternalSource(nullptr),
@@ -266,8 +266,6 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       BPFPtr(std::make_unique<SemaBPF>(*this)),
       CodeCompletionPtr(
           std::make_unique<SemaCodeCompletion>(*this, CodeCompleter)),
-      SummarizerPtr(SummaryManager ? std::make_unique<SemaSummarizer>(*this, *SummaryManager, SummaryConsumer)
-                                    : nullptr),
       CUDAPtr(std::make_unique<SemaCUDA>(*this)),
       DirectXPtr(std::make_unique<SemaDirectX>(*this)),
       HLSLPtr(std::make_unique<SemaHLSL>(*this)),
@@ -309,6 +307,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
       AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
       InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
       ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) {
+  assert((!SummaryConsumer || SummaryCtx) &&
+         "summary consumer without a summary context");
   assert(pp.TUKind == TUKind);
   TUScope = nullptr;
 
@@ -1147,9 +1147,9 @@ void Sema::ActOnStartOfTranslationUnit() {
   if (getLangOpts().CPlusPlusModules &&
       getLangOpts().getCompilingModule() == LangOptions::CMK_HeaderUnit)
     HandleStartOfHeaderUnit();
-  
-  if(SummarizerPtr)
-    SummarizerPtr->ActOnStartOfSourceFile();
+
+  if (SummaryCnsmr)
+    SummaryCnsmr->ProcessStartOfSourceFile();
 }
 
 void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
@@ -1225,8 +1225,9 @@ void Sema::ActOnEndOfTranslationUnit() {
   assert(DelayedDiagnostics.getCurrentPool() == nullptr
          && "reached end of translation unit with a pool attached?");
 
-  if(SummarizerPtr)
-    SummarizerPtr->ActOnEndOfSourceFile();
+  if (SummaryCnsmr)
+    SummaryCnsmr->ProcessEndOfSourceFile();
+
   // If code completion is enabled, don't perform any end-of-translation-unit
   // work.
   if (PP.isCodeCompletionEnabled())
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 45f1523868f75..1aa7a1cf178bd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -55,9 +55,9 @@
 #include "clang/Sema/SemaPPC.h"
 #include "clang/Sema/SemaRISCV.h"
 #include "clang/Sema/SemaSYCL.h"
-#include "clang/Sema/SemaSummarizer.h"
 #include "clang/Sema/SemaSwift.h"
 #include "clang/Sema/SemaWasm.h"
+#include "clang/Sema/SummaryContext.h"
 #include "clang/Sema/Template.h"
 #include "llvm/ADT/STLForwardCompat.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -16695,8 +16695,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
   if (FD && !FD->isDeleted())
     checkTypeSupport(FD->getType(), FD->getLocation(), FD);
 
-  if (FD && SummarizerPtr && SummarizerPtr->TheSummaryConsumer)
-    SummarizerPtr->SummarizeFunctionBody(FD);
+  if (SummaryCnsmr) {
+    SummaryCtx->SummarizeFunctionBody(FD);
+    SummaryCnsmr->ProcessFunctionSummary(*SummaryCtx->GetSummary(FD));
+  }
 
   return dcl;
 }
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index 6f141dde68e52..86d422c16fa30 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -1,5 +1,5 @@
 #include "clang/Sema/SummaryAttribute.h"
-#include "clang/Sema/SemaSummarizer.h"
+#include "clang/Sema/SummaryContext.h"
 
 namespace clang {
 void NoWriteGlobalAttr::Callback::run(
diff --git a/clang/lib/Sema/SummaryConsumer.cpp b/clang/lib/Sema/SummaryConsumer.cpp
index b10bc827f94ab..043873f236b93 100644
--- a/clang/lib/Sema/SummaryConsumer.cpp
+++ b/clang/lib/Sema/SummaryConsumer.cpp
@@ -1,5 +1,5 @@
 #include "clang/Sema/SummaryConsumer.h"
-#include "clang/Sema/SemaSummarizer.h"
+#include "clang/Sema/SummaryContext.h"
 
 namespace clang {
 void JSONPrintingSummaryConsumer::ProcessFunctionSummary(const FunctionSummary &Summary) {
diff --git a/clang/lib/Sema/SemaSummarizer.cpp b/clang/lib/Sema/SummaryContext.cpp
similarity index 82%
rename from clang/lib/Sema/SemaSummarizer.cpp
rename to clang/lib/Sema/SummaryContext.cpp
index 3b056fc53f8c9..6b7207eedb5e5 100644
--- a/clang/lib/Sema/SemaSummarizer.cpp
+++ b/clang/lib/Sema/SummaryContext.cpp
@@ -1,4 +1,4 @@
-#include "clang/Sema/SemaSummarizer.h"
+#include "clang/Sema/SummaryContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Index/USRGeneration.h"
 #include "clang/Sema/SummaryAttribute.h"
@@ -46,7 +46,7 @@ FunctionSummary::FunctionSummary(
     : ID(std::move(ID)), Attrs(std::move(FunctionAttrs)),
       Calls(std::move(Calls)) {}
 
-SummaryManager::SummaryManager() {
+SummaryContext::SummaryContext() {
   Attributes.emplace_back(std::make_unique<NoWriteGlobalAttr>());
 
   for (auto &&Attr : Attributes) {
@@ -56,7 +56,7 @@ SummaryManager::SummaryManager() {
   }
 }
 
-void SummaryManager::CreateSummary(SmallVector<char> ID,
+void SummaryContext::CreateSummary(SmallVector<char> ID,
                                    std::set<const SummaryAttribute *> Attrs,
                                    std::set<SmallVector<char>> Calls) {
   auto Summary = std::make_unique<FunctionSummary>(
@@ -66,12 +66,12 @@ void SummaryManager::CreateSummary(SmallVector<char> ID,
 }
 
 const FunctionSummary *
-SummaryManager::GetSummary(const FunctionDecl *FD) const {
+SummaryContext::GetSummary(const FunctionDecl *FD) const {
   auto USR = GetUSR(FD);
   return IDToSummary.count(USR) ? IDToSummary.at(USR) : nullptr;
 }
 
-void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
+void SummaryContext::SummarizeFunctionBody(const FunctionDecl *FD) {
   std::set<const SummaryAttribute *> Attrs;
 
   for (auto &&Attr : Attributes) {
@@ -82,7 +82,7 @@ void SummaryManager::SummarizeFunctionBody(const FunctionDecl *FD) {
   CreateSummary(GetUSR(FD), std::move(Attrs), CallCollector().collect(FD));
 }
 
-void SummaryManager::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
+void SummaryContext::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
   for (auto it = Summary.begin(); it != Summary.end(); ++it) {
     const llvm::json::Object *FunctionSummary = it->getAsObject();
 
@@ -108,7 +108,7 @@ void SummaryManager::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
   }
 }
 
-bool SummaryManager::ReduceFunctionSummary(FunctionSummary &Function) {
+bool SummaryContext::ReduceFunctionSummary(FunctionSummary &Function) {
   bool changed = false;
 
   for (auto &&call : Function.getCalls()) {
@@ -137,7 +137,7 @@ bool SummaryManager::ReduceFunctionSummary(FunctionSummary &Function) {
   return changed;
 }
 
-void SummaryManager::ReduceSummaries() {
+void SummaryContext::ReduceSummaries() {
   bool changed = true;
   while (changed) {
     changed = false;
@@ -146,23 +146,4 @@ void SummaryManager::ReduceSummaries() {
       changed |= ReduceFunctionSummary(*Function);
   }
 }
-
-void SemaSummarizer::ActOnStartOfSourceFile() {
-  if(TheSummaryConsumer)
-    TheSummaryConsumer->ProcessStartOfSourceFile();
-}
-
-void SemaSummarizer::ActOnEndOfSourceFile() {
-  if(TheSummaryConsumer)
-    TheSummaryConsumer->ProcessEndOfSourceFile();
-}
-
-void SemaSummarizer::SummarizeFunctionBody(const FunctionDecl *FD) {
-  TheSummaryManager->SummarizeFunctionBody(FD);
-
-  if(TheSummaryConsumer)
-    TheSummaryConsumer->ProcessFunctionSummary(
-        *TheSummaryManager->GetSummary(FD));
-}
-
 } // namespace clang

>From d635e3221b8218337b0f80d54eae390d417bba5d Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 00:17:47 +0200
Subject: [PATCH 16/24] [clang][Summary] give the summary consumer a default
 value to keep the diff smaller

---
 clang/include/clang/Frontend/CompilerInstance.h | 2 +-
 clang/lib/Frontend/ChainedIncludesSource.cpp    | 2 +-
 clang/lib/Frontend/FrontendActions.cpp          | 2 +-
 clang/lib/Testing/TestAST.cpp                   | 2 +-
 clang/unittests/CodeGen/TestCompiler.h          | 2 +-
 clang/unittests/Frontend/CodeGenActionTest.cpp  | 2 +-
 clang/unittests/Sema/ExternalSemaSourceTest.cpp | 2 +-
 clang/unittests/Sema/SemaLookupTest.cpp         | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index b107f15af9563..7cb82e587cdfe 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -774,7 +774,7 @@ class CompilerInstance : public ModuleLoader {
   /// Create the Sema object to be used for parsing.
   void createSema(TranslationUnitKind TUKind,
                   CodeCompleteConsumer *CompletionConsumer,
-                  SummaryConsumer *SummaryConsumer);
+                  SummaryConsumer *SummaryConsumer = nullptr);
 
   /// Create the frontend timer and replace any existing one with it.
   void createFrontendTimer();
diff --git a/clang/lib/Frontend/ChainedIncludesSource.cpp b/clang/lib/Frontend/ChainedIncludesSource.cpp
index 437f5387375f7..95b0ed248d545 100644
--- a/clang/lib/Frontend/ChainedIncludesSource.cpp
+++ b/clang/lib/Frontend/ChainedIncludesSource.cpp
@@ -142,7 +142,7 @@ IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource(
     Clang->getASTContext().setASTMutationListener(
                                             consumer->GetASTMutationListener());
     Clang->setASTConsumer(std::move(consumer));
-    Clang->createSema(TU_Prefix, nullptr, nullptr);
+    Clang->createSema(TU_Prefix, nullptr);
 
     if (firstInclude) {
       Preprocessor &PP = Clang->getPreprocessor();
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index 49f1420c75047..8c75e1a46da54 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -52,7 +52,7 @@ void EnsureSemaIsCreated(CompilerInstance &CI, FrontendAction &Action) {
 
   if (!CI.hasSema())
     CI.createSema(Action.getTranslationUnitKind(),
-                  GetCodeCompletionConsumer(CI), nullptr);
+                  GetCodeCompletionConsumer(CI));
 }
 } // namespace
 
diff --git a/clang/lib/Testing/TestAST.cpp b/clang/lib/Testing/TestAST.cpp
index db0490689da53..748f59b856e83 100644
--- a/clang/lib/Testing/TestAST.cpp
+++ b/clang/lib/Testing/TestAST.cpp
@@ -69,7 +69,7 @@ void createMissingComponents(CompilerInstance &Clang) {
   if (!Clang.hasASTContext())
     Clang.createASTContext();
   if (!Clang.hasSema())
-    Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr, nullptr);
+    Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr);
 }
 
 } // namespace
diff --git a/clang/unittests/CodeGen/TestCompiler.h b/clang/unittests/CodeGen/TestCompiler.h
index 760fa340c3d74..a6fec7fb0945d 100644
--- a/clang/unittests/CodeGen/TestCompiler.h
+++ b/clang/unittests/CodeGen/TestCompiler.h
@@ -69,7 +69,7 @@ struct TestCompiler {
 
     compiler.setASTConsumer(std::move(Consumer));
 
-    compiler.createSema(clang::TU_Prefix, nullptr, nullptr);
+    compiler.createSema(clang::TU_Prefix, nullptr);
 
     clang::SourceManager &sm = compiler.getSourceManager();
     sm.setMainFileID(sm.createFileID(
diff --git a/clang/unittests/Frontend/CodeGenActionTest.cpp b/clang/unittests/Frontend/CodeGenActionTest.cpp
index e958ea1993a4a..90818b72cd6e6 100644
--- a/clang/unittests/Frontend/CodeGenActionTest.cpp
+++ b/clang/unittests/Frontend/CodeGenActionTest.cpp
@@ -37,7 +37,7 @@ class NullCodeGenAction : public CodeGenAction {
     if (!CI.hasPreprocessor())
       return;
     if (!CI.hasSema())
-      CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
+      CI.createSema(getTranslationUnitKind(), nullptr);
   }
 };
 
diff --git a/clang/unittests/Sema/ExternalSemaSourceTest.cpp b/clang/unittests/Sema/ExternalSemaSourceTest.cpp
index d223a7135ee84..2b271d4bf7825 100644
--- a/clang/unittests/Sema/ExternalSemaSourceTest.cpp
+++ b/clang/unittests/Sema/ExternalSemaSourceTest.cpp
@@ -194,7 +194,7 @@ class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
   void ExecuteAction() override {
     CompilerInstance &CI = getCompilerInstance();
     ASSERT_FALSE(CI.hasSema());
-    CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
+    CI.createSema(getTranslationUnitKind(), nullptr);
     ASSERT_TRUE(CI.hasDiagnostics());
     DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
     DiagnosticConsumer *Client = Diagnostics.getClient();
diff --git a/clang/unittests/Sema/SemaLookupTest.cpp b/clang/unittests/Sema/SemaLookupTest.cpp
index 96c27945421f9..d97b571f6a37c 100644
--- a/clang/unittests/Sema/SemaLookupTest.cpp
+++ b/clang/unittests/Sema/SemaLookupTest.cpp
@@ -22,7 +22,7 @@ class LookupAction : public ASTFrontendAction {
   void ExecuteAction() override {
     CompilerInstance &CI = getCompilerInstance();
     ASSERT_FALSE(CI.hasSema());
-    CI.createSema(getTranslationUnitKind(), nullptr, nullptr);
+    CI.createSema(getTranslationUnitKind(), nullptr);
     ASSERT_TRUE(CI.hasSema());
     Sema &S = CI.getSema();
     ParseAST(S);

>From 757c0d6d3250203cdb70dc559303f4d8007c4ed5 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 02:01:47 +0200
Subject: [PATCH 17/24] [clang][Summary] change frontend action and summary
 interaction

---
 clang/lib/Frontend/FrontendAction.cpp | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 2821327c6a824..8e66d6b8a6c27 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -895,10 +895,6 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     }
   }
 
-  if (!CI.hasSummaryContext()) {
-    CI.createSummaryContext();
-  }
-
   // Set up embedding for any specified files. Do this before we load any
   // source files, including the primary module map for the compilation.
   for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) {
@@ -969,7 +965,12 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
     }
   }
 
-  // FIXME: lookup dirs recursively
+  bool ProcessesSummaries = !CI.getFrontendOpts().SummaryDirPath.empty() ||
+                            !CI.getFrontendOpts().SummaryFile.empty();
+  if (ProcessesSummaries && !CI.hasSummaryContext())
+    CI.createSummaryContext();
+
+  // FIXME: cleanup and lookup dirs recursively
   if (!CI.getFrontendOpts().SummaryDirPath.empty()) {
     FileManager &FileMgr = CI.getFileManager();
 
@@ -1371,12 +1372,10 @@ void ASTFrontendAction::ExecuteAction() {
   if (CI.hasCodeCompletionConsumer())
     CompletionConsumer = &CI.getCodeCompletionConsumer();
 
-  if (!CI.hasSummaryContext()) {
-    CI.createSummaryContext();
-  }
-  CI.createSummaryConsumer();
+  if (!CI.getFrontendOpts().SummaryFile.empty())
+    CI.createSummaryConsumer();
 
-  // Use a code completion consumer?
+  // Use a code summary consumer?
   SummaryConsumer *SummaryConsumer = nullptr;
   if (CI.hasSummaryConsumer())
     SummaryConsumer = &CI.getSummaryConsumer();

>From f511c22187178d637b184d15fb2effaf1c08dd19 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 02:21:29 +0200
Subject: [PATCH 18/24] [clang][Summary] explicitly flush summary

---
 clang/include/clang/Sema/SummaryConsumer.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Sema/SummaryConsumer.h b/clang/include/clang/Sema/SummaryConsumer.h
index f8844ffb64e21..3d308b8464ef8 100644
--- a/clang/include/clang/Sema/SummaryConsumer.h
+++ b/clang/include/clang/Sema/SummaryConsumer.h
@@ -35,7 +35,10 @@ class JSONPrintingSummaryConsumer : public PrintingSummaryConsumer {
 
   void ProcessStartOfSourceFile() override { JOS.arrayBegin(); };
   void ProcessFunctionSummary(const FunctionSummary &) override;
-  void ProcessEndOfSourceFile() override { JOS.arrayEnd(); };
+  void ProcessEndOfSourceFile() override {
+    JOS.arrayEnd();
+    JOS.flush();
+  };
 };
 } // namespace clang
 

>From 6964f2c54db47c9ab748af19a55f4af761ed786b Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 03:00:33 +0200
Subject: [PATCH 19/24] [clang][analyzer][Summary] pass summaries to the
 analyzer

---
 .../StaticAnalyzer/Core/PathSensitive/ExprEngine.h    | 11 +++++++----
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp          |  5 +++--
 .../lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp  |  7 ++++++-
 3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index b8a4dcbc727a6..5e3b338be6e68 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -21,18 +21,19 @@
 #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h"
 #include "clang/Analysis/ProgramPoint.h"
 #include "clang/Basic/LLVM.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/Sema/SummaryContext.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
 #include "llvm/ADT/ArrayRef.h"
 #include <cassert>
@@ -178,10 +179,12 @@ class ExprEngine {
   /// The flag, which specifies the mode of inlining for the engine.
   InliningModes HowToInline;
 
+  const SummaryContext *SummaryCtx;
+
 public:
   ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, AnalysisManager &mgr,
-             SetOfConstDecls *VisitedCalleesIn,
-             FunctionSummariesTy *FS, InliningModes HowToInlineIn);
+             SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS,
+             InliningModes HowToInlineIn, const SummaryContext *SummaryCtx);
 
   virtual ~ExprEngine() = default;
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1afd4b52eb354..c4836fb9d0aac 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -223,7 +223,8 @@ static const char* TagProviderName = "ExprEngine";
 
 ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
                        AnalysisManager &mgr, SetOfConstDecls *VisitedCalleesIn,
-                       FunctionSummariesTy *FS, InliningModes HowToInlineIn)
+                       FunctionSummariesTy *FS, InliningModes HowToInlineIn,
+                       const SummaryContext *SummaryCtx)
     : CTU(CTU), IsCTUEnabled(mgr.getAnalyzerOptions().IsNaiveCTUEnabled),
       AMgr(mgr), AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
       Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()),
@@ -232,7 +233,7 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
       SymMgr(StateMgr.getSymbolManager()), MRMgr(StateMgr.getRegionManager()),
       svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
       BR(mgr, *this), VisitedCallees(VisitedCalleesIn),
-      HowToInline(HowToInlineIn) {
+      HowToInline(HowToInlineIn), SummaryCtx(SummaryCtx) {
   unsigned TrimInterval = mgr.options.GraphTrimInterval;
   if (TrimInterval != 0) {
     // Enable eager node reclamation when constructing the ExplodedGraph.
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 491aa93c96e49..b87014e4dcd00 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -93,6 +93,7 @@ class AnalysisConsumer : public AnalysisASTConsumer,
   ArrayRef<std::string> Plugins;
   std::unique_ptr<CodeInjector> Injector;
   cross_tu::CrossTranslationUnitContext CTU;
+  const SummaryContext *SummaryCtx;
 
   /// Stores the declarations from the local translation unit.
   /// Note, we pre-compute the local declarations at parse time as an
@@ -152,6 +153,9 @@ class AnalysisConsumer : public AnalysisASTConsumer,
     if (Opts.ShouldDisplayMacroExpansions)
       MacroExpansions.registerForPreprocessor(PP);
 
+    if (CI.hasSummaryContext())
+      SummaryCtx = &CI.getSummaryContext();
+
     // Visitor options.
     ShouldWalkTypesOfTypeLocs = false;
   }
@@ -761,7 +765,8 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
   if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
     return;
 
-  ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode);
+  ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode,
+                 SummaryCtx);
 
   // Execute the worklist algorithm.
   llvm::TimeRecord ExprEngineStartTime;

>From 81b039f8abc76f83583a809bc9e363122062305f Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 03:47:15 +0200
Subject: [PATCH 20/24] [analyzer] don't invalidate global regions if a
 function doesn't write them

---
 clang/include/clang/Sema/SummaryContext.h     |  1 +
 .../Core/PathSensitive/ExprEngine.h           |  2 ++
 clang/lib/Sema/SummaryContext.cpp             |  5 +++++
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp   | 19 +++++++++++++++----
 4 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/SummaryContext.h b/clang/include/clang/Sema/SummaryContext.h
index 67b1162d61763..2675a85583d69 100644
--- a/clang/include/clang/Sema/SummaryContext.h
+++ b/clang/include/clang/Sema/SummaryContext.h
@@ -42,6 +42,7 @@ class SummaryContext {
 public:
   SummaryContext();
 
+  const SummaryAttribute *GetAttribute(SummaryAttributeKind kind) const;
   const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
   void SummarizeFunctionBody(const FunctionDecl *FD);
 
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index 5e3b338be6e68..68a8004a8ae26 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -217,6 +217,8 @@ class ExprEngine {
     return &CTU;
   }
 
+  const SummaryContext *getSummaryCtx() { return SummaryCtx; }
+
   const NodeBuilderContext &getBuilderContext() {
     assert(currBldrCtx);
     return *currBldrCtx;
diff --git a/clang/lib/Sema/SummaryContext.cpp b/clang/lib/Sema/SummaryContext.cpp
index 6b7207eedb5e5..50771d2da0963 100644
--- a/clang/lib/Sema/SummaryContext.cpp
+++ b/clang/lib/Sema/SummaryContext.cpp
@@ -65,6 +65,11 @@ void SummaryContext::CreateSummary(SmallVector<char> ID,
   IDToSummary[SummaryPtr->getID()] = SummaryPtr;
 }
 
+const SummaryAttribute *
+SummaryContext::GetAttribute(SummaryAttributeKind kind) const {
+  return KindToAttribute.at(kind);
+}
+
 const FunctionSummary *
 SummaryContext::GetSummary(const FunctionDecl *FD) const {
   auto USR = GetUSR(FD);
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 583315f4f3a90..aa9a236ead9a3 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -277,13 +277,24 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
             ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
   }
 
+  bool ShouldPreserveGlobals = false;
+  const SummaryContext *SummaryCtx =
+      State->getStateManager().getOwningEngine().getSummaryCtx();
+  if (SummaryCtx) {
+    const auto *Summary =
+        SummaryCtx->GetSummary(llvm::dyn_cast<FunctionDecl>(getDecl()));
+    ShouldPreserveGlobals =
+        Summary && Summary->getAttributes().count(
+                       SummaryCtx->GetAttribute(NO_WRITE_GLOBAL));
+  }
+
   // Invalidate designated regions using the batch invalidation API.
   // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
   //  global variables.
-  return Result->invalidateRegions(ValuesToInvalidate, getCFGElementRef(),
-                                   BlockCount, getLocationContext(),
-                                   /*CausedByPointerEscape*/ true,
-                                   /*Symbols=*/nullptr, this, &ETraits);
+  return Result->invalidateRegions(
+      ValuesToInvalidate, getCFGElementRef(), BlockCount, getLocationContext(),
+      /*CausedByPointerEscape*/ true,
+      /*Symbols=*/nullptr, ShouldPreserveGlobals ? nullptr : this, &ETraits);
 }
 
 ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,

>From 016dcdb4707a0b8369ca0ef6a0fe788d1fb2f78f Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 14:50:08 +0200
Subject: [PATCH 21/24] [clang][Summary] refactor summary attributes

---
 clang/include/clang/Sema/SummaryAttribute.h | 36 +++++++++++++--------
 clang/include/clang/Sema/SummaryContext.h   | 30 ++++++++++-------
 clang/lib/Sema/SummaryContext.cpp           | 35 +++++++++-----------
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp |  9 +++---
 4 files changed, 60 insertions(+), 50 deletions(-)

diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index 0014a9878bacd..b3b8d452dfd18 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -3,37 +3,40 @@
 
 #include "clang/AST/Decl.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
-#include <string>
 
 namespace clang {
-enum SummaryAttributeKind {
+enum SummaryAttrKind {
   NO_WRITE_GLOBAL,
 };
 
 class FunctionSummary;
+class SummaryContext;
 
-class SummaryAttribute {
-  const SummaryAttributeKind Kind;
-  std::string_view Serialzed;
+class SummaryAttr {
+  const SummaryAttrKind Kind;
+  const char *Spelling;
+
+protected:
+  SummaryAttr(SummaryAttrKind Kind, const char *Spelling)
+      : Kind(Kind), Spelling(Spelling){};
 
 public:
-  SummaryAttribute(SummaryAttributeKind Attr, const char *Str)
-      : Kind(Attr), Serialzed(Str) {}
-  virtual ~SummaryAttribute() = default;
+  virtual ~SummaryAttr() = default;
 
-  SummaryAttributeKind getKind() { return Kind; }
+  SummaryAttrKind getKind() const { return Kind; }
+  const char *getSpelling() const { return Spelling; }
 
   virtual bool infer(const FunctionDecl *FD) const = 0;
   virtual bool merge(const FunctionSummary &Caller,
                      const FunctionSummary &Callee) const = 0;
 
-  virtual std::string serialize() const { return std::string(Serialzed); };
+  virtual std::string serialize() const { return std::string(Spelling); };
   virtual bool parse(std::string_view input) const {
-    return input == Serialzed;
+    return input == Spelling;
   };
 };
 
-class NoWriteGlobalAttr : public SummaryAttribute {
+class NoWriteGlobalAttr : public SummaryAttr {
   class Callback : public ast_matchers::MatchFinder::MatchCallback {
   public:
     bool WriteGlobal = false;
@@ -42,12 +45,17 @@ class NoWriteGlobalAttr : public SummaryAttribute {
     run(const ast_matchers::MatchFinder::MatchResult &Result) override final;
   };
 
-public:
-  NoWriteGlobalAttr() : SummaryAttribute(NO_WRITE_GLOBAL, "no_write_global") {}
+  NoWriteGlobalAttr() : SummaryAttr(NO_WRITE_GLOBAL, "no_write_global") {}
 
+public:
   bool infer(const FunctionDecl *FD) const override final;
   bool merge(const FunctionSummary &Caller,
              const FunctionSummary &Callee) const override final;
+
+  static bool classof(const SummaryAttr *A) {
+    return A->getKind() == NO_WRITE_GLOBAL;
+  }
+  friend class SummaryContext;
 };
 } // namespace clang
 
diff --git a/clang/include/clang/Sema/SummaryContext.h b/clang/include/clang/Sema/SummaryContext.h
index 2675a85583d69..c142484b131dc 100644
--- a/clang/include/clang/Sema/SummaryContext.h
+++ b/clang/include/clang/Sema/SummaryContext.h
@@ -8,21 +8,27 @@
 namespace clang {
 class FunctionSummary {
   SmallVector<char> ID;
-  std::set<const SummaryAttribute *> Attrs;
+  std::set<const SummaryAttr *> Attrs;
   std::set<SmallVector<char>> Calls;
 
 public:
-  FunctionSummary(SmallVector<char> ID,
-                  std::set<const SummaryAttribute *> Attrs,
+  FunctionSummary(SmallVector<char> ID, std::set<const SummaryAttr *> Attrs,
                   std::set<SmallVector<char>> Calls);
 
   SmallVector<char> getID() const { return ID; }
-  const std::set<const SummaryAttribute *> &getAttributes() const {
-    return Attrs;
-  }
+  const std::set<const SummaryAttr *> &getAttributes() const { return Attrs; }
   const std::set<SmallVector<char>> &getCalls() const { return Calls; }
 
-  void replaceAttributes(std::set<const SummaryAttribute *> Attrs) {
+  template <typename T> bool hasAttribute() const {
+    for (auto &&attr : Attrs) {
+      if (llvm::isa<T>(attr))
+        return true;
+    }
+
+    return false;
+  }
+
+  void replaceAttributes(std::set<const SummaryAttr *> Attrs) {
     this->Attrs = std::move(Attrs);
   }
 };
@@ -31,18 +37,18 @@ class SummaryContext {
   std::map<SmallVector<char>, const FunctionSummary *> IDToSummary;
   std::vector<std::unique_ptr<FunctionSummary>> FunctionSummaries;
 
-  std::map<SummaryAttributeKind, const SummaryAttribute *> KindToAttribute;
-  std::vector<std::unique_ptr<SummaryAttribute>> Attributes;
+  std::map<SummaryAttrKind, const SummaryAttr *> KindToAttribute;
+  std::vector<std::unique_ptr<SummaryAttr>> Attributes;
 
-  void CreateSummary(SmallVector<char> ID,
-                     std::set<const SummaryAttribute *> Attrs,
+  void CreateSummary(SmallVector<char> ID, std::set<const SummaryAttr *> Attrs,
                      std::set<SmallVector<char>> Calls);
   bool ReduceFunctionSummary(FunctionSummary &FunctionSummary);
 
+  template <typename T> void registerAttr();
+
 public:
   SummaryContext();
 
-  const SummaryAttribute *GetAttribute(SummaryAttributeKind kind) const;
   const FunctionSummary *GetSummary(const FunctionDecl *FD) const;
   void SummarizeFunctionBody(const FunctionDecl *FD);
 
diff --git a/clang/lib/Sema/SummaryContext.cpp b/clang/lib/Sema/SummaryContext.cpp
index 50771d2da0963..b4e49451030c0 100644
--- a/clang/lib/Sema/SummaryContext.cpp
+++ b/clang/lib/Sema/SummaryContext.cpp
@@ -40,24 +40,26 @@ class CallCollector : public ast_matchers::MatchFinder::MatchCallback {
 };
 } // namespace
 
-FunctionSummary::FunctionSummary(
-    SmallVector<char> ID, std::set<const SummaryAttribute *> FunctionAttrs,
-    std::set<SmallVector<char>> Calls)
+FunctionSummary::FunctionSummary(SmallVector<char> ID,
+                                 std::set<const SummaryAttr *> FunctionAttrs,
+                                 std::set<SmallVector<char>> Calls)
     : ID(std::move(ID)), Attrs(std::move(FunctionAttrs)),
       Calls(std::move(Calls)) {}
 
-SummaryContext::SummaryContext() {
-  Attributes.emplace_back(std::make_unique<NoWriteGlobalAttr>());
+template <typename T> void SummaryContext::registerAttr() {
+  std::unique_ptr<T> attr(new T());
+  SummaryAttrKind Kind = attr->getKind();
 
-  for (auto &&Attr : Attributes) {
-    assert(KindToAttribute.count(Attr->getKind()) == 0 &&
-           "Attr already registered");
-    KindToAttribute[Attr->getKind()] = Attr.get();
-  }
+  if (KindToAttribute.count(Kind))
+    return;
+
+  KindToAttribute[Kind] = Attributes.emplace_back(std::move(attr)).get();
 }
 
+SummaryContext::SummaryContext() { registerAttr<NoWriteGlobalAttr>(); }
+
 void SummaryContext::CreateSummary(SmallVector<char> ID,
-                                   std::set<const SummaryAttribute *> Attrs,
+                                   std::set<const SummaryAttr *> Attrs,
                                    std::set<SmallVector<char>> Calls) {
   auto Summary = std::make_unique<FunctionSummary>(
       std::move(ID), std::move(Attrs), std::move(Calls));
@@ -65,11 +67,6 @@ void SummaryContext::CreateSummary(SmallVector<char> ID,
   IDToSummary[SummaryPtr->getID()] = SummaryPtr;
 }
 
-const SummaryAttribute *
-SummaryContext::GetAttribute(SummaryAttributeKind kind) const {
-  return KindToAttribute.at(kind);
-}
-
 const FunctionSummary *
 SummaryContext::GetSummary(const FunctionDecl *FD) const {
   auto USR = GetUSR(FD);
@@ -77,7 +74,7 @@ SummaryContext::GetSummary(const FunctionDecl *FD) const {
 }
 
 void SummaryContext::SummarizeFunctionBody(const FunctionDecl *FD) {
-  std::set<const SummaryAttribute *> Attrs;
+  std::set<const SummaryAttr *> Attrs;
 
   for (auto &&Attr : Attributes) {
     if (Attr->infer(FD))
@@ -92,7 +89,7 @@ void SummaryContext::ParseSummaryFromJSON(const llvm::json::Array &Summary) {
     const llvm::json::Object *FunctionSummary = it->getAsObject();
 
     SmallString<128> ID(*FunctionSummary->getString("id"));
-    std::set<const SummaryAttribute *> FunctionAttrs;
+    std::set<const SummaryAttr *> FunctionAttrs;
     const llvm::json::Array *FunctionAttributes =
         FunctionSummary->getObject("attrs")->getArray("function");
     for(auto attrIt = FunctionAttributes->begin(); attrIt != FunctionAttributes->end(); ++attrIt) {
@@ -117,7 +114,7 @@ bool SummaryContext::ReduceFunctionSummary(FunctionSummary &Function) {
   bool changed = false;
 
   for (auto &&call : Function.getCalls()) {
-    std::set<const SummaryAttribute *> reducedAttrs;
+    std::set<const SummaryAttr *> reducedAttrs;
 
     // If we don't have a summary about a called function, we forget
     // everything about the current one as well.
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index aa9a236ead9a3..a4885f62be627 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -280,12 +280,11 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
   bool ShouldPreserveGlobals = false;
   const SummaryContext *SummaryCtx =
       State->getStateManager().getOwningEngine().getSummaryCtx();
-  if (SummaryCtx) {
-    const auto *Summary =
-        SummaryCtx->GetSummary(llvm::dyn_cast<FunctionDecl>(getDecl()));
+  const FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(getDecl());
+  if (SummaryCtx && FD) {
+    const auto *Summary = SummaryCtx->GetSummary(FD);
     ShouldPreserveGlobals =
-        Summary && Summary->getAttributes().count(
-                       SummaryCtx->GetAttribute(NO_WRITE_GLOBAL));
+        Summary && Summary->hasAttribute<NoWriteGlobalAttr>();
   }
 
   // Invalidate designated regions using the batch invalidation API.

>From 115584471eb25cd5211d8bda0854c17b2bf3eee4 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 14:57:16 +0200
Subject: [PATCH 22/24] [Summary] move the ast matcher callback out of the
 attribute declaration

---
 clang/include/clang/Sema/SummaryAttribute.h |  8 -------
 clang/lib/Sema/SummaryAttribute.cpp         | 25 ++++++++++++---------
 2 files changed, 15 insertions(+), 18 deletions(-)

diff --git a/clang/include/clang/Sema/SummaryAttribute.h b/clang/include/clang/Sema/SummaryAttribute.h
index b3b8d452dfd18..27ceede1e4486 100644
--- a/clang/include/clang/Sema/SummaryAttribute.h
+++ b/clang/include/clang/Sema/SummaryAttribute.h
@@ -37,14 +37,6 @@ class SummaryAttr {
 };
 
 class NoWriteGlobalAttr : public SummaryAttr {
-  class Callback : public ast_matchers::MatchFinder::MatchCallback {
-  public:
-    bool WriteGlobal = false;
-
-    void
-    run(const ast_matchers::MatchFinder::MatchResult &Result) override final;
-  };
-
   NoWriteGlobalAttr() : SummaryAttr(NO_WRITE_GLOBAL, "no_write_global") {}
 
 public:
diff --git a/clang/lib/Sema/SummaryAttribute.cpp b/clang/lib/Sema/SummaryAttribute.cpp
index 86d422c16fa30..6bb6b938184de 100644
--- a/clang/lib/Sema/SummaryAttribute.cpp
+++ b/clang/lib/Sema/SummaryAttribute.cpp
@@ -2,19 +2,24 @@
 #include "clang/Sema/SummaryContext.h"
 
 namespace clang {
-void NoWriteGlobalAttr::Callback::run(
-    const ast_matchers::MatchFinder::MatchResult &Result) {
-  const auto *Assignment = Result.Nodes.getNodeAs<BinaryOperator>("assignment");
-  if (!Assignment)
-    return;
-
-  WriteGlobal = true;
-}
-
 bool NoWriteGlobalAttr::infer(const FunctionDecl *FD) const {
   using namespace ast_matchers;
   MatchFinder Finder;
-  Callback CB;
+
+  class Callback : public ast_matchers::MatchFinder::MatchCallback {
+  public:
+    bool WriteGlobal = false;
+
+    void
+    run(const ast_matchers::MatchFinder::MatchResult &Result) override final {
+      const auto *Assignment =
+          Result.Nodes.getNodeAs<BinaryOperator>("assignment");
+      if (!Assignment)
+        return;
+
+      WriteGlobal = true;
+    }
+  } CB;
 
   Finder.addMatcher(
       functionDecl(forEachDescendant(

>From 49937a6570ff49042b72a286896647dcdd926cb9 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 15:07:56 +0200
Subject: [PATCH 23/24] [clang] don't crash if there is no summary consumer

---
 clang/lib/Frontend/CompilerInstance.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 448d45b0f49db..9682a01fab226 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -764,7 +764,8 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind,
                                   CodeCompleteConsumer *CompletionConsumer,
                                   SummaryConsumer *SummaryConsumer) {
   TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
-                         TUKind, CompletionConsumer, &getSummaryContext(),
+                         TUKind, CompletionConsumer,
+                         hasSummaryContext() ? &getSummaryContext() : nullptr,
                          SummaryConsumer));
 
   // Set up API notes.

>From 2499ff2e7f92537a5617f7c91a6dfb14e0c2e179 Mon Sep 17 00:00:00 2001
From: isuckatcs <65320245+isuckatcs at users.noreply.github.com>
Date: Sat, 14 Jun 2025 15:50:54 +0200
Subject: [PATCH 24/24] [Driver][Summary] implement emitting summary next to
 the object file

---
 clang/lib/Driver/ToolChains/Clang.cpp | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 48b19615ab08f..f61bb983900f4 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5473,7 +5473,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   if (Args.getLastArg(options::OPT_summaries_dir_EQ))
     Args.AddLastArg(CmdArgs, options::OPT_summaries_dir_EQ);
 
-  // FIXME: This needs to be cleaned up and needs proper error handling as well.
   if (const Arg *A = Args.getLastArg(options::OPT_emit_summaries_EQ)) {
     llvm::SmallString<10> input;
     for (const auto &II : Inputs) {
@@ -5485,14 +5484,21 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
     }
 
     if (!input.empty()) {
-      if (A->containsValue("cwd")) {
-        llvm::SmallString<10> filename = llvm::sys::path::filename(input);
-        llvm::sys::path::replace_extension(filename, "json");
+      Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o);
+      StringRef filename = llvm::sys::path::filename(input);
+      llvm::SmallString<10> summaryFile;
+
+      if (A->containsValue("cwd") || !FinalOutput) {
+        summaryFile = filename;
+      } else if (A->containsValue("obj") && FinalOutput) {
+        summaryFile = llvm::sys::path::parent_path(FinalOutput->getValue());
+        llvm::sys::path::append(summaryFile, filename);
+      }
 
+      if (!summaryFile.empty()) {
+        llvm::sys::path::replace_extension(summaryFile, "json");
         CmdArgs.push_back(
-            Args.MakeArgString(Twine("-summary-file=") + filename));
-      } else if (A->containsValue("obj")) {
-        // FIXME: implement
+            Args.MakeArgString(Twine("-summary-file=") + summaryFile));
       }
     }
   }



More information about the cfe-commits mailing list