Skip to content

Commit c55d68f

Browse files
authored
[clang][deps] Serialize JSON without creating intermediate objects (#111734)
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).
1 parent 91dd4ec commit c55d68f

File tree

1 file changed

+87
-72
lines changed

1 file changed

+87
-72
lines changed

clang/tools/clang-scan-deps/ClangScanDeps.cpp

Lines changed: 87 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -330,38 +330,46 @@ handleMakeDependencyToolResult(const std::string &Input,
330330
return false;
331331
}
332332

333-
static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
334-
std::vector<llvm::StringRef> Strings;
335-
for (auto &&I : Set)
336-
Strings.push_back(I.getKey());
333+
template <typename Container>
334+
static auto toJSONStrings(llvm::json::OStream &JOS, Container &&Strings) {
335+
return [&JOS, Strings = std::forward<Container>(Strings)] {
336+
for (StringRef Str : Strings)
337+
JOS.value(Str);
338+
};
339+
}
340+
341+
static auto toJSONSorted(llvm::json::OStream &JOS,
342+
const llvm::StringSet<> &Set) {
343+
SmallVector<StringRef> Strings(Set.keys());
337344
llvm::sort(Strings);
338-
return llvm::json::Array(Strings);
345+
return toJSONStrings(JOS, std::move(Strings));
339346
}
340347

