[flang-commits] [flang] 68db0e2 - [flang] Update tco tool pipline and add translation to LLVM IR

Valentin Clement via flang-commits flang-commits at lists.llvm.org
Fri Jan 21 04:15:41 PST 2022


Author: Valentin Clement
Date: 2022-01-21T13:15:28+01:00
New Revision: 68db0e25df4b1edaa2c6080eb88453ab01ea01d3

URL: https://github.com/llvm/llvm-project/commit/68db0e25df4b1edaa2c6080eb88453ab01ea01d3
DIFF: https://github.com/llvm/llvm-project/commit/68db0e25df4b1edaa2c6080eb88453ab01ea01d3.diff

LOG: [flang] Update tco tool pipline and add translation to LLVM IR

tco is a tool to test the FIR to LLVM IR pipeline of the Flang compiler.

This patch update tco pipelines and adds the translation to LLVM IR.

A simple test is added to make sure the tool is working with a simple
FIR program.
More tests will be upstream in follow up patch from the fir-dev branch.

This patch is part of the upstreaming effort from fir-dev branch.

Reviewed By: schweitz, mehdi_amini

Differential Revision: https://reviews.llvm.org/D117781

Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Co-authored-by: Andrzej Warzynski <andrzej.warzynski at arm.com>

Added: 
    flang/include/flang/Tools/CLOptions.inc
    flang/lib/Optimizer/Support/InitFIR.cpp
    flang/test/Fir/basic-program.fir

