241 lines · cpp
1//===- tco.cpp - Tilikum Crossing Opt ---------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This is to be like LLVM's opt program, only for FIR. Such a program is10// required for roundtrip testing, etc.11//12//===----------------------------------------------------------------------===//13 14#include "flang/Optimizer/CodeGen/CodeGen.h"15#include "flang/Optimizer/Dialect/Support/FIRContext.h"16#include "flang/Optimizer/Dialect/Support/KindMapping.h"17#include "flang/Optimizer/Support/DataLayout.h"18#include "flang/Optimizer/Support/InitFIR.h"19#include "flang/Optimizer/Support/InternalNames.h"20#include "flang/Optimizer/Transforms/Passes.h"21#include "flang/Tools/CrossToolHelpers.h"22#include "mlir/IR/AsmState.h"23#include "mlir/IR/BuiltinOps.h"24#include "mlir/IR/MLIRContext.h"25#include "mlir/Parser/Parser.h"26#include "mlir/Pass/Pass.h"27#include "mlir/Pass/PassManager.h"28#include "mlir/Transforms/Passes.h"29#include "llvm/Passes/OptimizationLevel.h"30#include "llvm/Support/CommandLine.h"31#include "llvm/Support/ErrorOr.h"32#include "llvm/Support/FileSystem.h"33#include "llvm/Support/InitLLVM.h"34#include "llvm/Support/MemoryBuffer.h"35#include "llvm/Support/SourceMgr.h"36#include "llvm/Support/TargetSelect.h"37#include "llvm/Support/ToolOutputFile.h"38#include "llvm/Support/raw_ostream.h"39 40using namespace llvm;41 42static cl::opt<std::string>43 inputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));44 45static cl::opt<std::string> outputFilename("o",46 cl::desc("Specify output filename"),47 cl::value_desc("filename"),48 cl::init("-"));49 50static cl::opt<bool> emitFir("emit-fir",51 cl::desc("Parse and pretty-print the input"),52 cl::init(false));53 54static cl::opt<unsigned>55 OptLevel("O",56 cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "57 "(default = '-O2')"),58 cl::Prefix, cl::init(2));59 60static cl::opt<std::string> targetTriple("target",61 cl::desc("specify a target triple"),62 cl::init("native"));63 64static cl::opt<std::string>65 targetCPU("target-cpu", cl::desc("specify a target CPU"), cl::init(""));66 67static cl::opt<std::string> tuneCPU("tune-cpu", cl::desc("specify a tune CPU"),68 cl::init(""));69 70static cl::opt<std::string>71 targetFeatures("target-features", cl::desc("specify the target features"),72 cl::init(""));73 74static cl::opt<bool> codeGenLLVM(75 "code-gen-llvm",76 cl::desc("Run only CodeGen passes and translate FIR to LLVM IR"),77 cl::init(false));78 79static cl::opt<bool> emitFinalMLIR(80 "emit-final-mlir",81 cl::desc("Only translate FIR to MLIR, do not lower to LLVM IR"),82 cl::init(false));83 84static cl::opt<bool>85 simplifyMLIR("simplify-mlir",86 cl::desc("Run CSE and canonicalization on MLIR output"),87 cl::init(false));88 89// Enabled by default to accurately reflect -O290static cl::opt<bool> enableAliasAnalysis("enable-aa",91 cl::desc("Enable FIR alias analysis"),92 cl::init(true));93 94static cl::opt<bool> testGeneratorMode(95 "test-gen", cl::desc("-emit-final-mlir -simplify-mlir -enable-aa=false"),96 cl::init(false));97 98#include "flang/Optimizer/Passes/CommandLineOpts.h"99#include "flang/Optimizer/Passes/Pipelines.h"100 101static void printModule(mlir::ModuleOp mod, raw_ostream &output) {102 output << mod << '\n';103}104 105static std::optional<llvm::OptimizationLevel>106getOptimizationLevel(unsigned level) {107 switch (level) {108 default:109 return std::nullopt;110 case 0:111 return llvm::OptimizationLevel::O0;112 case 1:113 return llvm::OptimizationLevel::O1;114 case 2:115 return llvm::OptimizationLevel::O2;116 case 3:117 return llvm::OptimizationLevel::O3;118 }119}120 121// compile a .fir file122static llvm::LogicalResult123compileFIR(const mlir::PassPipelineCLParser &passPipeline) {124 // check that there is a file to load125 ErrorOr<std::unique_ptr<MemoryBuffer>> fileOrErr =126 MemoryBuffer::getFileOrSTDIN(inputFilename);127 128 if (std::error_code EC = fileOrErr.getError()) {129 errs() << "Could not open file: " << EC.message() << '\n';130 return mlir::failure();131 }132 133 // load the file into a module134 SourceMgr sourceMgr;135 sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc());136 mlir::DialectRegistry registry;137 fir::support::registerDialects(registry);138 fir::support::addFIRExtensions(registry);139 mlir::MLIRContext context(registry);140 fir::support::loadDialects(context);141 fir::support::registerLLVMTranslation(context);142 auto owningRef = mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, &context);143 144 if (!owningRef) {145 errs() << "Error can't load file " << inputFilename << '\n';146 return mlir::failure();147 }148 if (mlir::failed(owningRef->verifyInvariants())) {149 errs() << "Error verifying FIR module\n";150 return mlir::failure();151 }152 153 std::error_code ec;154 ToolOutputFile out(outputFilename, ec, sys::fs::OF_None);155 156 // run passes157 fir::KindMapping kindMap{&context};158 fir::setTargetTriple(*owningRef, targetTriple);159 fir::setKindMapping(*owningRef, kindMap);160 fir::setTargetCPU(*owningRef, targetCPU);161 fir::setTuneCPU(*owningRef, tuneCPU);162 fir::setTargetFeatures(*owningRef, targetFeatures);163 // tco is a testing tool, so it will happily use the target independent164 // data layout if none is on the module.165 fir::support::setMLIRDataLayoutFromAttributes(*owningRef,166 /*allowDefaultLayout=*/true);167 mlir::PassManager pm((*owningRef)->getName(),168 mlir::OpPassManager::Nesting::Implicit);169 pm.enableVerifier(/*verifyPasses=*/true);170 (void)mlir::applyPassManagerCLOptions(pm);171 if (emitFir) {172 // parse the input and pretty-print it back out173 // -emit-fir intentionally disables all the passes174 } else if (passPipeline.hasAnyOccurrences()) {175 auto errorHandler = [&](const Twine &msg) {176 mlir::emitError(mlir::UnknownLoc::get(pm.getContext())) << msg;177 return mlir::failure();178 };179 if (mlir::failed(passPipeline.addToPipeline(pm, errorHandler)))180 return mlir::failure();181 } else {182 std::optional<llvm::OptimizationLevel> level =183 getOptimizationLevel(OptLevel);184 if (!level) {185 errs() << "Error invalid optimization level\n";186 return mlir::failure();187 }188 MLIRToLLVMPassPipelineConfig config(*level);189 // TODO: config.StackArrays should be set here?190 config.EnableOpenMP = true; // assume the input contains OpenMP191 config.AliasAnalysis = enableAliasAnalysis && !testGeneratorMode;192 config.LoopVersioning = OptLevel > 2;193 if (codeGenLLVM) {194 // Run only CodeGen passes.195 fir::createDefaultFIRCodeGenPassPipeline(pm, config);196 } else {197 // Run tco with O2 by default.198 fir::registerDefaultInlinerPass(config);199 fir::createMLIRToLLVMPassPipeline(pm, config);200 }201 if (simplifyMLIR || testGeneratorMode) {202 pm.addPass(mlir::createCanonicalizerPass());203 pm.addPass(mlir::createCSEPass());204 }205 if (!emitFinalMLIR && !testGeneratorMode)206 fir::addLLVMDialectToLLVMPass(pm, out.os());207 }208 209 // run the pass manager210 if (mlir::succeeded(pm.run(*owningRef))) {211 // passes ran successfully, so keep the output212 if ((emitFir || passPipeline.hasAnyOccurrences() || emitFinalMLIR ||213 testGeneratorMode) &&214 !codeGenLLVM)215 printModule(*owningRef, out.os());216 out.keep();217 return mlir::success();218 }219 220 // pass manager failed221 printModule(*owningRef, errs());222 errs() << "\n\nFAILED: " << inputFilename << '\n';223 return mlir::failure();224}225 226int main(int argc, char **argv) {227 // Disable the ExternalNameConversion pass by default until all the tests have228 // been updated to pass with it enabled.229 disableExternalNameConversion = true;230 231 [[maybe_unused]] InitLLVM y(argc, argv);232 fir::support::registerMLIRPassesForFortranTools();233 fir::registerOptCodeGenPasses();234 fir::registerOptTransformPasses();235 mlir::registerMLIRContextCLOptions();236 mlir::registerPassManagerCLOptions();237 mlir::PassPipelineCLParser passPipe("", "Compiler passes to run");238 cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n");239 return mlir::failed(compileFIR(passPipe));240}241