MLIR  17.0.0git
DebugTranslation.cpp
Go to the documentation of this file.
1 //===- DebugTranslation.cpp - MLIR to LLVM Debug conversion ---------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DebugTranslation.h"
11 #include "llvm/ADT/TypeSwitch.h"
12 #include "llvm/IR/Metadata.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/Path.h"
16 
17 using namespace mlir;
18 using namespace mlir::LLVM;
19 using namespace mlir::LLVM::detail;
20 
21 /// A utility walker that interrupts if the operation has valid debug
22 /// information.
24  return isa<UnknownLoc>(op->getLoc()) ? WalkResult::advance()
26 }
27 
28 DebugTranslation::DebugTranslation(Operation *module, llvm::Module &llvmModule)
29  : debugEmissionIsEnabled(false), llvmModule(llvmModule),
30  llvmCtx(llvmModule.getContext()) {
31  // If the module has no location information, there is nothing to do.
32  if (!module->walk(interruptIfValidLocation).wasInterrupted())
33  return;
34  debugEmissionIsEnabled = true;
35 
36  // TODO: The version information should be encoded on the LLVM module itself,
37  // not implicitly set here.
38 
39  // Mark this module as having debug information.
40  StringRef debugVersionKey = "Debug Info Version";
41  if (!llvmModule.getModuleFlag(debugVersionKey))
42  llvmModule.addModuleFlag(llvm::Module::Warning, debugVersionKey,
43  llvm::DEBUG_METADATA_VERSION);
44 
45  if (auto targetTripleAttr =
46  module->getAttr(LLVM::LLVMDialect::getTargetTripleAttrName())) {
47  auto targetTriple =
48  llvm::Triple(cast<StringAttr>(targetTripleAttr).getValue());
49  if (targetTriple.isKnownWindowsMSVCEnvironment()) {
50  // Dwarf debugging files will be generated by default, unless "CodeView"
51  // is set explicitly. Windows/MSVC should use CodeView instead.
52  llvmModule.addModuleFlag(llvm::Module::Warning, "CodeView", 1);
53  }
54  }
55 }
56 
57 /// Finalize the translation of debug information.
59 
60 /// Translate the debug information for the given function.
61 void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) {
62  if (!debugEmissionIsEnabled)
63  return;
64 
65  // If we are to create debug info for the function, we need to ensure that all
66  // inlinable calls in it are with debug info, otherwise the LLVM verifier will
67  // complain. For now, be more restricted and treat all calls as inlinable.
68  const bool hasCallWithoutDebugInfo =
69  func.walk([&](LLVM::CallOp call) {
70  return call.getLoc()->walk([](Location l) {
71  return isa<UnknownLoc>(l) ? WalkResult::interrupt()
73  });
74  })
75  .wasInterrupted();
76  if (hasCallWithoutDebugInfo)
77  return;
78 
79  // Look for a sub program attached to the function.
80  auto spLoc =
81  func.getLoc()->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>();
82  if (!spLoc)
83  return;
84  llvmFunc.setSubprogram(translate(spLoc.getMetadata()));
85 }
86 
87 //===----------------------------------------------------------------------===//
88 // Attributes
89 //===----------------------------------------------------------------------===//
90 
91 llvm::DIType *DebugTranslation::translateImpl(DINullTypeAttr attr) {
92  // A DINullTypeAttr at the beginning of the subroutine types list models
93  // a void result type. If it is at the end, it models a variadic function.
94  // Translate the explicit DINullTypeAttr to a nullptr since LLVM IR metadata
95  // does not have an explicit void result type nor a variadic type
96  // representation.
97  return nullptr;
98 }
99 
100 llvm::MDString *DebugTranslation::getMDStringOrNull(StringAttr stringAttr) {
101  if (!stringAttr || stringAttr.getValue().empty())
102  return nullptr;
103  return llvm::MDString::get(llvmCtx, stringAttr);
104 }
105 
106 llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) {
107  return llvm::DIBasicType::get(
108  llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()),
109  attr.getSizeInBits(),
110  /*AlignInBits=*/0, attr.getEncoding(), llvm::DINode::FlagZero);
111 }
112 
113 llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) {
114  llvm::DIBuilder builder(llvmModule);
115  return builder.createCompileUnit(
116  attr.getSourceLanguage(), translate(attr.getFile()),
117  attr.getProducer() ? attr.getProducer().getValue() : "",
118  attr.getIsOptimized(),
119  /*Flags=*/"", /*RV=*/0);
120 }
121 
122 llvm::DICompositeType *
123 DebugTranslation::translateImpl(DICompositeTypeAttr attr) {
125  for (auto member : attr.getElements())
126  elements.push_back(translate(member));
128  llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()),
129  translate(attr.getFile()), attr.getLine(), translate(attr.getScope()),
130  translate(attr.getBaseType()), attr.getSizeInBits(),
131  attr.getAlignInBits(),
132  /*OffsetInBits=*/0,
133  /*Flags=*/static_cast<llvm::DINode::DIFlags>(attr.getFlags()),
134  llvm::MDNode::get(llvmCtx, elements),
135  /*RuntimeLang=*/0, /*VTableHolder=*/nullptr);
136 }
137 
138 llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) {
140  llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()),
141  /*File=*/nullptr, /*Line=*/0,
142  /*Scope=*/nullptr, translate(attr.getBaseType()), attr.getSizeInBits(),
143  attr.getAlignInBits(), attr.getOffsetInBits(),
144  /*DWARFAddressSpace=*/std::nullopt, /*Flags=*/llvm::DINode::FlagZero);
145 }
146 
147 llvm::DIFile *DebugTranslation::translateImpl(DIFileAttr attr) {
148  return llvm::DIFile::get(llvmCtx, getMDStringOrNull(attr.getName()),
149  getMDStringOrNull(attr.getDirectory()));
150 }
151 
152 llvm::DILexicalBlock *DebugTranslation::translateImpl(DILexicalBlockAttr attr) {
153  return llvm::DILexicalBlock::getDistinct(llvmCtx, translate(attr.getScope()),
154  translate(attr.getFile()),
155  attr.getLine(), attr.getColumn());
156 }
157 
158 llvm::DILexicalBlockFile *
159 DebugTranslation::translateImpl(DILexicalBlockFileAttr attr) {
160  return llvm::DILexicalBlockFile::getDistinct(
161  llvmCtx, translate(attr.getScope()), translate(attr.getFile()),
162  attr.getDiscriminator());
163 }
164 
165 llvm::DILocalScope *DebugTranslation::translateImpl(DILocalScopeAttr attr) {
166  return cast<llvm::DILocalScope>(translate(DINodeAttr(attr)));
167 }
168 
169 llvm::DILocalVariable *
170 DebugTranslation::translateImpl(DILocalVariableAttr attr) {
172  llvmCtx, translate(attr.getScope()), getMDStringOrNull(attr.getName()),
173  translate(attr.getFile()), attr.getLine(), translate(attr.getType()),
174  attr.getArg(),
175  /*Flags=*/llvm::DINode::FlagZero, attr.getAlignInBits(),
176  /*Annotations=*/nullptr);
177 }
178 
179 llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) {
180  return cast<llvm::DIScope>(translate(DINodeAttr(attr)));
181 }
182 
183 /// Return a new subprogram that is either distinct or not, depending on
184 /// `isDistinct`.
185 template <class... Ts>
186 static llvm::DISubprogram *getSubprogram(bool isDistinct, Ts &&...args) {
187  if (isDistinct)
188  return llvm::DISubprogram::getDistinct(std::forward<Ts>(args)...);
189  return llvm::DISubprogram::get(std::forward<Ts>(args)...);
190 }
191 
192 llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) {
193  bool isDefinition = static_cast<bool>(attr.getSubprogramFlags() &
194  LLVM::DISubprogramFlags::Definition);
195  return getSubprogram(
196  isDefinition, llvmCtx, translate(attr.getScope()),
197  getMDStringOrNull(attr.getName()),
198  getMDStringOrNull(attr.getLinkageName()), translate(attr.getFile()),
199  attr.getLine(), translate(attr.getType()), attr.getScopeLine(),
200  /*ContainingType=*/nullptr, /*VirtualIndex=*/0,
201  /*ThisAdjustment=*/0, llvm::DINode::FlagZero,
202  static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()),
203  translate(attr.getCompileUnit()));
204 }
205 
206 llvm::DINamespace *DebugTranslation::translateImpl(DINamespaceAttr attr) {
207  return llvm::DINamespace::get(llvmCtx, translate(attr.getScope()),
208  getMDStringOrNull(attr.getName()),
209  attr.getExportSymbols());
210 }
211 
212 llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) {
213  auto getMetadataOrNull = [&](IntegerAttr attr) -> llvm::Metadata * {
214  if (!attr)
215  return nullptr;
216  return llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned(
217  llvm::Type::getInt64Ty(llvmCtx), attr.getInt()));
218  };
219  return llvm::DISubrange::get(llvmCtx, getMetadataOrNull(attr.getCount()),
220  getMetadataOrNull(attr.getLowerBound()),
221  getMetadataOrNull(attr.getUpperBound()),
222  getMetadataOrNull(attr.getStride()));
223 }
224 
225 llvm::DISubroutineType *
226 DebugTranslation::translateImpl(DISubroutineTypeAttr attr) {
227  // Concatenate the result and argument types into a single array.
229  for (DITypeAttr type : attr.getTypes())
230  types.push_back(translate(type));
232  llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(),
233  llvm::DITypeRefArray(llvm::MDNode::get(llvmCtx, types)));
234 }
235 
236 llvm::DIType *DebugTranslation::translateImpl(DITypeAttr attr) {
237  return cast<llvm::DIType>(translate(DINodeAttr(attr)));
238 }
239 
241  if (!attr)
242  return nullptr;
243  // Check for a cached instance.
244  if (llvm::DINode *node = attrToNode.lookup(attr))
245  return node;
246 
247  llvm::DINode *node =
249  .Case<DIBasicTypeAttr, DICompileUnitAttr, DICompositeTypeAttr,
250  DIDerivedTypeAttr, DIFileAttr, DILexicalBlockAttr,
251  DILexicalBlockFileAttr, DILocalVariableAttr, DINamespaceAttr,
252  DINullTypeAttr, DISubprogramAttr, DISubrangeAttr,
253  DISubroutineTypeAttr>(
254  [&](auto attr) { return translateImpl(attr); });
255  attrToNode.insert({attr, node});
256  return node;
257 }
258 
259 //===----------------------------------------------------------------------===//
260 // Locations
261 //===----------------------------------------------------------------------===//
262 
263 /// Translate the given location to an llvm debug location.
265  llvm::DILocalScope *scope) {
266  if (!debugEmissionIsEnabled)
267  return nullptr;
268  return translateLoc(loc, scope, /*inlinedAt=*/nullptr);
269 }
270 
271 /// Translate the given location to an llvm DebugLoc.
272 llvm::DILocation *DebugTranslation::translateLoc(Location loc,
273  llvm::DILocalScope *scope,
274  llvm::DILocation *inlinedAt) {
275  // LLVM doesn't have a representation for unknown.
276  if (!scope || isa<UnknownLoc>(loc))
277  return nullptr;
278 
279  // Check for a cached instance.
280  auto existingIt = locationToLoc.find(std::make_tuple(loc, scope, inlinedAt));
281  if (existingIt != locationToLoc.end())
282  return existingIt->second;
283 
284  llvm::DILocation *llvmLoc = nullptr;
285  if (auto callLoc = dyn_cast<CallSiteLoc>(loc)) {
286  // For callsites, the caller is fed as the inlinedAt for the callee.
287  auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt);
288  llvmLoc = translateLoc(callLoc.getCallee(), scope, callerLoc);
289 
290  } else if (auto fileLoc = dyn_cast<FileLineColLoc>(loc)) {
291  llvm::DILocalScope *locationScope = scope;
292  // Only construct a new DIFile when no local scope is present. This
293  // prioritizes existing DI information when it's present.
294  if (!locationScope) {
295  auto *file = translateFile(fileLoc.getFilename());
296  locationScope = llvm::DILexicalBlockFile::get(llvmCtx, scope, file,
297  /*Discriminator=*/0);
298  }
299  llvmLoc = llvm::DILocation::get(llvmCtx, fileLoc.getLine(),
300  fileLoc.getColumn(), locationScope,
301  const_cast<llvm::DILocation *>(inlinedAt));
302 
303  } else if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
304  ArrayRef<Location> locations = fusedLoc.getLocations();
305 
306  // Check for a scope encoded with the location.
307  if (auto scopedAttr =
308  dyn_cast_or_null<LLVM::DILocalScopeAttr>(fusedLoc.getMetadata()))
309  scope = translate(scopedAttr);
310 
311  // For fused locations, merge each of the nodes.
312  llvmLoc = translateLoc(locations.front(), scope, inlinedAt);
313  for (Location locIt : locations.drop_front()) {
314  llvmLoc = llvm::DILocation::getMergedLocation(
315  llvmLoc, translateLoc(locIt, scope, inlinedAt));
316  }
317 
318  } else if (auto nameLoc = dyn_cast<NameLoc>(loc)) {
319  llvmLoc = translateLoc(nameLoc.getChildLoc(), scope, inlinedAt);
320 
321  } else if (auto opaqueLoc = dyn_cast<OpaqueLoc>(loc)) {
322  llvmLoc = translateLoc(opaqueLoc.getFallbackLocation(), scope, inlinedAt);
323  } else {
324  llvm_unreachable("unknown location kind");
325  }
326 
327  locationToLoc.try_emplace(std::make_tuple(loc, scope, inlinedAt), llvmLoc);
328  return llvmLoc;
329 }
330 
331 /// Create an llvm debug file for the given file path.
332 llvm::DIFile *DebugTranslation::translateFile(StringRef fileName) {
333  auto *&file = fileMap[fileName];
334  if (file)
335  return file;
336 
337  // Make sure the current working directory is up-to-date.
338  if (currentWorkingDir.empty())
339  llvm::sys::fs::current_path(currentWorkingDir);
340 
341  StringRef directory = currentWorkingDir;
342  SmallString<128> dirBuf;
343  SmallString<128> fileBuf;
344  if (llvm::sys::path::is_absolute(fileName)) {
345  // Strip the common prefix (if it is more than just "/") from current
346  // directory and FileName for a more space-efficient encoding.
347  auto fileIt = llvm::sys::path::begin(fileName);
348  auto fileE = llvm::sys::path::end(fileName);
349  auto curDirIt = llvm::sys::path::begin(directory);
350  auto curDirE = llvm::sys::path::end(directory);
351  for (; curDirIt != curDirE && *curDirIt == *fileIt; ++curDirIt, ++fileIt)
352  llvm::sys::path::append(dirBuf, *curDirIt);
353  if (std::distance(llvm::sys::path::begin(directory), curDirIt) == 1) {
354  // Don't strip the common prefix if it is only the root "/" since that
355  // would make LLVM diagnostic locations confusing.
356  directory = StringRef();
357  } else {
358  for (; fileIt != fileE; ++fileIt)
359  llvm::sys::path::append(fileBuf, *fileIt);
360  directory = dirBuf;
361  fileName = fileBuf;
362  }
363  }
364  return (file = llvm::DIFile::get(llvmCtx, fileName, directory));
365 }
static llvm::DISubprogram * getSubprogram(bool isDistinct, Ts &&...args)
Return a new subprogram that is either distinct or not, depending on isDistinct.
static WalkResult interruptIfValidLocation(Operation *op)
A utility walker that interrupts if the operation has valid debug information.
This class represents a fused location whose metadata is known to be an instance of the given type.
Definition: Location.h:145
This class represents a LLVM attribute that describes a local debug info scope.
Definition: LLVMAttrs.h:46
This class represents the base attribute for all debug info attributes.
Definition: LLVMAttrs.h:27
This class represents a LLVM attribute that describes a debug info scope.
Definition: LLVMAttrs.h:36
This class represents a LLVM attribute that describes a debug info type.
Definition: LLVMAttrs.h:55
void finalize()
Finalize the translation of debug information.
void translate(LLVMFuncOp func, llvm::Function &llvmFunc)
Translate the debug information for the given function.
DebugTranslation(Operation *module, llvm::Module &llvmModule)
llvm::DILocation * translateLoc(Location loc, llvm::DILocalScope *scope)
Translate the given location to an llvm debug location.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
Definition: Operation.h:495
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition: Operation.h:738
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
A utility result that is used to signal how to proceed with an ongoing walk:
Definition: Visitors.h:34
static WalkResult advance()
Definition: Visitors.h:52
static WalkResult interrupt()
Definition: Visitors.h:51
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition: Visitors.h:137
This header declares functions that assit transformations in the MemRef dialect.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...