1 //===- bbc.cpp - Burnside Bridge Compiler -----------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
13 /// This is a tool for translating Fortran sources to the FIR dialect of MLIR.
15 //===----------------------------------------------------------------------===//
17 #include "flang/Common/Fortran-features.h"
18 #include "flang/Common/OpenMP-features.h"
19 #include "flang/Common/default-kinds.h"
20 #include "flang/Lower/Bridge.h"
21 #include "flang/Lower/PFTBuilder.h"
22 #include "flang/Lower/Support/Verifier.h"
23 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
24 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
25 #include "flang/Optimizer/Support/InitFIR.h"
26 #include "flang/Optimizer/Support/InternalNames.h"
27 #include "flang/Optimizer/Support/Utils.h"
28 #include "flang/Optimizer/Transforms/Passes.h"
29 #include "flang/Parser/characters.h"
30 #include "flang/Parser/dump-parse-tree.h"
31 #include "flang/Parser/message.h"
32 #include "flang/Parser/parse-tree-visitor.h"
33 #include "flang/Parser/parse-tree.h"
34 #include "flang/Parser/parsing.h"
35 #include "flang/Parser/provenance.h"
36 #include "flang/Parser/unparse.h"
37 #include "flang/Semantics/expression.h"
38 #include "flang/Semantics/runtime-type-info.h"
39 #include "flang/Semantics/semantics.h"
40 #include "flang/Semantics/unparse-with-symbols.h"
41 #include "flang/Tools/CrossToolHelpers.h"
42 #include "flang/Version.inc"
43 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
44 #include "mlir/IR/AsmState.h"
45 #include "mlir/IR/BuiltinOps.h"
46 #include "mlir/IR/MLIRContext.h"
47 #include "mlir/Parser/Parser.h"
48 #include "mlir/Pass/Pass.h"
49 #include "mlir/Pass/PassManager.h"
50 #include "mlir/Pass/PassRegistry.h"
51 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
52 #include "mlir/Transforms/Passes.h"
53 #include "llvm/Passes/OptimizationLevel.h"
54 #include "llvm/Support/CommandLine.h"
55 #include "llvm/Support/ErrorOr.h"
56 #include "llvm/Support/FileSystem.h"
57 #include "llvm/Support/InitLLVM.h"
58 #include "llvm/Support/MemoryBuffer.h"
59 #include "llvm/Support/Path.h"
60 #include "llvm/Support/SourceMgr.h"
61 #include "llvm/Support/TargetSelect.h"
62 #include "llvm/Support/ToolOutputFile.h"
63 #include "llvm/Support/raw_ostream.h"
64 #include "llvm/TargetParser/Host.h"
65 #include "llvm/TargetParser/Triple.h"
67 //===----------------------------------------------------------------------===//
68 // Some basic command-line options
69 //===----------------------------------------------------------------------===//
71 static llvm::cl::opt
<std::string
> inputFilename(llvm::cl::Positional
,
73 llvm::cl::desc("<input file>"));
75 static llvm::cl::opt
<std::string
>
76 outputFilename("o", llvm::cl::desc("Specify the output filename"),
77 llvm::cl::value_desc("filename"));
79 static llvm::cl::list
<std::string
>
80 includeDirs("I", llvm::cl::desc("include module search paths"));
82 static llvm::cl::alias
includeAlias("module-directory",
83 llvm::cl::desc("module search directory"),
84 llvm::cl::aliasopt(includeDirs
));
86 static llvm::cl::list
<std::string
>
87 intrinsicIncludeDirs("J", llvm::cl::desc("intrinsic module search paths"));
89 static llvm::cl::alias
90 intrinsicIncludeAlias("intrinsic-module-directory",
91 llvm::cl::desc("intrinsic module directory"),
92 llvm::cl::aliasopt(intrinsicIncludeDirs
));
94 static llvm::cl::opt
<std::string
>
95 moduleDir("module", llvm::cl::desc("module output directory (default .)"),
98 static llvm::cl::opt
<std::string
>
99 moduleSuffix("module-suffix", llvm::cl::desc("module file suffix override"),
100 llvm::cl::init(".mod"));
102 static llvm::cl::opt
<bool>
104 llvm::cl::desc("Dump the FIR created by lowering and exit"),
105 llvm::cl::init(false));
107 static llvm::cl::opt
<bool>
108 emitHLFIR("emit-hlfir",
109 llvm::cl::desc("Dump the HLFIR created by lowering and exit"),
110 llvm::cl::init(false));
112 static llvm::cl::opt
<bool> warnStdViolation("Mstandard",
113 llvm::cl::desc("emit warnings"),
114 llvm::cl::init(false));
116 static llvm::cl::opt
<bool> warnIsError("Werror",
117 llvm::cl::desc("warnings are errors"),
118 llvm::cl::init(false));
120 static llvm::cl::opt
<bool> dumpSymbols("dump-symbols",
121 llvm::cl::desc("dump the symbol table"),
122 llvm::cl::init(false));
124 static llvm::cl::opt
<bool> pftDumpTest(
126 llvm::cl::desc("parse the input, create a PFT, dump it, and exit"),
127 llvm::cl::init(false));
129 static llvm::cl::opt
<bool> enableOpenMP("fopenmp",
130 llvm::cl::desc("enable openmp"),
131 llvm::cl::init(false));
133 static llvm::cl::opt
<bool>
134 enableOpenMPDevice("fopenmp-is-target-device",
135 llvm::cl::desc("enable openmp device compilation"),
136 llvm::cl::init(false));
138 static llvm::cl::opt
<bool>
139 enableOpenMPGPU("fopenmp-is-gpu",
140 llvm::cl::desc("enable openmp GPU target codegen"),
141 llvm::cl::init(false));
143 // A simplified subset of the OpenMP RTL Flags from Flang, only the primary
144 // positive options are available, no negative options e.g. fopen_assume* vs
146 static llvm::cl::opt
<uint32_t>
147 setOpenMPVersion("fopenmp-version",
148 llvm::cl::desc("OpenMP standard version"),
151 static llvm::cl::opt
<uint32_t> setOpenMPTargetDebug(
152 "fopenmp-target-debug",
153 llvm::cl::desc("Enable debugging in the OpenMP offloading device RTL"),
156 static llvm::cl::opt
<bool> setOpenMPThreadSubscription(
157 "fopenmp-assume-threads-oversubscription",
158 llvm::cl::desc("Assume work-shared loops do not have more "
159 "iterations than participating threads."),
160 llvm::cl::init(false));
162 static llvm::cl::opt
<bool> setOpenMPTeamSubscription(
163 "fopenmp-assume-teams-oversubscription",
164 llvm::cl::desc("Assume distributed loops do not have more iterations than "
165 "participating teams."),
166 llvm::cl::init(false));
168 static llvm::cl::opt
<bool> setOpenMPNoThreadState(
169 "fopenmp-assume-no-thread-state",
171 "Assume that no thread in a parallel region will modify an ICV."),
172 llvm::cl::init(false));
174 static llvm::cl::opt
<bool> setOpenMPNoNestedParallelism(
175 "fopenmp-assume-no-nested-parallelism",
176 llvm::cl::desc("Assume that no thread in a parallel region will encounter "
177 "a parallel region."),
178 llvm::cl::init(false));
180 static llvm::cl::opt
<bool> enableOpenACC("fopenacc",
181 llvm::cl::desc("enable openacc"),
182 llvm::cl::init(false));
184 static llvm::cl::opt
<bool> enablePolymorphic(
186 llvm::cl::desc("enable polymorphic type lowering (experimental)"),
187 llvm::cl::init(false));
189 static llvm::cl::opt
<bool> enableNoPPCNativeVecElemOrder(
190 "fno-ppc-native-vector-element-order",
191 llvm::cl::desc("no PowerPC native vector element order."),
192 llvm::cl::init(false));
194 static llvm::cl::opt
<bool> useHLFIR("hlfir",
195 llvm::cl::desc("Lower to high level FIR"),
196 llvm::cl::init(false));
198 static llvm::cl::opt
<bool> enableCUDA("fcuda",
199 llvm::cl::desc("enable CUDA Fortran"),
200 llvm::cl::init(false));
202 static llvm::cl::opt
<bool> fixedForm("ffixed-form",
203 llvm::cl::desc("enable fixed form"),
204 llvm::cl::init(false));
206 #define FLANG_EXCLUDE_CODEGEN
207 #include "flang/Tools/CLOptions.inc"
209 //===----------------------------------------------------------------------===//
211 using ProgramName
= std::string
;
213 // Print the module with the "module { ... }" wrapper, preventing
214 // information loss from attribute information appended to the module
215 static void printModule(mlir::ModuleOp mlirModule
, llvm::raw_ostream
&out
) {
216 out
<< mlirModule
<< '\n';
219 static void registerAllPasses() {
220 fir::support::registerMLIRPassesForFortranTools();
221 fir::registerOptTransformPasses();
224 //===----------------------------------------------------------------------===//
225 // Translate Fortran input to FIR, a dialect of MLIR.
226 //===----------------------------------------------------------------------===//
228 static mlir::LogicalResult
convertFortranSourceToMLIR(
229 std::string path
, Fortran::parser::Options options
,
230 const ProgramName
&programPrefix
,
231 Fortran::semantics::SemanticsContext
&semanticsContext
,
232 const mlir::PassPipelineCLParser
&passPipeline
) {
234 // prep for prescan and parse
235 Fortran::parser::Parsing parsing
{semanticsContext
.allCookedSources()};
236 parsing
.Prescan(path
, options
);
237 if (!parsing
.messages().empty() && (parsing
.messages().AnyFatalError())) {
238 llvm::errs() << programPrefix
<< "could not scan " << path
<< '\n';
239 parsing
.messages().Emit(llvm::errs(), parsing
.allCooked());
240 return mlir::failure();
243 // parse the input Fortran
244 parsing
.Parse(llvm::outs());
245 parsing
.messages().Emit(llvm::errs(), parsing
.allCooked());
246 if (!parsing
.consumedWholeFile()) {
247 parsing
.EmitMessage(llvm::errs(), parsing
.finalRestingPlace(),
248 "parser FAIL (final position)",
249 "error: ", llvm::raw_ostream::RED
);
250 return mlir::failure();
252 if ((!parsing
.messages().empty() && (parsing
.messages().AnyFatalError())) ||
253 !parsing
.parseTree().has_value()) {
254 llvm::errs() << programPrefix
<< "could not parse " << path
<< '\n';
255 return mlir::failure();
259 auto &parseTree
= *parsing
.parseTree();
260 Fortran::semantics::Semantics
semantics(semanticsContext
, parseTree
);
262 semantics
.EmitMessages(llvm::errs());
263 if (semantics
.AnyFatalError()) {
264 llvm::errs() << programPrefix
<< "semantic errors in " << path
<< '\n';
265 return mlir::failure();
267 Fortran::semantics::RuntimeDerivedTypeTables tables
;
268 if (!semantics
.AnyFatalError()) {
270 Fortran::semantics::BuildRuntimeDerivedTypeTables(semanticsContext
);
271 if (!tables
.schemata
)
272 llvm::errs() << programPrefix
273 << "could not find module file for __fortran_type_info\n";
277 semantics
.DumpSymbols(llvm::outs());
278 return mlir::success();
282 if (auto ast
= Fortran::lower::createPFT(parseTree
, semanticsContext
)) {
283 Fortran::lower::dumpPFT(llvm::outs(), *ast
);
284 return mlir::success();
286 llvm::errs() << "Pre FIR Tree is NULL.\n";
287 return mlir::failure();
290 // translate to FIR dialect of MLIR
291 mlir::DialectRegistry registry
;
292 fir::support::registerNonCodegenDialects(registry
);
293 mlir::MLIRContext
ctx(registry
);
294 fir::support::loadNonCodegenDialects(ctx
);
295 auto &defKinds
= semanticsContext
.defaultKinds();
296 fir::KindMapping
kindMap(
297 &ctx
, llvm::ArrayRef
<fir::KindTy
>{fir::fromDefaultKinds(defKinds
)});
298 // Use default lowering options for bbc.
299 Fortran::lower::LoweringOptions loweringOptions
{};
300 loweringOptions
.setPolymorphicTypeImpl(enablePolymorphic
);
301 loweringOptions
.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder
);
302 loweringOptions
.setLowerToHighLevelFIR(useHLFIR
|| emitHLFIR
);
303 auto burnside
= Fortran::lower::LoweringBridge::create(
304 ctx
, semanticsContext
, defKinds
, semanticsContext
.intrinsics(),
305 semanticsContext
.targetCharacteristics(), parsing
.allCooked(), "",
306 kindMap
, loweringOptions
, {});
307 burnside
.lower(parseTree
, semanticsContext
);
308 mlir::ModuleOp mlirModule
= burnside
.getModule();
310 if (enableOpenMPGPU
&& !enableOpenMPDevice
) {
311 llvm::errs() << "FATAL: -fopenmp-is-gpu can only be set if "
312 "-fopenmp-is-target-device is also set";
313 return mlir::failure();
315 auto offloadModuleOpts
=
316 OffloadModuleOpts(setOpenMPTargetDebug
, setOpenMPTeamSubscription
,
317 setOpenMPThreadSubscription
, setOpenMPNoThreadState
,
318 setOpenMPNoNestedParallelism
, enableOpenMPDevice
,
319 enableOpenMPGPU
, setOpenMPVersion
);
320 setOffloadModuleInterfaceAttributes(mlirModule
, offloadModuleOpts
);
321 setOpenMPVersionAttribute(mlirModule
, setOpenMPVersion
);
324 std::string outputName
= outputFilename
;
325 if (!outputName
.size())
326 outputName
= llvm::sys::path::stem(inputFilename
).str().append(".mlir");
327 llvm::raw_fd_ostream
out(outputName
, ec
);
329 return mlir::emitError(mlir::UnknownLoc::get(&ctx
),
330 "could not open output file ")
333 // Otherwise run the default passes.
334 mlir::PassManager
pm(mlirModule
->getName(),
335 mlir::OpPassManager::Nesting::Implicit
);
337 // WARNING: This pipeline must be run immediately after the lowering to
338 // ensure that the FIR is correct with respect to OpenMP operations/
340 fir::createOpenMPFIRPassPipeline(pm
, enableOpenMPDevice
);
341 pm
.enableVerifier(/*verifyPasses=*/true);
342 (void)mlir::applyPassManagerCLOptions(pm
);
343 if (passPipeline
.hasAnyOccurrences()) {
344 // run the command-line specified pipeline
345 hlfir::registerHLFIRPasses();
346 (void)passPipeline
.addToPipeline(pm
, [&](const llvm::Twine
&msg
) {
347 mlir::emitError(mlir::UnknownLoc::get(&ctx
)) << msg
;
348 return mlir::failure();
350 } else if (emitFIR
|| emitHLFIR
) {
351 // --emit-fir: Build the IR, verify it, and dump the IR if the IR passes
352 // verification. Use --dump-module-on-failure to dump invalid IR.
353 pm
.addPass(std::make_unique
<Fortran::lower::VerifierPass
>());
354 if (mlir::failed(pm
.run(mlirModule
))) {
355 llvm::errs() << "FATAL: verification of lowering to FIR failed";
356 return mlir::failure();
359 if (emitFIR
&& useHLFIR
) {
360 // lower HLFIR to FIR
361 fir::createHLFIRToFIRPassPipeline(pm
, llvm::OptimizationLevel::O2
);
362 if (mlir::failed(pm
.run(mlirModule
))) {
363 llvm::errs() << "FATAL: lowering from HLFIR to FIR failed";
364 return mlir::failure();
368 printModule(mlirModule
, out
);
369 return mlir::success();
371 // run the default canned pipeline
372 pm
.addPass(std::make_unique
<Fortran::lower::VerifierPass
>());
374 // Add O2 optimizer pass pipeline.
375 fir::createDefaultFIROptimizerPassPipeline(
376 pm
, MLIRToLLVMPassPipelineConfig(llvm::OptimizationLevel::O2
));
379 if (mlir::succeeded(pm
.run(mlirModule
))) {
380 // Emit MLIR and do not lower to LLVM IR.
381 printModule(mlirModule
, out
);
382 return mlir::success();
384 // Something went wrong. Try to dump the MLIR module.
385 llvm::errs() << "oops, pass manager reported failure\n";
386 return mlir::failure();
389 int main(int argc
, char **argv
) {
390 [[maybe_unused
]] llvm::InitLLVM
y(argc
, argv
);
393 mlir::registerMLIRContextCLOptions();
394 mlir::registerAsmPrinterCLOptions();
395 mlir::registerPassManagerCLOptions();
396 mlir::PassPipelineCLParser
passPipe("", "Compiler passes to run");
397 llvm::cl::ParseCommandLineOptions(argc
, argv
, "Burnside Bridge Compiler\n");
399 ProgramName programPrefix
;
400 programPrefix
= argv
[0] + ": "s
;
402 if (includeDirs
.size() == 0) {
403 includeDirs
.push_back(".");
404 // Default Fortran modules should be installed in include/flang (a sibling
405 // to the bin) directory.
406 intrinsicIncludeDirs
.push_back(
407 llvm::sys::path::parent_path(
408 llvm::sys::path::parent_path(
409 llvm::sys::fs::getMainExecutable(argv
[0], nullptr)))
414 Fortran::parser::Options options
;
415 options
.predefinitions
.emplace_back("__flang__"s
, "1"s
);
416 options
.predefinitions
.emplace_back("__flang_major__"s
,
417 std::string
{FLANG_VERSION_MAJOR_STRING
});
418 options
.predefinitions
.emplace_back("__flang_minor__"s
,
419 std::string
{FLANG_VERSION_MINOR_STRING
});
420 options
.predefinitions
.emplace_back(
421 "__flang_patchlevel__"s
, std::string
{FLANG_VERSION_PATCHLEVEL_STRING
});
423 // enable parsing of OpenMP
425 options
.features
.Enable(Fortran::common::LanguageFeature::OpenMP
);
426 Fortran::common::setOpenMPMacro(setOpenMPVersion
, options
.predefinitions
);
429 // enable parsing of OpenACC
431 options
.features
.Enable(Fortran::common::LanguageFeature::OpenACC
);
432 options
.predefinitions
.emplace_back("_OPENACC", "202211");
435 // enable parsing of CUDA Fortran
437 options
.features
.Enable(Fortran::common::LanguageFeature::CUDA
);
441 options
.isFixedForm
= fixedForm
;
444 Fortran::common::IntrinsicTypeDefaultKinds defaultKinds
;
445 Fortran::parser::AllSources allSources
;
446 Fortran::parser::AllCookedSources
allCookedSources(allSources
);
447 Fortran::semantics::SemanticsContext semanticsContext
{
448 defaultKinds
, options
.features
, allCookedSources
};
449 semanticsContext
.set_moduleDirectory(moduleDir
)
450 .set_moduleFileSuffix(moduleSuffix
)
451 .set_searchDirectories(includeDirs
)
452 .set_intrinsicModuleDirectories(intrinsicIncludeDirs
)
453 .set_warnOnNonstandardUsage(warnStdViolation
)
454 .set_warningsAreErrors(warnIsError
);
456 llvm::Triple targetTriple
{llvm::Triple(
457 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()))};
458 // FIXME: Handle real(3) ?
459 if (targetTriple
.getArch() != llvm::Triple::ArchType::x86
&&
460 targetTriple
.getArch() != llvm::Triple::ArchType::x86_64
) {
461 semanticsContext
.targetCharacteristics().DisableType(
462 Fortran::common::TypeCategory::Real
, /*kind=*/10);
464 if (targetTriple
.isPPC())
465 semanticsContext
.targetCharacteristics().set_isPPC(true);
467 return mlir::failed(convertFortranSourceToMLIR(
468 inputFilename
, options
, programPrefix
, semanticsContext
, passPipe
));