1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-20 03:23:01 +02:00
llvm-mirror/tools/llvm-opt-report/OptReport.cpp
Chandler Carruth ae65e281f3 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

456 lines
15 KiB
C++

//===------------------ llvm-opt-report/OptReport.cpp ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements a tool that can parse the YAML optimization
/// records and generate an optimization summary annotated source listing
/// report.
///
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm-c/OptRemarks.h"
#include <cstdlib>
#include <map>
#include <set>
using namespace llvm;
using namespace llvm::yaml;
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
// Mark all our options with this category, everything else (except for -version
// and -help) will be hidden.
static cl::OptionCategory
OptReportCategory("llvm-opt-report options");
static cl::opt<std::string>
InputFileName(cl::Positional, cl::desc("<input>"), cl::init("-"),
cl::cat(OptReportCategory));
static cl::opt<std::string>
OutputFileName("o", cl::desc("Output file"), cl::init("-"),
cl::cat(OptReportCategory));
static cl::opt<std::string>
InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),
cl::cat(OptReportCategory));
static cl::opt<bool>
Succinct("s", cl::desc("Don't include vectorization factors, etc."),
cl::init(false), cl::cat(OptReportCategory));
static cl::opt<bool>
NoDemangle("no-demangle", cl::desc("Don't demangle function names"),
cl::init(false), cl::cat(OptReportCategory));
namespace {
// For each location in the source file, the common per-transformation state
// collected.
struct OptReportLocationItemInfo {
bool Analyzed = false;
bool Transformed = false;
OptReportLocationItemInfo &operator |= (
const OptReportLocationItemInfo &RHS) {
Analyzed |= RHS.Analyzed;
Transformed |= RHS.Transformed;
return *this;
}
bool operator < (const OptReportLocationItemInfo &RHS) const {
if (Analyzed < RHS.Analyzed)
return true;
else if (Analyzed > RHS.Analyzed)
return false;
else if (Transformed < RHS.Transformed)
return true;
return false;
}
};
// The per-location information collected for producing an optimization report.
struct OptReportLocationInfo {
OptReportLocationItemInfo Inlined;
OptReportLocationItemInfo Unrolled;
OptReportLocationItemInfo Vectorized;
int VectorizationFactor = 1;
int InterleaveCount = 1;
int UnrollCount = 1;
OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) {
Inlined |= RHS.Inlined;
Unrolled |= RHS.Unrolled;
Vectorized |= RHS.Vectorized;
VectorizationFactor =
std::max(VectorizationFactor, RHS.VectorizationFactor);
InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount);
UnrollCount = std::max(UnrollCount, RHS.UnrollCount);
return *this;
}
bool operator < (const OptReportLocationInfo &RHS) const {
if (Inlined < RHS.Inlined)
return true;
else if (RHS.Inlined < Inlined)
return false;
else if (Unrolled < RHS.Unrolled)
return true;
else if (RHS.Unrolled < Unrolled)
return false;
else if (Vectorized < RHS.Vectorized)
return true;
else if (RHS.Vectorized < Vectorized || Succinct)
return false;
else if (VectorizationFactor < RHS.VectorizationFactor)
return true;
else if (VectorizationFactor > RHS.VectorizationFactor)
return false;
else if (InterleaveCount < RHS.InterleaveCount)
return true;
else if (InterleaveCount > RHS.InterleaveCount)
return false;
else if (UnrollCount < RHS.UnrollCount)
return true;
return false;
}
};
typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,
OptReportLocationInfo>>>> LocationInfoTy;
} // anonymous namespace
static bool readLocationInfo(LocationInfoTy &LocationInfo) {
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFile(InputFileName.c_str());
if (std::error_code EC = Buf.getError()) {
WithColor::error() << "Can't open file " << InputFileName << ": "
<< EC.message() << "\n";
return false;
}
StringRef Buffer = (*Buf)->getBuffer();
LLVMOptRemarkParserRef Parser =
LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size());
LLVMOptRemarkEntry *Remark = nullptr;
while ((Remark = LLVMOptRemarkParserGetNext(Parser))) {
bool Transformed =
StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed";
StringRef Pass(Remark->PassName.Str, Remark->PassName.Len);
StringRef File(Remark->DebugLoc.SourceFile.Str,
Remark->DebugLoc.SourceFile.Len);
StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len);
uint32_t Line = Remark->DebugLoc.SourceLineNumber;
uint32_t Column = Remark->DebugLoc.SourceColumnNumber;
ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs);
int VectorizationFactor = 1;
int InterleaveCount = 1;
int UnrollCount = 1;
for (const LLVMOptRemarkArg &Arg : Args) {
StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len);
StringRef ArgValue(Arg.Value.Str, Arg.Value.Len);
if (ArgKeyName == "VectorizationFactor")
ArgValue.getAsInteger(10, VectorizationFactor);
else if (ArgKeyName == "InterleaveCount")
ArgValue.getAsInteger(10, InterleaveCount);
else if (ArgKeyName == "UnrollCount")
ArgValue.getAsInteger(10, UnrollCount);
}
if (Line < 1 || File.empty())
continue;
// We track information on both actual and potential transformations. This
// way, if there are multiple possible things on a line that are, or could
// have been transformed, we can indicate that explicitly in the output.
auto UpdateLLII = [Transformed](OptReportLocationItemInfo &LLII) {
LLII.Analyzed = true;
if (Transformed)
LLII.Transformed = true;
};
if (Pass == "inline") {
auto &LI = LocationInfo[File][Line][Function][Column];
UpdateLLII(LI.Inlined);
} else if (Pass == "loop-unroll") {
auto &LI = LocationInfo[File][Line][Function][Column];
LI.UnrollCount = UnrollCount;
UpdateLLII(LI.Unrolled);
} else if (Pass == "loop-vectorize") {
auto &LI = LocationInfo[File][Line][Function][Column];
LI.VectorizationFactor = VectorizationFactor;
LI.InterleaveCount = InterleaveCount;
UpdateLLII(LI.Vectorized);
}
}
bool HasError = LLVMOptRemarkParserHasError(Parser);
if (HasError)
WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n";
LLVMOptRemarkParserDispose(Parser);
return !HasError;
}
static bool writeReport(LocationInfoTy &LocationInfo) {
std::error_code EC;
llvm::raw_fd_ostream OS(OutputFileName, EC,
llvm::sys::fs::F_Text);
if (EC) {
WithColor::error() << "Can't open file " << OutputFileName << ": "
<< EC.message() << "\n";
return false;
}
bool FirstFile = true;
for (auto &FI : LocationInfo) {
SmallString<128> FileName(FI.first);
if (!InputRelDir.empty())
sys::fs::make_absolute(InputRelDir, FileName);
const auto &FileInfo = FI.second;
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
MemoryBuffer::getFile(FileName);
if (std::error_code EC = Buf.getError()) {
WithColor::error() << "Can't open file " << FileName << ": "
<< EC.message() << "\n";
return false;
}
if (FirstFile)
FirstFile = false;
else
OS << "\n";
OS << "< " << FileName << "\n";
// Figure out how many characters we need for the vectorization factors
// and similar.
OptReportLocationInfo MaxLI;
for (auto &FLI : FileInfo)
for (auto &FI : FLI.second)
for (auto &LI : FI.second)
MaxLI |= LI.second;
bool NothingInlined = !MaxLI.Inlined.Transformed;
bool NothingUnrolled = !MaxLI.Unrolled.Transformed;
bool NothingVectorized = !MaxLI.Vectorized.Transformed;
unsigned VFDigits = llvm::utostr(MaxLI.VectorizationFactor).size();
unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size();
unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size();
// Figure out how many characters we need for the line numbers.
int64_t NumLines = 0;
for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI)
++NumLines;
unsigned LNDigits = llvm::utostr(NumLines).size();
for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) {
int64_t L = LI.line_number();
auto LII = FileInfo.find(L);
auto PrintLine = [&](bool PrintFuncName,
const std::set<std::string> &FuncNameSet) {
OptReportLocationInfo LLI;
std::map<int, OptReportLocationInfo> ColsInfo;
unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0;
if (LII != FileInfo.end() && !FuncNameSet.empty()) {
const auto &LineInfo = LII->second;
for (auto &CI : LineInfo.find(*FuncNameSet.begin())->second) {
int Col = CI.first;
ColsInfo[Col] = CI.second;
InlinedCols += CI.second.Inlined.Analyzed;
UnrolledCols += CI.second.Unrolled.Analyzed;
VectorizedCols += CI.second.Vectorized.Analyzed;
LLI |= CI.second;
}
}
if (PrintFuncName) {
OS << " > ";
bool FirstFunc = true;
for (const auto &FuncName : FuncNameSet) {
if (FirstFunc)
FirstFunc = false;
else
OS << ", ";
bool Printed = false;
if (!NoDemangle) {
int Status = 0;
char *Demangled =
itaniumDemangle(FuncName.c_str(), nullptr, nullptr, &Status);
if (Demangled && Status == 0) {
OS << Demangled;
Printed = true;
}
if (Demangled)
std::free(Demangled);
}
if (!Printed)
OS << FuncName;
}
OS << ":\n";
}
// We try to keep the output as concise as possible. If only one thing on
// a given line could have been inlined, vectorized, etc. then we can put
// the marker on the source line itself. If there are multiple options
// then we want to distinguish them by placing the marker for each
// transformation on a separate line following the source line. When we
// do this, we use a '^' character to point to the appropriate column in
// the source line.
std::string USpaces(Succinct ? 0 : UCDigits, ' ');
std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' ');
auto UStr = [UCDigits](OptReportLocationInfo &LLI) {
std::string R;
raw_string_ostream RS(R);
if (!Succinct) {
RS << LLI.UnrollCount;
RS << std::string(UCDigits - RS.str().size(), ' ');
}
return RS.str();
};
auto VStr = [VFDigits,
ICDigits](OptReportLocationInfo &LLI) -> std::string {
std::string R;
raw_string_ostream RS(R);
if (!Succinct) {
RS << LLI.VectorizationFactor << "," << LLI.InterleaveCount;
RS << std::string(VFDigits + ICDigits + 1 - RS.str().size(), ' ');
}
return RS.str();
};
OS << llvm::format_decimal(L, LNDigits) << " ";
OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" :
(NothingInlined ? "" : " "));
OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ?
"U" + UStr(LLI) : (NothingUnrolled ? "" : " " + USpaces));
OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ?
"V" + VStr(LLI) : (NothingVectorized ? "" : " " + VSpaces));
OS << " | " << *LI << "\n";
for (auto &J : ColsInfo) {
if ((J.second.Inlined.Transformed && InlinedCols > 1) ||
(J.second.Unrolled.Transformed && UnrolledCols > 1) ||
(J.second.Vectorized.Transformed && VectorizedCols > 1)) {
OS << std::string(LNDigits + 1, ' ');
OS << (J.second.Inlined.Transformed &&
InlinedCols > 1 ? "I" : (NothingInlined ? "" : " "));
OS << (J.second.Unrolled.Transformed &&
UnrolledCols > 1 ? "U" + UStr(J.second) :
(NothingUnrolled ? "" : " " + USpaces));
OS << (J.second.Vectorized.Transformed &&
VectorizedCols > 1 ? "V" + VStr(J.second) :
(NothingVectorized ? "" : " " + VSpaces));
OS << " | " << std::string(J.first - 1, ' ') << "^\n";
}
}
};
// We need to figure out if the optimizations for this line were the same
// in each function context. If not, then we want to group the similar
// function contexts together and display each group separately. If
// they're all the same, then we only display the line once without any
// additional markings.
std::map<std::map<int, OptReportLocationInfo>,
std::set<std::string>> UniqueLIs;
OptReportLocationInfo AllLI;
if (LII != FileInfo.end()) {
const auto &FuncLineInfo = LII->second;
for (const auto &FLII : FuncLineInfo) {
UniqueLIs[FLII.second].insert(FLII.first);
for (const auto &OI : FLII.second)
AllLI |= OI.second;
}
}
bool NothingHappened = !AllLI.Inlined.Transformed &&
!AllLI.Unrolled.Transformed &&
!AllLI.Vectorized.Transformed;
if (UniqueLIs.size() > 1 && !NothingHappened) {
OS << " [[\n";
for (const auto &FSLI : UniqueLIs)
PrintLine(true, FSLI.second);
OS << " ]]\n";
} else if (UniqueLIs.size() == 1) {
PrintLine(false, UniqueLIs.begin()->second);
} else {
PrintLine(false, std::set<std::string>());
}
}
}
return true;
}
int main(int argc, const char **argv) {
InitLLVM X(argc, argv);
cl::HideUnrelatedOptions(OptReportCategory);
cl::ParseCommandLineOptions(
argc, argv,
"A tool to generate an optimization report from YAML optimization"
" record files.\n");
if (Help) {
cl::PrintHelpMessage();
return 0;
}
LocationInfoTy LocationInfo;
if (!readLocationInfo(LocationInfo))
return 1;
if (!writeReport(LocationInfo))
return 1;
return 0;
}