Skip to content

Commit d556e38

Browse files
authored
[flang][debug] Support derived type components with box types. (#109424)
Our support for derived types uses `getTypeSizeAndAlignment` to calculate the offset of the members. The `fir.box` was not supported in that function. It meant that any member which required descriptor was not supported in the derived type. We convert the type into an llvm type and then use the DataLayout to calculate the size/offset of a member. There is no dependency on `getTypeSizeAndAlignment` to get the size of the types. There are 2 other changes in this PR: 1. The `recID` field is used to handle cases where we have a member references its parent type. 2. A type cache is maintained to avoid duplication. It is also needed for circular reference case. Fixes #108001.
1 parent 6f956e3 commit d556e38

File tree

5 files changed

+163
-21
lines changed

5 files changed

+163
-21
lines changed

flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
#include "DebugTypeGenerator.h"
1616
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
17-
#include "flang/Optimizer/CodeGen/TypeConverter.h"
1817
#include "flang/Optimizer/Support/InternalNames.h"
1918
#include "mlir/Pass/Pass.h"
2019
#include "llvm/ADT/ScopeExit.h"
@@ -48,7 +47,7 @@ DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
4847
mlir::SymbolTable *symbolTable_,
4948
const mlir::DataLayout &dl)
5049
: module(m), symbolTable(symbolTable_), dataLayout{&dl},
51-
kindMapping(getKindMapping(m)) {
50+
kindMapping(getKindMapping(m)), llvmTypeConverter(m, false, false, dl) {
5251
LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");
5352

5453
mlir::MLIRContext *context = module.getContext();
@@ -160,29 +159,110 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
160159
dataLocation, /*rank=*/nullptr, allocated, associated);
161160
}
162161

162+
// If the type is a pointer or array type then gets its underlying type.
163+
static mlir::LLVM::DITypeAttr getUnderlyingType(mlir::LLVM::DITypeAttr Ty) {
164+
if (auto ptrTy =
165+
mlir::dyn_cast_if_present<mlir::LLVM::DIDerivedTypeAttr>(Ty)) {
166+
if (ptrTy.getTag() == llvm::dwarf::DW_TAG_pointer_type)
167+
Ty = getUnderlyingType(ptrTy.getBaseType());
168+
}
169+
if (auto comTy =
170+
mlir::dyn_cast_if_present<mlir::LLVM::DICompositeTypeAttr>(Ty)) {
171+
if (comTy.getTag() == llvm::dwarf::DW_TAG_array_type)
172+
Ty = getUnderlyingType(comTy.getBaseType());
173+
}
174+
return Ty;
175+
}
176+
177+
// Currently, the handling of recursive debug type in mlir has some limitations.
178+
// Those limitations were discussed at the end of the thread for following PR.
179+
// https://github.com/llvm/llvm-project/pull/106571
180+
//
181+
// Problem could be explained with the following example code:
182+
// type t2
183+
// type(t1), pointer :: p1
184+
// end type
185+
// type t1
186+
// type(t2), pointer :: p2
187+
// end type
188+
// In the description below, type_self means a temporary type that is generated
189+
// as a place holder while the members of that type are being processed.
190+
//
191+
// If we process t1 first then we will have the following structure after it has
192+
// been processed.
193+
// t1 -> t2 -> t1_self
194+
// This is because when we started processing t2, we did not have the complete
195+
// t1 but its place holder t1_self.
196+
// Now if some entity requires t2, we will already have that in cache and will
197+
// return it. But this t2 refers to t1_self and not to t1. In mlir handling,
198+
// only those types are allowed to have _self reference which are wrapped by
199+
// entity whose reference it is. So t1 -> t2 -> t1_self is ok because the
200+
// t1_self reference can be resolved by the outer t1. But standalone t2 is not
201+
// because there will be no way to resolve it. Until this is fixed in mlir, we
202+
// avoid caching such types. Please see DebugTranslation::translateRecursive for
203+
// details on how mlir handles recursive types.
204+
static bool canCacheThisType(mlir::LLVM::DICompositeTypeAttr comTy) {
205+
for (auto el : comTy.getElements()) {
206+
if (auto mem =
207+
mlir::dyn_cast_if_present<mlir::LLVM::DIDerivedTypeAttr>(el)) {
208+
mlir::LLVM::DITypeAttr memTy = getUnderlyingType(mem.getBaseType());
209+
if (auto baseTy =
210+
mlir::dyn_cast_if_present<mlir::LLVM::DICompositeTypeAttr>(
211+
memTy)) {
212+
// We will not cache a type if one of its member meets the following
213+
// conditions:
214+
// 1. It is a structure type
215+
// 2. It is a place holder type (getIsRecSelf() is true)
216+
// 3. It is not a self reference. It is ok to have t1_self in t1.
217+
if (baseTy.getTag() == llvm::dwarf::DW_TAG_structure_type &&
218+
baseTy.getIsRecSelf() && (comTy.getRecId() != baseTy.getRecId()))
219+
return false;
220+
}
221+
}
222+
}
223+
return true;
224+
}
225+
163226
mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
164227
fir::RecordType Ty, mlir::LLVM::DIFileAttr fileAttr,
165228
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) {
229+
// Check if this type has already been converted.
230+
auto iter = typeCache.find(Ty);
231+
if (iter != typeCache.end())
232+
return iter->second;
233+
234+
llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
166235
mlir::MLIRContext *context = module.getContext();
236+
auto recId = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
237+
// Generate a place holder TypeAttr which will be used if a member
238+
// references the parent type.
239+
auto comAttr = mlir::LLVM::DICompositeTypeAttr::get(
240+
context, recId, /*isRecSelf=*/true, llvm::dwarf::DW_TAG_structure_type,
241+
mlir::StringAttr::get(context, ""), fileAttr, /*line=*/0, scope,
242+
/*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0,
243+
/*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
244+
/*allocated=*/nullptr, /*associated=*/nullptr);
245+
typeCache[Ty] = comAttr;
246+
167247
auto result = fir::NameUniquer::deconstruct(Ty.getName());
168248
if (result.first != fir::NameUniquer::NameKind::DERIVED_TYPE)
169249
return genPlaceholderType(context);
170250

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

174-
llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
175254
std::uint64_t offset = 0;
176255
for (auto [fieldName, fieldTy] : Ty.getTypeList()) {
177-
auto result = fir::getTypeSizeAndAlignment(module.getLoc(), fieldTy,
178-
*dataLayout, kindMapping);
179-
// If we get a type whose size we can't determine, we will break the loop
180-
// and generate the derived type with whatever components we have
181-
// assembled thus far.
182-
if (!result)
183-
break;
184-
auto [byteSize, byteAlign] = *result;
256+
mlir::Type llvmTy;
257+
if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(fieldTy))
258+
llvmTy =
259+
llvmTypeConverter.convertBoxTypeAsStruct(boxTy, getBoxRank(boxTy));
260+
else
261+
llvmTy = llvmTypeConverter.convertType(fieldTy);
262+
185263
// FIXME: Handle non defaults array bound in derived types
264+
uint64_t byteSize = dataLayout->getTypeSize(llvmTy);
265+
unsigned short byteAlign = dataLayout->getTypeABIAlignment(llvmTy);
186266
mlir::LLVM::DITypeAttr elemTy =
187267
convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr);
188268
offset = llvm::alignTo(offset, byteAlign);
@@ -195,12 +275,20 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
195275
offset += llvm::alignTo(byteSize, byteAlign);
196276
}
197277

