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% |
348 lines
13 KiB
C++
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
|