[flang-commits] [flang] [flang][nfc] replace fir.dispatch_table with more generic fir.type_info (PR #68309)

via flang-commits flang-commits at lists.llvm.org
Thu Oct 5 06:39:40 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-codegen

<details>
<summary>Changes</summary>

The goal is to progressively propagate all the derived type info that is currently in the runtime type info globals into a FIR operation that can be easily queried and used by FIR/HLFIR passes.

When this will be complete, the last step will be to stop generating the runtime info global in lowering, but to do that later in or just before codegen to keep the FIR files readable (on the added type-info.f90 tests, the lowered runtime info globals takes a wooping 2.6 millions characters of 1600 lines the FIR textual output. The fir.type_info that contains all the info required to generate those globals for such "trivial" types takes 1721 characters on 9 lines).

So far this patch simply starts by replacing the fir.dispatch_table operation by the fir.type_info operation and to add the noinit/ nofinal/nodestroy flags to it. These flags will soon be used in HLFIR to better rewrite hlfir.assign with derived types.

---

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


16 Files Affected:

- (modified) flang/include/flang/Lower/AbstractConverter.h (+4-6) 
- (modified) flang/include/flang/Optimizer/Builder/FIRBuilder.h (-5) 
- (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+47-18) 
- (modified) flang/include/flang/Optimizer/Support/Utils.h (+8-7) 
- (modified) flang/lib/Lower/Bridge.cpp (+90-99) 
- (modified) flang/lib/Lower/ConvertType.cpp (+1-3) 
- (modified) flang/lib/Optimizer/Builder/FIRBuilder.cpp (-12) 
- (modified) flang/lib/Optimizer/CodeGen/CodeGen.cpp (+19-18) 
- (modified) flang/lib/Optimizer/Dialect/FIROps.cpp (+22-61) 
- (modified) flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp (+7-9) 
- (modified) flang/test/Fir/array-value-copy-cam4.fir (+1-1) 
- (modified) flang/test/Fir/convert-to-llvm-invalid.fir (+18-7) 
- (modified) flang/test/Fir/dispatch.f90 (+6-6) 
- (modified) flang/test/Fir/fir-ops.fir (+5-2) 
- (added) flang/test/Lower/HLFIR/type-info.f90 (+60) 
- (modified) flang/test/Lower/dispatch-table.f90 (+4-4) 


``````````diff
diff --git a/flang/include/flang/Lower/AbstractConverter.h b/flang/include/flang/Lower/AbstractConverter.h
index 477c8164a18eafd..c792e75f1146499 100644
--- a/flang/include/flang/Lower/AbstractConverter.h
+++ b/flang/include/flang/Lower/AbstractConverter.h
@@ -212,12 +212,10 @@ class AbstractConverter {
 
   /// Register a runtime derived type information object symbol to ensure its
   /// object will be generated as a global.
-  virtual void registerRuntimeTypeInfo(mlir::Location loc,
-                                       SymbolRef typeInfoSym) = 0;
-
-  virtual void registerDispatchTableInfo(
-      mlir::Location loc,
-      const Fortran::semantics::DerivedTypeSpec *typeSpec) = 0;
+  virtual void
+  registerTypeInfo(mlir::Location loc, SymbolRef typeInfoSym,
+                   const Fortran::semantics::DerivedTypeSpec &typeSpec,
+                   fir::RecordType type) = 0;
 
   //===--------------------------------------------------------------------===//
   // Locations
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index bba8bc23c26aa51..0b36186d68a4614 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -254,11 +254,6 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
                         bodyBuilder, linkage);
   }
 
-  /// Create a fir::DispatchTable operation.
-  fir::DispatchTableOp createDispatchTableOp(mlir::Location loc,
-                                             llvm::StringRef name,
-                                             llvm::StringRef parentName);
-
   /// Convert a StringRef string into a fir::StringLitOp.
   fir::StringLitOp createStringLitOp(mlir::Location loc,
                                      llvm::StringRef string);
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index a57add9f731979d..0329a5325a21c9c 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2778,6 +2778,10 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
           (*this)->getAttrOfType<mlir::StringAttr>(
               mlir::SymbolTable::getSymbolAttrName()).getValue());
     }
+
+    bool isInitialized() {
+      return getInitVal() || hasInitializationBody();
+    }
   }];
 }
 
@@ -2809,20 +2813,31 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
 
 def ImplicitFirTerminator : SingleBlockImplicitTerminator<"FirEndOp">;
 
