518 lines · cpp
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-exception6//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/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 31Location DebugImporter::translateFuncLocation(llvm::Function *func) {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());38 return FusedLocWith<DISubprogramAttr>::get(39 {FileLineColLoc::get(fileName, subprogram->getLine(), /*column=*/0)},40 translate(subprogram), context);41}42 43//===----------------------------------------------------------------------===//44// Attributes45//===----------------------------------------------------------------------===//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());69 SmallVector<DINodeAttr> elements;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 to84 // 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 142DILexicalBlockFileAttr143DebugImporter::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 152DIGlobalVariableAttr153DebugImporter::translateImpl(llvm::DIGlobalVariable *node) {154 // Names of DIGlobalVariables can be empty. MLIR models them as null, instead155 // 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 205DIImportedEntityAttr206DebugImporter::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 using246 // 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, the292 // 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 307DIGenericSubrangeAttr308DebugImporter::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, the329 // metadata is invalid.330 if (!count && !upperBound)331 return {};332 return DIGenericSubrangeAttr::get(context, count, lowerBound, upperBound,333 stride);334}335 336DISubroutineTypeAttr337DebugImporter::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 the342 // subroutine types list modeling either a void result type or the type of343 // a variadic argument. Translate the nullptr to an explicit344 // 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 360DINodeAttr DebugImporter::translate(llvm::DINode *node) {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 the413 // 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 its427/// translated DITypeAttr supports recursion. Otherwise, returns nullptr.428static function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>429getRecSelfConstructor(llvm::DINode *node) {430 using CtorType = function_ref<DIRecursiveTypeAttrInterface(DistinctAttr)>;431 return TypeSwitch<llvm::DINode *, CtorType>(node)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 from447 // 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// Locations459//===----------------------------------------------------------------------===//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");471 result = FusedLocWith<DIScopeAttr>::get({result}, translate(loc->getScope()),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 485 SmallVector<DIExpressionElemAttr> ops;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 499DIGlobalVariableExpressionAttr DebugImporter::translateGlobalVariableExpression(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}518