[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