1 //===- bbc.cpp - Burnside Bridge Compiler -----------------------*- 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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 /// 13 /// This is a tool for translating Fortran sources to the FIR dialect of MLIR. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "flang/Common/Fortran-features.h" 18 #include "flang/Common/default-kinds.h" 19 #include "flang/Lower/Bridge.h" 20 #include "flang/Lower/PFTBuilder.h" 21 #include "flang/Lower/Support/Verifier.h" 22 #include "flang/Optimizer/Support/FIRContext.h" 23 #include "flang/Optimizer/Support/InitFIR.h" 24 #include "flang/Optimizer/Support/InternalNames.h" 25 #include "flang/Optimizer/Support/KindMapping.h" 26 #include "flang/Optimizer/Support/Utils.h" 27 #include "flang/Optimizer/Transforms/Passes.h" 28 #include "flang/Parser/characters.h" 29 #include "flang/Parser/dump-parse-tree.h" 30 #include "flang/Parser/message.h" 31 #include "flang/Parser/parse-tree-visitor.h" 32 #include "flang/Parser/parse-tree.h" 33 #include "flang/Parser/parsing.h" 34 #include "flang/Parser/provenance.h" 35 #include "flang/Parser/unparse.h" 36 #include "flang/Semantics/expression.h" 37 #include "flang/Semantics/runtime-type-info.h" 38 #include "flang/Semantics/semantics.h" 39 #include "flang/Semantics/unparse-with-symbols.h" 40 #include "flang/Version.inc" 41 #include "mlir/Conversion/SCFToStandard/SCFToStandard.h" 42 #include "mlir/IR/AsmState.h" 43 #include "mlir/IR/BuiltinOps.h" 44 #include "mlir/IR/MLIRContext.h" 45 #include "mlir/Parser.h" 46 #include "mlir/Pass/Pass.h" 47 #include "mlir/Pass/PassManager.h" 48 #include "mlir/Pass/PassRegistry.h" 49 #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 50 #include "mlir/Transforms/Passes.h" 51 #include "llvm/Support/CommandLine.h" 52 #include "llvm/Support/ErrorOr.h" 53 #include "llvm/Support/FileSystem.h" 54 #include "llvm/Support/InitLLVM.h" 55 #include "llvm/Support/MemoryBuffer.h" 56 #include "llvm/Support/Path.h" 57 #include "llvm/Support/SourceMgr.h" 58 #include "llvm/Support/TargetSelect.h" 59 #include "llvm/Support/ToolOutputFile.h" 60 #include "llvm/Support/raw_ostream.h" 61 62 //===----------------------------------------------------------------------===// 63 // Some basic command-line options 64 //===----------------------------------------------------------------------===// 65 66 static llvm::cl::opt<std::string> inputFilename(llvm::cl::Positional, 67 llvm::cl::Required, 68 llvm::cl::desc("<input file>")); 69 70 static llvm::cl::opt<std::string> 71 outputFilename("o", llvm::cl::desc("Specify the output filename"), 72 llvm::cl::value_desc("filename")); 73 74 static llvm::cl::opt<bool> 75 emitFIR("emit-fir", 76 llvm::cl::desc("Dump the FIR created by lowering and exit"), 77 llvm::cl::init(false)); 78 79 static llvm::cl::opt<bool> pftDumpTest( 80 "pft-test", 81 llvm::cl::desc("parse the input, create a PFT, dump it, and exit"), 82 llvm::cl::init(false)); 83 84 #define FLANG_EXCLUDE_CODEGEN 85 #include "flang/Tools/CLOptions.inc" 86 87 //===----------------------------------------------------------------------===// 88 89 using ProgramName = std::string; 90 91 // Print the module without the "module { ... }" wrapper. 92 static void printModule(mlir::ModuleOp mlirModule, llvm::raw_ostream &out) { 93 for (auto &op : *mlirModule.getBody()) 94 out << op << '\n'; 95 out << '\n'; 96 } 97 98 static void registerAllPasses() { 99 fir::support::registerMLIRPassesForFortranTools(); 100 fir::registerOptTransformPasses(); 101 } 102 103 //===----------------------------------------------------------------------===// 104 // Translate Fortran input to FIR, a dialect of MLIR. 105 //===----------------------------------------------------------------------===// 106 107 static mlir::LogicalResult convertFortranSourceToMLIR( 108 std::string path, Fortran::parser::Options options, 109 const ProgramName &programPrefix, 110 Fortran::semantics::SemanticsContext &semanticsContext, 111 const mlir::PassPipelineCLParser &passPipeline) { 112 113 // prep for prescan and parse 114 Fortran::parser::Parsing parsing{semanticsContext.allCookedSources()}; 115 parsing.Prescan(path, options); 116 if (!parsing.messages().empty() && (parsing.messages().AnyFatalError())) { 117 llvm::errs() << programPrefix << "could not scan " << path << '\n'; 118 parsing.messages().Emit(llvm::errs(), parsing.allCooked()); 119 return mlir::failure(); 120 } 121 122 // parse the input Fortran 123 parsing.Parse(llvm::outs()); 124 parsing.messages().Emit(llvm::errs(), parsing.allCooked()); 125 if (!parsing.consumedWholeFile()) { 126 parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), 127 "parser FAIL (final position)"); 128 return mlir::failure(); 129 } 130 if ((!parsing.messages().empty() && (parsing.messages().AnyFatalError())) || 131 !parsing.parseTree().has_value()) { 132 llvm::errs() << programPrefix << "could not parse " << path << '\n'; 133 return mlir::failure(); 134 } 135 136 // run semantics 137 auto &parseTree = *parsing.parseTree(); 138 Fortran::semantics::Semantics semantics(semanticsContext, parseTree); 139 semantics.Perform(); 140 semantics.EmitMessages(llvm::errs()); 141 if (semantics.AnyFatalError()) { 142 llvm::errs() << programPrefix << "semantic errors in " << path << '\n'; 143 return mlir::failure(); 144 } 145 Fortran::semantics::RuntimeDerivedTypeTables tables; 146 if (!semantics.AnyFatalError()) { 147 tables = 148 Fortran::semantics::BuildRuntimeDerivedTypeTables(semanticsContext); 149 if (!tables.schemata) 150 llvm::errs() << programPrefix 151 << "could not find module file for __fortran_type_info\n"; 152 } 153 154 if (pftDumpTest) { 155 if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext)) { 156 Fortran::lower::dumpPFT(llvm::outs(), *ast); 157 return mlir::success(); 158 } 159 llvm::errs() << "Pre FIR Tree is NULL.\n"; 160 return mlir::failure(); 161 } 162 163 // translate to FIR dialect of MLIR 164 mlir::DialectRegistry registry; 165 fir::support::registerNonCodegenDialects(registry); 166 mlir::MLIRContext ctx(registry); 167 fir::support::loadNonCodegenDialects(ctx); 168 auto &defKinds = semanticsContext.defaultKinds(); 169 fir::KindMapping kindMap( 170 &ctx, llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)}); 171 auto burnside = Fortran::lower::LoweringBridge::create( 172 ctx, defKinds, semanticsContext.intrinsics(), parsing.allCooked(), "", 173 kindMap); 174 burnside.lower(parseTree, semanticsContext); 175 mlir::ModuleOp mlirModule = burnside.getModule(); 176 std::error_code ec; 177 std::string outputName = outputFilename; 178 if (!outputName.size()) 179 outputName = llvm::sys::path::stem(inputFilename).str().append(".mlir"); 180 llvm::raw_fd_ostream out(outputName, ec); 181 if (ec) 182 return mlir::emitError(mlir::UnknownLoc::get(&ctx), 183 "could not open output file ") 184 << outputName; 185 186 // Otherwise run the default passes. 187 mlir::PassManager pm(&ctx, mlir::OpPassManager::Nesting::Implicit); 188 pm.enableVerifier(/*verifyPasses=*/true); 189 mlir::applyPassManagerCLOptions(pm); 190 if (passPipeline.hasAnyOccurrences()) { 191 // run the command-line specified pipeline 192 (void)passPipeline.addToPipeline(pm, [&](const llvm::Twine &msg) { 193 mlir::emitError(mlir::UnknownLoc::get(&ctx)) << msg; 194 return mlir::failure(); 195 }); 196 } else if (emitFIR) { 197 // --emit-fir: Build the IR, verify it, and dump the IR if the IR passes 198 // verification. Use --dump-module-on-failure to dump invalid IR. 199 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); 200 if (mlir::failed(pm.run(mlirModule))) { 201 llvm::errs() << "FATAL: verification of lowering to FIR failed"; 202 return mlir::failure(); 203 } 204 printModule(mlirModule, out); 205 return mlir::success(); 206 } else { 207 // run the default canned pipeline 208 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); 209 210 // Add default optimizer pass pipeline. 211 fir::createDefaultFIROptimizerPassPipeline(pm); 212 } 213 214 if (mlir::succeeded(pm.run(mlirModule))) { 215 // Emit MLIR and do not lower to LLVM IR. 216 printModule(mlirModule, out); 217 return mlir::success(); 218 } 219 // Something went wrong. Try to dump the MLIR module. 220 llvm::errs() << "oops, pass manager reported failure\n"; 221 return mlir::failure(); 222 } 223 224 int main(int argc, char **argv) { 225 [[maybe_unused]] llvm::InitLLVM y(argc, argv); 226 registerAllPasses(); 227 228 mlir::registerMLIRContextCLOptions(); 229 mlir::registerPassManagerCLOptions(); 230 mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); 231 llvm::cl::ParseCommandLineOptions(argc, argv, "Burnside Bridge Compiler\n"); 232 233 ProgramName programPrefix; 234 programPrefix = argv[0] + ": "s; 235 236 Fortran::parser::Options options; 237 options.predefinitions.emplace_back("__flang__", "1"); 238 options.predefinitions.emplace_back("__flang_major__", 239 FLANG_VERSION_MAJOR_STRING); 240 options.predefinitions.emplace_back("__flang_minor__", 241 FLANG_VERSION_MINOR_STRING); 242 options.predefinitions.emplace_back("__flang_patchlevel__", 243 FLANG_VERSION_PATCHLEVEL_STRING); 244 245 Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; 246 Fortran::parser::AllSources allSources; 247 Fortran::parser::AllCookedSources allCookedSources(allSources); 248 Fortran::semantics::SemanticsContext semanticsContext{ 249 defaultKinds, options.features, allCookedSources}; 250 251 return mlir::failed(convertFortranSourceToMLIR( 252 inputFilename, options, programPrefix, semanticsContext, passPipe)); 253 } 254