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