forked from OSchip/llvm-project
368 lines
12 KiB
C++
368 lines
12 KiB
C++
//===- OptRemarksParser.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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file provides utility methods used by clients that want to use the
|
|
// parser for optimization remarks in LLVM.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm-c/OptRemarks.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/YAMLTraits.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
struct RemarkParser {
|
|
/// Source manager for better error messages.
|
|
SourceMgr SM;
|
|
/// Stream for yaml parsing.
|
|
yaml::Stream Stream;
|
|
/// Storage for the error stream.
|
|
std::string ErrorString;
|
|
/// The error stream.
|
|
raw_string_ostream ErrorStream;
|
|
/// Iterator in the YAML stream.
|
|
yaml::document_iterator DI;
|
|
/// The parsed remark (if any).
|
|
Optional<LLVMOptRemarkEntry> LastRemark;
|
|
/// Temporary parsing buffer for the arguments.
|
|
SmallVector<LLVMOptRemarkArg, 8> TmpArgs;
|
|
/// The state used by the parser to parse a remark entry. Invalidated with
|
|
/// every call to `parseYAMLElement`.
|
|
struct ParseState {
|
|
/// Temporary parsing buffer for the arguments.
|
|
SmallVectorImpl<LLVMOptRemarkArg> *Args;
|
|
StringRef Type;
|
|
StringRef Pass;
|
|
StringRef Name;
|
|
StringRef Function;
|
|
/// Optional.
|
|
Optional<StringRef> File;
|
|
Optional<unsigned> Line;
|
|
Optional<unsigned> Column;
|
|
Optional<unsigned> Hotness;
|
|
|
|
ParseState(SmallVectorImpl<LLVMOptRemarkArg> &Args) : Args(&Args) {}
|
|
/// Use Args only as a **temporary** buffer.
|
|
~ParseState() { Args->clear(); }
|
|
};
|
|
|
|
ParseState State;
|
|
|
|
/// Set to `true` if we had any errors during parsing.
|
|
bool HadAnyErrors = false;
|
|
|
|
RemarkParser(StringRef Buf)
|
|
: SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString),
|
|
DI(Stream.begin()), LastRemark(), TmpArgs(), State(TmpArgs) {
|
|
SM.setDiagHandler(RemarkParser::HandleDiagnostic, this);
|
|
}
|
|
|
|
/// Parse a YAML element.
|
|
Error parseYAMLElement(yaml::Document &Remark);
|
|
|
|
private:
|
|
/// Parse one key to a string.
|
|
/// otherwise.
|
|
Error parseKey(StringRef &Result, yaml::KeyValueNode &Node);
|
|
/// Parse one value to a string.
|
|
Error parseValue(StringRef &Result, yaml::KeyValueNode &Node);
|
|
/// Parse one value to an unsigned.
|
|
Error parseValue(Optional<unsigned> &Result, yaml::KeyValueNode &Node);
|
|
/// Parse a debug location.
|
|
Error parseDebugLoc(Optional<StringRef> &File, Optional<unsigned> &Line,
|
|
Optional<unsigned> &Column, yaml::KeyValueNode &Node);
|
|
/// Parse an argument.
|
|
Error parseArg(SmallVectorImpl<LLVMOptRemarkArg> &TmpArgs, yaml::Node &Node);
|
|
|
|
/// Handle a diagnostic from the YAML stream. Records the error in the
|
|
/// RemarkParser class.
|
|
static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
|
|
assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
|
|
auto *Parser = static_cast<RemarkParser *>(Ctx);
|
|
Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false,
|
|
/*ShowKindLabels*/ true);
|
|
}
|
|
};
|
|
|
|
class ParseError : public ErrorInfo<ParseError> {
|
|
public:
|
|
static char ID;
|
|
|
|
ParseError(StringRef Message, yaml::Node &Node)
|
|
: Message(Message), Node(Node) {}
|
|
|
|
void log(raw_ostream &OS) const override { OS << Message; }
|
|
std::error_code convertToErrorCode() const override {
|
|
return inconvertibleErrorCode();
|
|
}
|
|
|
|
StringRef getMessage() const { return Message; }
|
|
yaml::Node &getNode() const { return Node; }
|
|
|
|
private:
|
|
StringRef Message; // No need to hold a full copy of the buffer.
|
|
yaml::Node &Node;
|
|
};
|
|
|
|
char ParseError::ID = 0;
|
|
|
|
static LLVMOptRemarkStringRef toOptRemarkStr(StringRef Str) {
|
|
return {Str.data(), static_cast<uint32_t>(Str.size())};
|
|
}
|
|
|
|
Error RemarkParser::parseKey(StringRef &Result, yaml::KeyValueNode &Node) {
|
|
auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey());
|
|
if (!Key)
|
|
return make_error<ParseError>("key is not a string.", Node);
|
|
|
|
Result = Key->getRawValue();
|
|
return Error::success();
|
|
}
|
|
|
|
Error RemarkParser::parseValue(StringRef &Result, yaml::KeyValueNode &Node) {
|
|
auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
|
|
if (!Value)
|
|
return make_error<ParseError>("expected a value of scalar type.", Node);
|
|
Result = Value->getRawValue();
|
|
|
|
if (Result.front() == '\'')
|
|
Result = Result.drop_front();
|
|
|
|
if (Result.back() == '\'')
|
|
Result = Result.drop_back();
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error RemarkParser::parseValue(Optional<unsigned> &Result,
|
|
yaml::KeyValueNode &Node) {
|
|
SmallVector<char, 4> Tmp;
|
|
auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue());
|
|
if (!Value)
|
|
return make_error<ParseError>("expected a value of scalar type.", Node);
|
|
unsigned UnsignedValue = 0;
|
|
if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue))
|
|
return make_error<ParseError>("expected a value of integer type.", *Value);
|
|
Result = UnsignedValue;
|
|
return Error::success();
|
|
}
|
|
|
|
Error RemarkParser::parseDebugLoc(Optional<StringRef> &File,
|
|
Optional<unsigned> &Line,
|
|
Optional<unsigned> &Column,
|
|
yaml::KeyValueNode &Node) {
|
|
auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue());
|
|
if (!DebugLoc)
|
|
return make_error<ParseError>("expected a value of mapping type.", Node);
|
|
|
|
for (yaml::KeyValueNode &DLNode : *DebugLoc) {
|
|
StringRef KeyName;
|
|
if (Error E = parseKey(KeyName, DLNode))
|
|
return E;
|
|
if (KeyName == "File") {
|
|
File = StringRef(); // Set the optional to contain a default constructed
|
|
// value, to be passed to the parsing function.
|
|
if (Error E = parseValue(*File, DLNode))
|
|
return E;
|
|
} else if (KeyName == "Column") {
|
|
if (Error E = parseValue(Column, DLNode))
|
|
return E;
|
|
} else if (KeyName == "Line") {
|
|
if (Error E = parseValue(Line, DLNode))
|
|
return E;
|
|
} else {
|
|
return make_error<ParseError>("unknown entry in DebugLoc map.", DLNode);
|
|
}
|
|
}
|
|
|
|
// If any of the debug loc fields is missing, return an error.
|
|
if (!File || !Line || !Column)
|
|
return make_error<ParseError>("DebugLoc node incomplete.", Node);
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error RemarkParser::parseArg(SmallVectorImpl<LLVMOptRemarkArg> &Args,
|
|
yaml::Node &Node) {
|
|
auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node);
|
|
if (!ArgMap)
|
|
return make_error<ParseError>("expected a value of mapping type.", Node);
|
|
|
|
StringRef ValueStr;
|
|
StringRef KeyStr;
|
|
Optional<StringRef> File;
|
|
Optional<unsigned> Line;
|
|
Optional<unsigned> Column;
|
|
|
|
for (yaml::KeyValueNode &ArgEntry : *ArgMap) {
|
|
StringRef KeyName;
|
|
if (Error E = parseKey(KeyName, ArgEntry))
|
|
return E;
|
|
|
|
// Try to parse debug locs.
|
|
if (KeyName == "DebugLoc") {
|
|
// Can't have multiple DebugLoc entries per argument.
|
|
if (File || Line || Column)
|
|
return make_error<ParseError>(
|
|
"only one DebugLoc entry is allowed per argument.", ArgEntry);
|
|
|
|
if (Error E = parseDebugLoc(File, Line, Column, ArgEntry))
|
|
return E;
|
|
continue;
|
|
}
|
|
|
|
// If we already have a string, error out.
|
|
if (!ValueStr.empty())
|
|
return make_error<ParseError>(
|
|
"only one string entry is allowed per argument.", ArgEntry);
|
|
|
|
// Try to parse a string.
|
|
if (Error E = parseValue(ValueStr, ArgEntry))
|
|
return E;
|
|
|
|
// Keep the key from the string.
|
|
KeyStr = KeyName;
|
|
}
|
|
|
|
if (KeyStr.empty())
|
|
return make_error<ParseError>("argument key is missing.", *ArgMap);
|
|
if (ValueStr.empty())
|
|
return make_error<ParseError>("argument value is missing.", *ArgMap);
|
|
|
|
Args.push_back(LLVMOptRemarkArg{
|
|
toOptRemarkStr(KeyStr), toOptRemarkStr(ValueStr),
|
|
LLVMOptRemarkDebugLoc{toOptRemarkStr(File.getValueOr(StringRef())),
|
|
Line.getValueOr(0), Column.getValueOr(0)}});
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Error RemarkParser::parseYAMLElement(yaml::Document &Remark) {
|
|
// Parsing a new remark, clear the previous one.
|
|
LastRemark = None;
|
|
State = ParseState(TmpArgs);
|
|
|
|
auto *Root = dyn_cast<yaml::MappingNode>(Remark.getRoot());
|
|
if (!Root)
|
|
return make_error<ParseError>("document root is not of mapping type.",
|
|
*Remark.getRoot());
|
|
|
|
State.Type = Root->getRawTag();
|
|
|
|
for (yaml::KeyValueNode &RemarkField : *Root) {
|
|
StringRef KeyName;
|
|
if (Error E = parseKey(KeyName, RemarkField))
|
|
return E;
|
|
|
|
if (KeyName == "Pass") {
|
|
if (Error E = parseValue(State.Pass, RemarkField))
|
|
return E;
|
|
} else if (KeyName == "Name") {
|
|
if (Error E = parseValue(State.Name, RemarkField))
|
|
return E;
|
|
} else if (KeyName == "Function") {
|
|
if (Error E = parseValue(State.Function, RemarkField))
|
|
return E;
|
|
} else if (KeyName == "Hotness") {
|
|
if (Error E = parseValue(State.Hotness, RemarkField))
|
|
return E;
|
|
} else if (KeyName == "DebugLoc") {
|
|
if (Error E =
|
|
parseDebugLoc(State.File, State.Line, State.Column, RemarkField))
|
|
return E;
|
|
} else if (KeyName == "Args") {
|
|
auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue());
|
|
if (!Args)
|
|
return make_error<ParseError>("wrong value type for key.", RemarkField);
|
|
|
|
for (yaml::Node &Arg : *Args)
|
|
if (Error E = parseArg(*State.Args, Arg))
|
|
return E;
|
|
} else {
|
|
return make_error<ParseError>("unknown key.", RemarkField);
|
|
}
|
|
}
|
|
|
|
// If the YAML parsing failed, don't even continue parsing. We might
|
|
// encounter malformed YAML.
|
|
if (Stream.failed())
|
|
return make_error<ParseError>("YAML parsing failed.", *Remark.getRoot());
|
|
|
|
// Check if any of the mandatory fields are missing.
|
|
if (State.Type.empty() || State.Pass.empty() || State.Name.empty() ||
|
|
State.Function.empty())
|
|
return make_error<ParseError>("Type, Pass, Name or Function missing.",
|
|
*Remark.getRoot());
|
|
|
|
LastRemark = LLVMOptRemarkEntry{
|
|
toOptRemarkStr(State.Type),
|
|
toOptRemarkStr(State.Pass),
|
|
toOptRemarkStr(State.Name),
|
|
toOptRemarkStr(State.Function),
|
|
LLVMOptRemarkDebugLoc{toOptRemarkStr(State.File.getValueOr(StringRef())),
|
|
State.Line.getValueOr(0),
|
|
State.Column.getValueOr(0)},
|
|
State.Hotness.getValueOr(0),
|
|
static_cast<uint32_t>(State.Args->size()),
|
|
State.Args->data()};
|
|
|
|
return Error::success();
|
|
}
|
|
} // namespace
|
|
|
|
// Create wrappers for C Binding types (see CBindingWrapping.h).
|
|
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(RemarkParser, LLVMOptRemarkParserRef)
|
|
|
|
extern "C" LLVMOptRemarkParserRef LLVMOptRemarkParserCreate(const void *Buf,
|
|
uint64_t Size) {
|
|
return wrap(
|
|
new RemarkParser(StringRef(static_cast<const char *>(Buf), Size)));
|
|
}
|
|
|
|
extern "C" LLVMOptRemarkEntry *
|
|
LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser) {
|
|
RemarkParser &TheParser = *unwrap(Parser);
|
|
// Check for EOF.
|
|
if (TheParser.HadAnyErrors || TheParser.DI == TheParser.Stream.end())
|
|
return nullptr;
|
|
|
|
// Try to parse an entry.
|
|
if (Error E = TheParser.parseYAMLElement(*TheParser.DI)) {
|
|
handleAllErrors(std::move(E), [&](const ParseError &PE) {
|
|
TheParser.Stream.printError(&PE.getNode(),
|
|
Twine(PE.getMessage()) + Twine('\n'));
|
|
TheParser.HadAnyErrors = true;
|
|
});
|
|
return nullptr;
|
|
}
|
|
|
|
// Move on.
|
|
++TheParser.DI;
|
|
|
|
// Return the just-parsed remark.
|
|
if (Optional<LLVMOptRemarkEntry> &Entry = TheParser.LastRemark)
|
|
return &*Entry;
|
|
return nullptr;
|
|
}
|
|
|
|
extern "C" LLVMBool LLVMOptRemarkParserHasError(LLVMOptRemarkParserRef Parser) {
|
|
return unwrap(Parser)->HadAnyErrors;
|
|
}
|
|
|
|
extern "C" const char *
|
|
LLVMOptRemarkParserGetErrorMessage(LLVMOptRemarkParserRef Parser) {
|
|
return unwrap(Parser)->ErrorStream.str().c_str();
|
|
}
|
|
|
|
extern "C" void LLVMOptRemarkParserDispose(LLVMOptRemarkParserRef Parser) {
|
|
delete unwrap(Parser);
|
|
}
|