[clang-tools-extra] 9870819 - Restore clang-doc arena allocation (#197595)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 14 12:47:50 PDT 2026
Author: Paul Kirth
Date: 2026-05-14T12:47:45-07:00
New Revision: 9870819761b07a99c44ded0f1750fca7ec226223
URL: https://github.com/llvm/llvm-project/commit/9870819761b07a99c44ded0f1750fca7ec226223
DIFF: https://github.com/llvm/llvm-project/commit/9870819761b07a99c44ded0f1750fca7ec226223.diff
LOG: Restore clang-doc arena allocation (#197595)
This patch restores the commits reverted in
49f8ad172446dd54dd14c2333a7a0f638e37d05a.
It effectively reapplies:
Reapply "[clang-doc][nfc] Default initialize all StringRef members
(#191641)"
Reapply "[clang-doc] Initialize StringRef members in Info types
(#191637)"
Reapply "[clang-doc] Initialize member variable (#191570)"
Reapply "[clang-doc] Merge data into persistent memory (#190056)"
Reapply "[clang-doc] Support deep copy between arenas for merging
(#190055)"
This version has been updated to account for the new InfoNode<T>
paradigm and APIs introduced earlier. Its logic is largely unchanged: it
still performs deep copies between the transient and persistent arenas,
merges the Info types incrementally, and clears the transient data
between merge operations. It also incorporates a few of the cleanups we
applied when trying to fix the Darwin x86 bots prior to reverting, and a
new test case for issues brought up at that time.
Added:
Modified:
clang-tools-extra/clang-doc/Representation.cpp
clang-tools-extra/clang-doc/Representation.h
clang-tools-extra/clang-doc/Serialize.cpp
clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
clang-tools-extra/unittests/clang-doc/MergeTest.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index a0b334e0be598..738c8b6559eb6 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -116,7 +116,30 @@ static void reduceChildren(OwningVec<T> &Children,
Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
if (It == Children.end()) {
- Children.push_back(*allocateListNodeTransient<T>(Ptr));
+ InfoNode<T> *NewNode = allocateListNodePersistent<T>(Ptr->USR);
+ NewNode->Ptr->merge(std::move(*Ptr));
+ Children.push_back(*NewNode);
+ } else {
+ It->Ptr->merge(std::move(*Ptr));
+ }
+ }
+}
+
+template <>
+void reduceChildren<Reference>(OwningVec<Reference> &Children,
+ OwningVec<Reference> &&ChildrenToMerge) {
+ while (!ChildrenToMerge.empty()) {
+ Reference *Ptr = ChildrenToMerge.front().Ptr;
+ ChildrenToMerge.pop_front();
+
+ auto It = llvm::find_if(
+ Children, [Ptr](const auto &C) { return C.Ptr->USR == Ptr->USR; });
+ if (It == Children.end()) {
+ InfoNode<Reference> *NewNode = allocateListNodePersistent<Reference>();
+ NewNode->Ptr->USR = Ptr->USR;
+ NewNode->Ptr->RefType = Ptr->RefType;
+ NewNode->Ptr->merge(std::move(*Ptr));
+ Children.push_back(*NewNode);
} else {
It->Ptr->merge(std::move(*Ptr));
}
@@ -129,11 +152,108 @@ static void mergeUnkeyed(OwningVec<T> &Target, OwningVec<T> &&Source) {
T *Ptr = Source.front().Ptr;
Source.pop_front();
- if (!llvm::any_of(Target, [Ptr](const auto &E) { return *E.Ptr == *Ptr; }))
- Target.push_back(*allocateListNodeTransient<T>(Ptr));
+ if (!llvm::any_of(Target,
+ [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
+ Target.push_back(*allocateListNodePersistent<T>(*Ptr));
+ }
}
}
+template <>
+void mergeUnkeyed<CommentInfo>(OwningVec<CommentInfo> &Target,
+ OwningVec<CommentInfo> &&Source) {
+ while (!Source.empty()) {
+ CommentInfo *Ptr = Source.front().Ptr;
+ Source.pop_front();
+
+ if (!llvm::any_of(Target,
+ [Ptr](const auto &E) { return *E.Ptr == *Ptr; })) {
+ Target.push_back(
+ *allocateListNodePersistent<CommentInfo>(*Ptr, PersistentArena));
+ }
+ }
+}
+
+llvm::Error mergeSingleInfo(doc::OwnedPtr<doc::Info> &Reduced,
+ doc::OwnedPtr<doc::Info> &&NewInfo,
+ llvm::BumpPtrAllocator &Arena) {
+ if (!Reduced) {
+ switch (NewInfo->IT) {
+ case InfoType::IT_namespace:
+ Reduced = allocatePtr<NamespaceInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_record:
+ Reduced = allocatePtr<RecordInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_enum:
+ Reduced = allocatePtr<EnumInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_function:
+ Reduced = allocatePtr<FunctionInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_typedef:
+ Reduced = allocatePtr<TypedefInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_concept:
+ Reduced = allocatePtr<ConceptInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_variable:
+ Reduced = allocatePtr<VarInfo>(Arena, NewInfo->USR);
+ break;
+ case InfoType::IT_friend:
+ Reduced = allocatePtr<FriendInfo>(Arena, NewInfo->USR);
+ break;
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "unknown info type");
+ }
+ }
+
+ if (Reduced->IT != NewInfo->IT)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "info types mismatch");
+
+ switch (Reduced->IT) {
+ case InfoType::IT_namespace:
+ static_cast<NamespaceInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<NamespaceInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_record:
+ static_cast<RecordInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<RecordInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_enum:
+ static_cast<EnumInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<EnumInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_function:
+ static_cast<FunctionInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<FunctionInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_typedef:
+ static_cast<TypedefInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<TypedefInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_concept:
+ static_cast<ConceptInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<ConceptInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_variable:
+ static_cast<VarInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<VarInfo *>(getPtr(NewInfo))));
+ break;
+ case InfoType::IT_friend:
+ static_cast<FriendInfo *>(getPtr(Reduced))
+ ->merge(std::move(*static_cast<FriendInfo *>(getPtr(NewInfo))));
+ break;
+ default:
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "unknown info type");
+ }
+
+ return llvm::Error::success();
+}
+
// Dispatch function.
llvm::Expected<OwnedPtr<Info>> mergeInfos(OwningPtrArray<Info> &Values) {
if (Values.empty() || !Values[0])
@@ -164,6 +284,20 @@ llvm::Expected<OwnedPtr<Info>> mergeInfos(OwningPtrArray<Info> &Values) {
llvm_unreachable("unhandled enumerator");
}
+TemplateSpecializationInfo::TemplateSpecializationInfo(
+ const TemplateSpecializationInfo &Other, llvm::BumpPtrAllocator &Arena)
+ : SpecializationOf(Other.SpecializationOf) {
+ Params = allocateArray(Other.Params, Arena);
+}
+
+TemplateInfo::TemplateInfo(const TemplateInfo &Other,
+ llvm::BumpPtrAllocator &Arena) {
+ Params = allocateArray(Other.Params, Arena);
+ if (Other.Specialization)
+ Specialization = TemplateSpecializationInfo(*Other.Specialization, Arena);
+ Constraints = allocateArray(Other.Constraints, Arena);
+}
+
bool CommentInfo::operator==(const CommentInfo &Other) const {
auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName,
SelfClosing, Explicit, AttrKeys, AttrValues, Args);
@@ -273,10 +407,14 @@ bool Reference::mergeable(const Reference &Other) {
void Reference::merge(Reference &&Other) {
assert(mergeable(Other));
+ assert(RefType != InfoType::IT_default &&
+ "Merging reference with default InfoType");
if (Name.empty())
Name = Other.Name;
if (Path.empty())
Path = Other.Path;
+ if (QualName.empty())
+ QualName = Other.QualName;
if (DocumentationFileName.empty())
DocumentationFileName = Other.DocumentationFileName;
}
@@ -291,16 +429,42 @@ void FriendInfo::merge(FriendInfo &&Other) {
SymbolInfo::merge(std::move(Other));
}
+FriendInfo::FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena)
+ : SymbolInfo(Other, Arena) {
+ Ref = Other.Ref;
+ if (Other.Template)
+ Template.emplace(*Other.Template, Arena);
+ if (Other.ReturnType)
+ ReturnType = Other.ReturnType;
+ if (!Other.Params.empty())
+ Params = allocateArray(Other.Params, Arena);
+ IsClass = Other.IsClass;
+}
+
+Info::Info(const Info &Other, llvm::BumpPtrAllocator &Arena)
+ : Path(Other.Path), Name(Other.Name),
+ DocumentationFileName(Other.DocumentationFileName), USR(Other.USR),
+ ParentUSR(Other.ParentUSR), IT(Other.IT) {
+ Namespace = allocateArray(Other.Namespace, Arena);
+ if (!Other.Description.empty()) {
+ for (const auto &Desc : Other.Description) {
+ CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
+ Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
+ }
+ }
+}
+
void Info::mergeBase(Info &&Other) {
assert(mergeable(Other));
+ assert(IT != InfoType::IT_default && "Merging info with default InfoType");
if (USR == EmptySID)
USR = Other.USR;
if (Name == "")
Name = Other.Name;
if (Path == "")
Path = Other.Path;
- if (Namespace.empty())
- Namespace = std::move(Other.Namespace);
+ if (Namespace.empty() && !Other.Namespace.empty())
+ Namespace = allocateArray(Other.Namespace, PersistentArena);
// Unconditionally extend the description, since each decl may have a comment.
mergeUnkeyed(Description, std::move(Other.Description));
if (ParentUSR == EmptySID)
@@ -313,6 +477,17 @@ bool Info::mergeable(const Info &Other) {
return IT == Other.IT && USR == Other.USR;
}
+SymbolInfo::SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena)
+ : Info(Other, Arena), DefLoc(Other.DefLoc), MangledName(Other.MangledName),
+ IsStatic(Other.IsStatic) {
+ if (!Other.Loc.empty()) {
+ for (const auto &L : Other.Loc) {
+ Location *NewL = allocatePtr<Location>(Arena, L);
+ Loc.push_back(*allocateListNode<Location>(Arena, NewL));
+ }
+ }
+}
+
void SymbolInfo::merge(SymbolInfo &&Other) {
assert(mergeable(Other));
if (!DefLoc)
@@ -322,6 +497,8 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
// Unconditionally extend the list of locations, since we want all of them.
mergeUnkeyed(Loc, std::move(Other.Loc));
mergeBase(std::move(Other));
+ if (!IsStatic)
+ IsStatic = Other.IsStatic;
}
NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
@@ -343,37 +520,76 @@ void NamespaceInfo::merge(NamespaceInfo &&Other) {
RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
: SymbolInfo(InfoType::IT_record, USR, Name, Path) {}
+// FIXME: This constructor is currently unsafe for cross-arena copies of
+// populated records. Because a default copy of ScopeChildren will shallow-copy
+// the intrusive pointers, leading to a use-after-free when the TransientArena
+// is reset. Subsequent patches will address this by deep-copying children
+// individually via reduceChildren.
+RecordInfo::RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena)
+ : SymbolInfo(Other, Arena), TagType(Other.TagType),
+ IsTypeDef(Other.IsTypeDef) {
+ Members = deepCopyArray(Other.Members, Arena);
+ Parents = allocateArray(Other.Parents, Arena);
+ VirtualParents = allocateArray(Other.VirtualParents, Arena);
+ Bases = deepCopyArray(Other.Bases, Arena);
+ Friends = deepCopyArray(Other.Friends, Arena);
+}
+
+MemberTypeInfo::MemberTypeInfo(const MemberTypeInfo &Other,
+ llvm::BumpPtrAllocator &Arena)
+ : FieldTypeInfo(Other), Access(Other.Access), IsStatic(Other.IsStatic) {
+ if (!Other.Description.empty()) {
+ for (const auto &Desc : Other.Description) {
+ CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
+ Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
+ }
+ }
+}
+
void RecordInfo::merge(RecordInfo &&Other) {
assert(mergeable(Other));
if (!llvm::to_underlying(TagType))
TagType = Other.TagType;
IsTypeDef = IsTypeDef || Other.IsTypeDef;
- if (Members.empty())
- Members = std::move(Other.Members);
- if (Bases.empty())
- Bases = std::move(Other.Bases);
- if (Parents.empty())
- Parents = std::move(Other.Parents);
- if (VirtualParents.empty())
- VirtualParents = std::move(Other.VirtualParents);
- if (Friends.empty())
- Friends = std::move(Other.Friends);
+ if (Members.empty() && !Other.Members.empty())
+ Members = deepCopyArray(Other.Members, PersistentArena);
+ if (Bases.empty() && !Other.Bases.empty())
+ Bases = deepCopyArray(Other.Bases, PersistentArena);
+ if (Parents.empty() && !Other.Parents.empty())
+ Parents = allocateArray(Other.Parents, PersistentArena);
+ if (VirtualParents.empty() && !Other.VirtualParents.empty())
+ VirtualParents = allocateArray(Other.VirtualParents, PersistentArena);
+ if (Friends.empty() && !Other.Friends.empty())
+ Friends = deepCopyArray(Other.Friends, PersistentArena);
// Reduce children if necessary.
reduceChildren(Children.Records, std::move(Other.Children.Records));
reduceChildren(Children.Functions, std::move(Other.Children.Functions));
reduceChildren(Children.Enums, std::move(Other.Children.Enums));
reduceChildren(Children.Typedefs, std::move(Other.Children.Typedefs));
- if (!Template)
- Template = Other.Template;
+ if (!Template && Other.Template)
+ Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
+EnumValueInfo::EnumValueInfo(const EnumValueInfo &Other,
+ llvm::BumpPtrAllocator &Arena)
+ : Name(Other.Name), Value(Other.Value), ValueExpr(Other.ValueExpr) {
+ if (!Other.Description.empty()) {
+ for (const auto &Desc : Other.Description) {
+ CommentInfo *NewDesc = allocatePtr<CommentInfo>(Arena, Desc, Arena);
+ Description.push_back(*allocateListNode<CommentInfo>(Arena, NewDesc));
+ }
+ }
+}
+
void EnumInfo::merge(EnumInfo &&Other) {
assert(mergeable(Other));
if (!Scoped)
Scoped = Other.Scoped;
- if (Members.empty())
- Members = std::move(Other.Members);
+ if (!BaseType && Other.BaseType)
+ BaseType = std::move(Other.BaseType);
+ if (Members.empty() && !Other.Members.empty())
+ Members = deepCopyArray(Other.Members, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
@@ -387,10 +603,10 @@ void FunctionInfo::merge(FunctionInfo &&Other) {
ReturnType = std::move(Other.ReturnType);
if (Parent.USR == EmptySID && Parent.Name == "")
Parent = std::move(Other.Parent);
- if (Params.empty())
- Params = std::move(Other.Params);
- if (!Template)
- Template = Other.Template;
+ if (Params.empty() && !Other.Params.empty())
+ Params = allocateArray(Other.Params, PersistentArena);
+ if (!Template && Other.Template)
+ Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
@@ -400,8 +616,8 @@ void TypedefInfo::merge(TypedefInfo &&Other) {
IsUsing = Other.IsUsing;
if (Underlying.Type.Name == "")
Underlying = Other.Underlying;
- if (!Template)
- Template = Other.Template;
+ if (!Template && Other.Template)
+ Template = TemplateInfo(*Other.Template, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
@@ -411,10 +627,11 @@ void ConceptInfo::merge(ConceptInfo &&Other) {
IsType = Other.IsType;
if (ConstraintExpression.empty())
ConstraintExpression = std::move(Other.ConstraintExpression);
- if (Template.Constraints.empty())
- Template.Constraints = std::move(Other.Template.Constraints);
- if (Template.Params.empty())
- Template.Params = std::move(Other.Template.Params);
+ if (Template.Constraints.empty() && !Other.Template.Constraints.empty())
+ Template.Constraints =
+ allocateArray(Other.Template.Constraints, PersistentArena);
+ if (Template.Params.empty() && !Other.Template.Params.empty())
+ Template.Params = allocateArray(Other.Template.Params, PersistentArena);
SymbolInfo::merge(std::move(Other));
}
@@ -429,6 +646,11 @@ void VarInfo::merge(VarInfo &&Other) {
BaseRecordInfo::BaseRecordInfo() : RecordInfo() {}
+BaseRecordInfo::BaseRecordInfo(const BaseRecordInfo &Other,
+ llvm::BumpPtrAllocator &Arena)
+ : RecordInfo(Other, Arena), Access(Other.Access),
+ IsVirtual(Other.IsVirtual), IsParent(Other.IsParent) {}
+
BaseRecordInfo::BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path,
bool IsVirtual, AccessSpecifier Access,
bool IsParent)
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 638110acd479a..7d84998afd77b 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -52,6 +52,7 @@ class ConcurrentStringPool {
ConcurrentStringPool &getGlobalStringPool();
extern thread_local llvm::BumpPtrAllocator TransientArena;
+extern thread_local llvm::BumpPtrAllocator PersistentArena;
inline StringRef internString(const Twine &T) {
if (T.isTriviallyEmpty())
@@ -175,6 +176,15 @@ template <typename T> InfoNode<T> *allocateListNodeTransient(T *Item) {
return allocateListNode<T>(TransientArena, Item);
}
+template <typename T, typename... Args>
+InfoNode<T> *allocateListNodePersistent(Args &&...args) {
+ return allocateListNode<T>(PersistentArena, std::forward<Args>(args)...);
+}
+
+template <typename T> InfoNode<T> *allocateListNodePersistent(T *Item) {
+ return allocateListNode<T>(PersistentArena, Item);
+}
+
// An abstraction for lists that are dynamically managed (inserted/removed).
// To be eventually transitioned to llvm::simple_ilist.
template <typename T> using OwningVec = llvm::simple_ilist<InfoNode<T>>;
@@ -411,6 +421,10 @@ struct TemplateParamInfo {
};
struct TemplateSpecializationInfo {
+ TemplateSpecializationInfo() = default;
+ TemplateSpecializationInfo(const TemplateSpecializationInfo &Other,
+ llvm::BumpPtrAllocator &Arena);
+
// Indicates the declaration that this specializes.
SymbolID SpecializationOf;
@@ -430,6 +444,9 @@ struct ConstraintInfo {
// Records the template information for a struct or function that is a template
// or an explicit template specialization.
struct TemplateInfo {
+ TemplateInfo() = default;
+ TemplateInfo(const TemplateInfo &Other, llvm::BumpPtrAllocator &Arena);
+
// May be empty for non-partial specializations.
llvm::ArrayRef<TemplateParamInfo> Params = {};
@@ -461,6 +478,7 @@ struct FieldTypeInfo : public TypeInfo {
// Info for member types.
struct MemberTypeInfo : public FieldTypeInfo {
MemberTypeInfo() = default;
+ MemberTypeInfo(const MemberTypeInfo &Other, llvm::BumpPtrAllocator &Arena);
MemberTypeInfo(const TypeInfo &TI, StringRef Name, AccessSpecifier Access,
bool IsStatic = false)
: FieldTypeInfo(TI, Name), Access(Access), IsStatic(IsStatic) {}
@@ -517,6 +535,7 @@ struct Info {
StringRef Name = StringRef(), StringRef Path = StringRef())
: Path(internString(Path)), Name(internString(Name)), USR(USR), IT(IT) {}
+ Info(const Info &Other, llvm::BumpPtrAllocator &Arena);
Info(const Info &Other) = delete;
Info(Info &&Other) = default;
@@ -579,6 +598,8 @@ struct SymbolInfo : public Info {
StringRef Name = StringRef(), StringRef Path = StringRef())
: Info(IT, USR, Name, Path) {}
+ SymbolInfo(const SymbolInfo &Other, llvm::BumpPtrAllocator &Arena);
+
void merge(SymbolInfo &&I);
bool operator<(const SymbolInfo &Other) const {
@@ -607,6 +628,7 @@ struct FriendInfo : public SymbolInfo {
FriendInfo(const InfoType IT, const SymbolID &USR,
const StringRef Name = StringRef())
: SymbolInfo(IT, USR, Name) {}
+ FriendInfo(const FriendInfo &Other, llvm::BumpPtrAllocator &Arena);
bool mergeable(const FriendInfo &Other);
void merge(FriendInfo &&Other);
@@ -658,6 +680,8 @@ struct RecordInfo : public SymbolInfo {
RecordInfo(SymbolID USR = SymbolID(), StringRef Name = StringRef(),
StringRef Path = StringRef());
+ RecordInfo(const RecordInfo &Other, llvm::BumpPtrAllocator &Arena);
+
void merge(RecordInfo &&I);
// Type of this record (struct, class, union, interface).
@@ -712,6 +736,7 @@ struct TypedefInfo : public SymbolInfo {
struct BaseRecordInfo : public RecordInfo {
BaseRecordInfo();
+ BaseRecordInfo(const BaseRecordInfo &Other, llvm::BumpPtrAllocator &Arena);
BaseRecordInfo(SymbolID USR, StringRef Name, StringRef Path, bool IsVirtual,
AccessSpecifier Access, bool IsParent);
@@ -731,6 +756,8 @@ struct EnumValueInfo {
: Name(internString(Name)), Value(internString(Value)),
ValueExpr(internString(ValueExpr)) {}
+ EnumValueInfo(const EnumValueInfo &Other, llvm::BumpPtrAllocator &Arena);
+
bool operator==(const EnumValueInfo &Other) const {
return std::tie(Name, Value, ValueExpr) ==
std::tie(Other.Name, Other.Value, Other.ValueExpr);
@@ -806,6 +833,12 @@ struct Index : public Reference {
// if they are
diff erent.
llvm::Expected<OwnedPtr<Info>> mergeInfos(OwningPtrArray<Info> &Values);
+// Merges a single new Info into an existing Reduced Info (allocating it if
+// needed).
+llvm::Error mergeSingleInfo(doc::OwnedPtr<doc::Info> &Reduced,
+ doc::OwnedPtr<doc::Info> &&NewInfo,
+ llvm::BumpPtrAllocator &Arena);
+
struct ClangDocContext {
ClangDocContext(tooling::ExecutionContext *ECtx, StringRef ProjectName,
bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot,
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 17180368550b6..1c8969b56441a 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -233,14 +233,16 @@ void ClangDocCommentVisitor::parseComment(const comments::Comment *C) {
if (NumChildren > 0) {
CommentInfo *ChildrenArray =
TransientArena.Allocate<CommentInfo>(NumChildren);
- unsigned I = 0;
+ unsigned Idx = 0;
for (comments::Comment *Child :
llvm::make_range(C->child_begin(), C->child_end())) {
- new (&ChildrenArray[I]) CommentInfo();
- ClangDocCommentVisitor Visitor(ChildrenArray[I]);
+ new (&ChildrenArray[Idx]) CommentInfo();
+ ClangDocCommentVisitor Visitor(ChildrenArray[Idx]);
Visitor.parseComment(Child);
- I++;
+ Idx++;
}
+ assert(Idx == NumChildren &&
+ "Mismatch between child_count and actual children");
CurrentCI.Children =
llvm::ArrayRef<CommentInfo>(ChildrenArray, NumChildren);
}
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index f627ee5887528..3c38901f4a0f9 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -29,6 +29,7 @@
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
@@ -358,15 +359,22 @@ Example usage for a project using a compile commands database:
llvm::hardware_concurrency(ExecutorConcurrency));
{
llvm::TimeTraceScope TS("Reduce");
- for (auto &Group : USRToBitcode) {
- Pool.async([&, &Diags = Diags]() { // time trace decoding bitcode
- if (FTimeTrace)
+ for (const auto &Group : USRToBitcode) {
+ StringRef Key = Group.getKey();
+ std::vector<StringRef> Bitcodes = Group.getValue();
+ Pool.async([Key, Bitcodes, &CDCtx, &Diags, &USRToInfo, &USRToInfoMutex,
+ &IndexMutex, &DiagMutex, &Error, DiagIDBitcodeReading,
+ DiagIDBitcodeMerging]() {
+ if (CDCtx.FTimeTrace)
llvm::timeTraceProfilerInitialize(200, "clang-doc");
- doc::OwningPtrVec<doc::Info> Infos;
+ doc::OwnedPtr<doc::Info> Reduced = nullptr;
{
- llvm::TimeTraceScope Red("decoding bitcode");
- for (auto &Bitcode : Group.getValue()) {
+ llvm::TimeTraceScope Red("decoding and merging bitcode");
+ for (const auto &Bitcode : Bitcodes) {
+
+ llvm::scope_exit ArenaGuard(
+ [] { clang::doc::TransientArena.Reset(); });
llvm::BitstreamCursor Stream(Bitcode);
doc::ClangDocBitcodeReader Reader(Stream, Diags);
auto ReadInfos = Reader.readBitcode();
@@ -378,25 +386,17 @@ Example usage for a project using a compile commands database:
Error = true;
return;
}
- std::move(ReadInfos->begin(), ReadInfos->end(),
- std::back_inserter(Infos));
- }
- } // time trace decoding bitcode
-
- doc::OwnedPtr<doc::Info> Reduced;
-
- {
- llvm::TimeTraceScope Merge("merging bitcode");
- auto ExpReduced = doc::mergeInfos(Infos);
-
- if (!ExpReduced) {
- std::lock_guard<llvm::sys::Mutex> Guard(DiagMutex);
- Diags.Report(DiagIDBitcodeMerging)
- << toString(ExpReduced.takeError());
- return;
+ for (auto &I : *ReadInfos) {
+ if (auto Err = doc::mergeSingleInfo(
+ Reduced, std::move(I), clang::doc::PersistentArena)) {
+ std::lock_guard<llvm::sys::Mutex> Guard(DiagMutex);
+ Diags.Report(DiagIDBitcodeMerging)
+ << toString(std::move(Err));
+ return;
+ }
+ }
}
- Reduced = std::move(*ExpReduced);
- } // time trace merging bitcode
+ } // time trace decoding and merging bitcode
// Add a reference to this Info in the Index
{
@@ -408,7 +408,7 @@ Example usage for a project using a compile commands database:
{
llvm::TimeTraceScope Merge("USRToInfo");
std::lock_guard<llvm::sys::Mutex> Guard(USRToInfoMutex);
- USRToInfo[Group.getKey()] = std::move(Reduced);
+ USRToInfo[Key] = std::move(Reduced);
}
if (CDCtx.FTimeTrace)
diff --git a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
index 8b7c20bc0adfb..a8e7b9be1c39a 100644
--- a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
@@ -111,6 +111,113 @@ TEST_F(MergeTest, mergeNamespaceInfos) {
CheckNamespaceInfo(InfoAsNamespace(&Expected), InfoAsNamespace(Actual.get()));
}
+TEST_F(MergeTest, mergeSingleNamespaceInfo) {
+ NamespaceInfo One;
+ One.Name = "Namespace";
+ Reference Ns1[] = {Reference(EmptySID, "A", InfoType::IT_namespace)};
+ One.Namespace = llvm::ArrayRef(Ns1);
+
+ Reference RA(NonEmptySID, "ChildNamespace", InfoType::IT_namespace);
+ InfoNode<Reference> RANode(&RA);
+ One.Children.Namespaces.push_back(RANode);
+ Reference RC1(NonEmptySID, "ChildStruct", InfoType::IT_record);
+ InfoNode<Reference> RC1Node(&RC1);
+ One.Children.Records.push_back(RC1Node);
+
+ FunctionInfo F1;
+ F1.Name = "OneFunction";
+ F1.USR = NonEmptySID;
+ InfoNode<FunctionInfo> F1Node(&F1);
+ One.Children.Functions.push_back(F1Node);
+
+ EnumInfo E1;
+ E1.Name = "OneEnum";
+ E1.USR = NonEmptySID;
+ InfoNode<EnumInfo> E1Node(&E1);
+ One.Children.Enums.push_back(E1Node);
+
+ NamespaceInfo Two;
+ Two.Name = "Namespace";
+ Reference Ns2[] = {Reference(EmptySID, "A", InfoType::IT_namespace)};
+ Two.Namespace = llvm::ArrayRef(Ns2);
+
+ Reference RB(EmptySID, "OtherChildNamespace", InfoType::IT_namespace);
+ InfoNode<Reference> RBNode(&RB);
+ Two.Children.Namespaces.push_back(RBNode);
+ Reference RC2(EmptySID, "OtherChildStruct", InfoType::IT_record);
+ InfoNode<Reference> RC2Node(&RC2);
+ Two.Children.Records.push_back(RC2Node);
+
+ FunctionInfo F2;
+ F2.Name = "TwoFunction";
+ InfoNode<FunctionInfo> F2Node(&F2);
+ Two.Children.Functions.push_back(F2Node);
+
+ EnumInfo E2;
+ E2.Name = "TwoEnum";
+ InfoNode<EnumInfo> E2Node(&E2);
+ Two.Children.Enums.push_back(E2Node);
+
+ NamespaceInfo Expected;
+ Expected.Name = "Namespace";
+ Reference NsExpected[] = {Reference(EmptySID, "A", InfoType::IT_namespace)};
+ Expected.Namespace = llvm::ArrayRef(NsExpected);
+
+ Reference RC(NonEmptySID, "ChildNamespace", InfoType::IT_namespace);
+ InfoNode<Reference> RCNode(&RC);
+ Expected.Children.Namespaces.push_back(RCNode);
+ Reference RCE1(NonEmptySID, "ChildStruct", InfoType::IT_record);
+ InfoNode<Reference> RCE1Node(&RCE1);
+ Expected.Children.Records.push_back(RCE1Node);
+ Reference RD(EmptySID, "OtherChildNamespace", InfoType::IT_namespace);
+ InfoNode<Reference> RDNode(&RD);
+ Expected.Children.Namespaces.push_back(RDNode);
+ Reference RCE2(EmptySID, "OtherChildStruct", InfoType::IT_record);
+ InfoNode<Reference> RCE2Node(&RCE2);
+ Expected.Children.Records.push_back(RCE2Node);
+
+ FunctionInfo FE1;
+ FE1.Name = "OneFunction";
+ FE1.USR = NonEmptySID;
+ InfoNode<FunctionInfo> FE1Node(&FE1);
+ Expected.Children.Functions.push_back(FE1Node);
+
+ FunctionInfo FE2;
+ FE2.Name = "TwoFunction";
+ InfoNode<FunctionInfo> FE2Node(&FE2);
+ Expected.Children.Functions.push_back(FE2Node);
+
+ EnumInfo EE1;
+ EE1.Name = "OneEnum";
+ EE1.USR = NonEmptySID;
+ InfoNode<EnumInfo> EE1Node(&EE1);
+ Expected.Children.Enums.push_back(EE1Node);
+
+ EnumInfo EE2;
+ EE2.Name = "TwoEnum";
+ InfoNode<EnumInfo> EE2Node(&EE2);
+ Expected.Children.Enums.push_back(EE2Node);
+ NamespaceInfo ReducedObj;
+ ReducedObj.IT = InfoType::IT_namespace;
+ doc::OwnedPtr<doc::Info> Reduced = &ReducedObj;
+
+ Info *PtrOne = &One;
+ auto Err1 = mergeSingleInfo(Reduced, std::move(PtrOne), doc::PersistentArena);
+ assert(!Err1);
+
+ Info *PtrTwo = &Two;
+ auto Err2 = mergeSingleInfo(Reduced, std::move(PtrTwo), doc::PersistentArena);
+ assert(!Err2);
+
+ CheckNamespaceInfo(InfoAsNamespace(&Expected),
+ static_cast<NamespaceInfo *>(getPtr(Reduced)));
+
+ auto *RedNS = static_cast<NamespaceInfo *>(getPtr(Reduced));
+ // Check that children functions are NOT the same instances as in One or Two
+ ASSERT_NE(RedNS->Children.Functions.front().Ptr, &F1);
+ ASSERT_NE(RedNS->Children.Functions.back().Ptr, &F2);
+}
+
TEST_F(MergeTest, mergeRecordInfos) {
RecordInfo One;
One.Name = "r";
More information about the cfe-commits
mailing list