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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This is to be like LLVM's opt program, only for FIR. Such a program is 10 // 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 40 using namespace llvm; 41 42 static cl::opt<std::string> 43 inputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); 44 45 static cl::opt<std::string> outputFilename("o", 46 cl::desc("Specify output filename"), 47 cl::value_desc("filename"), 48 cl::init("-")); 49 50 static cl::opt<bool> emitFir("emit-fir", 51 cl::desc("Parse and pretty-print the input"), 52 cl::init(false)); 53 54 static cl::opt<std::string> targetTriple("target", 55 cl::desc("specify a target triple"), 56 cl::init("native")); 57 58 static cl::opt<std::string> 59 targetCPU("target-cpu", cl::desc("specify a target CPU"), cl::init("")); 60 61 static cl::opt<std::string> tuneCPU("tune-cpu", cl::desc("specify a tune CPU"), 62 cl::init("")); 63 64 static cl::opt<std::string> 65 targetFeatures("target-features", cl::desc("specify the target features"), 66 cl::init("")); 67 68 static cl::opt<bool> codeGenLLVM( 69 "code-gen-llvm", 70 cl::desc("Run only CodeGen passes and translate FIR to LLVM IR"), 71 cl::init(false)); 72 73 #include "flang/Optimizer/Passes/CommandLineOpts.h" 74 #include "flang/Optimizer/Passes/Pipelines.h" 75 76 static void printModule(mlir::ModuleOp mod, raw_ostream &output) { 77 output << mod << '\n'; 78 } 79 80 // compile a .fir file 81 static llvm::LogicalResult 82 compileFIR(const mlir::PassPipelineCLParser &passPipeline) { 83 // check that there is a file to load 84 ErrorOr<std::unique_ptr<MemoryBuffer>> fileOrErr = 85 MemoryBuffer::getFileOrSTDIN(inputFilename); 86 87 if (std::error_code EC = fileOrErr.getError()) { 88 errs() << "Could not open file: " << EC.message() << '\n'; 89 return mlir::failure(); 90 } 91 92 // load the file into a module 93 SourceMgr sourceMgr; 94 sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc()); 95 mlir::DialectRegistry registry; 96 fir::support::registerDialects(registry); 97 fir::support::addFIRExtensions(registry); 98 mlir::MLIRContext context(registry); 99 fir::support::loadDialects(context); 100 fir::support::registerLLVMTranslation(context); 101 auto owningRef = mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, &context); 102 103 if (!owningRef) { 104 errs() << "Error can't load file " << inputFilename << '\n'; 105 return mlir::failure(); 106 } 107 if (mlir::failed(owningRef->verifyInvariants())) { 108 errs() << "Error verifying FIR module\n"; 109 return mlir::failure(); 110 } 111 112 std::error_code ec; 113 ToolOutputFile out(outputFilename, ec, sys::fs::OF_None); 114 115 // run passes 116 fir::KindMapping kindMap{&context}; 117 fir::setTargetTriple(*owningRef, targetTriple); 118 fir::setKindMapping(*owningRef, kindMap); 119 fir::setTargetCPU(*owningRef, targetCPU); 120 fir::setTuneCPU(*owningRef, tuneCPU); 121 fir::setTargetFeatures(*owningRef, targetFeatures); 122 // tco is a testing tool, so it will happily use the target independent 123 // data layout if none is on the module. 124 fir::support::setMLIRDataLayoutFromAttributes(*owningRef, 125 /*allowDefaultLayout=*/true); 126 mlir::PassManager pm((*owningRef)->getName(), 127 mlir::OpPassManager::Nesting::Implicit); 128 pm.enableVerifier(/*verifyPasses=*/true); 129 (void)mlir::applyPassManagerCLOptions(pm); 130 if (emitFir) { 131 // parse the input and pretty-print it back out 132 // -emit-fir intentionally disables all the passes 133 } else if (passPipeline.hasAnyOccurrences()) { 134 auto errorHandler = [&](const Twine &msg) { 135 mlir::emitError(mlir::UnknownLoc::get(pm.getContext())) << msg; 136 return mlir::failure(); 137 }; 138 if (mlir::failed(passPipeline.addToPipeline(pm, errorHandler))) 139 return mlir::failure(); 140 } else { 141 MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); 142 config.EnableOpenMP = true; // assume the input contains OpenMP 143 config.AliasAnalysis = true; // enabled when optimizing for speed 144 if (codeGenLLVM) { 145 // Run only CodeGen passes. 146 fir::createDefaultFIRCodeGenPassPipeline(pm, config); 147 } else { 148 // Run tco with O2 by default. 149 fir::registerDefaultInlinerPass(config); 150 fir::createMLIRToLLVMPassPipeline(pm, config); 151 } 152 fir::addLLVMDialectToLLVMPass(pm, out.os()); 153 } 154 155 // run the pass manager 156 if (mlir::succeeded(pm.run(*owningRef))) { 157 // passes ran successfully, so keep the output 158 if ((emitFir || passPipeline.hasAnyOccurrences()) && !codeGenLLVM) 159 printModule(*owningRef, out.os()); 160 out.keep(); 161 return mlir::success(); 162 } 163 164 // pass manager failed 165 printModule(*owningRef, errs()); 166 errs() << "\n\nFAILED: " << inputFilename << '\n'; 167 return mlir::failure(); 168 } 169 170 int main(int argc, char **argv) { 171 // Disable the ExternalNameConversion pass by default until all the tests have 172 // been updated to pass with it enabled. 173 disableExternalNameConversion = true; 174 175 [[maybe_unused]] InitLLVM y(argc, argv); 176 fir::support::registerMLIRPassesForFortranTools(); 177 fir::registerOptCodeGenPasses(); 178 fir::registerOptTransformPasses(); 179 mlir::registerMLIRContextCLOptions(); 180 mlir::registerPassManagerCLOptions(); 181 mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); 182 cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n"); 183 return mlir::failed(compileFIR(passPipe)); 184 } 185