[clang] [clang][deps] Serialize JSON without creating intermediate objects (PR #111734)

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 9 14:48:59 PDT 2024


https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/111734

>From c4c5b9cd372707a53cfe1948ab3881a8a64001a3 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Tue, 8 Oct 2024 16:21:40 -0700
Subject: [PATCH 1/2] [clang][deps] Serialize JSON without creating
 intermediate objects

The dependency scanner uses the `llvm::json` library for outputting the dependency information. Until now, it created an in-memory representation of the dependency graph using the `llvm::json::Object` hierarchy. This not only creates unnecessary copies of the data, but also forces lexicographical ordering of attributes in the output, both of which I'd like to avoid. This patch adopts the `llvm::json::OStream` API instead and reorders the attribute printing logic such that the existing lexicographical ordering is preserved (for now).
---
 clang/tools/clang-scan-deps/ClangScanDeps.cpp | 159 ++++++++++--------
 1 file changed, 87 insertions(+), 72 deletions(-)

diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index b642a37c79e980..0c1774e60e7cc2 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -330,38 +330,46 @@ handleMakeDependencyToolResult(const std::string &Input,
   return false;
 }
 
-static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
-  std::vector<llvm::StringRef> Strings;
-  for (auto &&I : Set)
-    Strings.push_back(I.getKey());
+template <typename Container>
+static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
+  return [&JOS, Strings = std::forward<Container>(Strings)]() {
+    for (StringRef Str : Strings)
+      JOS.value(Str);
+  };
+}
+
+static auto toJSONSorted(llvm::json::OStream &JOS,
+                         const llvm::StringSet<> &Set) {
+  SmallVector<StringRef> Strings(Set.keys());
   llvm::sort(Strings);
-  return llvm::json::Array(Strings);
+  return toJSONStrings(JOS, std::move(Strings));
 }
 
 // Technically, we don't need to sort the dependency list to get determinism.
 // Leaving these be will simply preserve the import order.
