diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h --- 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 namespace fir { @@ -36,9 +38,13 @@ /// Convert FIR to the LLVM IR dialect std::unique_ptr createFIRToLLVMPass(); +using LLVMIRLoweringPrinter = + std::function; /// Convert the LLVM IR dialect to LLVM-IR proper -std::unique_ptr -createLLVMDialectToLLVMPass(llvm::raw_ostream &output); +std::unique_ptr 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 --- 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 @@ #define FLANG_DIALECT_LIST \ FLANG_NONCODEGEN_DIALECT_LIST, FIRCodeGenDialect, mlir::LLVM::LLVMDialect +inline void registerNonCodegenDialects(mlir::DialectRegistry ®istry) { + registry.insert(); +} + /// Register all the dialects used by flang. inline void registerDialects(mlir::DialectRegistry ®istry) { registry.insert(); } +inline void loadNonCodegenDialects(mlir::MLIRContext &context) { + context.loadDialect(); +} + /// 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 @@ 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 --- /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 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 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 arrayStackAllocationThreshold( + "fstack-array-size", + llvm::cl::desc( + "place all array allocations more than elements on the heap"), + llvm::cl::init(~static_cast(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 +void addPassConditionally( + mlir::PassManager &pm, llvm::cl::opt &disabled, F ctor) { + if (!disabled) + pm.addPass(ctor()); +} + +template +void addNestedPassConditionally( + mlir::PassManager &pm, llvm::cl::opt &disabled, F ctor) { + if (!disabled) + pm.addNestedPass(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( + pm, disableCfgConversion, fir::createFirToCfgPass); +} + +inline void addAVC(mlir::PassManager &pm) { + addNestedPassConditionally( + 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(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 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(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 --- 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 @@ } } }; + +/// Lower from LLVM IR dialect to proper LLVM-IR and dump the module +struct LLVMIRLoweringPass + : public mlir::PassWrapper> { + 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 fir::createFIRToLLVMPass() { return std::make_unique(); } + +std::unique_ptr +fir::createLLVMDialectToLLVMPass(raw_ostream &output, + fir::LLVMIRLoweringPrinter printer) { + return std::make_unique(output, printer); +} diff --git a/flang/lib/Optimizer/Support/CMakeLists.txt b/flang/lib/Optimizer/Support/CMakeLists.txt --- a/flang/lib/Optimizer/Support/CMakeLists.txt +++ b/flang/lib/Optimizer/Support/CMakeLists.txt @@ -2,6 +2,7 @@ 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 --- /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 --- /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 --- 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 @@ 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 --- 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 @@ cl::desc("Parse and pretty-print the input"), cl::init(false)); +static cl::opt 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 @@ 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 @@ 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 @@ } 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");