Skip to content
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

[flang][debug] Support derived type components with box types. #109424

Merged
merged 6 commits into from
Sep 30, 2024

Conversation

abidh
Copy link
Contributor

@abidh abidh commented Sep 20, 2024

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.

This PR enhances getTypeSizeAndAlignment to support fir.box. The implementation uses the following formula to calculate the size.
size = offset_of_dims_field + (rank * size_of_dims_field)
If an addendum is required then size of its 2 field is also added to the final size.

The code to calculate the offset of the descriptor was already present in the DebugTypeGenerator.cpp. It has been moved to a separate file so that it can be used in multiple places.

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.

I tested it with the following derived type and the values of all the fields were correct in the debugger. The debug-cyclic-derived-type.f90 which tests the cyclic dependency has been updated and works as expected.

  type :: sometype
    integer :: m_array(3) = 42
    type(t1), allocatable :: m_vt1
    integer v2
    integer, allocatable :: m_alloc(:)
    integer v3
    character(len=:), allocatable :: m_first
    integer v4
    integer, pointer :: m_p1 => my_target
    integer v5
    integer, pointer :: m_p2! => NULL()
    integer v6
    integer, pointer :: m_p3(:)
    integer v7
  end type

Fixes #108001.

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.

This PR enhances `getTypeSizeAndAlignment` to support `fir.box`. The
implementation uses the following formula to calculate the size.
size = offset_of_dims_field + (rank * size_of_dims_field)
If an addendum is required then size of its 2 field is also added to the
final size.

The code to calculate the offset of the descriptor was already present
in the `DebugTypeGenerator.cpp`. It has been moved to a separate file
so that it can be used in multiple places.

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 llvm#108001.
@llvmbot
Copy link
Collaborator

llvmbot commented Sep 20, 2024

@llvm/pr-subscribers-flang-fir-hlfir

@llvm/pr-subscribers-flang-codegen

Author: Abid Qadeer (abidh)

Changes

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.

This PR enhances getTypeSizeAndAlignment to support fir.box. The implementation uses the following formula to calculate the size.
size = offset_of_dims_field + (rank * size_of_dims_field)
If an addendum is required then size of its 2 field is also added to the final size.

The code to calculate the offset of the descriptor was already present in the DebugTypeGenerator.cpp. It has been moved to a separate file so that it can be used in multiple places.

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.

I tested it with the following derived type and the values of all the fields were correct in the debugger. The debug-cyclic-derived-type.f90 which tests the cyclic dependency has been updated and works as expected.

  type :: sometype
    integer :: m_array(3) = 42
    type(t1), allocatable :: m_vt1
    integer v2
    integer, allocatable :: m_alloc(:)
    integer v3
    character(len=:), allocatable :: m_first
    integer v4
    integer, pointer :: m_p1 => my_target
    integer v5
    integer, pointer :: m_p2! => NULL()
    integer v6
    integer, pointer :: m_p3(:)
    integer v7
  end type

Fixes #108001.


Patch is 21.17 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/109424.diff

6 Files Affected:

  • (added) flang/include/flang/Optimizer/CodeGen/DescriptorOffsets.h (+39)
  • (modified) flang/lib/Optimizer/Dialect/FIRType.cpp (+28)
  • (modified) flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp (+25-7)
  • (modified) flang/lib/Optimizer/Transforms/DebugTypeGenerator.h (+1)
  • (modified) flang/test/Integration/debug-cyclic-derived-type.f90 (+8-2)
  • (modified) flang/test/Transforms/debug-derived-type-1.fir (+29-6)