-static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
+static auto toJSONSorted(llvm::json::OStream &JOS, std::vector<ModuleID> V) {
   llvm::sort(V);
-
-  llvm::json::Array Ret;
-  for (const ModuleID &MID : V)
-    Ret.push_back(llvm::json::Object(
-        {{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
-  return Ret;
+  return [&JOS, V = std::move(V)]() {
+    for (const ModuleID &MID : V)
+      JOS.object([&]() {
+        JOS.attribute("context-hash", StringRef(MID.ContextHash));
+        JOS.attribute("module-name", StringRef(MID.ModuleName));
+      });
+  };
 }
 
-static llvm::json::Array
-toJSONSorted(llvm::SmallVector<Module::LinkLibrary, 2> &LinkLibs) {
-  llvm::sort(LinkLibs, [](const Module::LinkLibrary &lhs,
-                          const Module::LinkLibrary &rhs) {
-    return lhs.Library < rhs.Library;
+static auto toJSONSorted(llvm::json::OStream &JOS,
+                         SmallVector<Module::LinkLibrary, 2> LinkLibs) {
+  llvm::sort(LinkLibs, [](const auto &LHS, const auto &RHS) {
+    return LHS.Library < RHS.Library;
   });
-
-  llvm::json::Array Ret;
-  for (const Module::LinkLibrary &LL : LinkLibs)
-    Ret.push_back(llvm::json::Object(
-        {{"link-name", LL.Library}, {"isFramework", LL.IsFramework}}));
-  return Ret;
+  return [&JOS, LinkLibs = std::move(LinkLibs)]() {
+    for (const auto &LL : LinkLibs)
+      JOS.object([&]() {
+        JOS.attribute("isFramework", LL.IsFramework);
+        JOS.attribute("link-name", StringRef(LL.Library));
+      });
+  };
 }
 
 // Thread safe.
@@ -450,58 +458,65 @@ class FullDeps {
       ModuleIDs.push_back(M.first);
     llvm::sort(ModuleIDs);
 
-    using namespace llvm::json;
-
-    Array OutModules;
-    for (auto &&ModID : ModuleIDs) {
-      auto &MD = Modules[ModID];
-      Object O{{"name", MD.ID.ModuleName},
-               {"context-hash", MD.ID.ContextHash},
-               {"file-deps", toJSONSorted(MD.FileDeps)},
-               {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
-               {"clang-modulemap-file", MD.ClangModuleMapFile},
-               {"command-line", MD.getBuildArguments()},
-               {"link-libraries", toJSONSorted(MD.LinkLibraries)}};
-      OutModules.push_back(std::move(O));
-    }
-
-    Array TUs;
-    for (auto &&I : Inputs) {
-      Array Commands;
-      if (I.DriverCommandLine.empty()) {
-        for (const auto &Cmd : I.Commands) {
-          Object O{
-              {"input-file", I.FileName},
-              {"clang-context-hash", I.ContextHash},
-              {"file-deps", I.FileDeps},
-              {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
-              {"executable", Cmd.Executable},
-              {"command-line", Cmd.Arguments},
-          };
-          Commands.push_back(std::move(O));
+    llvm::json::OStream JOS(OS, /*IndentSize=*/2);
+
+    JOS.object([&]() {
+      JOS.attributeArray("modules", [&]() {
+        for (auto &&ModID : ModuleIDs) {
+          auto &MD = Modules[ModID];
+          JOS.object([&]() {
+            JOS.attributeArray("clang-module-deps",
+                               toJSONSorted(JOS, MD.ClangModuleDeps));
+            JOS.attribute("clang-modulemap-file",
+                          StringRef(MD.ClangModuleMapFile));
+            JOS.attributeArray("command-line",
+                               toJSONStrings(JOS, MD.getBuildArguments()));
+            JOS.attribute("context-hash", StringRef(MD.ID.ContextHash));
+            JOS.attributeArray("file-deps", toJSONSorted(JOS, MD.FileDeps));
+            JOS.attributeArray("link-libraries",
+                               toJSONSorted(JOS, MD.LinkLibraries));
+            JOS.attribute("name", StringRef(MD.ID.ModuleName));
+          });
         }
-      } else {
-        Object O{
-            {"input-file", I.FileName},
-            {"clang-context-hash", I.ContextHash},
-            {"file-deps", I.FileDeps},
-            {"clang-module-deps", toJSONSorted(I.ModuleDeps)},
-            {"executable", "clang"},
-            {"command-line", I.DriverCommandLine},
-        };
-        Commands.push_back(std::move(O));
-      }
-      TUs.push_back(Object{
-          {"commands", std::move(Commands)},
       });
-    }
-
-    Object Output{
-        {"modules", std::move(OutModules)},
-        {"translation-units", std::move(TUs)},
-    };
 
-    OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
+      JOS.attributeArray("translation-units", [&]() {
+        for (auto &&I : Inputs) {
+          JOS.object([&]() {
+            JOS.attributeArray("commands", [&]() {
+              if (I.DriverCommandLine.empty()) {
+                for (const auto &Cmd : I.Commands) {
+                  JOS.object([&]() {
+                    JOS.attribute("clang-context-hash",
+                                  StringRef(I.ContextHash));
+                    JOS.attributeArray("clang-module-deps",
+                                       toJSONSorted(JOS, I.ModuleDeps));
+                    JOS.attributeArray("command-line",
+                                       toJSONStrings(JOS, Cmd.Arguments));
+                    JOS.attribute("executable", StringRef(Cmd.Executable));
+                    JOS.attributeArray("file-deps",
+                                       toJSONStrings(JOS, I.FileDeps));
+                    JOS.attribute("input-file", StringRef(I.FileName));
+                  });
+                }
+              } else {
+                JOS.object([&]() {
+                  JOS.attribute("clang-context-hash", StringRef(I.ContextHash));
+                  JOS.attributeArray("clang-module-deps",
+                                     toJSONSorted(JOS, I.ModuleDeps));
+                  JOS.attributeArray("command-line",
+                                     toJSONStrings(JOS, I.DriverCommandLine));
+                  JOS.attribute("executable", "clang");
+                  JOS.attributeArray("file-deps",
+                                     toJSONStrings(JOS, I.FileDeps));
+                  JOS.attribute("input-file", StringRef(I.FileName));
+                });
+              }
+            });
+          });
+        }
+      });
+    });
   }
 
 private:

>From 5c3e53a6708af857e85de1f1f27ea82777ce409a Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Wed, 9 Oct 2024 14:47:54 -0700
Subject: [PATCH 2/2] Drop empty argument list from lambdas

---
 clang/tools/clang-scan-deps/ClangScanDeps.cpp | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
index 0c1774e60e7cc2..7d36cee7a22b39 100644
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -332,7 +332,7 @@ handleMakeDependencyToolResult(const std::string &Input,
 
 template <typename Container>
 static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
-  return [&JOS, Strings = std::forward<Container>(Strings)]() {
+  return [&JOS, Strings = std::forward<Container>(Strings)] {
     for (StringRef Str : Strings)
       JOS.value(Str);
   };
@@ -349,9 +349,9 @@ static auto toJSONSorted(llvm::json::OStream &JOS,
 // Leaving these be will simply preserve the import order.
 static auto toJSONSorted(llvm::json::OStream &JOS, std::vector<ModuleID> V) {
   llvm::sort(V);
-  return [&JOS, V = std::move(V)]() {
+  return [&JOS, V = std::move(V)] {
     for (const ModuleID &MID : V)
-      JOS.object([&]() {
+      JOS.object([&] {
         JOS.attribute("context-hash", StringRef(MID.ContextHash));
         JOS.attribute("module-name", StringRef(MID.ModuleName));
       });
@@ -363,9 +363,9 @@ static auto toJSONSorted(llvm::json::OStream &JOS,
   llvm::sort(LinkLibs, [](const auto &LHS, const auto &RHS) {
     return LHS.Library < RHS.Library;
   });
-  return [&JOS, LinkLibs = std::move(LinkLibs)]() {
+  return [&JOS, LinkLibs = std::move(LinkLibs)] {
     for (const auto &LL : LinkLibs)
-      JOS.object([&]() {
+      JOS.object([&] {
         JOS.attribute("isFramework", LL.IsFramework);
         JOS.attribute("link-name", StringRef(LL.Library));
       });
@@ -460,11 +460,11 @@ class FullDeps {
 
     llvm::json::OStream JOS(OS, /*IndentSize=*/2);
 
-    JOS.object([&]() {
-      JOS.attributeArray("modules", [&]() {
+    JOS.object([&] {
+      JOS.attributeArray("modules", [&] {
         for (auto &&ModID : ModuleIDs) {
           auto &MD = Modules[ModID];
-          JOS.object([&]() {
+          JOS.object([&] {
             JOS.attributeArray("clang-module-deps",
                                toJSONSorted(JOS, MD.ClangModuleDeps));
             JOS.attribute("clang-modulemap-file",
@@ -480,13 +480,13 @@ class FullDeps {
         }
       });
 
-      JOS.attributeArray("translation-units", [&]() {
+      JOS.attributeArray("translation-units", [&] {
         for (auto &&I : Inputs) {
-          JOS.object([&]() {
-            JOS.attributeArray("commands", [&]() {
+          JOS.object([&] {
+            JOS.attributeArray("commands", [&] {
               if (I.DriverCommandLine.empty()) {
                 for (const auto &Cmd : I.Commands) {
-                  JOS.object([&]() {
+                  JOS.object([&] {
                     JOS.attribute("clang-context-hash",
                                   StringRef(I.ContextHash));
                     JOS.attributeArray("clang-module-deps",
@@ -500,7 +500,7 @@ class FullDeps {
                   });
                 }
               } else {
-                JOS.object([&]() {
+                JOS.object([&] {
                   JOS.attribute("clang-context-hash", StringRef(I.ContextHash));
                   JOS.attributeArray("clang-module-deps",
                                      toJSONSorted(JOS, I.ModuleDeps));



More information about the cfe-commits mailing list