Add DebugIR pass -- emits IR file and replace source lines with IR lines in MD
[oota-llvm.git] / lib / Transforms / Instrumentation / DebugIR.cpp
1 //===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // A Module transform pass that emits a succinct version of the IR and replaces
11 // the source file metadata to allow debuggers to step through the IR.
12 //
13 // The location where the IR file is emitted is the same as the directory
14 // operand of the !llvm.dbg.cu metadata node present in the input module. The
15 // file name is constructed from the original file name by stripping the
16 // extension and replacing it with "-debug.ll" or the Postfix string specified
17 // at construction.
18 //
19 // FIXME: instead of replacing debug metadata, additional metadata should be
20 // used to point capable debuggers to the IR file without destroying the
21 // mapping to the original source file.
22 //
23 // FIXME: this pass should not depend on the existance of debug metadata in
24 // the module as it does now. Instead, it should use DIBuilder to create the
25 // required metadata.
26 //
27 //===----------------------------------------------------------------------===//
28
29 #include <string>
30
31 #include "llvm/ADT/ArrayRef.h"
32 #include "llvm/ADT/SmallSet.h"
33 #include "llvm/DebugInfo.h"
34 #include "llvm/DIBuilder.h"
35 #include "llvm/IR/AsmWriter.h"
36 #include "llvm/IR/Instruction.h"
37 #include "llvm/IR/IntrinsicInst.h"
38 #include "llvm/IR/Module.h"
39 #include "llvm/Pass.h"
40 #include "llvm/Transforms/Instrumentation.h"
41 #include "llvm/Support/Debug.h"
42 #include "llvm/Support/ToolOutputFile.h"
43 #include "llvm/Support/raw_ostream.h"
44 using namespace llvm;
45
46 namespace {
47
48 /// Returns true if Node's name contains the string "llvm.dbg"
49 bool isDebugNamedMetadata(const NamedMDNode *Node) {
50   return Node->getName().str().find("llvm.dbg") != std::string::npos;
51 }
52
53 /// Returns true if Inst is a call to llvm.dbg.value or llvm.dbg.declare
54 bool isDebugIntrinsic(const IntrinsicInst *Inst) {
55   Intrinsic::ID id = Inst->getIntrinsicID();
56   return id == Intrinsic::dbg_value || id == Intrinsic::dbg_declare;
57 }
58
59 /// An AssemblyWriter which generates a succinct representation of the module
60 /// (without debug intrinsics or metadata) suitable for debugging. As IR
61 /// instructions are printed, !dbg metadata nodes are added (or updated)
62 /// to point to the corresponding line in the generated IR file instead
63 /// of the original source file. The input module must have been constructed
64 /// with debug metadata (i.e. clang -g).
65 class IRDebugInfoHelper : public llvm::AssemblyWriter {
66   /// Directory of debug metadata
67   const DebugInfoFinder &Finder;
68
69   /// Flags to control the verbosity of the generated IR file
70   bool hideDebugIntrinsics;
71   bool hideDebugMetadata;
72
73   /// Set to track metadata nodes to be printed (used only when
74   /// printDebugMetadata == false)
75   SmallSet<const MDNode *, 4> NonDebugNodes;
76
77 public:
78   IRDebugInfoHelper(
79       formatted_raw_ostream &o, const Module *M,
80       AssemblyAnnotationWriter *AAW, const DebugInfoFinder &Finder,
81       bool hideDebugIntrinsics = true, bool hideDebugMetadata = true)
82       : AssemblyWriter(o, M, AAW), Finder(Finder),
83         hideDebugIntrinsics(hideDebugIntrinsics),
84         hideDebugMetadata(hideDebugMetadata) {}
85
86 private:
87   virtual void printInstruction(const Instruction &I) {
88     DebugLoc Loc(I.getDebugLoc());
89
90     if (hideDebugMetadata)
91       removeDebugMetadata(const_cast<Instruction &>(I));
92
93     AssemblyWriter::printInstruction(I);
94     Out.flush();
95     // Adjust line number by 1 because we have not yet printed the \n
96     unsigned Line = Out.getLine() + 1;
97
98     DebugLoc NewLoc;
99     if (!Loc.isUnknown())
100       // I had a previous debug location: re-use the DebugLoc
101       NewLoc = DebugLoc::get(Line, /* FIXME: support columns */ 0,
102                              Loc.getScope(I.getContext()),
103                              Loc.getInlinedAt(I.getContext()));
104     else if (MDNode *scope = findFunctionMD(I.getParent()->getParent()))
105       // I had no previous debug location, but M has some debug information
106       NewLoc = DebugLoc::get(Line, 0, scope, /*FIXME: inlined instructions*/ 0);
107     else
108       // Neither I nor M has any debug information -- nothing to do here.
109       // FIXME: support debugging of undecorated IR (generated by clang without
110       //        the -g option)
111       return;
112
113     if (hideDebugMetadata)
114       saveNonDebugMetadata(I);
115
116     addDebugLocation(const_cast<Instruction &>(I), NewLoc);
117   }
118
119   virtual void printInstructionLine(const Instruction &I) {
120     if (hideDebugIntrinsics)
121       if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I))
122         if (isDebugIntrinsic(II))
123           return;
124     AssemblyWriter::printInstructionLine(I);
125   }
126
127   virtual void writeMDNode(unsigned Slot, const MDNode *Node) {
128     if (hideDebugMetadata == false || isDebugMetadata(Node) == false)
129       AssemblyWriter::writeMDNode(Slot, Node);
130   }
131
132   virtual void printNamedMDNode(const NamedMDNode *NMD) {
133     if (hideDebugMetadata == false || isDebugNamedMetadata(NMD) == false)
134       AssemblyWriter::printNamedMDNode(NMD);
135   }
136
137   /// Returns the MDNode that corresponds with F
138   MDNode *findFunctionMD(const Function *F) {
139     for (DebugInfoFinder::iterator i = Finder.subprogram_begin(),
140                                    e = Finder.subprogram_end();
141          i != e; ++i) {
142       DISubprogram S(*i);
143       if (S.getFunction() == F)
144         return *i;
145     }
146     // cannot find F -- likely means there is no debug information
147     return 0;
148   }
149
150   /// Saves all non-debug metadata attached to I
151   void saveNonDebugMetadata(const Instruction &I) {
152     typedef SmallVector<std::pair<unsigned, MDNode *>, 4> MDNodeVector;
153     MDNodeVector Others;
154     I.getAllMetadataOtherThanDebugLoc(Others);
155     for (MDNodeVector::iterator i = Others.begin(), e = Others.end(); i != e;
156          ++i)
157       NonDebugNodes.insert(i->second);
158   }
159
160   /// Returns true if Node was not saved as non-debug metadata with
161   /// saveNonDebugMetadata(), false otherwise.
162   bool isDebugMetadata(const MDNode *Node) {
163     return NonDebugNodes.count(Node) == 0;
164   }
165
166   void removeDebugMetadata(Instruction &I) {
167     if (I.getMetadata(LLVMContext::MD_dbg))
168       I.setMetadata(LLVMContext::MD_dbg, 0);
169   }
170
171   void addDebugLocation(Instruction &I, DebugLoc Loc) {
172     MDNode *MD = Loc.getAsMDNode(I.getContext());
173     I.setMetadata(LLVMContext::MD_dbg, MD);
174   }
175 };
176
177 class DebugIR : public ModulePass {
178   std::string Postfix;
179   std::string Filename;
180   DebugInfoFinder Finder;
181
182 public:
183   static char ID;
184
185   DebugIR() : ModulePass(ID), Postfix("-debug.ll") {}
186
187   /// Customize the postfix string used to replace the extension of the
188   /// original filename that appears in the !llvm.dbg.cu metadata node.
189   DebugIR(StringRef postfix) : ModulePass(ID), Postfix(postfix) {}
190
191 private:
192   // Modify the filename embedded in the Compilation-Unit debug information of M
193   bool replaceFilename(Module &M) {
194     bool changed = false;
195
196     // Sanity check -- if llvm.dbg.cu node exists, the DebugInfoFinder
197     // better have found at least one CU!
198     if (M.getNamedMetadata("llvm.dbg.cu"))
199       assert(Finder.compile_unit_count() > 0 &&
200              "Found no compile units but llvm.dbg.cu node exists");
201
202     for (DebugInfoFinder::iterator i = Finder.compile_unit_begin(),
203                                    e = Finder.compile_unit_end();
204          i != e; ++i) {
205       DICompileUnit CU(*i);
206       Filename = CU.getFilename();
207
208       // Replace extension with postfix
209       size_t dot = Filename.find_last_of(".");
210       if (dot != std::string::npos)
211         Filename.erase(dot);
212       Filename += Postfix;
213
214       CU.setFilename(Filename, M.getContext());
215       changed = true;
216     }
217     return changed;
218   }
219
220   void writeAndUpdateDebugIRFile(Module *M) {
221     std::string error;
222     tool_output_file OutFile(Filename.c_str(), error);
223     OutFile.keep();
224     formatted_raw_ostream OS;
225     OS.setStream(OutFile.os(), false);
226
227     IRDebugInfoHelper W(OS, M, 0, Finder);
228     W.printModule(M);
229   }
230
231   bool runOnModule(Module &M) {
232     Finder.processModule(M);
233     bool changed = replaceFilename(M);
234     if (changed)
235       writeAndUpdateDebugIRFile(&M);
236     return changed;
237   }
238 };
239
240 } // anonymous namespace
241
242 char DebugIR::ID = 0;
243 INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false)
244     ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix) {
245   return new DebugIR(FilenamePostfix);
246 }