Skip to content

Commit d07dc73

Browse files
authored
[flang][debug] Support derived types. (#99476)
This PR adds initial debug support for derived type. It handles `RecordType` and generates appropriate `DICompositeTypeAttr`. The `TypeInfoOp` is used to get information about the parent and location of the derived type. We use `getTypeSizeAndAlignment` to get the size and alignment of the components of the derived types. This function needed a few changes to be suitable to be used here: 1. The `getTypeSizeAndAlignment` errored out on unsupported type which would not work with incremental way we are building debug support. A new variant of this function has been that returns an std::optional. The original function has been renamed to `getTypeSizeAndAlignmentOrCrash` as it will call `TODO()` for unsupported types. 2. The Character type was returning size of just element and not the whole string which has been fixed. The testcase checks for offsets of the components which had to be hardcoded in the test. So the testcase is currently enabled on x86_64. With this PR in place, this is how the debugging of derived types look like: ``` type :: t_date integer :: year, month, day end type type :: t_address integer :: house_number end type type, extends(t_address) :: t_person character(len=20) name end type type, extends(t_person) :: t_employee type(t_date) :: hired_date real :: monthly_salary end type type(t_employee) :: employee (gdb) p employee $1 = ( t_person = ( t_address = ( house_number = 1 ), name = 'John', ' ' <repeats 16 times> ), hired_date = ( year = 2020, month = 1, day = 20 ), monthly_salary = 3.1400001 ) ```
1 parent 1e15346 commit d07dc73

File tree

9 files changed

+234
-50
lines changed

9 files changed

+234
-50
lines changed

flang/include/flang/Optimizer/Dialect/FIRType.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,11 +487,18 @@ std::string getTypeAsString(mlir::Type ty, const KindMapping &kindMap,
487487
/// target dependent type size inquiries in lowering. It would also not be
488488
/// straightforward given the need for a kind map that would need to be
489489
/// converted in terms of mlir::DataLayoutEntryKey.
490+
491+
/// This variant terminates the compilation if an unsupported type is passed.
490492
std::pair<std::uint64_t, unsigned short>
493+
getTypeSizeAndAlignmentOrCrash(mlir::Location loc, mlir::Type ty,
494+
const mlir::DataLayout &dl,
495+
const fir::KindMapping &kindMap);
496+
497+
/// This variant returns std::nullopt if an unsupported type is passed.
498+
std::optional<std::pair<uint64_t, unsigned short>>
491499
getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
492500
const mlir::DataLayout &dl,
493501
const fir::KindMapping &kindMap);
494-
495502
} // namespace fir
496503

497504
#endif // FORTRAN_OPTIMIZER_DIALECT_FIRTYPE_H

