//===- tco.cpp - Tilikum Crossing Opt ---------------------------*- 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 is to be like LLVM's opt program, only for FIR. Such a program is // required for roundtrip testing, etc. // //===----------------------------------------------------------------------===// #include "flang/Optimizer/CodeGen/CodeGen.h" #include "flang/Optimizer/Dialect/Support/FIRContext.h" #include "flang/Optimizer/Dialect/Support/KindMapping.h" #include "flang/Optimizer/Support/DataLayout.h" #include "flang/Optimizer/Support/InitFIR.h" #include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Transforms/Passes.h" #include "flang/Tools/CrossToolHelpers.h" #include "mlir/IR/AsmState.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "mlir/Parser/Parser.h" #include "mlir/Pass/Pass.h" #include "mlir/Pass/PassManager.h" #include "mlir/Transforms/Passes.h" #include "llvm/Passes/OptimizationLevel.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #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; static cl::opt inputFilename(cl::Positional, cl::desc(""), cl::init("-")); static cl::opt outputFilename("o", cl::desc("Specify output filename"), cl::value_desc("filename"), cl::init("-")); static cl::opt emitFir("emit-fir", 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")); static cl::opt codeGenLLVM( "code-gen-llvm", cl::desc("Run only CodeGen passes and translate FIR to LLVM IR"), cl::init(false)); #include "flang/Tools/CLOptions.inc" static void printModule(mlir::ModuleOp mod, raw_ostream &output) { output << mod << '\n'; } // compile a .fir file static mlir::LogicalResult compileFIR(const mlir::PassPipelineCLParser &passPipeline) { // check that there is a file to load ErrorOr> fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFilename); if (std::error_code EC = fileOrErr.getError()) { errs() << "Could not open file: " << EC.message() << '\n'; return mlir::failure(); } // load the file into a module SourceMgr sourceMgr; sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc()); 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) { errs() << "Error can't load file " << inputFilename << '\n'; return mlir::failure(); } if (mlir::failed(owningRef->verifyInvariants())) { errs() << "Error verifying FIR module\n"; return mlir::failure(); } std::error_code ec; ToolOutputFile out(outputFilename, ec, sys::fs::OF_None); // run passes fir::KindMapping kindMap{&context}; fir::setTargetTriple(*owningRef, targetTriple); fir::setKindMapping(*owningRef, kindMap); // tco is a testing tool, so it will happily use the target independent // data layout if none is on the module. fir::support::setMLIRDataLayoutFromAttributes(*owningRef, /*allowDefaultLayout=*/true); mlir::PassManager pm((*owningRef)->getName(), mlir::OpPassManager::Nesting::Implicit); pm.enableVerifier(/*verifyPasses=*/true); (void)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 { MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); config.AliasAnalysis = true; // enabled when optimizing for speed if (codeGenLLVM) { // Run only CodeGen passes. fir::createDefaultFIRCodeGenPassPipeline(pm, config); } else { // Run tco with O2 by default. fir::createMLIRToLLVMPassPipeline(pm, config); } fir::addLLVMDialectToLLVMPass(pm, out.os()); } // run the pass manager if (mlir::succeeded(pm.run(*owningRef))) { // passes ran successfully, so keep the output if ((emitFir || passPipeline.hasAnyOccurrences()) && !codeGenLLVM) printModule(*owningRef, out.os()); out.keep(); return mlir::success(); } // pass manager failed printModule(*owningRef, errs()); errs() << "\n\nFAILED: " << inputFilename << '\n'; return mlir::failure(); } int main(int argc, char **argv) { // Disable the ExternalNameConversion pass by default until all the tests have // been updated to pass with it enabled. disableExternalNameConversion = true; [[maybe_unused]] InitLLVM y(argc, argv); fir::support::registerMLIRPassesForFortranTools(); fir::registerOptCodeGenPasses(); fir::registerOptTransformPasses(); mlir::registerMLIRContextCLOptions(); mlir::registerPassManagerCLOptions(); mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n"); return mlir::failed(compileFIR(passPipe)); }