Skip to content

Commit a72f90a

Browse files
committed
[flang][debug] Improve handling of cyclic derived types.
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 agin 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. We previously had a check in the step 6 above to not cache it. 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 the following PR. llvm#106571 Fixes llvm#122024.
1 parent 9256485 commit a72f90a

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)