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/OpenMP-features.h"
19#include "flang/Common/Version.h"
20#include "flang/Common/default-kinds.h"
21#include "flang/Lower/Bridge.h"
22#include "flang/Lower/PFTBuilder.h"
23#include "flang/Lower/Support/Verifier.h"
24#include "flang/Optimizer/Dialect/Support/FIRContext.h"
25#include "flang/Optimizer/Dialect/Support/KindMapping.h"
26#include "flang/Optimizer/Support/InitFIR.h"
27#include "flang/Optimizer/Support/InternalNames.h"
28#include "flang/Optimizer/Support/Utils.h"
29#include "flang/Optimizer/Transforms/Passes.h"
30#include "flang/Parser/characters.h"
31#include "flang/Parser/dump-parse-tree.h"
32#include "flang/Parser/message.h"
33#include "flang/Parser/parse-tree-visitor.h"
34#include "flang/Parser/parse-tree.h"
35#include "flang/Parser/parsing.h"
36#include "flang/Parser/provenance.h"
37#include "flang/Parser/unparse.h"
38#include "flang/Semantics/expression.h"
39#include "flang/Semantics/runtime-type-info.h"
40#include "flang/Semantics/semantics.h"
41#include "flang/Semantics/unparse-with-symbols.h"
42#include "flang/Tools/CrossToolHelpers.h"
43#include "flang/Tools/TargetSetup.h"
44#include "flang/Version.inc"
45#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
46#include "mlir/IR/AsmState.h"
47#include "mlir/IR/BuiltinOps.h"
48#include "mlir/IR/MLIRContext.h"
49#include "mlir/Parser/Parser.h"
50#include "mlir/Pass/Pass.h"
51#include "mlir/Pass/PassManager.h"
52#include "mlir/Pass/PassRegistry.h"
53#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
54#include "mlir/Transforms/Passes.h"
55#include "llvm/MC/TargetRegistry.h"
56#include "llvm/Passes/OptimizationLevel.h"
57#include "llvm/Support/CommandLine.h"
58#include "llvm/Support/ErrorOr.h"
59#include "llvm/Support/FileSystem.h"
60#include "llvm/Support/InitLLVM.h"
61#include "llvm/Support/MemoryBuffer.h"
62#include "llvm/Support/Path.h"
63#include "llvm/Support/SourceMgr.h"
64#include "llvm/Support/TargetSelect.h"
65#include "llvm/Support/ToolOutputFile.h"
66#include "llvm/Support/raw_ostream.h"
67#include "llvm/TargetParser/Host.h"
68#include "llvm/TargetParser/Triple.h"
69#include <memory>
70
71//===----------------------------------------------------------------------===//
72// Some basic command-line options
73//===----------------------------------------------------------------------===//
74
75static llvm::cl::opt<std::string> inputFilename(llvm::cl::Positional,
76 llvm::cl::Required,
77 llvm::cl::desc("<input file>"));
78
79static llvm::cl::opt<std::string>
80 outputFilename("o", llvm::cl::desc("Specify the output filename"),
81 llvm::cl::value_desc("filename"));
82
83static llvm::cl::list<std::string>
84 includeDirs("I", llvm::cl::desc("include module search paths"));
85
86static llvm::cl::alias includeAlias("module-directory",
87 llvm::cl::desc("module search directory"),
88 llvm::cl::aliasopt(includeDirs));
89
90static llvm::cl::list<std::string>
91 intrinsicIncludeDirs("J", llvm::cl::desc("intrinsic module search paths"));
92
93static llvm::cl::alias
94 intrinsicIncludeAlias("intrinsic-module-directory",
95 llvm::cl::desc("intrinsic module directory"),
96 llvm::cl::aliasopt(intrinsicIncludeDirs));
97
98static llvm::cl::opt<std::string>
99 moduleDir("module", llvm::cl::desc("module output directory (default .)"),
100 llvm::cl::init(Val: "."));
101
102static llvm::cl::opt<std::string>
103 moduleSuffix("module-suffix", llvm::cl::desc("module file suffix override"),
104 llvm::cl::init(Val: ".mod"));
105
106static llvm::cl::opt<bool>
107 emitFIR("emit-fir",
108 llvm::cl::desc("Dump the FIR created by lowering and exit"),
109 llvm::cl::init(Val: false));
110
111static llvm::cl::opt<bool>
112 emitHLFIR("emit-hlfir",
113 llvm::cl::desc("Dump the HLFIR created by lowering and exit"),
114 llvm::cl::init(Val: false));
115
116static llvm::cl::opt<bool> warnStdViolation("Mstandard",
117 llvm::cl::desc("emit warnings"),
118 llvm::cl::init(Val: false));
119
120static llvm::cl::opt<bool> warnIsError("Werror",
121 llvm::cl::desc("warnings are errors"),
122 llvm::cl::init(Val: false));
123
124static llvm::cl::opt<bool> dumpSymbols("dump-symbols",
125 llvm::cl::desc("dump the symbol table"),
126 llvm::cl::init(Val: false));
127
128static llvm::cl::opt<bool> pftDumpTest(
129 "pft-test",
130 llvm::cl::desc("parse the input, create a PFT, dump it, and exit"),
131 llvm::cl::init(Val: false));
132
133static llvm::cl::opt<bool> enableOpenMP("fopenmp",
134 llvm::cl::desc("enable openmp"),
135 llvm::cl::init(Val: false));
136
137static llvm::cl::opt<bool>
138 enableOpenMPDevice("fopenmp-is-target-device",
139 llvm::cl::desc("enable openmp device compilation"),
140 llvm::cl::init(Val: false));
141
142static llvm::cl::opt<bool>
143 enableOpenMPGPU("fopenmp-is-gpu",
144 llvm::cl::desc("enable openmp GPU target codegen"),
145 llvm::cl::init(Val: false));
146
147// A simplified subset of the OpenMP RTL Flags from Flang, only the primary
148// positive options are available, no negative options e.g. fopen_assume* vs
149// fno_open_assume*
150static llvm::cl::opt<uint32_t>
151 setOpenMPVersion("fopenmp-version",
152 llvm::cl::desc("OpenMP standard version"),
153 llvm::cl::init(Val: 11));
154
155static llvm::cl::opt<uint32_t> setOpenMPTargetDebug(
156 "fopenmp-target-debug",
157 llvm::cl::desc("Enable debugging in the OpenMP offloading device RTL"),
158 llvm::cl::init(Val: 0));
159
160static llvm::cl::opt<bool> setOpenMPThreadSubscription(
161 "fopenmp-assume-threads-oversubscription",
162 llvm::cl::desc("Assume work-shared loops do not have more "
163 "iterations than participating threads."),
164 llvm::cl::init(Val: false));
165
166static llvm::cl::opt<bool> setOpenMPTeamSubscription(
167 "fopenmp-assume-teams-oversubscription",
168 llvm::cl::desc("Assume distributed loops do not have more iterations than "
169 "participating teams."),
170 llvm::cl::init(Val: false));
171
172static llvm::cl::opt<bool> setOpenMPNoThreadState(
173 "fopenmp-assume-no-thread-state",
174 llvm::cl::desc(
175 "Assume that no thread in a parallel region will modify an ICV."),
176 llvm::cl::init(Val: false));
177
178static llvm::cl::opt<bool> setOpenMPNoNestedParallelism(
179 "fopenmp-assume-no-nested-parallelism",
180 llvm::cl::desc("Assume that no thread in a parallel region will encounter "
181 "a parallel region."),
182 llvm::cl::init(Val: false));
183
184static llvm::cl::opt<bool>
185 setNoGPULib("nogpulib",
186 llvm::cl::desc("Do not link device library for CUDA/HIP device "
187 "compilation"),
188 llvm::cl::init(Val: false));
189
190static llvm::cl::opt<bool> enableOpenACC("fopenacc",
191 llvm::cl::desc("enable openacc"),
192 llvm::cl::init(Val: false));
193
194static llvm::cl::opt<bool> enableNoPPCNativeVecElemOrder(
195 "fno-ppc-native-vector-element-order",
196 llvm::cl::desc("no PowerPC native vector element order."),
197 llvm::cl::init(Val: false));
198
199static llvm::cl::opt<bool> useHLFIR("hlfir",
200 llvm::cl::desc("Lower to high level FIR"),
201 llvm::cl::init(Val: true));
202
203static llvm::cl::opt<bool> enableCUDA("fcuda",
204 llvm::cl::desc("enable CUDA Fortran"),
205 llvm::cl::init(Val: false));
206
207static llvm::cl::opt<bool> fixedForm("ffixed-form",
208 llvm::cl::desc("enable fixed form"),
209 llvm::cl::init(Val: false));
210static llvm::cl::opt<std::string>
211 targetTripleOverride("target",
212 llvm::cl::desc("Override host target triple"),
213 llvm::cl::init(Val: ""));
214
215#define FLANG_EXCLUDE_CODEGEN
216#include "flang/Tools/CLOptions.inc"
217
218//===----------------------------------------------------------------------===//
219
220using ProgramName = std::string;
221
222// Print the module with the "module { ... }" wrapper, preventing
223// information loss from attribute information appended to the module
224static void printModule(mlir::ModuleOp mlirModule, llvm::raw_ostream &out) {
225 out << mlirModule << '\n';
226}
227
228static void registerAllPasses() {
229 fir::support::registerMLIRPassesForFortranTools();
230 fir::registerOptTransformPasses();
231}
232
233/// Create a target machine that is at least sufficient to get data-layout
234/// information required by flang semantics and lowering. Note that it may not
235/// contain all the CPU feature information to get optimized assembly generation
236/// from LLVM IR. Drivers that needs to generate assembly from LLVM IR should
237/// create a target machine according to their specific options.
238static std::unique_ptr<llvm::TargetMachine>
239createTargetMachine(llvm::StringRef targetTriple, std::string &error) {
240 std::string triple{targetTriple};
241 if (triple.empty())
242 triple = llvm::sys::getDefaultTargetTriple();
243
244 const llvm::Target *theTarget =
245 llvm::TargetRegistry::lookupTarget(Triple: triple, Error&: error);
246 if (!theTarget)
247 return nullptr;
248 return std::unique_ptr<llvm::TargetMachine>{
249 theTarget->createTargetMachine(TT: triple, /*CPU=*/"",
250 /*Features=*/"", Options: llvm::TargetOptions(),
251 /*Reloc::Model=*/RM: std::nullopt)};
252}
253
254/// Build and execute the OpenMPFIRPassPipeline with its own instance
255/// of the pass manager, allowing it to be invoked as soon as it's
256/// required without impacting the main pass pipeline that may be invoked
257/// more than once for verification.
258static mlir::LogicalResult runOpenMPPasses(mlir::ModuleOp mlirModule) {
259 mlir::PassManager pm(mlirModule->getName(),
260 mlir::OpPassManager::Nesting::Implicit);
261 fir::createOpenMPFIRPassPipeline(pm, enableOpenMPDevice);
262 (void)mlir::applyPassManagerCLOptions(pm);
263 if (mlir::failed(pm.run(mlirModule))) {
264 llvm::errs() << "FATAL: failed to correctly apply OpenMP pass pipeline";
265 return mlir::failure();
266 }
267 return mlir::success();
268}
269
270//===----------------------------------------------------------------------===//
271// Translate Fortran input to FIR, a dialect of MLIR.
272//===----------------------------------------------------------------------===//
273
274static mlir::LogicalResult convertFortranSourceToMLIR(
275 std::string path, Fortran::parser::Options options,
276 const ProgramName &programPrefix,
277 Fortran::semantics::SemanticsContext &semanticsContext,
278 const mlir::PassPipelineCLParser &passPipeline,
279 const llvm::TargetMachine &targetMachine) {
280
281 // prep for prescan and parse
282 Fortran::parser::Parsing parsing{semanticsContext.allCookedSources()};
283 parsing.Prescan(path, options);
284 if (!parsing.messages().empty() && (parsing.messages().AnyFatalError())) {
285 llvm::errs() << programPrefix << "could not scan " << path << '\n';
286 parsing.messages().Emit(llvm::errs(), parsing.allCooked());
287 return mlir::failure();
288 }
289
290 // parse the input Fortran
291 parsing.Parse(llvm::outs());
292 parsing.messages().Emit(llvm::errs(), parsing.allCooked());
293 if (!parsing.consumedWholeFile()) {
294 parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(),
295 "parser FAIL (final position)",
296 "error: ", llvm::raw_ostream::RED);
297 return mlir::failure();
298 }
299 if ((!parsing.messages().empty() && (parsing.messages().AnyFatalError())) ||
300 !parsing.parseTree().has_value()) {
301 llvm::errs() << programPrefix << "could not parse " << path << '\n';
302 return mlir::failure();
303 }
304
305 // run semantics
306 auto &parseTree = *parsing.parseTree();
307 Fortran::semantics::Semantics semantics(semanticsContext, parseTree);
308 semantics.Perform();
309 semantics.EmitMessages(llvm::errs());
310 if (semantics.AnyFatalError()) {
311 llvm::errs() << programPrefix << "semantic errors in " << path << '\n';
312 return mlir::failure();
313 }
314 Fortran::semantics::RuntimeDerivedTypeTables tables;
315 if (!semantics.AnyFatalError()) {
316 tables =
317 Fortran::semantics::BuildRuntimeDerivedTypeTables(semanticsContext);
318 if (!tables.schemata)
319 llvm::errs() << programPrefix
320 << "could not find module file for __fortran_type_info\n";
321 }
322
323 if (dumpSymbols) {
324 semantics.DumpSymbols(llvm::outs());
325 return mlir::success();
326 }
327
328 if (pftDumpTest) {
329 if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext)) {
330 Fortran::lower::dumpPFT(llvm::outs(), *ast);
331 return mlir::success();
332 }
333 llvm::errs() << "Pre FIR Tree is NULL.\n";
334 return mlir::failure();
335 }
336
337 // translate to FIR dialect of MLIR
338 mlir::DialectRegistry registry;
339 fir::support::registerNonCodegenDialects(registry);
340 fir::support::addFIRExtensions(registry);
341 mlir::MLIRContext ctx(registry);
342 fir::support::loadNonCodegenDialects(ctx);
343 auto &defKinds = semanticsContext.defaultKinds();
344 fir::KindMapping kindMap(
345 &ctx, llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
346 std::string targetTriple = targetMachine.getTargetTriple().normalize();
347 // Use default lowering options for bbc.
348 Fortran::lower::LoweringOptions loweringOptions{};
349 loweringOptions.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder);
350 loweringOptions.setLowerToHighLevelFIR(useHLFIR || emitHLFIR);
351 std::vector<Fortran::lower::EnvironmentDefault> envDefaults = {};
352 auto burnside = Fortran::lower::LoweringBridge::create(
353 ctx, semanticsContext, defKinds, semanticsContext.intrinsics(),
354 semanticsContext.targetCharacteristics(), parsing.allCooked(),
355 targetTriple, kindMap, loweringOptions, envDefaults,
356 semanticsContext.languageFeatures(), targetMachine);
357 mlir::ModuleOp mlirModule = burnside.getModule();
358 if (enableOpenMP) {
359 if (enableOpenMPGPU && !enableOpenMPDevice) {
360 llvm::errs() << "FATAL: -fopenmp-is-gpu can only be set if "
361 "-fopenmp-is-target-device is also set";
362 return mlir::failure();
363 }
364 auto offloadModuleOpts =
365 OffloadModuleOpts(setOpenMPTargetDebug, setOpenMPTeamSubscription,
366 setOpenMPThreadSubscription, setOpenMPNoThreadState,
367 setOpenMPNoNestedParallelism, enableOpenMPDevice,
368 enableOpenMPGPU, setOpenMPVersion, "", setNoGPULib);
369 setOffloadModuleInterfaceAttributes(mlirModule, offloadModuleOpts);
370 setOpenMPVersionAttribute(mlirModule, setOpenMPVersion);
371 }
372 burnside.lower(parseTree, semanticsContext);
373 std::error_code ec;
374 std::string outputName = outputFilename;
375 if (!outputName.size())
376 outputName = llvm::sys::path::stem(path: inputFilename).str().append(s: ".mlir");
377 llvm::raw_fd_ostream out(outputName, ec);
378 if (ec)
379 return mlir::emitError(mlir::UnknownLoc::get(&ctx),
380 "could not open output file ")
381 << outputName;
382
383 // WARNING: This pipeline must be run immediately after the lowering to
384 // ensure that the FIR is correct with respect to OpenMP operations/
385 // attributes.
386 if (enableOpenMP)
387 if (mlir::failed(runOpenMPPasses(mlirModule)))
388 return mlir::failure();
389
390 // Otherwise run the default passes.
391 mlir::PassManager pm(mlirModule->getName(),
392 mlir::OpPassManager::Nesting::Implicit);
393 pm.enableVerifier(/*verifyPasses=*/true);
394 (void)mlir::applyPassManagerCLOptions(pm);
395 if (passPipeline.hasAnyOccurrences()) {
396 // run the command-line specified pipeline
397 hlfir::registerHLFIRPasses();
398 (void)passPipeline.addToPipeline(pm, [&](const llvm::Twine &msg) {
399 mlir::emitError(mlir::UnknownLoc::get(&ctx)) << msg;
400 return mlir::failure();
401 });
402 } else if (emitFIR || emitHLFIR) {
403 // --emit-fir: Build the IR, verify it, and dump the IR if the IR passes
404 // verification. Use --dump-module-on-failure to dump invalid IR.
405 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
406 if (mlir::failed(pm.run(mlirModule))) {
407 llvm::errs() << "FATAL: verification of lowering to FIR failed";
408 return mlir::failure();
409 }
410
411 if (emitFIR && useHLFIR) {
412 // lower HLFIR to FIR
413 fir::createHLFIRToFIRPassPipeline(pm, llvm::OptimizationLevel::O2);
414 if (mlir::failed(pm.run(mlirModule))) {
415 llvm::errs() << "FATAL: lowering from HLFIR to FIR failed";
416 return mlir::failure();
417 }
418 }
419
420 printModule(mlirModule, out);
421 return mlir::success();
422 } else {
423 // run the default canned pipeline
424 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
425
426 // Add O2 optimizer pass pipeline.
427 fir::createDefaultFIROptimizerPassPipeline(
428 pm, MLIRToLLVMPassPipelineConfig(llvm::OptimizationLevel::O2));
429 }
430
431 if (mlir::succeeded(pm.run(mlirModule))) {
432 // Emit MLIR and do not lower to LLVM IR.
433 printModule(mlirModule, out);
434 return mlir::success();
435 }
436 // Something went wrong. Try to dump the MLIR module.
437 llvm::errs() << "oops, pass manager reported failure\n";
438 return mlir::failure();
439}
440
441int main(int argc, char **argv) {
442 [[maybe_unused]] llvm::InitLLVM y(argc, argv);
443 llvm::InitializeAllTargets();
444 llvm::InitializeAllTargetMCs();
445 registerAllPasses();
446
447 mlir::registerMLIRContextCLOptions();
448 mlir::registerAsmPrinterCLOptions();
449 mlir::registerPassManagerCLOptions();
450 mlir::PassPipelineCLParser passPipe("", "Compiler passes to run");
451 llvm::cl::ParseCommandLineOptions(argc, argv, Overview: "Burnside Bridge Compiler\n");
452
453 ProgramName programPrefix;
454 programPrefix = argv[0] + ": "s;
455
456 if (includeDirs.size() == 0) {
457 includeDirs.push_back(value: ".");
458 // Default Fortran modules should be installed in include/flang (a sibling
459 // to the bin) directory.
460 intrinsicIncludeDirs.push_back(
461 value: llvm::sys::path::parent_path(
462 path: llvm::sys::path::parent_path(
463 path: llvm::sys::fs::getMainExecutable(argv0: argv[0], MainExecAddr: nullptr)))
464 .str() +
465 "/include/flang");
466 }
467
468 Fortran::parser::Options options;
469 options.predefinitions.emplace_back("__flang__"s, "1"s);
470 options.predefinitions.emplace_back("__flang_major__"s,
471 std::string{FLANG_VERSION_MAJOR_STRING});
472 options.predefinitions.emplace_back("__flang_minor__"s,
473 std::string{FLANG_VERSION_MINOR_STRING});
474 options.predefinitions.emplace_back(
475 "__flang_patchlevel__"s, std::string{FLANG_VERSION_PATCHLEVEL_STRING});
476
477 // enable parsing of OpenMP
478 if (enableOpenMP) {
479 options.features.Enable(Fortran::common::LanguageFeature::OpenMP);
480 Fortran::common::setOpenMPMacro(setOpenMPVersion, options.predefinitions);
481 }
482
483 // enable parsing of OpenACC
484 if (enableOpenACC) {
485 options.features.Enable(Fortran::common::LanguageFeature::OpenACC);
486 options.predefinitions.emplace_back("_OPENACC", "202211");
487 }
488
489 // enable parsing of CUDA Fortran
490 if (enableCUDA) {
491 options.features.Enable(Fortran::common::LanguageFeature::CUDA);
492 }
493
494 if (fixedForm) {
495 options.isFixedForm = fixedForm;
496 }
497
498 Fortran::common::IntrinsicTypeDefaultKinds defaultKinds;
499 Fortran::parser::AllSources allSources;
500 Fortran::parser::AllCookedSources allCookedSources(allSources);
501 Fortran::semantics::SemanticsContext semanticsContext{
502 defaultKinds, options.features, allCookedSources};
503 semanticsContext.set_moduleDirectory(moduleDir)
504 .set_moduleFileSuffix(moduleSuffix)
505 .set_searchDirectories(includeDirs)
506 .set_intrinsicModuleDirectories(intrinsicIncludeDirs)
507 .set_warnOnNonstandardUsage(warnStdViolation)
508 .set_warningsAreErrors(warnIsError);
509
510 std::string error;
511 // Create host target machine.
512 std::unique_ptr<llvm::TargetMachine> targetMachine =
513 createTargetMachine(targetTriple: targetTripleOverride, error);
514 if (!targetMachine) {
515 llvm::errs() << "failed to create target machine: " << error << "\n";
516 return mlir::failed(mlir::failure());
517 }
518 std::string compilerVersion = Fortran::common::getFlangToolFullVersion("bbc");
519 std::string compilerOptions = "";
520 Fortran::tools::setUpTargetCharacteristics(
521 semanticsContext.targetCharacteristics(), *targetMachine, compilerVersion,
522 compilerOptions);
523
524 return mlir::failed(
525 convertFortranSourceToMLIR(inputFilename, options, programPrefix,
526 semanticsContext, passPipe, *targetMachine));
527}
528

source code of flang/tools/bbc/bbc.cpp