| //===- DebugTranslation.cpp - MLIR to LLVM 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 "DebugTranslation.h" |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| #include "llvm/IR/Metadata.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Path.h" |
| |
| using namespace mlir; |
| using namespace mlir::LLVM; |
| using namespace mlir::LLVM::detail; |
| |
| /// A utility walker that interrupts if the operation has valid debug |
| /// information. |
| static WalkResult interruptIfValidLocation(Operation *op) { |
| return isa<UnknownLoc>(op->getLoc()) ? WalkResult::advance() |
| : WalkResult::interrupt(); |
| } |
| |
| DebugTranslation::DebugTranslation(Operation *module, llvm::Module &llvmModule) |
| : debugEmissionIsEnabled(false), llvmModule(llvmModule), |
| llvmCtx(llvmModule.getContext()) { |
| // If the module has no location information, there is nothing to do. |
| if (!module->walk(interruptIfValidLocation).wasInterrupted()) |
| return; |
| debugEmissionIsEnabled = true; |
| } |
| |
| static constexpr StringRef kDebugVersionKey = "Debug Info Version"; |
| static constexpr StringRef kCodeViewKey = "CodeView"; |
| |
| void DebugTranslation::addModuleFlagsIfNotPresent() { |
| // TODO: The version information should be encoded on the LLVM module itself, |
| // not implicitly set here. |
| |
| // Mark this module as having debug information. |
| if (!llvmModule.getModuleFlag(kDebugVersionKey)) |
| llvmModule.addModuleFlag(llvm::Module::Warning, kDebugVersionKey, |
| llvm::DEBUG_METADATA_VERSION); |
| |
| const llvm::Triple &targetTriple = llvmModule.getTargetTriple(); |
| if (targetTriple.isKnownWindowsMSVCEnvironment()) { |
| // Dwarf debugging files will be generated by default, unless "CodeView" |
| // is set explicitly. Windows/MSVC should use CodeView instead. |
| if (!llvmModule.getModuleFlag(kCodeViewKey)) |
| llvmModule.addModuleFlag(llvm::Module::Warning, kCodeViewKey, 1); |
| } |
| } |
| |
| /// Translate the debug information for the given function. |
| void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) { |
| if (!debugEmissionIsEnabled) |
| return; |
| |
| // Look for a sub program attached to the function. |
| auto spLoc = |
| func.getLoc()->findInstanceOf<FusedLocWith<LLVM::DISubprogramAttr>>(); |
| if (!spLoc) |
| return; |
| llvmFunc.setSubprogram(translate(spLoc.getMetadata())); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Attributes |
| //===----------------------------------------------------------------------===// |
| |
| llvm::DIType *DebugTranslation::translateImpl(DINullTypeAttr attr) { |
| // A DINullTypeAttr at the beginning of the subroutine types list models |
| // a void result type. If it is at the end, it models a variadic function. |
| // Translate the explicit DINullTypeAttr to a nullptr since LLVM IR metadata |
| // does not have an explicit void result type nor a variadic type |
| // representation. |
| return nullptr; |
| } |
| |
| llvm::DIExpression * |
| DebugTranslation::getExpressionAttrOrNull(DIExpressionAttr attr) { |
| if (!attr) |
| return nullptr; |
| return translateExpression(attr); |
| } |
| |
| llvm::MDString *DebugTranslation::getMDStringOrNull(StringAttr stringAttr) { |
| if (!stringAttr || stringAttr.empty()) |
| return nullptr; |
| return llvm::MDString::get(llvmCtx, stringAttr); |
| } |
| |
| llvm::MDTuple * |
| DebugTranslation::getMDTupleOrNull(ArrayRef<DINodeAttr> elements) { |
| if (elements.empty()) |
| return nullptr; |
| SmallVector<llvm::Metadata *> llvmElements = llvm::to_vector( |
| llvm::map_range(elements, [&](DINodeAttr attr) -> llvm::Metadata * { |
| if (DIAnnotationAttr annAttr = dyn_cast<DIAnnotationAttr>(attr)) { |
| llvm::Metadata *ops[2] = { |
| llvm::MDString::get(llvmCtx, annAttr.getName()), |
| llvm::MDString::get(llvmCtx, annAttr.getValue())}; |
| return llvm::MDNode::get(llvmCtx, ops); |
| } |
| return translate(attr); |
| })); |
| return llvm::MDNode::get(llvmCtx, llvmElements); |
| } |
| |
| llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) { |
| return llvm::DIBasicType::get( |
| llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
| attr.getSizeInBits(), |
| /*AlignInBits=*/0, attr.getEncoding(), llvm::DINode::FlagZero); |
| } |
| |
| llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) { |
| llvm::DIBuilder builder(llvmModule); |
| return builder.createCompileUnit( |
| attr.getSourceLanguage(), translate(attr.getFile()), |
| attr.getProducer() ? attr.getProducer().getValue() : "", |
| attr.getIsOptimized(), |
| /*Flags=*/"", /*RV=*/0, /*SplitName=*/{}, |
| static_cast<llvm::DICompileUnit::DebugEmissionKind>( |
| attr.getEmissionKind()), |
| 0, true, false, |
| static_cast<llvm::DICompileUnit::DebugNameTableKind>( |
| attr.getNameTableKind())); |
| } |
| |
| /// Returns a new `DINodeT` that is either distinct or not, depending on |
| /// `isDistinct`. |
| template <class DINodeT, class... Ts> |
| static DINodeT *getDistinctOrUnique(bool isDistinct, Ts &&...args) { |
| if (isDistinct) |
| return DINodeT::getDistinct(std::forward<Ts>(args)...); |
| return DINodeT::get(std::forward<Ts>(args)...); |
| } |
| |
| llvm::TempDICompositeType |
| DebugTranslation::translateTemporaryImpl(DICompositeTypeAttr attr) { |
| return llvm::DICompositeType::getTemporary( |
| llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), nullptr, |
| attr.getLine(), nullptr, nullptr, attr.getSizeInBits(), |
| attr.getAlignInBits(), |
| /*OffsetInBits=*/0, |
| /*Flags=*/static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
| /*Elements=*/nullptr, /*RuntimeLang=*/0, /*EnumKind=*/std::nullopt, |
| /*VTableHolder=*/nullptr); |
| } |
| |
| llvm::TempDISubprogram |
| DebugTranslation::translateTemporaryImpl(DISubprogramAttr attr) { |
| return llvm::DISubprogram::getTemporary( |
| llvmCtx, /*Scope=*/nullptr, /*Name=*/{}, /*LinkageName=*/{}, |
| /*File=*/nullptr, attr.getLine(), /*Type=*/nullptr, |
| /*ScopeLine=*/0, /*ContainingType=*/nullptr, /*VirtualIndex=*/0, |
| /*ThisAdjustment=*/0, llvm::DINode::FlagZero, |
| static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()), |
| /*Unit=*/nullptr); |
| } |
| |
| llvm::DICompositeType * |
| DebugTranslation::translateImpl(DICompositeTypeAttr attr) { |
| // TODO: Use distinct attributes to model this, once they have landed. |
| // Depending on the tag, composite types must be distinct. |
| bool isDistinct = false; |
| switch (attr.getTag()) { |
| case llvm::dwarf::DW_TAG_class_type: |
| case llvm::dwarf::DW_TAG_enumeration_type: |
| case llvm::dwarf::DW_TAG_structure_type: |
| case llvm::dwarf::DW_TAG_union_type: |
| isDistinct = true; |
| } |
| |
| return getDistinctOrUnique<llvm::DICompositeType>( |
| isDistinct, llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
| translate(attr.getFile()), attr.getLine(), translate(attr.getScope()), |
| translate(attr.getBaseType()), attr.getSizeInBits(), |
| attr.getAlignInBits(), |
| /*OffsetInBits=*/0, |
| /*Flags=*/static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
| getMDTupleOrNull(attr.getElements()), |
| /*RuntimeLang=*/0, /*EnumKind*/ std::nullopt, /*VTableHolder=*/nullptr, |
| /*TemplateParams=*/nullptr, /*Identifier=*/nullptr, |
| /*Discriminator=*/nullptr, |
| getExpressionAttrOrNull(attr.getDataLocation()), |
| getExpressionAttrOrNull(attr.getAssociated()), |
| getExpressionAttrOrNull(attr.getAllocated()), |
| getExpressionAttrOrNull(attr.getRank())); |
| } |
| |
| llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) { |
| return llvm::DIDerivedType::get( |
| llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
| /*File=*/nullptr, /*Line=*/0, |
| /*Scope=*/nullptr, translate(attr.getBaseType()), attr.getSizeInBits(), |
| attr.getAlignInBits(), attr.getOffsetInBits(), |
| attr.getDwarfAddressSpace(), /*PtrAuthData=*/std::nullopt, |
| /*Flags=*/llvm::DINode::FlagZero, translate(attr.getExtraData())); |
| } |
| |
| llvm::DIStringType *DebugTranslation::translateImpl(DIStringTypeAttr attr) { |
| return llvm::DIStringType::get( |
| llvmCtx, attr.getTag(), getMDStringOrNull(attr.getName()), |
| translate(attr.getStringLength()), |
| getExpressionAttrOrNull(attr.getStringLengthExp()), |
| getExpressionAttrOrNull(attr.getStringLocationExp()), |
| attr.getSizeInBits(), attr.getAlignInBits(), attr.getEncoding()); |
| } |
| |
| llvm::DIFile *DebugTranslation::translateImpl(DIFileAttr attr) { |
| return llvm::DIFile::get(llvmCtx, getMDStringOrNull(attr.getName()), |
| getMDStringOrNull(attr.getDirectory())); |
| } |
| |
| llvm::DILabel *DebugTranslation::translateImpl(DILabelAttr attr) { |
| return llvm::DILabel::get(llvmCtx, translate(attr.getScope()), |
| getMDStringOrNull(attr.getName()), |
| translate(attr.getFile()), attr.getLine()); |
| } |
| |
| llvm::DILexicalBlock *DebugTranslation::translateImpl(DILexicalBlockAttr attr) { |
| return llvm::DILexicalBlock::getDistinct(llvmCtx, translate(attr.getScope()), |
| translate(attr.getFile()), |
| attr.getLine(), attr.getColumn()); |
| } |
| |
| llvm::DILexicalBlockFile * |
| DebugTranslation::translateImpl(DILexicalBlockFileAttr attr) { |
| return llvm::DILexicalBlockFile::getDistinct( |
| llvmCtx, translate(attr.getScope()), translate(attr.getFile()), |
| attr.getDiscriminator()); |
| } |
| |
| llvm::DILocalScope *DebugTranslation::translateImpl(DILocalScopeAttr attr) { |
| return cast<llvm::DILocalScope>(translate(DINodeAttr(attr))); |
| } |
| |
| llvm::DIVariable *DebugTranslation::translateImpl(DIVariableAttr attr) { |
| return cast<llvm::DIVariable>(translate(DINodeAttr(attr))); |
| } |
| |
| llvm::DILocalVariable * |
| DebugTranslation::translateImpl(DILocalVariableAttr attr) { |
| return llvm::DILocalVariable::get( |
| llvmCtx, translate(attr.getScope()), getMDStringOrNull(attr.getName()), |
| translate(attr.getFile()), attr.getLine(), translate(attr.getType()), |
| attr.getArg(), static_cast<llvm::DINode::DIFlags>(attr.getFlags()), |
| attr.getAlignInBits(), |
| /*Annotations=*/nullptr); |
| } |
| |
| llvm::DIGlobalVariable * |
| DebugTranslation::translateImpl(DIGlobalVariableAttr attr) { |
| return llvm::DIGlobalVariable::getDistinct( |
| llvmCtx, translate(attr.getScope()), getMDStringOrNull(attr.getName()), |
| getMDStringOrNull(attr.getLinkageName()), translate(attr.getFile()), |
| attr.getLine(), translate(attr.getType()), attr.getIsLocalToUnit(), |
| attr.getIsDefined(), nullptr, nullptr, attr.getAlignInBits(), nullptr); |
| } |
| |
| llvm::DINode * |
| DebugTranslation::translateRecursive(DIRecursiveTypeAttrInterface attr) { |
| DistinctAttr recursiveId = attr.getRecId(); |
| if (auto *iter = recursiveNodeMap.find(recursiveId); |
| iter != recursiveNodeMap.end()) { |
| return iter->second; |
| } |
| assert(!attr.getIsRecSelf() && "unbound DI recursive self reference"); |
| |
| auto setRecursivePlaceholder = [&](llvm::DINode *placeholder) { |
| recursiveNodeMap.try_emplace(recursiveId, placeholder); |
| }; |
| |
| llvm::DINode *result = |
| TypeSwitch<DIRecursiveTypeAttrInterface, llvm::DINode *>(attr) |
| .Case<DICompositeTypeAttr>([&](auto attr) { |
| auto temporary = translateTemporaryImpl(attr); |
| setRecursivePlaceholder(temporary.get()); |
| // Must call `translateImpl` directly instead of `translate` to |
| // avoid handling the recursive interface again. |
| auto *concrete = translateImpl(attr); |
| temporary->replaceAllUsesWith(concrete); |
| return concrete; |
| }) |
| .Case<DISubprogramAttr>([&](auto attr) { |
| auto temporary = translateTemporaryImpl(attr); |
| setRecursivePlaceholder(temporary.get()); |
| // Must call `translateImpl` directly instead of `translate` to |
| // avoid handling the recursive interface again. |
| auto *concrete = translateImpl(attr); |
| temporary->replaceAllUsesWith(concrete); |
| return concrete; |
| }); |
| |
| assert(recursiveNodeMap.back().first == recursiveId && |
| "internal inconsistency: unexpected recursive translation stack"); |
| recursiveNodeMap.pop_back(); |
| |
| return result; |
| } |
| |
| llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) { |
| return cast<llvm::DIScope>(translate(DINodeAttr(attr))); |
| } |
| |
| llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) { |
| if (auto iter = distinctAttrToNode.find(attr.getId()); |
| iter != distinctAttrToNode.end()) |
| return cast<llvm::DISubprogram>(iter->second); |
| |
| llvm::DIScope *scope = translate(attr.getScope()); |
| llvm::DIFile *file = translate(attr.getFile()); |
| llvm::DIType *type = translate(attr.getType()); |
| llvm::DICompileUnit *compileUnit = translate(attr.getCompileUnit()); |
| |
| // Check again after recursive calls in case this distinct node recurses back |
| // to itself. |
| if (auto iter = distinctAttrToNode.find(attr.getId()); |
| iter != distinctAttrToNode.end()) |
| return cast<llvm::DISubprogram>(iter->second); |
| |
| bool isDefinition = static_cast<bool>(attr.getSubprogramFlags() & |
| LLVM::DISubprogramFlags::Definition); |
| |
| llvm::DISubprogram *node = getDistinctOrUnique<llvm::DISubprogram>( |
| isDefinition, llvmCtx, scope, getMDStringOrNull(attr.getName()), |
| getMDStringOrNull(attr.getLinkageName()), file, attr.getLine(), type, |
| attr.getScopeLine(), |
| /*ContainingType=*/nullptr, /*VirtualIndex=*/0, |
| /*ThisAdjustment=*/0, llvm::DINode::FlagZero, |
| static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()), |
| compileUnit, /*TemplateParams=*/nullptr, /*Declaration=*/nullptr, |
| getMDTupleOrNull(attr.getRetainedNodes()), nullptr, |
| getMDTupleOrNull(attr.getAnnotations())); |
| if (attr.getId()) |
| distinctAttrToNode.try_emplace(attr.getId(), node); |
| return node; |
| } |
| |
| llvm::DIModule *DebugTranslation::translateImpl(DIModuleAttr attr) { |
| return llvm::DIModule::get( |
| llvmCtx, translate(attr.getFile()), translate(attr.getScope()), |
| getMDStringOrNull(attr.getName()), |
| getMDStringOrNull(attr.getConfigMacros()), |
| getMDStringOrNull(attr.getIncludePath()), |
| getMDStringOrNull(attr.getApinotes()), attr.getLine(), attr.getIsDecl()); |
| } |
| |
| llvm::DINamespace *DebugTranslation::translateImpl(DINamespaceAttr attr) { |
| return llvm::DINamespace::get(llvmCtx, translate(attr.getScope()), |
| getMDStringOrNull(attr.getName()), |
| attr.getExportSymbols()); |
| } |
| |
| llvm::DIImportedEntity * |
| DebugTranslation::translateImpl(DIImportedEntityAttr attr) { |
| return llvm::DIImportedEntity::get( |
| llvmCtx, attr.getTag(), translate(attr.getScope()), |
| translate(attr.getEntity()), translate(attr.getFile()), attr.getLine(), |
| getMDStringOrNull(attr.getName()), getMDTupleOrNull(attr.getElements())); |
| } |
| |
| llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) { |
| auto getMetadataOrNull = [&](Attribute attr) -> llvm::Metadata * { |
| if (!attr) |
| return nullptr; |
| |
| llvm::Metadata *metadata = |
| llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr) |
| .Case([&](IntegerAttr intAttr) { |
| return llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( |
| llvm::Type::getInt64Ty(llvmCtx), intAttr.getInt())); |
| }) |
| .Case([&](LLVM::DIExpressionAttr expr) { |
| return translateExpression(expr); |
| }) |
| .Case([&](LLVM::DILocalVariableAttr local) { |
| return translate(local); |
| }) |
| .Case<>([&](LLVM::DIGlobalVariableAttr global) { |
| return translate(global); |
| }) |
| .Default([&](Attribute attr) { return nullptr; }); |
| return metadata; |
| }; |
| return llvm::DISubrange::get(llvmCtx, getMetadataOrNull(attr.getCount()), |
| getMetadataOrNull(attr.getLowerBound()), |
| getMetadataOrNull(attr.getUpperBound()), |
| getMetadataOrNull(attr.getStride())); |
| } |
| |
| llvm::DICommonBlock *DebugTranslation::translateImpl(DICommonBlockAttr attr) { |
| return llvm::DICommonBlock::get(llvmCtx, translate(attr.getScope()), |
| translate(attr.getDecl()), |
| getMDStringOrNull(attr.getName()), |
| translate(attr.getFile()), attr.getLine()); |
| } |
| |
| llvm::DIGenericSubrange * |
| DebugTranslation::translateImpl(DIGenericSubrangeAttr attr) { |
| auto getMetadataOrNull = [&](Attribute attr) -> llvm::Metadata * { |
| if (!attr) |
| return nullptr; |
| |
| llvm::Metadata *metadata = |
| llvm::TypeSwitch<Attribute, llvm::Metadata *>(attr) |
| .Case([&](LLVM::DIExpressionAttr expr) { |
| return translateExpression(expr); |
| }) |
| .Case([&](LLVM::DILocalVariableAttr local) { |
| return translate(local); |
| }) |
| .Case<>([&](LLVM::DIGlobalVariableAttr global) { |
| return translate(global); |
| }) |
| .Default([&](Attribute attr) { return nullptr; }); |
| return metadata; |
| }; |
| return llvm::DIGenericSubrange::get(llvmCtx, |
| getMetadataOrNull(attr.getCount()), |
| getMetadataOrNull(attr.getLowerBound()), |
| getMetadataOrNull(attr.getUpperBound()), |
| getMetadataOrNull(attr.getStride())); |
| } |
| |
| llvm::DISubroutineType * |
| DebugTranslation::translateImpl(DISubroutineTypeAttr attr) { |
| // Concatenate the result and argument types into a single array. |
| SmallVector<llvm::Metadata *> types; |
| for (DITypeAttr type : attr.getTypes()) |
| types.push_back(translate(type)); |
| return llvm::DISubroutineType::get( |
| llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(), |
| llvm::DITypeRefArray(llvm::MDNode::get(llvmCtx, types))); |
| } |
| |
| llvm::DIType *DebugTranslation::translateImpl(DITypeAttr attr) { |
| return cast<llvm::DIType>(translate(DINodeAttr(attr))); |
| } |
| |
| llvm::DINode *DebugTranslation::translate(DINodeAttr attr) { |
| if (!attr) |
| return nullptr; |
| // Check for a cached instance. |
| if (llvm::DINode *node = attrToNode.lookup(attr)) |
| return node; |
| |
| llvm::DINode *node = nullptr; |
| // Recursive types go through a dedicated handler. All other types are |
| // dispatched directly to their specific handlers. |
| if (auto recTypeAttr = dyn_cast<DIRecursiveTypeAttrInterface>(attr)) |
| if (recTypeAttr.getRecId()) |
| node = translateRecursive(recTypeAttr); |
| |
| if (!node) |
| node = TypeSwitch<DINodeAttr, llvm::DINode *>(attr) |
| .Case<DIBasicTypeAttr, DICommonBlockAttr, DICompileUnitAttr, |
| DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr, |
| DIGenericSubrangeAttr, DIGlobalVariableAttr, |
| DIImportedEntityAttr, DILabelAttr, DILexicalBlockAttr, |
| DILexicalBlockFileAttr, DILocalVariableAttr, DIModuleAttr, |
| DINamespaceAttr, DINullTypeAttr, DIStringTypeAttr, |
| DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>( |
| [&](auto attr) { return translateImpl(attr); }); |
| |
| if (node && !node->isTemporary()) |
| attrToNode.insert({attr, node}); |
| return node; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Locations |
| //===----------------------------------------------------------------------===// |
| |
| /// Translate the given location to an llvm debug location. |
| llvm::DILocation *DebugTranslation::translateLoc(Location loc, |
| llvm::DILocalScope *scope) { |
| if (!debugEmissionIsEnabled) |
| return nullptr; |
| return translateLoc(loc, scope, /*inlinedAt=*/nullptr); |
| } |
| |
| llvm::DIExpression * |
| DebugTranslation::translateExpression(LLVM::DIExpressionAttr attr) { |
| SmallVector<uint64_t, 1> ops; |
| if (attr) { |
| // Append operations their operands to the list. |
| for (const DIExpressionElemAttr &op : attr.getOperations()) { |
| ops.push_back(op.getOpcode()); |
| append_range(ops, op.getArguments()); |
| } |
| } |
| return llvm::DIExpression::get(llvmCtx, ops); |
| } |
| |
| llvm::DIGlobalVariableExpression * |
| DebugTranslation::translateGlobalVariableExpression( |
| LLVM::DIGlobalVariableExpressionAttr attr) { |
| return llvm::DIGlobalVariableExpression::get( |
| llvmCtx, translate(attr.getVar()), translateExpression(attr.getExpr())); |
| } |
| |
| /// Translate the given location to an llvm DebugLoc. |
| llvm::DILocation *DebugTranslation::translateLoc(Location loc, |
| llvm::DILocalScope *scope, |
| llvm::DILocation *inlinedAt) { |
| // LLVM doesn't have a representation for unknown. |
| if (isa<UnknownLoc>(loc)) |
| return nullptr; |
| |
| // Check for a cached instance. |
| auto existingIt = locationToLoc.find(std::make_tuple(loc, scope, inlinedAt)); |
| if (existingIt != locationToLoc.end()) |
| return existingIt->second; |
| |
| llvm::DILocation *llvmLoc = nullptr; |
| if (auto callLoc = dyn_cast<CallSiteLoc>(loc)) { |
| // For callsites, the caller is fed as the inlinedAt for the callee. |
| auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt); |
| // If the caller scope is not translatable, the overall callsite cannot be |
| // represented in LLVM (the callee scope may not match the parent function). |
| if (!callerLoc) { |
| // If there is an inlinedAt scope (an outer caller), skip to that |
| // directly. Otherwise, cannot translate. |
| if (!inlinedAt) |
| return nullptr; |
| callerLoc = inlinedAt; |
| } |
| llvmLoc = translateLoc(callLoc.getCallee(), nullptr, callerLoc); |
| // Fallback: Ignore callee if it has no debug scope. |
| if (!llvmLoc) |
| llvmLoc = callerLoc; |
| |
| } else if (auto fileLoc = dyn_cast<FileLineColLoc>(loc)) { |
| // A scope of a DILocation cannot be null. |
| if (!scope) |
| return nullptr; |
| llvmLoc = |
| llvm::DILocation::get(llvmCtx, fileLoc.getLine(), fileLoc.getColumn(), |
| scope, const_cast<llvm::DILocation *>(inlinedAt)); |
| |
| } else if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) { |
| ArrayRef<Location> locations = fusedLoc.getLocations(); |
| |
| // Check for a scope encoded with the location. |
| if (auto scopedAttr = |
| dyn_cast_or_null<LLVM::DILocalScopeAttr>(fusedLoc.getMetadata())) |
| scope = translate(scopedAttr); |
| |
| // For fused locations, merge each of the nodes. |
| llvmLoc = translateLoc(locations.front(), scope, inlinedAt); |
| for (Location locIt : locations.drop_front()) { |
| llvmLoc = llvm::DILocation::getMergedLocation( |
| llvmLoc, translateLoc(locIt, scope, inlinedAt)); |
| } |
| |
| } else if (auto nameLoc = dyn_cast<NameLoc>(loc)) { |
| llvmLoc = translateLoc(nameLoc.getChildLoc(), scope, inlinedAt); |
| |
| } else if (auto opaqueLoc = dyn_cast<OpaqueLoc>(loc)) { |
| llvmLoc = translateLoc(opaqueLoc.getFallbackLocation(), scope, inlinedAt); |
| } else { |
| llvm_unreachable("unknown location kind"); |
| } |
| |
| locationToLoc.try_emplace(std::make_tuple(loc, scope, inlinedAt), llvmLoc); |
| return llvmLoc; |
| } |
| |
| /// Create an llvm debug file for the given file path. |
| llvm::DIFile *DebugTranslation::translateFile(StringRef fileName) { |
| auto *&file = fileMap[fileName]; |
| if (file) |
| return file; |
| |
| // Make sure the current working directory is up-to-date. |
| if (currentWorkingDir.empty()) |
| llvm::sys::fs::current_path(currentWorkingDir); |
| |
| StringRef directory = currentWorkingDir; |
| SmallString<128> dirBuf; |
| SmallString<128> fileBuf; |
| if (llvm::sys::path::is_absolute(fileName)) { |
| // Strip the common prefix (if it is more than just "/") from current |
| // directory and FileName for a more space-efficient encoding. |
| auto fileIt = llvm::sys::path::begin(fileName); |
| auto fileE = llvm::sys::path::end(fileName); |
| auto curDirIt = llvm::sys::path::begin(directory); |
| auto curDirE = llvm::sys::path::end(directory); |
| for (; curDirIt != curDirE && *curDirIt == *fileIt; ++curDirIt, ++fileIt) |
| llvm::sys::path::append(dirBuf, *curDirIt); |
| if (std::distance(llvm::sys::path::begin(directory), curDirIt) == 1) { |
| // Don't strip the common prefix if it is only the root "/" since that |
| // would make LLVM diagnostic locations confusing. |
| directory = StringRef(); |
| } else { |
| for (; fileIt != fileE; ++fileIt) |
| llvm::sys::path::append(fileBuf, *fileIt); |
| directory = dirBuf; |
| fileName = fileBuf; |
| } |
| } |
| return (file = llvm::DIFile::get(llvmCtx, fileName, directory)); |
| } |