summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-07-13 19:25:18 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-07-13 19:25:18 +0000
commitca089b24d48ef6fa8da2d0bb8c25bb802c4a95c0 (patch)
tree3a28a772df9b17aef34f49e3c727965ad28c0c93 /tools
parent9df3605dea17e84f8183581f6103bd0c79e2a606 (diff)
Notes
Diffstat (limited to 'tools')
-rw-r--r--tools/gold/gold-plugin.cpp2
-rw-r--r--tools/lli/OrcLazyJIT.cpp25
-rw-r--r--tools/lli/OrcLazyJIT.h29
-rw-r--r--tools/lli/RemoteJITUtils.h4
-rw-r--r--tools/lli/lli.cpp2
-rw-r--r--tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp8
-rw-r--r--tools/llvm-c-test/echo.cpp8
-rw-r--r--tools/llvm-cov/CodeCoverage.cpp19
-rw-r--r--tools/llvm-lto/llvm-lto.cpp6
-rw-r--r--tools/llvm-objdump/llvm-objdump.cpp2
-rw-r--r--tools/llvm-pdbutil/CMakeLists.txt1
-rw-r--r--tools/llvm-pdbutil/Diff.cpp692
-rw-r--r--tools/llvm-pdbutil/DiffPrinter.cpp147
-rw-r--r--tools/llvm-pdbutil/DiffPrinter.h172
-rw-r--r--tools/llvm-pdbutil/DumpOutputStyle.cpp7
-rw-r--r--tools/llvm-pdbutil/FormatUtil.cpp52
-rw-r--r--tools/llvm-pdbutil/FormatUtil.h10
-rw-r--r--tools/llvm-pdbutil/MinimalTypeDumper.cpp6
-rw-r--r--tools/llvm-pdbutil/StreamUtil.cpp85
-rw-r--r--tools/llvm-pdbutil/StreamUtil.h5
-rw-r--r--tools/llvm-pdbutil/llvm-pdbutil.cpp44
-rw-r--r--tools/llvm-pdbutil/llvm-pdbutil.h7
-rw-r--r--tools/llvm-profdata/llvm-profdata.cpp74
-rw-r--r--tools/llvm-readobj/COFFDumper.cpp6
-rw-r--r--tools/llvm-readobj/WasmDumper.cpp6
-rw-r--r--tools/llvm-shlib/CMakeLists.txt2
-rw-r--r--tools/llvm-stress/llvm-stress.cpp16
-rw-r--r--tools/obj2yaml/wasm2yaml.cpp7
-rw-r--r--tools/opt-viewer/CMakeLists.txt13
-rwxr-xr-xtools/opt-viewer/opt-diff.py71
-rwxr-xr-xtools/opt-viewer/opt-stats.py56
-rwxr-xr-xtools/opt-viewer/opt-viewer.py262
-rw-r--r--tools/opt-viewer/optpmap.py53
-rw-r--r--tools/opt-viewer/optrecord.py235
-rw-r--r--tools/opt-viewer/style.css198
-rw-r--r--tools/opt/NewPMDriver.cpp101
-rw-r--r--tools/sanstats/sanstats.cpp5
-rw-r--r--tools/yaml2obj/yaml2wasm.cpp2
38 files changed, 2058 insertions, 382 deletions
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp
index cf207d9dbbb3f..6d011bab079d5 100644
--- a/tools/gold/gold-plugin.cpp
+++ b/tools/gold/gold-plugin.cpp
@@ -477,7 +477,7 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
std::unique_ptr<InputFile> Obj = std::move(*ObjOrErr);
- Modules.resize(Modules.size() + 1);
+ Modules.emplace_back();
claimed_file &cf = Modules.back();
cf.handle = file->handle;
diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp
index 2e15894152f98..f1a752e0790de 100644
--- a/tools/lli/OrcLazyJIT.cpp
+++ b/tools/lli/OrcLazyJIT.cpp
@@ -148,18 +148,19 @@ int llvm::runOrcLazyJIT(std::vector<std::unique_ptr<Module>> Ms,
// Add the module, look up main and run it.
for (auto &M : Ms)
- J.addModule(std::shared_ptr<Module>(std::move(M)));
- auto MainSym = J.findSymbol("main");
-
- if (!MainSym) {
+ cantFail(J.addModule(std::shared_ptr<Module>(std::move(M))));
+
+ if (auto MainSym = J.findSymbol("main")) {
+ typedef int (*MainFnPtr)(int, const char*[]);
+ std::vector<const char *> ArgV;
+ for (auto &Arg : Args)
+ ArgV.push_back(Arg.c_str());
+ auto Main = fromTargetAddress<MainFnPtr>(cantFail(MainSym.getAddress()));
+ return Main(ArgV.size(), (const char**)ArgV.data());
+ } else if (auto Err = MainSym.takeError())
+ logAllUnhandledErrors(std::move(Err), llvm::errs(), "");
+ else
errs() << "Could not find main function.\n";
- return 1;
- }
- using MainFnPtr = int (*)(int, const char*[]);
- std::vector<const char *> ArgV;
- for (auto &Arg : Args)
- ArgV.push_back(Arg.c_str());
- auto Main = fromTargetAddress<MainFnPtr>(MainSym.getAddress());
- return Main(ArgV.size(), (const char**)ArgV.data());
+ return 1;
}
diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h
index fc02a10b514e0..47a2acc4d7e60 100644
--- a/tools/lli/OrcLazyJIT.h
+++ b/tools/lli/OrcLazyJIT.h
@@ -61,7 +61,8 @@ public:
IndirectStubsManagerBuilder IndirectStubsMgrBuilder,
bool InlineStubs)
: TM(std::move(TM)), DL(this->TM->createDataLayout()),
- CCMgr(std::move(CCMgr)),
+ CCMgr(std::move(CCMgr)),
+ ObjectLayer([]() { return std::make_shared<SectionMemoryManager>(); }),
CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)),
IRDumpLayer(CompileLayer, createDebugDumper()),
CODLayer(IRDumpLayer, extractSingleFunction, *this->CCMgr,
@@ -74,10 +75,14 @@ public:
CXXRuntimeOverrides.runDestructors();
// Run any IR destructors.
for (auto &DtorRunner : IRStaticDestructorRunners)
- DtorRunner.runViaLayer(CODLayer);
+ if (auto Err = DtorRunner.runViaLayer(CODLayer)) {
+ // FIXME: OrcLazyJIT should probably take a "shutdownError" callback to
+ // report these errors on.
+ report_fatal_error(std::move(Err));
+ }
}
- void addModule(std::shared_ptr<Module> M) {
+ Error addModule(std::shared_ptr<Module> M) {
if (M->getDataLayout().isDefault())
M->setDataLayout(DL);
@@ -124,21 +129,27 @@ public:
);
// Add the module to the JIT.
- ModulesHandle =
- CODLayer.addModule(std::move(M),
- llvm::make_unique<SectionMemoryManager>(),
- std::move(Resolver));
+ if (auto ModulesHandleOrErr =
+ CODLayer.addModule(std::move(M), std::move(Resolver)))
+ ModulesHandle = std::move(*ModulesHandleOrErr);
+ else
+ return ModulesHandleOrErr.takeError();
+
} else
- CODLayer.addExtraModule(ModulesHandle, std::move(M));
+ if (auto Err = CODLayer.addExtraModule(ModulesHandle, std::move(M)))
+ return Err;
// Run the static constructors, and save the static destructor runner for
// execution when the JIT is torn down.
orc::CtorDtorRunner<CODLayerT> CtorRunner(std::move(CtorNames),
ModulesHandle);
- CtorRunner.runViaLayer(CODLayer);
+ if (auto Err = CtorRunner.runViaLayer(CODLayer))
+ return Err;
IRStaticDestructorRunners.emplace_back(std::move(DtorNames),
ModulesHandle);
+
+ return Error::success();
}
JITSymbol findSymbol(const std::string &Name) {
diff --git a/tools/lli/RemoteJITUtils.h b/tools/lli/RemoteJITUtils.h
index 3c82f73ff0724..4e948413865cb 100644
--- a/tools/lli/RemoteJITUtils.h
+++ b/tools/lli/RemoteJITUtils.h
@@ -84,7 +84,7 @@ public:
this->MemMgr = std::move(MemMgr);
}
- void setResolver(std::unique_ptr<JITSymbolResolver> Resolver) {
+ void setResolver(std::shared_ptr<JITSymbolResolver> Resolver) {
this->Resolver = std::move(Resolver);
}
@@ -145,7 +145,7 @@ public:
private:
std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr;
- std::unique_ptr<JITSymbolResolver> Resolver;
+ std::shared_ptr<JITSymbolResolver> Resolver;
};
}
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp
index f228a36194573..091ca22b4e82c 100644
--- a/tools/lli/lli.cpp
+++ b/tools/lli/lli.cpp
@@ -646,7 +646,7 @@ int main(int argc, char **argv, char * const *envp) {
// else == "if (RemoteMCJIT)"
// Remote target MCJIT doesn't (yet) support static constructors. No reason
- // it couldn't. This is a limitation of the LLI implemantation, not the
+ // it couldn't. This is a limitation of the LLI implementation, not the
// MCJIT itself. FIXME.
// Lanch the remote process and get a channel to it.
diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index 528247c2dbc3b..529bdf5b7d933 100644
--- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -71,6 +71,10 @@ static cl::opt<bool>
ShowBinaryBlobs("show-binary-blobs",
cl::desc("Print binary blobs using hex escapes"));
+static cl::opt<std::string> CheckHash(
+ "check-hash",
+ cl::desc("Check module hash using the argument as a string table"));
+
namespace {
/// CurStreamTypeType - A type for CurStreamType
@@ -652,13 +656,15 @@ static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo,
}
// If we found a module hash, let's verify that it matches!
- if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH) {
+ if (BlockID == bitc::MODULE_BLOCK_ID && Code == bitc::MODULE_CODE_HASH &&
+ !CheckHash.empty()) {
if (Record.size() != 5)
outs() << " (invalid)";
else {
// Recompute the hash and compare it to the one in the bitcode
SHA1 Hasher;
StringRef Hash;
+ Hasher.update(CheckHash);
{
int BlockSize = (CurrentRecordPos / 8) - BlockEntryPos;
auto Ptr = Stream.getPointerToByte(BlockEntryPos, BlockSize);
diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp
index 52ce85c577821..966c0083bf872 100644
--- a/tools/llvm-c-test/echo.cpp
+++ b/tools/llvm-c-test/echo.cpp
@@ -765,7 +765,7 @@ static void declare_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
LLVMValueRef Next = nullptr;
if (!Begin) {
if (End != nullptr)
- report_fatal_error("Range has an end but no begining");
+ report_fatal_error("Range has an end but no beginning");
goto FunDecl;
}
@@ -794,7 +794,7 @@ FunDecl:
End = LLVMGetLastFunction(Src);
if (!Begin) {
if (End != nullptr)
- report_fatal_error("Range has an end but no begining");
+ report_fatal_error("Range has an end but no beginning");
return;
}
@@ -844,7 +844,7 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
LLVMValueRef Next = nullptr;
if (!Begin) {
if (End != nullptr)
- report_fatal_error("Range has an end but no begining");
+ report_fatal_error("Range has an end but no beginning");
goto FunClone;
}
@@ -885,7 +885,7 @@ FunClone:
End = LLVMGetLastFunction(Src);
if (!Begin) {
if (End != nullptr)
- report_fatal_error("Range has an end but no begining");
+ report_fatal_error("Range has an end but no beginning");
return;
}
diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp
index 6179c760d5b20..3cbd6591134b0 100644
--- a/tools/llvm-cov/CodeCoverage.cpp
+++ b/tools/llvm-cov/CodeCoverage.cpp
@@ -32,6 +32,7 @@
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/ToolOutputFile.h"
#include <functional>
@@ -705,6 +706,12 @@ int CodeCoverageTool::show(int argc, const char **argv,
"project-title", cl::Optional,
cl::desc("Set project title for the coverage report"));
+ cl::opt<unsigned> NumThreads(
+ "num-threads", cl::init(0),
+ cl::desc("Number of merge threads to use (default: autodetect)"));
+ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
+ cl::aliasopt(NumThreads));
+
auto Err = commandLineParser(argc, argv);
if (Err)
return Err;
@@ -790,15 +797,19 @@ int CodeCoverageTool::show(int argc, const char **argv,
}
}
- // FIXME: Sink the hardware_concurrency() == 1 check into ThreadPool.
- if (!ViewOpts.hasOutputDirectory() ||
- std::thread::hardware_concurrency() == 1) {
+ // If NumThreads is not specified, auto-detect a good default.
+ if (NumThreads == 0)
+ NumThreads =
+ std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
+ unsigned(SourceFiles.size())));
+
+ if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) {
for (const std::string &SourceFile : SourceFiles)
writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
ShowFilenames);
} else {
// In -output-dir mode, it's safe to use multiple threads to print files.
- ThreadPool Pool;
+ ThreadPool Pool(NumThreads);
for (const std::string &SourceFile : SourceFiles)
Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
Coverage.get(), Printer.get(), ShowFilenames);
diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp
index ccc673be45702..87cd13ad70de9 100644
--- a/tools/llvm-lto/llvm-lto.cpp
+++ b/tools/llvm-lto/llvm-lto.cpp
@@ -383,7 +383,7 @@ loadAllFilesForIndex(const ModuleSummaryIndex &Index) {
for (auto &ModPath : Index.modulePaths()) {
const auto &Filename = ModPath.first();
- auto CurrentActivity = "loading file '" + Filename + "'";
+ std::string CurrentActivity = ("loading file '" + Filename + "'").str();
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
@@ -475,7 +475,7 @@ private:
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (unsigned i = 0; i < InputFilenames.size(); ++i) {
auto &Filename = InputFilenames[i];
- StringRef CurrentActivity = "loading file '" + Filename + "'";
+ std::string CurrentActivity = "loading file '" + Filename + "'";
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
@@ -710,7 +710,7 @@ private:
std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers;
for (unsigned i = 0; i < InputFilenames.size(); ++i) {
auto &Filename = InputFilenames[i];
- StringRef CurrentActivity = "loading file '" + Filename + "'";
+ std::string CurrentActivity = "loading file '" + Filename + "'";
auto InputOrErr = MemoryBuffer::getFile(Filename);
error(InputOrErr, "error " + CurrentActivity);
InputBuffers.push_back(std::move(*InputOrErr));
diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp
index be5635a3d4c69..812f1af3ac680 100644
--- a/tools/llvm-objdump/llvm-objdump.cpp
+++ b/tools/llvm-objdump/llvm-objdump.cpp
@@ -1032,7 +1032,7 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj,
case MachO::ARM_RELOC_HALF_SECTDIFF: {
// Half relocations steal a bit from the length field to encode
// whether this is an upper16 or a lower16 relocation.
- bool isUpper = Obj->getAnyRelocationLength(RE) >> 1;
+ bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1;
if (isUpper)
fmt << ":upper16:(";
diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt
index 7a3245424efc6..bc28e6bdd7eaa 100644
--- a/tools/llvm-pdbutil/CMakeLists.txt
+++ b/tools/llvm-pdbutil/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_tool(llvm-pdbutil
Analyze.cpp
BytesOutputStyle.cpp
Diff.cpp
+ DiffPrinter.cpp
DumpOutputStyle.cpp
llvm-pdbutil.cpp
FormatUtil.cpp
diff --git a/tools/llvm-pdbutil/Diff.cpp b/tools/llvm-pdbutil/Diff.cpp
index 9b38ae1d603ef..aad4e1bf14279 100644
--- a/tools/llvm-pdbutil/Diff.cpp
+++ b/tools/llvm-pdbutil/Diff.cpp
@@ -9,22 +9,162 @@
#include "Diff.h"
+#include "DiffPrinter.h"
+#include "FormatUtil.h"
#include "StreamUtil.h"
#include "llvm-pdbutil.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/Formatters.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::pdb;
+namespace {
+// Compare and format two stream numbers. Stream numbers are considered
+// identical if they contain the same value, equivalent if they are both
+// the invalid stream or neither is the invalid stream, and different if
+// one is the invalid stream and another isn't.
+struct StreamNumberProvider {
+ static DiffResult compare(uint16_t L, uint16_t R) {
+ if (L == R)
+ return DiffResult::IDENTICAL;
+ bool LP = L != kInvalidStreamIndex;
+ bool RP = R != kInvalidStreamIndex;
+ if (LP != RP)
+ return DiffResult::DIFFERENT;
+ return DiffResult::EQUIVALENT;
+ }
+
+ static std::string format(uint16_t SN, bool Right) {
+ if (SN == kInvalidStreamIndex)
+ return "(not present)";
+ return formatv("{0}", SN).str();
+ }
+};
+
+// Compares and formats two module indices. Modis are considered identical
+// if they are identical, equivalent if they either both contain a value or
+// both don't contain a value, and different if one contains a value and the
+// other doesn't.
+struct ModiProvider {
+ DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) {
+ if (L == R)
+ return DiffResult::IDENTICAL;
+ if (L.hasValue() != R.hasValue())
+ return DiffResult::DIFFERENT;
+ return DiffResult::EQUIVALENT;
+ }
+
+ std::string format(Optional<uint32_t> Modi, bool Right) {
+ if (!Modi.hasValue())
+ return "(not present)";
+ return formatv("{0}", *Modi).str();
+ }
+};
+
+// Compares and formats two paths embedded in the PDB, ignoring the beginning
+// of the path if the user specified it as a "root path" on the command line.
+struct BinaryPathProvider {
+ explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
+
+ DiffResult compare(StringRef L, StringRef R) {
+ if (L == R)
+ return DiffResult::IDENTICAL;
+
+ SmallString<64> LN = removeRoot(L, false);
+ SmallString<64> RN = removeRoot(R, true);
+
+ return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT
+ : DiffResult::DIFFERENT;
+ }
+
+ std::string format(StringRef S, bool Right) {
+ if (S.empty())
+ return "(empty)";
+
+ SmallString<64> Native = removeRoot(S, Right);
+ return truncateStringFront(Native.str(), MaxLen);
+ }
+
+ SmallString<64> removeRoot(StringRef Path, bool IsRight) const {
+ SmallString<64> Native(Path);
+ auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot;
+ SmallString<64> Root(static_cast<std::string>(RootOpt));
+ // pdb paths always use windows syntax, convert slashes to backslashes.
+ sys::path::native(Root, sys::path::Style::windows);
+ if (sys::path::has_stem(Root, sys::path::Style::windows))
+ sys::path::append(Root, sys::path::Style::windows,
+ sys::path::get_separator(sys::path::Style::windows));
+
+ sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows);
+ return Native;
+ }
+ uint32_t MaxLen;
+};
+
+// Compare and format two stream purposes. For general streams, this just
+// compares the description. For module streams it uses the path comparison
+// algorithm taking into consideration the binary root, described above.
+// Formatting stream purposes just prints the stream purpose, except for
+// module streams and named streams, where it prefixes the name / module
+// with an identifier. Example:
+//
+// Named Stream "\names"
+// Module Stream "foo.obj"
+//
+// If a named stream is too long to fit in a column, it is truncated at the
+// end, and if a module is too long to fit in a column, it is truncated at the
+// beginning. Example:
+//
+// Named Stream "\Really Long Str..."
+// Module Stream "...puts\foo.obj"
+//
+struct StreamPurposeProvider {
+ explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
+
+ DiffResult compare(const std::pair<StreamPurpose, std::string> &L,
+ const std::pair<StreamPurpose, std::string> &R) {
+ if (L.first != R.first)
+ return DiffResult::DIFFERENT;
+ if (L.first == StreamPurpose::ModuleStream) {
+ BinaryPathProvider PathProvider(MaxLen);
+ return PathProvider.compare(L.second, R.second);
+ }
+ return (L.second == R.second) ? DiffResult::IDENTICAL
+ : DiffResult::DIFFERENT;
+ }
+
+ std::string format(const std::pair<StreamPurpose, std::string> &P,
+ bool Right) {
+ if (P.first == StreamPurpose::Other)
+ return truncateStringBack(P.second, MaxLen);
+ if (P.first == StreamPurpose::NamedStream)
+ return truncateQuotedNameBack("Named Stream", P.second, MaxLen);
+
+ assert(P.first == StreamPurpose::ModuleStream);
+ uint32_t ExtraChars = strlen("Module \"\"");
+ BinaryPathProvider PathProvider(MaxLen - ExtraChars);
+ std::string Result = PathProvider.format(P.second, Right);
+ return formatv("Module \"{0}\"", Result);
+ }
+
+ uint32_t MaxLen;
+};
+} // namespace
+
namespace llvm {
template <> struct format_provider<PdbRaw_FeatureSig> {
static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
@@ -49,47 +189,6 @@ template <> struct format_provider<PdbRaw_FeatureSig> {
template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
-template <typename Range, typename Comp>
-static void set_differences(Range &&R1, Range &&R2,
- SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
- SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
- SmallVectorImpl<ValueOfRange<Range>> *Intersection,
- Comp Comparator) {
-
- std::sort(R1.begin(), R1.end(), Comparator);
- std::sort(R2.begin(), R2.end(), Comparator);
-
- if (OnlyLeft) {
- OnlyLeft->reserve(R1.size());
- auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(),
- OnlyLeft->begin(), Comparator);
- OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End));
- }
- if (OnlyRight) {
- OnlyLeft->reserve(R2.size());
- auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(),
- OnlyRight->begin(), Comparator);
- OnlyRight->set_size(std::distance(OnlyRight->begin(), End));
- }
- if (Intersection) {
- Intersection->reserve(std::min(R1.size(), R2.size()));
- auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(),
- Intersection->begin(), Comparator);
- Intersection->set_size(std::distance(Intersection->begin(), End));
- }
-}
-
-template <typename Range>
-static void
-set_differences(Range &&R1, Range &&R2,
- SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
- SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
- SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) {
- std::less<ValueOfRange<Range>> Comp;
- set_differences(std::forward<Range>(R1), std::forward<Range>(R2), OnlyLeft,
- OnlyRight, Intersection, Comp);
-}
-
DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
: File1(File1), File2(File2) {}
@@ -136,300 +235,363 @@ Error DiffStyle::dump() {
return Error::success();
}
-template <typename T>
-static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1,
- T V2) {
- if (V1 == V2) {
- outs() << formatv(" {0}: No differences detected!\n", Label);
- return false;
- }
-
- outs().indent(2) << Label << "\n";
- outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1);
- outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2);
- return true;
-}
-
-template <typename T>
-static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
- ArrayRef<T> V1, ArrayRef<T> V2) {
- if (V1 == V2) {
- outs() << formatv(" {0}: No differences detected!\n", Label);
- return false;
- }
-
- outs().indent(2) << Label << "\n";
- outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(),
- make_range(V1.begin(), V1.end()));
- outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(),
- make_range(V2.begin(), V2.end()));
- return true;
-}
-
-template <typename T>
-static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2,
- T &&OnlyRange1, T &&OnlyRange2,
- StringRef Label) {
- bool HasDiff = false;
- if (!OnlyRange1.empty()) {
- HasDiff = true;
- outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label,
- File1.getFilePath());
- for (const auto &Item : OnlyRange1)
- outs() << formatv(" {0}\n", Label, Item);
- }
- if (!OnlyRange2.empty()) {
- HasDiff = true;
- outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange2.size(),
- File2.getFilePath());
- for (const auto &Item : OnlyRange2)
- outs() << formatv(" {0}\n", Item);
- }
- return HasDiff;
-}
-
Error DiffStyle::diffSuperBlock() {
- outs() << "MSF Super Block: Searching for differences...\n";
- bool Diffs = false;
-
- Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(),
- File2.getBlockSize());
- Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(),
- File2.getBlockCount());
- Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(),
- File2.getUnknown1());
- if (!Diffs)
- outs() << "MSF Super Block: No differences detected...\n";
+ DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn,
+ opts::diff::PrintValueColumns, outs());
+ D.printExplicit("File", DiffResult::UNSPECIFIED,
+ truncateStringFront(File1.getFilePath(), 18),
+ truncateStringFront(File2.getFilePath(), 18));
+ D.print("Block Size", File1.getBlockSize(), File2.getBlockSize());
+ D.print("Block Count", File1.getBlockCount(), File2.getBlockCount());
+ D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1());
+ D.print("Directory Size", File1.getNumDirectoryBytes(),
+ File2.getNumDirectoryBytes());
return Error::success();
}
Error DiffStyle::diffStreamDirectory() {
- SmallVector<std::string, 32> P;
- SmallVector<std::string, 32> Q;
+ DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn,
+ opts::diff::PrintValueColumns, outs());
+ D.printExplicit("File", DiffResult::UNSPECIFIED,
+ truncateStringFront(File1.getFilePath(), 18),
+ truncateStringFront(File2.getFilePath(), 18));
+
+ SmallVector<std::pair<StreamPurpose, std::string>, 32> P;
+ SmallVector<std::pair<StreamPurpose, std::string>, 32> Q;
discoverStreamPurposes(File1, P);
discoverStreamPurposes(File2, Q);
- outs() << "Stream Directory: Searching for differences...\n";
-
- bool HasDifferences = false;
+ D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams());
auto PI = to_vector<32>(enumerate(P));
auto QI = to_vector<32>(enumerate(Q));
- typedef decltype(PI) ContainerType;
- typedef typename ContainerType::value_type value_type;
-
- auto Comparator = [](const value_type &I1, const value_type &I2) {
- return I1.value() < I2.value();
- };
-
- decltype(PI) OnlyP;
- decltype(QI) OnlyQ;
- decltype(PI) Common;
-
- set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator);
-
- if (!OnlyP.empty()) {
- HasDifferences = true;
- outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(),
- File1.getFilePath());
- for (auto &Item : OnlyP) {
- outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
- Item.value());
+ // Scan all streams in the left hand side, looking for ones that are also
+ // in the right. Each time we find one, remove it. When we're done, Q
+ // should contain all the streams that are in the right but not in the left.
+ StreamPurposeProvider StreamProvider(28);
+ for (const auto &P : PI) {
+ typedef decltype(PI) ContainerType;
+ typedef typename ContainerType::value_type value_type;
+
+ auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) {
+ DiffResult Result = StreamProvider.compare(P.value(), V.value());
+ return Result == DiffResult::EQUIVALENT ||
+ Result == DiffResult::IDENTICAL;
+ });
+
+ if (Iter == QI.end()) {
+ D.printExplicit(StreamProvider.format(P.value(), false),
+ DiffResult::DIFFERENT, P.index(), "(not present)");
+ continue;
}
- }
- if (!OnlyQ.empty()) {
- HasDifferences = true;
- outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", OnlyQ.size(),
- File2.getFilePath());
- for (auto &Item : OnlyQ) {
- outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
- Item.value());
- }
+ D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false),
+ P.index(), Iter->index());
+ QI.erase(Iter);
}
- if (!Common.empty()) {
- outs().indent(2) << formatv("Found {0} common streams. Searching for "
- "intra-stream differences.\n",
- Common.size());
- bool HasCommonDifferences = false;
- for (const auto &Left : Common) {
- // Left was copied from the first range so its index refers to a stream
- // index in the first file. Find the corresponding stream index in the
- // second file.
- auto Range =
- std::equal_range(QI.begin(), QI.end(), Left,
- [](const value_type &L, const value_type &R) {
- return L.value() < R.value();
- });
- const auto &Right = *Range.first;
- assert(Left.value() == Right.value());
- uint32_t LeftSize = File1.getStreamByteSize(Left.index());
- uint32_t RightSize = File2.getStreamByteSize(Right.index());
- if (LeftSize != RightSize) {
- HasDifferences = true;
- HasCommonDifferences = true;
- outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n",
- Left.value(), File1.getFilePath(), LeftSize,
- File2.getFilePath(), RightSize);
- }
- }
- if (!HasCommonDifferences)
- outs().indent(2) << "Common Streams: No differences detected!\n";
+
+ for (const auto &Q : QI) {
+ D.printExplicit(StreamProvider.format(Q.value(), true),
+ DiffResult::DIFFERENT, "(not present)", Q.index());
}
- if (!HasDifferences)
- outs() << "Stream Directory: No differences detected!\n";
return Error::success();
}
Error DiffStyle::diffStringTable() {
+ DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn,
+ opts::diff::PrintValueColumns, outs());
+ D.printExplicit("File", DiffResult::UNSPECIFIED,
+ truncateStringFront(File1.getFilePath(), 18),
+ truncateStringFront(File2.getFilePath(), 18));
+
auto ExpectedST1 = File1.getStringTable();
auto ExpectedST2 = File2.getStringTable();
- outs() << "String Table: Searching for differences...\n";
bool Has1 = !!ExpectedST1;
bool Has2 = !!ExpectedST2;
- if (!(Has1 && Has2)) {
- // If one has a string table and the other doesn't, we can print less
- // output.
- if (Has1 != Has2) {
- if (Has1) {
- outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(),
- ExpectedST1->getNameCount());
- outs() << formatv(" {0}: (string table not present)\n",
- File2.getFilePath());
- } else {
- outs() << formatv(" {0}: (string table not present)\n",
- File1.getFilePath());
- outs() << formatv(" {0}: ({1})\n", File2.getFilePath(),
- ExpectedST2->getNameCount());
- }
- }
+ std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount())
+ : "(string table not present)";
+ std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount())
+ : "(string table not present)";
+ D.print("Number of Strings", Count1, Count2);
+
+ if (!Has1 || !Has2) {
consumeError(ExpectedST1.takeError());
consumeError(ExpectedST2.takeError());
return Error::success();
}
- bool HasDiff = false;
auto &ST1 = *ExpectedST1;
auto &ST2 = *ExpectedST2;
- if (ST1.getByteSize() != ST2.getByteSize()) {
- outs() << " Stream Size\n";
- outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
- ST1.getByteSize());
- outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
- ST2.getByteSize());
- outs() << formatv(" Difference: {0} bytes\n",
- AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize()));
- HasDiff = true;
- }
- HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(),
- ST1.getHashVersion());
- HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(),
- ST1.getSignature());
+ D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion());
+ D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize());
+ D.print("Signature", ST1.getSignature(), ST2.getSignature());
// Both have a valid string table, dive in and compare individual strings.
auto IdList1 = ST1.name_ids();
auto IdList2 = ST2.name_ids();
- std::vector<StringRef> Strings1, Strings2;
- Strings1.reserve(IdList1.size());
- Strings2.reserve(IdList2.size());
+ StringSet<> LS;
+ StringSet<> RS;
+ uint32_t Empty1 = 0;
+ uint32_t Empty2 = 0;
for (auto ID : IdList1) {
auto S = ST1.getStringForID(ID);
if (!S)
return S.takeError();
- Strings1.push_back(*S);
+ if (S->empty())
+ ++Empty1;
+ else
+ LS.insert(*S);
}
for (auto ID : IdList2) {
auto S = ST2.getStringForID(ID);
if (!S)
return S.takeError();
- Strings2.push_back(*S);
+ if (S->empty())
+ ++Empty2;
+ else
+ RS.insert(*S);
+ }
+ D.print("Empty Strings", Empty1, Empty2);
+
+ for (const auto &S : LS) {
+ auto R = RS.find(S.getKey());
+ std::string Truncated = truncateStringMiddle(S.getKey(), 28);
+ uint32_t I = cantFail(ST1.getIDForString(S.getKey()));
+ if (R == RS.end()) {
+ D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)");
+ continue;
+ }
+
+ uint32_t J = cantFail(ST2.getIDForString(R->getKey()));
+ D.print<EquivalentDiffProvider>(Truncated, I, J);
+ RS.erase(R);
}
- SmallVector<StringRef, 64> OnlyP;
- SmallVector<StringRef, 64> OnlyQ;
- auto End1 = std::remove(Strings1.begin(), Strings1.end(), "");
- auto End2 = std::remove(Strings2.begin(), Strings2.end(), "");
- uint32_t Empty1 = std::distance(End1, Strings1.end());
- uint32_t Empty2 = std::distance(End2, Strings2.end());
- Strings1.erase(End1, Strings1.end());
- Strings2.erase(End2, Strings2.end());
- set_differences(Strings1, Strings2, &OnlyP, &OnlyQ);
- printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String");
-
- if (Empty1 != Empty2) {
- PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2;
- PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2;
- uint32_t Difference = AbsoluteDifference(Empty1, Empty2);
- outs() << formatv(" {0} had {1} more empty strings than {2}\n",
- MoreF.getFilePath(), Difference, LessF.getFilePath());
+ for (const auto &S : RS) {
+ auto L = LS.find(S.getKey());
+ std::string Truncated = truncateStringMiddle(S.getKey(), 28);
+ uint32_t J = cantFail(ST2.getIDForString(S.getKey()));
+ if (L == LS.end()) {
+ D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J);
+ continue;
+ }
+
+ uint32_t I = cantFail(ST1.getIDForString(L->getKey()));
+ D.print<EquivalentDiffProvider>(Truncated, I, J);
}
- if (!HasDiff)
- outs() << "String Table: No differences detected!\n";
return Error::success();
}
Error DiffStyle::diffFreePageMap() { return Error::success(); }
Error DiffStyle::diffInfoStream() {
+ DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn,
+ opts::diff::PrintValueColumns, outs());
+ D.printExplicit("File", DiffResult::UNSPECIFIED,
+ truncateStringFront(File1.getFilePath(), 38),
+ truncateStringFront(File2.getFilePath(), 38));
+
auto ExpectedInfo1 = File1.getPDBInfoStream();
auto ExpectedInfo2 = File2.getPDBInfoStream();
- outs() << "PDB Stream: Searching for differences...\n";
bool Has1 = !!ExpectedInfo1;
bool Has2 = !!ExpectedInfo2;
if (!(Has1 && Has2)) {
- if (Has1 != Has2)
- outs() << formatv("{0} does not have a PDB Stream!\n",
- Has1 ? File1.getFilePath() : File2.getFilePath());
- consumeError(ExpectedInfo2.takeError());
+ std::string L = Has1 ? "(present)" : "(not present)";
+ std::string R = Has2 ? "(present)" : "(not present)";
+ D.print("Stream", L, R);
+
+ consumeError(ExpectedInfo1.takeError());
consumeError(ExpectedInfo2.takeError());
return Error::success();
}
- bool HasDiff = false;
auto &IS1 = *ExpectedInfo1;
auto &IS2 = *ExpectedInfo2;
- if (IS1.getStreamSize() != IS2.getStreamSize()) {
- outs() << " Stream Size\n";
- outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
- IS1.getStreamSize());
- outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
- IS2.getStreamSize());
- outs() << formatv(
- " Difference: {0} bytes\n",
- AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize()));
- HasDiff = true;
+ D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize());
+ D.print("Age", IS1.getAge(), IS2.getAge());
+ D.print("Guid", IS1.getGuid(), IS2.getGuid());
+ D.print("Signature", IS1.getSignature(), IS2.getSignature());
+ D.print("Version", IS1.getVersion(), IS2.getVersion());
+ D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(),
+ IS2.getFeatureSignatures());
+ D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(),
+ IS2.getNamedStreamMapByteSize());
+ StringMap<uint32_t> NSL = IS1.getNamedStreams().getStringMap();
+ StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap();
+ D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR);
+ return Error::success();
+}
+
+static std::vector<std::pair<uint32_t, DbiModuleDescriptor>>
+getModuleDescriptors(const DbiModuleList &ML) {
+ std::vector<std::pair<uint32_t, DbiModuleDescriptor>> List;
+ List.reserve(ML.getModuleCount());
+ for (uint32_t I = 0; I < ML.getModuleCount(); ++I)
+ List.emplace_back(I, ML.getModuleDescriptor(I));
+ return List;
+}
+
+static void
+diffOneModule(DiffPrinter &D,
+ const std::pair<uint32_t, DbiModuleDescriptor> Item,
+ std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other,
+ bool ItemIsRight) {
+ StreamPurposeProvider HeaderProvider(70);
+ std::pair<StreamPurpose, std::string> Header;
+ Header.first = StreamPurpose::ModuleStream;
+ Header.second = Item.second.getModuleName();
+ D.printFullRow(HeaderProvider.format(Header, ItemIsRight));
+
+ const auto *L = &Item;
+
+ BinaryPathProvider PathProvider(28);
+ auto Iter = llvm::find_if(
+ Other, [&PathProvider, ItemIsRight,
+ L](const std::pair<uint32_t, DbiModuleDescriptor> &Other) {
+ const auto *Left = L;
+ const auto *Right = &Other;
+ if (ItemIsRight)
+ std::swap(Left, Right);
+ DiffResult Result = PathProvider.compare(Left->second.getModuleName(),
+ Right->second.getModuleName());
+ return Result == DiffResult::EQUIVALENT ||
+ Result == DiffResult::IDENTICAL;
+ });
+ if (Iter == Other.end()) {
+ // We didn't find this module at all on the other side. Just print one row
+ // and continue.
+ D.print<ModiProvider>("- Modi", Item.first, None);
+ return;
}
- HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge());
- HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid());
- HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(),
- IS2.getSignature());
- HasDiff |=
- diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion());
- HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(),
- IS2.getFeatureSignatures());
- HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2,
- IS1.getNamedStreamMapByteSize(),
- IS2.getNamedStreamMapByteSize());
- SmallVector<StringRef, 4> NS1;
- SmallVector<StringRef, 4> NS2;
- for (const auto &X : IS1.getNamedStreams().entries())
- NS1.push_back(X.getKey());
- for (const auto &X : IS2.getNamedStreams().entries())
- NS2.push_back(X.getKey());
- SmallVector<StringRef, 4> OnlyP;
- SmallVector<StringRef, 4> OnlyQ;
- set_differences(NS1, NS2, &OnlyP, &OnlyQ);
- printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
- if (!HasDiff)
- outs() << "PDB Stream: No differences detected!\n";
- return Error::success();
+ // We did find this module. Go through and compare each field.
+ const auto *R = &*Iter;
+ if (ItemIsRight)
+ std::swap(L, R);
+
+ D.print<ModiProvider>("- Modi", L->first, R->first);
+ D.print<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(),
+ R->second.getObjFileName(), PathProvider);
+ D.print<StreamNumberProvider>("- Debug Stream",
+ L->second.getModuleStreamIndex(),
+ R->second.getModuleStreamIndex());
+ D.print("- C11 Byte Size", L->second.getC11LineInfoByteSize(),
+ R->second.getC11LineInfoByteSize());
+ D.print("- C13 Byte Size", L->second.getC13LineInfoByteSize(),
+ R->second.getC13LineInfoByteSize());
+ D.print("- # of files", L->second.getNumberOfFiles(),
+ R->second.getNumberOfFiles());
+ D.print("- Pdb File Path Index", L->second.getPdbFilePathNameIndex(),
+ R->second.getPdbFilePathNameIndex());
+ D.print("- Source File Name Index", L->second.getSourceFileNameIndex(),
+ R->second.getSourceFileNameIndex());
+ D.print("- Symbol Byte Size", L->second.getSymbolDebugInfoByteSize(),
+ R->second.getSymbolDebugInfoByteSize());
+ Other.erase(Iter);
}
-Error DiffStyle::diffDbiStream() { return Error::success(); }
+Error DiffStyle::diffDbiStream() {
+ DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn,
+ opts::diff::PrintValueColumns, outs());
+ D.printExplicit("File", DiffResult::UNSPECIFIED,
+ truncateStringFront(File1.getFilePath(), 28),
+ truncateStringFront(File2.getFilePath(), 28));
+
+ auto ExpectedDbi1 = File1.getPDBDbiStream();
+ auto ExpectedDbi2 = File2.getPDBDbiStream();
+
+ bool Has1 = !!ExpectedDbi1;
+ bool Has2 = !!ExpectedDbi2;
+ if (!(Has1 && Has2)) {
+ std::string L = Has1 ? "(present)" : "(not present)";
+ std::string R = Has2 ? "(present)" : "(not present)";
+ D.print("Stream", L, R);
+
+ consumeError(ExpectedDbi1.takeError());
+ consumeError(ExpectedDbi2.takeError());
+ return Error::success();
+ }
+
+ auto &DL = *ExpectedDbi1;
+ auto &DR = *ExpectedDbi2;
+
+ D.print("Dbi Version", (uint32_t)DL.getDbiVersion(),
+ (uint32_t)DR.getDbiVersion());
+ D.print("Age", DL.getAge(), DR.getAge());
+ D.print("Machine", (uint16_t)DL.getMachineType(),
+ (uint16_t)DR.getMachineType());
+ D.print("Flags", DL.getFlags(), DR.getFlags());
+ D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion());
+ D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion());
+ D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber());
+ D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion());
+ D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld());
+ D.print<StreamNumberProvider>("DBG (FPO)",
+ DL.getDebugStreamIndex(DbgHeaderType::FPO),
+ DR.getDebugStreamIndex(DbgHeaderType::FPO));
+ D.print<StreamNumberProvider>(
+ "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception),
+ DR.getDebugStreamIndex(DbgHeaderType::Exception));
+ D.print<StreamNumberProvider>("DBG (Fixup)",
+ DL.getDebugStreamIndex(DbgHeaderType::Fixup),
+ DR.getDebugStreamIndex(DbgHeaderType::Fixup));
+ D.print<StreamNumberProvider>(
+ "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc),
+ DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc));
+ D.print<StreamNumberProvider>(
+ "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc),
+ DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc));
+ D.print<StreamNumberProvider>(
+ "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr),
+ DR.getDebugStreamIndex(DbgHeaderType::SectionHdr));
+ D.print<StreamNumberProvider>(
+ "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap),
+ DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap));
+ D.print<StreamNumberProvider>("DBG (Xdata)",
+ DL.getDebugStreamIndex(DbgHeaderType::Xdata),
+ DR.getDebugStreamIndex(DbgHeaderType::Xdata));
+ D.print<StreamNumberProvider>("DBG (Pdata)",
+ DL.getDebugStreamIndex(DbgHeaderType::Pdata),
+ DR.getDebugStreamIndex(DbgHeaderType::Pdata));
+ D.print<StreamNumberProvider>("DBG (NewFPO)",
+ DL.getDebugStreamIndex(DbgHeaderType::NewFPO),
+ DR.getDebugStreamIndex(DbgHeaderType::NewFPO));
+ D.print<StreamNumberProvider>(
+ "DBG (SectionHdrOrig)",
+ DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig),
+ DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig));
+ D.print<StreamNumberProvider>("Globals Stream",
+ DL.getGlobalSymbolStreamIndex(),
+ DR.getGlobalSymbolStreamIndex());
+ D.print<StreamNumberProvider>("Publics Stream",
+ DL.getPublicSymbolStreamIndex(),
+ DR.getPublicSymbolStreamIndex());
+ D.print<StreamNumberProvider>("Symbol Records", DL.getSymRecordStreamIndex(),
+ DR.getSymRecordStreamIndex());
+ D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes());
+ D.print("Is Incrementally Linked", DL.isIncrementallyLinked(),
+ DR.isIncrementallyLinked());
+ D.print("Is Stripped", DL.isStripped(), DR.isStripped());
+ const DbiModuleList &ML = DL.modules();
+ const DbiModuleList &MR = DR.modules();
+ D.print("Module Count", ML.getModuleCount(), MR.getModuleCount());
+ D.print("Source File Count", ML.getSourceFileCount(),
+ MR.getSourceFileCount());
+ auto MDL = getModuleDescriptors(ML);
+ auto MDR = getModuleDescriptors(MR);
+ // Scan all module descriptors from the left, and look for corresponding
+ // module descriptors on the right.
+ for (const auto &L : MDL)
+ diffOneModule(D, L, MDR, false);
+
+ for (const auto &R : MDR)
+ diffOneModule(D, R, MDL, true);
+
+ return Error::success();
+}
Error DiffStyle::diffSectionContribs() { return Error::success(); }
diff --git a/tools/llvm-pdbutil/DiffPrinter.cpp b/tools/llvm-pdbutil/DiffPrinter.cpp
new file mode 100644
index 0000000000000..dd61cc1825936
--- /dev/null
+++ b/tools/llvm-pdbutil/DiffPrinter.cpp
@@ -0,0 +1,147 @@
+
+#include "DiffPrinter.h"
+
+#include "llvm/Support/FormatAdapters.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+namespace {
+struct Colorize {
+ Colorize(raw_ostream &OS, DiffResult Result) : OS(OS) {
+ if (!OS.has_colors())
+ return;
+ switch (Result) {
+ case DiffResult::IDENTICAL:
+ OS.changeColor(raw_ostream::Colors::GREEN, false);
+ break;
+ case DiffResult::EQUIVALENT:
+ OS.changeColor(raw_ostream::Colors::YELLOW, true);
+ break;
+ default:
+ OS.changeColor(raw_ostream::Colors::RED, false);
+ break;
+ }
+ }
+
+ ~Colorize() {
+ if (OS.has_colors())
+ OS.resetColor();
+ }
+
+ raw_ostream &OS;
+};
+}
+
+DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header,
+ uint32_t PropertyWidth, uint32_t FieldWidth,
+ bool Result, bool Fields, raw_ostream &Stream)
+ : PrintResult(Result), PrintValues(Fields), Indent(Indent),
+ PropertyWidth(PropertyWidth), FieldWidth(FieldWidth), OS(Stream) {
+ printHeaderRow();
+ printFullRow(Header);
+}
+
+DiffPrinter::~DiffPrinter() {}
+
+uint32_t DiffPrinter::tableWidth() const {
+ // `|`
+ uint32_t W = 1;
+
+ // `<width>|`
+ W += PropertyWidth + 1;
+
+ if (PrintResult) {
+ // ` I |`
+ W += 4;
+ }
+
+ if (PrintValues) {
+ // `<width>|<width>|`
+ W += 2 * (FieldWidth + 1);
+ }
+ return W;
+}
+
+void DiffPrinter::printFullRow(StringRef Text) {
+ newLine();
+ printValue(Text, DiffResult::UNSPECIFIED, AlignStyle::Center,
+ tableWidth() - 2, true);
+ printSeparatorRow();
+}
+
+void DiffPrinter::printSeparatorRow() {
+ newLine();
+ OS << formatv("{0}", fmt_repeat('-', PropertyWidth));
+ if (PrintResult) {
+ OS << '+';
+ OS << formatv("{0}", fmt_repeat('-', 3));
+ }
+ if (PrintValues) {
+ OS << '+';
+ OS << formatv("{0}", fmt_repeat('-', FieldWidth));
+ OS << '+';
+ OS << formatv("{0}", fmt_repeat('-', FieldWidth));
+ }
+ OS << '|';
+}
+
+void DiffPrinter::printHeaderRow() {
+ newLine('-');
+ OS << formatv("{0}", fmt_repeat('-', tableWidth() - 1));
+}
+
+void DiffPrinter::newLine(char InitialChar) {
+ OS << "\n";
+ OS.indent(Indent) << InitialChar;
+}
+
+void DiffPrinter::printExplicit(StringRef Property, DiffResult C,
+ StringRef Left, StringRef Right) {
+ newLine();
+ printValue(Property, DiffResult::UNSPECIFIED, AlignStyle::Right,
+ PropertyWidth, true);
+ printResult(C);
+ printValue(Left, C, AlignStyle::Center, FieldWidth, false);
+ printValue(Right, C, AlignStyle::Center, FieldWidth, false);
+ printSeparatorRow();
+}
+
+void DiffPrinter::printResult(DiffResult Result) {
+ if (!PrintResult)
+ return;
+ switch (Result) {
+ case DiffResult::DIFFERENT:
+ printValue("D", Result, AlignStyle::Center, 3, true);
+ break;
+ case DiffResult::EQUIVALENT:
+ printValue("E", Result, AlignStyle::Center, 3, true);
+ break;
+ case DiffResult::IDENTICAL:
+ printValue("I", Result, AlignStyle::Center, 3, true);
+ break;
+ case DiffResult::UNSPECIFIED:
+ printValue(" ", Result, AlignStyle::Center, 3, true);
+ break;
+ }
+}
+
+void DiffPrinter::printValue(StringRef Value, DiffResult C, AlignStyle Style,
+ uint32_t Width, bool Force) {
+ if (!Force && !PrintValues)
+ return;
+
+ if (Style == AlignStyle::Right)
+ --Width;
+
+ std::string FormattedItem =
+ formatv("{0}", fmt_align(Value, Style, Width)).str();
+ if (C != DiffResult::UNSPECIFIED) {
+ Colorize Color(OS, C);
+ OS << FormattedItem;
+ } else
+ OS << FormattedItem;
+ if (Style == AlignStyle::Right)
+ OS << ' ';
+ OS << '|';
+}
diff --git a/tools/llvm-pdbutil/DiffPrinter.h b/tools/llvm-pdbutil/DiffPrinter.h
new file mode 100644
index 0000000000000..475747d8dc11d
--- /dev/null
+++ b/tools/llvm-pdbutil/DiffPrinter.h
@@ -0,0 +1,172 @@
+//===- DiffPrinter.h ------------------------------------------ *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+#include <unordered_set>
+
+namespace std {
+template <> struct hash<llvm::pdb::PdbRaw_FeatureSig> {
+ typedef llvm::pdb::PdbRaw_FeatureSig argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type Item) const {
+ return std::hash<uint32_t>{}(uint32_t(Item));
+ }
+};
+} // namespace std
+
+namespace llvm {
+namespace pdb {
+
+class PDBFile;
+
+enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT };
+
+struct IdenticalDiffProvider {
+ template <typename T, typename U>
+ DiffResult compare(const T &Left, const U &Right) {
+ return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT;
+ }
+
+ template <typename T> std::string format(const T &Item, bool Right) {
+ return formatv("{0}", Item).str();
+ }
+};
+
+struct EquivalentDiffProvider {
+ template <typename T, typename U>
+ DiffResult compare(const T &Left, const U &Right) {
+ return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT;
+ }
+
+ template <typename T> std::string format(const T &Item, bool Right) {
+ return formatv("{0}", Item).str();
+ }
+};
+
+class DiffPrinter {
+public:
+ DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth,
+ uint32_t FieldWidth, bool Result, bool Values,
+ raw_ostream &Stream);
+ ~DiffPrinter();
+
+ template <typename T, typename U> struct Identical {};
+
+ template <typename Provider = IdenticalDiffProvider, typename T, typename U>
+ void print(StringRef Property, const T &Left, const U &Right,
+ Provider P = Provider()) {
+ std::string L = P.format(Left, false);
+ std::string R = P.format(Right, true);
+
+ DiffResult Result = P.compare(Left, Right);
+ printExplicit(Property, Result, L, R);
+ }
+
+ void printExplicit(StringRef Property, DiffResult C, StringRef Left,
+ StringRef Right);
+
+ template <typename T, typename U>
+ void printExplicit(StringRef Property, DiffResult C, const T &Left,
+ const U &Right) {
+ std::string L = formatv("{0}", Left).str();
+ std::string R = formatv("{0}", Right).str();
+ printExplicit(Property, C, StringRef(L), StringRef(R));
+ }
+
+ template <typename T, typename U>
+ void diffUnorderedArray(StringRef Property, ArrayRef<T> Left,
+ ArrayRef<U> Right) {
+ std::unordered_set<T> LS(Left.begin(), Left.end());
+ std::unordered_set<U> RS(Right.begin(), Right.end());
+ std::string Count1 = formatv("{0} element(s)", Left.size());
+ std::string Count2 = formatv("{0} element(s)", Right.size());
+ print(std::string(Property) + "s (set)", Count1, Count2);
+ for (const auto &L : LS) {
+ auto Iter = RS.find(L);
+ std::string Text = formatv("{0}", L).str();
+ if (Iter == RS.end()) {
+ print(Property, Text, "(not present)");
+ continue;
+ }
+ print(Property, Text, Text);
+ RS.erase(Iter);
+ }
+ for (const auto &R : RS) {
+ auto Iter = LS.find(R);
+ std::string Text = formatv("{0}", R).str();
+ if (Iter == LS.end()) {
+ print(Property, "(not present)", Text);
+ continue;
+ }
+ print(Property, Text, Text);
+ }
+ }
+
+ template <typename ValueProvider = IdenticalDiffProvider, typename T,
+ typename U>
+ void diffUnorderedMap(StringRef Property, const StringMap<T> &Left,
+ const StringMap<U> &Right,
+ ValueProvider P = ValueProvider()) {
+ StringMap<U> RightCopy(Right);
+
+ std::string Count1 = formatv("{0} element(s)", Left.size());
+ std::string Count2 = formatv("{0} element(s)", Right.size());
+ print(std::string(Property) + "s (map)", Count1, Count2);
+
+ for (const auto &L : Left) {
+ auto Iter = RightCopy.find(L.getKey());
+ if (Iter == RightCopy.end()) {
+ printExplicit(L.getKey(), DiffResult::DIFFERENT, L.getValue(),
+ "(not present)");
+ continue;
+ }
+
+ print(L.getKey(), L.getValue(), Iter->getValue(), P);
+ RightCopy.erase(Iter);
+ }
+
+ for (const auto &R : RightCopy) {
+ printExplicit(R.getKey(), DiffResult::DIFFERENT, "(not present)",
+ R.getValue());
+ }
+ }
+
+ void printFullRow(StringRef Text);
+
+private:
+ uint32_t tableWidth() const;
+
+ void printHeaderRow();
+ void printSeparatorRow();
+ void newLine(char InitialChar = '|');
+ void printValue(StringRef Value, DiffResult C, AlignStyle Style,
+ uint32_t Width, bool Force);
+ void printResult(DiffResult Result);
+
+ bool PrintResult;
+ bool PrintValues;
+ uint32_t Indent;
+ uint32_t PropertyWidth;
+ uint32_t FieldWidth;
+ raw_ostream &OS;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp
index a1f919b4dd065..0642d841fd9f2 100644
--- a/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -418,6 +418,13 @@ Error DumpOutputStyle::dumpModules() {
P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}",
Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(),
Modi.hasECInfo());
+ StringRef PdbFilePath =
+ Err(Stream.getECName(Modi.getPdbFilePathNameIndex()));
+ StringRef SrcFilePath =
+ Err(Stream.getECName(Modi.getSourceFileNameIndex()));
+ P.formatLine(" pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
+ Modi.getPdbFilePathNameIndex(), PdbFilePath,
+ Modi.getSourceFileNameIndex(), SrcFilePath);
}
return Error::success();
}
diff --git a/tools/llvm-pdbutil/FormatUtil.cpp b/tools/llvm-pdbutil/FormatUtil.cpp
index 1bbe2724f0ab9..02030272dd4da 100644
--- a/tools/llvm-pdbutil/FormatUtil.cpp
+++ b/tools/llvm-pdbutil/FormatUtil.cpp
@@ -16,6 +16,58 @@
using namespace llvm;
using namespace llvm::pdb;
+std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return S;
+
+ assert(MaxLen >= 3);
+ uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
+ S = S.take_front(FinalLen);
+ return std::string(S) + std::string("...");
+}
+
+std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return S;
+
+ assert(MaxLen >= 3);
+ uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3);
+ StringRef Front = S.take_front(FinalLen / 2);
+ StringRef Back = S.take_back(Front.size());
+ return std::string(Front) + std::string("...") + std::string(Back);
+}
+
+std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) {
+ if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3)
+ return S;
+
+ assert(MaxLen >= 3);
+ S = S.take_back(MaxLen - 3);
+ return std::string("...") + std::string(S);
+}
+
+std::string llvm::pdb::truncateQuotedNameFront(StringRef Label, StringRef Name,
+ uint32_t MaxLen) {
+ uint32_t RequiredExtraChars = Label.size() + 1 + 2;
+ if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen)
+ return formatv("{0} \"{1}\"", Label, Name).str();
+
+ assert(MaxLen >= RequiredExtraChars);
+ std::string TN = truncateStringFront(Name, MaxLen - RequiredExtraChars);
+ return formatv("{0} \"{1}\"", Label, TN).str();
+}
+
+std::string llvm::pdb::truncateQuotedNameBack(StringRef Label, StringRef Name,
+ uint32_t MaxLen) {
+ uint32_t RequiredExtraChars = Label.size() + 1 + 2;
+ if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen)
+ return formatv("{0} \"{1}\"", Label, Name).str();
+
+ assert(MaxLen >= RequiredExtraChars);
+ std::string TN = truncateStringBack(Name, MaxLen - RequiredExtraChars);
+ return formatv("{0} \"{1}\"", Label, TN).str();
+}
+
std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts,
uint32_t IndentLevel, uint32_t GroupSize,
StringRef Sep) {
diff --git a/tools/llvm-pdbutil/FormatUtil.h b/tools/llvm-pdbutil/FormatUtil.h
index 3db2dbacc57b6..df32ed9360fba 100644
--- a/tools/llvm-pdbutil/FormatUtil.h
+++ b/tools/llvm-pdbutil/FormatUtil.h
@@ -22,6 +22,14 @@
namespace llvm {
namespace pdb {
+std::string truncateStringBack(StringRef S, uint32_t MaxLen);
+std::string truncateStringMiddle(StringRef S, uint32_t MaxLen);
+std::string truncateStringFront(StringRef S, uint32_t MaxLen);
+std::string truncateQuotedNameFront(StringRef Label, StringRef Name,
+ uint32_t MaxLen);
+std::string truncateQuotedNameBack(StringRef Label, StringRef Name,
+ uint32_t MaxLen);
+
#define PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, Text) \
if (Enum::TheOpt == (Value & Mask)) \
Opts.push_back(Text);
@@ -33,7 +41,7 @@ namespace pdb {
case Enum::X: \
return Ret;
-template <typename T> static std::string formatUnknownEnum(T Value) {
+template <typename T> std::string formatUnknownEnum(T Value) {
return formatv("unknown ({0})",
static_cast<typename std::underlying_type<T>::type>(Value))
.str();
diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index 1af53e35ed111..9621320ea99ad 100644
--- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -299,7 +299,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
ClassRecord &Class) {
- P.formatLine("class name: `{0}`", Class.Name);
+ P.format(" `{0}`", Class.Name);
if (Class.hasUniqueName())
P.formatLine("unique name: `{0}`", Class.UniqueName);
P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
@@ -311,7 +311,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
UnionRecord &Union) {
- P.formatLine("class name: `{0}`", Union.Name);
+ P.format(" `{0}`", Union.Name);
if (Union.hasUniqueName())
P.formatLine("unique name: `{0}`", Union.UniqueName);
P.formatLine("field list: {0}", Union.FieldList);
@@ -321,7 +321,7 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
}
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
- P.formatLine("name: `{0}`", Enum.Name);
+ P.format(" `{0}`", Enum.Name);
if (Enum.hasUniqueName())
P.formatLine("unique name: `{0}`", Enum.UniqueName);
P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
diff --git a/tools/llvm-pdbutil/StreamUtil.cpp b/tools/llvm-pdbutil/StreamUtil.cpp
index 81aa256b5002d..4d352004dec30 100644
--- a/tools/llvm-pdbutil/StreamUtil.cpp
+++ b/tools/llvm-pdbutil/StreamUtil.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "StreamUtil.h"
+#include "FormatUtil.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
@@ -18,11 +19,12 @@
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
-namespace llvm {
-namespace pdb {
-void discoverStreamPurposes(PDBFile &File,
- SmallVectorImpl<std::string> &Purposes) {
+using namespace llvm;
+using namespace llvm::pdb;
+void llvm::pdb::discoverStreamPurposes(
+ PDBFile &File,
+ SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes) {
// It's OK if we fail to load some of these streams, we still attempt to print
// what we can.
auto Dbi = File.getPDBDbiStream();
@@ -52,74 +54,72 @@ void discoverStreamPurposes(PDBFile &File,
Purposes.resize(StreamCount);
for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
- std::string Value;
+ std::pair<StreamPurpose, std::string> Value;
if (StreamIdx == OldMSFDirectory)
- Value = "Old MSF Directory";
+ Value = std::make_pair(StreamPurpose::Other, "Old MSF Directory");
else if (StreamIdx == StreamPDB)
- Value = "PDB Stream";
+ Value = std::make_pair(StreamPurpose::Other, "PDB Stream");
else if (StreamIdx == StreamDBI)
- Value = "DBI Stream";
+ Value = std::make_pair(StreamPurpose::Other, "DBI Stream");
else if (StreamIdx == StreamTPI)
- Value = "TPI Stream";
+ Value = std::make_pair(StreamPurpose::Other, "TPI Stream");
else if (StreamIdx == StreamIPI)
- Value = "IPI Stream";
+ Value = std::make_pair(StreamPurpose::Other, "IPI Stream");
else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
- Value = "Global Symbol Hash";
+ Value = std::make_pair(StreamPurpose::Other, "Global Symbol Hash");
else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
- Value = "Public Symbol Hash";
+ Value = std::make_pair(StreamPurpose::Other, "Public Symbol Hash");
else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
- Value = "Public Symbol Records";
+ Value = std::make_pair(StreamPurpose::Other, "Public Symbol Records");
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
- Value = "TPI Hash";
+ Value = std::make_pair(StreamPurpose::Other, "TPI Hash");
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
- Value = "TPI Aux Hash";
+ Value = std::make_pair(StreamPurpose::Other, "TPI Aux Hash");
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
- Value = "IPI Hash";
+ Value = std::make_pair(StreamPurpose::Other, "IPI Hash");
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
- Value = "IPI Aux Hash";
+ Value = std::make_pair(StreamPurpose::Other, "IPI Aux Hash");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
- Value = "Exception Data";
+ Value = std::make_pair(StreamPurpose::Other, "Exception Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
- Value = "Fixup Data";
+ Value = std::make_pair(StreamPurpose::Other, "Fixup Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
- Value = "FPO Data";
+ Value = std::make_pair(StreamPurpose::Other, "FPO Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
- Value = "New FPO Data";
+ Value = std::make_pair(StreamPurpose::Other, "New FPO Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
- Value = "Omap From Source Data";
+ Value = std::make_pair(StreamPurpose::Other, "Omap From Source Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
- Value = "Omap To Source Data";
+ Value = std::make_pair(StreamPurpose::Other, "Omap To Source Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
- Value = "Pdata";
+ Value = std::make_pair(StreamPurpose::Other, "Pdata");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
- Value = "Section Header Data";
+ Value = std::make_pair(StreamPurpose::Other, "Section Header Data");
else if (Dbi &&
StreamIdx ==
Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
- Value = "Section Header Original Data";
+ Value =
+ std::make_pair(StreamPurpose::Other, "Section Header Original Data");
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
- Value = "Token Rid Data";
+ Value = std::make_pair(StreamPurpose::Other, "Token Rid Data");
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
- Value = "Xdata";
+ Value = std::make_pair(StreamPurpose::Other, "Xdata");
else {
auto ModIter = ModStreams.find(StreamIdx);
auto NSIter = NamedStreams.find(StreamIdx);
if (ModIter != ModStreams.end()) {
- Value = "Module \"";
- Value += ModIter->second.getModuleName();
- Value += "\"";
+ Value = std::make_pair(StreamPurpose::ModuleStream,
+ ModIter->second.getModuleName());
} else if (NSIter != NamedStreams.end()) {
- Value = "Named Stream \"";
- Value += NSIter->second;
- Value += "\"";
+ Value = std::make_pair(StreamPurpose::NamedStream, NSIter->second);
} else {
- Value = "???";
+ Value = std::make_pair(StreamPurpose::Other, "???");
}
}
Purposes[StreamIdx] = Value;
@@ -135,5 +135,18 @@ void discoverStreamPurposes(PDBFile &File,
if (!Info)
consumeError(Info.takeError());
}
-}
+
+void llvm::pdb::discoverStreamPurposes(PDBFile &File,
+ SmallVectorImpl<std::string> &Purposes) {
+ SmallVector<std::pair<StreamPurpose, std::string>, 24> SP;
+ discoverStreamPurposes(File, SP);
+ Purposes.reserve(SP.size());
+ for (const auto &P : SP) {
+ if (P.first == StreamPurpose::NamedStream)
+ Purposes.push_back(formatv("Named Stream \"{0}\"", P.second));
+ else if (P.first == StreamPurpose::ModuleStream)
+ Purposes.push_back(formatv("Module \"{0}\"", P.second));
+ else
+ Purposes.push_back(P.second);
+ }
}
diff --git a/tools/llvm-pdbutil/StreamUtil.h b/tools/llvm-pdbutil/StreamUtil.h
index b5c0beba44fed..f49c0a0eceb66 100644
--- a/tools/llvm-pdbutil/StreamUtil.h
+++ b/tools/llvm-pdbutil/StreamUtil.h
@@ -17,8 +17,13 @@
namespace llvm {
namespace pdb {
class PDBFile;
+enum class StreamPurpose { NamedStream, ModuleStream, Other };
+
void discoverStreamPurposes(PDBFile &File,
SmallVectorImpl<std::string> &Purposes);
+void discoverStreamPurposes(
+ PDBFile &File,
+ SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes);
}
}
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp
index ad11ad4980008..6aa08ff3cd872 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -284,9 +284,32 @@ cl::opt<bool> NoEnumDefs("no-enum-definitions",
}
namespace diff {
-cl::list<std::string> InputFilenames(cl::Positional,
- cl::desc("<first> <second>"),
- cl::OneOrMore, cl::sub(DiffSubcommand));
+cl::opt<bool> PrintValueColumns(
+ "values", cl::init(true),
+ cl::desc("Print one column for each PDB with the field value"),
+ cl::Optional, cl::sub(DiffSubcommand));
+cl::opt<bool>
+ PrintResultColumn("result", cl::init(false),
+ cl::desc("Print a column with the result status"),
+ cl::Optional, cl::sub(DiffSubcommand));
+
+cl::opt<std::string> LeftRoot(
+ "left-bin-root", cl::Optional,
+ cl::desc("Treats the specified path as the root of the tree containing "
+ "binaries referenced by the left PDB. The root is stripped from "
+ "embedded paths when doing equality comparisons."),
+ cl::sub(DiffSubcommand));
+cl::opt<std::string> RightRoot(
+ "right-bin-root", cl::Optional,
+ cl::desc("Treats the specified path as the root of the tree containing "
+ "binaries referenced by the right PDB. The root is stripped from "
+ "embedded paths when doing equality comparisons"),
+ cl::sub(DiffSubcommand));
+
+cl::opt<std::string> Left(cl::Positional, cl::desc("<left>"),
+ cl::sub(DiffSubcommand));
+cl::opt<std::string> Right(cl::Positional, cl::desc("<right>"),
+ cl::sub(DiffSubcommand));
}
cl::OptionCategory FileOptions("Module & File Options");
@@ -399,7 +422,7 @@ cl::opt<bool> DumpTypeExtras("type-extras",
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
cl::list<uint32_t> DumpTypeIndex(
- "type-index", cl::ZeroOrMore,
+ "type-index", cl::ZeroOrMore, cl::CommaSeparated,
cl::desc("only dump types with the specified hexadecimal type index"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
@@ -415,7 +438,7 @@ cl::opt<bool> DumpIdExtras("id-extras",
cl::desc("dump id hashes and index offsets"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
cl::list<uint32_t> DumpIdIndex(
- "id-index", cl::ZeroOrMore,
+ "id-index", cl::ZeroOrMore, cl::CommaSeparated,
cl::desc("only dump ids with the specified hexadecimal type index"),
cl::cat(TypeOptions), cl::sub(DumpSubcommand));
@@ -1079,6 +1102,11 @@ int main(int argc_, const char *argv_[]) {
if (opts::pdb2yaml::DumpModules)
opts::pdb2yaml::DbiStream = true;
}
+ if (opts::DiffSubcommand) {
+ if (!opts::diff::PrintResultColumn && !opts::diff::PrintValueColumns) {
+ llvm::errs() << "WARNING: No diff columns specified\n";
+ }
+ }
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
@@ -1137,11 +1165,7 @@ int main(int argc_, const char *argv_[]) {
std::for_each(opts::bytes::InputFilenames.begin(),
opts::bytes::InputFilenames.end(), dumpBytes);
} else if (opts::DiffSubcommand) {
- if (opts::diff::InputFilenames.size() != 2) {
- errs() << "diff subcommand expects exactly 2 arguments.\n";
- exit(1);
- }
- diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]);
+ diff(opts::diff::Left, opts::diff::Right);
} else if (opts::MergeSubcommand) {
if (opts::merge::InputFilenames.size() < 2) {
errs() << "merge subcommand requires at least 2 input files.\n";
diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h
index 9ee5866bbeffc..4e92e639a1278 100644
--- a/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -168,6 +168,13 @@ extern llvm::cl::opt<bool> DumpModuleFiles;
extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections;
extern llvm::cl::opt<bool> DumpModuleSyms;
} // namespace pdb2yaml
+
+namespace diff {
+extern llvm::cl::opt<bool> PrintValueColumns;
+extern llvm::cl::opt<bool> PrintResultColumn;
+extern llvm::cl::opt<std::string> LeftRoot;
+extern llvm::cl::opt<std::string> RightRoot;
+} // namespace diff
}
#endif
diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp
index e9bc2de82bdf0..eee242107dabe 100644
--- a/tools/llvm-profdata/llvm-profdata.cpp
+++ b/tools/llvm-profdata/llvm-profdata.cpp
@@ -159,14 +159,20 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) {
for (auto &I : *Reader) {
const StringRef FuncName = I.Name;
- if (Error E = WC->Writer.addRecord(std::move(I), Input.Weight)) {
+ bool Reported = false;
+ WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) {
+ if (Reported) {
+ consumeError(std::move(E));
+ return;
+ }
+ Reported = true;
// Only show hint the first time an error occurs.
instrprof_error IPE = InstrProfError::take(std::move(E));
std::unique_lock<std::mutex> ErrGuard{WC->ErrLock};
bool firstTime = WC->WriterErrorCodes.insert(IPE).second;
handleMergeWriterError(make_error<InstrProfError>(IPE), Input.Filename,
FuncName, firstTime);
- }
+ });
}
if (Reader->hasError())
WC->Err = Reader->getError();
@@ -174,8 +180,15 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) {
/// Merge the \p Src writer context into \p Dst.
static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) {
- if (Error E = Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer)))
+ bool Reported = false;
+ Dst->Writer.mergeRecordsFromWriter(std::move(Src->Writer), [&](Error E) {
+ if (Reported) {
+ consumeError(std::move(E));
+ return;
+ }
+ Reported = true;
Dst->Err = std::move(E);
+ });
}
static void mergeInstrProfile(const WeightedFileVector &Inputs,
@@ -499,8 +512,8 @@ static void showValueSitesStats(raw_fd_ostream &OS, uint32_t VK,
}
static int showInstrProfile(const std::string &Filename, bool ShowCounts,
- bool ShowIndirectCallTargets, bool ShowMemOPSizes,
- bool ShowDetailedSummary,
+ uint32_t TopN, bool ShowIndirectCallTargets,
+ bool ShowMemOPSizes, bool ShowDetailedSummary,
std::vector<uint32_t> DetailedSummaryCutoffs,
bool ShowAllFunctions,
const std::string &ShowFunction, bool TextFormat,
@@ -519,6 +532,17 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
size_t ShownFunctions = 0;
int NumVPKind = IPVK_Last - IPVK_First + 1;
std::vector<ValueSitesStats> VPStats(NumVPKind);
+
+ auto MinCmp = [](const std::pair<std::string, uint64_t> &v1,
+ const std::pair<std::string, uint64_t> &v2) {
+ return v1.second > v2.second;
+ };
+
+ std::priority_queue<std::pair<std::string, uint64_t>,
+ std::vector<std::pair<std::string, uint64_t>>,
+ decltype(MinCmp)>
+ HottestFuncs(MinCmp);
+
for (const auto &Func : *Reader) {
bool Show =
ShowAllFunctions || (!ShowFunction.empty() &&
@@ -528,13 +552,28 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
if (doTextFormatDump) {
InstrProfSymtab &Symtab = Reader->getSymtab();
- InstrProfWriter::writeRecordInText(Func, Symtab, OS);
+ InstrProfWriter::writeRecordInText(Func.Name, Func.Hash, Func, Symtab,
+ OS);
continue;
}
assert(Func.Counts.size() > 0 && "function missing entry counter");
Builder.addRecord(Func);
+ if (TopN) {
+ uint64_t FuncMax = 0;
+ for (size_t I = 0, E = Func.Counts.size(); I < E; ++I)
+ FuncMax = std::max(FuncMax, Func.Counts[I]);
+
+ if (HottestFuncs.size() == TopN) {
+ if (HottestFuncs.top().second < FuncMax) {
+ HottestFuncs.pop();
+ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
+ }
+ } else
+ HottestFuncs.emplace(std::make_pair(std::string(Func.Name), FuncMax));
+ }
+
if (Show) {
if (!ShownFunctions)
@@ -592,6 +631,18 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts,
OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n";
OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n";
+ if (TopN) {
+ std::vector<std::pair<std::string, uint64_t>> SortedHottestFuncs;
+ while (!HottestFuncs.empty()) {
+ SortedHottestFuncs.emplace_back(HottestFuncs.top());
+ HottestFuncs.pop();
+ }
+ OS << "Top " << TopN
+ << " functions with the largest internal block counts: \n";
+ for (auto &hotfunc : llvm::reverse(SortedHottestFuncs))
+ OS << " " << hotfunc.first << ", max count = " << hotfunc.second << "\n";
+ }
+
if (ShownFunctions && ShowIndirectCallTargets) {
OS << "Statistics for indirect call sites profile:\n";
showValueSitesStats(OS, IPVK_IndirectCallTarget,
@@ -675,6 +726,9 @@ static int show_main(int argc, const char *argv[]) {
cl::desc("Profile kind:"), cl::init(instr),
cl::values(clEnumVal(instr, "Instrumentation profile (default)"),
clEnumVal(sample, "Sample profile")));
+ cl::opt<uint32_t> TopNFunctions(
+ "topn", cl::init(0),
+ cl::desc("Show the list of functions with the largest internal counts"));
cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n");
@@ -692,10 +746,10 @@ static int show_main(int argc, const char *argv[]) {
std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
DetailedSummaryCutoffs.end());
if (ProfileKind == instr)
- return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets,
- ShowMemOPSizes, ShowDetailedSummary,
- DetailedSummaryCutoffs, ShowAllFunctions,
- ShowFunction, TextFormat, OS);
+ return showInstrProfile(Filename, ShowCounts, TopNFunctions,
+ ShowIndirectCallTargets, ShowMemOPSizes,
+ ShowDetailedSummary, DetailedSummaryCutoffs,
+ ShowAllFunctions, ShowFunction, TextFormat, OS);
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowFunction, OS);
diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp
index e5ff3e4186dec..9fb3267e2f9d3 100644
--- a/tools/llvm-readobj/COFFDumper.cpp
+++ b/tools/llvm-readobj/COFFDumper.cpp
@@ -1637,7 +1637,11 @@ static StringRef getBaseRelocTypeName(uint8_t Type) {
case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ";
case COFF::IMAGE_REL_BASED_ARM_MOV32T: return "ARM_MOV32(T)";
case COFF::IMAGE_REL_BASED_DIR64: return "DIR64";
- default: return "unknown (" + llvm::utostr(Type) + ")";
+ default: {
+ static std::string Result;
+ Result = "unknown (" + llvm::utostr(Type) + ")";
+ return Result;
+ }
}
}
diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp
index 14603f8a2b094..266226d59ee87 100644
--- a/tools/llvm-readobj/WasmDumper.cpp
+++ b/tools/llvm-readobj/WasmDumper.cpp
@@ -153,6 +153,12 @@ void WasmDumper::printSections() {
switch (WasmSec.Type) {
case wasm::WASM_SEC_CUSTOM:
W.printString("Name", WasmSec.Name);
+ if (WasmSec.Name == "linking") {
+ const wasm::WasmLinkingData &LinkingData = Obj->linkingData();
+ W.printNumber("DataSize", LinkingData.DataSize);
+ if (LinkingData.DataAlignment)
+ W.printNumber("DataAlignment", LinkingData.DataAlignment);
+ }
break;
case wasm::WASM_SEC_MEMORY:
ListScope Group(W, "Memories");
diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt
index 3ebede00cc434..907345a94023b 100644
--- a/tools/llvm-shlib/CMakeLists.txt
+++ b/tools/llvm-shlib/CMakeLists.txt
@@ -37,7 +37,7 @@ endif()
add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES})
list(REMOVE_DUPLICATES LIB_NAMES)
-if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")) # FIXME: It should be "GNU ld for elf"
+if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly")) # FIXME: It should be "GNU ld for elf"
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in
${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map)
diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp
index 3cf8b37bc2e29..3945da7020b0b 100644
--- a/tools/llvm-stress/llvm-stress.cpp
+++ b/tools/llvm-stress/llvm-stress.cpp
@@ -385,10 +385,10 @@ struct ConstModifier: public Modifier {
if (Ty->isVectorTy()) {
switch (getRandom() % 2) {
- case 0: if (Ty->getScalarType()->isIntegerTy())
+ case 0: if (Ty->isIntOrIntVectorTy())
return PT->push_back(ConstantVector::getAllOnesValue(Ty));
break;
- case 1: if (Ty->getScalarType()->isIntegerTy())
+ case 1: if (Ty->isIntOrIntVectorTy())
return PT->push_back(ConstantVector::getNullValue(Ty));
}
}
@@ -531,8 +531,7 @@ struct CastModifier: public Modifier {
}
// Both types are integers:
- if (VTy->getScalarType()->isIntegerTy() &&
- DestTy->getScalarType()->isIntegerTy()) {
+ if (VTy->isIntOrIntVectorTy() && DestTy->isIntOrIntVectorTy()) {
if (VSize > DestSize) {
return PT->push_back(
new TruncInst(V, DestTy, "Tr", BB->getTerminator()));
@@ -546,8 +545,7 @@ struct CastModifier: public Modifier {
}
// Fp to int.
- if (VTy->getScalarType()->isFloatingPointTy() &&
- DestTy->getScalarType()->isIntegerTy()) {
+ if (VTy->isFPOrFPVectorTy() && DestTy->isIntOrIntVectorTy()) {
if (getRandom() & 1)
return PT->push_back(
new FPToSIInst(V, DestTy, "FC", BB->getTerminator()));
@@ -555,8 +553,7 @@ struct CastModifier: public Modifier {
}
// Int to fp.
- if (VTy->getScalarType()->isIntegerTy() &&
- DestTy->getScalarType()->isFloatingPointTy()) {
+ if (VTy->isIntOrIntVectorTy() && DestTy->isFPOrFPVectorTy()) {
if (getRandom() & 1)
return PT->push_back(
new SIToFPInst(V, DestTy, "FC", BB->getTerminator()));
@@ -565,8 +562,7 @@ struct CastModifier: public Modifier {
}
// Both floats.
- if (VTy->getScalarType()->isFloatingPointTy() &&
- DestTy->getScalarType()->isFloatingPointTy()) {
+ if (VTy->isFPOrFPVectorTy() && DestTy->isFPOrFPVectorTy()) {
if (VSize > DestSize) {
return PT->push_back(
new FPTruncInst(V, DestTy, "Tr", BB->getTerminator()));
diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp
index 1df6afcf3c46d..a1da4b6a748c7 100644
--- a/tools/obj2yaml/wasm2yaml.cpp
+++ b/tools/obj2yaml/wasm2yaml.cpp
@@ -236,9 +236,10 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() {
auto DataSec = make_unique<WasmYAML::DataSection>();
for (auto &Segment : Obj.dataSegments()) {
WasmYAML::DataSegment Seg;
- Seg.Index = Segment.Index;
- Seg.Offset = Segment.Offset;
- Seg.Content = yaml::BinaryRef(Segment.Content);
+ Seg.SectionOffset = Segment.SectionOffset;
+ Seg.MemoryIndex = Segment.Data.MemoryIndex;
+ Seg.Offset = Segment.Data.Offset;
+ Seg.Content = yaml::BinaryRef(Segment.Data.Content);
DataSec->Segments.push_back(Seg);
}
S = std::move(DataSec);
diff --git a/tools/opt-viewer/CMakeLists.txt b/tools/opt-viewer/CMakeLists.txt
new file mode 100644
index 0000000000000..19b6069330820
--- /dev/null
+++ b/tools/opt-viewer/CMakeLists.txt
@@ -0,0 +1,13 @@
+set (files
+ "opt-diff.py"
+ "opt-stats.py"
+ "opt-viewer.py"
+ "optpmap.py"
+ "optrecord.py"
+ "style.css")
+
+foreach (file ${files})
+ install(PROGRAMS ${file}
+ DESTINATION share/opt-viewer
+ COMPONENT opt-viewer)
+endforeach (file)
diff --git a/tools/opt-viewer/opt-diff.py b/tools/opt-viewer/opt-diff.py
new file mode 100755
index 0000000000000..9e921f8488d36
--- /dev/null
+++ b/tools/opt-viewer/opt-diff.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+desc = '''Generate the difference of two YAML files into a new YAML file (works on
+pair of directories too). A new attribute 'Added' is set to True or False
+depending whether the entry is added or removed from the first input to the
+next.
+
+The tools requires PyYAML.'''
+
+import yaml
+# Try to use the C parser.
+try:
+ from yaml import CLoader as Loader
+except ImportError:
+ from yaml import Loader
+
+import optrecord
+import argparse
+from collections import defaultdict
+from multiprocessing import cpu_count, Pool
+import os, os.path
+import fnmatch
+
+def find_files(dir_or_file):
+ if os.path.isfile(dir_or_file):
+ return [dir_or_file]
+
+ all = []
+ for dir, subdirs, files in os.walk(dir_or_file):
+ for file in files:
+ if fnmatch.fnmatch(file, "*.opt.yaml"):
+ all.append( os.path.join(dir, file))
+ return all
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('yaml_dir_or_file_1')
+ parser.add_argument('yaml_dir_or_file_2')
+ parser.add_argument(
+ '--jobs',
+ '-j',
+ default=cpu_count(),
+ type=int,
+ help='Max job count (defaults to %(default)s, the current CPU count)')
+ parser.add_argument(
+ '--no-progress-indicator',
+ '-n',
+ action='store_true',
+ default=False,
+ help='Do not display any indicator of how many YAML files were read.')
+ parser.add_argument('--output', '-o', default='diff.opt.yaml')
+ args = parser.parse_args()
+
+ files1 = find_files(args.yaml_dir_or_file_1)
+ files2 = find_files(args.yaml_dir_or_file_2)
+
+ print_progress = not args.no_progress_indicator
+ all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress)
+ all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress)
+
+ added = set(all_remarks2.values()) - set(all_remarks1.values())
+ removed = set(all_remarks1.values()) - set(all_remarks2.values())
+
+ for r in added:
+ r.Added = True
+ for r in removed:
+ r.Added = False
+ with open(args.output, 'w') as stream:
+ yaml.dump_all(added | removed, stream)
diff --git a/tools/opt-viewer/opt-stats.py b/tools/opt-viewer/opt-stats.py
new file mode 100755
index 0000000000000..a7e598fdfd026
--- /dev/null
+++ b/tools/opt-viewer/opt-stats.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+desc = '''Generate statistics about optimization records from the YAML files
+generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
+
+The tools requires PyYAML and Pygments Python packages.'''
+
+import optrecord
+import argparse
+import operator
+from collections import defaultdict
+from multiprocessing import cpu_count, Pool
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('yaml_files', nargs='+')
+ parser.add_argument(
+ '--jobs',
+ '-j',
+ default=cpu_count(),
+ type=int,
+ help='Max job count (defaults to %(default)s, the current CPU count)')
+ parser.add_argument(
+ '--no-progress-indicator',
+ '-n',
+ action='store_true',
+ default=False,
+ help='Do not display any indicator of how many YAML files were read.')
+ args = parser.parse_args()
+
+ print_progress = not args.no_progress_indicator
+ all_remarks, file_remarks, _ = optrecord.gather_results(
+ args.yaml_files, args.jobs, print_progress)
+ if print_progress:
+ print('\n')
+
+ bypass = defaultdict(int)
+ byname = defaultdict(int)
+ for r in optrecord.itervalues(all_remarks):
+ bypass[r.Pass] += 1
+ byname[r.Pass + "/" + r.Name] += 1
+
+ total = len(all_remarks)
+ print("{:24s} {:10d}\n".format("Total number of remarks", total))
+
+ print("Top 10 remarks by pass:")
+ for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1),
+ reverse=True)[:10]:
+ print(" {:30s} {:2.0f}%". format(passname, count * 100. / total))
+
+ print("\nTop 10 remarks:")
+ for (name, count) in sorted(byname.items(), key=operator.itemgetter(1),
+ reverse=True)[:10]:
+ print(" {:30s} {:2.0f}%". format(name, count * 100. / total))
diff --git a/tools/opt-viewer/opt-viewer.py b/tools/opt-viewer/opt-viewer.py
new file mode 100755
index 0000000000000..e6dd6a0286fe7
--- /dev/null
+++ b/tools/opt-viewer/opt-viewer.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+import argparse
+import cgi
+import errno
+import functools
+from multiprocessing import cpu_count
+import os.path
+import re
+import shutil
+
+from pygments import highlight
+from pygments.lexers.c_cpp import CppLexer
+from pygments.formatters import HtmlFormatter
+
+import optpmap
+import optrecord
+
+
+desc = '''Generate HTML output to visualize optimization records from the YAML files
+generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
+
+The tools requires PyYAML and Pygments Python packages.'''
+
+
+# This allows passing the global context to the child processes.
+class Context:
+ def __init__(self, caller_loc = dict()):
+ # Map function names to their source location for function where inlining happened
+ self.caller_loc = caller_loc
+
+context = Context()
+
+class SourceFileRenderer:
+ def __init__(self, source_dir, output_dir, filename):
+ existing_filename = None
+ if os.path.exists(filename):
+ existing_filename = filename
+ else:
+ fn = os.path.join(source_dir, filename)
+ if os.path.exists(fn):
+ existing_filename = fn
+
+ self.stream = open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w')
+ if existing_filename:
+ self.source_stream = open(existing_filename)
+ else:
+ self.source_stream = None
+ print('''
+<html>
+<h1>Unable to locate file {}</h1>
+</html>
+ '''.format(filename), file=self.stream)
+
+ self.html_formatter = HtmlFormatter(encoding='utf-8')
+ self.cpp_lexer = CppLexer(stripnl=False)
+
+ def render_source_lines(self, stream, line_remarks):
+ file_text = stream.read()
+ html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter)
+
+ # Take off the header and footer, these must be
+ # reapplied line-wise, within the page structure
+ html_highlighted = html_highlighted.replace('<div class="highlight"><pre>', '')
+ html_highlighted = html_highlighted.replace('</pre></div>', '')
+
+ for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1):
+ print('''
+<tr>
+<td><a name=\"L{linenum}\">{linenum}</a></td>
+<td></td>
+<td></td>
+<td><div class="highlight"><pre>{html_line}</pre></div></td>
+</tr>'''.format(**locals()), file=self.stream)
+
+ for remark in line_remarks.get(linenum, []):
+ self.render_inline_remarks(remark, html_line)
+
+ def render_inline_remarks(self, r, line):
+ inlining_context = r.DemangledFunctionName
+ dl = context.caller_loc.get(r.Function)
+ if dl:
+ link = optrecord.make_link(dl['File'], dl['Line'] - 2)
+ inlining_context = "<a href={link}>{r.DemangledFunctionName}</a>".format(**locals())
+
+ # Column is the number of characters *including* tabs, keep those and
+ # replace everything else with spaces.
+ indent = line[:max(r.Column, 1) - 1]
+ indent = re.sub('\S', ' ', indent)
+
+ print('''
+<tr>
+<td></td>
+<td>{r.RelativeHotness}</td>
+<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
+<td><pre style="display:inline">{indent}</pre><span class=\"column-entry-yellow\"> {r.message}&nbsp;</span></td>
+<td class=\"column-entry-yellow\">{inlining_context}</td>
+</tr>'''.format(**locals()), file=self.stream)
+
+ def render(self, line_remarks):
+ if not self.source_stream:
+ return
+
+ print('''
+<html>
+<head>
+<link rel='stylesheet' type='text/css' href='style.css'>
+</head>
+<body>
+<div class="centered">
+<table>
+<tr>
+<td>Line</td>
+<td>Hotness</td>
+<td>Optimization</td>
+<td>Source</td>
+<td>Inline Context</td>
+</tr>''', file=self.stream)
+ self.render_source_lines(self.source_stream, line_remarks)
+
+ print('''
+</table>
+</body>
+</html>''', file=self.stream)
+
+
+class IndexRenderer:
+ def __init__(self, output_dir):
+ self.stream = open(os.path.join(output_dir, 'index.html'), 'w')
+
+ def render_entry(self, r, odd):
+ escaped_name = cgi.escape(r.DemangledFunctionName)
+ print('''
+<tr>
+<td class=\"column-entry-{odd}\"><a href={r.Link}>{r.DebugLocString}</a></td>
+<td class=\"column-entry-{odd}\">{r.RelativeHotness}</td>
+<td class=\"column-entry-{odd}\">{escaped_name}</td>
+<td class=\"column-entry-{r.color}\">{r.PassWithDiffPrefix}</td>
+</tr>'''.format(**locals()), file=self.stream)
+
+ def render(self, all_remarks):
+ print('''
+<html>
+<head>
+<link rel='stylesheet' type='text/css' href='style.css'>
+</head>
+<body>
+<div class="centered">
+<table>
+<tr>
+<td>Source Location</td>
+<td>Hotness</td>
+<td>Function</td>
+<td>Pass</td>
+</tr>''', file=self.stream)
+ for i, remark in enumerate(all_remarks):
+ self.render_entry(remark, i % 2)
+ print('''
+</table>
+</body>
+</html>''', file=self.stream)
+
+
+def _render_file(source_dir, output_dir, ctx, entry):
+ global context
+ context = ctx
+ filename, remarks = entry
+ SourceFileRenderer(source_dir, output_dir, filename).render(remarks)
+
+
+def map_remarks(all_remarks):
+ # Set up a map between function names and their source location for
+ # function where inlining happened
+ for remark in optrecord.itervalues(all_remarks):
+ if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined":
+ for arg in remark.Args:
+ caller = arg.get('Caller')
+ if caller:
+ try:
+ context.caller_loc[caller] = arg['DebugLoc']
+ except KeyError:
+ pass
+
+
+def generate_report(all_remarks,
+ file_remarks,
+ source_dir,
+ output_dir,
+ should_display_hotness,
+ num_jobs,
+ should_print_progress):
+ try:
+ os.makedirs(output_dir)
+ except OSError as e:
+ if e.errno == errno.EEXIST and os.path.isdir(output_dir):
+ pass
+ else:
+ raise
+
+ _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context)
+ if should_print_progress:
+ print('Rendering HTML files...')
+ optpmap.pmap(_render_file_bound,
+ file_remarks.items(),
+ num_jobs,
+ should_print_progress)
+
+ if should_display_hotness:
+ sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True)
+ else:
+ sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function))
+ IndexRenderer(args.output_dir).render(sorted_remarks)
+
+ shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ "style.css"), output_dir)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=desc)
+ parser.add_argument('yaml_files', nargs='+')
+ parser.add_argument(
+ '--output-dir',
+ '-o',
+ default='html',
+ help='Path to a directory where generated HTML files will be output. '
+ 'If the directory does not already exist, it will be created. '
+ '"%(default)s" by default.')
+ parser.add_argument(
+ '--jobs',
+ '-j',
+ default=cpu_count(),
+ type=int,
+ help='Max job count (defaults to %(default)s, the current CPU count)')
+ parser.add_argument(
+ '-source-dir',
+ '-s',
+ default='',
+ help='set source directory')
+ parser.add_argument(
+ '--no-progress-indicator',
+ '-n',
+ action='store_true',
+ default=False,
+ help='Do not display any indicator of how many YAML files were read '
+ 'or rendered into HTML.')
+ args = parser.parse_args()
+
+ print_progress = not args.no_progress_indicator
+ all_remarks, file_remarks, should_display_hotness = \
+ optrecord.gather_results(args.yaml_files, args.jobs, print_progress)
+
+ map_remarks(all_remarks)
+
+ generate_report(all_remarks,
+ file_remarks,
+ args.source_dir,
+ args.output_dir,
+ should_display_hotness,
+ args.jobs,
+ print_progress)
diff --git a/tools/opt-viewer/optpmap.py b/tools/opt-viewer/optpmap.py
new file mode 100644
index 0000000000000..01e848e03976d
--- /dev/null
+++ b/tools/opt-viewer/optpmap.py
@@ -0,0 +1,53 @@
+import sys
+import multiprocessing
+
+
+_current = None
+_total = None
+
+
+def _init(current, total):
+ global _current
+ global _total
+ _current = current
+ _total = total
+
+
+def _wrapped_func(func_and_args):
+ func, argument, should_print_progress = func_and_args
+
+ if should_print_progress:
+ with _current.get_lock():
+ _current.value += 1
+ sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value))
+
+ return func(argument)
+
+
+def pmap(func, iterable, processes, should_print_progress, *args, **kwargs):
+ """
+ A parallel map function that reports on its progress.
+
+ Applies `func` to every item of `iterable` and return a list of the
+ results. If `processes` is greater than one, a process pool is used to run
+ the functions in parallel. `should_print_progress` is a boolean value that
+ indicates whether a string 'N of M' should be printed to indicate how many
+ of the functions have finished being run.
+ """
+ global _current
+ global _total
+ _current = multiprocessing.Value('i', 0)
+ _total = multiprocessing.Value('i', len(iterable))
+
+ func_and_args = [(func, arg, should_print_progress,) for arg in iterable]
+ if processes <= 1:
+ result = map(_wrapped_func, func_and_args, *args, **kwargs)
+ else:
+ pool = multiprocessing.Pool(initializer=_init,
+ initargs=(_current, _total,),
+ processes=processes)
+ result = pool.map(_wrapped_func, func_and_args, *args, **kwargs)
+
+ if should_print_progress:
+ sys.stdout.write('\r')
+ return result
diff --git a/tools/opt-viewer/optrecord.py b/tools/opt-viewer/optrecord.py
new file mode 100644
index 0000000000000..61ed9626cffad
--- /dev/null
+++ b/tools/opt-viewer/optrecord.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+
+import yaml
+# Try to use the C parser.
+try:
+ from yaml import CLoader as Loader
+except ImportError:
+ print("For faster parsing, you may want to install libYAML for PyYAML")
+ from yaml import Loader
+
+import cgi
+from collections import defaultdict
+import functools
+from multiprocessing import Lock
+import subprocess
+
+import optpmap
+
+
+p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+p_lock = Lock()
+
+
+try:
+ dict.iteritems
+except AttributeError:
+ # Python 3
+ def itervalues(d):
+ return iter(d.values())
+ def iteritems(d):
+ return iter(d.items())
+else:
+ # Python 2
+ def itervalues(d):
+ return d.itervalues()
+ def iteritems(d):
+ return d.iteritems()
+
+
+def demangle(name):
+ with p_lock:
+ p.stdin.write((name + '\n').encode('utf-8'))
+ p.stdin.flush()
+ return p.stdout.readline().rstrip().decode('utf-8')
+
+
+def html_file_name(filename):
+ return filename.replace('/', '_') + ".html"
+
+
+def make_link(File, Line):
+ return "\"{}#L{}\"".format(html_file_name(File), Line)
+
+
+class Remark(yaml.YAMLObject):
+ # Work-around for http://pyyaml.org/ticket/154.
+ yaml_loader = Loader
+
+ def initmissing(self):
+ if not hasattr(self, 'Hotness'):
+ self.Hotness = 0
+ if not hasattr(self, 'Args'):
+ self.Args = []
+
+ @property
+ def File(self):
+ return self.DebugLoc['File']
+
+ @property
+ def Line(self):
+ return int(self.DebugLoc['Line'])
+
+ @property
+ def Column(self):
+ return self.DebugLoc['Column']
+
+ @property
+ def DebugLocString(self):
+ return "{}:{}:{}".format(self.File, self.Line, self.Column)
+
+ @property
+ def DemangledFunctionName(self):
+ return demangle(self.Function)
+
+ @property
+ def Link(self):
+ return make_link(self.File, self.Line)
+
+ def getArgString(self, mapping):
+ mapping = mapping.copy()
+ dl = mapping.get('DebugLoc')
+ if dl:
+ del mapping['DebugLoc']
+
+ assert(len(mapping) == 1)
+ (key, value) = mapping.items()[0]
+
+ if key == 'Caller' or key == 'Callee':
+ value = cgi.escape(demangle(value))
+
+ if dl and key != 'Caller':
+ return "<a href={}>{}</a>".format(
+ make_link(dl['File'], dl['Line']), value)
+ else:
+ return value
+
+ def getDiffPrefix(self):
+ if hasattr(self, 'Added'):
+ if self.Added:
+ return '+'
+ else:
+ return '-'
+ return ''
+
+ @property
+ def PassWithDiffPrefix(self):
+ return self.getDiffPrefix() + self.Pass
+
+ @property
+ def message(self):
+ # Args is a list of mappings (dictionaries)
+ values = [self.getArgString(mapping) for mapping in self.Args]
+ return "".join(values)
+
+ @property
+ def RelativeHotness(self):
+ if self.max_hotness:
+ return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness)))
+ else:
+ return ''
+
+ @property
+ def key(self):
+ k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function)
+ for arg in self.Args:
+ for (key, value) in iteritems(arg):
+ if type(value) is dict:
+ value = tuple(value.items())
+ k += (key, value)
+ return k
+
+ def __hash__(self):
+ return hash(self.key)
+
+ def __eq__(self, other):
+ return self.key == other.key
+
+ def __repr__(self):
+ return str(self.key)
+
+
+class Analysis(Remark):
+ yaml_tag = '!Analysis'
+
+ @property
+ def color(self):
+ return "white"
+
+
+class AnalysisFPCommute(Analysis):
+ yaml_tag = '!AnalysisFPCommute'
+
+
+class AnalysisAliasing(Analysis):
+ yaml_tag = '!AnalysisAliasing'
+
+
+class Passed(Remark):
+ yaml_tag = '!Passed'
+
+ @property
+ def color(self):
+ return "green"
+
+
+class Missed(Remark):
+ yaml_tag = '!Missed'
+
+ @property
+ def color(self):
+ return "red"
+
+
+def get_remarks(input_file):
+ max_hotness = 0
+ all_remarks = dict()
+ file_remarks = defaultdict(functools.partial(defaultdict, list))
+
+ with open(input_file) as f:
+ docs = yaml.load_all(f, Loader=Loader)
+ for remark in docs:
+ remark.initmissing()
+ # Avoid remarks withoug debug location or if they are duplicated
+ if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks:
+ continue
+ all_remarks[remark.key] = remark
+
+ file_remarks[remark.File][remark.Line].append(remark)
+
+ # If we're reading a back a diff yaml file, max_hotness is already
+ # captured which may actually be less than the max hotness found
+ # in the file.
+ if hasattr(remark, 'max_hotness'):
+ max_hotness = remark.max_hotness
+ max_hotness = max(max_hotness, remark.Hotness)
+
+ return max_hotness, all_remarks, file_remarks
+
+
+def gather_results(filenames, num_jobs, should_print_progress):
+ if should_print_progress:
+ print('Reading YAML files...')
+ remarks = optpmap.pmap(
+ get_remarks, filenames, num_jobs, should_print_progress)
+ max_hotness = max(entry[0] for entry in remarks)
+
+ def merge_file_remarks(file_remarks_job, all_remarks, merged):
+ for filename, d in iteritems(file_remarks_job):
+ for line, remarks in iteritems(d):
+ for remark in remarks:
+ # Bring max_hotness into the remarks so that
+ # RelativeHotness does not depend on an external global.
+ remark.max_hotness = max_hotness
+ if remark.key not in all_remarks:
+ merged[filename][line].append(remark)
+
+ all_remarks = dict()
+ file_remarks = defaultdict(functools.partial(defaultdict, list))
+ for _, all_remarks_job, file_remarks_job in remarks:
+ merge_file_remarks(file_remarks_job, all_remarks, file_remarks)
+ all_remarks.update(all_remarks_job)
+
+ return all_remarks, file_remarks, max_hotness != 0
diff --git a/tools/opt-viewer/style.css b/tools/opt-viewer/style.css
new file mode 100644
index 0000000000000..595c3e46847dd
--- /dev/null
+++ b/tools/opt-viewer/style.css
@@ -0,0 +1,198 @@
+.red {
+ background-color: #ffd0d0;
+}
+.cyan {
+ background-color: cyan;
+}
+body {
+ font-family: -apple-system, sans-serif;
+}
+pre {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+}
+.source-name-title {
+ padding: 5px 10px;
+ border-bottom: 1px solid #dbdbdb;
+ background-color: #eee;
+ line-height: 35px;
+}
+.centered {
+ display: table;
+ margin-left: left;
+ margin-right: auto;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+.expansion-view {
+ background-color: rgba(0, 0, 0, 0);
+ margin-left: 0px;
+ margin-top: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ border: 1px solid #dbdbdb;
+ border-radius: 3px;
+}
+table {
+ border-collapse: collapse;
+}
+.light-row {
+ background: #ffffff;
+ border: 1px solid #dbdbdb;
+}
+.column-entry {
+ text-align: right;
+}
+.column-entry-left {
+ text-align: left;
+}
+.column-entry-white {
+ text-align: right;
+ background-color: #ffffff;
+}
+.column-entry-red {
+ text-align: right;
+ background-color: #ffd0d0;
+}
+.column-entry-green {
+ text-align: right;
+ background-color: #d0ffd0;
+}
+.column-entry-yellow {
+ text-align: left;
+ background-color: #ffe1a6;
+}
+.column-entry-0 {
+ background-color: #ffffff;
+}
+.column-entry-1 {
+ background-color: #eeeeee;
+}
+.line-number {
+ text-align: right;
+ color: #aaa;
+}
+.covered-line {
+ text-align: right;
+ color: #0080ff;
+}
+.uncovered-line {
+ text-align: right;
+ color: #ff3300;
+}
+.tooltip {
+ position: relative;
+ display: inline;
+ background-color: #b3e6ff;
+ text-decoration: none;
+}
+.tooltip span.tooltip-content {
+ position: absolute;
+ width: 100px;
+ margin-left: -50px;
+ color: #FFFFFF;
+ background: #000000;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 6px;
+}
+.tooltip span.tooltip-content:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0; height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+}
+:hover.tooltip span.tooltip-content {
+ visibility: visible;
+ opacity: 0.8;
+ bottom: 30px;
+ left: 50%;
+ z-index: 999;
+}
+th, td {
+ vertical-align: top;
+ padding: 2px 5px;
+ border-collapse: collapse;
+ border-right: solid 1px #eee;
+ border-left: solid 1px #eee;
+}
+td:first-child {
+ border-left: none;
+}
+td:last-child {
+ border-right: none;
+}
+
+/* Generated with pygmentize -S colorful -f html >> style.css */
+
+.hll { background-color: #ffffcc }
+.c { color: #888888 } /* Comment */
+.err { color: #FF0000; background-color: #FFAAAA } /* Error */
+.k { color: #008800; font-weight: bold } /* Keyword */
+.o { color: #333333 } /* Operator */
+.ch { color: #888888 } /* Comment.Hashbang */
+.cm { color: #888888 } /* Comment.Multiline */
+.cp { color: #557799 } /* Comment.Preproc */
+.cpf { color: #888888 } /* Comment.PreprocFile */
+.c1 { color: #888888 } /* Comment.Single */
+.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #888888 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0044DD } /* Generic.Traceback */
+.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
+.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
+.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
+.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
+.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #333399; font-weight: bold } /* Keyword.Type */
+.m { color: #6600EE; font-weight: bold } /* Literal.Number */
+.s { background-color: #fff0f0 } /* Literal.String */
+.na { color: #0000CC } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #BB0066; font-weight: bold } /* Name.Class */
+.no { color: #003366; font-weight: bold } /* Name.Constant */
+.nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.ni { color: #880000; font-weight: bold } /* Name.Entity */
+.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
+.nf { color: #0066BB; font-weight: bold } /* Name.Function */
+.nl { color: #997700; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #007700 } /* Name.Tag */
+.nv { color: #996633 } /* Name.Variable */
+.ow { color: #000000; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
+.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
+.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
+.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
+.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
+.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
+.sc { color: #0044DD } /* Literal.String.Char */
+.sd { color: #DD4422 } /* Literal.String.Doc */
+.s2 { background-color: #fff0f0 } /* Literal.String.Double */
+.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
+.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
+.si { background-color: #eeeeee } /* Literal.String.Interpol */
+.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
+.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
+.s1 { background-color: #fff0f0 } /* Literal.String.Single */
+.ss { color: #AA6600 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #336699 } /* Name.Variable.Class */
+.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
+.vi { color: #3333BB } /* Name.Variable.Instance */
+.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp
index 58e9caeff0fb1..94242d795aaeb 100644
--- a/tools/opt/NewPMDriver.cpp
+++ b/tools/opt/NewPMDriver.cpp
@@ -48,6 +48,102 @@ static cl::opt<std::string>
"pipeline for handling managed aliasing queries"),
cl::Hidden);
+/// {{@ These options accept textual pipeline descriptions which will be
+/// inserted into default pipelines at the respective extension points
+static cl::opt<std::string> PeepholeEPPipeline(
+ "passes-ep-peephole",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the Peephole extension points into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> LateLoopOptimizationsEPPipeline(
+ "passes-ep-late-loop-optimizations",
+ cl::desc(
+ "A textual description of the loop pass pipeline inserted at "
+ "the LateLoopOptimizations extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> LoopOptimizerEndEPPipeline(
+ "passes-ep-loop-optimizer-end",
+ cl::desc("A textual description of the loop pass pipeline inserted at "
+ "the LoopOptimizerEnd extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> ScalarOptimizerLateEPPipeline(
+ "passes-ep-scalar-optimizer-late",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the ScalarOptimizerLate extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> CGSCCOptimizerLateEPPipeline(
+ "passes-ep-cgscc-optimizer-late",
+ cl::desc("A textual description of the cgscc pass pipeline inserted at "
+ "the CGSCCOptimizerLate extension point into default pipelines"),
+ cl::Hidden);
+static cl::opt<std::string> VectorizerStartEPPipeline(
+ "passes-ep-vectorizer-start",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the VectorizerStart extension point into default pipelines"),
+ cl::Hidden);
+/// @}}
+
+template <typename PassManagerT>
+bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) {
+ if (PipelineText.empty())
+ return false;
+
+ // Verify the pipeline is parseable:
+ PassManagerT PM;
+ if (PB.parsePassPipeline(PM, PipelineText))
+ return true;
+
+ errs() << "Could not parse pipeline '" << PipelineText
+ << "'. I'm going to igore it.\n";
+ return false;
+}
+
+/// If one of the EPPipeline command line options was given, register callbacks
+/// for parsing and inserting the given pipeline
+static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
+ bool DebugLogging) {
+ if (tryParsePipelineText<FunctionPassManager>(PB, PeepholeEPPipeline))
+ PB.registerPeepholeEPCallback([&PB, VerifyEachPass, DebugLogging](
+ FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass,
+ DebugLogging);
+ });
+ if (tryParsePipelineText<LoopPassManager>(PB,
+ LateLoopOptimizationsEPPipeline))
+ PB.registerLateLoopOptimizationsEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline,
+ VerifyEachPass, DebugLogging);
+ });
+ if (tryParsePipelineText<LoopPassManager>(PB, LoopOptimizerEndEPPipeline))
+ PB.registerLoopOptimizerEndEPCallback([&PB, VerifyEachPass, DebugLogging](
+ LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, VerifyEachPass,
+ DebugLogging);
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB,
+ ScalarOptimizerLateEPPipeline))
+ PB.registerScalarOptimizerLateEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](
+ FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline,
+ VerifyEachPass, DebugLogging);
+ });
+ if (tryParsePipelineText<CGSCCPassManager>(PB, CGSCCOptimizerLateEPPipeline))
+ PB.registerCGSCCOptimizerLateEPCallback([&PB, VerifyEachPass, DebugLogging](
+ CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, VerifyEachPass,
+ DebugLogging);
+ });
+ if (tryParsePipelineText<FunctionPassManager>(PB, VectorizerStartEPPipeline))
+ PB.registerVectorizerStartEPCallback([&PB, VerifyEachPass, DebugLogging](
+ FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+ PB.parsePassPipeline(PM, VectorizerStartEPPipeline, VerifyEachPass,
+ DebugLogging);
+ });
+}
+
bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
tool_output_file *Out,
tool_output_file *ThinLTOLinkOut,
@@ -56,7 +152,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
bool EmitSummaryIndex, bool EmitModuleHash) {
+ bool VerifyEachPass = VK == VK_VerifyEachPass;
PassBuilder PB(TM);
+ registerEPCallbacks(PB, VerifyEachPass, DebugPM);
// Specially handle the alias analysis manager so that we can register
// a custom pipeline of AA passes with it.
@@ -85,8 +183,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
if (VK > VK_NoVerifier)
MPM.addPass(VerifierPass());
- if (!PB.parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass,
- DebugPM)) {
+ if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
errs() << Arg0 << ": unable to parse pass pipeline description.\n";
return false;
}
diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp
index b2216eab119e1..4463c0f0e48c4 100644
--- a/tools/sanstats/sanstats.cpp
+++ b/tools/sanstats/sanstats.cpp
@@ -76,8 +76,11 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) {
if (Begin == End)
return nullptr;
+ // As the instrumentation tracks the return address and not
+ // the address of the call to `__sanitizer_stats_report` we
+ // remove one from the address to get the correct DI.
if (Expected<DILineInfo> LineInfo =
- Symbolizer.symbolizeCode(Filename, Addr)) {
+ Symbolizer.symbolizeCode(Filename, Addr - 1)) {
llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' '
<< LineInfo->FunctionName << ' ';
} else {
diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp
index 110700d40c328..059ec5f9edcda 100644
--- a/tools/yaml2obj/yaml2wasm.cpp
+++ b/tools/yaml2obj/yaml2wasm.cpp
@@ -338,7 +338,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS,
WasmYAML::DataSection &Section) {
encodeULEB128(Section.Segments.size(), OS);
for (auto &Segment : Section.Segments) {
- encodeULEB128(Segment.Index, OS);
+ encodeULEB128(Segment.MemoryIndex, OS);
writeInitExpr(Segment.Offset, OS);
encodeULEB128(Segment.Content.binary_size(), OS);
Segment.Content.writeAsBinary(OS);