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 : recursionPruner(mlirModule.getContext()),
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(), /*recId=*/{},
93 getStringAttrOrNull(node->getRawName()), translate(node->getFile()),
94 node->getLine(), translate(node->getScope()), baseType,
95 flags.value_or(DIFlags::Zero), node->getSizeInBits(),
96 node->getAlignInBits(), elements);
97}
98
99DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) {
100 // Return nullptr if the base type is invalid.
101 DITypeAttr baseType = translate(node: node->getBaseType());
102 if (node->getBaseType() && !baseType)
103 return nullptr;
104 DINodeAttr extraData =
105 translate(node: dyn_cast_or_null<llvm::DINode>(Val: node->getExtraData()));
106 return DIDerivedTypeAttr::get(
107 context, node->getTag(), getStringAttrOrNull(node->getRawName()),
108 baseType, node->getSizeInBits(), node->getAlignInBits(),
109 node->getOffsetInBits(), extraData);
110}
111
112DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) {
113 return DIFileAttr::get(context, node->getFilename(), node->getDirectory());
114}
115
116DILabelAttr DebugImporter::translateImpl(llvm::DILabel *node) {
117 // Return nullptr if the scope or type is a cyclic dependency.
118 DIScopeAttr scope = translate(node: node->getScope());
119 if (node->getScope() && !scope)
120 return nullptr;
121 return DILabelAttr::get(context, scope,
122 getStringAttrOrNull(node->getRawName()),
123 translate(node->getFile()), node->getLine());
124}
125
126DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) {
127 // Return nullptr if the scope or type is a cyclic dependency.
128 DIScopeAttr scope = translate(node: node->getScope());
129 if (node->getScope() && !scope)
130 return nullptr;
131 return DILexicalBlockAttr::get(context, scope, translate(node->getFile()),
132 node->getLine(), node->getColumn());
133}
134
135DILexicalBlockFileAttr
136DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) {
137 // Return nullptr if the scope or type is a cyclic dependency.
138 DIScopeAttr scope = translate(node: node->getScope());
139 if (node->getScope() && !scope)
140 return nullptr;
141 return DILexicalBlockFileAttr::get(context, scope, translate(node->getFile()),
142 node->getDiscriminator());
143}
144
145DIGlobalVariableAttr
146DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {
147 // Names of DIGlobalVariables can be empty. MLIR models them as null, instead
148 // of empty strings, so this special handling is necessary.
149 auto convertToStringAttr = [&](StringRef name) -> StringAttr {
150 if (name.empty())
151 return {};
152 return StringAttr::get(context, node->getName());
153 };
154 return DIGlobalVariableAttr::get(
155 context, translate(node->getScope()),
156 convertToStringAttr(node->getName()),
157 convertToStringAttr(node->getLinkageName()), translate(node->getFile()),
158 node->getLine(), translate(node->getType()), node->isLocalToUnit(),
159 node->isDefinition(), node->getAlignInBits());
160}
161
162DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) {
163 // Return nullptr if the scope or type is a cyclic dependency.
164 DIScopeAttr scope = translate(node: node->getScope());
165 if (node->getScope() && !scope)
166 return nullptr;
167 return DILocalVariableAttr::get(
168 context, scope, getStringAttrOrNull(node->getRawName()),
169 translate(node->getFile()), node->getLine(), node->getArg(),
170 node->getAlignInBits(), translate(node->getType()));
171}
172
173DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) {
174 return cast<DIScopeAttr>(Val: translate(node: static_cast<llvm::DINode *>(node)));
175}
176
177DIModuleAttr DebugImporter::translateImpl(llvm::DIModule *node) {
178 return DIModuleAttr::get(
179 context, translate(node->getFile()), translate(node->getScope()),
180 getStringAttrOrNull(node->getRawName()),
181 getStringAttrOrNull(node->getRawConfigurationMacros()),
182 getStringAttrOrNull(node->getRawIncludePath()),
183 getStringAttrOrNull(node->getRawAPINotesFile()), node->getLineNo(),
184 node->getIsDecl());
185}
186
187DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) {
188 return DINamespaceAttr::get(context, getStringAttrOrNull(node->getRawName()),
189 translate(node->getScope()),
190 node->getExportSymbols());
191}
192
193DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) {
194 // Only definitions require a distinct identifier.
195 mlir::DistinctAttr id;
196 if (node->isDistinct())
197 id = getOrCreateDistinctID(node);
198 // Return nullptr if the scope or type is invalid.
199 DIScopeAttr scope = translate(node: node->getScope());
200 if (node->getScope() && !scope)
201 return nullptr;
202 std::optional<DISubprogramFlags> subprogramFlags =
203 symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags());
204 assert(subprogramFlags && "expected valid subprogram flags");
205 DISubroutineTypeAttr type = translate(node->getType());
206 if (node->getType() && !type)
207 return nullptr;
208 return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope,
209 getStringAttrOrNull(node->getRawName()),
210 getStringAttrOrNull(node->getRawLinkageName()),
211 translate(node->getFile()), node->getLine(),
212 node->getScopeLine(), *subprogramFlags, type);
213}
214
215DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) {
216 auto getIntegerAttrOrNull = [&](llvm::DISubrange::BoundType data) {
217 if (auto *constInt = llvm::dyn_cast_or_null<llvm::ConstantInt *>(data))
218 return IntegerAttr::get(IntegerType::get(context, 64),
219 constInt->getSExtValue());
220 return IntegerAttr();
221 };
222 IntegerAttr count = getIntegerAttrOrNull(node->getCount());
223 IntegerAttr upperBound = getIntegerAttrOrNull(node->getUpperBound());
224 // Either count or the upper bound needs to be present. Otherwise, the
225 // metadata is invalid. The conversion might fail due to unsupported DI nodes.
226 if (!count && !upperBound)
227 return {};
228 return DISubrangeAttr::get(
229 context, count, getIntegerAttrOrNull(node->getLowerBound()), upperBound,
230 getIntegerAttrOrNull(node->getStride()));
231}
232
233DISubroutineTypeAttr
234DebugImporter::translateImpl(llvm::DISubroutineType *node) {
235 SmallVector<DITypeAttr> types;
236 for (llvm::DIType *type : node->getTypeArray()) {
237 if (!type) {
238 // A nullptr entry may appear at the beginning or the end of the
239 // subroutine types list modeling either a void result type or the type of
240 // a variadic argument. Translate the nullptr to an explicit
241 // DINullTypeAttr since the attribute list cannot contain a nullptr entry.
242 types.push_back(DINullTypeAttr::get(context));
243 continue;
244 }
245 types.push_back(Elt: translate(node: type));
246 }
247 // Return nullptr if any of the types is invalid.
248 if (llvm::is_contained(Range&: types, Element: nullptr))
249 return nullptr;
250 return DISubroutineTypeAttr::get(context, node->getCC(), types);
251}
252
253DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) {
254 return cast<DITypeAttr>(Val: translate(node: static_cast<llvm::DINode *>(node)));
255}
256
257DINodeAttr DebugImporter::translate(llvm::DINode *node) {
258 if (!node)
259 return nullptr;
260
261 // Check for a cached instance.
262 if (DINodeAttr attr = nodeToAttr.lookup(Val: node))
263 return attr;
264
265 // Register with the recursive translator. If it can be handled without
266 // recursing into it, return the result immediately.
267 if (DINodeAttr attr = recursionPruner.pruneOrPushTranslationStack(node))
268 return attr;
269
270 auto guard = llvm::make_scope_exit(
271 F: [&]() { recursionPruner.popTranslationStack(node); });
272
273 // Convert the debug metadata if possible.
274 auto translateNode = [this](llvm::DINode *node) -> DINodeAttr {
275 if (auto *casted = dyn_cast<llvm::DIBasicType>(Val: node))
276 return translateImpl(node: casted);
277 if (auto *casted = dyn_cast<llvm::DICompileUnit>(Val: node))
278 return translateImpl(node: casted);
279 if (auto *casted = dyn_cast<llvm::DICompositeType>(Val: node))
280 return translateImpl(node: casted);
281 if (auto *casted = dyn_cast<llvm::DIDerivedType>(Val: node))
282 return translateImpl(node: casted);
283 if (auto *casted = dyn_cast<llvm::DIFile>(Val: node))
284 return translateImpl(node: casted);
285 if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(Val: node))
286 return translateImpl(casted);
287 if (auto *casted = dyn_cast<llvm::DILabel>(Val: node))
288 return translateImpl(casted);
289 if (auto *casted = dyn_cast<llvm::DILexicalBlock>(Val: node))
290 return translateImpl(node: casted);
291 if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(Val: node))
292 return translateImpl(node: casted);
293 if (auto *casted = dyn_cast<llvm::DILocalVariable>(Val: node))
294 return translateImpl(casted);
295 if (auto *casted = dyn_cast<llvm::DIModule>(Val: node))
296 return translateImpl(node: casted);
297 if (auto *casted = dyn_cast<llvm::DINamespace>(Val: node))
298 return translateImpl(node: casted);
299 if (auto *casted = dyn_cast<llvm::DISubprogram>(Val: node))
300 return translateImpl(node: casted);
301 if (auto *casted = dyn_cast<llvm::DISubrange>(Val: node))
302 return translateImpl(casted);
303 if (auto *casted = dyn_cast<llvm::DISubroutineType>(Val: node))
304 return translateImpl(node: casted);
305 return nullptr;
306 };
307 if (DINodeAttr attr = translateNode(node)) {
308 auto [result, isSelfContained] =
309 recursionPruner.finalizeTranslation(node, result: attr);
310 // Only cache fully self-contained nodes.
311 if (isSelfContained)
312 nodeToAttr.try_emplace(Key: node, Args&: result);
313 return result;
314 }
315 return nullptr;
316}
317
318//===----------------------------------------------------------------------===//
319// RecursionPruner
320//===----------------------------------------------------------------------===//
321
322/// Get the `getRecSelf` constructor for the translated type of `node` if its
323/// translated DITypeAttr supports recursion. Otherwise, returns nullptr.
324static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>
325getRecSelfConstructor(llvm::DINode *node) {
326 using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;
327 return TypeSwitch<llvm::DINode *, CtorType>(node)
328 .Case([&](llvm::DICompositeType *) {
329 return CtorType(DICompositeTypeAttr::getRecSelf);
330 })
331 .Default(CtorType());
332}
333
334DINodeAttr DebugImporter::RecursionPruner::pruneOrPushTranslationStack(
335 llvm::DINode *node) {
336 // If the node type is capable of being recursive, check if it's seen
337 // before.
338 auto recSelfCtor = getRecSelfConstructor(node);
339 if (recSelfCtor) {
340 // If a cyclic dependency is detected since the same node is being
341 // traversed twice, emit a recursive self type, and mark the duplicate
342 // node on the translationStack so it can emit a recursive decl type.
343 auto [iter, inserted] = translationStack.try_emplace(Key: node);
344 if (!inserted) {
345 // The original node may have already been assigned a recursive ID from
346 // a different self-reference. Use that if possible.
347 DIRecursiveTypeAttrInterface recSelf = iter->second.recSelf;
348 if (!recSelf) {
349 DistinctAttr recId = nodeToRecId.lookup(Val: node);
350 if (!recId) {
351 recId = DistinctAttr::create(UnitAttr::get(context));
352 nodeToRecId[node] = recId;
353 }
354 recSelf = recSelfCtor(recId);
355 iter->second.recSelf = recSelf;
356 }
357 // Inject the self-ref into the previous layer.
358 translationStack.back().second.unboundSelfRefs.insert(recSelf);
359 return cast<DINodeAttr>(recSelf);
360 }
361 }
362
363 return lookup(node);
364}
365
366std::pair<DINodeAttr, bool>
367DebugImporter::RecursionPruner::finalizeTranslation(llvm::DINode *node,
368 DINodeAttr result) {
369 // If `node` is not a potentially recursive type, it will not be on the
370 // translation stack. Nothing to set in this case.
371 if (translationStack.empty())
372 return {result, true};
373 if (translationStack.back().first != node)
374 return {result, translationStack.back().second.unboundSelfRefs.empty()};
375
376 TranslationState &state = translationStack.back().second;
377
378 // If this node is actually recursive, set the recId onto `result`.
379 if (DIRecursiveTypeAttrInterface recSelf = state.recSelf) {
380 auto recType = cast<DIRecursiveTypeAttrInterface>(result);
381 result = cast<DINodeAttr>(recType.withRecId(recSelf.getRecId()));
382 // Remove this recSelf from the set of unbound selfRefs.
383 state.unboundSelfRefs.erase(recSelf);
384 }
385
386 // Insert the result into our internal cache if it's not self-contained.
387 if (!state.unboundSelfRefs.empty()) {
388 [[maybe_unused]] auto [_, inserted] = dependentCache.try_emplace(
389 Key: node, Args: DependentTranslation{result, state.unboundSelfRefs});
390 assert(inserted && "invalid state: caching the same DINode twice");
391 return {result, false};
392 }
393 return {result, true};
394}
395
396void DebugImporter::RecursionPruner::popTranslationStack(llvm::DINode *node) {
397 // If `node` is not a potentially recursive type, it will not be on the
398 // translation stack. Nothing to handle in this case.
399 if (translationStack.empty() || translationStack.back().first != node)
400 return;
401
402 // At the end of the stack, all unbound self-refs must be resolved already,
403 // and the entire cache should be accounted for.
404 TranslationState &currLayerState = translationStack.back().second;
405 if (translationStack.size() == 1) {
406 assert(currLayerState.unboundSelfRefs.empty() &&
407 "internal error: unbound recursive self reference at top level.");
408 translationStack.pop_back();
409 return;
410 }
411
412 // Copy unboundSelfRefs down to the previous level.
413 TranslationState &nextLayerState = (++translationStack.rbegin())->second;
414 nextLayerState.unboundSelfRefs.insert(currLayerState.unboundSelfRefs.begin(),
415 currLayerState.unboundSelfRefs.end());
416 translationStack.pop_back();
417}
418
419DINodeAttr DebugImporter::RecursionPruner::lookup(llvm::DINode *node) {
420 auto cacheIter = dependentCache.find(Val: node);
421 if (cacheIter == dependentCache.end())
422 return {};
423
424 DependentTranslation &entry = cacheIter->second;
425 if (llvm::set_is_subset(entry.unboundSelfRefs,
426 translationStack.back().second.unboundSelfRefs))
427 return entry.attr;
428
429 // Stale cache entry.
430 dependentCache.erase(I: cacheIter);
431 return {};
432}
433
434//===----------------------------------------------------------------------===//
435// Locations
436//===----------------------------------------------------------------------===//
437
438Location DebugImporter::translateLoc(llvm::DILocation *loc) {
439 if (!loc)
440 return UnknownLoc::get(context);
441
442 // Get the file location of the instruction.
443 Location result = FileLineColLoc::get(context, loc->getFilename(),
444 loc->getLine(), loc->getColumn());
445
446 // Add scope information.
447 assert(loc->getScope() && "expected non-null scope");
448 result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()),
449 context);
450
451 // Add call site information, if available.
452 if (llvm::DILocation *inlinedAt = loc->getInlinedAt())
453 result = CallSiteLoc::get(result, translateLoc(inlinedAt));
454
455 return result;
456}
457
458DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) {
459 SmallVector<DIExpressionElemAttr> ops;
460
461 // Begin processing the operations.
462 for (const llvm::DIExpression::ExprOperand &op : node->expr_ops()) {
463 SmallVector<uint64_t> operands;
464 operands.reserve(N: op.getNumArgs());
465 for (const auto &i : llvm::seq(Size: op.getNumArgs()))
466 operands.push_back(Elt: op.getArg(I: i));
467 const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands);
468 ops.push_back(attr);
469 }
470 return DIExpressionAttr::get(context, ops);
471}
472
473DIGlobalVariableExpressionAttr DebugImporter::translateGlobalVariableExpression(
474 llvm::DIGlobalVariableExpression *node) {
475 return DIGlobalVariableExpressionAttr::get(
476 context, translate(node->getVariable()),
477 translateExpression(node->getExpression()));
478}
479
480StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) {
481 if (!stringNode)
482 return StringAttr();
483 return StringAttr::get(context, stringNode->getString());
484}
485
486DistinctAttr DebugImporter::getOrCreateDistinctID(llvm::DINode *node) {
487 DistinctAttr &id = nodeToDistinctAttr[node];
488 if (!id)
489 id = DistinctAttr::create(UnitAttr::get(context));
490 return id;
491}
492

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