LLVM 23.0.0git
YAMLRemarkParser.cpp
Go to the documentation of this file.
1//===- YAMLRemarkParser.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file provides utility methods used by clients that want to use the
10// parser for remark diagnostics in LLVM.
11//
12//===----------------------------------------------------------------------===//
13
14#include "YAMLRemarkParser.h"
17#include "llvm/Support/Endian.h"
18#include "llvm/Support/Path.h"
19#include <optional>
20
21using namespace llvm;
22using namespace llvm::remarks;
23
24char YAMLParseError::ID = 0;
25
26static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
27 assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
28 std::string &Message = *static_cast<std::string *>(Ctx);
29 assert(Message.empty() && "Expected an empty string.");
30 raw_string_ostream OS(Message);
31 Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false,
32 /*ShowKindLabels*/ true);
33 OS << '\n';
34}
35
37 yaml::Stream &Stream, yaml::Node &Node) {
38 // 1) Set up a diagnostic handler to avoid errors being printed out to
39 // stderr.
40 // 2) Use the stream to print the error with the associated node.
41 // 3) The stream will use the source manager to print the error, which will
42 // call the diagnostic handler.
43 // 4) The diagnostic handler will stream the error directly into this object's
44 // Message member, which is used when logging is asked for.
45 auto OldDiagHandler = SM.getDiagHandler();
46 auto OldDiagCtx = SM.getDiagContext();
47 SM.setDiagHandler(handleDiagnostic, &Message);
48 Stream.printError(&Node, Twine(Msg) + Twine('\n'));
49 // Restore the old handlers.
50 SM.setDiagHandler(OldDiagHandler, OldDiagCtx);
51}
52
53static SourceMgr setupSM(std::string &LastErrorMessage) {
54 SourceMgr SM;
55 SM.setDiagHandler(handleDiagnostic, &LastErrorMessage);
56 return SM;
57}
58
59// Parse the magic number. This function returns true if this represents remark
60// metadata, false otherwise.
63 return false;
64
65 if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1)))
66 return createStringError(std::errc::illegal_byte_sequence,
67 "Expecting \\0 after magic number.");
68 return true;
69}
70
72 if (Buf.size() < sizeof(uint64_t))
73 return createStringError(std::errc::illegal_byte_sequence,
74 "Expecting version number.");
75
76 uint64_t Version =
78 if (Version != remarks::CurrentRemarkVersion)
79 return createStringError(std::errc::illegal_byte_sequence,
80 "Mismatching remark version. Got %" PRId64
81 ", expected %" PRId64 ".",
83 Buf = Buf.drop_front(sizeof(uint64_t));
84 return Version;
85}
86
88 if (Buf.size() < sizeof(uint64_t))
89 return createStringError(std::errc::illegal_byte_sequence,
90 "Expecting string table size.");
91 uint64_t StrTabSize =
93 Buf = Buf.drop_front(sizeof(uint64_t));
94 return StrTabSize;
95}
96
98 StringRef Buf, std::optional<StringRef> ExternalFilePrependPath) {
99 // We now have a magic number. The metadata has to be correct.
100 Expected<bool> isMeta = parseMagic(Buf);
101 if (!isMeta)
102 return isMeta.takeError();
103 // If it's not recognized as metadata, roll back.
104 std::unique_ptr<MemoryBuffer> SeparateBuf;
105 if (*isMeta) {
107 if (!Version)
108 return Version.takeError();
109
110 Expected<uint64_t> StrTabSize = parseStrTabSize(Buf);
111 if (!StrTabSize)
112 return StrTabSize.takeError();
113
114 if (*StrTabSize != 0) {
115 return createStringError(std::errc::illegal_byte_sequence,
116 "String table unsupported for YAML format.");
117 }
118 // If it starts with "---", there is no external file.
119 if (!Buf.starts_with("---")) {
120 // At this point, we expect Buf to contain the external file path.
121 StringRef ExternalFilePath = Buf;
122 SmallString<80> FullPath;
123 if (ExternalFilePrependPath)
124 FullPath = *ExternalFilePrependPath;
125 sys::path::append(FullPath, ExternalFilePath);
126
127 // Try to open the file and start parsing from there.
129 MemoryBuffer::getFile(FullPath);
130 if (std::error_code EC = BufferOrErr.getError())
131 return createFileError(FullPath, EC);
132
133 // Keep the buffer alive.
134 SeparateBuf = std::move(*BufferOrErr);
135 Buf = SeparateBuf->getBuffer();
136 }
137 }
138
139 std::unique_ptr<YAMLRemarkParser> Result =
140 std::make_unique<YAMLRemarkParser>(Buf);
141 if (SeparateBuf)
142 Result->SeparateBuf = std::move(SeparateBuf);
143 return std::move(Result);
144}
145
149
153
155 if (LastErrorMessage.empty())
156 return Error::success();
158 LastErrorMessage.clear();
159 return E;
160}
161
164 if (Error E = error())
165 return std::move(E);
166
167 yaml::Node *YAMLRoot = RemarkEntry.getRoot();
168 if (!YAMLRoot) {
169 return createStringError(std::make_error_code(std::errc::invalid_argument),
170 "not a valid YAML file.");
171 }
172
173 auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot);
174 if (!Root)
175 return error("document root is not of mapping type.", *YAMLRoot);
176
177 std::unique_ptr<Remark> Result = std::make_unique<Remark>();
178 Remark &TheRemark = *Result;
179
180 // First, the type. It needs special handling since is not part of the
181 // key-value stream.
182 Expected<Type> T = parseType(*Root);
183 if (!T)
184 return T.takeError();
185
186 TheRemark.RemarkType = *T;
187
188 // Then, parse the fields, one by one.
189 for (yaml::KeyValueNode &RemarkField : *Root) {
190 Expected<StringRef> MaybeKey = parseKey(RemarkField);
191 if (!MaybeKey)
192 return MaybeKey.takeError();
193 StringRef KeyName = *MaybeKey;
194
195 if (KeyName == "Pass") {
196 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
197 TheRemark.PassName = *MaybeStr;
198 else
199 return MaybeStr.takeError();
200 } else if (KeyName == "Name") {
201 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
202 TheRemark.RemarkName = *MaybeStr;
203 else
204 return MaybeStr.takeError();
205 } else if (KeyName == "Function") {
206 if (Expected<StringRef> MaybeStr = parseStr(RemarkField))
207 TheRemark.FunctionName = *MaybeStr;
208 else
209 return MaybeStr.takeError();
210 } else if (KeyName == "Hotness") {
211 if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField))
212 TheRemark.Hotness = *MaybeU;
213 else
214 return MaybeU.takeError();
215 } else if (KeyName == "DebugLoc") {
216 if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField))
217 TheRemark.Loc = *MaybeLoc;
218 else
219 return MaybeLoc.takeError();
220 } else if (KeyName == "Args") {
221 auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
222 if (!Args)
223 return error("wrong value type for key.", RemarkField);
224
225 for (yaml::Node &Arg : *Args) {
226 if (Expected<Argument> MaybeArg = parseArg(Arg))
227 TheRemark.Args.push_back(*MaybeArg);
228 else
229 return MaybeArg.takeError();
230 }
231 } else {
232 return error("unknown key.", RemarkField);
233 }
234 }
235
236 // Check if any of the mandatory fields are missing.
237 if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() ||
238 TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty())
239 return error("Type, Pass, Name or Function missing.",
240 *RemarkEntry.getRoot());
241
242 return std::move(Result);
243}
244
246 auto Type = StringSwitch<remarks::Type>(Node.getRawTag())
247 .Case("!Passed", remarks::Type::Passed)
248 .Case("!Missed", remarks::Type::Missed)
249 .Case("!Analysis", remarks::Type::Analysis)
250 .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute)
251 .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing)
252 .Case("!Failure", remarks::Type::Failure)
255 return error("expected a remark tag.", Node);
256 return Type;
257}
258
260 if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()))
261 return Key->getRawValue();
262
263 return error("key is not a string.", Node);
264}
265
267 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
268 yaml::BlockScalarNode *ValueBlock;
269 StringRef Result;
270 if (!Value) {
271 // Try to parse the value as a block node.
272 ValueBlock = dyn_cast<yaml::BlockScalarNode>(Node.getValue());
273 if (!ValueBlock)
274 return error("expected a value of scalar type.", Node);
275 Result = ValueBlock->getValue();
276 } else
277 Result = Value->getRawValue();
278
279 Result.consume_front("\'");
280 Result.consume_back("\'");
281
282 return Result;
283}
284
287 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
288 if (!Value)
289 return error("expected a value of scalar type.", Node);
290 unsigned UnsignedValue = 0;
291 if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
292 return error("expected a value of integer type.", *Value);
293 return UnsignedValue;
294}
295
298 auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
299 if (!DebugLoc)
300 return error("expected a value of mapping type.", Node);
301
302 std::optional<StringRef> File;
303 std::optional<unsigned> Line;
304 std::optional<unsigned> Column;
305
306 for (yaml::KeyValueNode &DLNode : *DebugLoc) {
307 Expected<StringRef> MaybeKey = parseKey(DLNode);
308 if (!MaybeKey)
309 return MaybeKey.takeError();
310 StringRef KeyName = *MaybeKey;
311
312 if (KeyName == "File") {
313 if (Expected<StringRef> MaybeStr = parseStr(DLNode))
314 File = *MaybeStr;
315 else
316 return MaybeStr.takeError();
317 } else if (KeyName == "Column") {
318 if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
319 Column = *MaybeU;
320 else
321 return MaybeU.takeError();
322 } else if (KeyName == "Line") {
323 if (Expected<unsigned> MaybeU = parseUnsigned(DLNode))
324 Line = *MaybeU;
325 else
326 return MaybeU.takeError();
327 } else {
328 return error("unknown entry in DebugLoc map.", DLNode);
329 }
330 }
331
332 // If any of the debug loc fields is missing, return an error.
333 if (!File || !Line || !Column)
334 return error("DebugLoc node incomplete.", Node);
335
336 return RemarkLocation{*File, *Line, *Column};
337}
338
340 auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
341 if (!ArgMap)
342 return error("expected a value of mapping type.", Node);
343
344 std::optional<StringRef> KeyStr;
345 std::optional<StringRef> ValueStr;
346 std::optional<RemarkLocation> Loc;
347
348 for (yaml::KeyValueNode &ArgEntry : *ArgMap) {
349 Expected<StringRef> MaybeKey = parseKey(ArgEntry);
350 if (!MaybeKey)
351 return MaybeKey.takeError();
352 StringRef KeyName = *MaybeKey;
353
354 // Try to parse debug locs.
355 if (KeyName == "DebugLoc") {
356 // Can't have multiple DebugLoc entries per argument.
357 if (Loc)
358 return error("only one DebugLoc entry is allowed per argument.",
359 ArgEntry);
360
361 if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) {
362 Loc = *MaybeLoc;
363 continue;
364 } else
365 return MaybeLoc.takeError();
366 }
367
368 // If we already have a string, error out.
369 if (ValueStr)
370 return error("only one string entry is allowed per argument.", ArgEntry);
371
372 // Try to parse the value.
373 if (Expected<StringRef> MaybeStr = parseStr(ArgEntry))
374 ValueStr = *MaybeStr;
375 else
376 return MaybeStr.takeError();
377
378 // Keep the key from the string.
379 KeyStr = KeyName;
380 }
381
382 if (!KeyStr)
383 return error("argument key is missing.", *ArgMap);
384 if (!ValueStr)
385 return error("argument value is missing.", *ArgMap);
386
387 Argument Arg;
388 Arg.Key = *KeyStr;
389 Arg.Val = *ValueStr;
390 Arg.Loc = Loc;
391 return Arg;
392}
393
395 if (YAMLIt == Stream.end())
397
399 if (!MaybeResult) {
400 // Avoid garbage input, set the iterator to the end.
401 YAMLIt = Stream.end();
402 return MaybeResult.takeError();
403 }
404
405 ++YAMLIt;
406
407 return std::move(*MaybeResult);
408}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static Version parseVersion(StringRef Name)
#define T
This file defines the SmallString class.
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
static SourceMgr setupSM(std::string &LastErrorMessage)
static Expected< uint64_t > parseStrTabSize(StringRef &Buf)
static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx)
static Expected< bool > parseMagic(StringRef &Buf)
A debug info location.
Definition DebugLoc.h:123
Represents either an error or a value T.
Definition ErrorOr.h:56
std::error_code getError() const
Definition ErrorOr.h:152
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
Instances of this class encapsulate one diagnostic report, allowing printing to a raw_ostream as a ca...
Definition SourceMgr.h:297
LLVM_ABI void print(const char *ProgName, raw_ostream &S, bool ShowColors=true, bool ShowKindLabel=true, bool ShowLocation=true) const
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
This owns the files read by a parser, handles include stacks, and handles diagnostic wrangling.
Definition SourceMgr.h:37
void * getDiagContext() const
Definition SourceMgr.h:134
DiagHandlerTy getDiagHandler() const
Definition SourceMgr.h:133
void setDiagHandler(DiagHandlerTy DH, void *Ctx=nullptr)
Specify a diagnostic handler to be invoked every time PrintMessage is called.
Definition SourceMgr.h:128
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:261
constexpr bool empty() const
empty - Check if the string is empty.
Definition StringRef.h:143
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition StringRef.h:611
constexpr size_t size() const
size - Get the string size.
Definition StringRef.h:146
constexpr const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition StringRef.h:140
bool consume_front(char Prefix)
Returns true if this StringRef has the given prefix and removes that prefix.
Definition StringRef.h:637
A switch()-like statement whose cases are string literals.
StringSwitch & Case(StringLiteral S, T Value)
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
LLVM Value Representation.
Definition Value.h:75
A raw_ostream that writes to an std::string.
YAMLParseError(StringRef Message, SourceMgr &SM, yaml::Stream &Stream, yaml::Node &Node)
A block scalar node is an opaque datum that can be presented as a series of zero or more Unicode scal...
Definition YAMLParser.h:262
StringRef getValue() const
Gets the value of this node as a StringRef.
Definition YAMLParser.h:275
A YAML Stream is a sequence of Documents.
Definition YAMLParser.h:538
Node * getRoot()
Parse and return the root level node.
Definition YAMLParser.h:550
A key and value pair.
Definition YAMLParser.h:292
Represents a YAML map created from either a block map for a flow map.
Definition YAMLParser.h:421
Abstract base class for all Nodes.
Definition YAMLParser.h:121
This class represents a YAML stream potentially containing multiple documents.
Definition YAMLParser.h:88
LLVM_ABI void printError(Node *N, const Twine &Msg, SourceMgr::DiagKind Kind=SourceMgr::DK_Error)
constexpr StringLiteral Magic("REMARKS")
Format
The format used for serializing/deserializing remarks.
constexpr uint64_t CurrentRemarkVersion
The current version of the remark entry.
Definition Remark.h:29
Expected< std::unique_ptr< YAMLRemarkParser > > createYAMLParserFromMeta(StringRef Buf, std::optional< StringRef > ExternalFilePrependPath=std::nullopt)
Type
The type of the remark.
Definition Remark.h:75
value_type read(const void *memory, endianness endian)
Read a value of a particular endianness from memory.
Definition Endian.h:60
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:457
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition Error.h:1399
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
FunctionAddr VTableAddr uintptr_t uintptr_t Version
Definition InstrProf.h:302
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
A key-value pair with a debug location that is used to display the remarks at the right place in the ...
Definition Remark.h:47
std::optional< RemarkLocation > Loc
Definition Remark.h:52
The debug location used to track a remark back to the source file.
Definition Remark.h:32
RemarkParser(Format ParserFormat)
A remark type used for both emission and parsing.
Definition Remark.h:107
Type RemarkType
The type of the remark.
Definition Remark.h:109
StringRef PassName
Name of the pass that triggers the emission of this remark.
Definition Remark.h:112
std::optional< RemarkLocation > Loc
The location in the source file of the remark.
Definition Remark.h:123
std::optional< uint64_t > Hotness
If profile information is available, this is the number of times the corresponding code was executed ...
Definition Remark.h:127
StringRef RemarkName
Textual identifier for the remark (single-word, camel-case).
Definition Remark.h:117
StringRef FunctionName
Mangled name of the function that triggers the emssion of this remark.
Definition Remark.h:120
SmallVector< Argument, 5 > Args
Arguments collected via the streaming interface.
Definition Remark.h:130
yaml::document_iterator YAMLIt
Iterator in the YAML stream.
Error error()
Create a YAMLParseError error from an existing error generated by the YAML parser.
Expected< unsigned > parseUnsigned(yaml::KeyValueNode &Node)
Parse one value to an unsigned.
yaml::Stream Stream
Stream for yaml parsing.
Expected< RemarkLocation > parseDebugLoc(yaml::KeyValueNode &Node)
Parse a debug location.
virtual Expected< StringRef > parseStr(yaml::KeyValueNode &Node)
Parse one value to a string.
Expected< StringRef > parseKey(yaml::KeyValueNode &Node)
Parse one key to a string.
Expected< Argument > parseArg(yaml::Node &Node)
Parse an argument.
Expected< std::unique_ptr< Remark > > parseRemark(yaml::Document &Remark)
Parse a YAML remark to a remarks::Remark object.
Expected< std::unique_ptr< Remark > > next() override
If no error occurs, this returns a valid Remark object.
SourceMgr SM
Source manager for better error messages.
std::string LastErrorMessage
Last error message that can come from the YAML parser diagnostics.
Expected< Type > parseType(yaml::MappingNode &Node)
Parse the type of a remark to an enum type.