| //===- DebugImporter.cpp - LLVM to MLIR Debug conversion ------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DebugImporter.h" |
| #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" |
| #include "mlir/IR/Attributes.h" |
| #include "mlir/IR/BuiltinAttributes.h" |
| #include "mlir/IR/Location.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/ScopeExit.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/DebugInfoMetadata.h" |
| #include "llvm/IR/Metadata.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| using namespace mlir; |
| using namespace mlir::LLVM; |
| using namespace mlir::LLVM::detail; |
| |
| Location DebugImporter::translateFuncLocation(llvm::Function *func) { |
| llvm::DISubprogram *subprogram = func->getSubprogram(); |
| if (!subprogram) |
| return UnknownLoc::get(context); |
| |
| // Add a fused location to link the subprogram information. |
| StringAttr funcName = StringAttr::get(context, subprogram->getName()); |
| StringAttr fileName = StringAttr::get(context, subprogram->getFilename()); |
| return FusedLocWith<DISubprogramAttr>::get( |
| {NameLoc::get(funcName), |
| FileLineColLoc::get(fileName, subprogram->getLine(), /*column=*/0)}, |
| translate(subprogram), context); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Attributes |
| //===----------------------------------------------------------------------===// |
| |
| DIBasicTypeAttr DebugImporter::translateImpl(llvm::DIBasicType *node) { |
| return DIBasicTypeAttr::get(context, node->getTag(), node->getName(), |
| node->getSizeInBits(), node->getEncoding()); |
| } |
| |
| DICompileUnitAttr DebugImporter::translateImpl(llvm::DICompileUnit *node) { |
| std::optional<DIEmissionKind> emissionKind = |
| symbolizeDIEmissionKind(node->getEmissionKind()); |
| return DICompileUnitAttr::get( |
| context, getOrCreateDistinctID(node), node->getSourceLanguage(), |
| translate(node->getFile()), getStringAttrOrNull(node->getRawProducer()), |
| node->isOptimized(), emissionKind.value()); |
| } |
| |
| DICompositeTypeAttr DebugImporter::translateImpl(llvm::DICompositeType *node) { |
| std::optional<DIFlags> flags = symbolizeDIFlags(node->getFlags()); |
| SmallVector<DINodeAttr> elements; |
| for (llvm::DINode *element : node->getElements()) { |
| assert(element && "expected a non-null element type"); |
| elements.push_back(translate(element)); |
| } |
| // Drop the elements parameter if any of the elements are invalid. |
| if (llvm::is_contained(elements, nullptr)) |
| elements.clear(); |
| DITypeAttr baseType = translate(node->getBaseType()); |
| // Arrays require a base type, otherwise the debug metadata is considered to |
| // be malformed. |
| if (node->getTag() == llvm::dwarf::DW_TAG_array_type && !baseType) |
| return nullptr; |
| return DICompositeTypeAttr::get( |
| context, node->getTag(), /*recId=*/{}, |
| getStringAttrOrNull(node->getRawName()), translate(node->getFile()), |
| node->getLine(), translate(node->getScope()), baseType, |
| flags.value_or(DIFlags::Zero), node->getSizeInBits(), |
| node->getAlignInBits(), elements); |
| } |
| |
| DIDerivedTypeAttr DebugImporter::translateImpl(llvm::DIDerivedType *node) { |
| // Return nullptr if the base type is invalid. |
| DITypeAttr baseType = translate(node->getBaseType()); |
| if (node->getBaseType() && !baseType) |
| return nullptr; |
| DINodeAttr extraData = |
| translate(dyn_cast_or_null<llvm::DINode>(node->getExtraData())); |
| return DIDerivedTypeAttr::get( |
| context, node->getTag(), getStringAttrOrNull(node->getRawName()), |
| baseType, node->getSizeInBits(), node->getAlignInBits(), |
| node->getOffsetInBits(), extraData); |
| } |
| |
| DIFileAttr DebugImporter::translateImpl(llvm::DIFile *node) { |
| return DIFileAttr::get(context, node->getFilename(), node->getDirectory()); |
| } |
| |
| DILabelAttr DebugImporter::translateImpl(llvm::DILabel *node) { |
| // Return nullptr if the scope or type is a cyclic dependency. |
| DIScopeAttr scope = translate(node->getScope()); |
| if (node->getScope() && !scope) |
| return nullptr; |
| return DILabelAttr::get(context, scope, |
| getStringAttrOrNull(node->getRawName()), |
| translate(node->getFile()), node->getLine()); |
| } |
| |
| DILexicalBlockAttr DebugImporter::translateImpl(llvm::DILexicalBlock *node) { |
| // Return nullptr if the scope or type is a cyclic dependency. |
| DIScopeAttr scope = translate(node->getScope()); |
| if (node->getScope() && !scope) |
| return nullptr; |
| return DILexicalBlockAttr::get(context, scope, translate(node->getFile()), |
| node->getLine(), node->getColumn()); |
| } |
| |
| DILexicalBlockFileAttr |
| DebugImporter::translateImpl(llvm::DILexicalBlockFile *node) { |
| // Return nullptr if the scope or type is a cyclic dependency. |
| DIScopeAttr scope = translate(node->getScope()); |
| if (node->getScope() && !scope) |
| return nullptr; |
| return DILexicalBlockFileAttr::get(context, scope, translate(node->getFile()), |
| node->getDiscriminator()); |
| } |
| |
| DIGlobalVariableAttr |
| DebugImporter::translateImpl(llvm::DIGlobalVariable *node) { |
| // Names of DIGlobalVariables can be empty. MLIR models them as null, instead |
| // of empty strings, so this special handling is necessary. |
| auto convertToStringAttr = [&](StringRef name) -> StringAttr { |
| if (name.empty()) |
| return {}; |
| return StringAttr::get(context, node->getName()); |
| }; |
| return DIGlobalVariableAttr::get( |
| context, translate(node->getScope()), |
| convertToStringAttr(node->getName()), |
| convertToStringAttr(node->getLinkageName()), translate(node->getFile()), |
| node->getLine(), translate(node->getType()), node->isLocalToUnit(), |
| node->isDefinition(), node->getAlignInBits()); |
| } |
| |
| DILocalVariableAttr DebugImporter::translateImpl(llvm::DILocalVariable *node) { |
| // Return nullptr if the scope or type is a cyclic dependency. |
| DIScopeAttr scope = translate(node->getScope()); |
| if (node->getScope() && !scope) |
| return nullptr; |
| return DILocalVariableAttr::get( |
| context, scope, getStringAttrOrNull(node->getRawName()), |
| translate(node->getFile()), node->getLine(), node->getArg(), |
| node->getAlignInBits(), translate(node->getType())); |
| } |
| |
| DIScopeAttr DebugImporter::translateImpl(llvm::DIScope *node) { |
| return cast<DIScopeAttr>(translate(static_cast<llvm::DINode *>(node))); |
| } |
| |
| DIModuleAttr DebugImporter::translateImpl(llvm::DIModule *node) { |
| return DIModuleAttr::get( |
| context, translate(node->getFile()), translate(node->getScope()), |
| getStringAttrOrNull(node->getRawName()), |
| getStringAttrOrNull(node->getRawConfigurationMacros()), |
| getStringAttrOrNull(node->getRawIncludePath()), |
| getStringAttrOrNull(node->getRawAPINotesFile()), node->getLineNo(), |
| node->getIsDecl()); |
| } |
| |
| DINamespaceAttr DebugImporter::translateImpl(llvm::DINamespace *node) { |
| return DINamespaceAttr::get(context, getStringAttrOrNull(node->getRawName()), |
| translate(node->getScope()), |
| node->getExportSymbols()); |
| } |
| |
| DISubprogramAttr DebugImporter::translateImpl(llvm::DISubprogram *node) { |
| // Only definitions require a distinct identifier. |
| mlir::DistinctAttr id; |
| if (node->isDistinct()) |
| id = getOrCreateDistinctID(node); |
| // Return nullptr if the scope or type is invalid. |
| DIScopeAttr scope = translate(node->getScope()); |
| if (node->getScope() && !scope) |
| return nullptr; |
| std::optional<DISubprogramFlags> subprogramFlags = |
| symbolizeDISubprogramFlags(node->getSubprogram()->getSPFlags()); |
| assert(subprogramFlags && "expected valid subprogram flags"); |
| DISubroutineTypeAttr type = translate(node->getType()); |
| if (node->getType() && !type) |
| return nullptr; |
| return DISubprogramAttr::get(context, id, translate(node->getUnit()), scope, |
| getStringAttrOrNull(node->getRawName()), |
| getStringAttrOrNull(node->getRawLinkageName()), |
| translate(node->getFile()), node->getLine(), |
| node->getScopeLine(), *subprogramFlags, type); |
| } |
| |
| DISubrangeAttr DebugImporter::translateImpl(llvm::DISubrange *node) { |
| auto getIntegerAttrOrNull = [&](llvm::DISubrange::BoundType data) { |
| if (auto *constInt = llvm::dyn_cast_or_null<llvm::ConstantInt *>(data)) |
| return IntegerAttr::get(IntegerType::get(context, 64), |
| constInt->getSExtValue()); |
| return IntegerAttr(); |
| }; |
| IntegerAttr count = getIntegerAttrOrNull(node->getCount()); |
| IntegerAttr upperBound = getIntegerAttrOrNull(node->getUpperBound()); |
| // Either count or the upper bound needs to be present. Otherwise, the |
| // metadata is invalid. The conversion might fail due to unsupported DI nodes. |
| if (!count && !upperBound) |
| return {}; |
| return DISubrangeAttr::get( |
| context, count, getIntegerAttrOrNull(node->getLowerBound()), upperBound, |
| getIntegerAttrOrNull(node->getStride())); |
| } |
| |
| DISubroutineTypeAttr |
| DebugImporter::translateImpl(llvm::DISubroutineType *node) { |
| SmallVector<DITypeAttr> types; |
| for (llvm::DIType *type : node->getTypeArray()) { |
| if (!type) { |
| // A nullptr entry may appear at the beginning or the end of the |
| // subroutine types list modeling either a void result type or the type of |
| // a variadic argument. Translate the nullptr to an explicit |
| // DINullTypeAttr since the attribute list cannot contain a nullptr entry. |
| types.push_back(DINullTypeAttr::get(context)); |
| continue; |
| } |
| types.push_back(translate(type)); |
| } |
| // Return nullptr if any of the types is invalid. |
| if (llvm::is_contained(types, nullptr)) |
| return nullptr; |
| return DISubroutineTypeAttr::get(context, node->getCC(), types); |
| } |
| |
| DITypeAttr DebugImporter::translateImpl(llvm::DIType *node) { |
| return cast<DITypeAttr>(translate(static_cast<llvm::DINode *>(node))); |
| } |
| |
| DINodeAttr DebugImporter::translate(llvm::DINode *node) { |
| if (!node) |
| return nullptr; |
| |
| // Check for a cached instance. |
| if (DINodeAttr attr = nodeToAttr.lookup(node)) |
| return attr; |
| |
| // If the node type is capable of being recursive, check if it's seen before. |
| auto recSelfCtor = getRecSelfConstructor(node); |
| if (recSelfCtor) { |
| // If a cyclic dependency is detected since the same node is being traversed |
| // twice, emit a recursive self type, and mark the duplicate node on the |
| // translationStack so it can emit a recursive decl type. |
| auto [iter, inserted] = translationStack.try_emplace(node, nullptr); |
| if (!inserted) { |
| // The original node may have already been assigned a recursive ID from |
| // a different self-reference. Use that if possible. |
| DistinctAttr recId = iter->second; |
| if (!recId) { |
| recId = DistinctAttr::create(UnitAttr::get(context)); |
| iter->second = recId; |
| } |
| unboundRecursiveSelfRefs.back().insert(recId); |
| return cast<DINodeAttr>(recSelfCtor(recId)); |
| } |
| } |
| |
| unboundRecursiveSelfRefs.emplace_back(); |
| |
| auto guard = llvm::make_scope_exit([&]() { |
| if (recSelfCtor) |
| translationStack.pop_back(); |
| |
| // Copy unboundRecursiveSelfRefs down to the previous level. |
| if (unboundRecursiveSelfRefs.size() == 1) |
| assert(unboundRecursiveSelfRefs.back().empty() && |
| "internal error: unbound recursive self reference at top level."); |
| else |
| unboundRecursiveSelfRefs[unboundRecursiveSelfRefs.size() - 2].insert( |
| unboundRecursiveSelfRefs.back().begin(), |
| unboundRecursiveSelfRefs.back().end()); |
| unboundRecursiveSelfRefs.pop_back(); |
| }); |
| |
| // Convert the debug metadata if possible. |
| auto translateNode = [this](llvm::DINode *node) -> DINodeAttr { |
| if (auto *casted = dyn_cast<llvm::DIBasicType>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DICompileUnit>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DICompositeType>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DIDerivedType>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DIFile>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DIGlobalVariable>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DILabel>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DILexicalBlock>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DILexicalBlockFile>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DILocalVariable>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DIModule>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DINamespace>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DISubprogram>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DISubrange>(node)) |
| return translateImpl(casted); |
| if (auto *casted = dyn_cast<llvm::DISubroutineType>(node)) |
| return translateImpl(casted); |
| return nullptr; |
| }; |
| if (DINodeAttr attr = translateNode(node)) { |
| // If this node was marked as recursive, set its recId. |
| if (auto recType = dyn_cast<DIRecursiveTypeAttrInterface>(attr)) { |
| if (DistinctAttr recId = translationStack.lookup(node)) { |
| attr = cast<DINodeAttr>(recType.withRecId(recId)); |
| // Remove the unbound recursive ID from the set of unbound self |
| // references in the translation stack. |
| unboundRecursiveSelfRefs.back().erase(recId); |
| } |
| } |
| |
| // Only cache fully self-contained nodes. |
| if (unboundRecursiveSelfRefs.back().empty()) |
| nodeToAttr.try_emplace(node, attr); |
| return attr; |
| } |
| return nullptr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Locations |
| //===----------------------------------------------------------------------===// |
| |
| Location DebugImporter::translateLoc(llvm::DILocation *loc) { |
| if (!loc) |
| return UnknownLoc::get(context); |
| |
| // Get the file location of the instruction. |
| Location result = FileLineColLoc::get(context, loc->getFilename(), |
| loc->getLine(), loc->getColumn()); |
| |
| // Add scope information. |
| assert(loc->getScope() && "expected non-null scope"); |
| result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()), |
| context); |
| |
| // Add call site information, if available. |
| if (llvm::DILocation *inlinedAt = loc->getInlinedAt()) |
| result = CallSiteLoc::get(result, translateLoc(inlinedAt)); |
| |
| return result; |
| } |
| |
| DIExpressionAttr DebugImporter::translateExpression(llvm::DIExpression *node) { |
| SmallVector<DIExpressionElemAttr> ops; |
| |
| // Begin processing the operations. |
| for (const llvm::DIExpression::ExprOperand &op : node->expr_ops()) { |
| SmallVector<uint64_t> operands; |
| operands.reserve(op.getNumArgs()); |
| for (const auto &i : llvm::seq(op.getNumArgs())) |
| operands.push_back(op.getArg(i)); |
| const auto attr = DIExpressionElemAttr::get(context, op.getOp(), operands); |
| ops.push_back(attr); |
| } |
| return DIExpressionAttr::get(context, ops); |
| } |
| |
| DIGlobalVariableExpressionAttr DebugImporter::translateGlobalVariableExpression( |
| llvm::DIGlobalVariableExpression *node) { |
| return DIGlobalVariableExpressionAttr::get( |
| context, translate(node->getVariable()), |
| translateExpression(node->getExpression())); |
| } |
| |
| StringAttr DebugImporter::getStringAttrOrNull(llvm::MDString *stringNode) { |
| if (!stringNode) |
| return StringAttr(); |
| return StringAttr::get(context, stringNode->getString()); |
| } |
| |
| DistinctAttr DebugImporter::getOrCreateDistinctID(llvm::DINode *node) { |
| DistinctAttr &id = nodeToDistinctAttr[node]; |
| if (!id) |
| id = DistinctAttr::create(UnitAttr::get(context)); |
| return id; |
| } |
| |
| function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)> |
| DebugImporter::getRecSelfConstructor(llvm::DINode *node) { |
| using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>; |
| return TypeSwitch<llvm::DINode *, CtorType>(node) |
| .Case([&](llvm::DICompositeType *concreteNode) { |
| return CtorType(DICompositeTypeAttr::getRecSelf); |
| }) |
| .Default(CtorType()); |
| } |