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/Frontend/CodeGenOptions.h" |
18 | #include "flang/Frontend/TargetOptions.h" |
19 | #include "flang/Lower/Bridge.h" |
20 | #include "flang/Lower/PFTBuilder.h" |
21 | #include "flang/Lower/Support/Verifier.h" |
22 | #include "flang/Optimizer/Dialect/Support/FIRContext.h" |
23 | #include "flang/Optimizer/Dialect/Support/KindMapping.h" |
24 | #include "flang/Optimizer/Support/InitFIR.h" |
25 | #include "flang/Optimizer/Support/InternalNames.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/Support/Fortran-features.h" |
41 | #include "flang/Support/LangOptions.h" |
42 | #include "flang/Support/OpenMP-features.h" |
43 | #include "flang/Support/Version.h" |
44 | #include "flang/Support/default-kinds.h" |
45 | #include "flang/Tools/CrossToolHelpers.h" |
46 | #include "flang/Tools/TargetSetup.h" |
47 | #include "flang/Version.inc" |
48 | #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
49 | #include "mlir/IR/AsmState.h" |
50 | #include "mlir/IR/BuiltinOps.h" |
51 | #include "mlir/IR/MLIRContext.h" |
52 | #include "mlir/Parser/Parser.h" |
53 | #include "mlir/Pass/Pass.h" |
54 | #include "mlir/Pass/PassManager.h" |
55 | #include "mlir/Pass/PassRegistry.h" |
56 | #include "mlir/Transforms/GreedyPatternRewriteDriver.h" |
57 | #include "mlir/Transforms/Passes.h" |
58 | #include "llvm/MC/TargetRegistry.h" |
59 | #include "llvm/Passes/OptimizationLevel.h" |
60 | #include "llvm/Support/CommandLine.h" |
61 | #include "llvm/Support/ErrorOr.h" |
62 | #include "llvm/Support/FileSystem.h" |
63 | #include "llvm/Support/InitLLVM.h" |
64 | #include "llvm/Support/MemoryBuffer.h" |
65 | #include "llvm/Support/Path.h" |
66 | #include "llvm/Support/SourceMgr.h" |
67 | #include "llvm/Support/TargetSelect.h" |
68 | #include "llvm/Support/ToolOutputFile.h" |
69 | #include "llvm/Support/raw_ostream.h" |
70 | #include "llvm/TargetParser/Host.h" |
71 | #include "llvm/TargetParser/Triple.h" |
72 | #include <memory> |
73 | |
74 | //===----------------------------------------------------------------------===// |
75 | // Some basic command-line options |
76 | //===----------------------------------------------------------------------===// |
77 | |
78 | static llvm::cl::opt<std::string> inputFilename(llvm::cl::Positional, |
79 | llvm::cl::Required, |
80 | llvm::cl::desc("<input file>" )); |
81 | |
82 | static llvm::cl::opt<std::string> |
83 | outputFilename("o" , llvm::cl::desc("Specify the output filename" ), |
84 | llvm::cl::value_desc("filename" )); |
85 | |
86 | static llvm::cl::list<std::string> |
87 | includeDirs("I" , llvm::cl::desc("include module search paths" )); |
88 | |
89 | static llvm::cl::alias includeAlias("module-directory" , |
90 | llvm::cl::desc("module search directory" ), |
91 | llvm::cl::aliasopt(includeDirs)); |
92 | |
93 | static llvm::cl::list<std::string> |
94 | intrinsicIncludeDirs("J" , llvm::cl::desc("intrinsic module search paths" )); |
95 | |
96 | static llvm::cl::alias |
97 | intrinsicIncludeAlias("intrinsic-module-directory" , |
98 | llvm::cl::desc("intrinsic module directory" ), |
99 | llvm::cl::aliasopt(intrinsicIncludeDirs)); |
100 | |
101 | static llvm::cl::opt<std::string> |
102 | moduleDir("module" , llvm::cl::desc("module output directory (default .)" ), |
103 | llvm::cl::init(Val: "." )); |
104 | |
105 | static llvm::cl::opt<std::string> |
106 | moduleSuffix("module-suffix" , llvm::cl::desc("module file suffix override" ), |
107 | llvm::cl::init(Val: ".mod" )); |
108 | |
109 | static llvm::cl::opt<bool> |
110 | emitFIR("emit-fir" , |
111 | llvm::cl::desc("Dump the FIR created by lowering and exit" ), |
112 | llvm::cl::init(Val: false)); |
113 | |
114 | static llvm::cl::opt<bool> |
115 | emitHLFIR("emit-hlfir" , |
116 | llvm::cl::desc("Dump the HLFIR created by lowering and exit" ), |
117 | llvm::cl::init(Val: false)); |
118 | |
119 | static llvm::cl::opt<bool> warnStdViolation("Mstandard" , |
120 | llvm::cl::desc("emit warnings" ), |
121 | llvm::cl::init(Val: false)); |
122 | |
123 | static llvm::cl::opt<bool> warnIsError("Werror" , |
124 | llvm::cl::desc("warnings are errors" ), |
125 | llvm::cl::init(Val: false)); |
126 | |
127 | static llvm::cl::opt<bool> dumpSymbols("dump-symbols" , |
128 | llvm::cl::desc("dump the symbol table" ), |
129 | llvm::cl::init(Val: false)); |
130 | |
131 | static llvm::cl::opt<bool> pftDumpTest( |
132 | "pft-test" , |
133 | llvm::cl::desc("parse the input, create a PFT, dump it, and exit" ), |
134 | llvm::cl::init(Val: false)); |
135 | |
136 | static llvm::cl::opt<bool> enableOpenMP("fopenmp" , |
137 | llvm::cl::desc("enable openmp" ), |
138 | llvm::cl::init(Val: false)); |
139 | |
140 | static llvm::cl::opt<bool> |
141 | enableOpenMPDevice("fopenmp-is-target-device" , |
142 | llvm::cl::desc("enable openmp device compilation" ), |
143 | llvm::cl::init(Val: false)); |
144 | |
145 | static llvm::cl::opt<std::string> enableDoConcurrentToOpenMPConversion( |
146 | "fdo-concurrent-to-openmp" , |
147 | llvm::cl::desc( |
148 | "Try to map `do concurrent` loops to OpenMP [none|host|device]" ), |
149 | llvm::cl::init(Val: "none" )); |
150 | |
151 | static llvm::cl::opt<bool> |
152 | enableOpenMPGPU("fopenmp-is-gpu" , |
153 | llvm::cl::desc("enable openmp GPU target codegen" ), |
154 | llvm::cl::init(Val: false)); |
155 | |
156 | static llvm::cl::opt<bool> enableOpenMPForceUSM( |
157 | "fopenmp-force-usm" , |
158 | llvm::cl::desc("force openmp unified shared memory mode" ), |
159 | llvm::cl::init(Val: false)); |
160 | |
161 | static llvm::cl::list<std::string> targetTriplesOpenMP( |
162 | "fopenmp-targets" , |
163 | llvm::cl::desc("comma-separated list of OpenMP offloading triples" ), |
164 | llvm::cl::CommaSeparated); |
165 | |
166 | // A simplified subset of the OpenMP RTL Flags from Flang, only the primary |
167 | // positive options are available, no negative options e.g. fopen_assume* vs |
168 | // fno_open_assume* |
169 | static llvm::cl::opt<uint32_t> |
170 | setOpenMPVersion("fopenmp-version" , |
171 | llvm::cl::desc("OpenMP standard version" ), |
172 | llvm::cl::init(Val: 31)); |
173 | |
174 | static llvm::cl::opt<uint32_t> setOpenMPTargetDebug( |
175 | "fopenmp-target-debug" , |
176 | llvm::cl::desc("Enable debugging in the OpenMP offloading device RTL" ), |
177 | llvm::cl::init(Val: 0)); |
178 | |
179 | static llvm::cl::opt<bool> setOpenMPThreadSubscription( |
180 | "fopenmp-assume-threads-oversubscription" , |
181 | llvm::cl::desc("Assume work-shared loops do not have more " |
182 | "iterations than participating threads." ), |
183 | llvm::cl::init(Val: false)); |
184 | |
185 | static llvm::cl::opt<bool> setOpenMPTeamSubscription( |
186 | "fopenmp-assume-teams-oversubscription" , |
187 | llvm::cl::desc("Assume distributed loops do not have more iterations than " |
188 | "participating teams." ), |
189 | llvm::cl::init(Val: false)); |
190 | |
191 | static llvm::cl::opt<bool> setOpenMPNoThreadState( |
192 | "fopenmp-assume-no-thread-state" , |
193 | llvm::cl::desc( |
194 | "Assume that no thread in a parallel region will modify an ICV." ), |
195 | llvm::cl::init(Val: false)); |
196 | |
197 | static llvm::cl::opt<bool> setOpenMPNoNestedParallelism( |
198 | "fopenmp-assume-no-nested-parallelism" , |
199 | llvm::cl::desc("Assume that no thread in a parallel region will encounter " |
200 | "a parallel region." ), |
201 | llvm::cl::init(Val: false)); |
202 | |
203 | static llvm::cl::opt<bool> |
204 | setNoGPULib("nogpulib" , |
205 | llvm::cl::desc("Do not link device library for CUDA/HIP device " |
206 | "compilation" ), |
207 | llvm::cl::init(Val: false)); |
208 | |
209 | static llvm::cl::opt<bool> enableOpenACC("fopenacc" , |
210 | llvm::cl::desc("enable openacc" ), |
211 | llvm::cl::init(Val: false)); |
212 | |
213 | static llvm::cl::opt<bool> enableNoPPCNativeVecElemOrder( |
214 | "fno-ppc-native-vector-element-order" , |
215 | llvm::cl::desc("no PowerPC native vector element order." ), |
216 | llvm::cl::init(Val: false)); |
217 | |
218 | static llvm::cl::opt<bool> useHLFIR("hlfir" , |
219 | llvm::cl::desc("Lower to high level FIR" ), |
220 | llvm::cl::init(Val: true)); |
221 | |
222 | static llvm::cl::opt<bool> enableCUDA("fcuda" , |
223 | llvm::cl::desc("enable CUDA Fortran" ), |
224 | llvm::cl::init(Val: false)); |
225 | |
226 | static llvm::cl::opt<bool> |
227 | disableCUDAWarpFunction("fcuda-disable-warp-function" , |
228 | llvm::cl::desc("Disable CUDA Warp Function" ), |
229 | llvm::cl::init(Val: false)); |
230 | |
231 | static llvm::cl::opt<std::string> |
232 | enableGPUMode("gpu" , llvm::cl::desc("Enable GPU Mode managed|unified" ), |
233 | llvm::cl::init(Val: "" )); |
234 | |
235 | static llvm::cl::opt<bool> fixedForm("ffixed-form" , |
236 | llvm::cl::desc("enable fixed form" ), |
237 | llvm::cl::init(Val: false)); |
238 | static llvm::cl::opt<std::string> |
239 | targetTripleOverride("target" , |
240 | llvm::cl::desc("Override host target triple" ), |
241 | llvm::cl::init(Val: "" )); |
242 | |
243 | static llvm::cl::opt<bool> integerWrapAround( |
244 | "fwrapv" , |
245 | llvm::cl::desc("Treat signed integer overflow as two's complement" ), |
246 | llvm::cl::init(Val: false)); |
247 | |
248 | static llvm::cl::opt<bool> initGlobalZero( |
249 | "finit-global-zero" , |
250 | llvm::cl::desc("Zero initialize globals without default initialization" ), |
251 | llvm::cl::init(Val: true)); |
252 | |
253 | static llvm::cl::opt<bool> |
254 | reallocateLHS("frealloc-lhs" , |
255 | llvm::cl::desc("Follow Fortran 2003 rules for (re)allocating " |
256 | "the LHS of the intrinsic assignment" ), |
257 | llvm::cl::init(Val: true)); |
258 | |
259 | static llvm::cl::opt<bool> stackRepackArrays( |
260 | "fstack-repack-arrays" , |
261 | llvm::cl::desc("Allocate temporary arrays for -frepack-arrays " |
262 | "in stack memory" ), |
263 | llvm::cl::init(Val: false)); |
264 | |
265 | static llvm::cl::opt<bool> |
266 | repackArrays("frepack-arrays" , |
267 | llvm::cl::desc("Pack non-contiguous assummed shape arrays " |
268 | "into contiguous memory" ), |
269 | llvm::cl::init(Val: false)); |
270 | |
271 | static llvm::cl::opt<bool> |
272 | repackArraysWhole("frepack-arrays-continuity-whole" , |
273 | llvm::cl::desc("Repack arrays that are non-contiguous " |
274 | "in any dimension. If set to false, " |
275 | "only the arrays non-contiguous in the " |
276 | "leading dimension will be repacked" ), |
277 | llvm::cl::init(Val: true)); |
278 | |
279 | #define FLANG_EXCLUDE_CODEGEN |
280 | #include "flang/Optimizer/Passes/CommandLineOpts.h" |
281 | #include "flang/Optimizer/Passes/Pipelines.h" |
282 | |
283 | //===----------------------------------------------------------------------===// |
284 | |
285 | using ProgramName = std::string; |
286 | |
287 | // Print the module with the "module { ... }" wrapper, preventing |
288 | // information loss from attribute information appended to the module |
289 | static void printModule(mlir::ModuleOp mlirModule, llvm::raw_ostream &out) { |
290 | out << mlirModule << '\n'; |
291 | } |
292 | |
293 | static void registerAllPasses() { |
294 | fir::support::registerMLIRPassesForFortranTools(); |
295 | fir::registerOptTransformPasses(); |
296 | } |
297 | |
298 | /// Create a target machine that is at least sufficient to get data-layout |
299 | /// information required by flang semantics and lowering. Note that it may not |
300 | /// contain all the CPU feature information to get optimized assembly generation |
301 | /// from LLVM IR. Drivers that needs to generate assembly from LLVM IR should |
302 | /// create a target machine according to their specific options. |
303 | static std::unique_ptr<llvm::TargetMachine> |
304 | createTargetMachine(llvm::StringRef targetTriple, std::string &error) { |
305 | std::string triple{targetTriple}; |
306 | if (triple.empty()) |
307 | triple = llvm::sys::getDefaultTargetTriple(); |
308 | |
309 | const llvm::Target *theTarget = |
310 | llvm::TargetRegistry::lookupTarget(TripleStr: triple, Error&: error); |
311 | if (!theTarget) |
312 | return nullptr; |
313 | return std::unique_ptr<llvm::TargetMachine>{ |
314 | theTarget->createTargetMachine(TT: llvm::Triple(triple), /*CPU=*/"" , |
315 | /*Features=*/"" , Options: llvm::TargetOptions(), |
316 | /*Reloc::Model=*/RM: std::nullopt)}; |
317 | } |
318 | |
319 | /// Build and execute the OpenMPFIRPassPipeline with its own instance |
320 | /// of the pass manager, allowing it to be invoked as soon as it's |
321 | /// required without impacting the main pass pipeline that may be invoked |
322 | /// more than once for verification. |
323 | static llvm::LogicalResult runOpenMPPasses(mlir::ModuleOp mlirModule) { |
324 | mlir::PassManager pm(mlirModule->getName(), |
325 | mlir::OpPassManager::Nesting::Implicit); |
326 | using DoConcurrentMappingKind = |
327 | Fortran::frontend::CodeGenOptions::DoConcurrentMappingKind; |
328 | |
329 | fir::OpenMPFIRPassPipelineOpts opts; |
330 | opts.isTargetDevice = enableOpenMPDevice; |
331 | opts.doConcurrentMappingKind = |
332 | llvm::StringSwitch<DoConcurrentMappingKind>( |
333 | enableDoConcurrentToOpenMPConversion) |
334 | .Case("host" , DoConcurrentMappingKind::DCMK_Host) |
335 | .Case("device" , DoConcurrentMappingKind::DCMK_Device) |
336 | .Default(DoConcurrentMappingKind::DCMK_None); |
337 | |
338 | fir::createOpenMPFIRPassPipeline(pm, opts); |
339 | (void)mlir::applyPassManagerCLOptions(pm); |
340 | if (mlir::failed(pm.run(mlirModule))) { |
341 | llvm::errs() << "FATAL: failed to correctly apply OpenMP pass pipeline" ; |
342 | return mlir::failure(); |
343 | } |
344 | return mlir::success(); |
345 | } |
346 | |
347 | //===----------------------------------------------------------------------===// |
348 | // Translate Fortran input to FIR, a dialect of MLIR. |
349 | //===----------------------------------------------------------------------===// |
350 | |
351 | static llvm::LogicalResult convertFortranSourceToMLIR( |
352 | std::string path, Fortran::parser::Options options, |
353 | const ProgramName &programPrefix, |
354 | Fortran::semantics::SemanticsContext &semanticsContext, |
355 | const mlir::PassPipelineCLParser &passPipeline, |
356 | const llvm::TargetMachine &targetMachine) { |
357 | |
358 | // prep for prescan and parse |
359 | Fortran::parser::Parsing parsing{semanticsContext.allCookedSources()}; |
360 | parsing.Prescan(path, options); |
361 | if (!parsing.messages().empty() && (parsing.messages().AnyFatalError())) { |
362 | llvm::errs() << programPrefix << "could not scan " << path << '\n'; |
363 | parsing.messages().Emit(llvm::errs(), parsing.allCooked()); |
364 | return mlir::failure(); |
365 | } |
366 | |
367 | // parse the input Fortran |
368 | parsing.Parse(llvm::outs()); |
369 | if (!parsing.consumedWholeFile()) { |
370 | parsing.messages().Emit(llvm::errs(), parsing.allCooked()); |
371 | parsing.EmitMessage(llvm::errs(), parsing.finalRestingPlace(), |
372 | "parser FAIL (final position)" , |
373 | "error: " , llvm::raw_ostream::RED); |
374 | return mlir::failure(); |
375 | } else if ((!parsing.messages().empty() && |
376 | (parsing.messages().AnyFatalError())) || |
377 | !parsing.parseTree().has_value()) { |
378 | parsing.messages().Emit(llvm::errs(), parsing.allCooked()); |
379 | llvm::errs() << programPrefix << "could not parse " << path << '\n'; |
380 | return mlir::failure(); |
381 | } else { |
382 | semanticsContext.messages().Annex(std::move(parsing.messages())); |
383 | } |
384 | |
385 | // run semantics |
386 | auto &parseTree = *parsing.parseTree(); |
387 | Fortran::semantics::Semantics semantics(semanticsContext, parseTree); |
388 | semantics.Perform(); |
389 | semantics.EmitMessages(llvm::errs()); |
390 | if (semantics.AnyFatalError()) { |
391 | llvm::errs() << programPrefix << "semantic errors in " << path << '\n'; |
392 | return mlir::failure(); |
393 | } |
394 | Fortran::semantics::RuntimeDerivedTypeTables tables; |
395 | if (!semantics.AnyFatalError()) { |
396 | tables = |
397 | Fortran::semantics::BuildRuntimeDerivedTypeTables(semanticsContext); |
398 | if (!tables.schemata) |
399 | llvm::errs() << programPrefix |
400 | << "could not find module file for __fortran_type_info\n" ; |
401 | } |
402 | |
403 | if (dumpSymbols) { |
404 | semantics.DumpSymbols(llvm::outs()); |
405 | return mlir::success(); |
406 | } |
407 | |
408 | if (pftDumpTest) { |
409 | if (auto ast = Fortran::lower::createPFT(parseTree, semanticsContext)) { |
410 | Fortran::lower::dumpPFT(llvm::outs(), *ast); |
411 | return mlir::success(); |
412 | } |
413 | llvm::errs() << "Pre FIR Tree is NULL.\n" ; |
414 | return mlir::failure(); |
415 | } |
416 | |
417 | // translate to FIR dialect of MLIR |
418 | mlir::DialectRegistry registry; |
419 | fir::support::registerNonCodegenDialects(registry); |
420 | fir::support::addFIRExtensions(registry); |
421 | mlir::MLIRContext ctx(registry); |
422 | fir::support::loadNonCodegenDialects(ctx); |
423 | auto &defKinds = semanticsContext.defaultKinds(); |
424 | fir::KindMapping kindMap( |
425 | &ctx, llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)}); |
426 | std::string targetTriple = targetMachine.getTargetTriple().normalize(); |
427 | // Use default lowering options for bbc. |
428 | Fortran::lower::LoweringOptions loweringOptions{}; |
429 | loweringOptions.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder); |
430 | loweringOptions.setLowerToHighLevelFIR(useHLFIR || emitHLFIR); |
431 | loweringOptions.setIntegerWrapAround(integerWrapAround); |
432 | loweringOptions.setInitGlobalZero(initGlobalZero); |
433 | loweringOptions.setReallocateLHS(reallocateLHS); |
434 | loweringOptions.setStackRepackArrays(stackRepackArrays); |
435 | loweringOptions.setRepackArrays(repackArrays); |
436 | loweringOptions.setRepackArraysWhole(repackArraysWhole); |
437 | std::vector<Fortran::lower::EnvironmentDefault> envDefaults = {}; |
438 | Fortran::frontend::TargetOptions targetOpts; |
439 | Fortran::frontend::CodeGenOptions cgOpts; |
440 | auto burnside = Fortran::lower::LoweringBridge::create( |
441 | ctx, semanticsContext, defKinds, semanticsContext.intrinsics(), |
442 | semanticsContext.targetCharacteristics(), parsing.allCooked(), |
443 | targetTriple, kindMap, loweringOptions, envDefaults, |
444 | semanticsContext.languageFeatures(), targetMachine, targetOpts, cgOpts); |
445 | mlir::ModuleOp mlirModule = burnside.getModule(); |
446 | if (enableOpenMP) { |
447 | if (enableOpenMPGPU && !enableOpenMPDevice) { |
448 | llvm::errs() << "FATAL: -fopenmp-is-gpu can only be set if " |
449 | "-fopenmp-is-target-device is also set" ; |
450 | return mlir::failure(); |
451 | } |
452 | // Construct offloading target triples vector. |
453 | std::vector<llvm::Triple> targetTriples; |
454 | targetTriples.reserve(n: targetTriplesOpenMP.size()); |
455 | for (llvm::StringRef s : targetTriplesOpenMP) |
456 | targetTriples.emplace_back(args&: s); |
457 | |
458 | auto offloadModuleOpts = OffloadModuleOpts( |
459 | setOpenMPTargetDebug, setOpenMPTeamSubscription, |
460 | setOpenMPThreadSubscription, setOpenMPNoThreadState, |
461 | setOpenMPNoNestedParallelism, enableOpenMPDevice, enableOpenMPGPU, |
462 | enableOpenMPForceUSM, setOpenMPVersion, "" , targetTriples, setNoGPULib); |
463 | setOffloadModuleInterfaceAttributes(mlirModule, offloadModuleOpts); |
464 | setOpenMPVersionAttribute(mlirModule, setOpenMPVersion); |
465 | } |
466 | burnside.lower(parseTree, semanticsContext); |
467 | std::error_code ec; |
468 | std::string outputName = outputFilename; |
469 | if (!outputName.size()) |
470 | outputName = llvm::sys::path::stem(path: inputFilename).str().append(s: ".mlir" ); |
471 | llvm::raw_fd_ostream out(outputName, ec); |
472 | if (ec) |
473 | return mlir::emitError(mlir::UnknownLoc::get(&ctx), |
474 | "could not open output file " ) |
475 | << outputName; |
476 | |
477 | // WARNING: This pipeline must be run immediately after the lowering to |
478 | // ensure that the FIR is correct with respect to OpenMP operations/ |
479 | // attributes. |
480 | if (enableOpenMP) |
481 | if (mlir::failed(runOpenMPPasses(mlirModule))) |
482 | return mlir::failure(); |
483 | |
484 | // Otherwise run the default passes. |
485 | mlir::PassManager pm(mlirModule->getName(), |
486 | mlir::OpPassManager::Nesting::Implicit); |
487 | pm.enableVerifier(/*verifyPasses=*/true); |
488 | (void)mlir::applyPassManagerCLOptions(pm); |
489 | if (passPipeline.hasAnyOccurrences()) { |
490 | // run the command-line specified pipeline |
491 | hlfir::registerHLFIRPasses(); |
492 | (void)passPipeline.addToPipeline(pm, [&](const llvm::Twine &msg) { |
493 | mlir::emitError(mlir::UnknownLoc::get(&ctx)) << msg; |
494 | return mlir::failure(); |
495 | }); |
496 | } else if (emitFIR || emitHLFIR) { |
497 | // --emit-fir: Build the IR, verify it, and dump the IR if the IR passes |
498 | // verification. Use --dump-module-on-failure to dump invalid IR. |
499 | pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); |
500 | if (mlir::failed(pm.run(mlirModule))) { |
501 | llvm::errs() << "FATAL: verification of lowering to FIR failed" ; |
502 | return mlir::failure(); |
503 | } |
504 | |
505 | if (emitFIR && useHLFIR) { |
506 | // lower HLFIR to FIR |
507 | fir::createHLFIRToFIRPassPipeline(pm, enableOpenMP, |
508 | llvm::OptimizationLevel::O2); |
509 | if (mlir::failed(pm.run(mlirModule))) { |
510 | llvm::errs() << "FATAL: lowering from HLFIR to FIR failed" ; |
511 | return mlir::failure(); |
512 | } |
513 | } |
514 | |
515 | printModule(mlirModule, out); |
516 | return mlir::success(); |
517 | } else { |
518 | // run the default canned pipeline |
519 | pm.addPass(std::make_unique<Fortran::lower::VerifierPass>()); |
520 | |
521 | // Add O2 optimizer pass pipeline. |
522 | MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); |
523 | if (enableOpenMP) |
524 | config.EnableOpenMP = true; |
525 | config.NSWOnLoopVarInc = !integerWrapAround; |
526 | fir::registerDefaultInlinerPass(config); |
527 | fir::createDefaultFIROptimizerPassPipeline(pm, config); |
528 | } |
529 | |
530 | if (mlir::succeeded(pm.run(mlirModule))) { |
531 | // Emit MLIR and do not lower to LLVM IR. |
532 | printModule(mlirModule, out); |
533 | return mlir::success(); |
534 | } |
535 | // Something went wrong. Try to dump the MLIR module. |
536 | llvm::errs() << "oops, pass manager reported failure\n" ; |
537 | return mlir::failure(); |
538 | } |
539 | |
540 | int main(int argc, char **argv) { |
541 | [[maybe_unused]] llvm::InitLLVM y(argc, argv); |
542 | llvm::InitializeAllTargets(); |
543 | llvm::InitializeAllTargetMCs(); |
544 | registerAllPasses(); |
545 | |
546 | mlir::registerMLIRContextCLOptions(); |
547 | mlir::registerAsmPrinterCLOptions(); |
548 | mlir::registerPassManagerCLOptions(); |
549 | mlir::PassPipelineCLParser passPipe("" , "Compiler passes to run" ); |
550 | llvm::cl::ParseCommandLineOptions(argc, argv, Overview: "Burnside Bridge Compiler\n" ); |
551 | |
552 | ProgramName programPrefix; |
553 | programPrefix = argv[0] + ": "s ; |
554 | |
555 | if (includeDirs.size() == 0) { |
556 | includeDirs.push_back(value: "." ); |
557 | // Default Fortran modules should be installed in include/flang (a sibling |
558 | // to the bin) directory. |
559 | intrinsicIncludeDirs.push_back( |
560 | value: llvm::sys::path::parent_path( |
561 | path: llvm::sys::path::parent_path( |
562 | path: llvm::sys::fs::getMainExecutable(argv0: argv[0], MainExecAddr: nullptr))) |
563 | .str() + |
564 | "/include/flang" ); |
565 | } |
566 | |
567 | Fortran::parser::Options options; |
568 | options.predefinitions.emplace_back("__flang__"s , "1"s ); |
569 | options.predefinitions.emplace_back("__flang_major__"s , |
570 | std::string{FLANG_VERSION_MAJOR_STRING}); |
571 | options.predefinitions.emplace_back("__flang_minor__"s , |
572 | std::string{FLANG_VERSION_MINOR_STRING}); |
573 | options.predefinitions.emplace_back( |
574 | "__flang_patchlevel__"s , std::string{FLANG_VERSION_PATCHLEVEL_STRING}); |
575 | |
576 | Fortran::common::LangOptions langOpts; |
577 | langOpts.NoGPULib = setNoGPULib; |
578 | langOpts.OpenMPVersion = setOpenMPVersion; |
579 | langOpts.OpenMPIsTargetDevice = enableOpenMPDevice; |
580 | langOpts.OpenMPIsGPU = enableOpenMPGPU; |
581 | langOpts.OpenMPForceUSM = enableOpenMPForceUSM; |
582 | langOpts.OpenMPTargetDebug = setOpenMPTargetDebug; |
583 | langOpts.OpenMPThreadSubscription = setOpenMPThreadSubscription; |
584 | langOpts.OpenMPTeamSubscription = setOpenMPTeamSubscription; |
585 | langOpts.OpenMPNoThreadState = setOpenMPNoThreadState; |
586 | langOpts.OpenMPNoNestedParallelism = setOpenMPNoNestedParallelism; |
587 | std::transform(targetTriplesOpenMP.begin(), targetTriplesOpenMP.end(), |
588 | std::back_inserter(langOpts.OMPTargetTriples), |
589 | [](const std::string &str) { return llvm::Triple(str); }); |
590 | |
591 | // enable parsing of OpenMP |
592 | if (enableOpenMP) { |
593 | options.features.Enable(Fortran::common::LanguageFeature::OpenMP); |
594 | Fortran::common::setOpenMPMacro(setOpenMPVersion, options.predefinitions); |
595 | } |
596 | |
597 | // enable parsing of OpenACC |
598 | if (enableOpenACC) { |
599 | options.features.Enable(Fortran::common::LanguageFeature::OpenACC); |
600 | options.predefinitions.emplace_back("_OPENACC" , "202211" ); |
601 | } |
602 | |
603 | // enable parsing of CUDA Fortran |
604 | if (enableCUDA) { |
605 | options.features.Enable(Fortran::common::LanguageFeature::CUDA); |
606 | } |
607 | |
608 | if (disableCUDAWarpFunction) { |
609 | options.features.Enable( |
610 | Fortran::common::LanguageFeature::CudaWarpMatchFunction, false); |
611 | } |
612 | |
613 | if (enableGPUMode == "managed" ) { |
614 | options.features.Enable(Fortran::common::LanguageFeature::CudaManaged); |
615 | } else if (enableGPUMode == "unified" ) { |
616 | options.features.Enable(Fortran::common::LanguageFeature::CudaUnified); |
617 | } |
618 | |
619 | if (fixedForm) { |
620 | options.isFixedForm = fixedForm; |
621 | } |
622 | |
623 | Fortran::common::IntrinsicTypeDefaultKinds defaultKinds; |
624 | Fortran::parser::AllSources allSources; |
625 | Fortran::parser::AllCookedSources allCookedSources(allSources); |
626 | Fortran::semantics::SemanticsContext semanticsContext{ |
627 | defaultKinds, options.features, langOpts, allCookedSources}; |
628 | semanticsContext.set_moduleDirectory(moduleDir) |
629 | .set_moduleFileSuffix(moduleSuffix) |
630 | .set_searchDirectories(includeDirs) |
631 | .set_intrinsicModuleDirectories(intrinsicIncludeDirs) |
632 | .set_warnOnNonstandardUsage(warnStdViolation) |
633 | .set_warningsAreErrors(warnIsError); |
634 | |
635 | std::string error; |
636 | // Create host target machine. |
637 | std::unique_ptr<llvm::TargetMachine> targetMachine = |
638 | createTargetMachine(targetTriple: targetTripleOverride, error); |
639 | if (!targetMachine) { |
640 | llvm::errs() << "failed to create target machine: " << error << "\n" ; |
641 | return mlir::failed(mlir::failure()); |
642 | } |
643 | std::string compilerVersion = Fortran::common::getFlangToolFullVersion("bbc" ); |
644 | std::string compilerOptions = "" ; |
645 | Fortran::tools::setUpTargetCharacteristics( |
646 | semanticsContext.targetCharacteristics(), *targetMachine, {}, |
647 | compilerVersion, compilerOptions); |
648 | |
649 | return mlir::failed( |
650 | convertFortranSourceToMLIR(inputFilename, options, programPrefix, |
651 | semanticsContext, passPipe, *targetMachine)); |
652 | } |
653 | |