mirror of
https://github.com/intel/llvm.git
synced 2026-01-28 09:14:23 +08:00
Previously, the DISubroutineType attribute used an optional result parameter and an optional argument types array to model the subroutine signature. LLVM IR debug metadata, on the other hand, has one types list whose first entry maps to the result type. That entry may be null to model a void result type. The type list may also be entirely empty not specifying any type information. The latter is problematic since the current DISubroutineType attribute cannot express it. The revision changes DISubroutineTypeAttr to closely follow the LLVM metadata design. In particular, it uses a single types parameter array to model the subroutine signature and introduces an explicit DIVoidResultTypeAttr to model the null entries. Reviewed By: Dinistro Differential Revision: https://reviews.llvm.org/D141261 This reverts commit81f57b6and relands commita960547Fixes flang build and drop_begin on an empty array ref.
345 lines
14 KiB
C++
345 lines
14 KiB
C++
//===- 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 op->getLoc().isa<UnknownLoc>() ? 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;
|
|
|
|
// TODO: The version information should be encoded on the LLVM module itself,
|
|
// not implicitly set here.
|
|
|
|
// Mark this module as having debug information.
|
|
StringRef debugVersionKey = "Debug Info Version";
|
|
if (!llvmModule.getModuleFlag(debugVersionKey))
|
|
llvmModule.addModuleFlag(llvm::Module::Warning, debugVersionKey,
|
|
llvm::DEBUG_METADATA_VERSION);
|
|
|
|
if (auto targetTripleAttr =
|
|
module->getAttr(LLVM::LLVMDialect::getTargetTripleAttrName())) {
|
|
auto targetTriple =
|
|
llvm::Triple(targetTripleAttr.cast<StringAttr>().getValue());
|
|
if (targetTriple.isKnownWindowsMSVCEnvironment()) {
|
|
// Dwarf debugging files will be generated by default, unless "CodeView"
|
|
// is set explicitly. Windows/MSVC should use CodeView instead.
|
|
llvmModule.addModuleFlag(llvm::Module::Warning, "CodeView", 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Finalize the translation of debug information.
|
|
void DebugTranslation::finalize() {}
|
|
|
|
/// Translate the debug information for the given function.
|
|
void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) {
|
|
if (!debugEmissionIsEnabled)
|
|
return;
|
|
|
|
// If we are to create debug info for the function, we need to ensure that all
|
|
// inlinable calls in it are with debug info, otherwise the LLVM verifier will
|
|
// complain. For now, be more restricted and treat all calls as inlinable.
|
|
const bool hasCallWithoutDebugInfo =
|
|
func.walk([&](LLVM::CallOp call) {
|
|
return call.getLoc()->walk([](Location l) {
|
|
return l.isa<UnknownLoc>() ? WalkResult::interrupt()
|
|
: WalkResult::advance();
|
|
});
|
|
})
|
|
.wasInterrupted();
|
|
if (hasCallWithoutDebugInfo)
|
|
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(DIVoidResultTypeAttr attr) {
|
|
// A DIVoidResultTypeAttr at the beginning of the subroutine types list models
|
|
// a void result type. Translate the explicit DIVoidResultTypeAttr to a
|
|
// nullptr since LLVM IR metadata does not have an explicit void result type
|
|
// representation.
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) {
|
|
return llvm::DIBasicType::get(
|
|
llvmCtx, attr.getTag(), 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.getIsOptimized(), /*Flags=*/"", /*RV=*/0);
|
|
}
|
|
|
|
llvm::DICompositeType *
|
|
DebugTranslation::translateImpl(DICompositeTypeAttr attr) {
|
|
SmallVector<llvm::Metadata *> elements;
|
|
for (auto member : attr.getElements())
|
|
elements.push_back(translate(member));
|
|
return llvm::DICompositeType::get(
|
|
llvmCtx, attr.getTag(), 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()),
|
|
llvm::MDNode::get(llvmCtx, elements),
|
|
/*RuntimeLang=*/0, /*VTableHolder=*/nullptr);
|
|
}
|
|
|
|
llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) {
|
|
auto getMDStringOrNull = [&](StringAttr attr) -> llvm::MDString * {
|
|
return attr ? llvm::MDString::get(llvmCtx, attr) : nullptr;
|
|
};
|
|
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(),
|
|
/*DWARFAddressSpace=*/std::nullopt, /*Flags=*/llvm::DINode::FlagZero);
|
|
}
|
|
|
|
llvm::DIFile *DebugTranslation::translateImpl(DIFileAttr attr) {
|
|
return llvm::DIFile::get(llvmCtx, attr.getName(), attr.getDirectory());
|
|
}
|
|
|
|
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::DILocalVariable *
|
|
DebugTranslation::translateImpl(DILocalVariableAttr attr) {
|
|
return llvm::DILocalVariable::get(
|
|
llvmCtx, translate(attr.getScope()),
|
|
llvm::MDString::get(llvmCtx, attr.getName()), translate(attr.getFile()),
|
|
attr.getLine(), translate(attr.getType()), attr.getArg(),
|
|
/*Flags=*/llvm::DINode::FlagZero, attr.getAlignInBits(),
|
|
/*Annotations=*/nullptr);
|
|
}
|
|
|
|
llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) {
|
|
return cast<llvm::DIScope>(translate(DINodeAttr(attr)));
|
|
}
|
|
|
|
/// Return a new subprogram that is either distinct or not, depending on
|
|
/// `isDistinct`.
|
|
template <class... Ts>
|
|
static llvm::DISubprogram *getSubprogram(bool isDistinct, Ts &&...args) {
|
|
if (isDistinct)
|
|
return llvm::DISubprogram::getDistinct(std::forward<Ts>(args)...);
|
|
return llvm::DISubprogram::get(std::forward<Ts>(args)...);
|
|
}
|
|
|
|
llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) {
|
|
bool isDefinition = static_cast<bool>(attr.getSubprogramFlags() &
|
|
LLVM::DISubprogramFlags::Definition);
|
|
auto getMDStringOrNull = [&](StringAttr attr) -> llvm::MDString * {
|
|
return attr ? llvm::MDString::get(llvmCtx, attr) : nullptr;
|
|
};
|
|
return getSubprogram(
|
|
isDefinition, llvmCtx, translate(attr.getScope()),
|
|
llvm::MDString::get(llvmCtx, attr.getName()),
|
|
getMDStringOrNull(attr.getLinkageName()), translate(attr.getFile()),
|
|
attr.getLine(), translate(attr.getType()), attr.getScopeLine(),
|
|
/*ContainingType=*/nullptr, /*VirtualIndex=*/0,
|
|
/*ThisAdjustment=*/0, llvm::DINode::FlagZero,
|
|
static_cast<llvm::DISubprogram::DISPFlags>(attr.getSubprogramFlags()),
|
|
translate(attr.getCompileUnit()));
|
|
}
|
|
|
|
llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) {
|
|
auto getMetadataOrNull = [&](IntegerAttr attr) -> llvm::Metadata * {
|
|
if (!attr)
|
|
return nullptr;
|
|
return llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned(
|
|
llvm::Type::getInt64Ty(llvmCtx), attr.getInt()));
|
|
};
|
|
return llvm::DISubrange::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 =
|
|
TypeSwitch<DINodeAttr, llvm::DINode *>(attr)
|
|
.Case<DIVoidResultTypeAttr, DIBasicTypeAttr, DICompileUnitAttr,
|
|
DICompositeTypeAttr, DIDerivedTypeAttr, DIFileAttr,
|
|
DILexicalBlockAttr, DILexicalBlockFileAttr, DILocalVariableAttr,
|
|
DISubprogramAttr, DISubrangeAttr, DISubroutineTypeAttr>(
|
|
[&](auto attr) { return translateImpl(attr); });
|
|
attrToNode.insert({attr, node});
|
|
return node;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Locations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Translate the given location to an llvm debug location.
|
|
const llvm::DILocation *
|
|
DebugTranslation::translateLoc(Location loc, llvm::DILocalScope *scope) {
|
|
if (!debugEmissionIsEnabled)
|
|
return nullptr;
|
|
return translateLoc(loc, scope, /*inlinedAt=*/nullptr);
|
|
}
|
|
|
|
/// Translate the given location to an llvm DebugLoc.
|
|
const llvm::DILocation *
|
|
DebugTranslation::translateLoc(Location loc, llvm::DILocalScope *scope,
|
|
const llvm::DILocation *inlinedAt) {
|
|
// LLVM doesn't have a representation for unknown.
|
|
if (!scope || loc.isa<UnknownLoc>())
|
|
return nullptr;
|
|
|
|
// Check for a cached instance.
|
|
auto existingIt = locationToLoc.find(std::make_tuple(loc, scope, inlinedAt));
|
|
if (existingIt != locationToLoc.end())
|
|
return existingIt->second;
|
|
|
|
const llvm::DILocation *llvmLoc = nullptr;
|
|
if (auto callLoc = loc.dyn_cast<CallSiteLoc>()) {
|
|
// For callsites, the caller is fed as the inlinedAt for the callee.
|
|
const auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt);
|
|
llvmLoc = translateLoc(callLoc.getCallee(), scope, callerLoc);
|
|
|
|
} else if (auto fileLoc = loc.dyn_cast<FileLineColLoc>()) {
|
|
auto *file = translateFile(fileLoc.getFilename());
|
|
auto *fileScope = llvm::DILexicalBlockFile::get(llvmCtx, scope, file,
|
|
/*Discriminator=*/0);
|
|
llvmLoc = llvm::DILocation::get(llvmCtx, fileLoc.getLine(),
|
|
fileLoc.getColumn(), fileScope,
|
|
const_cast<llvm::DILocation *>(inlinedAt));
|
|
|
|
} else if (auto fusedLoc = loc.dyn_cast<FusedLoc>()) {
|
|
ArrayRef<Location> locations = fusedLoc.getLocations();
|
|
|
|
// Check for a scope encoded with the location.
|
|
if (auto scopedAttr =
|
|
fusedLoc.getMetadata().dyn_cast_or_null<LLVM::DIScopeAttr>())
|
|
scope = cast<llvm::DILocalScope>(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 = loc.dyn_cast<NameLoc>()) {
|
|
llvmLoc = translateLoc(loc.cast<NameLoc>().getChildLoc(), scope, inlinedAt);
|
|
|
|
} else if (auto opaqueLoc = loc.dyn_cast<OpaqueLoc>()) {
|
|
llvmLoc = translateLoc(loc.cast<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));
|
|
}
|