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