-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[flang][debug] Support ClassType. #114809
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
Conversation
@llvm/pr-subscribers-flang-fir-hlfir Author: Abid Qadeer (abidh) ChangesThis PR adds the handling of If This is how it looks like in the debugger.
Full diff: https://github.com/llvm/llvm-project/pull/114809.diff 2 Files Affected:
diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
index a070c87137fa16..28c4b9fac74ec5 100644
--- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
+++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
@@ -617,6 +617,20 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
return convertRecordType(recTy, fileAttr, scope, declOp);
} else if (auto tupleTy = mlir::dyn_cast_if_present<mlir::TupleType>(Ty)) {
return convertTupleType(tupleTy, fileAttr, scope, declOp);
+ } else if (auto classTy = mlir::dyn_cast_if_present<fir::ClassType>(Ty)) {
+ // ClassType when passed to the function have double indirection so it
+ // is represented as pointer to type (and not type as a RecordType will be
+ // for example). If ClassType wraps a pointer or allocatable then we get
+ // the real underlying type to avoid translating the Ty to
+ // Ptr -> Ptr -> type.
+ mlir::Type elTy = classTy.getEleTy();
+ if (auto ptrTy = mlir::dyn_cast_if_present<fir::PointerType>(elTy))
+ elTy = ptrTy.getElementType();
+ else if (auto heapTy = mlir::dyn_cast_if_present<fir::HeapType>(elTy))
+ elTy = heapTy.getElementType();
+ return convertPointerLikeType(elTy, fileAttr, scope, declOp,
+ /*genAllocated=*/false,
+ /*genAssociated=*/false);
} else if (auto refTy = mlir::dyn_cast_if_present<fir::ReferenceType>(Ty)) {
auto elTy = refTy.getEleTy();
return convertPointerLikeType(elTy, fileAttr, scope, declOp,
diff --git a/flang/test/Transforms/debug-class-type.fir b/flang/test/Transforms/debug-class-type.fir
new file mode 100644
index 00000000000000..e11dc7b2a2086a
--- /dev/null
+++ b/flang/test/Transforms/debug-class-type.fir
@@ -0,0 +1,27 @@
+// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
+ fir.type_info @_QTtest_type nofinal : !fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}> dispatch_table {
+ fir.dt_entry "test_proc", @_QPtest_proc
+ } loc(#loc1)
+ func.func private @_QPtest_proc(%arg0: !fir.class<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>)
+
+ func.func @test() {
+ %0 = fir.address_of(@_QFEx) : !fir.ref<!fir.class<!fir.heap<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>>
+ %1 = fircg.ext_declare %0 {uniq_name = "_QFEx"} : (!fir.ref<!fir.class<!fir.heap<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>>) -> !fir.ref<!fir.class<!fir.heap<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>> loc(#loc3)
+ %2 = fir.address_of(@_QFEy) : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>>
+ %3 = fircg.ext_declare %2 {uniq_name = "_QFEy"} : (!fir.ref<!fir.class<!fir.ptr<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>>) -> !fir.ref<!fir.class<!fir.ptr<!fir.type<_QTtest_type{a:i32,b:!fir.box<!fir.heap<!fir.array<?x?xf32>>>}>>>> loc(#loc4)
+ return
+ } loc(#loc2)
+}
+
+#loc1 = loc("./simple.f90":2:1)
+#loc2 = loc("./simple.f90":10:1)
+#loc3 = loc("./simple.f90":15:1)
+#loc4 = loc("./simple.f90":22:1)
+
+// CHECK-DAG: #[[TY1:.*]] = #llvm.di_composite_type<{{.*}}name = "test_type"{{.*}}>
+// CHECK-DAG: #[[TY2:.*]] = #llvm.di_derived_type<tag = DW_TAG_pointer_type, name = "", baseType = #[[TY1]]{{.*}}>
+// CHECK-DAG: #llvm.di_subroutine_type<callingConvention = DW_CC_normal, types = #di_null_type, #[[TY2]]>
+// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "x"{{.*}}type = #[[TY2]]>
+// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "y"{{.*}}type = #[[TY2]]>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fir.class
type is used for polymorphic entities (including unlimited polymorphic entities, CLASS(*)
).
Does DWARF provide support for querying the dynamic type at runtime?
I saw dwarf has a DW_TAG_dynamic_type tag, but that seems related to POINTER/ALLOCATABLE length parametrized derived types rather than polymorphism.
@@ -617,6 +617,20 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr, | |||
return convertRecordType(recTy, fileAttr, scope, declOp); | |||
} else if (auto tupleTy = mlir::dyn_cast_if_present<mlir::TupleType>(Ty)) { | |||
return convertTupleType(tupleTy, fileAttr, scope, declOp); | |||
} else if (auto classTy = mlir::dyn_cast_if_present<fir::ClassType>(Ty)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ClassType and BoxType are implemented in the same way in the runtime, the difference is that the dynamic type inside a ClassType may mismatch its static type.
You can actually merge the handling of ClassType and BoxType by replacing auto boxTy = mlir::dyn_cast_or_null<fir::BoxType>(Ty)
with auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(Ty)
.
You would then get the static type description of the entity in the DWARF (which seems to be what is being done here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your comments. I updated the patch to handle it with the BoxType. I also made another small change to handle cases like CLASS(*)
.
Sorry I missed this comment earlier. I agree that
|
if (mlir::isa<fir::ClassType>(Ty)) | ||
return convertPointerLikeType(boxTy.unwrapInnerType(), fileAttr, scope, | ||
declOp, /*genAllocated=*/false, | ||
/*genAssociated=*/true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not get why ClassType
is bypassing the handling below made for BoxType and why genAssociated
would be always true for them even when they are not POINTERs.
With the exception of the polymorphic aspect (the fact that the dynamic type may be an extension of the static type), which seems not straightforward to represent in DWARF, a fir.class is just like a fir.box. So the representation of the static type of a fir.box and fir.class should be the the same I think.
My comment about CLASS(*)
may have mislead you here. I think it is OK to decide and agree how to represent those in a distinct patch, especially because they are also TYPE(*)
, so fir.box<none>
is also a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the code so that ClassType
is handled like any other BaseBoxType
. As its underlying type can be a RecordType
which was not being handled inside that if, I modified the default inside that if to be a pointer to underlying type instead of a place holder type.
This PR adds the handling of ClassType. It is treated as pointer to the underlying type. Note that ClassType when passed to the function have double indirection so it is represented as pointer to type (and not type as a RecordType will be for example). If ClassType wraps a pointer or allocatable then we take care to avoid double indirection. This is how it looks like in the debugger. subroutine test_proc (this) class(test_type), intent (inout) :: this allocate (this%b (3, 2)) call fill_array_2d (this%b) print *, this%a end (gdb) p this $6 = (PTR TO -> ( Type test_type )) 0x2052a0 (gdb) p this%a $7 = 0 (gdb) p this%b $8 = ((1, 2, 3) (4, 5, 6)) (gdb)
Merged handling of ClassType with BoxType. Used boxTy.unwrapInnerType() to avoid special casing for pointer or allocatable. Also improved convertPointerLikeType to better handle if underlying type in null or none.
Dont special case ClassTyp but handle it like a BoxType. Also in default case, generate a pointer to underlying type instead of a PlaceHolder type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This PR adds the handling of
ClassType
. It is treated as pointer to the underlying type. Note thatClassType
when passed to the function have double indirection so it is represented as pointer to type (compared to other types which may have a single indirection).If
ClassType
wraps a pointer or allocatable then we take care to generate it as PTR -> type (and not PTR -> PTR -> type).This is how it looks like in the debugger.