Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[flang][debug] Support derived types. #99476

Merged
merged 6 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion flang/include/flang/Optimizer/Dialect/FIRType.h
Original file line number Diff line number Diff line change
Expand Up @@ -487,11 +487,18 @@ std::string getTypeAsString(mlir::Type ty, const KindMapping &kindMap,
/// target dependent type size inquiries in lowering. It would also not be
/// straightforward given the need for a kind map that would need to be
/// converted in terms of mlir::DataLayoutEntryKey.

/// This variant terminates the compilation if an unsupported type is passed.
std::pair<std::uint64_t, unsigned short>
getTypeSizeAndAlignmentOrCrash(mlir::Location loc, mlir::Type ty,
const mlir::DataLayout &dl,
const fir::KindMapping &kindMap);

/// This variant returns std::nullopt if an unsupported type is passed.
std::optional<std::pair<uint64_t, unsigned short>>
getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
const mlir::DataLayout &dl,
const fir::KindMapping &kindMap);

} // namespace fir

#endif // FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H
10 changes: 5 additions & 5 deletions flang/lib/Optimizer/CodeGen/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
return byteOffset;
}
mlir::Type compType = component.second;
auto [compSize, compAlign] =
fir::getTypeSizeAndAlignment(loc, compType, getDataLayout(), kindMap);
auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash(
loc, compType, getDataLayout(), kindMap);
byteOffset = llvm::alignTo(byteOffset, compAlign);
ArgClass LoComp, HiComp;
classify(loc, compType, byteOffset, LoComp, HiComp);
Expand All @@ -452,8 +452,8 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
ArgClass &Hi) const {
mlir::Type eleTy = seqTy.getEleTy();
const std::uint64_t arraySize = seqTy.getConstantArraySize();
auto [eleSize, eleAlign] =
fir::getTypeSizeAndAlignment(loc, eleTy, getDataLayout(), kindMap);
auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash(
loc, eleTy, getDataLayout(), kindMap);
std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign);
for (std::uint64_t i = 0; i < arraySize; ++i) {
byteOffset = llvm::alignTo(byteOffset, eleAlign);
Expand Down Expand Up @@ -641,7 +641,7 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
mlir::Type ty) const {
CodeGenSpecifics::Marshalling marshal;
auto sizeAndAlign =
fir::getTypeSizeAndAlignment(loc, ty, getDataLayout(), kindMap);
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
// The stack is always 8 byte aligned (note 14 in 3.2.3).
unsigned short align =
std::max(sizeAndAlign.second, static_cast<unsigned short>(8));
Expand Down
50 changes: 37 additions & 13 deletions flang/lib/Optimizer/Dialect/FIRType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1393,43 +1393,50 @@ void FIROpsDialect::registerTypes() {
OpenACCPointerLikeModel<fir::LLVMPointerType>>(*getContext());
}

std::pair<std::uint64_t, unsigned short>
std::optional<std::pair<uint64_t, unsigned short>>
fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
const mlir::DataLayout &dl,
const fir::KindMapping &kindMap) {
if (mlir::isa<mlir::IntegerType, mlir::FloatType, mlir::ComplexType>(ty)) {
llvm::TypeSize size = dl.getTypeSize(ty);
unsigned short alignment = dl.getTypeABIAlignment(ty);
return {size, alignment};
return std::pair{size, alignment};
}
if (auto firCmplx = mlir::dyn_cast<fir::ComplexType>(ty)) {
auto [floatSize, floatAlign] =
auto result =
getTypeSizeAndAlignment(loc, firCmplx.getEleType(kindMap), dl, kindMap);
return {llvm::alignTo(floatSize, floatAlign) + floatSize, floatAlign};
if (!result)
return result;
auto [floatSize, floatAlign] = *result;
return std::pair{llvm::alignTo(floatSize, floatAlign) + floatSize,
floatAlign};
}
if (auto real = mlir::dyn_cast<fir::RealType>(ty))
return getTypeSizeAndAlignment(loc, real.getFloatType(kindMap), dl,
kindMap);

if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) {
auto [eleSize, eleAlign] =
getTypeSizeAndAlignment(loc, seqTy.getEleTy(), dl, kindMap);

auto result = getTypeSizeAndAlignment(loc, seqTy.getEleTy(), dl, kindMap);
if (!result)
return result;
auto [eleSize, eleAlign] = *result;
std::uint64_t size =
llvm::alignTo(eleSize, eleAlign) * seqTy.getConstantArraySize();
return {size, eleAlign};
return std::pair{size, eleAlign};
}
if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) {
std::uint64_t size = 0;
unsigned short align = 1;
for (auto component : recTy.getTypeList()) {
auto [compSize, compAlign] =
getTypeSizeAndAlignment(loc, component.second, dl, kindMap);
auto result = getTypeSizeAndAlignment(loc, component.second, dl, kindMap);
if (!result)
return result;
auto [compSize, compAlign] = *result;
size =
llvm::alignTo(size, compAlign) + llvm::alignTo(compSize, compAlign);
align = std::max(align, compAlign);
}
return {size, align};
return std::pair{size, align};
}
if (auto logical = mlir::dyn_cast<fir::LogicalType>(ty)) {
mlir::Type intTy = mlir::IntegerType::get(
Expand All @@ -1440,7 +1447,24 @@ fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
mlir::Type intTy = mlir::IntegerType::get(
character.getContext(),
kindMap.getCharacterBitsize(character.getFKind()));
return getTypeSizeAndAlignment(loc, intTy, dl, kindMap);
auto result = getTypeSizeAndAlignment(loc, intTy, dl, kindMap);
if (!result)
return result;
auto [compSize, compAlign] = *result;
if (character.hasConstantLen())
compSize *= character.getLen();
return std::pair{compSize, compAlign};
}
TODO(loc, "computing size of a component");
return std::nullopt;
}