diff --git a/flang/include/flang/Optimizer/CodeGen/DescriptorOffsets.h b/flang/include/flang/Optimizer/CodeGen/DescriptorOffsets.h
new file mode 100644
index 00000000000000..1d62197f38fca3
--- /dev/null
+++ b/flang/include/flang/Optimizer/CodeGen/DescriptorOffsets.h
@@ -0,0 +1,39 @@
+//===-- DescriptorOffsets.h -- offsets of descriptors fields ---*- C++ -*-====//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OPTIMIZER_DESCRIPTOR_OFFSETS_H
+#define OPTIMIZER_DESCRIPTOR_OFFSETS_H
+
+#include "flang/Optimizer/CodeGen/DescriptorModel.h"
+
+namespace fir {
+
+/// Calculate offset of any field in the descriptor.
+template <int Field>
+static std::uint64_t getDescComponentOffset(const mlir::DataLayout &dl,
+                                            mlir::MLIRContext *context,
+                                            mlir::Type fieldType) {
+  static_assert(Field > 0 && Field < 8);
+  mlir::Type previousFieldType = getDescFieldTypeModel<Field - 1>()(context);
+  std::uint64_t previousOffset =
+      getDescComponentOffset<Field - 1>(dl, context, previousFieldType);
+  std::uint64_t offset = previousOffset + dl.getTypeSize(previousFieldType);
+  std::uint64_t fieldAlignment = dl.getTypeABIAlignment(fieldType);
+  return llvm::alignTo(offset, fieldAlignment);
+}
+
+template <>
+std::uint64_t getDescComponentOffset<0>(const mlir::DataLayout &dl,
+                                        mlir::MLIRContext *context,
+                                        mlir::Type fieldType) {
+  return 0;
+}
+
+} // namespace fir
+
+#endif // OPTIMIZER_DESCRIPTOR_OFFSETS_H
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index 05f644654efe1b..694c66e2fc9969 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -13,6 +13,9 @@
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/ISO_Fortran_binding_wrapper.h"
 #include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/CodeGen/DescriptorModel.h"
+#include "flang/Optimizer/CodeGen/DescriptorOffsets.h"
+#include "flang/Optimizer/CodeGen/TypeConverter.h"
 #include "flang/Optimizer/Dialect/FIRDialect.h"
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
 #include "flang/Tools/PointerModels.h"
@@ -1455,6 +1458,31 @@ fir::getTypeSizeAndAlignment(mlir::Location loc, mlir::Type ty,
       compSize *= character.getLen();
     return std::pair{compSize, compAlign};
   }
+  if (auto boxTy = mlir::dyn_cast<fir::BoxType>(ty)) {
+    mlir::MLIRContext *context = boxTy.getContext();
+    mlir::Type ptrType = getDescFieldTypeModel<kAddrPosInBox>()(context);
+    mlir::Type dimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
+    std::uint64_t size =
+        getDescComponentOffset<kDimsPosInBox>(dl, context, dimsType);
+    unsigned rank = getBoxRank(ty);
+    if (rank > 0) {
+      std::uint64_t dimsSize = dl.getTypeSize(dimsType);
+      size += (rank * dimsSize);
+    }
+    if (boxHasAddendum(boxTy)) {
+      mlir::Type optType =
+          getExtendedDescFieldTypeModel<kOptTypePtrPosInBox>()(context);
+      mlir::Type rowType =
+          getExtendedDescFieldTypeModel<kOptRowTypePosInBox>()(context);
+      std::uint64_t fieldSize = dl.getTypeSize(optType);
+      std::uint64_t fieldAlignment = dl.getTypeABIAlignment(optType);
+      size += llvm::alignTo(fieldSize, fieldAlignment);
+      fieldSize = dl.getTypeSize(rowType);
+      fieldAlignment = dl.getTypeABIAlignment(rowType);
+      size += llvm::alignTo(fieldSize, fieldAlignment);
+    }
+    return std::pair{size, (unsigned short)dl.getTypeSize(ptrType)};
+  }
   return std::nullopt;
 }
 
diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
index 1390fae062b934..65784224e07339 100644
--- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
+++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
@@ -13,7 +13,7 @@
 #define DEBUG_TYPE "flang-debug-type-generator"
 
 #include "DebugTypeGenerator.h"
-#include "flang/Optimizer/CodeGen/DescriptorModel.h"
+#include "flang/Optimizer/CodeGen/DescriptorOffsets.h"
 #include "flang/Optimizer/CodeGen/TypeConverter.h"
 #include "flang/Optimizer/Support/InternalNames.h"
 #include "mlir/Pass/Pass.h"
@@ -59,11 +59,11 @@ DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
   mlir::Type llvmPtrType = getDescFieldTypeModel<kAddrPosInBox>()(context);
   mlir::Type llvmLenType = getDescFieldTypeModel<kElemLenPosInBox>()(context);
   dimsOffset =
