Index: include/llvm-c/OptRemarks.h =================================================================== --- /dev/null +++ include/llvm-c/OptRemarks.h @@ -0,0 +1,199 @@ +/*===-- 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 +extern "C" { +#else +#include +#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: + LLVMOptRemarkStringRef PassName; + // Name: + LLVMOptRemarkStringRef RemarkName; + // Function: + 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 has to 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 by the return value is invalidated by the next call to + * LLVMOptRemarkParserGetNext(). + * + * In the case of the parser reaching 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 on 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 */ Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(Object) add_subdirectory(ObjectYAML) add_subdirectory(Option) +add_subdirectory(OptRemarks) add_subdirectory(DebugInfo) add_subdirectory(ExecutionEngine) add_subdirectory(Target) Index: lib/LLVMBuild.txt =================================================================== --- lib/LLVMBuild.txt +++ lib/LLVMBuild.txt @@ -35,6 +35,7 @@ BinaryFormat ObjectYAML Option + OptRemarks Passes ProfileData Support Index: lib/OptRemarks/CMakeLists.txt =================================================================== --- /dev/null +++ lib/OptRemarks/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMOptRemarks + OptRemarksParser.cpp +) Index: lib/OptRemarks/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/OptRemarks/LLVMBuild.txt @@ -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 Index: lib/OptRemarks/OptRemarksParser.cpp =================================================================== --- /dev/null +++ lib/OptRemarks/OptRemarksParser.cpp @@ -0,0 +1,375 @@ +//===- 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 LastRemark; + // Temporary parsing buffer for the arguments. + SmallVector 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 *Args; + // Mandatory. Will Skip the handler if the remark does not contain them. + StringRef Tag; // The type of the remark. e.g. Passed, Failed, etc. + StringRef Pass; + StringRef Name; + StringRef Function; + // Optional. + StringRef File; + Optional Line; + Optional Column; + Optional Hotness; + // Set to `true` when we successfully parse the YAML but the remark is + // either incomplete or contains extra information. + bool SkipThisOne = false; + + ParseState(SmallVectorImpl &Args) : Args(&Args) {} + // Use TmpArgs 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); + } + + bool parseYAMLElement(yaml::Document &Remark); + +private: + // Parse one value to a string. + bool parseValue(StringRef &Result, yaml::KeyValueNode &Node); + // Parse one value to an unsigned. + bool parseValue(Optional &Result, yaml::KeyValueNode &Node); + // Parse a debug location. + bool parseDebugLoc(StringRef &File, Optional &Line, + Optional &Column, yaml::KeyValueNode &Node); + // Parse an argument. + bool parseArg(SmallVectorImpl &TmpArgs, yaml::Node &Node); + // Report an error to the error stream. + bool error(StringRef Report, yaml::Node &N) { + Stream.printError(&N, Twine(Report) + Twine('\n')); + HadAnyErrors = true; + return true; + } + + // Report an error and skip the handler call for the current remark. + bool errorAndSkip(StringRef Report, yaml::Node &N) { + State.SkipThisOne = true; + return error(Report, N); + } + + static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { + auto *Parser = static_cast(Ctx); + Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false, + /*ShowKindLabels*/ true); + } +}; + +bool RemarkParser::parseValue(StringRef &Result, yaml::KeyValueNode &Node) { + auto *Value = dyn_cast(Node.getValue()); + if (!Value) + return true; + Result = Value->getRawValue(); + + if (Result.front() == '\'') + Result = Result.drop_front(); + + if (Result.back() == '\'') + Result = Result.drop_back(); + + return false; +} + +bool RemarkParser::parseValue(Optional &Result, + yaml::KeyValueNode &Node) { + SmallVector Tmp; + auto *Value = dyn_cast(Node.getValue()); + if (!Value) + return true; + unsigned UnsignedValue = 0; + if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) + return errorAndSkip("expecting a value of integer type.", Node); + Result = UnsignedValue; + return false; +} + +bool RemarkParser::parseDebugLoc(StringRef &File, Optional &Line, + Optional &Column, + yaml::KeyValueNode &Node) { + auto *DebugLoc = dyn_cast(Node.getValue()); + if (!DebugLoc) + return errorAndSkip("expecting a value of mapping type.", Node); + + // Save the current skip bool to check if it changed during the parsing of + // the debug loc. + bool oldSkipThisOne = State.SkipThisOne; + State.SkipThisOne = false; + + for (yaml::KeyValueNode &DLNode : *DebugLoc) { + auto *Key = dyn_cast(DLNode.getKey()); + if (!Key) + continue; + StringRef KeyName = Key->getRawValue(); + if (KeyName == "File") { + if (parseValue(File, DLNode)) + continue; + } else if (KeyName == "Column") { + if (parseValue(Column, DLNode)) + continue; + } else if (KeyName == "Line") { + if (parseValue(Line, DLNode)) + continue; + } else { + errorAndSkip("unknown entry in DebugLoc map.", DLNode); + } + } + + // If any of the debug loc fields is missing, return an error. + if (File.empty() || !Line || !Column) + errorAndSkip("DebugLoc node incomplete.", Node); + + // Switch them back. + std::swap(oldSkipThisOne, State.SkipThisOne); + + // Propagate any errors that occurred in this function to the parent. + State.SkipThisOne |= oldSkipThisOne; + + return oldSkipThisOne; +} + +bool RemarkParser::parseArg(SmallVectorImpl &Args, + yaml::Node &Node) { + auto *ArgMap = dyn_cast(&Node); + if (!ArgMap) + return true; + + StringRef ValueStr; + StringRef KeyStr; + StringRef File; + Optional Line; + Optional Column; + + // Save the current skip bool to check if it changed during the parsing of + // the debug loc. + bool oldSkipThisOne = State.SkipThisOne; + State.SkipThisOne = false; + + for (yaml::KeyValueNode &ArgEntry : *ArgMap) { + auto *Key = dyn_cast(ArgEntry.getKey()); + if (!Key && errorAndSkip("key is not a string.", ArgEntry)) + continue; + + StringRef KeyName = Key->getRawValue(); + + // Try to parse debug locs. + if (KeyName == "DebugLoc") { + // Can't have multiple DebugLoc entries per file. + if ((!File.empty() || Line || Column)) + errorAndSkip("only one DebugLoc entry is allowed per argument.", + ArgEntry); + + parseDebugLoc(File, Line, Column, ArgEntry); + continue; + } + + // If we already have a string, error out. + if (!ValueStr.empty() && + errorAndSkip("only one string entry is allowed per argument.", + ArgEntry)) + continue; + + // Try to parse a string. + if (parseValue(ValueStr, ArgEntry) && + errorAndSkip("invalid string for argument", ArgEntry)) + continue; + + // Keep the key from the string. + KeyStr = KeyName; + } + + if (ValueStr.empty() || KeyStr.empty()) + errorAndSkip("value or key missing.", *ArgMap); + + // Switch them back. + std::swap(oldSkipThisOne, State.SkipThisOne); + + // Propagate any errors that occurred in this function to the parent. + State.SkipThisOne |= oldSkipThisOne; + + if (!oldSkipThisOne) + Args.push_back(LLVMOptRemarkArg{ + LLVMOptRemarkStringRef{KeyStr.data(), + static_cast(KeyStr.size())}, + LLVMOptRemarkStringRef{ValueStr.data(), + static_cast(ValueStr.size())}, + LLVMOptRemarkDebugLoc{ + LLVMOptRemarkStringRef{File.data(), + static_cast(File.size())}, + Line ? *Line : 0, Column ? *Column : 0}}); + + return oldSkipThisOne; +} + +bool RemarkParser::parseYAMLElement(yaml::Document &Remark) { + // Parsing a new remark, clear the previous one. + LastRemark = None; + State = ParseState(TmpArgs); + + auto *Root = dyn_cast(Remark.getRoot()); + if (!Root) + return false; + + State.Tag = Root->getRawTag(); + + for (yaml::KeyValueNode &RemarkField : *Root) { + auto *Key = dyn_cast(RemarkField.getKey()); + if (!Key && errorAndSkip("key is not a string.", RemarkField)) + continue; + + StringRef KeyName = Key->getRawValue(); + if (KeyName == "Pass") { + if (parseValue(State.Pass, RemarkField) && + errorAndSkip("failed to parse value.", RemarkField)) + continue; + } else if (KeyName == "Name") { + if (parseValue(State.Name, RemarkField) && + errorAndSkip("failed to parse value.", RemarkField)) + continue; + } else if (KeyName == "Function") { + if (parseValue(State.Function, RemarkField) && + errorAndSkip("failed to parse value.", RemarkField)) + continue; + } else if (KeyName == "Hotness") { + if (parseValue(State.Hotness, RemarkField) && + errorAndSkip("failed to parse value.", RemarkField)) + continue; + } else if (KeyName == "DebugLoc") { + if (parseDebugLoc(State.File, State.Line, State.Column, RemarkField)) + continue; + } else if (KeyName == "Args") { + auto *Args = dyn_cast(RemarkField.getValue()); + if (!Args && errorAndSkip("wrong value type for key.", RemarkField)) + continue; + + for (yaml::Node &Arg : *Args) + parseArg(*State.Args, Arg); + } else { + errorAndSkip("unknown key.", RemarkField); + } + } + + // If the YAML parsing failed, don't even continue parsing. We might + // encounter malformed YAML. + if (Stream.failed()) { + error("YAML parsing failed.", *Remark.getRoot()); + return true; + } + + // Check if any of the mandatory fields are missing. + if (State.Tag.empty() || State.Pass.empty() || State.Name.empty() || + State.Function.empty()) + errorAndSkip("Tag, Pass, Name or Function missing.", *Remark.getRoot()); + + // Call the handler if we don't need to Skip it. + if (!State.SkipThisOne) + LastRemark = LLVMOptRemarkEntry{ + LLVMOptRemarkStringRef{State.Tag.data(), + static_cast(State.Tag.size())}, + LLVMOptRemarkStringRef{State.Pass.data(), + static_cast(State.Pass.size())}, + LLVMOptRemarkStringRef{State.Name.data(), + static_cast(State.Name.size())}, + LLVMOptRemarkStringRef{State.Function.data(), + static_cast(State.Function.size())}, + LLVMOptRemarkDebugLoc{ + LLVMOptRemarkStringRef{State.File.data(), + static_cast(State.File.size())}, + State.Line ? *State.Line : 0, State.Column ? *State.Column : 0}, + State.Hotness ? *State.Hotness : 0, + static_cast(State.Args->size()), + State.Args->data()}; + + return false; +} +} // 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(Buf), Size))); +} + +extern "C" LLVMOptRemarkEntry * +LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser) { + RemarkParser &TheParser = *unwrap(Parser); + // Check for EOF. + if (TheParser.di == TheParser.Stream.end()) + return nullptr; + + // Try to parse an entry. + bool HadError = TheParser.parseYAMLElement(*TheParser.di); + if (HadError) + return nullptr; + + // Move on. + ++TheParser.di; + + // Return the just-parsed remark. + if (Optional &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); +} Index: tools/llvm-opt-report/CMakeLists.txt =================================================================== --- tools/llvm-opt-report/CMakeLists.txt +++ tools/llvm-opt-report/CMakeLists.txt @@ -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 Index: tools/llvm-opt-report/OptReport.cpp =================================================================== --- tools/llvm-opt-report/OptReport.cpp +++ tools/llvm-opt-report/OptReport.cpp @@ -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 #include #include @@ -142,104 +143,44 @@ OptReportLocationInfo>>>> LocationInfoTy; } // anonymous namespace -static void collectLocationInfo(yaml::Stream &Stream, - LocationInfoTy &LocationInfo) { - SmallVector 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(Doc.getRoot()); - if (!Root) - continue; +static bool readLocationInfo(LocationInfoTy &LocationInfo) { + ErrorOr> 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 Args(Remark->Args, Remark->NumArgs); int VectorizationFactor = 1; int InterleaveCount = 1; int UnrollCount = 1; - for (auto &RootChild : *Root) { - auto *Key = dyn_cast(RootChild.getKey()); - if (!Key) - continue; - StringRef KeyName = Key->getValue(Tmp); - if (KeyName == "Pass") { - auto *Value = dyn_cast(RootChild.getValue()); - if (!Value) - continue; - Pass = Value->getValue(Tmp); - } else if (KeyName == "Function") { - auto *Value = dyn_cast(RootChild.getValue()); - if (!Value) - continue; - Function = Value->getValue(Tmp); - } else if (KeyName == "DebugLoc") { - auto *DebugLoc = dyn_cast(RootChild.getValue()); - if (!DebugLoc) - continue; - - for (auto &DLChild : *DebugLoc) { - auto *DLKey = dyn_cast(DLChild.getKey()); - if (!DLKey) - continue; - StringRef DLKeyName = DLKey->getValue(Tmp); - if (DLKeyName == "File") { - auto *Value = dyn_cast(DLChild.getValue()); - if (!Value) - continue; - File = Value->getValue(Tmp); - } else if (DLKeyName == "Line") { - auto *Value = dyn_cast(DLChild.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, Line); - } else if (DLKeyName == "Column") { - auto *Value = dyn_cast(DLChild.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, Column); - } - } - } else if (KeyName == "Args") { - auto *Args = dyn_cast(RootChild.getValue()); - if (!Args) - continue; - for (auto &ArgChild : *Args) { - auto *ArgMap = dyn_cast(&ArgChild); - if (!ArgMap) - continue; - for (auto &ArgKV : *ArgMap) { - auto *ArgKey = dyn_cast(ArgKV.getKey()); - if (!ArgKey) - continue; - StringRef ArgKeyName = ArgKey->getValue(Tmp); - if (ArgKeyName == "VectorizationFactor") { - auto *Value = dyn_cast(ArgKV.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, VectorizationFactor); - } else if (ArgKeyName == "InterleaveCount") { - auto *Value = dyn_cast(ArgKV.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, InterleaveCount); - } else if (ArgKeyName == "UnrollCount") { - auto *Value = dyn_cast(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()) @@ -268,22 +209,13 @@ UpdateLLII(LI.Vectorized); } } -} - -static bool readLocationInfo(LocationInfoTy &LocationInfo) { - ErrorOr> 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) { Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory(Object) add_subdirectory(ObjectYAML) add_subdirectory(Option) +add_subdirectory(OptRemarks) add_subdirectory(Passes) add_subdirectory(ProfileData) add_subdirectory(Support) Index: unittests/OptRemarks/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/OptRemarks/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + OptRemarks + Support + ) + +add_llvm_unittest(OptRemarksTests + OptRemarksParsingTest.cpp + ) Index: unittests/OptRemarks/OptRemarksParsingTest.cpp =================================================================== --- /dev/null +++ unittests/OptRemarks/OptRemarksParsingTest.cpp @@ -0,0 +1,355 @@ +//===- 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 bool tryParse(const char (&Buf)[N]) { + LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1); + while (LLVMOptRemarkParserGetNext(Parser)) + continue; + bool HasError = LLVMOptRemarkParserHasError(Parser); + LLVMOptRemarkParserDispose(Parser); + return HasError; +} + +template +bool parseExpectError(const char (&Buf)[N], const char *Error) { + LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1); + while (LLVMOptRemarkParserGetNext(Parser)) + continue; + bool HasError = LLVMOptRemarkParserHasError(Parser); + bool MatchError = + HasError && + StringRef(LLVMOptRemarkParserGetErrorMessage(Parser)).contains(Error); + LLVMOptRemarkParserDispose(Parser); + + return HasError && MatchError; +} + +TEST(OptRemarks, OptRemarksParsingGood) { + EXPECT_FALSE(tryParse(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: file.c, Line: 3, Column: 12 } +Function: foo +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + DebugLoc: { File: file.c, Line: 2, Column: 0 } + - String: ' because its definition is unavailable' +)YAML")); + + // No debug loc should also pass. + EXPECT_FALSE(tryParse(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + DebugLoc: { File: file.c, Line: 2, Column: 0 } + - String: ' because its definition is unavailable' +)YAML")); + + // No args is also ok. + EXPECT_FALSE(tryParse(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: file.c, Line: 3, Column: 12 } +Function: foo +)YAML")); + + // Different order. + EXPECT_FALSE(tryParse(R"YAML( +--- !Missed +DebugLoc: { Line: 3, Column: 12, File: file.c } +Function: foo +Name: NoDefinition +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + DebugLoc: { File: file.c, Line: 2, Column: 0 } + - String: ' because its definition is unavailable' +Pass: inline +)YAML")); +} + +// Mandatory common part of a remark. +#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n" +// Test all the tags. +TEST(OptRemarks, OptRemarksParsingTags) { + // Tag: Passed + EXPECT_FALSE(tryParse("--- !Passed" COMMON_REMARK)); + // Tag: Missed + EXPECT_FALSE(tryParse("--- !Missed" COMMON_REMARK)); + // Tag: Analysis + EXPECT_FALSE(tryParse("--- !Analysis" COMMON_REMARK)); + // Tag: AnalysisFPCompute + EXPECT_FALSE(tryParse("--- !AnalysisFPCompute" COMMON_REMARK)); + // Tag: AnalysisAliasing + EXPECT_FALSE(tryParse("--- !AnalysisAliasing" COMMON_REMARK)); + // Tag: Failure + EXPECT_FALSE(tryParse("--- !Failure" COMMON_REMARK)); +} +#undef COMMON_REMARK + +TEST(OptRemarks, OptRemarksParsingMissingFields) { + // No tag. + EXPECT_TRUE(parseExpectError(R"YAML( +--- +Pass: inline +Name: NoDefinition +Function: foo +)YAML", + "error: Tag, Pass, Name or Function missing.")); + // No pass. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Name: NoDefinition +Function: foo +)YAML", + "error: Tag, Pass, Name or Function missing.")); + // No name. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Function: foo +)YAML", + "error: Tag, Pass, Name or Function missing.")); + // No function. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +)YAML", + "error: Tag, Pass, Name or Function missing.")); + // Debug loc but no file. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { Line: 3, Column: 12 } +)YAML", + "DebugLoc node incomplete.")); + // Debug loc but no line. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { File: file.c, Column: 12 } +)YAML", + "DebugLoc node incomplete.")); + // Debug loc but no column. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { File: file.c, Line: 3 } +)YAML", + "DebugLoc node incomplete.")); +} + +TEST(OptRemarks, OptRemarksParsingWrongTypes) { + // Wrong debug loc type. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: foo +)YAML", + "expecting a value of mapping type.")); + // Wrong line type. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { File: file.c, Line: b, Column: 12 } +)YAML", + "expecting a value of integer type.")); + // Wrong column type. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { File: file.c, Line: 3, Column: c } +)YAML", + "expecting a value of integer type.")); + // Wrong args type. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +Args: foo +)YAML", + "wrong value type for key.")); + // Wrong key type. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +{ A: a }: inline +Name: NoDefinition +Function: foo +)YAML", + "key is not a string.")); + // Debug loc with unknown entry. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +DebugLoc: { File: file.c, Column: 12, Unknown: 12 } +)YAML", + "unknown entry in DebugLoc map.")); + // Unknown entry. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Unknown: inline +)YAML", + "unknown key.")); +} + +TEST(OptRemarks, OptRemarksParsingWrongArgs) { + // Multiple debug locs per arg. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +Args: + - Str: string + DebugLoc: { File: a, Line: 1, Column: 2 } + DebugLoc: { File: a, Line: 1, Column: 2 } +)YAML", + "only one DebugLoc entry is allowed per argument.")); + // Multiple strings per arg. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +Args: + - Str: string + Str2: string + DebugLoc: { File: a, Line: 1, Column: 2 } +)YAML", + "only one string entry is allowed per argument.")); + // Multiple strings per arg. + EXPECT_TRUE(parseExpectError(R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +Function: foo +Args: + - DebugLoc: { File: a, Line: 1, Column: 2 } +)YAML", + "value or key missing.")); +} + +TEST(OptRemarks, OptRemarksGoodStruct) { + StringRef Buf = R"YAML( +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: file.c, Line: 3, Column: 12 } +Function: foo +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + DebugLoc: { File: file.c, Line: 2, Column: 0 } + - String: ' because its definition is unavailable' +)YAML"; + + LLVMOptRemarkParserRef Parser = + LLVMOptRemarkParserCreate(Buf.data(), Buf.size()); + LLVMOptRemarkEntry *Remark = LLVMOptRemarkParserGetNext(Parser); + 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); +}