368 changes: 368 additions & 0 deletions llvm/lib/OptRemarks/OptRemarksParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
//===- OptRemarksParser.cpp -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// 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);
}
2 changes: 1 addition & 1 deletion llvm/tools/llvm-opt-report/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(LLVM_LINK_COMPONENTS Core Demangle Object Support)
set(LLVM_LINK_COMPONENTS Core Demangle Object OptRemarks Support)

add_llvm_tool(llvm-opt-report
OptReport.cpp
Expand Down
144 changes: 38 additions & 106 deletions llvm/tools/llvm-opt-report/OptReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm-c/OptRemarks.h"
#include <cstdlib>
#include <map>
#include <set>
Expand Down Expand Up @@ -142,104 +143,44 @@ typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,
OptReportLocationInfo>>>> LocationInfoTy;
} // anonymous namespace

static void collectLocationInfo(yaml::Stream &Stream,
LocationInfoTy &LocationInfo) {
SmallVector<char, 8> Tmp;

// Note: We're using the YAML parser here directly, instead of using the
// YAMLTraits implementation, because the YAMLTraits implementation does not
// support a way to handle only a subset of the input keys (it will error out
// if there is an input key that you don't map to your class), and
// furthermore, it does not provide a way to handle the Args sequence of
// key/value pairs, where the order must be captured and the 'String' key
// might be repeated.
for (auto &Doc : Stream) {
auto *Root = dyn_cast<yaml::MappingNode>(Doc.getRoot());
if (!Root)
continue;
static bool readLocationInfo(LocationInfoTy &LocationInfo) {
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFile(InputFileName.c_str());
if (std::error_code EC = Buf.getError()) {
WithColor::error() << "Can't open file " << InputFileName << ": "
<< EC.message() << "\n";
return false;
}

bool Transformed = Root->getRawTag() == "!Passed";
std::string Pass, File, Function;
int Line = 0, Column = 1;
StringRef Buffer = (*Buf)->getBuffer();
LLVMOptRemarkParserRef Parser =
LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size());

LLVMOptRemarkEntry *Remark = nullptr;
while ((Remark = LLVMOptRemarkParserGetNext(Parser))) {
bool Transformed =
StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed";
StringRef Pass(Remark->PassName.Str, Remark->PassName.Len);
StringRef File(Remark->DebugLoc.SourceFile.Str,
Remark->DebugLoc.SourceFile.Len);
StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len);
uint32_t Line = Remark->DebugLoc.SourceLineNumber;
uint32_t Column = Remark->DebugLoc.SourceColumnNumber;
ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs);

int VectorizationFactor = 1;
int InterleaveCount = 1;
int UnrollCount = 1;

for (auto &RootChild : *Root) {
auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey());
if (!Key)
continue;
StringRef KeyName = Key->getValue(Tmp);
if (KeyName == "Pass") {
auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
if (!Value)
continue;
Pass = Value->getValue(Tmp);
} else if (KeyName == "Function") {
auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
if (!Value)
continue;
Function = Value->getValue(Tmp);
} else if (KeyName == "DebugLoc") {
auto *DebugLoc = dyn_cast<yaml::MappingNode>(RootChild.getValue());
if (!DebugLoc)
continue;

for (auto &DLChild : *DebugLoc) {
auto *DLKey = dyn_cast<yaml::ScalarNode>(DLChild.getKey());
if (!DLKey)
continue;
StringRef DLKeyName = DLKey->getValue(Tmp);
if (DLKeyName == "File") {
auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
if (!Value)
continue;
File = Value->getValue(Tmp);
} else if (DLKeyName == "Line") {
auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
if (!Value)
continue;
Value->getValue(Tmp).getAsInteger(10, Line);
} else if (DLKeyName == "Column") {
auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
if (!Value)
continue;
Value->getValue(Tmp).getAsInteger(10, Column);
}
}
} else if (KeyName == "Args") {
auto *Args = dyn_cast<yaml::SequenceNode>(RootChild.getValue());
if (!Args)
continue;
for (auto &ArgChild : *Args) {
auto *ArgMap = dyn_cast<yaml::MappingNode>(&ArgChild);
if (!ArgMap)
continue;
for (auto &ArgKV : *ArgMap) {
auto *ArgKey = dyn_cast<yaml::ScalarNode>(ArgKV.getKey());
if (!ArgKey)
continue;
StringRef ArgKeyName = ArgKey->getValue(Tmp);
if (ArgKeyName == "VectorizationFactor") {
auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
if (!Value)
continue;
Value->getValue(Tmp).getAsInteger(10, VectorizationFactor);
} else if (ArgKeyName == "InterleaveCount") {
auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
if (!Value)
continue;
Value->getValue(Tmp).getAsInteger(10, InterleaveCount);
} else if (ArgKeyName == "UnrollCount") {
auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
if (!Value)
continue;
Value->getValue(Tmp).getAsInteger(10, UnrollCount);
}
}
}
}
for (const LLVMOptRemarkArg &Arg : Args) {
StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len);
StringRef ArgValue(Arg.Value.Str, Arg.Value.Len);
if (ArgKeyName == "VectorizationFactor")
ArgValue.getAsInteger(10, VectorizationFactor);
else if (ArgKeyName == "InterleaveCount")
ArgValue.getAsInteger(10, InterleaveCount);
else if (ArgKeyName == "UnrollCount")
ArgValue.getAsInteger(10, UnrollCount);
}

if (Line < 1 || File.empty())
Expand Down Expand Up @@ -268,22 +209,13 @@ static void collectLocationInfo(yaml::Stream &Stream,
UpdateLLII(LI.Vectorized);
}
}
}

static bool readLocationInfo(LocationInfoTy &LocationInfo) {
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFileOrSTDIN(InputFileName);
if (std::error_code EC = Buf.getError()) {
WithColor::error() << "Can't open file " << InputFileName << ": "
<< EC.message() << "\n";
return false;
}

SourceMgr SM;
yaml::Stream Stream(Buf.get()->getBuffer(), SM);
collectLocationInfo(Stream, LocationInfo);
bool HasError = LLVMOptRemarkParserHasError(Parser);
if (HasError)
WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n";

return true;
LLVMOptRemarkParserDispose(Parser);
return !HasError;
}

static bool writeReport(LocationInfoTy &LocationInfo) {
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ add_subdirectory(MI)
add_subdirectory(Object)
add_subdirectory(ObjectYAML)
add_subdirectory(Option)
add_subdirectory(OptRemarks)
add_subdirectory(Passes)
add_subdirectory(ProfileData)
add_subdirectory(Support)
Expand Down
8 changes: 8 additions & 0 deletions llvm/unittests/OptRemarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
set(LLVM_LINK_COMPONENTS
OptRemarks
Support
)

add_llvm_unittest(OptRemarksTests
OptRemarksParsingTest.cpp
)
433 changes: 433 additions & 0 deletions llvm/unittests/OptRemarks/OptRemarksParsingTest.cpp

Large diffs are not rendered by default.