mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 12:12:47 +01:00
Reland: [OptRemarks] Add library for parsing optimization remarks
Add a library that parses optimization remarks (currently YAML, so based on the YAMLParser). The goal is to be able to provide tools a remark parser that is not completely dependent on YAML, in case we decide to change the format later. It exposes a C API which takes a handler that is called with the remark structure. It adds a libLLVMOptRemark.a static library, and it's used in-tree by the llvm-opt-report tool (from which the parser has been mostly moved out). Differential Revision: https://reviews.llvm.org/D52776 Fixed the tests by removing the usage of C++11 strings, which seems not to be supported by gcc 4.8.4 if they're used as a macro argument. llvm-svn: 344171
This commit is contained in:
parent
3aeeb26969
commit
254ec48163
197
include/llvm-c/OptRemarks.h
Normal file
197
include/llvm-c/OptRemarks.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
/*===-- llvm-c/OptRemarks.h - OptRemarks Public C Interface -------*- C -*-===*\
|
||||||
|
|* *|
|
||||||
|
|* The LLVM Compiler Infrastructure *|
|
||||||
|
|* *|
|
||||||
|
|* This file is distributed under the University of Illinois Open Source *|
|
||||||
|
|* License. See LICENSE.TXT for details. *|
|
||||||
|
|* *|
|
||||||
|
|*===----------------------------------------------------------------------===*|
|
||||||
|
|* *|
|
||||||
|
|* This header provides a public interface to an opt-remark library. *|
|
||||||
|
|* LLVM provides an implementation of this interface. *|
|
||||||
|
|* *|
|
||||||
|
\*===----------------------------------------------------------------------===*/
|
||||||
|
|
||||||
|
#ifndef LLVM_C_OPT_REMARKS_H
|
||||||
|
#define LLVM_C_OPT_REMARKS_H
|
||||||
|
|
||||||
|
#include "llvm-c/Core.h"
|
||||||
|
#include "llvm-c/Types.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <cstddef>
|
||||||
|
extern "C" {
|
||||||
|
#else
|
||||||
|
#include <stddef.h>
|
||||||
|
#endif /* !defined(__cplusplus) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup LLVMCOPTREMARKS OptRemarks
|
||||||
|
* @ingroup LLVMC
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define OPT_REMARKS_API_VERSION 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String containing a buffer and a length. The buffer is not guaranteed to be
|
||||||
|
* zero-terminated.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char *Str;
|
||||||
|
uint32_t Len;
|
||||||
|
} LLVMOptRemarkStringRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DebugLoc containing File, Line and Column.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// File:
|
||||||
|
LLVMOptRemarkStringRef SourceFile;
|
||||||
|
// Line:
|
||||||
|
uint32_t SourceLineNumber;
|
||||||
|
// Column:
|
||||||
|
uint32_t SourceColumnNumber;
|
||||||
|
} LLVMOptRemarkDebugLoc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element of the "Args" list. The key might give more information about what
|
||||||
|
* are the semantics of the value, e.g. "Callee" will tell you that the value
|
||||||
|
* is a symbol that names a function.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// e.g. "Callee"
|
||||||
|
LLVMOptRemarkStringRef Key;
|
||||||
|
// e.g. "malloc"
|
||||||
|
LLVMOptRemarkStringRef Value;
|
||||||
|
|
||||||
|
// "DebugLoc": Optional
|
||||||
|
LLVMOptRemarkDebugLoc DebugLoc;
|
||||||
|
} LLVMOptRemarkArg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One remark entry.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
// e.g. !Missed, !Passed
|
||||||
|
LLVMOptRemarkStringRef RemarkType;
|
||||||
|
// "Pass": Required
|
||||||
|
LLVMOptRemarkStringRef PassName;
|
||||||
|
// "Name": Required
|
||||||
|
LLVMOptRemarkStringRef RemarkName;
|
||||||
|
// "Function": Required
|
||||||
|
LLVMOptRemarkStringRef FunctionName;
|
||||||
|
|
||||||
|
// "DebugLoc": Optional
|
||||||
|
LLVMOptRemarkDebugLoc DebugLoc;
|
||||||
|
// "Hotness": Optional
|
||||||
|
uint32_t Hotness;
|
||||||
|
// "Args": Optional. It is an array of `num_args` elements.
|
||||||
|
uint32_t NumArgs;
|
||||||
|
LLVMOptRemarkArg *Args;
|
||||||
|
} LLVMOptRemarkEntry;
|
||||||
|
|
||||||
|
typedef struct LLVMOptRemarkOpaqueParser *LLVMOptRemarkParserRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a remark parser that can be used to read and parse the buffer located
|
||||||
|
* in \p Buf of size \p Size.
|
||||||
|
*
|
||||||
|
* \p Buf cannot be NULL.
|
||||||
|
*
|
||||||
|
* This function should be paired with LLVMOptRemarkParserDispose() to avoid
|
||||||
|
* leaking resources.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
extern LLVMOptRemarkParserRef LLVMOptRemarkParserCreate(const void *Buf,
|
||||||
|
uint64_t Size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next remark in the file.
|
||||||
|
*
|
||||||
|
* The value pointed to by the return value is invalidated by the next call to
|
||||||
|
* LLVMOptRemarkParserGetNext().
|
||||||
|
*
|
||||||
|
* If the parser reaches the end of the buffer, the return value will be NULL.
|
||||||
|
*
|
||||||
|
* In the case of an error, the return value will be NULL, and:
|
||||||
|
*
|
||||||
|
* 1) LLVMOptRemarkParserHasError() will return `1`.
|
||||||
|
*
|
||||||
|
* 2) LLVMOptRemarkParserGetErrorMessage() will return a descriptive error
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* An error may occur if:
|
||||||
|
*
|
||||||
|
* 1) An argument is invalid.
|
||||||
|
*
|
||||||
|
* 2) There is a YAML parsing error. This type of error aborts parsing
|
||||||
|
* immediately and returns `1`. It can occur on malformed YAML.
|
||||||
|
*
|
||||||
|
* 3) Remark parsing error. If this type of error occurs, the parser won't call
|
||||||
|
* the handler and will continue to the next one. It can occur on malformed
|
||||||
|
* remarks, like missing or extra fields in the file.
|
||||||
|
*
|
||||||
|
* Here is a quick example of the usage:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, Size);
|
||||||
|
* LLVMOptRemarkEntry *Remark = NULL;
|
||||||
|
* while ((Remark == LLVMOptRemarkParserGetNext(Parser))) {
|
||||||
|
* // use Remark
|
||||||
|
* }
|
||||||
|
* bool HasError = LLVMOptRemarkParserHasError(Parser);
|
||||||
|
* LLVMOptRemarkParserDispose(Parser);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
extern LLVMOptRemarkEntry *
|
||||||
|
LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `1` if the parser encountered an error while parsing the buffer.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
extern LLVMBool LLVMOptRemarkParserHasError(LLVMOptRemarkParserRef Parser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a null-terminated string containing an error message.
|
||||||
|
*
|
||||||
|
* In case of no error, the result is `NULL`.
|
||||||
|
*
|
||||||
|
* The memory of the string is bound to the lifetime of \p Parser. If
|
||||||
|
* LLVMOptRemarkParserDispose() is called, the memory of the string will be
|
||||||
|
* released.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
extern const char *
|
||||||
|
LLVMOptRemarkParserGetErrorMessage(LLVMOptRemarkParserRef Parser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases all the resources used by \p Parser.
|
||||||
|
*
|
||||||
|
* \since OPT_REMARKS_API_VERSION=0
|
||||||
|
*/
|
||||||
|
extern void LLVMOptRemarkParserDispose(LLVMOptRemarkParserRef Parser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} // endgoup LLVMCOPTREMARKS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* !defined(__cplusplus) */
|
||||||
|
|
||||||
|
#endif /* LLVM_C_OPT_REMARKS_H */
|
@ -15,6 +15,7 @@ add_subdirectory(MC)
|
|||||||
add_subdirectory(Object)
|
add_subdirectory(Object)
|
||||||
add_subdirectory(ObjectYAML)
|
add_subdirectory(ObjectYAML)
|
||||||
add_subdirectory(Option)
|
add_subdirectory(Option)
|
||||||
|
add_subdirectory(OptRemarks)
|
||||||
add_subdirectory(DebugInfo)
|
add_subdirectory(DebugInfo)
|
||||||
add_subdirectory(ExecutionEngine)
|
add_subdirectory(ExecutionEngine)
|
||||||
add_subdirectory(Target)
|
add_subdirectory(Target)
|
||||||
|
@ -35,6 +35,7 @@ subdirectories =
|
|||||||
BinaryFormat
|
BinaryFormat
|
||||||
ObjectYAML
|
ObjectYAML
|
||||||
Option
|
Option
|
||||||
|
OptRemarks
|
||||||
Passes
|
Passes
|
||||||
ProfileData
|
ProfileData
|
||||||
Support
|
Support
|
||||||
|
3
lib/OptRemarks/CMakeLists.txt
Normal file
3
lib/OptRemarks/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
add_llvm_library(LLVMOptRemarks
|
||||||
|
OptRemarksParser.cpp
|
||||||
|
)
|
22
lib/OptRemarks/LLVMBuild.txt
Normal file
22
lib/OptRemarks/LLVMBuild.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
;===- ./lib/OptRemarks/LLVMBuild.txt ---------------------------*- Conf -*--===;
|
||||||
|
;
|
||||||
|
; The LLVM Compiler Infrastructure
|
||||||
|
;
|
||||||
|
; This file is distributed under the University of Illinois Open Source
|
||||||
|
; License. See LICENSE.TXT for details.
|
||||||
|
;
|
||||||
|
;===------------------------------------------------------------------------===;
|
||||||
|
;
|
||||||
|
; This is an LLVMBuild description file for the components in this subdirectory.
|
||||||
|
;
|
||||||
|
; For more information on the LLVMBuild system, please see:
|
||||||
|
;
|
||||||
|
; http://llvm.org/docs/LLVMBuild.html
|
||||||
|
;
|
||||||
|
;===------------------------------------------------------------------------===;
|
||||||
|
|
||||||
|
[component_0]
|
||||||
|
type = Library
|
||||||
|
name = OptRemarks
|
||||||
|
parent = Libraries
|
||||||
|
required_libraries = Support
|
368
lib/OptRemarks/OptRemarksParser.cpp
Normal file
368
lib/OptRemarks/OptRemarksParser.cpp
Normal file
@ -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);
|
||||||
|
}
|
@ -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
|
add_llvm_tool(llvm-opt-report
|
||||||
OptReport.cpp
|
OptReport.cpp
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "llvm/Support/WithColor.h"
|
#include "llvm/Support/WithColor.h"
|
||||||
#include "llvm/Support/YAMLTraits.h"
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include "llvm-c/OptRemarks.h"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -142,104 +143,44 @@ typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,
|
|||||||
OptReportLocationInfo>>>> LocationInfoTy;
|
OptReportLocationInfo>>>> LocationInfoTy;
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
static void collectLocationInfo(yaml::Stream &Stream,
|
static bool readLocationInfo(LocationInfoTy &LocationInfo) {
|
||||||
LocationInfoTy &LocationInfo) {
|
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
|
||||||
SmallVector<char, 8> Tmp;
|
MemoryBuffer::getFile(InputFileName.c_str());
|
||||||
|
if (std::error_code EC = Buf.getError()) {
|
||||||
|
WithColor::error() << "Can't open file " << InputFileName << ": "
|
||||||
|
<< EC.message() << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: We're using the YAML parser here directly, instead of using the
|
StringRef Buffer = (*Buf)->getBuffer();
|
||||||
// YAMLTraits implementation, because the YAMLTraits implementation does not
|
LLVMOptRemarkParserRef Parser =
|
||||||
// support a way to handle only a subset of the input keys (it will error out
|
LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size());
|
||||||
// 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;
|
|
||||||
|
|
||||||
bool Transformed = Root->getRawTag() == "!Passed";
|
LLVMOptRemarkEntry *Remark = nullptr;
|
||||||
std::string Pass, File, Function;
|
while ((Remark = LLVMOptRemarkParserGetNext(Parser))) {
|
||||||
int Line = 0, Column = 1;
|
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 VectorizationFactor = 1;
|
||||||
int InterleaveCount = 1;
|
int InterleaveCount = 1;
|
||||||
int UnrollCount = 1;
|
int UnrollCount = 1;
|
||||||
|
|
||||||
for (auto &RootChild : *Root) {
|
for (const LLVMOptRemarkArg &Arg : Args) {
|
||||||
auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey());
|
StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len);
|
||||||
if (!Key)
|
StringRef ArgValue(Arg.Value.Str, Arg.Value.Len);
|
||||||
continue;
|
if (ArgKeyName == "VectorizationFactor")
|
||||||
StringRef KeyName = Key->getValue(Tmp);
|
ArgValue.getAsInteger(10, VectorizationFactor);
|
||||||
if (KeyName == "Pass") {
|
else if (ArgKeyName == "InterleaveCount")
|
||||||
auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
|
ArgValue.getAsInteger(10, InterleaveCount);
|
||||||
if (!Value)
|
else if (ArgKeyName == "UnrollCount")
|
||||||
continue;
|
ArgValue.getAsInteger(10, UnrollCount);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Line < 1 || File.empty())
|
if (Line < 1 || File.empty())
|
||||||
@ -268,22 +209,13 @@ static void collectLocationInfo(yaml::Stream &Stream,
|
|||||||
UpdateLLII(LI.Vectorized);
|
UpdateLLII(LI.Vectorized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static bool readLocationInfo(LocationInfoTy &LocationInfo) {
|
bool HasError = LLVMOptRemarkParserHasError(Parser);
|
||||||
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
|
if (HasError)
|
||||||
MemoryBuffer::getFileOrSTDIN(InputFileName);
|
WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n";
|
||||||
if (std::error_code EC = Buf.getError()) {
|
|
||||||
WithColor::error() << "Can't open file " << InputFileName << ": "
|
|
||||||
<< EC.message() << "\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceMgr SM;
|
LLVMOptRemarkParserDispose(Parser);
|
||||||
yaml::Stream Stream(Buf.get()->getBuffer(), SM);
|
return !HasError;
|
||||||
collectLocationInfo(Stream, LocationInfo);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool writeReport(LocationInfoTy &LocationInfo) {
|
static bool writeReport(LocationInfoTy &LocationInfo) {
|
||||||
|
@ -26,6 +26,7 @@ add_subdirectory(MI)
|
|||||||
add_subdirectory(Object)
|
add_subdirectory(Object)
|
||||||
add_subdirectory(ObjectYAML)
|
add_subdirectory(ObjectYAML)
|
||||||
add_subdirectory(Option)
|
add_subdirectory(Option)
|
||||||
|
add_subdirectory(OptRemarks)
|
||||||
add_subdirectory(Passes)
|
add_subdirectory(Passes)
|
||||||
add_subdirectory(ProfileData)
|
add_subdirectory(ProfileData)
|
||||||
add_subdirectory(Support)
|
add_subdirectory(Support)
|
||||||
|
8
unittests/OptRemarks/CMakeLists.txt
Normal file
8
unittests/OptRemarks/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
set(LLVM_LINK_COMPONENTS
|
||||||
|
OptRemarks
|
||||||
|
Support
|
||||||
|
)
|
||||||
|
|
||||||
|
add_llvm_unittest(OptRemarksTests
|
||||||
|
OptRemarksParsingTest.cpp
|
||||||
|
)
|
433
unittests/OptRemarks/OptRemarksParsingTest.cpp
Normal file
433
unittests/OptRemarks/OptRemarksParsingTest.cpp
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
//===- unittest/Support/OptRemarksParsingTest.cpp - OptTable tests --------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm-c/OptRemarks.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
template <size_t N> bool tryParse(const char (&Buf)[N]) {
|
||||||
|
LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1);
|
||||||
|
LLVMOptRemarkEntry *Remark = nullptr;
|
||||||
|
while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) {
|
||||||
|
EXPECT_TRUE(Remark == nullptr); // Only one remark per test.
|
||||||
|
Remark = NewRemark;
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(Remark != nullptr); // We need *exactly* one remark per test.
|
||||||
|
bool HasError = LLVMOptRemarkParserHasError(Parser);
|
||||||
|
LLVMOptRemarkParserDispose(Parser);
|
||||||
|
return !HasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
bool parseExpectError(const char (&Buf)[N], const char *Error) {
|
||||||
|
LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1);
|
||||||
|
LLVMOptRemarkEntry *Remark = nullptr;
|
||||||
|
while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) {
|
||||||
|
EXPECT_FALSE(NewRemark);
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(Remark == nullptr); // We are parsing only one malformed remark.
|
||||||
|
EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser));
|
||||||
|
bool MatchesError =
|
||||||
|
StringRef(LLVMOptRemarkParserGetErrorMessage(Parser)).contains(Error);
|
||||||
|
LLVMOptRemarkParserDispose(Parser);
|
||||||
|
|
||||||
|
return MatchesError;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksParsingEmpty) {
|
||||||
|
StringRef Buf = "\n"
|
||||||
|
"\n";
|
||||||
|
LLVMOptRemarkParserRef Parser =
|
||||||
|
LLVMOptRemarkParserCreate(Buf.data(), Buf.size());
|
||||||
|
LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser);
|
||||||
|
EXPECT_TRUE(NewRemark == nullptr); // No remark expected.
|
||||||
|
EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser));
|
||||||
|
EXPECT_TRUE(StringRef(LLVMOptRemarkParserGetErrorMessage(Parser))
|
||||||
|
.contains("document root is not of mapping type."));
|
||||||
|
LLVMOptRemarkParserDispose(Parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksParsingGood) {
|
||||||
|
EXPECT_TRUE(tryParse("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Callee: bar\n"
|
||||||
|
" - String: ' will not be inlined into '\n"
|
||||||
|
" - Caller: foo\n"
|
||||||
|
" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
|
||||||
|
" - String: ' because its definition is unavailable'\n"
|
||||||
|
""));
|
||||||
|
|
||||||
|
// No debug loc should also pass.
|
||||||
|
EXPECT_TRUE(tryParse("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Callee: bar\n"
|
||||||
|
" - String: ' will not be inlined into '\n"
|
||||||
|
" - Caller: foo\n"
|
||||||
|
" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
|
||||||
|
" - String: ' because its definition is unavailable'\n"
|
||||||
|
""));
|
||||||
|
|
||||||
|
// No args is also ok.
|
||||||
|
EXPECT_TRUE(tryParse("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
""));
|
||||||
|
|
||||||
|
// Different order.
|
||||||
|
EXPECT_TRUE(tryParse("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"DebugLoc: { Line: 3, Column: 12, File: file.c }\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Callee: bar\n"
|
||||||
|
" - String: ' will not be inlined into '\n"
|
||||||
|
" - Caller: foo\n"
|
||||||
|
" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
|
||||||
|
" - String: ' because its definition is unavailable'\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mandatory common part of a remark.
|
||||||
|
#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"
|
||||||
|
// Test all the types.
|
||||||
|
TEST(OptRemarks, OptRemarksParsingTypes) {
|
||||||
|
// Type: Passed
|
||||||
|
EXPECT_TRUE(tryParse("--- !Passed" COMMON_REMARK));
|
||||||
|
// Type: Missed
|
||||||
|
EXPECT_TRUE(tryParse("--- !Missed" COMMON_REMARK));
|
||||||
|
// Type: Analysis
|
||||||
|
EXPECT_TRUE(tryParse("--- !Analysis" COMMON_REMARK));
|
||||||
|
// Type: AnalysisFPCompute
|
||||||
|
EXPECT_TRUE(tryParse("--- !AnalysisFPCompute" COMMON_REMARK));
|
||||||
|
// Type: AnalysisAliasing
|
||||||
|
EXPECT_TRUE(tryParse("--- !AnalysisAliasing" COMMON_REMARK));
|
||||||
|
// Type: Failure
|
||||||
|
EXPECT_TRUE(tryParse("--- !Failure" COMMON_REMARK));
|
||||||
|
}
|
||||||
|
#undef COMMON_REMARK
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksParsingMissingFields) {
|
||||||
|
// No type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"---\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"",
|
||||||
|
"error: Type, Pass, Name or Function missing."));
|
||||||
|
// No pass.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"",
|
||||||
|
"error: Type, Pass, Name or Function missing."));
|
||||||
|
// No name.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"",
|
||||||
|
"error: Type, Pass, Name or Function missing."));
|
||||||
|
// No function.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"",
|
||||||
|
"error: Type, Pass, Name or Function missing."));
|
||||||
|
// Debug loc but no file.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { Line: 3, Column: 12 }\n"
|
||||||
|
"",
|
||||||
|
"DebugLoc node incomplete."));
|
||||||
|
// Debug loc but no line.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Column: 12 }\n"
|
||||||
|
"",
|
||||||
|
"DebugLoc node incomplete."));
|
||||||
|
// Debug loc but no column.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: 3 }\n"
|
||||||
|
"",
|
||||||
|
"DebugLoc node incomplete."));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksParsingWrongTypes) {
|
||||||
|
// Wrong debug loc type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: foo\n"
|
||||||
|
"",
|
||||||
|
"expected a value of mapping type."));
|
||||||
|
// Wrong line type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: b, Column: 12 }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of integer type."));
|
||||||
|
// Wrong column type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: 3, Column: c }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of integer type."));
|
||||||
|
// Wrong args type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args: foo\n"
|
||||||
|
"",
|
||||||
|
"wrong value type for key."));
|
||||||
|
// Wrong key type.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"{ A: a }: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"",
|
||||||
|
"key is not a string."));
|
||||||
|
// Debug loc with unknown entry.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Column: 12, Unknown: 12 }\n"
|
||||||
|
"",
|
||||||
|
"unknown entry in DebugLoc map."));
|
||||||
|
// Unknown entry.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Unknown: inline\n"
|
||||||
|
"",
|
||||||
|
"unknown key."));
|
||||||
|
// Not a scalar.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"",
|
||||||
|
"expected a value of scalar type."));
|
||||||
|
// Not a string file in debug loc.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: { a: b }, Column: 12, Line: 12 }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of scalar type."));
|
||||||
|
// Not a integer column in debug loc.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of scalar type."));
|
||||||
|
// Not a integer line in debug loc.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of scalar type."));
|
||||||
|
// Not a mapping type value for args.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
|
||||||
|
"",
|
||||||
|
"expected a value of scalar type."));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksParsingWrongArgs) {
|
||||||
|
// Multiple debug locs per arg.
|
||||||
|
EXPECT_TRUE(
|
||||||
|
parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Str: string\n"
|
||||||
|
" DebugLoc: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
" DebugLoc: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
"",
|
||||||
|
"only one DebugLoc entry is allowed per argument."));
|
||||||
|
// Multiple strings per arg.
|
||||||
|
EXPECT_TRUE(
|
||||||
|
parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Str: string\n"
|
||||||
|
" Str2: string\n"
|
||||||
|
" DebugLoc: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
"",
|
||||||
|
"only one string entry is allowed per argument."));
|
||||||
|
// No arg value.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Callee: ''\n"
|
||||||
|
" - DebugLoc: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
"",
|
||||||
|
"argument value is missing."));
|
||||||
|
// No arg value.
|
||||||
|
EXPECT_TRUE(parseExpectError("\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - DebugLoc: { File: a, Line: 1, Column: 2 }\n"
|
||||||
|
"",
|
||||||
|
"argument key is missing."));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OptRemarks, OptRemarksGoodStruct) {
|
||||||
|
StringRef Buf = "\n"
|
||||||
|
"--- !Missed\n"
|
||||||
|
"Pass: inline\n"
|
||||||
|
"Name: NoDefinition\n"
|
||||||
|
"DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
|
||||||
|
"Function: foo\n"
|
||||||
|
"Args:\n"
|
||||||
|
" - Callee: bar\n"
|
||||||
|
" - String: ' will not be inlined into '\n"
|
||||||
|
" - Caller: foo\n"
|
||||||
|
" DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
|
||||||
|
" - String: ' because its definition is unavailable'\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
LLVMOptRemarkParserRef Parser =
|
||||||
|
LLVMOptRemarkParserCreate(Buf.data(), Buf.size());
|
||||||
|
LLVMOptRemarkEntry *Remark = LLVMOptRemarkParserGetNext(Parser);
|
||||||
|
EXPECT_FALSE(Remark == nullptr);
|
||||||
|
EXPECT_EQ(StringRef(Remark->RemarkType.Str, 7), "!Missed");
|
||||||
|
EXPECT_EQ(Remark->RemarkType.Len, 7U);
|
||||||
|
EXPECT_EQ(StringRef(Remark->PassName.Str, 6), "inline");
|
||||||
|
EXPECT_EQ(Remark->PassName.Len, 6U);
|
||||||
|
EXPECT_EQ(StringRef(Remark->RemarkName.Str, 12), "NoDefinition");
|
||||||
|
EXPECT_EQ(Remark->RemarkName.Len, 12U);
|
||||||
|
EXPECT_EQ(StringRef(Remark->FunctionName.Str, 3), "foo");
|
||||||
|
EXPECT_EQ(Remark->FunctionName.Len, 3U);
|
||||||
|
EXPECT_EQ(StringRef(Remark->DebugLoc.SourceFile.Str, 6), "file.c");
|
||||||
|
EXPECT_EQ(Remark->DebugLoc.SourceFile.Len, 6U);
|
||||||
|
EXPECT_EQ(Remark->DebugLoc.SourceLineNumber, 3U);
|
||||||
|
EXPECT_EQ(Remark->DebugLoc.SourceColumnNumber, 12U);
|
||||||
|
EXPECT_EQ(Remark->Hotness, 0U);
|
||||||
|
EXPECT_EQ(Remark->NumArgs, 4U);
|
||||||
|
// Arg 0
|
||||||
|
{
|
||||||
|
LLVMOptRemarkArg &Arg = Remark->Args[0];
|
||||||
|
EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Callee");
|
||||||
|
EXPECT_EQ(Arg.Key.Len, 6U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.Value.Str, 3), "bar");
|
||||||
|
EXPECT_EQ(Arg.Value.Len, 3U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
|
||||||
|
}
|
||||||
|
// Arg 1
|
||||||
|
{
|
||||||
|
LLVMOptRemarkArg &Arg = Remark->Args[1];
|
||||||
|
EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String");
|
||||||
|
EXPECT_EQ(Arg.Key.Len, 6U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.Value.Str, 26), " will not be inlined into ");
|
||||||
|
EXPECT_EQ(Arg.Value.Len, 26U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
|
||||||
|
}
|
||||||
|
// Arg 2
|
||||||
|
{
|
||||||
|
LLVMOptRemarkArg &Arg = Remark->Args[2];
|
||||||
|
EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Caller");
|
||||||
|
EXPECT_EQ(Arg.Key.Len, 6U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.Value.Str, 3), "foo");
|
||||||
|
EXPECT_EQ(Arg.Value.Len, 3U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 6), "file.c");
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 6U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 2U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
|
||||||
|
}
|
||||||
|
// Arg 3
|
||||||
|
{
|
||||||
|
LLVMOptRemarkArg &Arg = Remark->Args[3];
|
||||||
|
EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String");
|
||||||
|
EXPECT_EQ(Arg.Key.Len, 6U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.Value.Str, 38),
|
||||||
|
" because its definition is unavailable");
|
||||||
|
EXPECT_EQ(Arg.Value.Len, 38U);
|
||||||
|
EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
|
||||||
|
EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(LLVMOptRemarkParserGetNext(Parser), nullptr);
|
||||||
|
|
||||||
|
EXPECT_FALSE(LLVMOptRemarkParserHasError(Parser));
|
||||||
|
LLVMOptRemarkParserDispose(Parser);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user