Paul Kirth 1deab59f6f
[clang-doc] Migrate Namespaces to arena allocation (#190048)
This patch allocates the NamespaceInfo types in the local arenas, and
adapts the merging logic for the new list type and its children.
Memory use and performance improve slightly. Micro-benchmarks show a
regression in merge operations due to the more complex list operations.

 ## Build Clang-Doc Documentation
| Metric | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| Time | 920.5s | 1009.2s | 1002.4s | +8.9% | -0.7% |
| Memory | 86.0G | 43.2G | 43.9G | -49.0% | +1.6% |

 ## Microbenchmarks (Filtered for >1% Delta)
| Benchmark | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| BM_BitcodeReader_Scale/10 | 67.9us | 69.7us | 69.3us | +1.9% | -0.7% |
| BM_BitcodeReader_Scale/10000 | 70.5ms | 22.3ms | 24.8ms | -64.8% |
+11.4% |
| BM_BitcodeReader_Scale/4096 | 23.2ms | 4.7ms | 4.4ms | -80.9% | -5.7%
|
| BM_BitcodeReader_Scale/512 | 509.4us | 558.7us | 540.2us | +6.0% |
-3.3% |
| BM_BitcodeReader_Scale/64 | 114.8us | 119.2us | 117.0us | +1.9% |
-1.8% |
| BM_EmitInfoFunction | 1.6us | 1.6us | 1.6us | -1.1% | +0.8% |
| BM_Index_Insertion/10 | 2.3us | 4.2us | 3.7us | +61.7% | -11.3% |
| BM_Index_Insertion/10000 | 3.1ms | 5.4ms | 4.9ms | +56.8% | -9.1% |
| BM_Index_Insertion/4096 | 1.3ms | 2.2ms | 2.0ms | +54.5% | -8.2% |
| BM_Index_Insertion/512 | 153.6us | 259.6us | 236.7us | +54.1% | -8.8%
|
| BM_Index_Insertion/64 | 18.1us | 30.7us | 27.9us | +54.5% | -9.0% |
| BM_JSONGenerator_Scale/10 | 36.8us | 37.6us | 36.6us | -0.7% | -2.7% |
| BM_JSONGenerator_Scale/4096 | 33.7ms | 34.3ms | 34.2ms | +1.4% | -0.3%
|
| BM_JSONGenerator_Scale/64 | 222.4us | 225.6us | 220.2us | -1.0% |
-2.4% |
| BM_Mapper_Scale/10 | 2.5ms | 2.5ms | 2.5ms | -1.4% | -1.7% |
| BM_Mapper_Scale/10000 | 104.3ms | 109.3ms | 105.9ms | +1.6% | -3.1% |
| BM_Mapper_Scale/4096 | 44.3ms | 43.9ms | 44.7ms | +0.8% | +1.8% |
| BM_Mapper_Scale/512 | 7.6ms | 7.6ms | 7.6ms | +0.8% | +1.2% |
| BM_MergeInfos_Scale/10000 | 12.2ms | 1.6ms | 2.0ms | -83.6% | +28.7% |
| BM_MergeInfos_Scale/2 | 1.9us | 1.7us | 1.7us | -8.1% | +0.9% |
| BM_MergeInfos_Scale/4096 | 2.8ms | 520.5us | 557.4us | -79.9% | +7.1%
|
| BM_MergeInfos_Scale/512 | 68.9us | 37.9us | 39.9us | -42.0% | +5.3% |
| BM_MergeInfos_Scale/64 | 10.3us | 6.3us | 6.5us | -36.7% | +2.7% |
| BM_MergeInfos_Scale/8 | 2.8us | 2.2us | 2.2us | -20.7% | +1.2% |
| BM_SerializeFunctionInfo | 25.5us | 25.8us | 26.1us | +2.1% | +1.1% |
2026-04-03 20:33:56 +00:00

348 lines
13 KiB
C++

//===-- clang-doc/BitcodeTest.cpp -----------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "BitcodeReader.h"
#include "BitcodeWriter.h"
#include "ClangDocTest.h"
#include "Representation.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Bitstream/BitstreamWriter.h"
#include "gtest/gtest.h"
namespace clang {
namespace doc {
template <typename T>
static std::string writeInfo(T &I, DiagnosticsEngine &Diags) {
SmallString<2048> Buffer;
llvm::BitstreamWriter Stream(Buffer);
ClangDocBitcodeWriter Writer(Stream, Diags);
Writer.emitBlock(I);
return Buffer.str().str();
}
static std::string writeInfo(Info *I, DiagnosticsEngine &Diags) {
switch (I->IT) {
case InfoType::IT_namespace:
return writeInfo(*static_cast<NamespaceInfo *>(I), Diags);
case InfoType::IT_record:
return writeInfo(*static_cast<RecordInfo *>(I), Diags);
case InfoType::IT_enum:
return writeInfo(*static_cast<EnumInfo *>(I), Diags);
case InfoType::IT_function:
return writeInfo(*static_cast<FunctionInfo *>(I), Diags);
case InfoType::IT_typedef:
return writeInfo(*static_cast<TypedefInfo *>(I), Diags);
case InfoType::IT_concept:
return writeInfo(*static_cast<ConceptInfo *>(I), Diags);
case InfoType::IT_variable:
return writeInfo(*static_cast<VarInfo *>(I), Diags);
case InfoType::IT_friend:
return writeInfo(*static_cast<FriendInfo *>(I), Diags);
case InfoType::IT_default:
return "";
}
}
static OwningPtrVec<Info> readInfo(StringRef Bitcode, size_t NumInfos,
DiagnosticsEngine &Diags) {
llvm::BitstreamCursor Stream(Bitcode);
doc::ClangDocBitcodeReader Reader(Stream, Diags);
auto Infos = Reader.readBitcode();
// Check that there was no error in the read.
assert(Infos);
EXPECT_EQ(Infos.get().size(), NumInfos);
return std::move(Infos.get());
}
class BitcodeTest : public ClangDocContextTest {};
TEST_F(BitcodeTest, emitNamespaceInfoBitcode) {
NamespaceInfo I;
I.Name = "r";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
Reference NewNamespace(EmptySID, "ChildNamespace", InfoType::IT_namespace);
I.Children.Namespaces.push_back(NewNamespace);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Enums.emplace_back();
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckNamespaceInfo(&I, InfoAsNamespace(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitRecordInfoBitcode) {
RecordInfo I;
I.Name = "r";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
I.TagType = TagTypeKind::Class;
I.IsTypeDef = true;
I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_public, true);
I.Bases.back().Children.Functions.emplace_back();
I.Bases.back().Members.emplace_back(TypeInfo("int"), "X",
AccessSpecifier::AS_private);
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
// Documentation for the data member.
CommentInfo TopComment;
TopComment.Kind = CommentKind::CK_FullComment;
TopComment.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Brief = TopComment.Children.back().get();
Brief->Kind = CommentKind::CK_ParagraphComment;
Brief->Children.emplace_back(allocatePtr<CommentInfo>());
Brief->Children.back()->Kind = CommentKind::CK_TextComment;
Brief->Children.back()->Name = "ParagraphComment";
Brief->Children.back()->Text = "Value of the thing.";
I.Bases.back().Members.back().Description.emplace_back(std::move(TopComment));
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Enums.emplace_back();
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckRecordInfo(&I, InfoAsRecord(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitFunctionInfoBitcode) {
FunctionInfo I;
I.Name = "f";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.ReturnType = TypeInfo("void");
I.Params.emplace_back(TypeInfo("int"), "P");
I.Access = AccessSpecifier::AS_none;
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckFunctionInfo(&I, InfoAsFunction(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitMethodInfoBitcode) {
FunctionInfo I;
I.Name = "f";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.ReturnType = TypeInfo("void");
I.Params.emplace_back(TypeInfo("int"), "P");
I.IsMethod = true;
I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
I.Access = AccessSpecifier::AS_public;
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckFunctionInfo(&I, InfoAsFunction(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitEnumInfoBitcode) {
EnumInfo I;
I.Name = "e";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.Members.emplace_back("X");
I.Scoped = true;
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckEnumInfo(&I, InfoAsEnum(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitTypedefInfoBitcode) {
TypedefInfo I;
I.Name = "MyInt";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Underlying = TypeInfo("unsigned");
I.IsUsing = true;
CommentInfo Top;
Top.Kind = CommentKind::CK_FullComment;
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *BlankLine = Top.Children.back().get();
BlankLine->Kind = CommentKind::CK_ParagraphComment;
BlankLine->Children.emplace_back(allocatePtr<CommentInfo>());
BlankLine->Children.back()->Kind = CommentKind::CK_TextComment;
I.Description.emplace_back(std::move(Top));
std::string WriteResult = writeInfo(&I, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckTypedefInfo(&I, InfoAsTypedef(ReadResults[0].get()));
// Check one with no IsUsing set, no description, and no definition location.
TypedefInfo I2;
I2.Name = "SomethingElse";
I2.IsUsing = false;
I2.Underlying = TypeInfo("int");
WriteResult = writeInfo(&I2, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckTypedefInfo(&I2, InfoAsTypedef(ReadResults[0].get()));
}
TEST_F(BitcodeTest, emitInfoWithCommentBitcode) {
FunctionInfo F;
F.Name = "F";
F.ReturnType = TypeInfo("void");
F.DefLoc = Location(0, 0, "test.cpp");
F.Params.emplace_back(TypeInfo("int"), "I");
CommentInfo Top;
Top.Kind = CommentKind::CK_FullComment;
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *BlankLine = Top.Children.back().get();
BlankLine->Kind = CommentKind::CK_ParagraphComment;
BlankLine->Children.emplace_back(allocatePtr<CommentInfo>());
BlankLine->Children.back()->Kind = CommentKind::CK_TextComment;
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Brief = Top.Children.back().get();
Brief->Kind = CommentKind::CK_ParagraphComment;
Brief->Children.emplace_back(allocatePtr<CommentInfo>());
Brief->Children.back()->Kind = CommentKind::CK_TextComment;
Brief->Children.back()->Name = "ParagraphComment";
Brief->Children.back()->Text = " Brief description.";
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Extended = Top.Children.back().get();
Extended->Kind = CommentKind::CK_ParagraphComment;
Extended->Children.emplace_back(allocatePtr<CommentInfo>());
Extended->Children.back()->Kind = CommentKind::CK_TextComment;
Extended->Children.back()->Text = " Extended description that";
Extended->Children.emplace_back(allocatePtr<CommentInfo>());
Extended->Children.back()->Kind = CommentKind::CK_TextComment;
Extended->Children.back()->Text = " continues onto the next line.";
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *HTML = Top.Children.back().get();
HTML->Kind = CommentKind::CK_ParagraphComment;
HTML->Children.emplace_back(allocatePtr<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_TextComment;
HTML->Children.emplace_back(allocatePtr<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLStartTagComment;
HTML->Children.back()->Name = "ul";
{
llvm::SmallVector<StringRef, 1> Keys = {"class"};
HTML->Children.back()->AttrKeys =
allocateArray<StringRef>(Keys, TransientArena);
llvm::SmallVector<StringRef, 1> Values = {"test"};
HTML->Children.back()->AttrValues =
allocateArray<StringRef>(Values, TransientArena);
}
HTML->Children.emplace_back(allocatePtr<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLStartTagComment;
HTML->Children.back()->Name = "li";
HTML->Children.emplace_back(allocatePtr<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_TextComment;
HTML->Children.back()->Text = " Testing.";
HTML->Children.emplace_back(allocatePtr<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLEndTagComment;
HTML->Children.back()->Name = "ul";
HTML->Children.back()->SelfClosing = true;
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Verbatim = Top.Children.back().get();
Verbatim->Kind = CommentKind::CK_VerbatimBlockComment;
Verbatim->Name = "verbatim";
Verbatim->CloseName = "endverbatim";
Verbatim->Children.emplace_back(allocatePtr<CommentInfo>());
Verbatim->Children.back()->Kind = CommentKind::CK_VerbatimBlockLineComment;
Verbatim->Children.back()->Text = " The description continues.";
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *ParamOut = Top.Children.back().get();
ParamOut->Kind = CommentKind::CK_ParamCommandComment;
ParamOut->Direction = "[out]";
ParamOut->ParamName = "I";
ParamOut->Explicit = true;
ParamOut->Children.emplace_back(allocatePtr<CommentInfo>());
ParamOut->Children.back()->Kind = CommentKind::CK_ParagraphComment;
ParamOut->Children.back()->Children.emplace_back(allocatePtr<CommentInfo>());
ParamOut->Children.back()->Children.back()->Kind =
CommentKind::CK_TextComment;
ParamOut->Children.back()->Children.emplace_back(allocatePtr<CommentInfo>());
ParamOut->Children.back()->Children.back()->Kind =
CommentKind::CK_TextComment;
ParamOut->Children.back()->Children.back()->Text = " is a parameter.";
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *ParamIn = Top.Children.back().get();
ParamIn->Kind = CommentKind::CK_ParamCommandComment;
ParamIn->Direction = "[in]";
ParamIn->ParamName = "J";
ParamIn->Children.emplace_back(allocatePtr<CommentInfo>());
ParamIn->Children.back()->Kind = CommentKind::CK_ParagraphComment;
ParamIn->Children.back()->Children.emplace_back(allocatePtr<CommentInfo>());
ParamIn->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
ParamIn->Children.back()->Children.back()->Text = " is a parameter.";
ParamIn->Children.back()->Children.emplace_back(allocatePtr<CommentInfo>());
ParamIn->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
Top.Children.emplace_back(allocatePtr<CommentInfo>());
CommentInfo *Return = Top.Children.back().get();
Return->Kind = CommentKind::CK_BlockCommandComment;
Return->Name = "return";
Return->Explicit = true;
Return->Children.emplace_back(allocatePtr<CommentInfo>());
Return->Children.back()->Kind = CommentKind::CK_ParagraphComment;
Return->Children.back()->Children.emplace_back(allocatePtr<CommentInfo>());
Return->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
Return->Children.back()->Children.back()->Text = "void";
F.Description.emplace_back(std::move(Top));
std::string WriteResult = writeInfo(&F, this->Diags);
EXPECT_TRUE(WriteResult.size() > 0);
OwningPtrVec<Info> ReadResults = readInfo(WriteResult, 1, this->Diags);
CheckFunctionInfo(&F, InfoAsFunction(ReadResults[0].get()));
}
} // namespace doc
} // namespace clang