341348
// Technically, we don't need to sort the dependency list to get determinism.
342349
// Leaving these be will simply preserve the import order.
343-
static llvm::json::Array toJSONSorted(std::vector<ModuleID> V) {
350+
static auto toJSONSorted(llvm::json::OStream &JOS, std::vector<ModuleID> V) {
344351
llvm::sort(V);
345-
346-
llvm::json::Array Ret;
347-
for (const ModuleID &MID : V)
348-
Ret.push_back(llvm::json::Object(
349-
{{"module-name", MID.ModuleName}, {"context-hash", MID.ContextHash}}));
350-
return Ret;
352+
return [&JOS, V = std::move(V)] {
353+
for (const ModuleID &MID : V)
354+
JOS.object([&] {
355+
JOS.attribute("context-hash", StringRef(MID.ContextHash));
356+
JOS.attribute("module-name", StringRef(MID.ModuleName));
357+
});
358+
};
351359
}
352360

353-
static llvm::json::Array
354-
toJSONSorted(llvm::SmallVector<Module::LinkLibrary, 2> &LinkLibs) {
355-
llvm::sort(LinkLibs, [](const Module::LinkLibrary &lhs,
356-
const Module::LinkLibrary &rhs) {
357-
return lhs.Library < rhs.Library;
361+
static auto toJSONSorted(llvm::json::OStream &JOS,
362+
SmallVector<Module::LinkLibrary, 2> LinkLibs) {
363+
llvm::sort(LinkLibs, [](const auto &LHS, const auto &RHS) {
364+
return LHS.Library < RHS.Library;
358365
});
359-
360-
llvm::json::Array Ret;
361-
for (const Module::LinkLibrary &LL : LinkLibs)
362-
Ret.push_back(llvm::json::Object(
363-
{{"link-name", LL.Library}, {"isFramework", LL.IsFramework}}));
364-
return Ret;
366+
return [&JOS, LinkLibs = std::move(LinkLibs)] {
367+
for (const auto &LL : LinkLibs)
368+
JOS.object([&] {
369+
JOS.attribute("isFramework", LL.IsFramework);
370+
JOS.attribute("link-name", StringRef(LL.Library));
371+
});
372+
};
365373
}
366374

367375
// Thread safe.
@@ -450,58 +458,65 @@ class FullDeps {
450458
ModuleIDs.push_back(M.first);
451459
llvm::sort(ModuleIDs);
452460

453-
using namespace llvm::json;
454-
455-
Array OutModules;
456-
for (auto &&ModID : ModuleIDs) {
457-
auto &MD = Modules[ModID];
458-
Object O{{"name", MD.ID.ModuleName},
459-
{"context-hash", MD.ID.ContextHash},
460-
{"file-deps", toJSONSorted(MD.FileDeps)},
461-
{"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
462-
{"clang-modulemap-file", MD.ClangModuleMapFile},
463-
{"command-line", MD.getBuildArguments()},
464-
{"link-libraries", toJSONSorted(MD.LinkLibraries)}};
465-
OutModules.push_back(std::move(O));
466-
}
467-
468-
Array TUs;
469-
for (auto &&I : Inputs) {
470-
Array Commands;
471-
if (I.DriverCommandLine.empty()) {
472-
for (const auto &Cmd : I.Commands) {
473-
Object O{
474-
{"input-file", I.FileName},
475-
{"clang-context-hash", I.ContextHash},
476-
{"file-deps", I.FileDeps},
477-
{"clang-module-deps", toJSONSorted(I.ModuleDeps)},
478-
{"executable", Cmd.Executable},
479-
{"command-line", Cmd.Arguments},
480-
};
481-
Commands.push_back(std::move(O));
461+
llvm::json::OStream JOS(OS, /*IndentSize=*/2);
462+
463+
JOS.object([&] {
464+
JOS.attributeArray("modules", [&] {
465+
for (auto &&ModID : ModuleIDs) {
466+
auto &MD = Modules[ModID];
467+
JOS.object([&] {
468+
JOS.attributeArray("clang-module-deps",
469+
toJSONSorted(JOS, MD.ClangModuleDeps));
470+
JOS.attribute("clang-modulemap-file",
471+
StringRef(MD.ClangModuleMapFile));
472+
JOS.attributeArray("command-line",
473+
toJSONStrings(JOS, MD.getBuildArguments()));
474+
JOS.attribute("context-hash", StringRef(MD.ID.ContextHash));
475+
JOS.attributeArray("file-deps", toJSONSorted(JOS, MD.FileDeps));
476+
JOS.attributeArray("link-libraries",
477+
toJSONSorted(JOS, MD.LinkLibraries));
478+
JOS.attribute("name", StringRef(MD.ID.ModuleName));
479+
});
482480
}
483-
} else {
484-
Object O{
485-
{"input-file", I.FileName},
486-
{"clang-context-hash", I.ContextHash},
487-
{"file-deps", I.FileDeps},
488-
{"clang-module-deps", toJSONSorted(I.ModuleDeps)},
489-
{"executable", "clang"},
490-
{"command-line", I.DriverCommandLine},
491-
};
492-
Commands.push_back(std::move(O));
493-
}
494-
TUs.push_back(Object{
495-
{"commands", std::move(Commands)},
496481
});
497-
}
498-
499-
Object Output{
500-
{"modules", std::move(OutModules)},
501-
{"translation-units", std::move(TUs)},
502-
};
503482

504-
OS << llvm::formatv("{0:2}\n", Value(std::move(Output)));
483+
JOS.attributeArray("translation-units", [&] {
484+
for (auto &&I : Inputs) {
485+
JOS.object([&] {
486+
JOS.attributeArray("commands", [&] {
487+
if (I.DriverCommandLine.empty()) {
488+
for (const auto &Cmd : I.Commands) {
489+
JOS.object([&] {
490+
JOS.attribute("clang-context-hash",
491+
StringRef(I.ContextHash));
492+
JOS.attributeArray("clang-module-deps",
493+
toJSONSorted(JOS, I.ModuleDeps));
494+
JOS.attributeArray("command-line",
495+
toJSONStrings(JOS, Cmd.Arguments));
496+
JOS.attribute("executable", StringRef(Cmd.Executable));
497+
JOS.attributeArray("file-deps",
498+
toJSONStrings(JOS, I.FileDeps));
499+
JOS.attribute("input-file", StringRef(I.FileName));
500+
});
501+
}
502+
} else {
503+
JOS.object([&] {
504+
JOS.attribute("clang-context-hash", StringRef(I.ContextHash));
505+
JOS.attributeArray("clang-module-deps",
506+
toJSONSorted(JOS, I.ModuleDeps));
507+
JOS.attributeArray("command-line",
508+
toJSONStrings(JOS, I.DriverCommandLine));
509+
JOS.attribute("executable", "clang");
510+
JOS.attributeArray("file-deps",
511+
toJSONStrings(JOS, I.FileDeps));
512+
JOS.attribute("input-file", StringRef(I.FileName));
513+
});
514+
}
515+
});
516+
});
517+
}
518+
});
519+
});
505520
}
506521

507522
private:

0 commit comments

Comments
 (0)