Files
llvm/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
Tobias Gysi f61f42b9d6 Reland "[mlir][llvm] Add an explicit void type debug info attribute."
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 commit 81f57b6
and relands commit a960547

Fixes flang build and drop_begin on an empty array ref.
2023-01-13 10:22:55 +01:00

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));
}