1 //===------------------ llvm-opt-report/OptReport.cpp ---------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// This file implements a tool that can parse the YAML optimization 12 /// records and generate an optimization summary annotated source listing 13 /// report. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "llvm/Demangle/Demangle.h" 18 #include "llvm/Support/CommandLine.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/ErrorOr.h" 21 #include "llvm/Support/FileSystem.h" 22 #include "llvm/Support/Format.h" 23 #include "llvm/Support/InitLLVM.h" 24 #include "llvm/Support/LineIterator.h" 25 #include "llvm/Support/MemoryBuffer.h" 26 #include "llvm/Support/Path.h" 27 #include "llvm/Support/Program.h" 28 #include "llvm/Support/WithColor.h" 29 #include "llvm/Support/YAMLTraits.h" 30 #include "llvm/Support/raw_ostream.h" 31 #include "llvm-c/OptRemarks.h" 32 #include <cstdlib> 33 #include <map> 34 #include <set> 35 36 using namespace llvm; 37 using namespace llvm::yaml; 38 39 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); 40 41 // Mark all our options with this category, everything else (except for -version 42 // and -help) will be hidden. 43 static cl::OptionCategory 44 OptReportCategory("llvm-opt-report options"); 45 46 static cl::opt<std::string> 47 InputFileName(cl::Positional, cl::desc("<input>"), cl::init("-"), 48 cl::cat(OptReportCategory)); 49 50 static cl::opt<std::string> 51 OutputFileName("o", cl::desc("Output file"), cl::init("-"), 52 cl::cat(OptReportCategory)); 53 54 static cl::opt<std::string> 55 InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""), 56 cl::cat(OptReportCategory)); 57 58 static cl::opt<bool> 59 Succinct("s", cl::desc("Don't include vectorization factors, etc."), 60 cl::init(false), cl::cat(OptReportCategory)); 61 62 static cl::opt<bool> 63 NoDemangle("no-demangle", cl::desc("Don't demangle function names"), 64 cl::init(false), cl::cat(OptReportCategory)); 65 66 namespace { 67 // For each location in the source file, the common per-transformation state 68 // collected. 69 struct OptReportLocationItemInfo { 70 bool Analyzed = false; 71 bool Transformed = false; 72 73 OptReportLocationItemInfo &operator |= ( 74 const OptReportLocationItemInfo &RHS) { 75 Analyzed |= RHS.Analyzed; 76 Transformed |= RHS.Transformed; 77 78 return *this; 79 } 80 81 bool operator < (const OptReportLocationItemInfo &RHS) const { 82 if (Analyzed < RHS.Analyzed) 83 return true; 84 else if (Analyzed > RHS.Analyzed) 85 return false; 86 else if (Transformed < RHS.Transformed) 87 return true; 88 return false; 89 } 90 }; 91 92 // The per-location information collected for producing an optimization report. 93 struct OptReportLocationInfo { 94 OptReportLocationItemInfo Inlined; 95 OptReportLocationItemInfo Unrolled; 96 OptReportLocationItemInfo Vectorized; 97 98 int VectorizationFactor = 1; 99 int InterleaveCount = 1; 100 int UnrollCount = 1; 101 102 OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) { 103 Inlined |= RHS.Inlined; 104 Unrolled |= RHS.Unrolled; 105 Vectorized |= RHS.Vectorized; 106 107 VectorizationFactor = 108 std::max(VectorizationFactor, RHS.VectorizationFactor); 109 InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount); 110 UnrollCount = std::max(UnrollCount, RHS.UnrollCount); 111 112 return *this; 113 } 114 115 bool operator < (const OptReportLocationInfo &RHS) const { 116 if (Inlined < RHS.Inlined) 117 return true; 118 else if (RHS.Inlined < Inlined) 119 return false; 120 else if (Unrolled < RHS.Unrolled) 121 return true; 122 else if (RHS.Unrolled < Unrolled) 123 return false; 124 else if (Vectorized < RHS.Vectorized) 125 return true; 126 else if (RHS.Vectorized < Vectorized || Succinct) 127 return false; 128 else if (VectorizationFactor < RHS.VectorizationFactor) 129 return true; 130 else if (VectorizationFactor > RHS.VectorizationFactor) 131 return false; 132 else if (InterleaveCount < RHS.InterleaveCount) 133 return true; 134 else if (InterleaveCount > RHS.InterleaveCount) 135 return false; 136 else if (UnrollCount < RHS.UnrollCount) 137 return true; 138 return false; 139 } 140 }; 141 142 typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int, 143 OptReportLocationInfo>>>> LocationInfoTy; 144 } // anonymous namespace 145 146 static bool readLocationInfo(LocationInfoTy &LocationInfo) { 147 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = 148 MemoryBuffer::getFile(InputFileName.c_str()); 149 if (std::error_code EC = Buf.getError()) { 150 WithColor::error() << "Can't open file " << InputFileName << ": " 151 << EC.message() << "\n"; 152 return false; 153 } 154 155 StringRef Buffer = (*Buf)->getBuffer(); 156 LLVMOptRemarkParserRef Parser = 157 LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size()); 158 159 LLVMOptRemarkEntry *Remark = nullptr; 160 while ((Remark = LLVMOptRemarkParserGetNext(Parser))) { 161 bool Transformed = 162 StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed"; 163 StringRef Pass(Remark->PassName.Str, Remark->PassName.Len); 164 StringRef File(Remark->DebugLoc.SourceFile.Str, 165 Remark->DebugLoc.SourceFile.Len); 166 StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len); 167 uint32_t Line = Remark->DebugLoc.SourceLineNumber; 168 uint32_t Column = Remark->DebugLoc.SourceColumnNumber; 169 ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs); 170 171 int VectorizationFactor = 1; 172 int InterleaveCount = 1; 173 int UnrollCount = 1; 174 175 for (const LLVMOptRemarkArg &Arg : Args) { 176 StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len); 177 StringRef ArgValue(Arg.Value.Str, Arg.Value.Len); 178 if (ArgKeyName == "VectorizationFactor") 179 ArgValue.getAsInteger(10, VectorizationFactor); 180 else if (ArgKeyName == "InterleaveCount") 181 ArgValue.getAsInteger(10, InterleaveCount); 182 else if (ArgKeyName == "UnrollCount") 183 ArgValue.getAsInteger(10, UnrollCount); 184 } 185 186 if (Line < 1 || File.empty()) 187 continue; 188 189 // We track information on both actual and potential transformations. This 190 // way, if there are multiple possible things on a line that are, or could 191 // have been transformed, we can indicate that explicitly in the output. 192 auto UpdateLLII = [Transformed](OptReportLocationItemInfo &LLII) { 193 LLII.Analyzed = true; 194 if (Transformed) 195 LLII.Transformed = true; 196 }; 197 198 if (Pass == "inline") { 199 auto &LI = LocationInfo[File][Line][Function][Column]; 200 UpdateLLII(LI.Inlined); 201 } else if (Pass == "loop-unroll") { 202 auto &LI = LocationInfo[File][Line][Function][Column]; 203 LI.UnrollCount = UnrollCount; 204 UpdateLLII(LI.Unrolled); 205 } else if (Pass == "loop-vectorize") { 206 auto &LI = LocationInfo[File][Line][Function][Column]; 207 LI.VectorizationFactor = VectorizationFactor; 208 LI.InterleaveCount = InterleaveCount; 209 UpdateLLII(LI.Vectorized); 210 } 211 } 212 213 bool HasError = LLVMOptRemarkParserHasError(Parser); 214 if (HasError) 215 WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n"; 216 217 LLVMOptRemarkParserDispose(Parser); 218 return !HasError; 219 } 220 221 static bool writeReport(LocationInfoTy &LocationInfo) { 222 std::error_code EC; 223 llvm::raw_fd_ostream OS(OutputFileName, EC, 224 llvm::sys::fs::F_Text); 225 if (EC) { 226 WithColor::error() << "Can't open file " << OutputFileName << ": " 227 << EC.message() << "\n"; 228 return false; 229 } 230 231 bool FirstFile = true; 232 for (auto &FI : LocationInfo) { 233 SmallString<128> FileName(FI.first); 234 if (!InputRelDir.empty()) { 235 if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) { 236 WithColor::error() << "Can't resolve file path to " << FileName << ": " 237 << EC.message() << "\n"; 238 return false; 239 } 240 } 241 242 const auto &FileInfo = FI.second; 243 244 ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = 245 MemoryBuffer::getFile(FileName); 246 if (std::error_code EC = Buf.getError()) { 247 WithColor::error() << "Can't open file " << FileName << ": " 248 << EC.message() << "\n"; 249 return false; 250 } 251 252 if (FirstFile) 253 FirstFile = false; 254 else 255 OS << "\n"; 256 257 OS << "< " << FileName << "\n"; 258 259 // Figure out how many characters we need for the vectorization factors 260 // and similar. 261 OptReportLocationInfo MaxLI; 262 for (auto &FLI : FileInfo) 263 for (auto &FI : FLI.second) 264 for (auto &LI : FI.second) 265 MaxLI |= LI.second; 266 267 bool NothingInlined = !MaxLI.Inlined.Transformed; 268 bool NothingUnrolled = !MaxLI.Unrolled.Transformed; 269 bool NothingVectorized = !MaxLI.Vectorized.Transformed; 270 271 unsigned VFDigits = llvm::utostr(MaxLI.VectorizationFactor).size(); 272 unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size(); 273 unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size(); 274 275 // Figure out how many characters we need for the line numbers. 276 int64_t NumLines = 0; 277 for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) 278 ++NumLines; 279 280 unsigned LNDigits = llvm::utostr(NumLines).size(); 281 282 for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) { 283 int64_t L = LI.line_number(); 284 auto LII = FileInfo.find(L); 285 286 auto PrintLine = [&](bool PrintFuncName, 287 const std::set<std::string> &FuncNameSet) { 288 OptReportLocationInfo LLI; 289 290 std::map<int, OptReportLocationInfo> ColsInfo; 291 unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0; 292 293 if (LII != FileInfo.end() && !FuncNameSet.empty()) { 294 const auto &LineInfo = LII->second; 295 296 for (auto &CI : LineInfo.find(*FuncNameSet.begin())->second) { 297 int Col = CI.first; 298 ColsInfo[Col] = CI.second; 299 InlinedCols += CI.second.Inlined.Analyzed; 300 UnrolledCols += CI.second.Unrolled.Analyzed; 301 VectorizedCols += CI.second.Vectorized.Analyzed; 302 LLI |= CI.second; 303 } 304 } 305 306 if (PrintFuncName) { 307 OS << " > "; 308 309 bool FirstFunc = true; 310 for (const auto &FuncName : FuncNameSet) { 311 if (FirstFunc) 312 FirstFunc = false; 313 else 314 OS << ", "; 315 316 bool Printed = false; 317 if (!NoDemangle) { 318 int Status = 0; 319 char *Demangled = 320 itaniumDemangle(FuncName.c_str(), nullptr, nullptr, &Status); 321 if (Demangled && Status == 0) { 322 OS << Demangled; 323 Printed = true; 324 } 325 326 if (Demangled) 327 std::free(Demangled); 328 } 329 330 if (!Printed) 331 OS << FuncName; 332 } 333 334 OS << ":\n"; 335 } 336 337 // We try to keep the output as concise as possible. If only one thing on 338 // a given line could have been inlined, vectorized, etc. then we can put 339 // the marker on the source line itself. If there are multiple options 340 // then we want to distinguish them by placing the marker for each 341 // transformation on a separate line following the source line. When we 342 // do this, we use a '^' character to point to the appropriate column in 343 // the source line. 344 345 std::string USpaces(Succinct ? 0 : UCDigits, ' '); 346 std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' '); 347 348 auto UStr = [UCDigits](OptReportLocationInfo &LLI) { 349 std::string R; 350 raw_string_ostream RS(R); 351 352 if (!Succinct) { 353 RS << LLI.UnrollCount; 354 RS << std::string(UCDigits - RS.str().size(), ' '); 355 } 356 357 return RS.str(); 358 }; 359 360 auto VStr = [VFDigits, 361 ICDigits](OptReportLocationInfo &LLI) -> std::string { 362 std::string R; 363 raw_string_ostream RS(R); 364 365 if (!Succinct) { 366 RS << LLI.VectorizationFactor << "," << LLI.InterleaveCount; 367 RS << std::string(VFDigits + ICDigits + 1 - RS.str().size(), ' '); 368 } 369 370 return RS.str(); 371 }; 372 373 OS << llvm::format_decimal(L, LNDigits) << " "; 374 OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" : 375 (NothingInlined ? "" : " ")); 376 OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ? 377 "U" + UStr(LLI) : (NothingUnrolled ? "" : " " + USpaces)); 378 OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ? 379 "V" + VStr(LLI) : (NothingVectorized ? "" : " " + VSpaces)); 380 381 OS << " | " << *LI << "\n"; 382 383 for (auto &J : ColsInfo) { 384 if ((J.second.Inlined.Transformed && InlinedCols > 1) || 385 (J.second.Unrolled.Transformed && UnrolledCols > 1) || 386 (J.second.Vectorized.Transformed && VectorizedCols > 1)) { 387 OS << std::string(LNDigits + 1, ' '); 388 OS << (J.second.Inlined.Transformed && 389 InlinedCols > 1 ? "I" : (NothingInlined ? "" : " ")); 390 OS << (J.second.Unrolled.Transformed && 391 UnrolledCols > 1 ? "U" + UStr(J.second) : 392 (NothingUnrolled ? "" : " " + USpaces)); 393 OS << (J.second.Vectorized.Transformed && 394 VectorizedCols > 1 ? "V" + VStr(J.second) : 395 (NothingVectorized ? "" : " " + VSpaces)); 396 397 OS << " | " << std::string(J.first - 1, ' ') << "^\n"; 398 } 399 } 400 }; 401 402 // We need to figure out if the optimizations for this line were the same 403 // in each function context. If not, then we want to group the similar 404 // function contexts together and display each group separately. If 405 // they're all the same, then we only display the line once without any 406 // additional markings. 407 std::map<std::map<int, OptReportLocationInfo>, 408 std::set<std::string>> UniqueLIs; 409 410 OptReportLocationInfo AllLI; 411 if (LII != FileInfo.end()) { 412 const auto &FuncLineInfo = LII->second; 413 for (const auto &FLII : FuncLineInfo) { 414 UniqueLIs[FLII.second].insert(FLII.first); 415 416 for (const auto &OI : FLII.second) 417 AllLI |= OI.second; 418 } 419 } 420 421 bool NothingHappened = !AllLI.Inlined.Transformed && 422 !AllLI.Unrolled.Transformed && 423 !AllLI.Vectorized.Transformed; 424 if (UniqueLIs.size() > 1 && !NothingHappened) { 425 OS << " [[\n"; 426 for (const auto &FSLI : UniqueLIs) 427 PrintLine(true, FSLI.second); 428 OS << " ]]\n"; 429 } else if (UniqueLIs.size() == 1) { 430 PrintLine(false, UniqueLIs.begin()->second); 431 } else { 432 PrintLine(false, std::set<std::string>()); 433 } 434 } 435 } 436 437 return true; 438 } 439 440 int main(int argc, const char **argv) { 441 InitLLVM X(argc, argv); 442 443 cl::HideUnrelatedOptions(OptReportCategory); 444 cl::ParseCommandLineOptions( 445 argc, argv, 446 "A tool to generate an optimization report from YAML optimization" 447 " record files.\n"); 448 449 if (Help) { 450 cl::PrintHelpMessage(); 451 return 0; 452 } 453 454 LocationInfoTy LocationInfo; 455 if (!readLocationInfo(LocationInfo)) 456 return 1; 457 if (!writeReport(LocationInfo)) 458 return 1; 459 460 return 0; 461 } 462