1 //===- OptRemarksParser.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file provides utility methods used by clients that want to use the
10 // parser for optimization remarks in LLVM.
12 //===----------------------------------------------------------------------===//
14 #include "llvm-c/OptRemarks.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "llvm/Support/YAMLTraits.h"
23 /// Source manager for better error messages.
25 /// Stream for yaml parsing.
27 /// Storage for the error stream.
28 std::string ErrorString
;
30 raw_string_ostream ErrorStream
;
31 /// Iterator in the YAML stream.
32 yaml::document_iterator DI
;
33 /// The parsed remark (if any).
34 Optional
<LLVMOptRemarkEntry
> LastRemark
;
35 /// Temporary parsing buffer for the arguments.
36 SmallVector
<LLVMOptRemarkArg
, 8> TmpArgs
;
37 /// The state used by the parser to parse a remark entry. Invalidated with
38 /// every call to `parseYAMLElement`.
40 /// Temporary parsing buffer for the arguments.
41 SmallVectorImpl
<LLVMOptRemarkArg
> *Args
;
47 Optional
<StringRef
> File
;
48 Optional
<unsigned> Line
;
49 Optional
<unsigned> Column
;
50 Optional
<unsigned> Hotness
;
52 ParseState(SmallVectorImpl
<LLVMOptRemarkArg
> &Args
) : Args(&Args
) {}
53 /// Use Args only as a **temporary** buffer.
54 ~ParseState() { Args
->clear(); }
59 /// Set to `true` if we had any errors during parsing.
60 bool HadAnyErrors
= false;
62 RemarkParser(StringRef Buf
)
63 : SM(), Stream(Buf
, SM
), ErrorString(), ErrorStream(ErrorString
),
64 DI(Stream
.begin()), LastRemark(), TmpArgs(), State(TmpArgs
) {
65 SM
.setDiagHandler(RemarkParser::HandleDiagnostic
, this);
68 /// Parse a YAML element.
69 Error
parseYAMLElement(yaml::Document
&Remark
);
72 /// Parse one key to a string.
74 Error
parseKey(StringRef
&Result
, yaml::KeyValueNode
&Node
);
75 /// Parse one value to a string.
76 Error
parseValue(StringRef
&Result
, yaml::KeyValueNode
&Node
);
77 /// Parse one value to an unsigned.
78 Error
parseValue(Optional
<unsigned> &Result
, yaml::KeyValueNode
&Node
);
79 /// Parse a debug location.
80 Error
parseDebugLoc(Optional
<StringRef
> &File
, Optional
<unsigned> &Line
,
81 Optional
<unsigned> &Column
, yaml::KeyValueNode
&Node
);
82 /// Parse an argument.
83 Error
parseArg(SmallVectorImpl
<LLVMOptRemarkArg
> &TmpArgs
, yaml::Node
&Node
);
85 /// Handle a diagnostic from the YAML stream. Records the error in the
86 /// RemarkParser class.
87 static void HandleDiagnostic(const SMDiagnostic
&Diag
, void *Ctx
) {
88 assert(Ctx
&& "Expected non-null Ctx in diagnostic handler.");
89 auto *Parser
= static_cast<RemarkParser
*>(Ctx
);
90 Diag
.print(/*ProgName=*/nullptr, Parser
->ErrorStream
, /*ShowColors*/ false,
91 /*ShowKindLabels*/ true);
95 class ParseError
: public ErrorInfo
<ParseError
> {
99 ParseError(StringRef Message
, yaml::Node
&Node
)
100 : Message(Message
), Node(Node
) {}
102 void log(raw_ostream
&OS
) const override
{ OS
<< Message
; }
103 std::error_code
convertToErrorCode() const override
{
104 return inconvertibleErrorCode();
107 StringRef
getMessage() const { return Message
; }
108 yaml::Node
&getNode() const { return Node
; }
111 StringRef Message
; // No need to hold a full copy of the buffer.
115 char ParseError::ID
= 0;
117 static LLVMOptRemarkStringRef
toOptRemarkStr(StringRef Str
) {
118 return {Str
.data(), static_cast<uint32_t>(Str
.size())};
121 Error
RemarkParser::parseKey(StringRef
&Result
, yaml::KeyValueNode
&Node
) {
122 auto *Key
= dyn_cast
<yaml::ScalarNode
>(Node
.getKey());
124 return make_error
<ParseError
>("key is not a string.", Node
);
126 Result
= Key
->getRawValue();
127 return Error::success();
130 Error
RemarkParser::parseValue(StringRef
&Result
, yaml::KeyValueNode
&Node
) {
131 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
133 return make_error
<ParseError
>("expected a value of scalar type.", Node
);
134 Result
= Value
->getRawValue();
136 if (Result
.front() == '\'')
137 Result
= Result
.drop_front();
139 if (Result
.back() == '\'')
140 Result
= Result
.drop_back();
142 return Error::success();
145 Error
RemarkParser::parseValue(Optional
<unsigned> &Result
,
146 yaml::KeyValueNode
&Node
) {
147 SmallVector
<char, 4> Tmp
;
148 auto *Value
= dyn_cast
<yaml::ScalarNode
>(Node
.getValue());
150 return make_error
<ParseError
>("expected a value of scalar type.", Node
);
151 unsigned UnsignedValue
= 0;
152 if (Value
->getValue(Tmp
).getAsInteger(10, UnsignedValue
))
153 return make_error
<ParseError
>("expected a value of integer type.", *Value
);
154 Result
= UnsignedValue
;
155 return Error::success();
158 Error
RemarkParser::parseDebugLoc(Optional
<StringRef
> &File
,
159 Optional
<unsigned> &Line
,
160 Optional
<unsigned> &Column
,
161 yaml::KeyValueNode
&Node
) {
162 auto *DebugLoc
= dyn_cast
<yaml::MappingNode
>(Node
.getValue());
164 return make_error
<ParseError
>("expected a value of mapping type.", Node
);
166 for (yaml::KeyValueNode
&DLNode
: *DebugLoc
) {
168 if (Error E
= parseKey(KeyName
, DLNode
))
170 if (KeyName
== "File") {
171 File
= StringRef(); // Set the optional to contain a default constructed
172 // value, to be passed to the parsing function.
173 if (Error E
= parseValue(*File
, DLNode
))
175 } else if (KeyName
== "Column") {
176 if (Error E
= parseValue(Column
, DLNode
))
178 } else if (KeyName
== "Line") {
179 if (Error E
= parseValue(Line
, DLNode
))
182 return make_error
<ParseError
>("unknown entry in DebugLoc map.", DLNode
);
186 // If any of the debug loc fields is missing, return an error.
187 if (!File
|| !Line
|| !Column
)
188 return make_error
<ParseError
>("DebugLoc node incomplete.", Node
);
190 return Error::success();
193 Error
RemarkParser::parseArg(SmallVectorImpl
<LLVMOptRemarkArg
> &Args
,
195 auto *ArgMap
= dyn_cast
<yaml::MappingNode
>(&Node
);
197 return make_error
<ParseError
>("expected a value of mapping type.", Node
);
201 Optional
<StringRef
> File
;
202 Optional
<unsigned> Line
;
203 Optional
<unsigned> Column
;
205 for (yaml::KeyValueNode
&ArgEntry
: *ArgMap
) {
207 if (Error E
= parseKey(KeyName
, ArgEntry
))
210 // Try to parse debug locs.
211 if (KeyName
== "DebugLoc") {
212 // Can't have multiple DebugLoc entries per argument.
213 if (File
|| Line
|| Column
)
214 return make_error
<ParseError
>(
215 "only one DebugLoc entry is allowed per argument.", ArgEntry
);
217 if (Error E
= parseDebugLoc(File
, Line
, Column
, ArgEntry
))
222 // If we already have a string, error out.
223 if (!ValueStr
.empty())
224 return make_error
<ParseError
>(
225 "only one string entry is allowed per argument.", ArgEntry
);
227 // Try to parse a string.
228 if (Error E
= parseValue(ValueStr
, ArgEntry
))
231 // Keep the key from the string.
236 return make_error
<ParseError
>("argument key is missing.", *ArgMap
);
237 if (ValueStr
.empty())
238 return make_error
<ParseError
>("argument value is missing.", *ArgMap
);
240 Args
.push_back(LLVMOptRemarkArg
{
241 toOptRemarkStr(KeyStr
), toOptRemarkStr(ValueStr
),
242 LLVMOptRemarkDebugLoc
{toOptRemarkStr(File
.getValueOr(StringRef())),
243 Line
.getValueOr(0), Column
.getValueOr(0)}});
245 return Error::success();
248 Error
RemarkParser::parseYAMLElement(yaml::Document
&Remark
) {
249 // Parsing a new remark, clear the previous one.
251 State
= ParseState(TmpArgs
);
253 auto *Root
= dyn_cast
<yaml::MappingNode
>(Remark
.getRoot());
255 return make_error
<ParseError
>("document root is not of mapping type.",
258 State
.Type
= Root
->getRawTag();
260 for (yaml::KeyValueNode
&RemarkField
: *Root
) {
262 if (Error E
= parseKey(KeyName
, RemarkField
))
265 if (KeyName
== "Pass") {
266 if (Error E
= parseValue(State
.Pass
, RemarkField
))
268 } else if (KeyName
== "Name") {
269 if (Error E
= parseValue(State
.Name
, RemarkField
))
271 } else if (KeyName
== "Function") {
272 if (Error E
= parseValue(State
.Function
, RemarkField
))
274 } else if (KeyName
== "Hotness") {
275 if (Error E
= parseValue(State
.Hotness
, RemarkField
))
277 } else if (KeyName
== "DebugLoc") {
279 parseDebugLoc(State
.File
, State
.Line
, State
.Column
, RemarkField
))
281 } else if (KeyName
== "Args") {
282 auto *Args
= dyn_cast
<yaml::SequenceNode
>(RemarkField
.getValue());
284 return make_error
<ParseError
>("wrong value type for key.", RemarkField
);
286 for (yaml::Node
&Arg
: *Args
)
287 if (Error E
= parseArg(*State
.Args
, Arg
))
290 return make_error
<ParseError
>("unknown key.", RemarkField
);
294 // If the YAML parsing failed, don't even continue parsing. We might
295 // encounter malformed YAML.
297 return make_error
<ParseError
>("YAML parsing failed.", *Remark
.getRoot());
299 // Check if any of the mandatory fields are missing.
300 if (State
.Type
.empty() || State
.Pass
.empty() || State
.Name
.empty() ||
301 State
.Function
.empty())
302 return make_error
<ParseError
>("Type, Pass, Name or Function missing.",
305 LastRemark
= LLVMOptRemarkEntry
{
306 toOptRemarkStr(State
.Type
),
307 toOptRemarkStr(State
.Pass
),
308 toOptRemarkStr(State
.Name
),
309 toOptRemarkStr(State
.Function
),
310 LLVMOptRemarkDebugLoc
{toOptRemarkStr(State
.File
.getValueOr(StringRef())),
311 State
.Line
.getValueOr(0),
312 State
.Column
.getValueOr(0)},
313 State
.Hotness
.getValueOr(0),
314 static_cast<uint32_t>(State
.Args
->size()),
317 return Error::success();
321 // Create wrappers for C Binding types (see CBindingWrapping.h).
322 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(RemarkParser
, LLVMOptRemarkParserRef
)
324 extern "C" LLVMOptRemarkParserRef
LLVMOptRemarkParserCreate(const void *Buf
,
327 new RemarkParser(StringRef(static_cast<const char *>(Buf
), Size
)));
330 extern "C" LLVMOptRemarkEntry
*
331 LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser
) {
332 RemarkParser
&TheParser
= *unwrap(Parser
);
334 if (TheParser
.HadAnyErrors
|| TheParser
.DI
== TheParser
.Stream
.end())
337 // Try to parse an entry.
338 if (Error E
= TheParser
.parseYAMLElement(*TheParser
.DI
)) {
339 handleAllErrors(std::move(E
), [&](const ParseError
&PE
) {
340 TheParser
.Stream
.printError(&PE
.getNode(),
341 Twine(PE
.getMessage()) + Twine('\n'));
342 TheParser
.HadAnyErrors
= true;
350 // Return the just-parsed remark.
351 if (Optional
<LLVMOptRemarkEntry
> &Entry
= TheParser
.LastRemark
)
356 extern "C" LLVMBool
LLVMOptRemarkParserHasError(LLVMOptRemarkParserRef Parser
) {
357 return unwrap(Parser
)->HadAnyErrors
;
360 extern "C" const char *
361 LLVMOptRemarkParserGetErrorMessage(LLVMOptRemarkParserRef Parser
) {
362 return unwrap(Parser
)->ErrorStream
.str().c_str();
365 extern "C" void LLVMOptRemarkParserDispose(LLVMOptRemarkParserRef Parser
) {
366 delete unwrap(Parser
);