Modified: 
    flang/include/flang/Optimizer/CodeGen/CodeGen.h
    flang/include/flang/Optimizer/Support/InitFIR.h
    flang/lib/Optimizer/CodeGen/CodeGen.cpp
    flang/lib/Optimizer/Support/CMakeLists.txt
    flang/tools/tco/CMakeLists.txt
    flang/tools/tco/tco.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index 1bd31b207859a..939d6aebb524d 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -12,6 +12,8 @@
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/Pass/Pass.h"
 #include "mlir/Pass/PassRegistry.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/raw_ostream.h"
 #include <memory>
 
 namespace fir {
@@ -36,9 +38,13 @@ std::unique_ptr<mlir::OperationPass<mlir::ModuleOp>> createFirTargetRewritePass(
 /// Convert FIR to the LLVM IR dialect
 std::unique_ptr<mlir::Pass> createFIRToLLVMPass();
 
+using LLVMIRLoweringPrinter =
+    std::function<void(llvm::Module &, llvm::raw_ostream &)>;
 /// Convert the LLVM IR dialect to LLVM-IR proper
-std::unique_ptr<mlir::Pass>
-createLLVMDialectToLLVMPass(llvm::raw_ostream &output);
+std::unique_ptr<mlir::Pass> createLLVMDialectToLLVMPass(
+    llvm::raw_ostream &output,
+    LLVMIRLoweringPrinter printer =
+        [](llvm::Module &m, llvm::raw_ostream &out) { m.print(out, nullptr); });
 
 // declarative passes
 #define GEN_PASS_REGISTRATION

diff  --git a/flang/include/flang/Optimizer/Support/InitFIR.h b/flang/include/flang/Optimizer/Support/InitFIR.h
index e78967de2a383..2e8c1685a06f7 100644
--- a/flang/include/flang/Optimizer/Support/InitFIR.h
+++ b/flang/include/flang/Optimizer/Support/InitFIR.h
@@ -13,7 +13,6 @@
 #ifndef FORTRAN_OPTIMIZER_SUPPORT_INITFIR_H
 #define FORTRAN_OPTIMIZER_SUPPORT_INITFIR_H
 
-#include "flang/Optimizer/CodeGen/CodeGen.h"
 #include "flang/Optimizer/Dialect/FIRDialect.h"
 #include "mlir/Conversion/Passes.h"
 #include "mlir/Dialect/Affine/Passes.h"
@@ -35,11 +34,19 @@ namespace fir::support {
 #define FLANG_DIALECT_LIST                                                     \
   FLANG_NONCODEGEN_DIALECT_LIST, FIRCodeGenDialect, mlir::LLVM::LLVMDialect
 
+inline void registerNonCodegenDialects(mlir::DialectRegistry &registry) {
+  registry.insert<FLANG_NONCODEGEN_DIALECT_LIST>();
+}
+
 /// Register all the dialects used by flang.
 inline void registerDialects(mlir::DialectRegistry &registry) {
   registry.insert<FLANG_DIALECT_LIST>();
 }
 
+inline void loadNonCodegenDialects(mlir::MLIRContext &context) {
+  context.loadDialect<FLANG_NONCODEGEN_DIALECT_LIST>();
+}
+
 /// Forced load of all the dialects used by flang.  Lowering is not an MLIR
 /// pass, but a producer of FIR and MLIR. It is therefore a requirement that the
 /// dialects be preloaded to be able to build the IR.
@@ -75,6 +82,9 @@ inline void registerMLIRPassesForFortranTools() {
   mlir::registerConvertAffineToStandardPass();
 }
 
+/// Register the interfaces needed to lower to LLVM IR.
+void registerLLVMTranslation(mlir::MLIRContext &context);
+
 } // namespace fir::support
 
 #endif // FORTRAN_OPTIMIZER_SUPPORT_INITFIR_H

diff  --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc
new file mode 100644
index 0000000000000..1c85075d5cc17
--- /dev/null
+++ b/flang/include/flang/Tools/CLOptions.inc
@@ -0,0 +1,160 @@
+//===-- CLOptions.inc -- command line options -------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+/// This file defines some shared command-line options that can be used when
+/// debugging the test tools. This file must be included into the tool.
+
+#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
+#include "mlir/Pass/PassManager.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "mlir/Transforms/Passes.h"
+#include "flang/Optimizer/CodeGen/CodeGen.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "llvm/Support/CommandLine.h"
+
+#define DisableOption(DOName, DOOption, DODescription) \
+  static llvm::cl::opt<bool> disable##DOName("disable-" DOOption, \
+      llvm::cl::desc("disable " DODescription " pass"), llvm::cl::init(false), \
+      llvm::cl::Hidden)
+
+/// Shared option in tools to control whether dynamically sized array
+/// allocations should always be on the heap.
+static llvm::cl::opt<bool> dynamicArrayStackToHeapAllocation(
+    "fdynamic-heap-array",
+    llvm::cl::desc("place all array allocations of dynamic size on the heap"),
+    llvm::cl::init(false), llvm::cl::Hidden);
+
+/// Shared option in tools to set a maximum value for the number of elements in
+/// a compile-time sized array that can be allocated on the stack.
+static llvm::cl::opt<std::size_t> arrayStackAllocationThreshold(
+    "fstack-array-size",
+    llvm::cl::desc(
+        "place all array allocations more than <size> elements on the heap"),
+    llvm::cl::init(~static_cast<std::size_t>(0)), llvm::cl::Hidden);
+
+namespace {
+/// Optimizer Passes
+DisableOption(CfgConversion, "cfg-conversion", "disable FIR to CFG pass");
+DisableOption(FirAvc, "avc", "array value copy analysis and transformation");
+DisableOption(
+    FirMao, "memory-allocation-opt", "memory allocation optimization");
+
+/// CodeGen Passes
+#if !defined(FLANG_EXCLUDE_CODEGEN)
+DisableOption(CodeGenRewrite, "codegen-rewrite", "rewrite FIR for codegen");
+DisableOption(TargetRewrite, "target-rewrite", "rewrite FIR for target");
+DisableOption(FirToLlvmIr, "fir-to-llvmir", "FIR to LLVM-IR dialect");
+DisableOption(LlvmIrToLlvm, "llvm", "conversion to LLVM");
+#endif
+
+/// Generic for adding a pass to the pass manager if it is not disabled.
+template <typename F>
+void addPassConditionally(
+    mlir::PassManager &pm, llvm::cl::opt<bool> &disabled, F ctor) {
+  if (!disabled)
+    pm.addPass(ctor());
+}
+
+template <typename OP, typename F>
+void addNestedPassConditionally(
+    mlir::PassManager &pm, llvm::cl::opt<bool> &disabled, F ctor) {
+  if (!disabled)
+    pm.addNestedPass<OP>(ctor());
+}
+
+} // namespace
+
+namespace fir {
+
+static void defaultFlangInlinerOptPipeline(mlir::OpPassManager &pm) {
+  mlir::GreedyRewriteConfig config;
+  config.enableRegionSimplification = false;
+  pm.addPass(mlir::createCanonicalizerPass(config));
+}
+
+inline void addCfgConversionPass(mlir::PassManager &pm) {
+  addNestedPassConditionally<mlir::FuncOp>(
+      pm, disableCfgConversion, fir::createFirToCfgPass);
+}
+
+inline void addAVC(mlir::PassManager &pm) {
+  addNestedPassConditionally<mlir::FuncOp>(
+      pm, disableFirAvc, fir::createArrayValueCopyPass);
+}
+
+#if !defined(FLANG_EXCLUDE_CODEGEN)
+inline void addCodeGenRewritePass(mlir::PassManager &pm) {
+  addPassConditionally(
+      pm, disableCodeGenRewrite, fir::createFirCodeGenRewritePass);
+}
+
+inline void addTargetRewritePass(mlir::PassManager &pm) {
+  addPassConditionally(pm, disableTargetRewrite, []() {
+    return fir::createFirTargetRewritePass(fir::TargetRewriteOptions{});
+  });
+}
+
+inline void addFIRToLLVMPass(mlir::PassManager &pm) {
+  addPassConditionally(pm, disableFirToLlvmIr, fir::createFIRToLLVMPass);
+}
+
+inline void addLLVMDialectToLLVMPass(
+    mlir::PassManager &pm, llvm::raw_ostream &output) {
+  addPassConditionally(pm, disableLlvmIrToLlvm,
+      [&]() { return fir::createLLVMDialectToLLVMPass(output); });
+}
+#endif
+
+/// Create a pass pipeline for running default optimization passes for
+/// incremental conversion of FIR.
+///
+/// \param pm - MLIR pass manager that will hold the pipeline definition
+inline void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm) {
+  // simplify the IR
+  mlir::GreedyRewriteConfig config;
+  config.enableRegionSimplification = false;
+  fir::addAVC(pm);
+  pm.addNestedPass<mlir::FuncOp>(fir::createCharacterConversionPass());
+  pm.addPass(mlir::createCanonicalizerPass(config));
+
+  // The default inliner pass adds the canonicalizer pass with the default
+  // configuration. Create the inliner pass with tco config.
+  llvm::StringMap<mlir::OpPassManager> pipelines;
+  pm.addPass(
+      mlir::createInlinerPass(pipelines, defaultFlangInlinerOptPipeline));
+  pm.addPass(mlir::createCSEPass());
+
+  // convert control flow to CFG form
+  fir::addCfgConversionPass(pm);
+  pm.addPass(mlir::createLowerToCFGPass());
+
+  pm.addPass(mlir::createCanonicalizerPass(config));
+}
+
+#if !defined(FLANG_EXCLUDE_CODEGEN)
+inline void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm) {
+  pm.addNestedPass<mlir::FuncOp>(fir::createAbstractResultOptPass());
+  fir::addCodeGenRewritePass(pm);
+  fir::addTargetRewritePass(pm);
+  fir::addFIRToLLVMPass(pm);
+}
+
+/// Create a pass pipeline for lowering from MLIR to LLVM IR
+///
+/// \param pm - MLIR pass manager that will hold the pipeline definition
+inline void createMLIRToLLVMPassPipeline(mlir::PassManager &pm) {
+  // Add default optimizer pass pipeline.
+  fir::createDefaultFIROptimizerPassPipeline(pm);
+
+  // Add codegen pass pipeline.
+  fir::createDefaultFIRCodeGenPassPipeline(pm);
+}
+#undef FLANG_EXCLUDE_CODEGEN
+#endif
+
+} // namespace fir

