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.
printModule(mlir::ModuleOp mlirModule,llvm::raw_ostream & out)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
registerAllPasses()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
convertFortranSourceToMLIR(std::string path,Fortran::parser::Options options,const ProgramName & programPrefix,Fortran::semantics::SemanticsContext & semanticsContext,const mlir::PassPipelineCLParser & passPipeline)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
main(int argc,char ** argv)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