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