flang/lib/Optimizer/CodeGen/Target.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,8 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
431431
return byteOffset;
432432
}
433433
mlir::Type compType = component.second;
434-
auto [compSize, compAlign] =
435-
fir::getTypeSizeAndAlignment(loc, compType, getDataLayout(), kindMap);
434+
auto [compSize, compAlign] = fir::getTypeSizeAndAlignmentOrCrash(
435+
loc, compType, getDataLayout(), kindMap);
436436
byteOffset = llvm::alignTo(byteOffset, compAlign);
437437
ArgClass LoComp, HiComp;
438438
classify(loc, compType, byteOffset, LoComp, HiComp);
@@ -452,8 +452,8 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
452452
ArgClass &Hi) const {
453453
mlir::Type eleTy = seqTy.getEleTy();
454454
const std::uint64_t arraySize = seqTy.getConstantArraySize();
455-
auto [eleSize, eleAlign] =
456-
fir::getTypeSizeAndAlignment(loc, eleTy, getDataLayout(), kindMap);
455+
auto [eleSize, eleAlign] = fir::getTypeSizeAndAlignmentOrCrash(
456+
loc, eleTy, getDataLayout(), kindMap);
457457
std::uint64_t eleStorageSize = llvm::alignTo(eleSize, eleAlign);
458458
for (std::uint64_t i = 0; i < arraySize; ++i) {
459459
byteOffset = llvm::alignTo(byteOffset, eleAlign);
@@ -641,7 +641,7 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
641641
mlir::Type ty) const {
642642
CodeGenSpecifics::Marshalling marshal;
643643
auto sizeAndAlign =
644-
fir::getTypeSizeAndAlignment(loc, ty, getDataLayout(), kindMap);
644+
fir::getTypeSizeAndAlignmentOrCrash(loc, ty, getDataLayout(), kindMap);
645645
// The stack is always 8 byte aligned (note 14 in 3.2.3).
646646
unsigned short align =
647647
std::max(sizeAndAlign.second, static_cast<unsigned short>(8));

flang/lib/Optimizer/Dialect/FIRType.cpp

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,43 +1393,50 @@ void FIROpsDialect::registerTypes() {
13931393
OpenACCPointerLikeModel<fir::LLVMPointerType>>(*getContext());
13941394
}
13951395

1396-
std::pair<std::uint64_t, unsigned short>
1396+
std::optional<std::pair<uint64_t, unsigned short>>
13971397
fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
13981398
const mlir::DataLayout &dl,
13991399
const fir::KindMapping &kindMap) {
14001400
if (mlir::isa<mlir::IntegerType, mlir::FloatType, mlir::ComplexType>(ty)) {
14011401
llvm::TypeSize size = dl.getTypeSize(ty);
14021402
unsigned short alignment = dl.getTypeABIAlignment(ty);
1403-
return {size, alignment};
1403+
return std::pair{size, alignment};
14041404
}
14051405
if (auto firCmplx = mlir::dyn_cast<fir::ComplexType>(ty)) {
1406-
auto [floatSize, floatAlign] =
1406+
auto result =
14071407
getTypeSizeAndAlignment(loc, firCmplx.getEleType(kindMap), dl, kindMap);
1408-
return {llvm::alignTo(floatSize, floatAlign) + floatSize, floatAlign};
1408+
if (!result)
1409+
return result;
1410+
auto [floatSize, floatAlign] = *result;
1411+
return std::pair{llvm::alignTo(floatSize, floatAlign) + floatSize,
1412+
floatAlign};
14091413
}
14101414
if (auto real = mlir::dyn_cast<fir::RealType>(ty))
14111415
return getTypeSizeAndAlignment(loc, real.getFloatType(kindMap), dl,
14121416
kindMap);
14131417

14141418
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) {
1415-
auto [eleSize, eleAlign] =
1416-
getTypeSizeAndAlignment(loc, seqTy.getEleTy(), dl, kindMap);
1417-
1419+
auto result = getTypeSizeAndAlignment(loc, seqTy.getEleTy(), dl, kindMap);
1420+
if (!result)
1421+
return result;
1422+
auto [eleSize, eleAlign] = *result;
14181423
std::uint64_t size =
14191424
llvm::alignTo(eleSize, eleAlign) * seqTy.getConstantArraySize();
1420-
return {size, eleAlign};
1425+
return std::pair{size, eleAlign};
14211426
}
14221427
if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) {
14231428
std::uint64_t size = 0;
14241429
unsigned short align = 1;
14251430
for (auto component : recTy.getTypeList()) {
1426-
auto [compSize, compAlign] =
1427-
getTypeSizeAndAlignment(loc, component.second, dl, kindMap);
1431+
auto result = getTypeSizeAndAlignment(loc, component.second, dl, kindMap);
1432+
if (!result)
1433+
return result;
1434+
auto [compSize, compAlign] = *result;
14281435
size =
14291436
llvm::alignTo(size, compAlign) + llvm::alignTo(compSize, compAlign);
14301437
align = std::max(align, compAlign);
14311438
}
1432-
return {size, align};
1439+
return std::pair{size, align};
14331440
}
14341441
if (auto logical = mlir::dyn_cast<fir::LogicalType>(ty)) {
14351442
mlir::Type intTy = mlir::IntegerType::get(
@@ -1440,7 +1447,24 @@ fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
14401447
mlir::Type intTy = mlir::IntegerType::get(
14411448
character.getContext(),
14421449
kindMap.getCharacterBitsize(character.getFKind()));
1443-
return getTypeSizeAndAlignment(loc, intTy, dl, kindMap);
1450+
auto result = getTypeSizeAndAlignment(loc, intTy, dl, kindMap);
1451+
if (!result)
1452+
return result;
1453+
auto [compSize, compAlign] = *result;
1454+
if (character.hasConstantLen())
1455+
compSize *= character.getLen();
1456+
return std::pair{compSize, compAlign};
14441457
}
1445-
TODO(loc, "computing size of a component");
1458+
return std::nullopt;
14461459
}
1460+
1461+
std::pair<std::uint64_t, unsigned short>
1462+
fir::getTypeSizeAndAlignmentOrCrash(mlir::Location loc, mlir::Type ty,
1463+
const mlir::DataLayout &dl,
1464+
const fir::KindMapping &kindMap) {
1465+
std::optional<std::pair<uint64_t, unsigned short>> result =
1466+
getTypeSizeAndAlignment(loc, ty, dl, kindMap);
1467+
if (result)
1468+
return *result;
1469+
TODO(loc, "computing size of a component");
1470+
}

