Skip to content

Commit af91372

Browse files
authored
[flang][debug] Improve handling of cyclic derived types. (#122770)
When `RecordType` is converted to corresponding `DIType`, we cache the information to avoid doing the conversion again. Our conversion of `RecordType` looks like this: `ConvertRecordType(RecordType Ty)` 1. If type `Ty` is already in the cache, then return the corresponding item. 2. Create a place holder `DICompositeTypeAttr` (called `ty_self` below) for `Ty` 3. Put `Ty->ty_self` in the cache 4. Convert members of `Ty`. This may cause `ConvertRecordType` to be called again with other types. 5. Create final `DICompositeTypeAttr` 6. Replace the `ty_self` in the cache with one created in step 5 end The purpose of creating `ty_self` is to handle cases where a member may have reference to parent type. Now consider the code below: ``` type t1 type(t2), pointer :: p1 end type type t2 type(t1), pointer :: p2 end type ``` While processing t1, we could have a structure like below. `t1 -> t2 -> t1_self` The `t2` created during handling of `t1` cant be cached on its own as it contains a place holder reference. It will fail an assert in MLIR if it is processed standalone. To avoid this problem, we have a check in the step 6 above to not cache such types. But this check was not tight enough. It just checked if a type should not have a place holder reference to another type. It missed the following case where the place holder reference can be in a type further down the line. ``` type t1 type(t2), pointer :: p1 end type type t2 type(t3), pointer :: p2 end type type t3 type(t1), pointer :: p3 end type ``` So while processing `t1`, we have to stop caching of not only `t3` but also of `t2`. This PR improves the check and moves the logic inside `convertRecordType`. Please note that this limitation of why a type cant have a placeholder reference is because of how such references are resolved in the mlir. Please see the discussion at the end of this [PR](#106571). I have to change `getDerivedType` so that it will also get the derived type for things like `type(t2), pointer :: p1` which are wrapped in `BoxType`. Happy to move it to a new function or a local helper in case this change is problematic. Fixes #122024.
1 parent a79ae86 commit af91372

File tree

3 files changed

+91
-50
lines changed

3 files changed

+91
-50
lines changed

flang/lib/Optimizer/Dialect/FIRType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ mlir::Type getDerivedType(mlir::Type ty) {
210210
return seq.getEleTy();
211211
return p.getEleTy();
212212
})
213+
.Case<fir::BoxType>([](auto p) { return getDerivedType(p.getEleTy()); })
213214
.Default([](mlir::Type t) { return t; });
214215
}
215216

flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -273,55 +273,6 @@ static mlir::LLVM::DITypeAttr getUnderlyingType(mlir::LLVM::DITypeAttr Ty) {
273273
return Ty;
274274
}
275275

