[flang-commits] [flang] [flang] Separate definition of class `FirConverter` into header file (PR #74864)

via flang-commits flang-commits at lists.llvm.org
Fri Dec 8 08:55:13 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Krzysztof Parzyszek (kparzysz)

<details>
<summary>Changes</summary>

The order of functions has been preserved, the order in the cpp file matches the order in the header. This was meant to limit the visual differences between the old and the new files.

Small functions and templates have their definitions in the header (except `genFIR`).

All `genFIR` functions (except those explicitly listed as no-ops) are defined in the cpp file.

The semantic changes are
- `FirCoverter` is defined in namespace `Fortran::lower` (was in anonymous namespace before).
- Helper classes `IncrementLoopInfo`, `ConstructContext`, and `TypeInfoConverter` are now nested inside of `FirConverter` (were
in anonymous namespace before).

---

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


2 Files Affected:

- (modified) flang/lib/Lower/Bridge.cpp (+4077-4678) 
- (added) flang/lib/Lower/FirConverter.h (+1270) 


``````````diff
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 7e64adc3c144c9..885c9307b8caf2 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -10,8 +10,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "flang/Lower/Bridge.h"
+#include "FirConverter.h"
 #include "flang/Lower/Allocatable.h"
+#include "flang/Lower/Bridge.h"
 #include "flang/Lower/CallInterface.h"
 #include "flang/Lower/Coarray.h"
 #include "flang/Lower/ConvertCall.h"
@@ -76,4985 +77,4383 @@ static llvm::cl::opt<bool> forceLoopToExecuteOnce(
     "always-execute-loop-body", llvm::cl::init(false),
     llvm::cl::desc("force the body of a loop to execute at least once"));
 
-namespace {
-/// Information for generating a structured or unstructured increment loop.
-struct IncrementLoopInfo {
-  template <typename T>
-  explicit IncrementLoopInfo(Fortran::semantics::Symbol &sym, const T &lower,
-                             const T &upper, const std::optional<T> &step,
-                             bool isUnordered = false)
-      : loopVariableSym{&sym}, lowerExpr{Fortran::semantics::GetExpr(lower)},
-        upperExpr{Fortran::semantics::GetExpr(upper)},
-        stepExpr{Fortran::semantics::GetExpr(step)}, isUnordered{isUnordered} {}
-
-  IncrementLoopInfo(IncrementLoopInfo &&) = default;
-  IncrementLoopInfo &operator=(IncrementLoopInfo &&x) = default;
-
-  bool isStructured() const { return !headerBlock; }
-
-  mlir::Type getLoopVariableType() const {
-    assert(loopVariable && "must be set");
-    return fir::unwrapRefType(loopVariable.getType());
-  }
+namespace Fortran::lower {
+
+//===----------------------------------------------------------------------===//
+// FirConverter
+//===----------------------------------------------------------------------===//
 
-  bool hasLocalitySpecs() const {
-    return !localSymList.empty() || !localInitSymList.empty() ||
-           !sharedSymList.empty();
+/// Convert the PFT to FIR.
+void FirConverter::run(Fortran::lower::pft::Program &pft) {
+  // Preliminary translation pass.
+
+  // Lower common blocks, taking into account initialization and the largest
+  // size of all instances of each common block. This is done before lowering
+  // since the global definition may differ from any one local definition.
+  lowerCommonBlocks(pft.getCommonBlocks());
+
+  // - Declare all functions that have definitions so that definition
+  //   signatures prevail over call site signatures.
+  // - Define module variables and OpenMP/OpenACC declarative constructs so
+  //   they are available before lowering any function that may use them.
+  bool hasMainProgram = false;
+  const Fortran::semantics::Symbol *globalOmpRequiresSymbol = nullptr;
+  for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
+    std::visit(Fortran::common::visitors{
+                   [&](Fortran::lower::pft::FunctionLikeUnit &f) {
+                     if (f.isMainProgram())
+                       hasMainProgram = true;
+                     declareFunction(f);
+                     if (!globalOmpRequiresSymbol)
+                       globalOmpRequiresSymbol = f.getScope().symbol();
+                   },
+                   [&](Fortran::lower::pft::ModuleLikeUnit &m) {
+                     lowerModuleDeclScope(m);
+                     for (Fortran::lower::pft::FunctionLikeUnit &f :
+                          m.nestedFunctions)
+                       declareFunction(f);
+                   },
+                   [&](Fortran::lower::pft::BlockDataUnit &b) {
+                     if (!globalOmpRequiresSymbol)
+                       globalOmpRequiresSymbol = b.symTab.symbol();
+                   },
+                   [&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
+                   [&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {},
+               },
+               u);
   }
 
-  // Data members common to both structured and unstructured loops.
-  const Fortran::semantics::Symbol *loopVariableSym;
-  const Fortran::lower::SomeExpr *lowerExpr;
-  const Fortran::lower::SomeExpr *upperExpr;
-  const Fortran::lower::SomeExpr *stepExpr;
-  const Fortran::lower::SomeExpr *maskExpr = nullptr;
-  bool isUnordered; // do concurrent, forall
-  llvm::SmallVector<const Fortran::semantics::Symbol *> localSymList;
-  llvm::SmallVector<const Fortran::semantics::Symbol *> localInitSymList;
-  llvm::SmallVector<const Fortran::semantics::Symbol *> sharedSymList;
-  mlir::Value loopVariable = nullptr;
-
-  // Data members for structured loops.
-  fir::DoLoopOp doLoop = nullptr;
-
-  // Data members for unstructured loops.
-  bool hasRealControl = false;
-  mlir::Value tripVariable = nullptr;
-  mlir::Value stepVariable = nullptr;
-  mlir::Block *headerBlock = nullptr; // loop entry and test block
-  mlir::Block *maskBlock = nullptr;   // concurrent loop mask block
-  mlir::Block *bodyBlock = nullptr;   // first loop body block
-  mlir::Block *exitBlock = nullptr;   // loop exit target block
-};
-
-/// Information to support stack management, object deallocation, and
-/// object finalization at early and normal construct exits.
-struct ConstructContext {
-  explicit ConstructContext(Fortran::lower::pft::Evaluation &eval,
-                            Fortran::lower::StatementContext &stmtCtx)
-      : eval{eval}, stmtCtx{stmtCtx} {}
-
-  Fortran::lower::pft::Evaluation &eval;     // construct eval
-  Fortran::lower::StatementContext &stmtCtx; // construct exit code
-};
-
-/// 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 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 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;
-  };
+  // Create definitions of intrinsic module constants.
+  createGlobalOutsideOfFunctionLowering(
+      [&]() { createIntrinsicModuleDefinitions(pft); });
 
-public:
-  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) {
-      registeredTypeInfo.emplace_back(
-          TypeInfo{typeInfoSym, typeSpec, type, loc});
-      return;
-    }
-    // Once the registration is closed, symbols cannot be added to the
-    // registeredTypeInfoSymbols list because it may be iterated over.
-    // 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.
-    createTypeInfoOpAndGlobal(converter,
-                              TypeInfo{typeInfoSym, typeSpec, type, loc});
+  // Primary translation pass.
+  for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) {
+    std::visit(
+        Fortran::common::visitors{
+            [&](Fortran::lower::pft::FunctionLikeUnit &f) { lowerFunc(f); },
+            [&](Fortran::lower::pft::ModuleLikeUnit &m) { lowerMod(m); },
+            [&](Fortran::lower::pft::BlockDataUnit &b) {},
+            [&](Fortran::lower::pft::CompilerDirectiveUnit &d) {},
+            [&](Fortran::lower::pft::OpenACCDirectiveUnit &d) {
+              builder = new fir::FirOpBuilder(bridge.getModule(),
+                                              bridge.getKindMap());
+              Fortran::lower::genOpenACCRoutineConstruct(
+                  *this, bridge.getSemanticsContext(), bridge.getModule(),
+                  d.routine, accRoutineInfos);
+              builder = nullptr;
+            },
+        },
+        u);
   }
 
-  void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
-    skipRegistration = true;
-    for (const TypeInfo &info : registeredTypeInfo)
-      createTypeInfoOpAndGlobal(converter, info);
-    registeredTypeInfo.clear();
-  }
+  // Once all the code has been translated, create global runtime type info
+  // data structures for the derived types that have been processed, as well
+  // as fir.type_info operations for the dispatch tables.
+  createGlobalOutsideOfFunctionLowering(
+      [&]() { 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
+  // it only happens once and to provide consistent results if multiple files
+  // are compiled separately.
+  if (hasMainProgram)
+    createGlobalOutsideOfFunctionLowering([&]() {
+      // FIXME: Ideally, this would create a call to a runtime function
+      // accepting the list of environment defaults. That way, we would not
+      // need to add an extern pointer to the runtime and said pointer would
+      // not need to be generated even if no defaults are specified.
+      // However, generating main or changing when the runtime reads
+      // environment variables is required to do so.
+      fir::runtime::genEnvironmentDefaults(*builder, toLocation(),
+                                           bridge.getEnvironmentDefaults());
+    });
 
-private:
-  void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
-                                 const TypeInfo &info) {
-    Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
-    createTypeInfoOp(converter, info);
+  finalizeOpenACCLowering();
+  finalizeOpenMPLowering(globalOmpRequiresSymbol);
+}
+
+/// Generate FIR for Evaluation \p eval.
+void FirConverter::genFIR(Fortran::lower::pft::Evaluation &eval,
+                          bool unstructuredContext) {
+  // Start a new unstructured block when applicable. When transitioning
+  // from unstructured to structured code, unstructuredContext is true,
+  // which accounts for the possibility that the structured code could be
+  // a target that starts a new block.
+  if (unstructuredContext)
+    maybeStartBlock(eval.isConstruct() && eval.lowerAsStructured()
+                        ? eval.getFirstNestedEvaluation().block
+                        : eval.block);
+
+  // Generate evaluation specific code. Even nop calls should usually reach
+  // here in case they start a new block or require generation of a generic
+  // end-of-block branch. An alternative is to add special case code
+  // elsewhere, such as in the genFIR code for a parent construct.
+  setCurrentEval(eval);
+  setCurrentPosition(eval.position);
+  eval.visit([&](const auto &stmt) { genFIR(stmt); });
+
+  // Generate an end-of-block branch for several special cases. For
+  // constructs, this can be done for either the end construct statement,
+  // or for the construct itself, which will skip this code if the
+  // end statement was visited first and generated a branch.
+  Fortran::lower::pft::Evaluation *successor =
+      eval.isConstruct() ? eval.getLastNestedEvaluation().lexicalSuccessor
+                         : eval.lexicalSuccessor;
+  if (successor && blockIsUnterminated()) {
+    if (successor->isIntermediateConstructStmt() &&
+        successor->parentConstruct->lowerAsUnstructured())
+      // Exit from an intermediate unstructured IF or SELECT construct block.
+      genBranch(successor->parentConstruct->constructExit->block);
+    else if (unstructuredContext && eval.isConstructStmt() &&
+             successor == eval.controlSuccessor)
+      // Exit from a degenerate, empty construct block.
+      genBranch(eval.parentConstruct->constructExit->block);
   }
+}
+
+void FirConverter::genFIR(const Fortran::parser::AllocateStmt &stmt) {
+  Fortran::lower::genAllocateStmt(*this, stmt, toLocation());
+}
+
+void FirConverter::genFIR(const Fortran::parser::ArithmeticIfStmt &stmt) {
+  Fortran::lower::StatementContext stmtCtx;
+  mlir::Value expr = createFIRExpr(
+      toLocation(),
+      Fortran::semantics::GetExpr(std::get<Fortran::parser::Expr>(stmt.t)),
+      stmtCtx);
+  stmtCtx.finalizeAndReset();
+  // Raise an exception if REAL expr is a NaN.
+  if (expr.getType().isa<mlir::FloatType>())
+    expr = builder->create<mlir::arith::AddFOp>(toLocation(), expr, expr);
+  // An empty valueList indicates to genMultiwayBranch that the branch is
+  // an ArithmeticIfStmt that has two branches on value 0 or 0.0.
+  llvm::SmallVector<int64_t> valueList;
+  llvm::SmallVector<Fortran::parser::Label> labelList;
+  labelList.push_back(std::get<1>(stmt.t));
+  labelList.push_back(std::get<3>(stmt.t));
+  const Fortran::lower::pft::LabelEvalMap &labelEvaluationMap =
+      getEval().getOwningProcedure()->labelEvaluationMap;
+  const auto iter = labelEvaluationMap.find(std::get<2>(stmt.t));
+  assert(iter != labelEvaluationMap.end() && "label missing from map");
+  genMultiwayBranch(expr, valueList, labelList, *iter->second);
+}
 
-  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>();
-        std::string tbpName = binding.get().name().ToString();
-        if (details.numPrivatesNotOverridden() > 0)
-          tbpName += "."s + std::to_string(details.numPrivatesNotOverridden());
-        std::string bindingName = converter.mangleName(details.symbol());
-        builder.create<fir::DTEntryOp>(
-            info.loc, mlir::StringAttr::get(builder.getContext(), tbpName),
-            mlir::SymbolRefAttr::get(builder.getContext(), bindingName));
+void FirConverter::genFIR(const Fortran::parser::AssignedGotoStmt &stmt) {
+  // See Fortran 90 Clause 8.2.4.
+  // Relax the requirement that the GOTO variable must have a value in the
+  // label list when a list is present, and allow a branch to any non-format
+  // target that has an ASSIGN statement for the variable.
+  mlir::Location loc = toLocation();
+  Fortran::lower::pft::Evaluation &eval = getEval();
+  Fortran::lower::pft::FunctionLikeUnit &owningProc =
+      *eval.getOwningProcedure();
+  const Fortran::lower::pft::SymbolLabelMap &symbolLabelMap =
+      owningProc.assignSymbolLabelMap;
+  const Fortran::lower::pft::LabelEvalMap &labelEvalMap =
+      owningProc.labelEvaluationMap;
+  const Fortran::semantics::Symbol &symbol =
+      *std::get<Fortran::parser::Name>(stmt.t).symbol;
+  auto labelSetIter = symbolLabelMap.find(symbol);
+  llvm::SmallVector<int64_t> valueList;
+  llvm::SmallVector<Fortran::parser::Label> labelList;
+  if (labelSetIter != symbolLabelMap.end()) {
+    for (auto &label : labelSetIter->second) {
+      const auto evalIter = labelEvalMap.find(label);
+      assert(evalIter != labelEvalMap.end() && "assigned goto label missing");
+      if (evalIter->second->block) { // non-format statement
+        valueList.push_back(label);  // label as an integer
+        labelList.push_back(label);
       }
-      builder.create<fir::FirEndOp>(info.loc);
     }
-    builder.restoreInsertionPoint(insertPt);
   }
+  if (!labelList.empty()) {
+    auto selectExpr =
+        builder->create<fir::LoadOp>(loc, getSymbolAddress(symbol));
+    // Add a default error target in case the goto is nonconforming.
+    mlir::Block *errorBlock =
+        builder->getBlock()->splitBlock(builder->getInsertionPoint());
+    genMultiwayBranch(selectExpr, valueList, labelList, eval.nonNopSuccessor(),
+                      errorBlock);
+    startBlock(errorBlock);
+  }
+  fir::runtime::genReportFatalUserError(
+      *builder, loc,
+      "Assigned GOTO variable '" + symbol.name().ToString() +
+          "' does not have a valid target label value");
+  builder->create<fir::UnreachableOp>(loc);
+}
 
-  /// 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>;
-} // namespace
+void FirConverter::genFIR(const Fortran::parser::AssignmentStmt &stmt) {
+  genAssignment(*stmt.typedAssignment->v);
+}
 
-//===----------------------------------------------------------------------===//
-// FirConverter
-//===----------------------------------------------------------------------===//
+void FirConverter::genFIR(const Fortran::parser::AssignStmt &stmt) {
+  const Fortran::semantics::Symbol &symbol =
+      *std::get<Fortran::parser::Name>(stmt.t).symbol;
+  mlir::Location loc = toLocation();
+  mlir::Value labelValue = builder->createIntegerConstant(
+      loc, genType(symbol), std::get<Fortran::parser::Label>(stmt.t));
+  builder->create<fir::StoreOp>(loc, labelValue, getSymbolAddress(symbol));
+}
 
-namespace {
-
-/// Traverse the pre-FIR tree (PFT) to generate the FIR dialect of MLIR.
-class FirConverter : public Fortran::lower::AbstractConverter {
-public:
-  explicit FirConverter(Fortran::lower::LoweringBridge &bridge)
-      : Fortran::lower::AbstractConverter(bridge.getLoweringOptions()),
-        bridge{bridge}, foldingContext{bridge.createFoldingContext()} {}
-  virtual ~FirConverter() = default;
-
-  /// Convert the PFT to FIR.
-  void run(Fortran::lower::pft::Program &pft) {
-    // Preliminary translation pass.
-
-    // Lower common blocks, taking into account initialization and the largest
-    // size of all instances of e...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list