| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Helpers for remark utilites |
| 10 | // |
| 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 a |
| 27 | // 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 () \ |
| 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 | |
| 148 | namespace llvm { |
| 149 | namespace remarks { |
| 150 | Expected<std::unique_ptr<MemoryBuffer>> |
| 151 | (StringRef InputFileName); |
| 152 | Expected<std::unique_ptr<ToolOutputFile>> |
| 153 | (StringRef OutputFileName, sys::fs::OpenFlags Flags); |
| 154 | Expected<std::unique_ptr<ToolOutputFile>> |
| 155 | (StringRef OutputFileName, Format OutputFormat); |
| 156 | |
| 157 | /// Choose the serializer format. If \p SelectedFormat is Format::Auto, try to |
| 158 | /// detect the format based on the extension of \p OutputFileName or fall back |
| 159 | /// to \p DefaultFormat. |
| 160 | Format (StringRef OutputFileName, Format SelectedFormat, |
| 161 | Format DefaultFormat); |
| 162 | |
| 163 | /// Filter object which can be either a string or a regex to match with the |
| 164 | /// remark properties. |
| 165 | class { |
| 166 | Regex ; |
| 167 | std::string ; |
| 168 | bool ; |
| 169 | |
| 170 | (StringRef Filter, bool IsRegex) |
| 171 | : FilterRE(Filter), FilterStr(Filter), IsRegex(IsRegex) {} |
| 172 | |
| 173 | static Expected<FilterMatcher> (StringRef Arg, StringRef Value); |
| 174 | |
| 175 | public: |
| 176 | static FilterMatcher (StringRef Filter) { return {Filter, false}; } |
| 177 | |
| 178 | static Expected<FilterMatcher> |
| 179 | (const llvm::cl::opt<std::string> &Arg); |
| 180 | |
| 181 | static Expected<FilterMatcher> (StringRef Filter, |
| 182 | const cl::list<std::string> &Arg); |
| 183 | |
| 184 | static Expected<std::optional<FilterMatcher>> |
| 185 | (const llvm::cl::opt<std::string> &ExactArg, |
| 186 | const llvm::cl::opt<std::string> &REArg); |
| 187 | |
| 188 | static FilterMatcher () { return {".*" , true}; } |
| 189 | |
| 190 | bool (StringRef StringToMatch) const { |
| 191 | if (IsRegex) |
| 192 | return FilterRE.match(String: StringToMatch); |
| 193 | return FilterStr == StringToMatch.trim().str(); |
| 194 | } |
| 195 | }; |
| 196 | |
| 197 | /// Filter out remarks based on remark properties (function, remark name, pass |
| 198 | /// name, argument values and type). |
| 199 | struct { |
| 200 | std::optional<FilterMatcher> ; |
| 201 | std::optional<FilterMatcher> ; |
| 202 | std::optional<FilterMatcher> ; |
| 203 | std::optional<FilterMatcher> ; |
| 204 | std::optional<Type> ; |
| 205 | |
| 206 | /// Returns true if \p Remark satisfies all the provided filters. |
| 207 | bool (const Remark &); |
| 208 | }; |
| 209 | |
| 210 | /// Helper to construct Remarks using an API similar to DiagnosticInfo. |
| 211 | /// Once this is more fully featured, consider implementing DiagnosticInfo using |
| 212 | /// RemarkBuilder. |
| 213 | class { |
| 214 | BumpPtrAllocator ; |
| 215 | UniqueStringSaver ; |
| 216 | |
| 217 | public: |
| 218 | Remark ; |
| 219 | struct { |
| 220 | std::string ; |
| 221 | std::string ; |
| 222 | std::optional<RemarkLocation> ; |
| 223 | (StringRef Key, StringRef Val, |
| 224 | std::optional<RemarkLocation> Loc = std::nullopt) |
| 225 | : Key(Key), Val(Val), Loc(Loc) {} |
| 226 | (StringRef Key, int Val, |
| 227 | std::optional<RemarkLocation> Loc = std::nullopt) |
| 228 | : Key(Key), Val(itostr(X: Val)), Loc(Loc) {} |
| 229 | }; |
| 230 | |
| 231 | (Type , StringRef PassName, StringRef , |
| 232 | StringRef FunctionName) |
| 233 | : Strs(Alloc) { |
| 234 | R.RemarkType = RemarkType; |
| 235 | R.PassName = Strs.save(S: PassName); |
| 236 | R.RemarkName = Strs.save(S: RemarkName); |
| 237 | R.FunctionName = Strs.save(S: FunctionName); |
| 238 | } |
| 239 | |
| 240 | RemarkBuilder &(Argument &&Arg) { |
| 241 | auto &RArg = R.Args.emplace_back(Args: Strs.save(S: Arg.Key), Args: Strs.save(S: Arg.Val)); |
| 242 | RArg.Loc = Arg.Loc; |
| 243 | return *this; |
| 244 | } |
| 245 | |
| 246 | RemarkBuilder &(const char *Str) { |
| 247 | R.Args.emplace_back(Args: "String" , Args&: Str); |
| 248 | return *this; |
| 249 | } |
| 250 | |
| 251 | RemarkBuilder &(StringRef Str) { |
| 252 | R.Args.emplace_back(Args: "String" , Args: Strs.save(S: Str)); |
| 253 | return *this; |
| 254 | } |
| 255 | }; |
| 256 | |
| 257 | using = RemarkBuilder::Argument; |
| 258 | |
| 259 | } // namespace remarks |
| 260 | } // namespace llvm |
| 261 | |