xref: /llvm-project-15.0.7/flang/tools/bbc/bbc.cpp (revision 8aad330e)
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