MLIR 22.0.0git
DebugImporter.cpp
Go to the documentation of this file.
1//===- DebugImporter.cpp - LLVM to MLIR 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 "DebugImporter.h"
11#include "mlir/IR/Attributes.h"
13#include "mlir/IR/Location.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/ADT/TypeSwitch.h"
16#include "llvm/BinaryFormat/Dwarf.h"
17#include "llvm/IR/Constants.h"
18#include "llvm/IR/DebugInfoMetadata.h"
19#include "llvm/IR/Metadata.h"
20
21using namespace mlir;
22using namespace mlir::LLVM;
23using namespace mlir::LLVM::detail;
24
25DebugImporter::DebugImporter(ModuleOp mlirModule,
26 bool dropDICompositeTypeElements)
27 : cache([&](llvm::DINode *node) { return createRecSelf(node); }),
28 context(mlirModule.getContext()), mlirModule(mlirModule),
29 dropDICompositeTypeElements(dropDICompositeTypeElements) {}
30
32 llvm::DISubprogram *subprogram = func->getSubprogram();
33 if (!subprogram)
34 return UnknownLoc::get(context);
35
36 // Add a fused location to link the subprogram information.
37 StringAttr fileName = StringAttr::get(context, subprogram->getFilename());
39 {FileLineColLoc::get(fileName, subprogram->getLine(), /*column=*/0)},
40 translate(subprogram), context);
41}
42
43//===----------------------------------------------------------------------===//
44// Attributes
45//===----------------------------------------------------------------------===//
46
47DIBasicTypeAttr DebugImporter::translateImpl(llvm::DIBasicType *node) {
48 return DIBasicTypeAttr::get(context, node->getTag(), node->getName(),
49 node->getSizeInBits(), node->getEncoding());
50}
51
52DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) {
53 std::optional<DIEmissionKind> emissionKind =
54 symbolizeDIEmissionKind(node->getEmissionKind());
55 std::optional<DINameTableKind> nameTableKind = symbolizeDINameTableKind(
56 static_cast<
57 std::underlying_type_t<llvm::DICompileUnit::DebugNameTableKind>>(
58 node->getNameTableKind()));
59 return DICompileUnitAttr::get(
60 context, getOrCreateDistinctID(node),
61 node->getSourceLanguage().getUnversionedName(),
62 translate(node->getFile()), getStringAttrOrNull(node->getRawProducer()),
63 node->isOptimized(), emissionKind.value(), nameTableKind.value(),
64 getStringAttrOrNull(node->getRawSplitDebugFilename()));
65}
66
67DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) {
68 std::optional<DIFlags> flags = symbolizeDIFlags(node->getFlags());
70
71 // A vector always requires an element.
72 bool isVectorType = flags && bitEnumContainsAll(*flags, DIFlags::Vector);
73 if (isVectorType || !dropDICompositeTypeElements) {
74 for (llvm::DINode *element : node->getElements()) {
75 assert(element && "expected a non-null element type");
76 elements.push_back(translate(element));
77 }
78 }
79 // Drop the elements parameter if any of the elements are invalid.
80 if (llvm::is_contained(elements, nullptr))
81 elements.clear();
82 DITypeAttr baseType = translate(node->getBaseType());
83 // Arrays require a base type, otherwise the debug metadata is considered to
84 // be malformed.
85 if (node->getTag() == llvm::dwarf::DW_TAG_array_type && !baseType)
86 return nullptr;
87 return DICompositeTypeAttr::get(
88 context, node->getTag(), getStringAttrOrNull(node->getRawName()),
89 translate(node->getFile()), node->getLine(), translate(node->getScope()),
90 baseType, flags.value_or(DIFlags::Zero), node->getSizeInBits(),
91 node->getAlignInBits(), translateExpression(node->getDataLocationExp()),
92 translateExpression(node->getRankExp()),
93 translateExpression(node->getAllocatedExp()),
94 translateExpression(node->getAssociatedExp()), elements);
95}
96
97DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) {
98 // Return nullptr if the base type is invalid.
99 DITypeAttr baseType = translate(node->getBaseType());
100 if (node->getBaseType() && !baseType)
101 return nullptr;
102 DINodeAttr extraData =
103 translate(dyn_cast_or_null<llvm::DINode>(node->getExtraData()));
104 return DIDerivedTypeAttr::get(
105 context, node->getTag(), getStringAttrOrNull(node->getRawName()),
106 baseType, node->getSizeInBits(), node->getAlignInBits(),
107 node->getOffsetInBits(), node->getDWARFAddressSpace(), extraData);
108}
109
110DIStringTypeAttr DebugImporter::translateImpl(llvm::DIStringType *node) {
111 return DIStringTypeAttr::get(
112 context, node->getTag(), getStringAttrOrNull(node->getRawName()),
113 node->getSizeInBits(), node->getAlignInBits(),
114 translate(node->getStringLength()),
115 translateExpression(node->getStringLengthExp()),
116 translateExpression(node->getStringLocationExp()), node->getEncoding());
117}
118
119DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) {
120 return DIFileAttr::get(context, node->getFilename(), node->getDirectory());
121}
122
123DILabelAttr DebugImporter::translateImpl(llvm::DILabel *node) {
124 // Return nullptr if the scope or type is a cyclic dependency.
125 DIScopeAttr scope = translate(node->getScope());
126 if (node->getScope() && !scope)
127 return nullptr;
128 return DILabelAttr::get(context, scope,
129 getStringAttrOrNull(node->getRawName()),
130 translate(node->getFile()), node->getLine());
131}
132
133DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) {
134 // Return nullptr if the scope or type is a cyclic dependency.
135 DIScopeAttr scope = translate(node->getScope());
136 if (node->getScope() && !scope)
137 return nullptr;
138 return DILexicalBlockAttr::get(context, scope, translate(node->getFile()),
139 node->getLine(), node->getColumn());
140}
141
142DILexicalBlockFileAttr
143DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
144 // Return nullptr if the scope or type is a cyclic dependency.
145 DIScopeAttr scope = translate(node->getScope());
146 if (node->getScope() && !scope)
147 return nullptr;
148 return DILexicalBlockFileAttr::get(context, scope, translate(node->getFile()),
149 node->getDiscriminator());
150}
151
152DIGlobalVariableAttr
153DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
154 // Names of DIGlobalVariables can be empty. MLIR models them as null, instead
155 // of empty strings, so this special handling is necessary.
156 auto convertToStringAttr = [&](StringRef name) -> StringAttr {
157 if (name.empty())
158 return {};
159 return StringAttr::get(context, node->getName());
160 };
161 return DIGlobalVariableAttr::get(
162 context, translate(node->getScope()),
163 convertToStringAttr(node->getName()),
164 convertToStringAttr(node->getLinkageName()), translate(node->getFile()),
165 node->getLine(), translate(node->getType()), node->isLocalToUnit(),
166 node->isDefinition(), node->getAlignInBits());
167}
168
169DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
170 // Return nullptr if the scope or type is a cyclic dependency.
171 DIScopeAttr scope = translate(node->getScope());
172 if (node->getScope() && !scope)
173 return nullptr;
174 return DILocalVariableAttr::get(
175 context, scope, getStringAttrOrNull(node->getRawName()),
176 translate(node->getFile()), node->getLine(), node->getArg(),
177 node->getAlignInBits(), translate(node->getType()),
178 symbolizeDIFlags(node->getFlags()).value_or(DIFlags::Zero));
179}
180
181DIVariableAttr DebugImporter::translateImpl(llvm::DIVariable *node) {
182 return cast<DIVariableAttr>(translate(static_cast<llvm::DINode *>(node)));
183}
184
185DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) {
186 return cast<DIScopeAttr>(translate(static_cast<llvm::DINode *>(node)));
187}
188
189DIModuleAttr DebugImporter::translateImpl(llvm::DIModule *node) {
190 return DIModuleAttr::get(
191 context, translate(node->getFile()), translate(node->getScope()),
192 getStringAttrOrNull(node->getRawName()),
193 getStringAttrOrNull(node->getRawConfigurationMacros()),
194 getStringAttrOrNull(node->getRawIncludePath()),
195 getStringAttrOrNull(node->getRawAPINotesFile()), node->getLineNo(),
196 node->getIsDecl());
197}
198
199DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) {
200 return DINamespaceAttr::get(context, getStringAttrOrNull(node->getRawName()),
201 translate(node->getScope()),
202 node->getExportSymbols());
203}
204
205DIImportedEntityAttr
206DebugImporter::translateImpl(llvm::DIImportedEntity *node) {
207 SmallVector<DINodeAttr> elements;
208 for (llvm::DINode *element : node->getElements()) {
209 assert(element && "expected a non-null element type");
210 elements.push_back(translate(element));
211 }
212
213 return DIImportedEntityAttr::get(
214 context, node->getTag(), translate(node->getScope()),
215 translate(node->getEntity()), translate(node->getFile()), node->getLine(),
216 getStringAttrOrNull(node->getRawName()), elements);
217}
218
219DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
220 // Only definitions require a distinct identifier.
221 mlir::DistinctAttr id;
222 if (node->isDistinct())
223 id = getOrCreateDistinctID(node);
224
225 // Return nullptr if the scope or type is invalid.
226 DIScopeAttr scope = translate(node->getScope());
227 if (node->getScope() && !scope)
228 return nullptr;
229 std::optional<DISubprogramFlags> subprogramFlags =
230 symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags());
231 assert(subprogramFlags && "expected valid subprogram flags");
232 DISubroutineTypeAttr type = translate(node->getType());
233 if (node->getType() && !type)
234 return nullptr;
235
236 // Convert the retained nodes but drop all of them if one of them is invalid.
237 SmallVector<DINodeAttr> retainedNodes;
238 for (llvm::DINode *retainedNode : node->getRetainedNodes())
239 retainedNodes.push_back(translate(retainedNode));
240 if (llvm::is_contained(retainedNodes, nullptr))
241 retainedNodes.clear();
242
243 SmallVector<DINodeAttr> annotations;
244 // We currently only support `string` values for annotations on the MLIR side.
245 // Theoretically we could support other primitives, but LLVM is not using
246 // other types in practice.
247 if (llvm::DINodeArray rawAnns = node->getAnnotations(); rawAnns) {
248 for (size_t i = 0, e = rawAnns->getNumOperands(); i < e; ++i) {
249 const llvm::MDTuple *tuple = cast<llvm::MDTuple>(rawAnns->getOperand(i));
250 if (tuple->getNumOperands() != 2)
251 continue;
252 const llvm::MDString *name = cast<llvm::MDString>(tuple->getOperand(0));
253 const llvm::MDString *value =
254 dyn_cast<llvm::MDString>(tuple->getOperand(1));
255 if (name && value) {
256 annotations.push_back(DIAnnotationAttr::get(
257 context, StringAttr::get(context, name->getString()),
258 StringAttr::get(context, value->getString())));
259 }
260 }
261 }
262
263 return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope,
264 getStringAttrOrNull(node->getRawName()),
265 getStringAttrOrNull(node->getRawLinkageName()),
266 translate(node->getFile()), node->getLine(),
267 node->getScopeLine(), *subprogramFlags, type,
268 retainedNodes, annotations);
269}
270
271DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
272 auto getAttrOrNull = [&](llvm::DISubrange::BoundType data) -> Attribute {
273 if (data.isNull())
274 return nullptr;
275 if (auto *constInt = dyn_cast<llvm::ConstantInt *>(data))
276 return IntegerAttr::get(IntegerType::get(context, 64),
277 constInt->getSExtValue());
278 if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
279 return translateExpression(expr);
280 if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
281 if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
282 return translate(local);
283 if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
284 return translate(global);
285 return nullptr;
286 }
287 return nullptr;
288 };
289 Attribute count = getAttrOrNull(node->getCount());
290 Attribute upperBound = getAttrOrNull(node->getUpperBound());
291 // Either count or the upper bound needs to be present. Otherwise, the
292 // metadata is invalid. The conversion might fail due to unsupported DI nodes.
293 if (!count && !upperBound)
294 return {};
295 return DISubrangeAttr::get(context, count,
296 getAttrOrNull(node->getLowerBound()), upperBound,
297 getAttrOrNull(node->getStride()));
298}
299
300DICommonBlockAttr DebugImporter::translateImpl(llvm::DICommonBlock *node) {
301 return DICommonBlockAttr::get(context, translate(node->getScope()),
302 translate(node->getDecl()),
303 getStringAttrOrNull(node->getRawName()),
304 translate(node->getFile()), node->getLineNo());
305}
306
307DIGenericSubrangeAttr
308DebugImporter::translateImpl(llvm::DIGenericSubrange *node) {
309 auto getAttrOrNull =
310 [&](llvm::DIGenericSubrange::BoundType data) -> Attribute {
311 if (data.isNull())
312 return nullptr;
313 if (auto *expr = dyn_cast<llvm::DIExpression *>(data))
314 return translateExpression(expr);
315 if (auto *var = dyn_cast<llvm::DIVariable *>(data)) {
316 if (auto *local = dyn_cast<llvm::DILocalVariable>(var))
317 return translate(local);
318 if (auto *global = dyn_cast<llvm::DIGlobalVariable>(var))
319 return translate(global);
320 return nullptr;
321 }
322 return nullptr;
323 };
324 Attribute count = getAttrOrNull(node->getCount());
325 Attribute upperBound = getAttrOrNull(node->getUpperBound());
326 Attribute lowerBound = getAttrOrNull(node->getLowerBound());
327 Attribute stride = getAttrOrNull(node->getStride());
328 // Either count or the upper bound needs to be present. Otherwise, the
329 // metadata is invalid.
330 if (!count && !upperBound)
331 return {};
332 return DIGenericSubrangeAttr::get(context, count, lowerBound, upperBound,
333 stride);
334}
335
336DISubroutineTypeAttr
337DebugImporter::translateImpl(llvm::DISubroutineType *node) {
338 SmallVector<DITypeAttr> types;
339 for (llvm::DIType *type : node->getTypeArray()) {
340 if (!type) {
341 // A nullptr entry may appear at the beginning or the end of the
342 // subroutine types list modeling either a void result type or the type of
343 // a variadic argument. Translate the nullptr to an explicit
344 // DINullTypeAttr since the attribute list cannot contain a nullptr entry.
345 types.push_back(DINullTypeAttr::get(context));
346 continue;
347 }
348 types.push_back(translate(type));
349 }
350 // Return nullptr if any of the types is invalid.
351 if (llvm::is_contained(types, nullptr))
352 return nullptr;
353 return DISubroutineTypeAttr::get(context, node->getCC(), types);
354}
355
356DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
357 return cast<DITypeAttr>(translate(static_cast<llvm::DINode *>(node)));
358}
359
361 if (!node)
362 return nullptr;
363
364 // Check for a cached instance.
365 auto cacheEntry = cache.lookupOrInit(node);
366 if (std::optional<DINodeAttr> result = cacheEntry.get())
367 return *result;
368
369 // Convert the debug metadata if possible.
370 auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
371 if (auto *casted = dyn_cast<llvm::DIBasicType>(node))
372 return translateImpl(casted);
373 if (auto *casted = dyn_cast<llvm::DICommonBlock>(node))
374 return translateImpl(casted);
375 if (auto *casted = dyn_cast<llvm::DICompileUnit>(node))
376 return translateImpl(casted);
377 if (auto *casted = dyn_cast<llvm::DICompositeType>(node))
378 return translateImpl(casted);
379 if (auto *casted = dyn_cast<llvm::DIDerivedType>(node))
380 return translateImpl(casted);
381 if (auto *casted = dyn_cast<llvm::DIStringType>(node))
382 return translateImpl(casted);
383 if (auto *casted = dyn_cast<llvm::DIFile>(node))
384 return translateImpl(casted);
385 if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(node))
386 return translateImpl(casted);
387 if (auto *casted = dyn_cast<llvm::DIImportedEntity>(node))
388 return translateImpl(casted);
389 if (auto *casted = dyn_cast<llvm::DILabel>(node))
390 return translateImpl(casted);
391 if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node))
392 return translateImpl(casted);
393 if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(node))
394 return translateImpl(casted);
395 if (auto *casted = dyn_cast<llvm::DILocalVariable>(node))
396 return translateImpl(casted);
397 if (auto *casted = dyn_cast<llvm::DIModule>(node))
398 return translateImpl(casted);
399 if (auto *casted = dyn_cast<llvm::DINamespace>(node))
400 return translateImpl(casted);
401 if (auto *casted = dyn_cast<llvm::DISubprogram>(node))
402 return translateImpl(casted);
403 if (auto *casted = dyn_cast<llvm::DISubrange>(node))
404 return translateImpl(casted);
405 if (auto *casted = dyn_cast<llvm::DIGenericSubrange>(node))
406 return translateImpl(casted);
407 if (auto *casted = dyn_cast<llvm::DISubroutineType>(node))
408 return translateImpl(casted);
409 return nullptr;
410 };
411 if (DINodeAttr attr = translateNode(node)) {
412 // If this node was repeated, lookup its recursive ID and assign it to the
413 // base result.
414 if (cacheEntry.wasRepeated()) {
415 DistinctAttr recId = nodeToRecId.lookup(node);
416 auto recType = cast<DIRecursiveTypeAttrInterface>(attr);
417 attr = cast<DINodeAttr>(recType.withRecId(recId));
418 }
419 cacheEntry.resolve(attr);
420 return attr;
421 }
422 cacheEntry.resolve(nullptr);
423 return nullptr;
424}
425
426/// Get the `getRecSelf` constructor for the translated type of `node` if its
427/// translated DITypeAttr supports recursion. Otherwise, returns nullptr.
428static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
429getRecSelfConstructor(llvm::DINode *node) {
430 using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;
432 .Case([&](llvm::DICompositeType *) {
433 return CtorType(DICompositeTypeAttr::getRecSelf);
434 })
435 .Case([&](llvm::DISubprogram *) {
436 return CtorType(DISubprogramAttr::getRecSelf);
437 })
438 .Default(CtorType());
439}
440
441std::optional<DINodeAttr> DebugImporter::createRecSelf(llvm::DINode *node) {
442 auto recSelfCtor = getRecSelfConstructor(node);
443 if (!recSelfCtor)
444 return std::nullopt;
445
446 // The original node may have already been assigned a recursive ID from
447 // a different self-reference. Use that if possible.
448 DistinctAttr recId = nodeToRecId.lookup(node);
449 if (!recId) {
450 recId = DistinctAttr::create(UnitAttr::get(context));
451 nodeToRecId[node] = recId;
452 }
453 DIRecursiveTypeAttrInterface recSelf = recSelfCtor(recId);
454 return cast<DINodeAttr>(recSelf);
455}
456
457//===----------------------------------------------------------------------===//
458// Locations
459//===----------------------------------------------------------------------===//
460
461Location DebugImporter::translateLoc(llvm::DILocation *loc) {
462 if (!loc)
463 return UnknownLoc::get(context);
464
465 // Get the file location of the instruction.
466 Location result = FileLineColLoc::get(context, loc->getFilename(),
467 loc->getLine(), loc->getColumn());
468
469 // Add scope information.
470 assert(loc->getScope() && "expected non-null scope");
472 context);
473
474 // Add call site information, if available.
475 if (llvm::DILocation *inlinedAt = loc->getInlinedAt())
476 result = CallSiteLoc::get(result, translateLoc(inlinedAt));
477
478 return result;
479}
480
481DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) {
482 if (!node)
483 return nullptr;
484
486
487 // Begin processing the operations.
488 for (const llvm::DIExpression::ExprOperand &op : node->expr_ops()) {
489 SmallVector<uint64_t> operands;
490 operands.reserve(op.getNumArgs());
491 for (const auto &i : llvm::seq(op.getNumArgs()))
492 operands.push_back(op.getArg(i));
493 const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands);
494 ops.push_back(attr);
495 }
496 return DIExpressionAttr::get(context, ops);
497}
498
500 llvm::DIGlobalVariableExpression *node) {
501 return DIGlobalVariableExpressionAttr::get(
502 context, translate(node->getVariable()),
503 translateExpression(node->getExpression()));
504}
505
506StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) {
507 if (!stringNode)
508 return StringAttr();
509 return StringAttr::get(context, stringNode->getString());
510}
511
512DistinctAttr DebugImporter::getOrCreateDistinctID(llvm::DINode *node) {
513 DistinctAttr &id = nodeToDistinctAttr[node];
514 if (!id)
515 id = DistinctAttr::create(UnitAttr::get(context));
516 return id;
517}
static function_ref< DIRecursiveTypeAttrInterface(DistinctAttr)> getRecSelfConstructor(llvm::DINode *node)
Get the getRecSelf constructor for the translated type of node if its translated DITypeAttr supports ...
An attribute that associates a referenced attribute with a unique identifier.
static DistinctAttr create(Attribute referencedAttr)
Creates a distinct attribute that associates a referenced attribute with a unique identifier.
static FileLineColLoc get(StringAttr filename, unsigned line, unsigned column)
Definition Location.cpp:157
This class represents a fused location whose metadata is known to be an instance of the given type.
Definition Location.h:149
This class represents the base attribute for all debug info attributes.
Definition LLVMAttrs.h:29
DINodeAttr translate(llvm::DINode *node)
Translates the given LLVM debug metadata to MLIR.
DIExpressionAttr translateExpression(llvm::DIExpression *node)
Translates the LLVM DWARF expression metadata to MLIR.
DIGlobalVariableExpressionAttr translateGlobalVariableExpression(llvm::DIGlobalVariableExpression *node)
Translates the LLVM DWARF global variable expression metadata to MLIR.
Location translateLoc(llvm::DILocation *loc)
Translates the given LLVM debug location to an MLIR location.
Location translateFuncLocation(llvm::Function *func)
Translates the debug information for the given function into a Location.
DebugImporter(ModuleOp mlirModule, bool dropDICompositeTypeElements)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition CallGraph.h:229
Include the generated interface declarations.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:144
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152