llvm.org GIT mirror llvm / 04dcbb5
[Remarks] Add string deduplication using a string table * Add support for uniquing strings in the remark streamer and emitting the string table in the remarks section. * Add parsing support for the string table in the RemarkParser. From this remark: ``` --- !Missed Pass: inline Name: NoDefinition DebugLoc: { File: 'test-suite/SingleSource/UnitTests/2002-04-17-PrintfChar.c', Line: 7, Column: 3 } Function: printArgsNoRet Args: - Callee: printf - String: ' will not be inlined into ' - Caller: printArgsNoRet DebugLoc: { File: 'test-suite/SingleSource/UnitTests/2002-04-17-PrintfChar.c', Line: 6, Column: 0 } - String: ' because its definition is unavailable' ... ``` to: ``` --- !Missed Pass: 0 Name: 1 DebugLoc: { File: 3, Line: 7, Column: 3 } Function: 2 Args: - Callee: 4 - String: 5 - Caller: 2 DebugLoc: { File: 3, Line: 6, Column: 0 } - String: 6 ... ``` And the string table in the .remarks/__remarks section containing: ``` inline\0NoDefinition\0printArgsNoRet\0 test-suite/SingleSource/UnitTests/2002-04-17-PrintfChar.c\0printf\0 will not be inlined into \0 because its definition is unavailable\0 ``` This is mostly supposed to be used for testing purposes, but it gives us a 2x reduction in the remark size, and is an incremental change for the updates to the remarks file format. Differential Revision: https://reviews.llvm.org/D60227 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@359050 91177308-0d34-0410-b5e6-96231b3b80d8 Francis Visoiu Mistrih 3 months ago
19 changed file(s) with 456 addition(s) and 21 deletion(s). Raw diff Collapse all Expand all
15961596
15971597 * a magic number: "REMARKS\0"
15981598 * the version number: a little-endian uint64_t
1599 * the string table:
1600 * the total size of the string table (the size itself excluded):
1601 little-endian uint64_t
1602 * a list of null-terminated strings
15991603 * the absolute file path to the serialized remark diagnostics: a
16001604 null-terminated string.
16011605
1313 #define LLVM_IR_REMARKSTREAMER_H
1414
1515 #include "llvm/IR/DiagnosticInfo.h"
16 #include "llvm/Remarks/RemarkStringTable.h"
1617 #include "llvm/Support/Error.h"
18 #include "llvm/Support/Regex.h"
1719 #include "llvm/Support/YAMLTraits.h"
1820 #include "llvm/Support/raw_ostream.h"
19 #include "llvm/Support/Regex.h"
2021 #include
2122 #include
2223
3334 /// The YAML streamer.
3435 yaml::Output YAMLOutput;
3536
37 /// The string table containing all the unique strings used in the output.
38 /// The table will be serialized in a section to be consumed after the
39 /// compilation.
40 remarks::StringTable StrTab;
41
3642 public:
3743 RemarkStreamer(StringRef Filename, raw_ostream& OS);
3844 /// Return the filename that the remark diagnostics are emitted to.
4450 Error setFilter(StringRef Filter);
4551 /// Emit a diagnostic through the streamer.
4652 void emit(const DiagnosticInfoOptimizationBase &Diag);
53 /// The string table used during emission.
54 remarks::StringTable &getStringTable() { return StrTab; }
55 const remarks::StringTable &getStringTable() const { return StrTab; }
4756 };
4857 } // end namespace llvm
4958
1212 #ifndef LLVM_REMARKS_REMARK_PARSER_H
1313 #define LLVM_REMARKS_REMARK_PARSER_H
1414
15 #include "llvm/ADT/SmallVector.h"
1516 #include "llvm/ADT/StringRef.h"
1617 #include "llvm/Remarks/Remark.h"
1718 #include "llvm/Support/Error.h"
3132 /// This constructor should be only used for parsing YAML remarks.
3233 Parser(StringRef Buffer);
3334
35 /// Create a parser parsing \p Buffer to Remark objects, using \p StrTabBuf as
36 /// string table.
37 /// This constructor should be only used for parsing YAML remarks.
38 Parser(StringRef Buffer, StringRef StrTabBuf);
39
3440 // Needed because ParserImpl is an incomplete type.
3541 ~Parser();
3642
3945 Expected getNext() const;
4046 };
4147
48 /// In-memory representation of the string table parsed from a buffer (e.g. the
49 /// remarks section).
50 struct ParsedStringTable {
51 /// The buffer mapped from the section contents.
52 StringRef Buffer;
53 /// Collection of offsets in the buffer for each string entry.
54 SmallVector Offsets;
55
56 Expected operator[](size_t Index);
57 ParsedStringTable(StringRef Buffer);
58 };
59
4260 } // end namespace remarks
4361 } // end namespace llvm
4462
0 //===-- RemarkStringTable.h - Serializing string table ----------*- C++/-*-===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This class is used to deduplicate and serialize a string table used for
9 // generating remarks.
10 //
11 // For parsing a string table, use ParsedStringTable in RemarkParser.h
12 //
13 //===----------------------------------------------------------------------===//
14
15 #ifndef LLVM_REMARKS_REMARK_STRING_TABLE_H
16 #define LLVM_REMARKS_REMARK_STRING_TABLE_H
17
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/Allocator.h"
21 #include
22
23 namespace llvm {
24
25 class raw_ostream;
26
27 namespace remarks {
28
29 /// The string table used for serializing remarks.
30 /// This table can be for example serialized in a section to be consumed after
31 /// the compilation.
32 struct StringTable {
33 /// Allocator holding all the memory used by the map.
34 BumpPtrAllocator Allocator;
35 /// The string table containing all the unique strings used in the output.
36 /// It maps a string to an unique ID.
37 StringMap StrTab;
38 /// Total size of the string table when serialized.
39 size_t SerializedSize = 0;
40
41 StringTable() : Allocator(), StrTab(Allocator) {}
42 /// Add a string to the table. It returns an unique ID of the string.
43 std::pair add(StringRef Str);
44 /// Serialize the string table to a stream. It is serialized as a little
45 /// endian uint64 (the size of the table in bytes) followed by a sequence of
46 /// NULL-terminated strings, where the N-th string is the string with the ID N
47 /// in the StrTab map.
48 void serialize(raw_ostream &OS) const;
49 /// Serialize the string table to a vector. This allows users to do the actual
50 /// writing to file/memory/other.
51 /// The string with the ID == N should be the N-th element in the vector.
52 std::vector serialize() const;
53 };
54
55 } // end namespace remarks
56 } // end namespace llvm
57
58 #endif /* LLVM_REMARKS_REMARK_STRING_TABLE_H */
19041904 typename std::enable_if
19051905 SequenceElementTraits::flow>::value>::type>
19061906 : SequenceTraitsImpl, SequenceElementTraits::flow> {};
1907 template
1908 struct SequenceTraits,
1909 typename std::enable_if
1910 SequenceElementTraits::flow>::value>::type>
1911 : SequenceTraitsImpl, SequenceElementTraits::flow> {};
19071912
19081913 // Sequences of fundamental types use flow formatting.
19091914 template
13611361 support::endian::write64le(Version.data(), remarks::Version);
13621362 OutStreamer->EmitBinaryData(StringRef(Version.data(), Version.size()));
13631363
1364 // Emit the string table in the section.
1365 // Note: we need to use the streamer here to emit it in the section. We can't
1366 // just use the serialize function with a raw_ostream because of the way
1367 // MCStreamers work.
1368 const remarks::StringTable &StrTab = RS->getStringTable();
1369 std::vector StrTabStrings = StrTab.serialize();
1370 uint64_t StrTabSize = StrTab.SerializedSize;
1371 // Emit the total size of the string table (the size itself excluded):
1372 // little-endian uint64_t.
1373 // The total size is located after the version number.
1374 std::array StrTabSizeBuf;
1375 support::endian::write64le(StrTabSizeBuf.data(), StrTabSize);
1376 OutStreamer->EmitBinaryData(
1377 StringRef(StrTabSizeBuf.data(), StrTabSizeBuf.size()));
1378 // Emit a list of null-terminated strings.
1379 // Note: the order is important here: the ID used in the remarks corresponds
1380 // to the position of the string in the section.
1381 for (StringRef Str : StrTabStrings) {
1382 OutStreamer->EmitBytes(Str);
1383 // Explicitly emit a '\0'.
1384 OutStreamer->EmitIntValue(/*Value=*/0, /*Size=*/1);
1385 }
1386
13641387 // Emit the null-terminated absolute path to the remark file.
13651388 // The path is located at the offset 0x4 in the section.
13661389 StringRef FilenameRef = RS->getFilename();
1717 type = Library
1818 name = AsmPrinter
1919 parent = Libraries
20 required_libraries = Analysis BinaryFormat CodeGen Core DebugInfoCodeView DebugInfoDWARF DebugInfoMSF MC MCParser Support Target
20 required_libraries = Analysis BinaryFormat CodeGen Core DebugInfoCodeView DebugInfoDWARF DebugInfoMSF MC MCParser Remarks Support Target
4242
4343 using namespace llvm;
4444
45 cl::opt UseStringTable("remarks-yaml-string-table", cl::init(false));
46
4547 int llvm::getNextAvailablePluginDiagnosticKind() {
4648 static std::atomic PluginKindID(DK_FirstPluginKind);
4749 return ++PluginKindID;
371373
372374 void OptimizationRemarkAnalysisFPCommute::anchor() {}
373375 void OptimizationRemarkAnalysisAliasing::anchor() {}
376
377 template
378 static void mapRemarkHeader(
379 yaml::IO &io, T PassName, T RemarkName, DiagnosticLocation DL,
380 T FunctionName, Optional Hotness,
381 SmallVectorImpl &Args) {
382 io.mapRequired("Pass", PassName);
383 io.mapRequired("Name", RemarkName);
384 if (!io.outputting() || DL.isValid())
385 io.mapOptional("DebugLoc", DL);
386 io.mapRequired("Function", FunctionName);
387 io.mapOptional("Hotness", Hotness);
388 io.mapOptional("Args", Args);
389 }
374390
375391 namespace llvm {
376392 namespace yaml {
412428 GlobalValue::dropLLVMManglingEscape(OptDiag->getFunction().getName());
413429
414430 StringRef PassName(OptDiag->PassName);
415 io.mapRequired("Pass", PassName);
416 io.mapRequired("Name", OptDiag->RemarkName);
417 if (!io.outputting() || DL.isValid())
418 io.mapOptional("DebugLoc", DL);
419 io.mapRequired("Function", FN);
420 io.mapOptional("Hotness", OptDiag->Hotness);
421 io.mapOptional("Args", OptDiag->Args);
431 if (UseStringTable) {
432 remarks::StringTable &StrTab =
433 reinterpret_cast(io.getContext())->getStringTable();
434 unsigned PassID = StrTab.add(PassName).first;
435 unsigned NameID = StrTab.add(OptDiag->RemarkName).first;
436 unsigned FunctionID = StrTab.add(FN).first;
437 mapRemarkHeader(io, PassID, NameID, DL, FunctionID, OptDiag->Hotness,
438 OptDiag->Args);
439 } else {
440 mapRemarkHeader(io, PassName, OptDiag->RemarkName, DL, FN, OptDiag->Hotness,
441 OptDiag->Args);
442 }
422443 }
423444
424445 template <> struct MappingTraits {
429450 unsigned Line = DL.getLine();
430451 unsigned Col = DL.getColumn();
431452
432 io.mapRequired("File", File);
453 if (UseStringTable) {
454 remarks::StringTable &StrTab =
455 reinterpret_cast(io.getContext())->getStringTable();
456 unsigned FileID = StrTab.add(File).first;
457 io.mapRequired("File", FileID);
458 } else {
459 io.mapRequired("File", File);
460 }
461
433462 io.mapRequired("Line", Line);
434463 io.mapRequired("Column", Col);
435464 }
458487 template <> struct MappingTraits {
459488 static void mapping(IO &io, DiagnosticInfoOptimizationBase::Argument &A) {
460489 assert(io.outputting() && "input not yet implemented");
461 // Emit a string block scalar for multiline strings, to preserve newlines.
462 if (StringRef(A.Val).count('\n') > 1) {
490
491 if (UseStringTable) {
492 remarks::StringTable &StrTab =
493 reinterpret_cast(io.getContext())->getStringTable();
494 auto ValueID = StrTab.add(A.Val).first;
495 io.mapRequired(A.Key.data(), ValueID);
496 } else if (StringRef(A.Val).count('\n') > 1) {
463497 StringBlockVal S(A.Val);
464498 io.mapRequired(A.Key.data(), S);
465 } else
499 } else {
466500 io.mapRequired(A.Key.data(), A.Val);
501 }
467502 if (A.Loc.isValid())
468503 io.mapOptional("DebugLoc", A.Loc);
469504 }
1717 type = Library
1818 name = Core
1919 parent = Libraries
20 required_libraries = BinaryFormat Support
20 required_libraries = BinaryFormat Remarks Support
1616
1717 RemarkStreamer::RemarkStreamer(StringRef Filename, raw_ostream &OS)
1818 : Filename(Filename), OS(OS),
19 YAMLOutput(OS, reinterpret_cast(this)) {
19 YAMLOutput(OS, reinterpret_cast(this)), StrTab() {
2020 assert(!Filename.empty() && "This needs to be a real filename.");
2121 }
2222
0 add_llvm_library(LLVMRemarks
11 Remark.cpp
22 RemarkParser.cpp
3 RemarkStringTable.cpp
34 YAMLRemarkParser.cpp
45 )
2020 using namespace llvm::remarks;
2121
2222 Parser::Parser(StringRef Buf) : Impl(llvm::make_unique(Buf)) {}
23
24 Parser::Parser(StringRef Buf, StringRef StrTabBuf)
25 : Impl(llvm::make_unique(Buf, StrTabBuf)) {}
2326
2427 Parser::~Parser() = default;
2528
5356 if (auto *Impl = dyn_cast(this->Impl.get()))
5457 return getNextYAML(*Impl);
5558 llvm_unreachable("Get next called with an unknown parsing implementation.");
59 }
60
61 ParsedStringTable::ParsedStringTable(StringRef InBuffer) : Buffer(InBuffer) {
62 while (!InBuffer.empty()) {
63 // Strings are separated by '\0' bytes.
64 std::pair Split = InBuffer.split('\0');
65 // We only store the offset from the beginning of the buffer.
66 Offsets.push_back(Split.first.data() - Buffer.data());
67 InBuffer = Split.second;
68 }
69 }
70
71 Expected ParsedStringTable::operator[](size_t Index) {
72 if (Index >= Offsets.size())
73 return createStringError(
74 std::make_error_code(std::errc::invalid_argument),
75 "String with index %u is out of bounds (size = %u).", Index,
76 Offsets.size());
77
78 size_t Offset = Offsets[Index];
79 // If it's the last offset, we can't use the next offset to know the size of
80 // the string.
81 size_t NextOffset =
82 (Index == Offsets.size() - 1) ? Buffer.size() : Offsets[Index + 1];
83 return StringRef(Buffer.data() + Offset, NextOffset - Offset - 1);
5684 }
5785
5886 // Create wrappers for C Binding types (see CBindingWrapping.h).
0 //===- RemarkStringTable.cpp ----------------------------------------------===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // Implementation of the Remark string table used at remark generation.
9 //
10 //===----------------------------------------------------------------------===//
11
12 #include "llvm/Remarks/RemarkStringTable.h"
13 #include "llvm/Support/EndianStream.h"
14 #include "llvm/Support/Error.h"
15 #include
16
17 using namespace llvm;
18 using namespace llvm::remarks;
19
20 std::pair StringTable::add(StringRef Str) {
21 size_t NextID = StrTab.size();
22 auto KV = StrTab.insert({Str, NextID});
23 // If it's a new string, add it to the final size.
24 if (KV.second)
25 SerializedSize += KV.first->first().size() + 1; // +1 for the '\0'
26 // Can be either NextID or the previous ID if the string is already there.
27 return {KV.first->second, KV.first->first()};
28 }
29
30 void StringTable::serialize(raw_ostream &OS) const {
31 // Emit the number of strings.
32 uint64_t StrTabSize = SerializedSize;
33 support::endian::write(OS, StrTabSize, support::little);
34 // Emit the sequence of strings.
35 for (StringRef Str : serialize()) {
36 OS << Str;
37 // Explicitly emit a '\0'.
38 OS.write('\0');
39 }
40 }
41
42 std::vector StringTable::serialize() const {
43 std::vector Strings{StrTab.size()};
44 for (const auto &KV : StrTab)
45 Strings[KV.second] = KV.first();
46 return Strings;
47 }
3333 auto *Value = dyn_cast(Node.getValue());
3434 if (!Value)
3535 return make_error("expected a value of scalar type.", Node);
36 StringRef Tmp = Value->getRawValue();
36 StringRef Tmp;
37 if (!StrTab) {
38 Tmp = Value->getRawValue();
39 } else {
40 // If we have a string table, parse it as an unsigned.
41 unsigned StrID = 0;
42 if (Error E = parseUnsigned(StrID, Node))
43 return E;
44 if (Expected Str = (*StrTab)[StrID])
45 Tmp = *Str;
46 else
47 return Str.takeError();
48 }
3749
3850 if (Tmp.front() == '\'')
3951 Tmp = Tmp.drop_front();
1616 #include "llvm/ADT/Optional.h"
1717 #include "llvm/ADT/SmallVector.h"
1818 #include "llvm/Remarks/Remark.h"
19 #include "llvm/Remarks/RemarkParser.h"
1920 #include "llvm/Support/Error.h"
2021 #include "llvm/Support/SourceMgr.h"
2122 #include "llvm/Support/YAMLParser.h"
3738 raw_string_ostream ErrorStream;
3839 /// Temporary parsing buffer for the arguments.
3940 SmallVector TmpArgs;
40
41 /// The string table used for parsing strings.
42 Optional StrTab;
4143 /// The state used by the parser to parse a remark entry. Invalidated with
4244 /// every call to `parseYAMLElement`.
4345 struct ParseState {
5658 /// not be containing any value.
5759 Optional State;
5860
59 YAMLRemarkParser(StringRef Buf)
61 YAMLRemarkParser(StringRef Buf, Optional StrTabBuf = None)
6062 : SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString),
61 TmpArgs() {
63 TmpArgs(), StrTab() {
6264 SM.setDiagHandler(YAMLRemarkParser::HandleDiagnostic, this);
65
66 if (StrTabBuf)
67 StrTab.emplace(*StrTabBuf);
6368 }
6469
6570 /// Parse a YAML element.
121126 /// Set to `true` if we had any errors during parsing.
122127 bool HasErrors = false;
123128
124 YAMLParserImpl(StringRef Buf)
125 : ParserImpl{ParserImpl::Kind::YAML}, YAMLParser(Buf),
129 YAMLParserImpl(StringRef Buf, Optional StrTabBuf = None)
130 : ParserImpl{ParserImpl::Kind::YAML}, YAMLParser(Buf, StrTabBuf),
126131 YAMLIt(YAMLParser.Stream.begin()), HasErrors(false) {}
127132
128133 static bool classof(const ParserImpl *PI) {
0 ; RUN: llc < %s -mtriple=x86_64-linux -remarks-section -pass-remarks-output=%/t.yaml | FileCheck -DPATH=%/t.yaml %s
11 ; RUN: llc < %s -mtriple=x86_64-darwin -remarks-section -pass-remarks-output=%/t.yaml | FileCheck --check-prefix=CHECK-DARWIN -DPATH=%/t.yaml %s
2 ; RUN: llc < %s -mtriple=x86_64-darwin -remarks-section -remarks-yaml-string-table -pass-remarks-output=%/t.yaml | FileCheck --check-prefix=CHECK-DARWIN-STRTAB -DPATH=%/t.yaml %s
23
34 ; CHECK-LABEL: func1:
45
1011 ; The version:
1112 ; CHECK-NEXT: .byte 0x00, 0x00, 0x00, 0x00
1213 ; CHECK-NEXT: .byte 0x00, 0x00, 0x00, 0x00
14 ; The string table size:
15 ; CHECK-NEXT: .byte 0x00, 0x00, 0x00, 0x00
16 ; CHECK-NEXT: .byte 0x00, 0x00, 0x00, 0x00
17 ; The string table:
18 ; EMPTY
1319 ; The remark file path:
1420 ; CHECK-NEXT: .ascii "[[PATH]]"
1521 ; Null-terminator:
2329 ; The version:
2430 ; CHECK-DARWIN-NEXT: .byte 0x00, 0x00, 0x00, 0x00
2531 ; CHECK-DARWIN-NEXT: .byte 0x00, 0x00, 0x00, 0x00
32 ; The string table size:
33 ; CHECK-DARWIN-NEXT: .byte 0x00, 0x00, 0x00, 0x00
34 ; CHECK-DARWIN-NEXT: .byte 0x00, 0x00, 0x00, 0x00
35 ; The string table:
36 ; EMPTY
2637 ; The remark file path:
2738 ; CHECK-DARWIN-NEXT: .ascii "[[PATH]]"
2839 ; Null-terminator:
2940 ; CHECK-DARWIN-NEXT: .byte 0
41
42 ; CHECK-DARWIN-STRTAB: .section __LLVM,__remarks,regular,debug
43 ; The magic number:
44 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "REMARKS"
45 ; Null-terminator:
46 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
47 ; The version:
48 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0x00, 0x00, 0x00, 0x00
49 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0x00, 0x00, 0x00, 0x00
50 ; The size of the string table:
51 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0x71, 0x00, 0x00, 0x00
52 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0x00, 0x00, 0x00, 0x00
53 ; The string table:
54 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "prologepilog"
55 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
56 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "StackSize"
57 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
58 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "func1"
59 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
60 ; CHECK-DARWIN-STRTAB-NEXT: .byte 48
61 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
62 ; CHECK-DARWIN-STRTAB-NEXT: .ascii " stack bytes in function"
63 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
64 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "asm-printer"
65 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
66 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "InstructionCount"
67 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
68 ; CHECK-DARWIN-STRTAB-NEXT: .byte 49
69 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
70 ; CHECK-DARWIN-STRTAB-NEXT: .ascii " instructions in function"
71 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
72 ; The remark file path:
73 ; CHECK-DARWIN-STRTAB-NEXT: .ascii "[[PATH]]"
74 ; Null-terminator:
75 ; CHECK-DARWIN-STRTAB-NEXT: .byte 0
3076 define void @func1() {
3177 ret void
3278 }
33 )
44
55 add_llvm_unittest(RemarksTests
6 RemarksStrTabParsingTest.cpp
67 YAMLRemarksParsingTest.cpp
78 )
0 //===- unittest/Support/RemarksStrTabParsingTest.cpp - StrTab tests -------===//
1 //
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7
8 #include "llvm/Remarks/Remark.h"
9 #include "llvm/Remarks/RemarkParser.h"
10 #include "gtest/gtest.h"
11
12 using namespace llvm;
13
14 TEST(RemarksStrTab, ParsingEmpty) {
15 StringRef Empty("", 0);
16 remarks::ParsedStringTable StrTab(Empty);
17 Expected Nothing = StrTab[0];
18 EXPECT_FALSE(static_cast(Nothing));
19 EXPECT_EQ(toString(Nothing.takeError()),
20 "String with index 0 is out of bounds (size = 0).");
21 }
22
23 TEST(RemarksStrTab, ParsingGood) {
24 StringRef Strings("str1\0str2\0str3\0str4", 20);
25 remarks::ParsedStringTable StrTab(Strings);
26 Expected Result = StrTab[0];
27 EXPECT_TRUE(static_cast(Result));
28 EXPECT_EQ(*Result, "str1");
29 Result = StrTab[1];
30 EXPECT_TRUE(static_cast(Result));
31 EXPECT_EQ(*Result, "str2");
32 Result = StrTab[2];
33 EXPECT_TRUE(static_cast(Result));
34 EXPECT_EQ(*Result, "str3");
35 Result = StrTab[3];
36 EXPECT_TRUE(static_cast(Result));
37 EXPECT_EQ(*Result, "str4");
38 }
491491 EXPECT_FALSE(LLVMRemarkParserHasError(Parser));
492492 LLVMRemarkParserDispose(Parser);
493493 }
494
495 TEST(YAMLRemarks, ContentsStrTab) {
496 StringRef Buf = "\n"
497 "--- !Missed\n"
498 "Pass: 0\n"
499 "Name: 1\n"
500 "DebugLoc: { File: 2, Line: 3, Column: 12 }\n"
501 "Function: 3\n"
502 "Hotness: 4\n"
503 "Args:\n"
504 " - Callee: 5\n"
505 " - String: 7\n"
506 " - Caller: 3\n"
507 " DebugLoc: { File: 2, Line: 2, Column: 0 }\n"
508 " - String: 8\n"
509 "\n";
510
511 StringRef StrTabBuf =
512 StringRef("inline\0NoDefinition\0file.c\0foo\0Callee\0bar\0String\0 "
513 "will not be inlined into \0 because its definition is "
514 "unavailable",
515 115);
516
517 remarks::Parser Parser(Buf, StrTabBuf);
518 Expected RemarkOrErr = Parser.getNext();
519 EXPECT_FALSE(errorToBool(RemarkOrErr.takeError()));
520 EXPECT_TRUE(*RemarkOrErr != nullptr);
521
522 const remarks::Remark &Remark = **RemarkOrErr;
523 EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
524 EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
525 EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
526 EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
527 EXPECT_TRUE(Remark.Loc);
528 const remarks::RemarkLocation &RL = *Remark.Loc;
529 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
530 EXPECT_EQ(RL.SourceLine, 3U);
531 EXPECT_EQ(RL.SourceColumn, 12U);
532 EXPECT_TRUE(Remark.Hotness);
533 EXPECT_EQ(*Remark.Hotness, 4U);
534 EXPECT_EQ(Remark.Args.size(), 4U);
535
536 unsigned ArgID = 0;
537 for (const remarks::Argument &Arg : Remark.Args) {
538 switch (ArgID) {
539 case 0:
540 EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
541 EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
542 EXPECT_FALSE(Arg.Loc);
543 break;
544 case 1:
545 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
546 EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
547 EXPECT_FALSE(Arg.Loc);
548 break;
549 case 2: {
550 EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
551 EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
552 EXPECT_TRUE(Arg.Loc);
553 const remarks::RemarkLocation &RL = *Arg.Loc;
554 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
555 EXPECT_EQ(RL.SourceLine, 2U);
556 EXPECT_EQ(RL.SourceColumn, 0U);
557 break;
558 }
559 case 3:
560 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
561 EXPECT_EQ(checkStr(Arg.Val, 38),
562 " because its definition is unavailable");
563 EXPECT_FALSE(Arg.Loc);
564 break;
565 default:
566 break;
567 }
568 ++ArgID;
569 }
570
571 RemarkOrErr = Parser.getNext();
572 EXPECT_FALSE(errorToBool(RemarkOrErr.takeError()));
573 EXPECT_EQ(*RemarkOrErr, nullptr);
574 }
575
576 TEST(YAMLRemarks, ParsingBadStringTableIndex) {
577 StringRef Buf = "\n"
578 "--- !Missed\n"
579 "Pass: 50\n"
580 "\n";
581
582 StringRef StrTabBuf = StringRef("inline");
583
584 remarks::Parser Parser(Buf, StrTabBuf);
585 Expected Remark = Parser.getNext();
586 EXPECT_FALSE(Remark); // Expect an error here.
587
588 std::string ErrorStr;
589 raw_string_ostream Stream(ErrorStr);
590 handleAllErrors(Remark.takeError(),
591 [&](const ErrorInfoBase &EIB) { EIB.log(Stream); });
592 EXPECT_TRUE(
593 StringRef(Stream.str())
594 .contains("String with index 50 is out of bounds (size = 1)."));
595 }