-      getComponentOffset<kDimsPosInBox>(*dataLayout, context, llvmDimsType);
+      getDescComponentOffset<kDimsPosInBox>(*dataLayout, context, llvmDimsType);
   dimsSize = dataLayout->getTypeSize(llvmDimsType);
   ptrSize = dataLayout->getTypeSize(llvmPtrType);
-  lenOffset =
-      getComponentOffset<kElemLenPosInBox>(*dataLayout, context, llvmLenType);
+  lenOffset = getDescComponentOffset<kElemLenPosInBox>(*dataLayout, context,
+                                                       llvmLenType);
 }
 
 static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context,
@@ -163,7 +163,24 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
 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<mlir::LLVM::DINodeAttr> 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,7 +188,6 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
   fir::TypeInfoOp tiOp = symbolTable->lookup<fir::TypeInfoOp>(Ty.getName());
   unsigned line = (tiOp) ? getLineFromLoc(tiOp.getLoc()) : 1;
 
-  llvm::SmallVector<mlir::LLVM::DINodeAttr> elements;
   std::uint64_t offset = 0;
   for (auto [fieldName, fieldTy] : Ty.getTypeList()) {
     auto result = fir::getTypeSizeAndAlignment(module.getLoc(), fieldTy,
@@ -195,12 +211,14 @@ 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);
+  typeCache[Ty] = finalAttr;
+  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..104f3591d5ba8a 100644
--- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h
+++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.h
@@ -72,6 +72,7 @@ class DebugTypeGenerator {
   std::uint64_t dimsOffset;
   std::uint64_t ptrSize;
   std::uint64_t lenOffset;
+  llvm::DenseMap<mlir::Type, mlir::LLVM::DITypeAttr> typeCache;
 };
 
 } // namespace fir