diff  --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index be2e7cde916df..40d6d2017b2fa 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -23,6 +23,7 @@
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Matchers.h"
 #include "mlir/Pass/Pass.h"
+#include "mlir/Target/LLVMIR/ModuleTranslation.h"
 #include "llvm/ADT/ArrayRef.h"
 
 #define DEBUG_TYPE "flang-codegen"
@@ -3305,8 +3306,44 @@ class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
     }
   }
 };
+
+/// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
+struct LLVMIRLoweringPass
+    : public mlir::PassWrapper<LLVMIRLoweringPass,
+                               mlir::OperationPass<mlir::ModuleOp>> {
+  using Printer = fir::LLVMIRLoweringPrinter;
+  LLVMIRLoweringPass(raw_ostream &output, Printer p)
+      : output{output}, printer{p} {}
+
+  mlir::ModuleOp getModule() { return getOperation(); }
+
+  void runOnOperation() override final {
+    auto *ctx = getModule().getContext();
+    auto optName = getModule().getName();
+    llvm::LLVMContext llvmCtx;
+    if (auto llvmModule = mlir::translateModuleToLLVMIR(
+            getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
+      printer(*llvmModule, output);
+      return;
+    }
+
+    mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
+    signalPassFailure();
+  }
+
+private:
+  raw_ostream &output;
+  Printer printer;
+};
+
 } // namespace
 
 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
   return std::make_unique<FIRToLLVMLowering>();
 }
