[clang] [clang-tools-extra] [llvm] [Support][JSON] Use `std::map` for object storage (PR #171230)
Zixu Wang via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 8 16:54:02 PST 2025
https://github.com/zixu-w updated https://github.com/llvm/llvm-project/pull/171230
>From cef7f9c203022471a7a3c8c2995688af521a3d5c Mon Sep 17 00:00:00 2001
From: Zixu Wang <zixu_wang at apple.com>
Date: Mon, 8 Dec 2025 15:21:23 -0800
Subject: [PATCH] [Support][JSON] Use `std::map` for object storage
`llvm::DenseMap` is not suitable for the key-value `Storage` inside
`llvm::json::Object`. Use `std::map` instead to optimize memory usage.
`llvm::DenseMap` is optimized for mapping small keys and values
(pointers), and it pre-allocates 64 buckets by default.
`llvm::json::ObjectKey` is 24 bytes in size, and `llvm::json::Value` is
40 bytes. Currently, the JSON parser allocates 4KB of memory for each
JSON object. In practice, most JSON objects contain only a handful of
fields, and likely to have lists of many small objects. This is a
significant waste of memory.
---
clang-tools-extra/clang-doc/JSONGenerator.cpp | 2 +-
clang/lib/Basic/DarwinSDKInfo.cpp | 10 +++++-----
clang/tools/clang-installapi/Options.cpp | 4 ++--
clang/unittests/Basic/SarifTest.cpp | 2 +-
llvm/include/llvm/Support/JSON.h | 10 +++++-----
llvm/lib/Support/JSON.cpp | 4 ++--
6 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 97c599a3f605c..8be1a27c27c70 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -94,7 +94,7 @@ static void insertComment(Object &Description, json::Value &Comment,
Description[Key] = std::move(CommentsArray);
Description["Has" + Key.str()] = true;
} else {
- DescriptionIt->getSecond().getAsArray()->push_back(Comment);
+ DescriptionIt->second.getAsArray()->push_back(Comment);
}
}
diff --git a/clang/lib/Basic/DarwinSDKInfo.cpp b/clang/lib/Basic/DarwinSDKInfo.cpp
index 6bcfb9d598377..82cb5d774363d 100644
--- a/clang/lib/Basic/DarwinSDKInfo.cpp
+++ b/clang/lib/Basic/DarwinSDKInfo.cpp
@@ -25,7 +25,7 @@ std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map(
return MaximumValue;
auto KV = Mapping.find(Key.normalize());
if (KV != Mapping.end())
- return KV->getSecond();
+ return KV->second;
// If no exact entry found, try just the major key version. Only do so when
// a minor version number is present, to avoid recursing indefinitely into
// the major-only check.
@@ -43,10 +43,10 @@ DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON(
VersionTuple MinValue = Min;
llvm::DenseMap<VersionTuple, VersionTuple> Mapping;
for (const auto &KV : Obj) {
- if (auto Val = KV.getSecond().getAsString()) {
+ if (auto Val = KV.second.getAsString()) {
llvm::VersionTuple KeyVersion;
llvm::VersionTuple ValueVersion;
- if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val))
+ if (KeyVersion.tryParse(KV.first) || ValueVersion.tryParse(*Val))
return std::nullopt;
Mapping[KeyVersion.normalize()] = ValueVersion;
if (KeyVersion < Min)
@@ -113,12 +113,12 @@ DarwinSDKInfo::parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) {
// FIXME: Generalize this out beyond iOS-deriving targets.
// Look for ios_<targetos> version mapping for targets that derive from ios.
for (const auto &KV : *VM) {
- auto Pair = StringRef(KV.getFirst()).split("_");
+ auto Pair = StringRef(KV.first).split("_");
if (Pair.first.compare_insensitive("ios") == 0) {
llvm::Triple TT(llvm::Twine("--") + Pair.second.lower());
if (TT.getOS() != llvm::Triple::UnknownOS) {
auto Mapping = RelatedTargetVersionMapping::parseJSON(
- *KV.getSecond().getAsObject(), *MaximumDeploymentVersion);
+ *KV.second.getAsObject(), *MaximumDeploymentVersion);
if (Mapping)
VersionMappings[OSEnvPair(llvm::Triple::IOS,
llvm::Triple::UnknownEnvironment,
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index f484d6f33ad8f..1151f65af4dce 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -84,8 +84,8 @@ getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table,
return llvm::opt::InputArgList();
for (const auto &KV : *Root) {
- const Array *ArgList = KV.getSecond().getAsArray();
- std::string Label = "-X" + KV.getFirst().str();
+ const Array *ArgList = KV.second.getAsArray();
+ std::string Label = "-X" + KV.first.str();
if (!ArgList)
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat);
for (auto Arg : *ArgList) {
diff --git a/clang/unittests/Basic/SarifTest.cpp b/clang/unittests/Basic/SarifTest.cpp
index 089b6cb01ded8..40c8ad4f749a9 100644
--- a/clang/unittests/Basic/SarifTest.cpp
+++ b/clang/unittests/Basic/SarifTest.cpp
@@ -83,7 +83,7 @@ TEST_F(SarifDocumentWriterTest, canCreateEmptyDocument) {
const llvm::json::Object &EmptyDoc = Writer.createDocument();
std::vector<StringRef> Keys(EmptyDoc.size());
std::transform(EmptyDoc.begin(), EmptyDoc.end(), Keys.begin(),
- [](auto Item) { return Item.getFirst(); });
+ [](auto Item) { return Item.first; });
// THEN:
ASSERT_THAT(Keys, testing::UnorderedElementsAre("$schema", "version"));
diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index a58332c912942..a34570fffa5ae 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -94,9 +94,9 @@ class Value;
template <typename T> Value toJSON(const std::optional<T> &Opt);
/// An Object is a JSON object, which maps strings to heterogenous JSON values.
-/// It simulates DenseMap<ObjectKey, Value>. ObjectKey is a maybe-owned string.
+/// ObjectKey is a maybe-owned string.
class Object {
- using Storage = DenseMap<ObjectKey, Value, llvm::DenseMapInfo<StringRef>>;
+ using Storage = std::map<ObjectKey, Value>;
Storage M;
public:
@@ -133,8 +133,8 @@ class Object {
bool erase(StringRef K);
void erase(iterator I) { M.erase(I); }
- iterator find(StringRef K) { return M.find_as(K); }
- const_iterator find(StringRef K) const { return M.find_as(K); }
+ iterator find(const ObjectKey &K) { return M.find(K); }
+ const_iterator find(const ObjectKey &K) const { return M.find(K); }
// operator[] acts as if Value was default-constructible as null.
LLVM_ABI Value &operator[](const ObjectKey &K);
LLVM_ABI Value &operator[](ObjectKey &&K);
@@ -649,7 +649,7 @@ inline Object::Object(std::initializer_list<KV> Properties) {
for (const auto &P : Properties) {
auto R = try_emplace(P.K, nullptr);
if (R.second)
- R.first->getSecond().moveFrom(std::move(P.V));
+ R.first->second.moveFrom(std::move(P.V));
}
}
inline std::pair<Object::iterator, bool> Object::insert(KV E) {
diff --git a/llvm/lib/Support/JSON.cpp b/llvm/lib/Support/JSON.cpp
index 4652c0740dc4d..ab46a8eca3188 100644
--- a/llvm/lib/Support/JSON.cpp
+++ b/llvm/lib/Support/JSON.cpp
@@ -22,10 +22,10 @@ namespace llvm {
namespace json {
Value &Object::operator[](const ObjectKey &K) {
- return try_emplace(K, nullptr).first->getSecond();
+ return try_emplace(K, nullptr).first->second;
}
Value &Object::operator[](ObjectKey &&K) {
- return try_emplace(std::move(K), nullptr).first->getSecond();
+ return try_emplace(std::move(K), nullptr).first->second;
}
Value *Object::get(StringRef K) {
auto I = find(K);
More information about the llvm-commits
mailing list