[flang-commits] [flang] 853e79d - [flang] Update tco tool pipline and add translation to LLVM IR
Valentin Clement via flang-commits
flang-commits at lists.llvm.org
Mon Jan 24 05:16:36 PST 2022
Author: Valentin Clement
Date: 2022-01-24T14:16:27+01:00
New Revision: 853e79d8d8af7376a81bf692f26a0c3468811d29
URL: https://github.com/llvm/llvm-project/commit/853e79d8d8af7376a81bf692f26a0c3468811d29
DIFF: https://github.com/llvm/llvm-project/commit/853e79d8d8af7376a81bf692f26a0c3468811d29.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: kiranchandramohan, awarzynski, 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 ®istry) {
+ registry.insert<FLANG_NONCODEGEN_DIALECT_LIST>();
+}
+
/// Register all the dialects used by flang.
inline void registerDialects(mlir::DialectRegistry ®istry) {
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..49ba32f01dd91 100644
--- a/flang/tools/tco/CMakeLists.txt
+++ b/flang/tools/tco/CMakeLists.txt
@@ -1,13 +1,21 @@
-get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+)
-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
@@ -19,6 +27,3 @@ set(LIBS
MLIRSupport
MLIRVectorToLLVM
)
-
-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..db01ea545580c 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,6 +31,7 @@
#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"
@@ -42,8 +49,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 +78,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 +95,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 +132,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