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/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::list<std::string> 74 includeDirs("I", llvm::cl::desc("include module search paths")); 75 76 static llvm::cl::alias includeAlias("module-directory", 77 llvm::cl::desc("module search directory"), 78 llvm::cl::aliasopt(includeDirs)); 79 80 static llvm::cl::list<std::string> 81 intrinsicIncludeDirs("J", llvm::cl::desc("intrinsic module search paths")); 82 83 static llvm::cl::alias 84 intrinsicIncludeAlias("intrinsic-module-directory", 85 llvm::cl::desc("intrinsic module directory"), 86 llvm::cl::aliasopt(intrinsicIncludeDirs)); 87 88 static llvm::cl::opt<std::string> 89 moduleDir("module", llvm::cl::desc("module output directory (default .)"), 90 llvm::cl::init(".")); 91 92 static llvm::cl::opt<std::string> 93 moduleSuffix("module-suffix", llvm::cl::desc("module file suffix override"), 94 llvm::cl::init(".mod")); 95 96 static llvm::cl::opt<bool> 97 emitFIR("emit-fir", 98 llvm::cl::desc("Dump the FIR created by lowering and exit"), 99 llvm::cl::init(false)); 100 101 static llvm::cl::opt<bool> warnStdViolation("Mstandard", 102 llvm::cl::desc("emit warnings"), 103 llvm::cl::init(false)); 104 105 static llvm::cl::opt<bool> warnIsError("Werror", 106 llvm::cl::desc("warnings are errors"), 107 llvm::cl::init(false)); 108 109 static llvm::cl::opt<bool> dumpSymbols("dump-symbols", 110 llvm::cl::desc("dump the symbol table"), 111 llvm::cl::init(false)); 112 113 static llvm::cl::opt<bool> pftDumpTest( 114 "pft-test", 115 llvm::cl::desc("parse the input, create a PFT, dump it, and exit"), 116 llvm::cl::init(false)); 117 118 static llvm::cl::opt<bool> enableOpenMP("fopenmp", 119 llvm::cl::desc("enable openmp"), 120 llvm::cl::init(false)); 121 122 static llvm::cl::opt<bool> enableOpenACC("fopenacc", 123 llvm::cl::desc("enable openacc"), 124 llvm::cl::init(false)); 125 126 #define FLANG_EXCLUDE_CODEGEN 127 #include "flang/Tools/CLOptions.inc" 128 129 //===----------------------------------------------------------------------===// 130 131 using ProgramName = std::string; 132 133 // Print the module without the "module { ... }" wrapper. 134 static void printModule(mlir::ModuleOp mlirModule, llvm::raw_ostream &out) { 135 for (auto &op : *mlirModule.getBody()) 136 out << op << '\n'; 137 out << '\n'; 138 } 139 140 static void registerAllPasses() { 141 fir::support::registerMLIRPassesForFortranTools(); 142 fir::registerOptTransformPasses(); 143 } 144 145 //===----------------------------------------------------------------------===// 146 // Translate Fortran input to FIR, a dialect of MLIR. 147 //===----------------------------------------------------------------------===// 148 149 static mlir::LogicalResult convertFortranSourceToMLIR( 150 std::string path, Fortran::parser::Options options, 151 const ProgramName &programPrefix, 152 Fortran::semantics::SemanticsContext &semanticsContext, 153 const mlir::PassPipelineCLParser &passPipeline) { 154 155 // prep for prescan and parse 156 Fortran::parser::Parsing parsing{semanticsContext.allCookedSources()}; 157 parsing.Prescan(path, options); 158 if (!parsing.messages().empty() && (parsing.messages().AnyFatalError())) { 159 llvm::errs() << programPrefix << "could not scan " << path << '\n'; 160 parsing.messages().Emit(llvm::errs(), parsing.allCooked()); 161 return mlir::failure(); 162 } 163 164 // parse the input Fortran 165 parsing.Parse(llvm::outs()); 166 parsing.messages().Emit(llvm::errs(), parsing.allCooked()); 167 if (!parsing.consumedWholeFile()) { 168 parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), 169 "parser FAIL (final position)"); 170 return mlir::failure(); 171 } 172 if ((!parsing.messages().empty() && (parsing.messages().AnyFatalError())) || 173 !parsing.parseTree().has_value()) { 174 llvm::errs() << programPrefix << "could not parse " << path << '\n'; 175 return mlir::failure(); 176 } 177 178 // run semantics 179 auto &parseTree = *parsing.parseTree(); 180 Fortran::semantics::Semantics semantics(semanticsContext, parseTree); 181 semantics.Perform(); 182 semantics.EmitMessages(llvm::errs()); 183 if (semantics.AnyFatalError()) { 184 llvm::errs() << programPrefix << "semantic errors in " << path << '\n'; 185 return mlir::failure(); 186 } 187 Fortran::semantics::RuntimeDerivedTypeTables tables; 188 if (!semantics.AnyFatalError()) { 189 tables = 190 Fortran::semantics::BuildRuntimeDerivedTypeTables(semanticsContext); 191 if (!tables.schemata) 192 llvm::errs() << programPrefix 193 << "could not find module file for __fortran_type_info\n"; 194 } 195 196 if (dumpSymbols) { 197 semantics.DumpSymbols(llvm::outs()); 198 return mlir::success(); 199 } 200 201 if (pftDumpTest) { 202 if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext)) { 203 Fortran::lower::dumpPFT(llvm::outs(), *ast); 204 return mlir::success(); 205 } 206 llvm::errs() << "Pre FIR Tree is NULL.\n"; 207 return mlir::failure(); 208 } 209 210 // translate to FIR dialect of MLIR 211 mlir::DialectRegistry registry; 212 fir::support::registerNonCodegenDialects(registry); 213 mlir::MLIRContext ctx(registry); 214 fir::support::loadNonCodegenDialects(ctx); 215 auto &defKinds = semanticsContext.defaultKinds(); 216 fir::KindMapping kindMap( 217 &ctx, llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)}); 218 auto burnside = Fortran::lower::LoweringBridge::create( 219 ctx, defKinds, semanticsContext.intrinsics(), 220 semanticsContext.targetCharacteristics(), parsing.allCooked(), "", 221 kindMap); 222 burnside.lower(parseTree, semanticsContext); 223 mlir::ModuleOp mlirModule = burnside.getModule(); 224 std::error_code ec; 225 std::string outputName = outputFilename; 226 if (!outputName.size()) 227 outputName = llvm::sys::path::stem(inputFilename).str().append(".mlir"); 228 llvm::raw_fd_ostream out(outputName, ec); 229 if (ec) 230 return mlir::emitError(mlir::UnknownLoc::get(&ctx), 231 "could not open output file ") 232 << outputName; 233 234 // Otherwise run the default passes. 235 mlir::PassManager pm(&ctx, mlir::OpPassManager::Nesting::Implicit); 236 pm.enableVerifier(/*verifyPasses=*/true); 237 mlir::applyPassManagerCLOptions(pm); 238 if (passPipeline.hasAnyOccurrences()) { 239 // run the command-line specified pipeline 240 (void)passPipeline.addToPipeline(pm, [&](const llvm::Twine &msg) { 241 mlir::emitError(mlir::UnknownLoc::get(&ctx)) << msg; 242 return mlir::failure(); 243 }); 244 } else if (emitFIR) { 245 // --emit-fir: Build the IR, verify it, and dump the IR if the IR passes 246 // verification. Use --dump-module-on-failure to dump invalid IR. 247 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); 248 if (mlir::failed(pm.run(mlirModule))) { 249 llvm::errs() << "FATAL: verification of lowering to FIR failed"; 250 return mlir::failure(); 251 } 252 printModule(mlirModule, out); 253 return mlir::success(); 254 } else { 255 // run the default canned pipeline 256 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); 257 258 // Add default optimizer pass pipeline. 259 fir::createDefaultFIROptimizerPassPipeline(pm); 260 } 261 262 if (mlir::succeeded(pm.run(mlirModule))) { 263 // Emit MLIR and do not lower to LLVM IR. 264 printModule(mlirModule, out); 265 return mlir::success(); 266 } 267 // Something went wrong. Try to dump the MLIR module. 268 llvm::errs() << "oops, pass manager reported failure\n"; 269 return mlir::failure(); 270 } 271 272 int main(int argc, char **argv) { 273 [[maybe_unused]] llvm::InitLLVM y(argc, argv); 274 registerAllPasses(); 275 276 mlir::registerMLIRContextCLOptions(); 277 mlir::registerPassManagerCLOptions(); 278 mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); 279 llvm::cl::ParseCommandLineOptions(argc, argv, "Burnside Bridge Compiler\n"); 280 281 ProgramName programPrefix; 282 programPrefix = argv[0] + ": "s; 283 284 if (includeDirs.size() == 0) { 285 includeDirs.push_back("."); 286 // Default Fortran modules should be installed in include/flang (a sibling 287 // to the bin) directory. 288 intrinsicIncludeDirs.push_back( 289 llvm::sys::path::parent_path( 290 llvm::sys::path::parent_path( 291 llvm::sys::fs::getMainExecutable(argv[0], nullptr))) 292 .str() + 293 "/include/flang"); 294 } 295 296 Fortran::parser::Options options; 297 options.predefinitions.emplace_back("__flang__"s, "1"s); 298 options.predefinitions.emplace_back("__flang_major__"s, 299 std::string{FLANG_VERSION_MAJOR_STRING}); 300 options.predefinitions.emplace_back("__flang_minor__"s, 301 std::string{FLANG_VERSION_MINOR_STRING}); 302 options.predefinitions.emplace_back( 303 "__flang_patchlevel__"s, std::string{FLANG_VERSION_PATCHLEVEL_STRING}); 304 305 // enable parsing of OpenMP 306 if (enableOpenMP) { 307 options.features.Enable(Fortran::common::LanguageFeature::OpenMP); 308 options.predefinitions.emplace_back("_OPENMP", "201511"); 309 } 310 311 // enable parsing of OpenACC 312 if (enableOpenACC) { 313 options.features.Enable(Fortran::common::LanguageFeature::OpenACC); 314 options.predefinitions.emplace_back("_OPENACC", "201911"); 315 } 316 317 Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; 318 Fortran::parser::AllSources allSources; 319 Fortran::parser::AllCookedSources allCookedSources(allSources); 320 Fortran::semantics::SemanticsContext semanticsContext{ 321 defaultKinds, options.features, allCookedSources}; 322 semanticsContext.set_moduleDirectory(moduleDir) 323 .set_moduleFileSuffix(moduleSuffix) 324 .set_searchDirectories(includeDirs) 325 .set_intrinsicModuleDirectories(intrinsicIncludeDirs) 326 .set_warnOnNonstandardUsage(warnStdViolation) 327 .set_warningsAreErrors(warnIsError); 328 329 return mlir::failed(convertFortranSourceToMLIR( 330 inputFilename, options, programPrefix, semanticsContext, passPipe)); 331 } 332