261 lines · c
1//===- RemarkUtilHelpers.h ------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Helpers for remark utilites10//11//===----------------------------------------------------------------------===//12#include "llvm/ADT/StringExtras.h"13#include "llvm/ADT/StringRef.h"14#include "llvm/Remarks/Remark.h"15#include "llvm/Remarks/RemarkFormat.h"16#include "llvm/Remarks/RemarkParser.h"17#include "llvm/Remarks/RemarkSerializer.h"18#include "llvm/Support/CommandLine.h"19#include "llvm/Support/Error.h"20#include "llvm/Support/FileSystem.h"21#include "llvm/Support/MemoryBuffer.h"22#include "llvm/Support/Regex.h"23#include "llvm/Support/StringSaver.h"24#include "llvm/Support/ToolOutputFile.h"25 26// Keep input + output help + names consistent across the various modes via a27// hideous macro.28#define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT) \29 static cl::opt<std::string> InputFileName(cl::Positional, cl::init("-"), \30 cl::desc("<input file>"), \31 cl::sub(SUBOPT)); \32 static cl::opt<std::string> OutputFileName( \33 "o", cl::init("-"), cl::desc("Output"), cl::value_desc("filename"), \34 cl::sub(SUBOPT));35 36// Keep Input format and names consistent accross the modes via a macro.37#define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT) \38 static cl::opt<Format> InputFormat( \39 "parser", cl::init(Format::Auto), \40 cl::desc("Input remark format to parse"), \41 cl::values( \42 clEnumValN(Format::Auto, "auto", "Automatic detection (default)"), \43 clEnumValN(Format::YAML, "yaml", "YAML"), \44 clEnumValN(Format::Bitstream, "bitstream", "Bitstream")), \45 cl::sub(SUBOPT));46 47#define OUTPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT) \48 static cl::opt<Format> OutputFormat( \49 "serializer", cl::init(Format::Auto), \50 cl::desc("Output remark format to serialize"), \51 cl::values(clEnumValN(Format::Auto, "auto", \52 "Automatic detection based on output file " \53 "extension or parser format (default)"), \54 clEnumValN(Format::YAML, "yaml", "YAML"), \55 clEnumValN(Format::Bitstream, "bitstream", "Bitstream")), \56 cl::sub(SUBOPT));57 58#define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT) \59 static cl::opt<bool> UseDebugLoc( \60 "use-debug-loc", \61 cl::desc( \62 "Add debug loc information when generating tables for " \63 "functions. The loc is represented as (path:line number:column " \64 "number)"), \65 cl::init(false), cl::sub(SUBOPT));66 67#define REMARK_FILTER_COMMAND_LINE_OPTIONS(SUBOPT) \68 static cl::opt<std::string> FunctionOpt( \69 "function", cl::sub(SUBOPT), cl::ValueOptional, \70 cl::desc("Optional function name to filter collection by.")); \71 static cl::opt<std::string> FunctionOptRE( \72 "rfunction", cl::sub(SUBOPT), cl::ValueOptional, \73 cl::desc("Optional function name to filter collection by " \74 "(accepts regular expressions).")); \75 static cl::opt<std::string> RemarkNameOpt( \76 "remark-name", \77 cl::desc("Optional remark name to filter collection by."), \78 cl::ValueOptional, cl::sub(SUBOPT)); \79 static cl::opt<std::string> RemarkNameOptRE( \80 "rremark-name", \81 cl::desc("Optional remark name to filter collection by " \82 "(accepts regular expressions)."), \83 cl::ValueOptional, cl::sub(SUBOPT)); \84 static cl::opt<std::string> PassNameOpt( \85 "pass-name", cl::ValueOptional, \86 cl::desc("Optional remark pass name to filter collection by."), \87 cl::sub(SUBOPT)); \88 static cl::opt<std::string> PassNameOptRE( \89 "rpass-name", cl::ValueOptional, \90 cl::desc("Optional remark pass name to filter collection " \91 "by (accepts regular expressions)."), \92 cl::sub(SUBOPT)); \93 static cl::opt<Type> RemarkTypeOpt( \94 "remark-type", \95 cl::desc("Optional remark type to filter collection by."), \96 cl::values(clEnumValN(Type::Unknown, "unknown", "UNKOWN"), \97 clEnumValN(Type::Passed, "passed", "PASSED"), \98 clEnumValN(Type::Missed, "missed", "MISSED"), \99 clEnumValN(Type::Analysis, "analysis", "ANALYSIS"), \100 clEnumValN(Type::AnalysisFPCommute, "analysis-fp-commute", \101 "ANALYSIS_FP_COMMUTE"), \102 clEnumValN(Type::AnalysisAliasing, "analysis-aliasing", \103 "ANALYSIS_ALIASING"), \104 clEnumValN(Type::Failure, "failure", "FAILURE")), \105 cl::sub(SUBOPT)); \106 static cl::opt<std::string> RemarkFilterArgByOpt( \107 "filter-arg-by", \108 cl::desc("Optional remark arg to filter collection by."), \109 cl::ValueOptional, cl::sub(SUBOPT)); \110 static cl::opt<std::string> RemarkArgFilterOptRE( \111 "rfilter-arg-by", \112 cl::desc("Optional remark arg to filter collection by " \113 "(accepts regular expressions)."), \114 cl::sub(SUBOPT), cl::ValueOptional);115 116#define REMARK_FILTER_SETUP_FUNC() \117 static Expected<Filters> getRemarkFilters() { \118 auto MaybeFunctionFilter = \119 FilterMatcher::createExactOrRE(FunctionOpt, FunctionOptRE); \120 if (!MaybeFunctionFilter) \121 return MaybeFunctionFilter.takeError(); \122 \123 auto MaybeRemarkNameFilter = \124 FilterMatcher::createExactOrRE(RemarkNameOpt, RemarkNameOptRE); \125 if (!MaybeRemarkNameFilter) \126 return MaybeRemarkNameFilter.takeError(); \127 \128 auto MaybePassNameFilter = \129 FilterMatcher::createExactOrRE(PassNameOpt, PassNameOptRE); \130 if (!MaybePassNameFilter) \131 return MaybePassNameFilter.takeError(); \132 \133 auto MaybeRemarkArgFilter = FilterMatcher::createExactOrRE( \134 RemarkFilterArgByOpt, RemarkArgFilterOptRE); \135 if (!MaybeRemarkArgFilter) \136 return MaybeRemarkArgFilter.takeError(); \137 \138 std::optional<Type> TypeFilter; \139 if (RemarkTypeOpt.getNumOccurrences()) \140 TypeFilter = RemarkTypeOpt.getValue(); \141 \142 return Filters{std::move(*MaybeFunctionFilter), \143 std::move(*MaybeRemarkNameFilter), \144 std::move(*MaybePassNameFilter), \145 std::move(*MaybeRemarkArgFilter), TypeFilter}; \146 }147 148namespace llvm {149namespace remarks {150Expected<std::unique_ptr<MemoryBuffer>>151getInputMemoryBuffer(StringRef InputFileName);152Expected<std::unique_ptr<ToolOutputFile>>153getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags);154Expected<std::unique_ptr<ToolOutputFile>>155getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat);156 157/// Choose the serializer format. If \p SelectedFormat is Format::Auto, try to158/// detect the format based on the extension of \p OutputFileName or fall back159/// to \p DefaultFormat.160Format getSerializerFormat(StringRef OutputFileName, Format SelectedFormat,161 Format DefaultFormat);162 163/// Filter object which can be either a string or a regex to match with the164/// remark properties.165class FilterMatcher {166 Regex FilterRE;167 std::string FilterStr;168 bool IsRegex;169 170 FilterMatcher(StringRef Filter, bool IsRegex)171 : FilterRE(Filter), FilterStr(Filter), IsRegex(IsRegex) {}172 173 static Expected<FilterMatcher> createRE(StringRef Arg, StringRef Value);174 175public:176 static FilterMatcher createExact(StringRef Filter) { return {Filter, false}; }177 178 static Expected<FilterMatcher>179 createRE(const llvm::cl::opt<std::string> &Arg);180 181 static Expected<FilterMatcher> createRE(StringRef Filter,182 const cl::list<std::string> &Arg);183 184 static Expected<std::optional<FilterMatcher>>185 createExactOrRE(const llvm::cl::opt<std::string> &ExactArg,186 const llvm::cl::opt<std::string> &REArg);187 188 static FilterMatcher createAny() { return {".*", true}; }189 190 bool match(StringRef StringToMatch) const {191 if (IsRegex)192 return FilterRE.match(StringToMatch);193 return FilterStr == StringToMatch.trim().str();194 }195};196 197/// Filter out remarks based on remark properties (function, remark name, pass198/// name, argument values and type).199struct Filters {200 std::optional<FilterMatcher> FunctionFilter;201 std::optional<FilterMatcher> RemarkNameFilter;202 std::optional<FilterMatcher> PassNameFilter;203 std::optional<FilterMatcher> ArgFilter;204 std::optional<Type> RemarkTypeFilter;205 206 /// Returns true if \p Remark satisfies all the provided filters.207 bool filterRemark(const Remark &Remark);208};209 210/// Helper to construct Remarks using an API similar to DiagnosticInfo.211/// Once this is more fully featured, consider implementing DiagnosticInfo using212/// RemarkBuilder.213class RemarkBuilder {214 BumpPtrAllocator Alloc;215 UniqueStringSaver Strs;216 217public:218 Remark R;219 struct Argument {220 std::string Key;221 std::string Val;222 std::optional<RemarkLocation> Loc;223 Argument(StringRef Key, StringRef Val,224 std::optional<RemarkLocation> Loc = std::nullopt)225 : Key(Key), Val(Val), Loc(Loc) {}226 Argument(StringRef Key, int Val,227 std::optional<RemarkLocation> Loc = std::nullopt)228 : Key(Key), Val(itostr(Val)), Loc(Loc) {}229 };230 231 RemarkBuilder(Type RemarkType, StringRef PassName, StringRef RemarkName,232 StringRef FunctionName)233 : Strs(Alloc) {234 R.RemarkType = RemarkType;235 R.PassName = Strs.save(PassName);236 R.RemarkName = Strs.save(RemarkName);237 R.FunctionName = Strs.save(FunctionName);238 }239 240 RemarkBuilder &operator<<(Argument &&Arg) {241 auto &RArg = R.Args.emplace_back(Strs.save(Arg.Key), Strs.save(Arg.Val));242 RArg.Loc = Arg.Loc;243 return *this;244 }245 246 RemarkBuilder &operator<<(const char *Str) {247 R.Args.emplace_back("String", Str);248 return *this;249 }250 251 RemarkBuilder &operator<<(StringRef Str) {252 R.Args.emplace_back("String", Strs.save(Str));253 return *this;254 }255};256 257using NV = RemarkBuilder::Argument;258 259} // namespace remarks260} // namespace llvm261