std::pair<std::uint64_t, unsigned short>
fir::getTypeSizeAndAlignmentOrCrash(mlir::Location loc, mlir::Type ty,
const mlir::DataLayout &dl,
const fir::KindMapping &kindMap) {
std::optional<std::pair<uint64_t, unsigned short>> result =
getTypeSizeAndAlignment(loc, ty, dl, kindMap);
if (result)
return *result;
TODO(loc, "computing size of a component");
}
37 changes: 22 additions & 15 deletions flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,15 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {

void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable,
fir::cg::XDeclareOp declOp);
void handleFuncOp(mlir::func::FuncOp funcOp, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DICompileUnitAttr cuAttr,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable);
};

static uint32_t getLineFromLoc(mlir::Location loc) {
uint32_t line = 1;
if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
line = fileLoc.getLine();
return line;
}

bool debugInfoIsAlreadySet(mlir::Location loc) {
if (mlir::isa<mlir::FusedLoc>(loc)) {
if (loc->findInstanceOf<mlir::FusedLocWith<fir::LocationKindAttr>>())
Expand All @@ -103,7 +98,7 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
return;
// If this DeclareOp actually represents a global then treat it as such.
if (auto global = symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
handleGlobalOp(global, fileAttr, scopeAttr, symbolTable, declOp);
handleGlobalOp(global, fileAttr, scopeAttr, typeGen, symbolTable, declOp);
return;
}

Expand Down Expand Up @@ -160,19 +155,24 @@ mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable,
fir::cg::XDeclareOp declOp) {
if (debugInfoIsAlreadySet(globalOp.getLoc()))
return;
mlir::ModuleOp module = getOperation();
mlir::MLIRContext *context = &getContext();
fir::DebugTypeGenerator typeGen(module);
mlir::OpBuilder builder(context);

std::pair result = fir::NameUniquer::deconstruct(globalOp.getSymName());
if (result.first != fir::NameUniquer::NameKind::VARIABLE)
return;

// Discard entries that describe a derived type. Usually start with '.c.',
// '.dt.' or '.n.'. It would be better if result of the deconstruct had a flag
// for such values so that we dont have to look at string values.
if (!result.second.name.empty() && result.second.name[0] == '.')
return;

unsigned line = getLineFromLoc(globalOp.getLoc());

// DWARF5 says following about the fortran modules:
Expand Down Expand Up @@ -214,14 +214,14 @@ void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DICompileUnitAttr cuAttr,
fir::DebugTypeGenerator &typeGen,
mlir::SymbolTable *symbolTable) {
mlir::Location l = funcOp->getLoc();
// If fused location has already been created then nothing to do
// Otherwise, create a fused location.
if (debugInfoIsAlreadySet(l))
return;

mlir::ModuleOp module = getOperation();
mlir::MLIRContext *context = &getContext();
mlir::OpBuilder builder(context);
llvm::StringRef fileName(fileAttr.getName());
Expand All @@ -245,7 +245,6 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
funcName = mlir::StringAttr::get(context, result.second.name);

llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
fir::DebugTypeGenerator typeGen(module);
for (auto resTy : funcOp.getResultTypes()) {
auto tyAttr =
typeGen.convertType(resTy, fileAttr, cuAttr, /*declOp=*/nullptr);
Expand Down Expand Up @@ -285,7 +284,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
if (auto func =
symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) {
// Make sure that parent is processed.
handleFuncOp(func, fileAttr, cuAttr, symbolTable);
handleFuncOp(func, fileAttr, cuAttr, typeGen, symbolTable);
if (auto fusedLoc =
mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) {
if (auto spAttr =
Expand Down Expand Up @@ -320,6 +319,14 @@ void AddDebugInfoPass::runOnOperation() {
mlir::SymbolTable symbolTable(module);
llvm::StringRef fileName;
std::string filePath;
std::optional<mlir::DataLayout> dl =
fir::support::getOrSetDataLayout(module, /*allowDefaultLayout=*/true);
if (!dl) {
mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
signalPassFailure();
return;
}
fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl);
// We need 2 type of file paths here.
// 1. Name of the file as was presented to compiler. This can be absolute
// or relative to 2.
Expand Down Expand Up @@ -354,13 +361,13 @@ void AddDebugInfoPass::runOnOperation() {
isOptimized, debugLevel);

module.walk([&](mlir::func::FuncOp funcOp) {
handleFuncOp(funcOp, fileAttr, cuAttr, &symbolTable);
handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable);
});
// Process any global which was not processed through DeclareOp.
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
// Process 'GlobalOp' only if full debug info is requested.
for (auto globalOp : module.getOps<fir::GlobalOp>())
handleGlobalOp(globalOp, fileAttr, cuAttr, &symbolTable,
handleGlobalOp(globalOp, fileAttr, cuAttr, typeGen, &symbolTable,
/*declOp=*/nullptr);
}
}
Expand Down
71 changes: 57 additions & 14 deletions flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "DebugTypeGenerator.h"
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
#include "flang/Optimizer/CodeGen/TypeConverter.h"
#include "flang/Optimizer/Support/DataLayout.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/BinaryFormat/Dwarf.h"
Expand Down Expand Up @@ -44,28 +44,26 @@ std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
return 0;
}

DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m)
: module(m), kindMapping(getKindMapping(m)) {
DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
mlir::SymbolTable *symbolTable_,
const mlir::DataLayout &dl)
: module(m), symbolTable(symbolTable_), dataLayout{&dl},
kindMapping(getKindMapping(m)) {
LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");

std::optional<mlir::DataLayout> dl =
fir::support::getOrSetDataLayout(module, /*allowDefaultLayout=*/true);
if (!dl) {
mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
return;
}

mlir::MLIRContext *context = module.getContext();

// The debug information requires the offset of certain fields in the
// descriptors like lower_bound and extent for each dimension.
mlir::Type llvmDimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
mlir::Type llvmPtrType = getDescFieldTypeModel<kAddrPosInBox>()(context);
mlir::Type llvmLenType = getDescFieldTypeModel<kElemLenPosInBox>()(context);
dimsOffset = getComponentOffset<kDimsPosInBox>(*dl, context, llvmDimsType);
dimsSize = dl->getTypeSize(llvmDimsType);
ptrSize = dl->getTypeSize(llvmPtrType);
lenOffset = getComponentOffset<kElemLenPosInBox>(*dl, context, llvmLenType);
dimsOffset =
getComponentOffset<kDimsPosInBox>(*dataLayout, context, llvmDimsType);
dimsSize = dataLayout->getTypeSize(llvmDimsType);
ptrSize = dataLayout->getTypeSize(llvmPtrType);
lenOffset =
getComponentOffset<kElemLenPosInBox>(*dataLayout, context, llvmLenType);
}

static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context,
Expand Down Expand Up @@ -154,6 +152,49 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
dataLocation, /*rank=*/nullptr, allocated, associated);
}

mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This processing is likely not cheap, do you know how many times this will being called for a given derived type in a program: once per type, or once for every variable with this type, or more?

It may be worth having some caching fir::RecordType Ty <-> mlir::LLVM::DITypeAttr, or even mlir::Type <-> mlir::LLVM::DITypeAttr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping a cache for mlir::Type to mlir::DITypeAttr was always the plan. Although I have noticed in the IR dump that there is no type duplication. So may be mlir does caching at some level. I will keep this thing as a TODO to avoid generating redundant types.

fir::RecordType Ty, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
mlir::MLIRContext *context = module.getContext();
auto result = fir::NameUniquer::deconstruct(Ty.getName());
if (result.first != fir::NameUniquer::NameKind::DERIVED_TYPE)
return genPlaceholderType(context);

fir::TypeInfoOp tiOp = symbolTable->lookup<fir::TypeInfoOp>(Ty.getName());
unsigned line = (tiOp) ? getLineFromLoc(tiOp.getLoc()) : 1;

llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
std::uint64_t offset = 0;
for (auto [fieldName, fieldTy] : Ty.getTypeList()) {
auto result = fir::getTypeSizeAndAlignment(module.getLoc(), fieldTy,
*dataLayout, kindMapping);
// If we get a type whose size we can't determine, we will break the loop
// and generate the derived type with whatever components we have
// assembled thus far.
if (!result)
break;
auto [byteSize, byteAlign] = *result;
// FIXME: Handle non defaults array bound in derived types
mlir::LLVM::DITypeAttr elemTy =
convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr);
offset = llvm::alignTo(offset, byteAlign);
mlir::LLVM::DIDerivedTypeAttr tyAttr = mlir::LLVM::DIDerivedTypeAttr::get(
context, llvm::dwarf::DW_TAG_member,
mlir::StringAttr::get(context, fieldName), elemTy, byteSize * 8,
byteAlign * 8, offset * 8, /*optional<address space>=*/std::nullopt,
/*extra data=*/nullptr);
elements.push_back(tyAttr);
offset += llvm::alignTo(byteSize, byteAlign);
}

return mlir::LLVM::DICompositeTypeAttr::get(
context, llvm::dwarf::DW_TAG_structure_type, /*recursive_id=*/{},
mlir::StringAttr::get(context, result.second.name), fileAttr, line, scope,
/*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
/*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
/*allocated=*/nullptr, /*associated=*/nullptr);
}

mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(
fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
Expand Down Expand Up @@ -312,6 +353,8 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
} else if (auto charTy = mlir::dyn_cast_or_null<fir::CharacterType>(Ty)) {
return convertCharacterType(charTy, fileAttr, scope, declOp,
/*hasDescriptor=*/false);
} else if (auto recTy = mlir::dyn_cast_or_null<fir::RecordType>(Ty)) {
return convertRecordType(recTy, fileAttr, scope, declOp);
} else if (auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(Ty)) {
auto elTy = boxTy.getElementType();
if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(elTy))
Expand Down
Loading
Loading