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