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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of mlir/lib/Target/LLVMIR/DebugImporter.cpp