+
+std::unique_ptr<mlir::Pass>
+fir::createLLVMDialectToLLVMPass(raw_ostream &output,
+                                 fir::LLVMIRLoweringPrinter printer) {
+  return std::make_unique<LLVMIRLoweringPass>(output, printer);
+}

diff  --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt
index 30a163de9ccaf..779e20711513e 100644
--- a/flang/lib/Optimizer/Support/CMakeLists.txt
+++ b/flang/lib/Optimizer/Support/CMakeLists.txt
@@ -2,6 +2,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
 
 add_flang_library(FIRSupport
   FIRContext.cpp
+  InitFIR.cpp
   InternalNames.cpp
   KindMapping.cpp
 

diff  --git a/flang/lib/Optimizer/Support/InitFIR.cpp b/flang/lib/Optimizer/Support/InitFIR.cpp
new file mode 100644
index 0000000000000..baa1336d9ca02
--- /dev/null
+++ b/flang/lib/Optimizer/Support/InitFIR.cpp
@@ -0,0 +1,20 @@
+//===-- Optimizer/Support/InitFIR.cpp -------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Support/InitFIR.h"
+#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
+#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
+
+void fir::support::registerLLVMTranslation(mlir::MLIRContext &context) {
+  mlir::DialectRegistry registry;
+  // Register OpenMP dialect interface here as well.
+  mlir::registerOpenMPDialectTranslation(registry);
+  // Register LLVM-IR dialect interface.
+  registerLLVMDialectTranslation(registry);
+  context.appendDialectRegistry(registry);
+}

diff  --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
new file mode 100644
index 0000000000000..02463bef99496
--- /dev/null
+++ b/flang/test/Fir/basic-program.fir
@@ -0,0 +1,11 @@
+// RUN: tco --target=x86_64-unknown-linux-gnu %s | FileCheck %s
+
+// Check that tco is working with a basic test.
+
+func @_QQmain() {
+  return
+}
+
+// CHECK: ; ModuleID = 'FIRModule'
+// CHECK-LABEL: define void @_QQmain()
+// CHECK:       ret void

diff  --git a/flang/tools/tco/CMakeLists.txt b/flang/tools/tco/CMakeLists.txt
index 1a9c5ac72f153..a64b9c59bd02a 100644
--- a/flang/tools/tco/CMakeLists.txt
+++ b/flang/tools/tco/CMakeLists.txt
@@ -1,13 +1,25 @@
-get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+set(LLVM_LINK_COMPONENTS
+  AllTargetsAsmParsers
+  AllTargetsCodeGens
+  AllTargetsDescs
+  AllTargetsInfos
+)
+llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
 
-set(LIBS
+add_flang_tool(tco tco.cpp)
+llvm_update_compile_flags(tco)
+get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+target_link_libraries(tco PRIVATE
   FIRCodeGen
   FIRDialect
   FIRSupport
   FIRTransforms
+  FIRBuilder
   ${dialect_libs}
   MLIRIR
   MLIRLLVMIR
+  MLIRLLVMToLLVMIRTranslation
+  MLIRTargetLLVMIRExport
   MLIRPass
   MLIRStandardToLLVM
   MLIRTransforms
@@ -18,7 +30,5 @@ set(LIBS
   MLIRStandardToLLVM
   MLIRSupport
   MLIRVectorToLLVM
+  ${llvm_libs}
 )
-
-add_flang_tool(tco tco.cpp)
-target_link_libraries(tco PRIVATE ${LIBS})

diff  --git a/flang/tools/tco/tco.cpp b/flang/tools/tco/tco.cpp
index 8f2c283bc82f9..2bb3b27e7eb63 100644
--- a/flang/tools/tco/tco.cpp
+++ b/flang/tools/tco/tco.cpp
@@ -11,8 +11,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "flang/Optimizer/CodeGen/CodeGen.h"
+#include "flang/Optimizer/Support/FIRContext.h"
 #include "flang/Optimizer/Support/InitFIR.h"
+#include "flang/Optimizer/Support/InternalNames.h"
 #include "flang/Optimizer/Support/KindMapping.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
+#include "mlir/IR/AsmState.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/MLIRContext.h"
 #include "mlir/Parser.h"
@@ -25,11 +31,13 @@
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace llvm;
 
+/// list of program return codes
 static cl::opt<std::string>
     inputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
 
@@ -42,8 +50,14 @@ static cl::opt<bool> emitFir("emit-fir",
                              cl::desc("Parse and pretty-print the input"),
                              cl::init(false));
 
+static cl::opt<std::string> targetTriple("target",
+                                         cl::desc("specify a target triple"),
+                                         cl::init("native"));
+
+#include "flang/Tools/CLOptions.inc"
+
 static void printModuleBody(mlir::ModuleOp mod, raw_ostream &output) {
-  for (auto &op : mod.getBody()->without_terminator())
+  for (auto &op : *mod.getBody())
     output << op << '\n';
 }
 
@@ -65,6 +79,8 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
   mlir::DialectRegistry registry;
   fir::support::registerDialects(registry);
   mlir::MLIRContext context(registry);
+  fir::support::loadDialects(context);
+  fir::support::registerLLVMTranslation(context);
   auto owningRef = mlir::parseSourceFile(sourceMgr, &context);
 
   if (!owningRef) {
@@ -80,21 +96,31 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
   ToolOutputFile out(outputFilename, ec, sys::fs::OF_None);
 
   // run passes
-  mlir::PassManager pm{&context};
+  fir::KindMapping kindMap{&context};
+  fir::setTargetTriple(*owningRef, targetTriple);
+  fir::setKindMapping(*owningRef, kindMap);
+  mlir::PassManager pm(&context, mlir::OpPassManager::Nesting::Implicit);
+  pm.enableVerifier(/*verifyPasses=*/true);
   mlir::applyPassManagerCLOptions(pm);
   if (emitFir) {
     // parse the input and pretty-print it back out
     // -emit-fir intentionally disables all the passes
+  } else if (passPipeline.hasAnyOccurrences()) {
+    auto errorHandler = [&](const Twine &msg) {
+      mlir::emitError(mlir::UnknownLoc::get(pm.getContext())) << msg;
+      return mlir::failure();
+    };
+    if (mlir::failed(passPipeline.addToPipeline(pm, errorHandler)))
+      return mlir::failure();
   } else {
-    // TODO: Actually add passes when added to FIR code base
-    // add all the passes
-    // the user can disable them individually
+    fir::createMLIRToLLVMPassPipeline(pm);
+    fir::addLLVMDialectToLLVMPass(pm, out.os());
   }
 
   // run the pass manager
   if (mlir::succeeded(pm.run(*owningRef))) {
     // passes ran successfully, so keep the output
-    if (emitFir)
+    if (emitFir || passPipeline.hasAnyOccurrences())
       printModuleBody(*owningRef, out.os());
     out.keep();
     return mlir::success();
@@ -107,8 +133,13 @@ compileFIR(const mlir::PassPipelineCLParser &passPipeline) {
 }
 
 int main(int argc, char **argv) {
-  fir::support::registerMLIRPassesForFortranTools();
   [[maybe_unused]] InitLLVM y(argc, argv);
+  fir::support::registerMLIRPassesForFortranTools();
+  fir::registerOptCodeGenPasses();
+  fir::registerOptTransformPasses();
+  InitializeAllTargets();
+  mlir::registerAsmPrinterCLOptions();
+  mlir::registerMLIRContextCLOptions();
   mlir::registerPassManagerCLOptions();
   mlir::PassPipelineCLParser passPipe("", "Compiler passes to run");
   cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n");


        


More information about the flang-commits mailing list