198-
return mlir::LLVM::DICompositeTypeAttr::get(
199-
context, llvm::dwarf::DW_TAG_structure_type,
278+
auto finalAttr = mlir::LLVM::DICompositeTypeAttr::get(
279+
context, recId, /*isRecSelf=*/false, llvm::dwarf::DW_TAG_structure_type,
200280
mlir::StringAttr::get(context, result.second.name), fileAttr, line, scope,
201281
/*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
202282
/*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
203283
/*allocated=*/nullptr, /*associated=*/nullptr);
284+
if (canCacheThisType(finalAttr)) {
285+
typeCache[Ty] = finalAttr;
286+
} else {
287+
auto iter = typeCache.find(Ty);
288+
if (iter != typeCache.end())
289+
typeCache.erase(iter);
290+
}
291+
return finalAttr;
204292
}
205293

206294
mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(

flang/lib/Optimizer/Transforms/DebugTypeGenerator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define FORTRAN_OPTIMIZER_TRANSFORMS_DEBUGTYPEGENERATOR_H
1515

1616
#include "flang/Optimizer/CodeGen/CGOps.h"
17+
#include "flang/Optimizer/CodeGen/TypeConverter.h"
1718
#include "flang/Optimizer/Dialect/FIRType.h"
1819
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
1920
#include "flang/Optimizer/Dialect/Support/KindMapping.h"
@@ -68,10 +69,12 @@ class DebugTypeGenerator {
6869
mlir::SymbolTable *symbolTable;
6970
const mlir::DataLayout *dataLayout;
7071
KindMapping kindMapping;
72+
fir::LLVMTypeConverter llvmTypeConverter;
7173
std::uint64_t dimsSize;
7274
std::uint64_t dimsOffset;
7375
std::uint64_t ptrSize;
7476
std::uint64_t lenOffset;
77+
llvm::DenseMap<mlir::Type, mlir::LLVM::DITypeAttr> typeCache;
7578
};
7679

7780
} // namespace fir
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
2+
3+
! mainly test that this program does not cause an assertion failure
4+
module m
5+
type t2
6+
type(t1), pointer :: p1
7+
end type
8+
type t1
9+
type(t2), pointer :: p2
10+
integer abc
11+
end type
12+
type(t1) :: tee1
13+
end module
14+
15+
program test
16+
use m
17+
type(t2) :: lc2
18+
print *, lc2%p1%abc
19+
end program test
20+
21+
! CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}})
22+
! CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}})

flang/test/Integration/debug-cyclic-derived-type.f90

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@ module m
1111
type(t2) :: v3
1212
end module
1313

14-
! CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}})
15-
! CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}})
14+
! CHECK-DAG: ![[T1:[0-9]+]] = {{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}}elements: ![[T1_ELEMS:[0-9]+]])
15+
! CHECK-DAG: ![[T1_ELEMS]] = !{![[T1_ELEM1:[0-9]+]]}
16+
! CHECK-DAG: ![[T1_ELEM1]] = !DIDerivedType(tag: DW_TAG_member, name: "p", baseType: ![[T2P:[0-9]+]]{{.*}})
17+
! CHECK-DAG: ![[T2P]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[T2:[0-9]+]]{{.*}})
18+
19+
! CHECK-DAG: ![[T2]] = {{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}}elements: ![[T2_ELEMS:[0-9]+]])
20+
! CHECK-DAG: ![[T2_ELEMS]] = !{![[T2_ELEM1:[0-9]+]]}
21+
! CHECK-DAG: ![[T2_ELEM1]] = !DIDerivedType(tag: DW_TAG_member, name: "v1", baseType: ![[T1]]{{.*}})

0 commit comments

Comments
 (0)