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