diff options
-rw-r--r-- | include/llvm/InitializePasses.h | 1 | ||||
-rw-r--r-- | include/llvm/LinkAllPasses.h | 1 | ||||
-rw-r--r-- | include/llvm/Transforms/Instrumentation.h | 4 | ||||
-rw-r--r-- | lib/Transforms/Instrumentation/Instrumentation.cpp | 1 | ||||
-rw-r--r-- | lib/Transforms/Instrumentation/LineProfiling.cpp | 217 | ||||
-rw-r--r-- | runtime/libprofile/LineProfiling.c | 37 | ||||
-rw-r--r-- | runtime/libprofile/libprofile.exports | 3 |
7 files changed, 263 insertions, 1 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 74687e1ad4..be71898af4 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -94,6 +94,7 @@ void initializeDominatorTreePass(PassRegistry&); void initializeEdgeBundlesPass(PassRegistry&); void initializeEdgeProfilerPass(PassRegistry&); void initializePathProfilerPass(PassRegistry&); +void initializeLineProfilerPass(PassRegistry&); void initializeEarlyCSEPass(PassRegistry&); void initializeExpandISelPseudosPass(PassRegistry&); void initializeFindUsedTypesPass(PassRegistry&); diff --git a/include/llvm/LinkAllPasses.h b/include/llvm/LinkAllPasses.h index c09c67788d..9c486b0181 100644 --- a/include/llvm/LinkAllPasses.h +++ b/include/llvm/LinkAllPasses.h @@ -70,6 +70,7 @@ namespace { (void) llvm::createEdgeProfilerPass(); (void) llvm::createOptimalEdgeProfilerPass(); (void) llvm::createPathProfilerPass(); + (void) llvm::createLineProfilerPass(); (void) llvm::createFunctionInliningPass(); (void) llvm::createAlwaysInlinerPass(); (void) llvm::createGlobalDCEPass(); diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index aa9873fb8a..d47216dec7 100644 --- a/include/llvm/Transforms/Instrumentation.h +++ b/include/llvm/Transforms/Instrumentation.h @@ -17,7 +17,6 @@ namespace llvm { class ModulePass; -class FunctionPass; // Insert edge profiling instrumentation ModulePass *createEdgeProfilerPass(); @@ -28,6 +27,9 @@ ModulePass *createOptimalEdgeProfilerPass(); // Insert path profiling instrumentation ModulePass *createPathProfilerPass(); +// Insert line profiling instrumentation +ModulePass *createLineProfilerPass(); + } // End llvm namespace #endif diff --git a/lib/Transforms/Instrumentation/Instrumentation.cpp b/lib/Transforms/Instrumentation/Instrumentation.cpp index 96ed4fa5c0..b299bb71a4 100644 --- a/lib/Transforms/Instrumentation/Instrumentation.cpp +++ b/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -23,6 +23,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) { initializeEdgeProfilerPass(Registry); initializeOptimalEdgeProfilerPass(Registry); initializePathProfilerPass(Registry); + initializeLineProfilerPass(Registry); } /// LLVMInitializeInstrumentation - C binding for diff --git a/lib/Transforms/Instrumentation/LineProfiling.cpp b/lib/Transforms/Instrumentation/LineProfiling.cpp new file mode 100644 index 0000000000..5628c3a811 --- /dev/null +++ b/lib/Transforms/Instrumentation/LineProfiling.cpp @@ -0,0 +1,217 @@ +//===- LineProfiling.cpp - Insert counters for line profiling -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass creates counters for the number of times that the original source +// lines of code were executed. +// +// The lines are found from existing debug info in the LLVM IR. Iterating +// through LLVM instructions, every time the debug location changes we insert a +// new counter and instructions to increment the counter there. A global +// destructor runs to dump the counters out to a file. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "insert-line-profiling" + +#include "ProfilingUtils.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Analysis/DebugInfo.h" +#include "llvm/Module.h" +#include "llvm/Pass.h" +#include "llvm/Instructions.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DebugLoc.h" +#include "llvm/Support/InstIterator.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include <set> +#include <string> +using namespace llvm; + +STATISTIC(NumUpdatesInserted, "The # of counter increments inserted."); + +namespace { + class LineProfiler : public ModulePass { + bool runOnModule(Module &M); + public: + static char ID; + LineProfiler() : ModulePass(ID) { + initializeLineProfilerPass(*PassRegistry::getPassRegistry()); + } + virtual const char *getPassName() const { + return "Line Profiler"; + } + + private: + // Get pointers to the functions in the runtime library. + Constant *getStartFileFunc(); + Constant *getCounterFunc(); + Constant *getEndFileFunc(); + + // Insert an increment of the counter before instruction I. + void InsertCounterUpdateBefore(Instruction *I); + + // Add the function to write out all our counters to the global destructor + // list. + void InsertCounterWriteout(); + + // Mapping from the source location to the counter tracking that location. + DenseMap<DebugLoc, GlobalVariable *> counters; + + Module *Mod; + LLVMContext *Ctx; + }; +} + +char LineProfiler::ID = 0; +INITIALIZE_PASS(LineProfiler, "insert-line-profiling", + "Insert instrumentation for line profiling", false, false) + +ModulePass *llvm::createLineProfilerPass() { return new LineProfiler(); } + +bool LineProfiler::runOnModule(Module &M) { + Mod = &M; + Ctx = &M.getContext(); + + DebugLoc last_line; // initializes to unknown + bool Changed = false; + for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { + for (inst_iterator II = inst_begin(F), IE = inst_end(F); II != IE; ++II) { + const DebugLoc &loc = II->getDebugLoc(); + if (loc.isUnknown()) continue; + if (loc == last_line) continue; + last_line = loc; + + InsertCounterUpdateBefore(&*II); + ++NumUpdatesInserted; + Changed = true; + } + } + + if (Changed) { + InsertCounterWriteout(); + } + + return Changed; +} + +void LineProfiler::InsertCounterUpdateBefore(Instruction *I) { + const DebugLoc &loc = I->getDebugLoc(); + GlobalVariable *&counter = counters[loc]; + const Type *Int64Ty = Type::getInt64Ty(*Ctx); + if (!counter) { + counter = new GlobalVariable(*Mod, Int64Ty, false, + GlobalValue::InternalLinkage, + Constant::getNullValue(Int64Ty), + "__llvm_prof_linecov_ctr", 0, false, 0); + counter->setVisibility(GlobalVariable::HiddenVisibility); + counter->setUnnamedAddr(true); + } + + if (isa<PHINode>(I)) { + // We may not error out or crash in this case, because a module could put + // changing line numbers on phi nodes and still pass the verifier. + dbgs() << "Refusing to insert code before phi: " << *I << "\n"; + I = I->getParent()->getFirstNonPHI(); + } + + IRBuilder<> builder(I); + Value *ctr = builder.CreateLoad(counter); + ctr = builder.CreateAdd(ctr, ConstantInt::get(Int64Ty, 1)); + builder.CreateStore(ctr, counter); +} + +static DISubprogram FindSubprogram(DIScope scope) { + while (!scope.isSubprogram()) { + assert(scope.isLexicalBlock() && + "Debug location not lexical block or subprogram"); + scope = DILexicalBlock(scope).getContext(); + } + return DISubprogram(scope); +} + +Constant *LineProfiler::getStartFileFunc() { + const Type *Args[1] = { Type::getInt8PtrTy(*Ctx) }; + const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), + Args, false); + return Mod->getOrInsertFunction("llvm_prof_linectr_start_file", FTy); +} + +Constant *LineProfiler::getCounterFunc() { + const Type *Args[] = { + Type::getInt8PtrTy(*Ctx), // const char *dir + Type::getInt8PtrTy(*Ctx), // const char *file + Type::getInt32Ty(*Ctx), // uint32_t line + Type::getInt32Ty(*Ctx), // uint32_t column + Type::getInt64PtrTy(*Ctx), // int64_t *counter + }; + const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), + Args, false); + return Mod->getOrInsertFunction("llvm_prof_linectr_emit_counter", FTy); +} + +Constant *LineProfiler::getEndFileFunc() { + const FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); + return Mod->getOrInsertFunction("llvm_prof_linectr_end_file", FTy); +} + +void LineProfiler::InsertCounterWriteout() { + std::set<std::string> compile_units; + for (DenseMap<DebugLoc, GlobalVariable *>::iterator I = counters.begin(), + E = counters.end(); I != E; ++I) { + const DebugLoc &loc = I->first; + DISubprogram subprogram(FindSubprogram(DIScope(loc.getScope(*Ctx)))); + compile_units.insert(subprogram.getCompileUnit().getFilename().str()); + } + + const FunctionType *WriteoutFTy = + FunctionType::get(Type::getVoidTy(*Ctx), false); + Function *WriteoutF = Function::Create(WriteoutFTy, + GlobalValue::InternalLinkage, + "__llvm_prof_linecov_dtor", + Mod); + WriteoutF->setUnnamedAddr(true); + BasicBlock *BB = BasicBlock::Create(*Ctx, "", WriteoutF); + IRBuilder<> builder(BB); + + Constant *StartFile = getStartFileFunc(); + Constant *EmitCounter = getCounterFunc(); + Constant *EndFile = getEndFileFunc(); + + for (std::set<std::string>::const_iterator CUI = compile_units.begin(), + CUE = compile_units.end(); CUI != CUE; ++CUI) { + builder.CreateCall(StartFile, + builder.CreateGlobalStringPtr(*CUI)); + for (DenseMap<DebugLoc, GlobalVariable *>::iterator I = counters.begin(), + E = counters.end(); I != E; ++I) { + const DebugLoc &loc = I->first; + DISubprogram subprogram(FindSubprogram(DIScope(loc.getScope(*Ctx)))); + DICompileUnit compileunit(subprogram.getCompileUnit()); + + if (compileunit.getFilename() != *CUI) + continue; + + Value *Args[] = { + builder.CreateGlobalStringPtr(subprogram.getDirectory()), + builder.CreateGlobalStringPtr(subprogram.getFilename()), + ConstantInt::get(Type::getInt32Ty(*Ctx), loc.getLine()), + ConstantInt::get(Type::getInt32Ty(*Ctx), loc.getCol()), + I->second + }; + builder.CreateCall(EmitCounter, Args); + } + builder.CreateCall(EndFile); + } + builder.CreateRetVoid(); + + InsertProfilingShutdownCall(WriteoutF, Mod); +} diff --git a/runtime/libprofile/LineProfiling.c b/runtime/libprofile/LineProfiling.c new file mode 100644 index 0000000000..6e6fa196be --- /dev/null +++ b/runtime/libprofile/LineProfiling.c @@ -0,0 +1,37 @@ +/*===- LineProfiling.c - Support library for line profiling ---------------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +|*===----------------------------------------------------------------------===*| +|* +|* This file implements the call back routines for the line profiling +|* instrumentation pass. Link against this library when running code through +|* the -insert-line-profiling LLVM pass. +|* +\*===----------------------------------------------------------------------===*/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> + +/* A file in this case is a translation unit. Each .o file built with line + * profiling enabled will emit to a different file. Only one file may be + * started at a time. + */ +void llvm_prof_linectr_start_file(const char *orig_filename) { + printf("[%s]\n", orig_filename); +} + +/* Emit data about a counter to the data file. */ +void llvm_prof_linectr_emit_counter(const char *dir, const char *file, + uint32_t line, uint32_t column, + int64_t *counter) { + printf("%s/%s:%u:%u %lu\n", dir, file, line, column, *counter); +} + +void llvm_prof_linectr_end_file() { + printf("-----\n"); +} diff --git a/runtime/libprofile/libprofile.exports b/runtime/libprofile/libprofile.exports index b8057c7aac..fb04ea3480 100644 --- a/runtime/libprofile/libprofile.exports +++ b/runtime/libprofile/libprofile.exports @@ -5,3 +5,6 @@ llvm_start_basic_block_tracing llvm_trace_basic_block llvm_increment_path_count llvm_decrement_path_count +llvm_prof_linectr_start_file +llvm_prof_linectr_emit_counter +llvm_prof_linectr_end_file |