diff --git a/flang/test/Integration/debug-cyclic-derived-type.f90 b/flang/test/Integration/debug-cyclic-derived-type.f90
index 03e06336a6e084..0325f62a0a9b4f 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]]{{.*}})
\ No newline at end of file
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<!llvm.ptr<272>, 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<!fir.heap<!fir.type<_QMtest_1Tt1{name:!fir.char<1,20>,height:f32}>>>,v2:i32,m_alloc:!fir.box<!fir.heap<!fir.array<?xi32>>>,v3:i32,m_first:!fir.box<!fir.heap<!fir.char<1,?>>>,v4:i32,m_p1:!fir.box<!fir.ptr<i32>>,v5:i32,m_p2:!fir.box<!fir.ptr<i32>>,v6:i32,m_p3:!fir.box<!fir.ptr<!fir.array<?xi32>>>,v7:i32}> {
+    %0 = fir.zero_bits !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box<!fir.heap<!fir.type<_QMtest_1Tt1{name:!fir.char<1,20>,height:f32}>>>,v2:i32,m_alloc:!fir.box<!fir.heap<!fir.array<?xi32>>>,v3:i32,m_first:!fir.box<!fir.heap<!fir.char<1,?>>>,v4:i32,m_p1:!fir.box<!fir.ptr<i32>>,v5:i32,m_p2:!fir.box<!fir.ptr<i32>>,v6:i32,m_p3:!fir.box<!fir.ptr<!fir.array<?xi32>>>,v7:i32}>
+    fir.has_value %0 : !fir.type<_QMtest_1Tsometype{m_array:!fir.array<3xi32>,m_vt1:!fir.box<!fir.heap<!fir.type<_QMtest_1Tt1{name:!fir.char<1,20>,height:f32}>>>,v2:i32,m_alloc:!fir.box<!fir.heap<!fir.array<?xi32>>>,v3:i32,m_first:!fir.box<!fir.heap<!fir.char<1,?>>>,v4:i32,m_p1:!fir.box<!fir.ptr<i32>>,v5:i32,m_p2:!fir.box<!fir.ptr<i32>>,v6:i32,m_p3:!fir.box<!fir.ptr<!fir.array<?xi32>>>,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<!fir.heap<!fir.type<_QMtest_1Tt1{name:!fir.char<1,20>,height:f32}>>>,v2:i32,m_alloc:!fir.box<!fir.heap<!fir.array<?xi32>>>,v3:i32,m_first:!fir.box<!fir.heap<!fir.char<1,?>>>,v4:i32,m_p1:!fir.box<!fir.ptr<i32>>,v5:i32,m_p2:!fir.box<!fir.ptr<i32>>,v6:i32,m_p3:!fir.box<!fir.ptr<!fir.array<?xi32>>>,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.type<_QFTt_pair{i:i64,x:f64}>>) -> !fir.ref<!fir.type<_QFTt_pair{i:i64,x:f64}>> loc(#loc9)
@@ -34,6 +40,8 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<!llvm.ptr<272>, 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<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
@@ -47,27 +55,42 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<!llvm.ptr<272>, 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<tag = DW_TAG_member, name = "house_number", baseType = #[[INT_TY]], sizeInBits = 32, alignInBits = 32>
-// 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: #[[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<tag = DW_TAG_member, name = "year", baseType = #[[INT_TY]], sizeInBits = 32, alignInBits = 32>
 // CHECK-DAG: #[[ELMD2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "month", baseType = #[[INT_TY]], sizeInBits = 32, alignInBits = 32, offsetInBits = 32>
 // CHECK-DAG: #[[ELMD3:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "day", baseType = #[[INT_TY]], sizeInBits = 32, alignInBits = 32, offsetInBits = 64>
-// 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: #[[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<tag = DW_TAG_member, name = "t_address", baseType = #[[ADDR]], sizeInBits = 32, alignInBits = 32>
 // CHECK-DAG: #[[ELMP2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "name", baseType = #[[STR_TY]], sizeInBits = 160, alignInBits = 8, offsetInBits = 32>
-// 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: #[[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<tag = DW_TAG_member, name = "t_person", baseType = #[[PERS]], sizeInBits = 192, alignInBits = 32>
 // CHECK-DAG: #[[ELME2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "hired_date", baseType = #[[DATE]], sizeInBits = 96, alignInBits = 32, offsetInBits = 192>
 // CHECK-DAG: #[[ELME3:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "monthly_salary", baseType = #[[REAL4_TY]], sizeInBits = 32, alignInBits = 32, offsetInBits = 288>
-// 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: #[[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<tag = DW_TAG_member, name = "age", baseType = #[[INT_TY]], sizeInBits = 32, alignInBits = 32>
 // CHECK-DAG: #[[ELM2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "points", baseType = #[[CMX_ARR]], sizeInBits = 192, alignInBits = 32, offsetInBits = 32>
 // CHECK-DAG: #[[ELM3:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "cond", baseType = #[[LOG_TY]], sizeInBits = 8, alignInBits = 8, offsetInBits = 224>
 // CHECK-DAG: #[[ELM4:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "name", baseType = #[[STR_TY]], sizeInBits = 160, alignInBits = 8, offsetInBits = 232>
 // CHECK-DAG: #[[ELM5:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "ratio", baseType = #[[REAL8_TY]], sizeInBits = 64, alignInBits = 64, offsetInBits = 448>
-// 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: #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<tag = DW_TAG_member, name = "i", baseType = #[[INT8_TY]], sizeInBits = 64, alignInBits = 64>
 // CHECK-DAG: #[[ELML2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "x", baseType = #[[REAL8_TY]], sizeInBits = 64, alignInBits = 64, offsetInBits = 64>
-// 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: #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<tag = DW_TAG_member, name = "m_array", baseType = #{{.*}}, sizeInBits = 96, alignInBits = 32>
+// CHECK-DAG: #[[E2:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "m_vt1", baseType = #{{.*}}, sizeInBits = 320, alignInBits = 64, offsetInBits = 128>
+// CHECK-DAG: #[[E3:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "v2", baseType = #{{.*}}, sizeInBits = 32, alignInBits = 32, offsetInBits = 448>
+// CHECK-DAG: #[[E4:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "m_alloc", baseType = #{{.*}}, sizeInBits = 384, alignInBits = 64, offsetInBits = 512>
+// CHECK-DAG: #[[E5:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "v3", baseType = #{{.*}}, sizeInBits = 32, alignInBits = 32, offsetInBits = 896>
+// CHECK-DAG: #[[E6:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "m_first", baseType = #{{.*}}, sizeInBits = 192, alignInBits = 64, offsetInBits = 960>
+// CHECK-DAG: #[[E7:.*]] = #llvm.di_derived_type<tag = DW_TAG_member, name = "v4", baseType = #{{.*}}, sizeInBits = 32, alignInBits = 32, offsetInBits = 1152>
+// CHECK-DAG: #[[E8:.*]] = ...
[truncated]

Copy link
Contributor

@pawosm-arm pawosm-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested it locally, it works, thanks for fixing this

Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fir.box type is rea

@@ -13,6 +13,9 @@
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/ISO_Fortran_binding_wrapper.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
#include "flang/Optimizer/CodeGen/DescriptorOffsets.h"
#include "flang/Optimizer/CodeGen/TypeConverter.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far I was rather against handling fir.box<> into getTypeSizeAndAlignment because to me it is meant to be an abstract type as much possible (could be implemented as a CFI descriptor, or some descriptor compatible with existing compilers if we wanted). Instead, I was more in favor of translating type to LLVM and using getTypeSizeAndAlignment at that level. That way, the codegen of abstract FIR types is centralized in the type codegen and needs not to be spilled in other places.

Now I understand this is inconvenient given there are several places that needs to know about the size at different stage of the compilation.

But as far as debugging is concerned, I would still be in favor of calling translating types to LLVM and calling getTypeSizeAndAlignment with that.

Otherwise, we at least need to centralize all low level descriptor related tools in some flang/Optimizer/Dialect/Support/DescriptorModel.h file. The Dialect library should not depend upon Codegen.

Copy link
Contributor Author

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 have updated the PR and it does not use getTypeSizeAndAlignment to get the size for any type. We use LLVMTypeConverter to convert fieldTy into LLVM type and then use DataLayout to get the size. It all seems to works ok. My concern was whether it is safe to call DataLayout::getTypeSize on any type that is returned by LLVMTypeConverter::convertType. I did not see any problem with my testing but would be happy to add any type specific check.

mlir::Type dimsType = getDescFieldTypeModel<kDimsPosInBox>()(context);
std::uint64_t size =
getDescComponentOffset<kDimsPosInBox>(dl, context, dimsType);
unsigned rank = getBoxRank(ty);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about assumed-ranks, the descriptor size is not a compile time constant? They cannot be used as derived type components, but this generic helper would need to handle them or bail correctly.

Use LLVMTypeConverter to convert the fir.box into an mlir::Type and then get its size from DataLayout.
Copy link
Contributor

@jeanPerier jeanPerier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the simplification, LGTM. Getting the size on the LLVM type is safe (the only trick/ugliness is to take care of the fir.box translation to LLVM that is not context free, and you did take care of that).

std::uint64_t offset = 0;
LLVMTypeConverter llvmTypeConverter(module, false, false, *dataLayout);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely not a trivial class/ctor, and I would not be surprised if it did caching somewhere. Probably best to keep it at the same level as the dataLayout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

This helps avoid its instantiation for every RecordType.
This commit fixes an issue that can cause an assertion to fail in some
circumstances at
https://github.com/llvm/llvm-project/blob/26029d77a57cb4aaa1479064109e985a90d0edd8/mlir/lib/Target/LLVMIR/DebugTranslation.cpp#L270.
The issue relates to the handling of recursive debug type in mlir.
See the discussion at the end of follow PR for more details.
llvm#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 contains. 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. Please see DebugTranslation::translateRecursive for
details on how mlir handles recursive types.

The fix is not to cache the type that will fail if used standalone.
@abidh
Copy link
Contributor Author

abidh commented Sep 25, 2024

While testing, I noticed an assertion failure at the following location. I got the following advice when I asked how best to handle the issue with the author of the code. I have implemented this in this commit by not caching a type if we know that its standalone use could cause a problem. This will result a new type attribute to be generated for that type if it is used standalone.

@abidh abidh merged commit d556e38 into llvm:main Sep 30, 2024
8 checks passed
xgupta pushed a commit to xgupta/llvm-project that referenced this pull request Oct 4, 2024
…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 llvm#108001.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:codegen flang:fir-hlfir flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[flang][debug] Derived types with allocatable members are not handled correctly.
4 participants