From 3dd706b5288b83967968287c0950480948e8c3f6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 29 Jul 2010 07:53:27 +0000 Subject: Add the llvm-diff tool, which performs a relatively naive structural diff of a function. There's a lot of cruft in the current version, and it's pretty far from perfect, but it's usable. Currently only capable of comparing functions. Currently ignores metadata. Currently ignores most attributes of functions and instructions. Patches welcome. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@109739 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/llvm-diff/llvm-diff.cpp | 301 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 tools/llvm-diff/llvm-diff.cpp (limited to 'tools/llvm-diff/llvm-diff.cpp') diff --git a/tools/llvm-diff/llvm-diff.cpp b/tools/llvm-diff/llvm-diff.cpp new file mode 100644 index 0000000000..7c2a5d66c3 --- /dev/null +++ b/tools/llvm-diff/llvm-diff.cpp @@ -0,0 +1,301 @@ +#include +#include + +#include +#include +#include + +// Required to parse .ll files. +#include +#include + +// Required to parse .bc files. +#include +#include + +#include +#include +#include +#include +#include + +#include "DifferenceEngine.h" + +using namespace llvm; + +/// Reads a module from a file. If the filename ends in .ll, it is +/// interpreted as an assembly file; otherwise, it is interpreted as +/// bitcode. On error, messages are written to stderr and null is +/// returned. +static Module *ReadModule(LLVMContext &Context, StringRef Name) { + // LLVM assembly path. + if (Name.endswith(".ll")) { + SMDiagnostic Diag; + Module *M = ParseAssemblyFile(Name, Diag, Context); + if (M) return M; + + Diag.Print("llvmdiff", errs()); + return 0; + } + + // Bitcode path. + MemoryBuffer *Buffer = MemoryBuffer::getFile(Name); + + // ParseBitcodeFile takes ownership of the buffer if it succeeds. + std::string Error; + Module *M = ParseBitcodeFile(Buffer, Context, &Error); + if (M) return M; + + errs() << "error parsing " << Name << ": " << Error; + delete Buffer; + return 0; +} + +static int usage() { + errs() << "expected usage:\n"; + errs() << " llvm-diff oldmodule.ll newmodule.ll [function list]\n"; + errs() << "Assembly or bitcode modules may be used interchangeably.\n"; + errs() << "If no functions are provided, all functions will be compared.\n"; + return 1; +} + +namespace { +struct DiffContext { + DiffContext(Value *L, Value *R) + : L(L), R(R), Differences(false), IsFunction(isa(L)) {} + Value *L; + Value *R; + bool Differences; + bool IsFunction; + DenseMap LNumbering; + DenseMap RNumbering; +}; + +void ComputeNumbering(Function *F, DenseMap &Numbering) { + unsigned BBN = 0; + unsigned IN = 0; + + // Arguments get the first numbers. + for (Function::arg_iterator + AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI) + if (!AI->hasName()) + Numbering[&*AI] = IN++; + + // Walk the basic blocks in order. + for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) { + // Basic blocks have their own 'namespace'. + if (!FI->hasName()) + Numbering[&*FI] = BBN++; + + // Walk the instructions in order. + for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI) + // void instructions don't get numbers. + if (!BI->hasName() && !BI->getType()->isVoidTy()) + Numbering[&*BI] = IN++; + } + + assert(!Numbering.empty() && "asked for numbering but numbering was no-op"); +} + +class DiffConsumer : public DifferenceEngine::Consumer { +private: + Module *LModule; + Module *RModule; + SmallVector contexts; + bool Differences; + unsigned Indent; + + void printValue(Value *V, bool isL) { + if (V->hasName()) { + errs() << (isa(V) ? '@' : '%') << V->getName(); + return; + } + if (V->getType()->isVoidTy()) { + if (isa(V)) { + errs() << "store to "; + printValue(cast(V)->getPointerOperand(), isL); + } else if (isa(V)) { + errs() << "call to "; + printValue(cast(V)->getCalledValue(), isL); + } else if (isa(V)) { + errs() << "invoke to "; + printValue(cast(V)->getCalledValue(), isL); + } else { + errs() << *V; + } + return; + } + + unsigned N = contexts.size(); + while (N > 0) { + --N; + DiffContext &ctxt = contexts[N]; + if (!ctxt.IsFunction) continue; + if (isL) { + if (ctxt.LNumbering.empty()) + ComputeNumbering(cast(ctxt.L), ctxt.LNumbering); + errs() << '%' << ctxt.LNumbering[V]; + return; + } else { + if (ctxt.RNumbering.empty()) + ComputeNumbering(cast(ctxt.R), ctxt.RNumbering); + errs() << '%' << ctxt.RNumbering[V]; + return; + } + } + + errs() << ""; + } + + void header() { + if (contexts.empty()) return; + for (SmallVectorImpl::iterator + I = contexts.begin(), E = contexts.end(); I != E; ++I) { + if (I->Differences) continue; + if (isa(I->L)) { + // Extra newline between functions. + if (Differences) errs() << "\n"; + + Function *L = cast(I->L); + Function *R = cast(I->R); + if (L->getName() != R->getName()) + errs() << "in function " << L->getName() << " / " << R->getName() << ":\n"; + else + errs() << "in function " << L->getName() << ":\n"; + } else if (isa(I->L)) { + BasicBlock *L = cast(I->L); + BasicBlock *R = cast(I->R); + errs() << " in block "; + printValue(L, true); + errs() << " / "; + printValue(R, false); + errs() << ":\n"; + } else if (isa(I->L)) { + errs() << " in instruction "; + printValue(I->L, true); + errs() << " / "; + printValue(I->R, false); + errs() << ":\n"; + } + + I->Differences = true; + } + } + + void indent() { + unsigned N = Indent; + while (N--) errs() << ' '; + } + +public: + DiffConsumer(Module *L, Module *R) + : LModule(L), RModule(R), Differences(false), Indent(0) {} + + bool hadDifferences() const { return Differences; } + + void enterContext(Value *L, Value *R) { + contexts.push_back(DiffContext(L, R)); + Indent += 2; + } + void exitContext() { + Differences |= contexts.back().Differences; + contexts.pop_back(); + Indent -= 2; + } + + void log(StringRef text) { + header(); + indent(); + errs() << text << "\n"; + } + + void logf(const DifferenceEngine::LogBuilder &Log) { + header(); + indent(); + + // FIXME: we don't know whether these are l-values or r-values (ha!) + // Print them in some saner way! + errs() << Log.getFormat() << "\n"; + for (unsigned I = 0, E = Log.getNumArguments(); I != E; ++I) + Log.getArgument(I)->dump(); + } + + void logd(const DifferenceEngine::DiffLogBuilder &Log) { + header(); + + for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) { + indent(); + switch (Log.getLineKind(I)) { + case DifferenceEngine::DC_match: + errs() << " "; + Log.getLeft(I)->dump(); + //printValue(Log.getLeft(I), true); + break; + case DifferenceEngine::DC_left: + errs() << "< "; + Log.getLeft(I)->dump(); + //printValue(Log.getLeft(I), true); + break; + case DifferenceEngine::DC_right: + errs() << "> "; + Log.getRight(I)->dump(); + //printValue(Log.getRight(I), false); + break; + } + //errs() << "\n"; + } + } + +}; +} + +int main(int argc, const char **argv) { + if (argc < 3) return usage(); + + // Don't make StringRef locals like this at home. + StringRef LModuleFile = argv[1]; + StringRef RModuleFile = argv[2]; + + LLVMContext Context; + + // Load both modules. Die if that fails. + Module *LModule = ReadModule(Context, LModuleFile); + Module *RModule = ReadModule(Context, RModuleFile); + if (!LModule || !RModule) return 1; + + DiffConsumer Consumer(LModule, RModule); + DifferenceEngine Engine(Context, Consumer); + + // If any function names were given, just diff those. + const char **FnNames = argv + 3; + unsigned NumFnNames = argc - 3; + if (NumFnNames) { + for (unsigned I = 0; I != NumFnNames; ++I) { + StringRef FnName = FnNames[I]; + + // Drop leading sigils from the function name. + if (FnName.startswith("@")) FnName = FnName.substr(1); + + Function *LFn = LModule->getFunction(FnName); + Function *RFn = RModule->getFunction(FnName); + if (LFn && RFn) + Engine.diff(LFn, RFn); + else { + if (!LFn && !RFn) + errs() << "No function named @" << FnName << " in either module\n"; + else if (!LFn) + errs() << "No function named @" << FnName << " in left module\n"; + else + errs() << "No function named @" << FnName << " in right module\n"; + } + } + } else { + // Otherwise, diff all functions in the modules. + Engine.diff(LModule, RModule); + } + + delete LModule; + delete RModule; + + return Consumer.hadDifferences(); +} -- cgit v1.2.3-18-g5258