276-
// Currently, the handling of recursive debug type in mlir has some limitations.
277-
// Those limitations were discussed at the end of the thread for following PR.
278-
// https://github.com/llvm/llvm-project/pull/106571
279-
//
280-
// Problem could be explained with the following example code:
281-
// type t2
282-
// type(t1), pointer :: p1
283-
// end type
284-
// type t1
285-
// type(t2), pointer :: p2
286-
// end type
287-
// In the description below, type_self means a temporary type that is generated
288-
// as a place holder while the members of that type are being processed.
289-
//
290-
// If we process t1 first then we will have the following structure after it has
291-
// been processed.
292-
// t1 -> t2 -> t1_self
293-
// This is because when we started processing t2, we did not have the complete
294-
// t1 but its place holder t1_self.
295-
// Now if some entity requires t2, we will already have that in cache and will
296-
// return it. But this t2 refers to t1_self and not to t1. In mlir handling,
297-
// only those types are allowed to have _self reference which are wrapped by
298-
// entity whose reference it is. So t1 -> t2 -> t1_self is ok because the
299-
// t1_self reference can be resolved by the outer t1. But standalone t2 is not
300-
// because there will be no way to resolve it. Until this is fixed in mlir, we
301-
// avoid caching such types. Please see DebugTranslation::translateRecursive for
302-
// details on how mlir handles recursive types.
303-
static bool canCacheThisType(mlir::LLVM::DICompositeTypeAttr comTy) {
304-
for (auto el : comTy.getElements()) {
305-
if (auto mem =
306-
mlir::dyn_cast_if_present<mlir::LLVM::DIDerivedTypeAttr>(el)) {
307-
mlir::LLVM::DITypeAttr memTy = getUnderlyingType(mem.getBaseType());
308-
if (auto baseTy =
309-
mlir::dyn_cast_if_present<mlir::LLVM::DICompositeTypeAttr>(
310-
memTy)) {
311-
// We will not cache a type if one of its member meets the following
312-
// conditions:
313-
// 1. It is a structure type
314-
// 2. It is a place holder type (getIsRecSelf() is true)
315-
// 3. It is not a self reference. It is ok to have t1_self in t1.
316-
if (baseTy.getTag() == llvm::dwarf::DW_TAG_structure_type &&
317-
baseTy.getIsRecSelf() && (comTy.getRecId() != baseTy.getRecId()))
318-
return false;
319-
}
320-
}
321-
}
322-
return true;
323-
}
324-
325276
std::pair<std::uint64_t, unsigned short>
326277
DebugTypeGenerator::getFieldSizeAndAlign(mlir::Type fieldTy) {
327278
mlir::Type llvmTy;
@@ -343,6 +294,7 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
343294
if (iter != typeCache.end())
344295
return iter->second;
345296

297+
bool canCacheThisType = true;
346298
llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
347299
mlir::MLIRContext *context = module.getContext();
348300
auto recId = mlir::DistinctAttr::create(mlir::UnitAttr::get(context));
@@ -406,6 +358,62 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
406358
/*extra data=*/nullptr);
407359
elements.push_back(tyAttr);
408360
offset += llvm::alignTo(byteSize, byteAlign);
361+
362+
// Currently, the handling of recursive debug type in mlir has some
363+
// limitations that were discussed at the end of the thread for following
364+
// PR.
365+
// https://github.com/llvm/llvm-project/pull/106571
366+
//
367+
// Problem could be explained with the following example code:
368+
// type t2
369+
// type(t1), pointer :: p1
370+
// end type
371+
// type t1
372+
// type(t2), pointer :: p2
373+
// end type
374+
// In the description below, type_self means a temporary type that is
375+
// generated
376+
// as a place holder while the members of that type are being processed.
377+
//
378+
// If we process t1 first then we will have the following structure after
379+
// it has been processed.
380+
// t1 -> t2 -> t1_self
381+
// This is because when we started processing t2, we did not have the
382+
// complete t1 but its place holder t1_self.
383+
// Now if some entity requires t2, we will already have that in cache and
384+
// will return it. But this t2 refers to t1_self and not to t1. In mlir
385+
// handling, only those types are allowed to have _self reference which are
386+
// wrapped by entity whose reference it is. So t1 -> t2 -> t1_self is ok
387+
// because the t1_self reference can be resolved by the outer t1. But
388+
// standalone t2 is not because there will be no way to resolve it. Until
389+
// this is fixed in mlir, we avoid caching such types. Please see
390+
// DebugTranslation::translateRecursive for details on how mlir handles
391+
// recursive types.
392+
// The code below checks for situation where it will be unsafe to cache
393+
// a type to avoid this problem. We do that in 2 situations.
394+
// 1. If a member is record type, then its type would have been processed
395+
// before reaching here. If it is not in the cache, it means that it was
396+
// found to be unsafe to cache. So any type containing it will also not
397+
// be cached
398+
// 2. The type of the member is found in the cache but it is a place holder.
399+
// In this case, its recID should match the recID of the type we are
400+
// processing. This helps us to cache the following type.
401+
// type t
402+
// type(t), allocatable :: p
403+
// end type
404+
mlir::Type baseTy = getDerivedType(fieldTy);
405+
if (auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy)) {
406+
auto iter = typeCache.find(recTy);
407+
if (iter == typeCache.end())
408+
canCacheThisType = false;
409+
else {
410+
if (auto tyAttr =
411+
mlir::dyn_cast<mlir::LLVM::DICompositeTypeAttr>(iter->second)) {
412+
if (tyAttr.getIsRecSelf() && tyAttr.getRecId() != recId)
413+
canCacheThisType = false;
414+
}
415+
}
416+
}
409417
}
410418

411419
auto finalAttr = mlir::LLVM::DICompositeTypeAttr::get(
@@ -414,7 +422,7 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
414422
/*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8,
415423
/*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr,
416424
/*allocated=*/nullptr, /*associated=*/nullptr);
417-
if (canCacheThisType(finalAttr)) {
425+
if (canCacheThisType) {
418426
typeCache[Ty] = finalAttr;
419427
} else {
420428
auto iter = typeCache.find(Ty);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o -
2+
3+
! mainly test that this program does not cause an assertion failure
4+
! testcase for issue 122024
5+
6+
module m1
7+
type t1
8+
type(t2),pointer :: x1
9+
end type
10+
type t2
11+
type(t3),pointer :: x2
12+
end type
13+
type t3
14+
type(t1),pointer :: x3
15+
end type
16+
end
17+
18+
program test
19+
use m1
20+
type(t1),pointer :: foo
21+
allocate(foo)
22+
allocate(foo%x1)
23+
allocate(foo%x1%x2)
24+
allocate(foo%x1%x2%x3)
25+
call sub1(foo%x1)
26+
print *,'done'
27+
end program
28+
29+
subroutine sub1(bar)
30+
use m1
31+
type(t2) :: bar
32+
end subroutine

0 commit comments

Comments
 (0)