flang/lib/Optimizer/Transforms/AddDebugInfo.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,15 @@ class AddDebugInfoPass : public fir::impl::AddDebugInfoBase<AddDebugInfoPass> {
6565

6666
void handleGlobalOp(fir::GlobalOp glocalOp, mlir::LLVM::DIFileAttr fileAttr,
6767
mlir::LLVM::DIScopeAttr scope,
68+
fir::DebugTypeGenerator &typeGen,
6869
mlir::SymbolTable *symbolTable,
6970
fir::cg::XDeclareOp declOp);
7071
void handleFuncOp(mlir::func::FuncOp funcOp, mlir::LLVM::DIFileAttr fileAttr,
7172
mlir::LLVM::DICompileUnitAttr cuAttr,
73+
fir::DebugTypeGenerator &typeGen,
7274
mlir::SymbolTable *symbolTable);
7375
};
7476

75-
static uint32_t getLineFromLoc(mlir::Location loc) {
76-
uint32_t line = 1;
77-
if (auto fileLoc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
78-
line = fileLoc.getLine();
79-
return line;
80-
}
81-
8277
bool debugInfoIsAlreadySet(mlir::Location loc) {
8378
if (mlir::isa<mlir::FusedLoc>(loc)) {
8479
if (loc->findInstanceOf<mlir::FusedLocWith<fir::LocationKindAttr>>())
@@ -103,7 +98,7 @@ void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp,
10398
return;
10499
// If this DeclareOp actually represents a global then treat it as such.
105100
if (auto global = symbolTable->lookup<fir::GlobalOp>(declOp.getUniqName())) {
106-
handleGlobalOp(global, fileAttr, scopeAttr, symbolTable, declOp);
101+
handleGlobalOp(global, fileAttr, scopeAttr, typeGen, symbolTable, declOp);
107102
return;
108103
}
109104

@@ -160,19 +155,24 @@ mlir::LLVM::DIModuleAttr AddDebugInfoPass::getOrCreateModuleAttr(
160155
void AddDebugInfoPass::handleGlobalOp(fir::GlobalOp globalOp,
161156
mlir::LLVM::DIFileAttr fileAttr,
162157
mlir::LLVM::DIScopeAttr scope,
158+
fir::DebugTypeGenerator &typeGen,
163159
mlir::SymbolTable *symbolTable,
164160
fir::cg::XDeclareOp declOp) {
165161
if (debugInfoIsAlreadySet(globalOp.getLoc()))
166162
return;
167-
mlir::ModuleOp module = getOperation();
168163
mlir::MLIRContext *context = &getContext();
169-
fir::DebugTypeGenerator typeGen(module);
170164
mlir::OpBuilder builder(context);
171165

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

170+
// Discard entries that describe a derived type. Usually start with '.c.',
171+
// '.dt.' or '.n.'. It would be better if result of the deconstruct had a flag
172+
// for such values so that we dont have to look at string values.
173+
if (!result.second.name.empty() && result.second.name[0] == '.')
174+
return;
175+
176176
unsigned line = getLineFromLoc(globalOp.getLoc());
177177

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

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

247247
llvm::SmallVector<mlir::LLVM::DITypeAttr> types;
248-
fir::DebugTypeGenerator typeGen(module);
249248
for (auto resTy : funcOp.getResultTypes()) {
250249
auto tyAttr =
251250
typeGen.convertType(resTy, fileAttr, cuAttr, /*declOp=*/nullptr);
@@ -285,7 +284,7 @@ void AddDebugInfoPass::handleFuncOp(mlir::func::FuncOp funcOp,
285284
if (auto func =
286285
symbolTable->lookup<mlir::func::FuncOp>(sym.getLeafReference())) {
287286
// Make sure that parent is processed.
288-
handleFuncOp(func, fileAttr, cuAttr, symbolTable);
287+
handleFuncOp(func, fileAttr, cuAttr, typeGen, symbolTable);
289288
if (auto fusedLoc =
290289
mlir::dyn_cast_if_present<mlir::FusedLoc>(func.getLoc())) {
291290
if (auto spAttr =
@@ -320,6 +319,14 @@ void AddDebugInfoPass::runOnOperation() {
320319
mlir::SymbolTable symbolTable(module);
321320
llvm::StringRef fileName;
322321
std::string filePath;
322+
std::optional<mlir::DataLayout> dl =
323+
fir::support::getOrSetDataLayout(module, /*allowDefaultLayout=*/true);
324+
if (!dl) {
325+
mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
326+
signalPassFailure();
327+
return;
328+
}
329+
fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl);
323330
// We need 2 type of file paths here.
324331
// 1. Name of the file as was presented to compiler. This can be absolute
325332
// or relative to 2.
@@ -354,13 +361,13 @@ void AddDebugInfoPass::runOnOperation() {
354361
isOptimized, debugLevel);
355362

356363
module.walk([&](mlir::func::FuncOp funcOp) {
357-
handleFuncOp(funcOp, fileAttr, cuAttr, &symbolTable);
364+
handleFuncOp(funcOp, fileAttr, cuAttr, typeGen, &symbolTable);
358365
});
359366
// Process any global which was not processed through DeclareOp.
360367
if (debugLevel == mlir::LLVM::DIEmissionKind::Full) {
361368
// Process 'GlobalOp' only if full debug info is requested.
362369
for (auto globalOp : module.getOps<fir::GlobalOp>())
363-
handleGlobalOp(globalOp, fileAttr, cuAttr, &symbolTable,
370+
handleGlobalOp(globalOp, fileAttr, cuAttr, typeGen, &symbolTable,
364371
/*declOp=*/nullptr);
365372
}
366373
}

flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "DebugTypeGenerator.h"
1616
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
1717
#include "flang/Optimizer/CodeGen/TypeConverter.h"
18-
#include "flang/Optimizer/Support/DataLayout.h"
18+
#include "flang/Optimizer/Support/InternalNames.h"
1919
#include "mlir/Pass/Pass.h"
2020
#include "llvm/ADT/ScopeExit.h"
2121
#include "llvm/BinaryFormat/Dwarf.h"
@@ -44,28 +44,26 @@ std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
4444
return 0;
4545
}
4646

47-
DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m)
48-
: module(m), kindMapping(getKindMapping(m)) {
47+
DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
48+
mlir::SymbolTable *symbolTable_,
49+
const mlir::DataLayout &dl)
50+
: module(m), symbolTable(symbolTable_), dataLayout{&dl},
51+
kindMapping(getKindMapping(m)) {
4952
LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");
5053

51-
std::optional<mlir::DataLayout> dl =
52-
fir::support::getOrSetDataLayout(module, /*allowDefaultLayout=*/true);
53-
if (!dl) {
54-
mlir::emitError(module.getLoc(), "Missing data layout attribute in module");
55-
return;
56-
}
57-
5854
mlir::MLIRContext *context = module.getContext();
5955

6056
// The debug information requires the offset of certain fields in the
6157
// descriptors like lower_bound and extent for each dimension.
6258
mlir::Type llvmDimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
6359
mlir::Type llvmPtrType = getDescFieldTypeModel<kAddrPosInBox>()(context);
6460
mlir::Type llvmLenType = getDescFieldTypeModel<kElemLenPosInBox>()(context);
65-
dimsOffset = getComponentOffset<kDimsPosInBox>(*dl, context, llvmDimsType);
66-
dimsSize = dl->getTypeSize(llvmDimsType);
67-
ptrSize = dl->getTypeSize(llvmPtrType);
68-
lenOffset = getComponentOffset<kElemLenPosInBox>(*dl, context, llvmLenType);
61+
dimsOffset =
62+
getComponentOffset<kDimsPosInBox>(*dataLayout, context, llvmDimsType);
63+
dimsSize = dataLayout->getTypeSize(llvmDimsType);
64+
ptrSize = dataLayout->getTypeSize(llvmPtrType);
65+
lenOffset =
66+
getComponentOffset<kElemLenPosInBox>(*dataLayout, context, llvmLenType);
6967
}
7068

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

155+
mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
156+
fir::RecordType Ty, mlir::LLVM::DIFileAttr fileAttr,
157+
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
158+
mlir::MLIRContext *context = module.getContext();
159+
auto result = fir::NameUniquer::deconstruct(Ty.getName());
160+
if (result.first != fir::NameUniquer::NameKind::DERIVED_TYPE)
161+
return genPlaceholderType(context);
162+
163+
fir::TypeInfoOp tiOp = symbolTable->lookup<fir::TypeInfoOp>(Ty.getName());
164+
unsigned line = (tiOp) ? getLineFromLoc(tiOp.getLoc()) : 1;
165+
166+
llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
167+
std::uint64_t offset = 0;
168+
for (auto [fieldName, fieldTy] : Ty.getTypeList()) {
169+
auto result = fir::getTypeSizeAndAlignment(module.getLoc(), fieldTy,
170+
*dataLayout, kindMapping);
171+
// If we get a type whose size we can't determine, we will break the loop
172+
// and generate the derived type with whatever components we have
173+
// assembled thus far.
174+
if (!result)
175+
break;
176+
auto [byteSize, byteAlign] = *result;
177+
// FIXME: Handle non defaults array bound in derived types
178+
mlir::LLVM::DITypeAttr elemTy =
179+
convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr);
180+
offset = llvm::alignTo(offset, byteAlign);
181+
mlir::LLVM::DIDerivedTypeAttr tyAttr = mlir::LLVM::DIDerivedTypeAttr::get(
182+
context, llvm::dwarf::DW_TAG_member,
183+
mlir::StringAttr::get(context, fieldName), elemTy, byteSize * 8,
184+
byteAlign * 8, offset * 8, /*optional<address space>=*/std::nullopt,
185+
/*extra data=*/nullptr);
186+
elements.push_back(tyAttr);
187+
offset += llvm::alignTo(byteSize, byteAlign);
188+
}
189+
190+
return mlir::LLVM::DICompositeTypeAttr::get(
191+
context, llvm::dwarf::DW_TAG_structure_type, /*recursive_id=*/{},
192+
mlir::StringAttr::get(context, result.second.name), fileAttr, line, scope,
193+
/*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
194+
/*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
195+
/*allocated=*/nullptr, /*associated=*/nullptr);
196+
}
197+
157198
mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(
158199
fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
159200
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
@@ -312,6 +353,8 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
312353
} else if (auto charTy = mlir::dyn_cast_or_null<fir::CharacterType>(Ty)) {
313354
return convertCharacterType(charTy, fileAttr, scope, declOp,
314355
/*hasDescriptor=*/false);
356+
} else if (auto recTy = mlir::dyn_cast_or_null<fir::RecordType>(Ty)) {
357+
return convertRecordType(recTy, fileAttr, scope, declOp);
315358
} else if (auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(Ty)) {
316359
auto elTy = boxTy.getElementType();
317360
if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(elTy))

0 commit comments

Comments
 (0)