diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp index 4aa14ca2c2bdd6..e52812fb320cb1 100644 --- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp +++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp @@ -14,7 +14,6 @@ #include "DebugTypeGenerator.h" #include "flang/Optimizer/CodeGen/DescriptorModel.h" -#include "flang/Optimizer/CodeGen/TypeConverter.h" #include "flang/Optimizer/Support/InternalNames.h" #include "mlir/Pass/Pass.h" #include "llvm/ADT/ScopeExit.h" @@ -48,7 +47,7 @@ DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m, mlir::SymbolTable *symbolTable_, const mlir::DataLayout &dl) : module(m), symbolTable(symbolTable_), dataLayout{&dl}, - kindMapping(getKindMapping(m)) { + kindMapping(getKindMapping(m)), llvmTypeConverter(m, false, false, dl) { LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n"); mlir::MLIRContext *context = module.getContext(); @@ -160,10 +159,91 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType( dataLocation, /*rank=*/nullptr, allocated, associated); } +// If the type is a pointer or array type then gets its underlying type. +static mlir::LLVM::DITypeAttr getUnderlyingType(mlir::LLVM::DITypeAttr Ty) { + if (auto ptrTy = + mlir::dyn_cast_if_present(Ty)) { + if (ptrTy.getTag() == llvm::dwarf::DW_TAG_pointer_type) + Ty = getUnderlyingType(ptrTy.getBaseType()); + } + if (auto comTy = + mlir::dyn_cast_if_present(Ty)) { + if (comTy.getTag() == llvm::dwarf::DW_TAG_array_type) + Ty = getUnderlyingType(comTy.getBaseType()); + } + return Ty; +} + +// Currently, the handling of recursive debug type in mlir has some limitations. +// Those limitations were discussed at the end of the thread for following PR. +// https://github.com/llvm/llvm-project/pull/106571 +// +// Problem could be explained with the following example code: +// type t2 +// type(t1), pointer :: p1 +// end type +// type t1 +// type(t2), pointer :: p2 +// end type +// In the description below, type_self means a temporary type that is generated +// as a place holder while the members of that type are being processed. +// +// If we process t1 first then we will have the following structure after it has +// been processed. +// t1 -> t2 -> t1_self +// This is because when we started processing t2, we did not have the complete +// t1 but its place holder t1_self. +// Now if some entity requires t2, we will already have that in cache and will +// return it. But this t2 refers to t1_self and not to t1. In mlir handling, +// only those types are allowed to have _self reference which are wrapped by +// entity whose reference it is. So t1 -> t2 -> t1_self is ok because the +// t1_self reference can be resolved by the outer t1. But standalone t2 is not +// because there will be no way to resolve it. Until this is fixed in mlir, we +// avoid caching such types. Please see DebugTranslation::translateRecursive for +// details on how mlir handles recursive types. +static bool canCacheThisType(mlir::LLVM::DICompositeTypeAttr comTy) { + for (auto el : comTy.getElements()) { + if (auto mem = + mlir::dyn_cast_if_present(el)) { + mlir::LLVM::DITypeAttr memTy = getUnderlyingType(mem.getBaseType()); + if (auto baseTy = + mlir::dyn_cast_if_present( + memTy)) { + // We will not cache a type if one of its member meets the following + // conditions: + // 1. It is a structure type + // 2. It is a place holder type (getIsRecSelf() is true) + // 3. It is not a self reference. It is ok to have t1_self in t1. + if (baseTy.getTag() == llvm::dwarf::DW_TAG_structure_type && + baseTy.getIsRecSelf() && (comTy.getRecId() != baseTy.getRecId())) + return false; + } + } + } + return true; +} + mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType( fir::RecordType Ty, mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp) { + // Check if this type has already been converted. + auto iter = typeCache.find(Ty); + if (iter != typeCache.end()) + return iter->second; + + llvm::SmallVector elements; mlir::MLIRContext *context = module.getContext(); + auto recId = mlir::DistinctAttr::create(mlir::UnitAttr::get(context)); + // Generate a place holder TypeAttr which will be used if a member + // references the parent type. + auto comAttr = mlir::LLVM::DICompositeTypeAttr::get( + context, recId, /*isRecSelf=*/true, llvm::dwarf::DW_TAG_structure_type, + mlir::StringAttr::get(context, ""), fileAttr, /*line=*/0, scope, + /*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, /*sizeInBits=*/0, + /*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr, + /*allocated=*/nullptr, /*associated=*/nullptr); + typeCache[Ty] = comAttr; + auto result = fir::NameUniquer::deconstruct(Ty.getName()); if (result.first != fir::NameUniquer::NameKind::DERIVED_TYPE) return genPlaceholderType(context); @@ -171,18 +251,18 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType( fir::TypeInfoOp tiOp = symbolTable->lookup(Ty.getName()); unsigned line = (tiOp) ? getLineFromLoc(tiOp.getLoc()) : 1; - llvm::SmallVector elements; std::uint64_t offset = 0; for (auto [fieldName, fieldTy] : Ty.getTypeList()) { - auto result = fir::getTypeSizeAndAlignment(module.getLoc(), fieldTy, - *dataLayout, kindMapping); - // If we get a type whose size we can't determine, we will break the loop - // and generate the derived type with whatever components we have - // assembled thus far. - if (!result) - break; - auto [byteSize, byteAlign] = *result; + mlir::Type llvmTy; + if (auto boxTy = mlir::dyn_cast_or_null(fieldTy)) + llvmTy = + llvmTypeConverter.convertBoxTypeAsStruct(boxTy, getBoxRank(boxTy)); + else + llvmTy = llvmTypeConverter.convertType(fieldTy); + // FIXME: Handle non defaults array bound in derived types + uint64_t byteSize = dataLayout->getTypeSize(llvmTy); + unsigned short byteAlign = dataLayout->getTypeABIAlignment(llvmTy); mlir::LLVM::DITypeAttr elemTy = convertType(fieldTy, fileAttr, scope, /*declOp=*/nullptr); offset = llvm::alignTo(offset, byteAlign); @@ -195,12 +275,20 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType( offset += llvm::alignTo(byteSize, byteAlign); } - return mlir::LLVM::DICompositeTypeAttr::get( - context, llvm::dwarf::DW_TAG_structure_type, + auto finalAttr = mlir::LLVM::DICompositeTypeAttr::get( + context, recId, /*isRecSelf=*/false, llvm::dwarf::DW_TAG_structure_type, mlir::StringAttr::get(context, result.second.name), fileAttr, line, scope, /*baseType=*/nullptr, mlir::LLVM::DIFlags::Zero, offset * 8, /*alignInBits=*/0, elements, /*dataLocation=*/nullptr, /*rank=*/nullptr, /*allocated=*/nullptr, /*associated=*/nullptr); + if (canCacheThisType(finalAttr)) { + typeCache[Ty] = finalAttr; + } else { + auto iter = typeCache.find(Ty); + if (iter != typeCache.end()) + typeCache.erase(iter); + } + return finalAttr; } mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType( diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h index e3220f18958df2..b8a068e5ba148b 100644 --- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h +++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h @@ -14,6 +14,7 @@ #define FORTRAN_OPTIMIZER_TRANSFORMS_DEBUGTYPEGENERATOR_H #include "flang/Optimizer/CodeGen/CGOps.h" +#include "flang/Optimizer/CodeGen/TypeConverter.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Dialect/Support/FIRContext.h" #include "flang/Optimizer/Dialect/Support/KindMapping.h" @@ -68,10 +69,12 @@ class DebugTypeGenerator { mlir::SymbolTable *symbolTable; const mlir::DataLayout *dataLayout; KindMapping kindMapping; + fir::LLVMTypeConverter llvmTypeConverter; std::uint64_t dimsSize; std::uint64_t dimsOffset; std::uint64_t ptrSize; std::uint64_t lenOffset; + llvm::DenseMap typeCache; }; } // namespace fir diff --git a/flang/test/Integration/debug-cyclic-derived-type-2.f90 b/flang/test/Integration/debug-cyclic-derived-type-2.f90 new file mode 100644 index 00000000000000..c49c9d00957e80 --- /dev/null +++ b/flang/test/Integration/debug-cyclic-derived-type-2.f90 @@ -0,0 +1,22 @@ +! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s + +! mainly test that this program does not cause an assertion failure +module m + type t2 + type(t1), pointer :: p1 + end type + type t1 + type(t2), pointer :: p2 + integer abc + end type + type(t1) :: tee1 +end module + +program test + use m + type(t2) :: lc2 + print *, lc2%p1%abc +end program test + +! CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}}) +! CHECK-DAG: DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}}) diff --git a/flang/test/Integration/debug-cyclic-derived-type.f90 b/flang/test/Integration/debug-cyclic-derived-type.f90 index 03e06336a6e084..a26ffd19ef6b1e 100644 --- a/flang/test/Integration/debug-cyclic-derived-type.f90 +++ b/flang/test/Integration/debug-cyclic-derived-type.f90 @@ -11,5 +11,11 @@ module m type(t2) :: v3 end module -! CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}}) -! CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}}) +! CHECK-DAG: ![[T1:[0-9]+]] = {{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "t1"{{.*}}elements: ![[T1_ELEMS:[0-9]+]]) +! CHECK-DAG: ![[T1_ELEMS]] = !{![[T1_ELEM1:[0-9]+]]} +! CHECK-DAG: ![[T1_ELEM1]] = !DIDerivedType(tag: DW_TAG_member, name: "p", baseType: ![[T2P:[0-9]+]]{{.*}}) +! CHECK-DAG: ![[T2P]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[T2:[0-9]+]]{{.*}}) + +! CHECK-DAG: ![[T2]] = {{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "t2"{{.*}}elements: ![[T2_ELEMS:[0-9]+]]) +! CHECK-DAG: ![[T2_ELEMS]] = !{![[T2_ELEM1:[0-9]+]]} +! CHECK-DAG: ![[T2_ELEM1]] = !DIDerivedType(tag: DW_TAG_member, name: "v1", baseType: ![[T1]]{{.*}}) diff --git a/flang/test/Transforms/debug-derived-type-1.fir b/flang/test/Transforms/debug-derived-type-1.fir index e453db6ae6fbb7..26f7017f5f5a38 100644 --- a/flang/test/Transforms/debug-derived-type-1.fir +++ b/flang/test/Transforms/debug-derived-type-1.fir @@ -12,12 +12,18 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry, d %0 = fir.zero_bits !fir.type<_QMt1Tt_t1{age:i32,points:!fir.array<3x!fir.complex<4>>,cond:!fir.logical<1>,name:!fir.char<1,20>,ratio:f64}> fir.has_value %0 : !fir.type<_QMt1Tt_t1{age:i32,points:!fir.array<3x!fir.complex<4>>,cond:!fir.logical<1>,name:!fir.char<1,20>,ratio:f64}> } loc(#loc6) + fir.global @_QMtest_1Exyz : !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box,height:f32}>>>,v2:i32,m_alloc:!fir.box>>,v3:i32,m_first:!fir.box>>,v4:i32,m_p1:!fir.box>,v5:i32,m_p2:!fir.box>,v6:i32,m_p3:!fir.box>>,v7:i32}> { + %0 = fir.zero_bits !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box,height:f32}>>>,v2:i32,m_alloc:!fir.box>>,v3:i32,m_first:!fir.box>>,v4:i32,m_p1:!fir.box>,v5:i32,m_p2:!fir.box>,v6:i32,m_p3:!fir.box>>,v7:i32}> + fir.has_value %0 : !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box,height:f32}>>>,v2:i32,m_alloc:!fir.box>>,v3:i32,m_first:!fir.box>>,v4:i32,m_p1:!fir.box>,v5:i32,m_p2:!fir.box>,v6:i32,m_p3:!fir.box>>,v7:i32}> + } loc(#loc12) fir.type_info @_QMt1Tt_t1 noinit nodestroy nofinal : !fir.type<_QMt1Tt_t1{age:i32,points:!fir.array<3x!fir.complex<4>>,cond:!fir.logical<1>,name:!fir.char<1,20>,ratio:f64}> loc(#loc7) fir.type_info @_QMm_employeeTt_address noinit nodestroy nofinal : !fir.type<_QMm_employeeTt_address{house_number:i32}> loc(#loc1) fir.type_info @_QMm_employeeTt_person noinit nodestroy nofinal extends !fir.type<_QMm_employeeTt_address{house_number:i32}> : !fir.type<_QMm_employeeTt_person{t_address:!fir.type<_QMm_employeeTt_address{house_number:i32}>,name:!fir.char<1,20>}> loc(#loc2) fir.type_info @_QMm_employeeTt_date noinit nodestroy nofinal : !fir.type<_QMm_employeeTt_date{year:i32,month:i32,day:i32}> loc(#loc3) fir.type_info @_QMm_employeeTt_employee noinit nodestroy nofinal extends !fir.type<_QMm_employeeTt_person{t_address:!fir.type<_QMm_employeeTt_address{house_number:i32}>,name:!fir.char<1,20>}> : !fir.type<_QMm_employeeTt_employee{t_person:!fir.type<_QMm_employeeTt_person{t_address:!fir.type<_QMm_employeeTt_address{house_number:i32}>,name:!fir.char<1,20>}>,hired_date:!fir.type<_QMm_employeeTt_date{year:i32,month:i32,day:i32}>,monthly_salary:f32}> loc(#loc4) fir.type_info @_QFTt_pair noinit nodestroy nofinal : !fir.type<_QFTt_pair{i:i64,x:f64}> loc(#loc8) + fir.type_info @_QMtest_1Tt1 noinit nodestroy nofinal : !fir.type<_QMtest_1Tt1{name:!fir.char<1,20>,height:f32}> loc(#loc11) + fir.type_info @_QMtest_1Tsometype nofinal : !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box,height:f32}>>>,v2:i32,m_alloc:!fir.box>>,v3:i32,m_first:!fir.box>>,v4:i32,m_p1:!fir.box>,v5:i32,m_p2:!fir.box>,v6:i32,m_p3:!fir.box>>,v7:i32}> loc(#loc12) func.func @_QQmain() attributes {fir.bindc_name = "test"} { %1 = fir.alloca !fir.type<_QFTt_pair{i:i64,x:f64}> {bindc_name = "pair", uniq_name = "_QFEpair"} %2 = fircg.ext_declare %1 {uniq_name = "_QFEpair"} : (!fir.ref>) -> !fir.ref> loc(#loc9) @@ -34,6 +40,8 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry, d #loc8 = loc("derived1.f90":85:3) #loc9 = loc("derived1.f90":77:3) #loc10 = loc("derived1.f90":75:3) +#loc11 = loc("derived1.f90":95:3) +#loc12 = loc("derived1.f90":105:3) // CHECK-DAG: #[[INT_TY:.*]] = #llvm.di_basic_type @@ -47,27 +55,42 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry, d // CHECK-DAG: #[[MOD:.*]] = #llvm.di_module<{{.*}}name = "m_employee"{{.*}}> // CHECK-DAG: #[[MOD1:.*]] = #llvm.di_module<{{.*}}name = "t1"{{.*}}> // CHECK-DAG: #[[ELMA1:.*]] = #llvm.di_derived_type -// CHECK-DAG: #[[ADDR:.*]] = #llvm.di_composite_type +// CHECK-DAG: #[[ADDR:.*]] = #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_address"{{.*}}line = 24, scope = #[[MOD]], sizeInBits = 32, elements = #[[ELMA1]]> // CHECK-DAG: #[[ELMD1:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELMD2:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELMD3:.*]] = #llvm.di_derived_type -// CHECK-DAG: #[[DATE:.*]] = #llvm.di_composite_type +// CHECK-DAG: #[[DATE:.*]] = #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_date", file = #di_file, line = 17, scope = #[[MOD]], sizeInBits = 96, elements = #[[ELMD1]], #[[ELMD2]], #[[ELMD3]]> // CHECK-DAG: #[[ELMP1:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELMP2:.*]] = #llvm.di_derived_type -// CHECK-DAG: #[[PERS:.*]] = #llvm.di_composite_type +// CHECK-DAG: #[[PERS:.*]] = #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_person"{{.*}}line = 35, scope = #[[MOD]], sizeInBits = 192, elements = #[[ELMP1]], #[[ELMP2]]> // CHECK-DAG: #[[ELME1:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELME2:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELME3:.*]] = #llvm.di_derived_type -// CHECK-DAG: #[[EMP:.*]] = #llvm.di_composite_type +// CHECK-DAG: #[[EMP:.*]] = #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_employee"{{.*}}line = 46, scope = #[[MOD]], sizeInBits = 320, elements = #[[ELME1]], #[[ELME2]], #[[ELME3]]> // CHECK-DAG: #[[ELM1:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELM2:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELM3:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELM4:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELM5:.*]] = #llvm.di_derived_type -// CHECK-DAG: #llvm.di_composite_type +// CHECK-DAG: #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_t1"{{.*}}, line = 70, scope = #[[MOD1]], sizeInBits = 512, elements = #[[ELM1]], #[[ELM2]], #[[ELM3]], #[[ELM4]], #[[ELM5]]> // CHECK-DAG: #[[SP:.*]] = #llvm.di_subprogram // CHECK-DAG: #[[ELML1:.*]] = #llvm.di_derived_type // CHECK-DAG: #[[ELML2:.*]] = #llvm.di_derived_type -// CHECK-DAG: #llvm.di_composite_type +// CHECK-DAG: #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "t_pair"{{.*}}line = 85, scope = #di_subprogram, sizeInBits = 128, elements = #[[ELML1]], #[[ELML2]]> + +// CHECK-DAG: #[[E1:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E2:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E3:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E4:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E5:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E6:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E7:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E8:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E9:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E10:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E11:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E12:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[E13:.*]] = #llvm.di_derived_type +// CHECK-DAG: #llvm.di_composite_type<{{.*}}tag = DW_TAG_structure_type, name = "sometype"{{.*}}sizeInBits = 2144, elements = #[[E1]], #[[E2]], #[[E3]], #[[E4]], #[[E5]], #[[E6]], #[[E7]], #[[E8]], #[[E9]], #[[E10]], #[[E11]], #[[E12]], #[[E13]]>