1 //===- YAMLRemarkParser.cpp -----------------------------------------------===// 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 // This file provides utility methods used by clients that want to use the 10 // parser for remark diagnostics in LLVM. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "YAMLRemarkParser.h" 15 #include "llvm/ADT/StringSwitch.h" 16 #include "llvm/Remarks/RemarkParser.h" 17 18 using namespace llvm; 19 using namespace llvm::remarks; 20 21 char YAMLParseError::ID = 0; 22 23 Error YAMLRemarkParser::parseKey(StringRef &Result, yaml::KeyValueNode &Node) { 24 if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey())) { 25 Result = Key->getRawValue(); 26 return Error::success(); 27 } 28 29 return make_error<YAMLParseError>("key is not a string.", Node); 30 } 31 32 template <typename T> 33 Error YAMLRemarkParser::parseStr(T &Result, yaml::KeyValueNode &Node) { 34 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); 35 if (!Value) 36 return make_error<YAMLParseError>("expected a value of scalar type.", Node); 37 StringRef Tmp; 38 if (!StrTab) { 39 Tmp = Value->getRawValue(); 40 } else { 41 // If we have a string table, parse it as an unsigned. 42 unsigned StrID = 0; 43 if (Error E = parseUnsigned(StrID, Node)) 44 return E; 45 if (Expected<StringRef> Str = (*StrTab)[StrID]) 46 Tmp = *Str; 47 else 48 return Str.takeError(); 49 } 50 51 if (Tmp.front() == '\'') 52 Tmp = Tmp.drop_front(); 53 54 if (Tmp.back() == '\'') 55 Tmp = Tmp.drop_back(); 56 57 Result = Tmp; 58 59 return Error::success(); 60 } 61 62 template <typename T> 63 Error YAMLRemarkParser::parseUnsigned(T &Result, yaml::KeyValueNode &Node) { 64 SmallVector<char, 4> Tmp; 65 auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); 66 if (!Value) 67 return make_error<YAMLParseError>("expected a value of scalar type.", Node); 68 unsigned UnsignedValue = 0; 69 if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) 70 return make_error<YAMLParseError>("expected a value of integer type.", 71 *Value); 72 Result = UnsignedValue; 73 return Error::success(); 74 } 75 76 Error YAMLRemarkParser::parseType(Type &Result, yaml::MappingNode &Node) { 77 auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) 78 .Case("!Passed", remarks::Type::Passed) 79 .Case("!Missed", remarks::Type::Missed) 80 .Case("!Analysis", remarks::Type::Analysis) 81 .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute) 82 .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing) 83 .Case("!Failure", remarks::Type::Failure) 84 .Default(remarks::Type::Unknown); 85 if (Type == remarks::Type::Unknown) 86 return make_error<YAMLParseError>("expected a remark tag.", Node); 87 Result = Type; 88 return Error::success(); 89 } 90 91 Error YAMLRemarkParser::parseDebugLoc(Optional<RemarkLocation> &Result, 92 yaml::KeyValueNode &Node) { 93 auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); 94 if (!DebugLoc) 95 return make_error<YAMLParseError>("expected a value of mapping type.", 96 Node); 97 98 Optional<StringRef> File; 99 Optional<unsigned> Line; 100 Optional<unsigned> Column; 101 102 for (yaml::KeyValueNode &DLNode : *DebugLoc) { 103 StringRef KeyName; 104 if (Error E = parseKey(KeyName, DLNode)) 105 return E; 106 if (KeyName == "File") { 107 if (Error E = parseStr(File, DLNode)) 108 return E; 109 } else if (KeyName == "Column") { 110 if (Error E = parseUnsigned(Column, DLNode)) 111 return E; 112 } else if (KeyName == "Line") { 113 if (Error E = parseUnsigned(Line, DLNode)) 114 return E; 115 } else { 116 return make_error<YAMLParseError>("unknown entry in DebugLoc map.", 117 DLNode); 118 } 119 } 120 121 // If any of the debug loc fields is missing, return an error. 122 if (!File || !Line || !Column) 123 return make_error<YAMLParseError>("DebugLoc node incomplete.", Node); 124 125 Result = RemarkLocation{*File, *Line, *Column}; 126 127 return Error::success(); 128 } 129 130 Error YAMLRemarkParser::parseRemarkField(yaml::KeyValueNode &RemarkField) { 131 132 StringRef KeyName; 133 if (Error E = parseKey(KeyName, RemarkField)) 134 return E; 135 136 if (KeyName == "Pass") { 137 if (Error E = parseStr(State->TheRemark.PassName, RemarkField)) 138 return E; 139 } else if (KeyName == "Name") { 140 if (Error E = parseStr(State->TheRemark.RemarkName, RemarkField)) 141 return E; 142 } else if (KeyName == "Function") { 143 if (Error E = parseStr(State->TheRemark.FunctionName, RemarkField)) 144 return E; 145 } else if (KeyName == "Hotness") { 146 State->TheRemark.Hotness = 0; 147 if (Error E = parseUnsigned(*State->TheRemark.Hotness, RemarkField)) 148 return E; 149 } else if (KeyName == "DebugLoc") { 150 if (Error E = parseDebugLoc(State->TheRemark.Loc, RemarkField)) 151 return E; 152 } else if (KeyName == "Args") { 153 auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); 154 if (!Args) 155 return make_error<YAMLParseError>("wrong value type for key.", 156 RemarkField); 157 158 for (yaml::Node &Arg : *Args) 159 if (Error E = parseArg(State->Args, Arg)) 160 return E; 161 162 State->TheRemark.Args = State->Args; 163 } else { 164 return make_error<YAMLParseError>("unknown key.", RemarkField); 165 } 166 167 return Error::success(); 168 } 169 170 Error YAMLRemarkParser::parseArg(SmallVectorImpl<Argument> &Args, 171 yaml::Node &Node) { 172 auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); 173 if (!ArgMap) 174 return make_error<YAMLParseError>("expected a value of mapping type.", 175 Node); 176 177 StringRef KeyStr; 178 StringRef ValueStr; 179 Optional<RemarkLocation> Loc; 180 181 for (yaml::KeyValueNode &ArgEntry : *ArgMap) 182 if (Error E = parseArgEntry(ArgEntry, KeyStr, ValueStr, Loc)) 183 return E; 184 185 if (KeyStr.empty()) 186 return make_error<YAMLParseError>("argument key is missing.", *ArgMap); 187 if (ValueStr.empty()) 188 return make_error<YAMLParseError>("argument value is missing.", *ArgMap); 189 190 Args.push_back(Argument{KeyStr, ValueStr, Loc}); 191 192 return Error::success(); 193 } 194 195 Error YAMLRemarkParser::parseArgEntry(yaml::KeyValueNode &ArgEntry, 196 StringRef &KeyStr, StringRef &ValueStr, 197 Optional<RemarkLocation> &Loc) { 198 StringRef KeyName; 199 if (Error E = parseKey(KeyName, ArgEntry)) 200 return E; 201 202 // Try to parse debug locs. 203 if (KeyName == "DebugLoc") { 204 // Can't have multiple DebugLoc entries per argument. 205 if (Loc) 206 return make_error<YAMLParseError>( 207 "only one DebugLoc entry is allowed per argument.", ArgEntry); 208 209 if (Error E = parseDebugLoc(Loc, ArgEntry)) 210 return E; 211 return Error::success(); 212 } 213 214 // If we already have a string, error out. 215 if (!ValueStr.empty()) 216 return make_error<YAMLParseError>( 217 "only one string entry is allowed per argument.", ArgEntry); 218 219 // Try to parse a string. 220 if (Error E = parseStr(ValueStr, ArgEntry)) 221 return E; 222 223 // Keep the key from the string. 224 KeyStr = KeyName; 225 return Error::success(); 226 } 227 228 Error YAMLRemarkParser::parseYAMLElement(yaml::Document &Remark) { 229 // Parsing a new remark, clear the previous one by re-constructing the state 230 // in-place in the Optional. 231 State.emplace(TmpArgs); 232 233 yaml::Node *YAMLRoot = Remark.getRoot(); 234 if (!YAMLRoot) 235 return createStringError(std::make_error_code(std::errc::invalid_argument), 236 "not a valid YAML file."); 237 238 auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot); 239 if (!Root) 240 return make_error<YAMLParseError>("document root is not of mapping type.", 241 *YAMLRoot); 242 243 if (Error E = parseType(State->TheRemark.RemarkType, *Root)) 244 return E; 245 246 for (yaml::KeyValueNode &RemarkField : *Root) 247 if (Error E = parseRemarkField(RemarkField)) 248 return E; 249 250 // If the YAML parsing failed, don't even continue parsing. We might 251 // encounter malformed YAML. 252 if (Stream.failed()) 253 return make_error<YAMLParseError>("YAML parsing failed.", 254 *Remark.getRoot()); 255 256 // Check if any of the mandatory fields are missing. 257 if (State->TheRemark.RemarkType == Type::Unknown || 258 State->TheRemark.PassName.empty() || 259 State->TheRemark.RemarkName.empty() || 260 State->TheRemark.FunctionName.empty()) 261 return make_error<YAMLParseError>("Type, Pass, Name or Function missing.", 262 *Remark.getRoot()); 263 264 return Error::success(); 265 } 266 267 /// Handle a diagnostic from the YAML stream. Records the error in the 268 /// YAMLRemarkParser class. 269 void YAMLRemarkParser::HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { 270 assert(Ctx && "Expected non-null Ctx in diagnostic handler."); 271 auto *Parser = static_cast<YAMLRemarkParser *>(Ctx); 272 Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false, 273 /*ShowKindLabels*/ true); 274 } 275