-def fir_DispatchTableOp : fir_Op<"dispatch_table",
+def fir_TypeInfoOp : fir_Op<"type_info",
     [IsolatedFromAbove, Symbol, ImplicitFirTerminator]> {
-  let summary = "Dispatch table definition";
+  let summary = "Derived type information";
 
   let description = [{
-    Define a dispatch table for a derived type with type-bound procedures.
+    Define extra information about a !fir.type<> that represents
+    a Fortran derived type.
 
-    A dispatch table is an untyped symbol that contains a list of associations
+    The optional dispatch table region defines a dispatch table with the derived
+    type type-bound procedures. It contains a list of associations
     between method identifiers and corresponding `FuncOp` symbols.
-
     The ordering of associations in the map is determined by the front end.
 
+    The "no_init" flag indicates that this type has no components requiring default
+    initialization (including setting allocatable component to a clean deallocated
+    state).
+
+    The "no_destroy" flag indicates that there are no allocatable components
+    that require deallocation.
+
+    The "no_final" flag indicates that there are no final methods for this type,
+    for its parents ,or for components.
+
     ```mlir
-      fir.dispatch_table @_QDTMquuzTfoo {
+      fir.type_info @_QMquuzTfoo noinit nofinal : !fir.type<_QMquuzTfoo{i:i32}> dispatch_table {
         fir.dt_entry method1, @_QFNMquuzTfooPmethod1AfooR
         fir.dt_entry method2, @_QFNMquuzTfooPmethod2AfooII
       }
@@ -2831,32 +2846,46 @@ def fir_DispatchTableOp : fir_Op<"dispatch_table",
 
   let arguments = (ins
     SymbolNameAttr:$sym_name,
-    OptionalAttr<StrAttr>:$parent
+    TypeAttr:$type,
+    OptionalAttr<TypeAttr>:$parent_type,
+    UnitAttr:$no_init,
+    UnitAttr:$no_destroy,
+    UnitAttr:$no_final
   );
 
-  let hasCustomAssemblyFormat = 1;
   let hasVerifier = 1;
 
-  let regions = (region AnyRegion:$region);
+  let regions = (region MaxSizedRegion<1>:$dispatch_table);
 
-  let skipDefaultBuilders = 1;
   let builders = [
-    OpBuilder<(ins "llvm::StringRef":$name, "mlir::Type":$type,
-      "llvm::StringRef":$parent,
+    OpBuilder<(ins "fir::RecordType":$type, "fir::RecordType":$parent_type,
       CArg<"llvm::ArrayRef<mlir::NamedAttribute>", "{}">:$attrs)>
   ];
 
-  let extraClassDeclaration = [{
-    static constexpr llvm::StringRef getParentAttrNameStr() { return "parent"; }
-    static constexpr llvm::StringRef getExtendsKeyword() { return "extends"; }
+  let assemblyFormat = [{
+    $sym_name (`noinit` $no_init^)? (`nodestroy` $no_destroy^)?
+    (`nofinal` $no_final^)? (`extends` $parent_type^)? attr-dict `:` $type
+    (`dispatch_table` $dispatch_table^)?
+  }];
 
-    mlir::Block &getBlock() {
-      return getRegion().front();
+  let extraClassDeclaration = [{
+    fir::RecordType getRecordType() {
+      return mlir::cast<fir::RecordType>(getType());
+    }
+    fir::RecordType getIfParentType() {
+      if (auto parentType = getParentType())
+        return mlir::cast<fir::RecordType>(*parentType);
+      return {};
+    }
+    std::optional<llvm::StringRef> getIfParentName() {
+      if (auto parentType = getIfParentType())
+        return parentType.getName();
+      return std::nullopt;
     }
   }];
 }
 
-def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"DispatchTableOp">]> {
+def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"TypeInfoOp">]> {
   let summary = "map entry in a dispatch table";
 
   let description = [{
diff --git a/flang/include/flang/Optimizer/Support/Utils.h b/flang/include/flang/Optimizer/Support/Utils.h
index ebf506543ebf4a2..d5b045924f3c0cb 100644
--- a/flang/include/flang/Optimizer/Support/Utils.h
+++ b/flang/include/flang/Optimizer/Support/Utils.h
@@ -36,21 +36,22 @@ using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
 inline void buildBindingTables(BindingTables &bindingTables,
                                mlir::ModuleOp mod) {
 
-  // The binding tables are defined in FIR from lowering as fir.dispatch_table
-  // operation. Go through each binding tables and store the procedure name and
+  // The binding tables are defined in FIR after lowering inside fir.type_info
+  // operations. Go through each binding tables and store the procedure name and
   // binding index for later use by the fir.dispatch conversion pattern.
-  for (auto dispatchTableOp : mod.getOps<fir::DispatchTableOp>()) {
+  for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
     unsigned bindingIdx = 0;
     BindingTable bindings;
-    if (dispatchTableOp.getRegion().empty()) {
-      bindingTables[dispatchTableOp.getSymName()] = bindings;
+    if (typeInfo.getDispatchTable().empty()) {
+      bindingTables[typeInfo.getSymName()] = bindings;
       continue;
     }
-    for (auto dtEntry : dispatchTableOp.getBlock().getOps<fir::DTEntryOp>()) {
+    for (auto dtEntry :
+         typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
       bindings[dtEntry.getMethod()] = bindingIdx;
       ++bindingIdx;
     }
-    bindingTables[dispatchTableOp.getSymName()] = bindings;
+    bindingTables[typeInfo.getSymName()] = bindings;
   }
 }
 
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index ee838b3b4a546b9..5ac4d822faaae58 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -137,34 +137,41 @@ struct ConstructContext {
   Fortran::lower::StatementContext &stmtCtx; // construct exit code
 };
 
-/// Helper class to generate the runtime type info global data. This data
-/// is required to describe the derived type to the runtime so that it can
-/// operate over it. It must be ensured this data will be generated for every
-/// derived type lowered in the current translated unit. However, this data
+/// Helper class to generate the runtime type info global data and the
+/// fir.type_info operations that contain the dipatch tables (if any).
+/// The type info global data is required to describe the derived type to the
+/// runtime so that it can operate over it.
+/// It must be ensured these operations will be generated for every derived type
+/// lowered in the current translated unit. However, these operations
 /// cannot be generated before FuncOp have been created for functions since the
 /// initializers may take their address (e.g for type bound procedures). This
-/// class allows registering all the required runtime type info while it is not
-/// possible to create globals, and to generate this data after function
-/// lowering.
-class RuntimeTypeInfoConverter {
+/// class allows registering all the required type info while it is not
+/// possible to create GlobalOp/TypeInfoOp, and to generate this data afte
+/// function lowering.
+class TypeInfoConverter {
   /// Store the location and symbols of derived type info to be generated.
   /// The location of the derived type instantiation is also stored because
-  /// runtime type descriptor symbol are compiler generated and cannot be mapped
-  /// to user code on their own.
-  struct TypeInfoSymbol {
+  /// runtime type descriptor symbols are compiler generated and cannot be
+  /// mapped to user code on their own.
+  struct TypeInfo {
     Fortran::semantics::SymbolRef symbol;
+    const Fortran::semantics::DerivedTypeSpec &typeSpec;
+    fir::RecordType type;
     mlir::Location loc;
   };
 
 public:
-  void registerTypeInfoSymbol(Fortran::lower::AbstractConverter &converter,
-                              mlir::Location loc,
-                              Fortran::semantics::SymbolRef typeInfoSym) {
+  void registerTypeInfo(Fortran::lower::AbstractConverter &converter,
+                        mlir::Location loc,
+                        Fortran::semantics::SymbolRef typeInfoSym,
+                        const Fortran::semantics::DerivedTypeSpec &typeSpec,
+                        fir::RecordType type) {
     if (seen.contains(typeInfoSym))
       return;
     seen.insert(typeInfoSym);
     if (!skipRegistration) {
-      registeredTypeInfoSymbols.emplace_back(TypeInfoSymbol{typeInfoSym, loc});
+      registeredTypeInfo.emplace_back(
+          TypeInfo{typeInfoSym, typeSpec, type, loc});
       return;
     }
     // Once the registration is closed, symbols cannot be added to the
@@ -172,67 +179,59 @@ class RuntimeTypeInfoConverter {
     // However, after registration is closed, it is safe to directly generate
     // the globals because all FuncOps whose addresses may be required by the
     // initializers have been generated.
-    Fortran::lower::createRuntimeTypeInfoGlobal(converter, loc,
-                                                typeInfoSym.get());
+    createTypeInfoOpAndGlobal(converter,
+                              TypeInfo{typeInfoSym, typeSpec, type, loc});
   }
 
-  void createTypeInfoGlobals(Fortran::lower::AbstractConverter &converter) {
+  void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
     skipRegistration = true;
-    for (const TypeInfoSymbol &info : registeredTypeInfoSymbols)
-      Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.loc,
-                                                  info.symbol.get());
-    registeredTypeInfoSymbols.clear();
+    for (const TypeInfo &info : registeredTypeInfo)
+      createTypeInfoOpAndGlobal(converter, info);
+    registeredTypeInfo.clear();
   }
 
 private:
-  /// Store the runtime type descriptors that will be required for the
-  /// derived type that have been converted to FIR derived types.
-  llvm::SmallVector<TypeInfoSymbol> registeredTypeInfoSymbols;
-  /// Create derived type runtime info global immediately without storing the
-  /// symbol in registeredTypeInfoSymbols.
-  bool skipRegistration = false;
-  /// Track symbols symbols processed during and after the registration
-  /// to avoid infinite loops between type conversions and global variable
-  /// creation.
-  llvm::SmallSetVector<Fortran::semantics::SymbolRef, 32> seen;
-};
-
-class DispatchTableConverter {
-  struct DispatchTableInfo {
-    const Fortran::semantics::DerivedTypeSpec *typeSpec;
-    mlir::Location loc;
-  };
-
-public:
-  void registerTypeSpec(Fortran::lower::AbstractConverter &converter,
-                        mlir::Location loc,
-                        const Fortran::semantics::DerivedTypeSpec *typeSpec) {
-    assert(typeSpec && "type spec is null");
-    std::string dtName = converter.mangleName(*typeSpec);
-    if (seen.contains(dtName) || dtName.find("__fortran") != std::string::npos)
-      return;
-    seen.insert(dtName);
-    registeredDispatchTableInfo.emplace_back(DispatchTableInfo{typeSpec, loc});
-  }
-
-  void createDispatchTableOps(Fortran::lower::AbstractConverter &converter) {
-    for (const DispatchTableInfo &info : registeredDispatchTableInfo) {
-      std::string dtName = converter.mangleName(*info.typeSpec);
-      const Fortran::semantics::DerivedTypeSpec *parent =
-          Fortran::evaluate::GetParentTypeSpec(*info.typeSpec);
-      fir::FirOpBuilder &builder = converter.getFirOpBuilder();
-      fir::DispatchTableOp dt = builder.createDispatchTableOp(
-          info.loc, dtName, parent ? converter.mangleName(*parent) : "");
-      auto insertPt = builder.saveInsertionPoint();
-      const Fortran::semantics::Scope *scope = info.typeSpec->scope();
-      if (!scope)
-        scope = info.typeSpec->typeSymbol().scope();
-      Fortran::semantics::SymbolVector bindings =
-          Fortran::semantics::CollectBindings(*scope);
-
-      if (!bindings.empty())
-        builder.createBlock(&dt.getRegion());
-
+  void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
+                                 const TypeInfo &info) {
+    Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.loc,
+                                                info.symbol.get());
+    createTypeInfoOp(converter, info);
+  }
+
+  void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
+                        const TypeInfo &info) {
+    fir::RecordType parentType{};
+    if (const Fortran::semantics::DerivedTypeSpec *parent =
+            Fortran::evaluate::GetParentTypeSpec(info.typeSpec))
+      parentType = mlir::cast<fir::RecordType>(converter.genType(*parent));
+
+    fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+    mlir::ModuleOp module = builder.getModule();
+    fir::TypeInfoOp dt =
+        module.lookupSymbol<fir::TypeInfoOp>(info.type.getName());
+    if (dt)
+      return; // Already created.
+    auto insertPt = builder.saveInsertionPoint();
+    builder.setInsertionPoint(module.getBody(), module.getBody()->end());
+    dt = builder.create<fir::TypeInfoOp>(info.loc, info.type, parentType);
+
+    if (!info.typeSpec.HasDefaultInitialization(/*ignoreAllocatable=*/false,
+                                                /*ignorePointer=*/false))
+      dt->setAttr(dt.getNoInitAttrName(), builder.getUnitAttr());
+    if (!info.typeSpec.HasDestruction())
+      dt->setAttr(dt.getNoDestroyAttrName(), builder.getUnitAttr());
+    if (!Fortran::semantics::MayRequireFinalization(info.typeSpec))
+      dt->setAttr(dt.getNoFinalAttrName(), builder.getUnitAttr());
+
+    const Fortran::semantics::Scope *scope = info.typeSpec.scope();
+    if (!scope)
+      scope = info.typeSpec.typeSymbol().scope();
+    assert(scope && "failed to find type scope");
+
+    Fortran::semantics::SymbolVector bindings =
+        Fortran::semantics::CollectBindings(*scope);
+    if (!bindings.empty()) {
+      builder.createBlock(&dt.getDispatchTable());
       for (const Fortran::semantics::SymbolRef &binding : bindings) {
         const auto &details =
             binding.get().get<Fortran::semantics::ProcBindingDetails>();
@@ -244,20 +243,21 @@ class DispatchTableConverter {
             info.loc, mlir::StringAttr::get(builder.getContext(), tbpName),
             mlir::SymbolRefAttr::get(builder.getContext(), bindingName));
       }
-      if (!bindings.empty())
-        builder.create<fir::FirEndOp>(info.loc);
-      builder.restoreInsertionPoint(insertPt);
+      builder.create<fir::FirEndOp>(info.loc);
     }
-    registeredDispatchTableInfo.clear();
+    builder.restoreInsertionPoint(insertPt);
   }
 
-private:
-  /// Store the semantic DerivedTypeSpec that will be required to generate the
-  /// dispatch table.
-  llvm::SmallVector<DispatchTableInfo> registeredDispatchTableInfo;
-
-  /// Track processed type specs to avoid multiple creation.
-  llvm::StringSet<> seen;
+  /// Store the front-end data that will be required to generate the type info
+  /// for the derived types that have been converted to fir.type<>.
+  llvm::SmallVector<TypeInfo> registeredTypeInfo;
+  /// Create derived type info immediately without storing the
+  /// symbol in registeredTypeInfo.
+  bool skipRegistration = false;
+  /// Track symbols symbols processed during and after the registration
+  /// to avoid infinite loops between type conversions and global variable
+  /// creation.
+  llvm::SmallSetVector<Fortran::semantics::SymbolRef, 32> seen;
 };
 
 using IncrementLoopNestInfo = llvm::SmallVector<IncrementLoopInfo, 8>;
@@ -334,13 +334,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
 
     /// Once all the code has been translated, create runtime type info
     /// global data structure for the derived types that have been
-    /// processed.
-    createGlobalOutsideOfFunctionLowering(
-        [&]() { runtimeTypeInfoConverter.createTypeInfoGlobals(*this); });
-
-    /// Create the dispatch tables for derived types.
+    /// processed as well as the fir.type_info operations with the
+    /// dispatch tables.
     createGlobalOutsideOfFunctionLowering(
-        [&]() { dispatchTableConverter.createDispatchTableOps(*this); });
+        [&]() { typeInfoConverter.createTypeInfo(*this); });
 
     // Create the list of any environment defaults for the runtime to set. The
     // runtime default list is only created if there is a main program to ensure
@@ -875,16 +872,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     hostAssocTuple = val;
   }
 
-  void registerRuntimeTypeInfo(
-      mlir::Location loc,
-      Fortran::lower::SymbolRef typeInfoSym) override final {
-    runtimeTypeInfoConverter.registerTypeInfoSymbol(*this, loc, typeInfoSym);
-  }
-
-  void registerDispatchTableInfo(
-      mlir::Location loc,
-      const Fortran::semantics::DerivedTypeSpec *typeSpec) override final {
-    dispatchTableConverter.registerTypeSpec(*this, loc, typeSpec);
+  void registerTypeInfo(mlir::Location loc,
+                        Fortran::lower::SymbolRef typeInfoSym,
+                        const Fortran::semantics::DerivedTypeSpec &typeSpec,
+                        fir::RecordType type) override final {
+    typeInfoConverter.registerTypeInfo(*this, loc, typeInfoSym, typeSpec, type);
   }
 
   llvm::StringRef
@@ -4811,8 +4803,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   Fortran::lower::pft::Evaluation *evalPtr = nullptr;
   Fortran::lower::SymMap localSymbols;
   Fortran::parser::CharBlock currentPosition;
-  RuntimeTypeInfoConverter runtimeTypeInfoConverter;
-  DispatchTableConverter dispatchTableConverter;
+  TypeInfoConverter typeInfoConverter;
 
   // Stack to manage object deallocation and finalization at construct exits.
   llvm::SmallVector<ConstructContext> activeConstructStack;
diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp
index efaeba29b762ad4..22b83efe8678bb0 100644
--- a/flang/lib/Lower/ConvertType.cpp
+++ b/flang/lib/Lower/ConvertType.cpp
@@ -426,14 +426,12 @@ struct TypeBuilderImpl {
     }
     LLVM_DEBUG(llvm::dbgs() << "derived type: " << rec << '\n');
 
-    converter.registerDispatchTableInfo(loc, &tySpec);
-
     // Generate the type descriptor object if any
     if (const Fortran::semant...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/68309


More information about the flang-commits mailing list