1//===--- FrontendActions.cpp ----------------------------------------------===//
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#include "flang/Frontend/FrontendActions.h"
14#include "flang/Frontend/CompilerInstance.h"
15#include "flang/Frontend/CompilerInvocation.h"
16#include "flang/Frontend/FrontendOptions.h"
17#include "flang/Frontend/ParserActions.h"
18#include "flang/Lower/Bridge.h"
19#include "flang/Lower/Support/Verifier.h"
20#include "flang/Optimizer/Dialect/Support/FIRContext.h"
21#include "flang/Optimizer/Dialect/Support/KindMapping.h"
22#include "flang/Optimizer/Passes/Pipelines.h"
23#include "flang/Optimizer/Support/DataLayout.h"
24#include "flang/Optimizer/Support/InitFIR.h"
25#include "flang/Optimizer/Support/Utils.h"
26#include "flang/Optimizer/Transforms/Passes.h"
27#include "flang/Semantics/runtime-type-info.h"
28#include "flang/Semantics/unparse-with-symbols.h"
29#include "flang/Support/default-kinds.h"
30#include "flang/Tools/CrossToolHelpers.h"
31
32#include "mlir/IR/Dialect.h"
33#include "mlir/Parser/Parser.h"
34#include "mlir/Pass/PassManager.h"
35#include "mlir/Support/LLVM.h"
36#include "mlir/Target/LLVMIR/Import.h"
37#include "mlir/Target/LLVMIR/ModuleTranslation.h"
38#include "clang/Basic/Diagnostic.h"
39#include "clang/Basic/DiagnosticFrontend.h"
40#include "clang/Basic/FileManager.h"
41#include "clang/Basic/FileSystemOptions.h"
42#include "clang/Driver/DriverDiagnostic.h"
43#include "llvm/ADT/SmallString.h"
44#include "llvm/ADT/StringRef.h"
45#include "llvm/Analysis/TargetLibraryInfo.h"
46#include "llvm/Analysis/TargetTransformInfo.h"
47#include "llvm/Bitcode/BitcodeWriterPass.h"
48#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
49#include "llvm/IR/LLVMRemarkStreamer.h"
50#include "llvm/IR/LegacyPassManager.h"
51#include "llvm/IR/Verifier.h"
52#include "llvm/IRPrinter/IRPrintingPasses.h"
53#include "llvm/IRReader/IRReader.h"
54#include "llvm/Linker/Linker.h"
55#include "llvm/Object/OffloadBinary.h"
56#include "llvm/Passes/PassBuilder.h"
57#include "llvm/Passes/PassPlugin.h"
58#include "llvm/Passes/StandardInstrumentations.h"
59#include "llvm/ProfileData/InstrProfCorrelator.h"
60#include "llvm/Support/AMDGPUAddrSpace.h"
61#include "llvm/Support/Error.h"
62#include "llvm/Support/ErrorHandling.h"
63#include "llvm/Support/FileSystem.h"
64#include "llvm/Support/PGOOptions.h"
65#include "llvm/Support/Path.h"
66#include "llvm/Support/SourceMgr.h"
67#include "llvm/Support/ToolOutputFile.h"
68#include "llvm/Target/TargetMachine.h"
69#include "llvm/TargetParser/RISCVISAInfo.h"
70#include "llvm/TargetParser/RISCVTargetParser.h"
71#include "llvm/Transforms/IPO/Internalize.h"
72#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
73#include "llvm/Transforms/Utils/ModuleUtils.h"
74#include <memory>
75#include <system_error>
76
77using namespace Fortran::frontend;
78
79constexpr llvm::StringLiteral timingIdParse = "Parse";
80constexpr llvm::StringLiteral timingIdMLIRGen = "MLIR generation";
81constexpr llvm::StringLiteral timingIdMLIRPasses =
82 "MLIR translation/optimization";
83constexpr llvm::StringLiteral timingIdLLVMIRGen = "LLVM IR generation";
84constexpr llvm::StringLiteral timingIdLLVMIRPasses = "LLVM IR optimizations";
85constexpr llvm::StringLiteral timingIdBackend =
86 "Assembly/Object code generation";
87
88// Declare plugin extension function declarations.
89#define HANDLE_EXTENSION(Ext) \
90 llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
91#include "llvm/Support/Extension.def"
92
93/// Save the given \c mlirModule to a temporary .mlir file, in a location
94/// decided by the -save-temps flag. No files are produced if the flag is not
95/// specified.
96static bool saveMLIRTempFile(const CompilerInvocation &ci,
97 mlir::ModuleOp mlirModule,
98 llvm::StringRef inputFile,
99 llvm::StringRef outputTag) {
100 if (!ci.getCodeGenOpts().SaveTempsDir.has_value())
101 return true;
102
103 const llvm::StringRef compilerOutFile = ci.getFrontendOpts().outputFile;
104 const llvm::StringRef saveTempsDir = ci.getCodeGenOpts().SaveTempsDir.value();
105 auto dir = llvm::StringSwitch<llvm::StringRef>(saveTempsDir)
106 .Case(S: "cwd", Value: "")
107 .Case(S: "obj", Value: llvm::sys::path::parent_path(path: compilerOutFile))
108 .Default(Value: saveTempsDir);
109
110 // Build path from the compiler output file name, triple, cpu and OpenMP
111 // information
112 llvm::SmallString<256> path(dir);
113 llvm::sys::path::append(path, a: llvm::sys::path::stem(path: inputFile) + "-" +
114 outputTag + ".mlir");
115
116 std::error_code ec;
117 llvm::ToolOutputFile out(path, ec, llvm::sys::fs::OF_Text);
118 if (ec)
119 return false;
120
121 mlirModule->print(out.os());
122 out.os().close();
123 out.keep();
124
125 return true;
126}
127
128//===----------------------------------------------------------------------===//
129// Custom BeginSourceFileAction
130//===----------------------------------------------------------------------===//
131
132bool PrescanAction::beginSourceFileAction() { return runPrescan(); }
133
134bool PrescanAndParseAction::beginSourceFileAction() {
135 return runPrescan() && runParse(/*emitMessages=*/true);
136}
137
138bool PrescanAndSemaAction::beginSourceFileAction() {
139 return runPrescan() && runParse(/*emitMessages=*/false) &&
140 runSemanticChecks() && generateRtTypeTables();
141}
142
143bool PrescanAndSemaDebugAction::beginSourceFileAction() {
144 // This is a "debug" action for development purposes. To facilitate this, the
145 // semantic checks are made to succeed unconditionally to prevent this action
146 // from exiting early (i.e. in the presence of semantic errors). We should
147 // never do this in actions intended for end-users or otherwise regular
148 // compiler workflows!
149 return runPrescan() && runParse(/*emitMessages=*/false) &&
150 (runSemanticChecks() || true) && (generateRtTypeTables() || true);
151}
152
153static void addDependentLibs(mlir::ModuleOp mlirModule, CompilerInstance &ci) {
154 const std::vector<std::string> &libs =
155 ci.getInvocation().getCodeGenOpts().DependentLibs;
156 if (libs.empty()) {
157 return;
158 }
159 // dependent-lib is currently only supported on Windows, so the list should be
160 // empty on non-Windows platforms
161 assert(
162 llvm::Triple(ci.getInvocation().getTargetOpts().triple).isOSWindows() &&
163 "--dependent-lib is only supported on Windows");
164 // Add linker options specified by --dependent-lib
165 auto builder = mlir::OpBuilder(mlirModule.getRegion());
166 for (const std::string &lib : libs) {
167 builder.create<mlir::LLVM::LinkerOptionsOp>(
168 mlirModule.getLoc(), builder.getStrArrayAttr({"/DEFAULTLIB:" + lib}));
169 }
170}
171
172bool CodeGenAction::beginSourceFileAction() {
173 // Delete previous LLVM module depending on old context before making a new
174 // one.
175 if (llvmModule)
176 llvmModule.reset(nullptr);
177 llvmCtx = std::make_unique<llvm::LLVMContext>();
178 CompilerInstance &ci = this->getInstance();
179 mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
180 mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
181
182 // This will provide timing information even when the input is an LLVM IR or
183 // MLIR file. That is fine because those do have to be parsed, so the label
184 // is still accurate.
185 mlir::TimingScope timingScopeParse = timingScopeRoot.nest(
186 mlir::TimingIdentifier::get(timingIdParse, timingMgr));
187
188 // If the input is an LLVM file, just parse it and return.
189 if (this->getCurrentInput().getKind().getLanguage() == Language::LLVM_IR) {
190 llvm::SMDiagnostic err;
191 llvmModule = llvm::parseIRFile(getCurrentInput().getFile(), err, *llvmCtx);
192 if (!llvmModule || llvm::verifyModule(*llvmModule, &llvm::errs())) {
193 err.print("flang", llvm::errs());
194 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
195 clang::DiagnosticsEngine::Error, "Could not parse IR");
196 ci.getDiagnostics().Report(diagID);
197 return false;
198 }
199
200 return true;
201 }
202
203 // Reset MLIR module if it was set before overriding the old context.
204 if (mlirModule)
205 mlirModule = mlir::OwningOpRef<mlir::ModuleOp>(nullptr);
206 // Load the MLIR dialects required by Flang
207 mlirCtx = std::make_unique<mlir::MLIRContext>();
208 fir::support::loadDialects(*mlirCtx);
209 fir::support::registerLLVMTranslation(*mlirCtx);
210 mlir::DialectRegistry registry;
211 fir::acc::registerOpenACCExtensions(registry);
212 fir::omp::registerOpenMPExtensions(registry);
213 mlirCtx->appendDialectRegistry(registry);
214
215 const llvm::TargetMachine &targetMachine = ci.getTargetMachine();
216
217 // If the input is an MLIR file, just parse it and return.
218 if (this->getCurrentInput().getKind().getLanguage() == Language::MLIR) {
219 llvm::SourceMgr sourceMgr;
220 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileOrErr =
221 llvm::MemoryBuffer::getFileOrSTDIN(getCurrentInput().getFile());
222 sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc());
223 mlir::OwningOpRef<mlir::ModuleOp> module =
224 mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, mlirCtx.get());
225
226 if (!module || mlir::failed(module->verifyInvariants())) {
227 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
228 clang::DiagnosticsEngine::Error, "Could not parse FIR");
229 ci.getDiagnostics().Report(diagID);
230 return false;
231 }
232
233 mlirModule = std::move(module);
234 const llvm::DataLayout &dl = targetMachine.createDataLayout();
235 fir::support::setMLIRDataLayout(*mlirModule, dl);
236 return true;
237 }
238
239 // Otherwise, generate an MLIR module from the input Fortran source
240 if (getCurrentInput().getKind().getLanguage() != Language::Fortran) {
241 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
242 clang::DiagnosticsEngine::Error,
243 "Invalid input type - expecting a Fortran file");
244 ci.getDiagnostics().Report(diagID);
245 return false;
246 }
247 bool res = runPrescan() && runParse(/*emitMessages=*/false) &&
248 runSemanticChecks() && generateRtTypeTables();
249 if (!res)
250 return res;
251
252 timingScopeParse.stop();
253 mlir::TimingScope timingScopeMLIRGen = timingScopeRoot.nest(
254 mlir::TimingIdentifier::get(timingIdMLIRGen, timingMgr));
255
256 // Create a LoweringBridge
257 const common::IntrinsicTypeDefaultKinds &defKinds =
258 ci.getSemanticsContext().defaultKinds();
259 fir::KindMapping kindMap(mlirCtx.get(), llvm::ArrayRef<fir::KindTy>{
260 fir::fromDefaultKinds(defKinds)});
261 lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(
262 *mlirCtx, ci.getSemanticsContext(), defKinds,
263 ci.getSemanticsContext().intrinsics(),
264 ci.getSemanticsContext().targetCharacteristics(), getAllCooked(ci),
265 ci.getInvocation().getTargetOpts().triple, kindMap,
266 ci.getInvocation().getLoweringOpts(),
267 ci.getInvocation().getFrontendOpts().envDefaults,
268 ci.getInvocation().getFrontendOpts().features, targetMachine,
269 ci.getInvocation().getTargetOpts(), ci.getInvocation().getCodeGenOpts());
270
271 if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
272 Fortran::common::LanguageFeature::OpenMP)) {
273 setOffloadModuleInterfaceAttributes(lb.getModule(),
274 ci.getInvocation().getLangOpts());
275 setOpenMPVersionAttribute(lb.getModule(),
276 ci.getInvocation().getLangOpts().OpenMPVersion);
277 }
278
279 // Create a parse tree and lower it to FIR
280 parseAndLowerTree(ci, lb);
281
282 // Fetch module from lb, so we can set
283 mlirModule = lb.getModuleAndRelease();
284
285 // Add target specific items like dependent libraries, target specific
286 // constants etc.
287 addDependentLibs(*mlirModule, ci);
288 timingScopeMLIRGen.stop();
289
290 // run the default passes.
291 mlir::PassManager pm((*mlirModule)->getName(),
292 mlir::OpPassManager::Nesting::Implicit);
293 (void)mlir::applyPassManagerCLOptions(pm);
294 // Add OpenMP-related passes
295 // WARNING: These passes must be run immediately after the lowering to ensure
296 // that the FIR is correct with respect to OpenMP operations/attributes.
297 bool isOpenMPEnabled =
298 ci.getInvocation().getFrontendOpts().features.IsEnabled(
299 Fortran::common::LanguageFeature::OpenMP);
300
301 fir::OpenMPFIRPassPipelineOpts opts;
302
303 using DoConcurrentMappingKind =
304 Fortran::frontend::CodeGenOptions::DoConcurrentMappingKind;
305 opts.doConcurrentMappingKind =
306 ci.getInvocation().getCodeGenOpts().getDoConcurrentMapping();
307
308 if (opts.doConcurrentMappingKind != DoConcurrentMappingKind::DCMK_None &&
309 !isOpenMPEnabled) {
310 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
311 clang::DiagnosticsEngine::Warning,
312 "OpenMP is required for lowering `do concurrent` loops to OpenMP."
313 "Enable OpenMP using `-fopenmp`."
314 "`do concurrent` loops will be serialized.");
315 ci.getDiagnostics().Report(diagID);
316 opts.doConcurrentMappingKind = DoConcurrentMappingKind::DCMK_None;
317 }
318
319 if (opts.doConcurrentMappingKind != DoConcurrentMappingKind::DCMK_None) {
320 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
321 clang::DiagnosticsEngine::Warning,
322 "Mapping `do concurrent` to OpenMP is still experimental.");
323 ci.getDiagnostics().Report(diagID);
324 }
325
326 if (isOpenMPEnabled) {
327 opts.isTargetDevice = false;
328 if (auto offloadMod = llvm::dyn_cast<mlir::omp::OffloadModuleInterface>(
329 mlirModule->getOperation()))
330 opts.isTargetDevice = offloadMod.getIsTargetDevice();
331
332 // WARNING: This pipeline must be run immediately after the lowering to
333 // ensure that the FIR is correct with respect to OpenMP operations/
334 // attributes.
335 fir::createOpenMPFIRPassPipeline(pm, opts);
336 }
337
338 pm.enableVerifier(/*verifyPasses=*/true);
339 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
340 pm.enableTiming(timingScopeMLIRGen);
341
342 if (mlir::failed(pm.run(*mlirModule))) {
343 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
344 clang::DiagnosticsEngine::Error,
345 "verification of lowering to FIR failed");
346 ci.getDiagnostics().Report(diagID);
347 return false;
348 }
349 timingScopeMLIRGen.stop();
350
351 // Print initial full MLIR module, before lowering or transformations, if
352 // -save-temps has been specified.
353 if (!saveMLIRTempFile(ci.getInvocation(), *mlirModule, getCurrentFile(),
354 "fir")) {
355 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
356 clang::DiagnosticsEngine::Error, "Saving MLIR temp file failed");
357 ci.getDiagnostics().Report(diagID);
358 return false;
359 }
360
361 return true;
362}
363
364//===----------------------------------------------------------------------===//
365// Custom ExecuteAction
366//===----------------------------------------------------------------------===//
367void InputOutputTestAction::executeAction() {
368 CompilerInstance &ci = getInstance();
369
370 // Create a stream for errors
371 std::string buf;
372 llvm::raw_string_ostream errorStream{buf};
373
374 // Read the input file
375 Fortran::parser::AllSources &allSources{ci.getAllSources()};
376 std::string path{getCurrentFileOrBufferName()};
377 const Fortran::parser::SourceFile *sf;
378 if (path == "-")
379 sf = allSources.ReadStandardInput(errorStream);
380 else
381 sf = allSources.Open(path, errorStream, std::optional<std::string>{"."s});
382 llvm::ArrayRef<char> fileContent = sf->content();
383
384 // Output file descriptor to receive the contents of the input file.
385 std::unique_ptr<llvm::raw_ostream> os;
386
387 // Copy the contents from the input file to the output file
388 if (!ci.isOutputStreamNull()) {
389 // An output stream (outputStream_) was set earlier
390 ci.writeOutputStream(fileContent.data());
391 } else {
392 // No pre-set output stream - create an output file
393 os = ci.createDefaultOutputFile(
394 /*binary=*/true, getCurrentFileOrBufferName(), "txt");
395 if (!os)
396 return;
397 (*os) << fileContent.data();
398 }
399}
400
401void PrintPreprocessedAction::executeAction() {
402 std::string buf;
403 llvm::raw_string_ostream outForPP{buf};
404
405 CompilerInstance &ci = this->getInstance();
406 formatOrDumpPrescanner(buf, outForPP, ci);
407
408 // If a pre-defined output stream exists, dump the preprocessed content there
409 if (!ci.isOutputStreamNull()) {
410 // Send the output to the pre-defined output buffer.
411 ci.writeOutputStream(buf);
412 return;
413 }
414
415 // Create a file and save the preprocessed output there
416 std::unique_ptr<llvm::raw_pwrite_stream> os{ci.createDefaultOutputFile(
417 /*Binary=*/true, /*InFile=*/getCurrentFileOrBufferName())};
418 if (!os) {
419 return;
420 }
421
422 (*os) << buf;
423}
424
425void DebugDumpProvenanceAction::executeAction() {
426 dumpProvenance(this->getInstance());
427}
428
429void ParseSyntaxOnlyAction::executeAction() {}
430
431void DebugUnparseNoSemaAction::executeAction() {
432 debugUnparseNoSema(this->getInstance(), llvm::outs());
433}
434
435void DebugUnparseAction::executeAction() {
436 CompilerInstance &ci = this->getInstance();
437 auto os{ci.createDefaultOutputFile(
438 /*Binary=*/false, /*InFile=*/getCurrentFileOrBufferName())};
439
440 debugUnparseNoSema(ci, *os);
441 reportFatalSemanticErrors();
442}
443
444void DebugUnparseWithSymbolsAction::executeAction() {
445 debugUnparseWithSymbols(this->getInstance());
446 reportFatalSemanticErrors();
447}
448
449void DebugUnparseWithModulesAction::executeAction() {
450 debugUnparseWithModules(this->getInstance());
451 reportFatalSemanticErrors();
452}
453
454void DebugDumpSymbolsAction::executeAction() {
455 CompilerInstance &ci = this->getInstance();
456
457 if (!ci.getRtTyTables().schemata) {
458 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
459 clang::DiagnosticsEngine::Error,
460 "could not find module file for __fortran_type_info");
461 ci.getDiagnostics().Report(diagID);
462 llvm::errs() << "\n";
463 return;
464 }
465
466 // Dump symbols
467 ci.getSemantics().DumpSymbols(llvm::outs());
468}
469
470void DebugDumpAllAction::executeAction() {
471 CompilerInstance &ci = this->getInstance();
472
473 // Dump parse tree
474 dumpTree(ci);
475
476 if (!ci.getRtTyTables().schemata) {
477 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
478 clang::DiagnosticsEngine::Error,
479 "could not find module file for __fortran_type_info");
480 ci.getDiagnostics().Report(diagID);
481 llvm::errs() << "\n";
482 return;
483 }
484
485 // Dump symbols
486 llvm::outs() << "=====================";
487 llvm::outs() << " Flang: symbols dump ";
488 llvm::outs() << "=====================\n";
489 ci.getSemantics().DumpSymbols(llvm::outs());
490}
491
492void DebugDumpParseTreeNoSemaAction::executeAction() {
493 dumpTree(this->getInstance());
494}
495
496void DebugDumpParseTreeAction::executeAction() {
497 dumpTree(this->getInstance());
498
499 // Report fatal semantic errors
500 reportFatalSemanticErrors();
501}
502
503void DebugMeasureParseTreeAction::executeAction() {
504 CompilerInstance &ci = this->getInstance();
505 debugMeasureParseTree(ci, getCurrentFileOrBufferName());
506}
507
508void DebugPreFIRTreeAction::executeAction() {
509 // Report and exit if fatal semantic errors are present
510 if (reportFatalSemanticErrors()) {
511 return;
512 }
513
514 dumpPreFIRTree(this->getInstance());
515}
516
517void DebugDumpParsingLogAction::executeAction() {
518 debugDumpParsingLog(this->getInstance());
519}
520
521void GetDefinitionAction::executeAction() {
522 CompilerInstance &ci = this->getInstance();
523
524 // Report and exit if fatal semantic errors are present
525 if (reportFatalSemanticErrors()) {
526 return;
527 }
528
529 parser::AllCookedSources &cs = ci.getAllCookedSources();
530 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
531 clang::DiagnosticsEngine::Error, "Symbol not found");
532
533 auto gdv = ci.getInvocation().getFrontendOpts().getDefVals;
534 auto charBlock{cs.GetCharBlockFromLineAndColumns(gdv.line, gdv.startColumn,
535 gdv.endColumn)};
536 if (!charBlock) {
537 ci.getDiagnostics().Report(diagID);
538 return;
539 }
540
541 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
542
543 auto *symbol{
544 ci.getSemanticsContext().FindScope(*charBlock).FindSymbol(*charBlock)};
545 if (!symbol) {
546 ci.getDiagnostics().Report(diagID);
547 return;
548 }
549
550 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
551
552 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
553 if (!sourceInfo) {
554 llvm_unreachable(
555 "Failed to obtain SourcePosition."
556 "TODO: Please, write a test and replace this with a diagnostic!");
557 return;
558 }
559
560 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
561 llvm::outs() << symbol->name().ToString() << ": " << sourceInfo->first.path
562 << ", " << sourceInfo->first.line << ", "
563 << sourceInfo->first.column << "-" << sourceInfo->second.column
564 << "\n";
565}
566
567void GetSymbolsSourcesAction::executeAction() {
568 CompilerInstance &ci = this->getInstance();
569
570 // Report and exit if fatal semantic errors are present
571 if (reportFatalSemanticErrors()) {
572 return;
573 }
574
575 ci.getSemantics().DumpSymbolsSources(llvm::outs());
576}
577
578//===----------------------------------------------------------------------===//
579// CodeGenActions
580//===----------------------------------------------------------------------===//
581
582CodeGenAction::~CodeGenAction() = default;
583
584static llvm::OptimizationLevel
585mapToLevel(const Fortran::frontend::CodeGenOptions &opts) {
586 switch (opts.OptimizationLevel) {
587 default:
588 llvm_unreachable("Invalid optimization level!");
589 case 0:
590 return llvm::OptimizationLevel::O0;
591 case 1:
592 return llvm::OptimizationLevel::O1;
593 case 2:
594 return llvm::OptimizationLevel::O2;
595 case 3:
596 return llvm::OptimizationLevel::O3;
597 }
598}
599
600// Lower using HLFIR then run the FIR to HLFIR pipeline
601void CodeGenAction::lowerHLFIRToFIR() {
602 assert(mlirModule && "The MLIR module has not been generated yet.");
603
604 CompilerInstance &ci = this->getInstance();
605 const CodeGenOptions &opts = ci.getInvocation().getCodeGenOpts();
606 llvm::OptimizationLevel level = mapToLevel(opts);
607 mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
608 mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
609
610 fir::support::loadDialects(*mlirCtx);
611
612 // Set-up the MLIR pass manager
613 mlir::PassManager pm((*mlirModule)->getName(),
614 mlir::OpPassManager::Nesting::Implicit);
615
616 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
617 pm.enableVerifier(/*verifyPasses=*/true);
618
619 // Create the pass pipeline
620 fir::createHLFIRToFIRPassPipeline(
621 pm,
622 ci.getInvocation().getFrontendOpts().features.IsEnabled(
623 Fortran::common::LanguageFeature::OpenMP),
624 level);
625 (void)mlir::applyPassManagerCLOptions(pm);
626
627 mlir::TimingScope timingScopeMLIRPasses = timingScopeRoot.nest(
628 mlir::TimingIdentifier::get(timingIdMLIRPasses, timingMgr));
629 pm.enableTiming(timingScopeMLIRPasses);
630 if (!mlir::succeeded(pm.run(*mlirModule))) {
631 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
632 clang::DiagnosticsEngine::Error, "Lowering to FIR failed");
633 ci.getDiagnostics().Report(diagID);
634 }
635}
636
637static std::optional<std::pair<unsigned, unsigned>>
638getAArch64VScaleRange(CompilerInstance &ci) {
639 const auto &langOpts = ci.getInvocation().getLangOpts();
640
641 if (langOpts.VScaleMin || langOpts.VScaleMax)
642 return std::pair<unsigned, unsigned>(
643 langOpts.VScaleMin ? langOpts.VScaleMin : 1, langOpts.VScaleMax);
644
645 std::string featuresStr = ci.getTargetFeatures();
646 if (featuresStr.find(s: "+sve") != std::string::npos)
647 return std::pair<unsigned, unsigned>(1, 16);
648
649 return std::nullopt;
650}
651
652static std::optional<std::pair<unsigned, unsigned>>
653getRISCVVScaleRange(CompilerInstance &ci) {
654 const auto &langOpts = ci.getInvocation().getLangOpts();
655 const auto targetOpts = ci.getInvocation().getTargetOpts();
656 const llvm::Triple triple(targetOpts.triple);
657
658 auto parseResult = llvm::RISCVISAInfo::parseFeatures(
659 XLen: triple.isRISCV64() ? 64 : 32, Features: targetOpts.featuresAsWritten);
660 if (!parseResult) {
661 std::string buffer;
662 llvm::raw_string_ostream outputErrMsg(buffer);
663 handleAllErrors(parseResult.takeError(), [&](llvm::StringError &errMsg) {
664 outputErrMsg << errMsg.getMessage();
665 });
666 ci.getDiagnostics().Report(clang::diag::err_invalid_feature_combination)
667 << buffer;
668 return std::nullopt;
669 }
670
671 llvm::RISCVISAInfo *const isaInfo = parseResult->get();
672
673 // RISCV::RVVBitsPerBlock is 64.
674 unsigned vscaleMin = isaInfo->getMinVLen() / llvm::RISCV::RVVBitsPerBlock;
675
676 if (langOpts.VScaleMin || langOpts.VScaleMax) {
677 // Treat Zvl*b as a lower bound on vscale.
678 vscaleMin = std::max(vscaleMin, langOpts.VScaleMin);
679 unsigned vscaleMax = langOpts.VScaleMax;
680 if (vscaleMax != 0 && vscaleMax < vscaleMin)
681 vscaleMax = vscaleMin;
682 return std::pair<unsigned, unsigned>(vscaleMin ? vscaleMin : 1, vscaleMax);
683 }
684
685 if (vscaleMin > 0) {
686 unsigned vscaleMax = isaInfo->getMaxVLen() / llvm::RISCV::RVVBitsPerBlock;
687 return std::make_pair(x&: vscaleMin, y&: vscaleMax);
688 }
689
690 return std::nullopt;
691}
692
693// TODO: We should get this from TargetInfo. However, that depends on
694// too much of clang, so for now, replicate the functionality.
695static std::optional<std::pair<unsigned, unsigned>>
696getVScaleRange(CompilerInstance &ci) {
697 const llvm::Triple triple(ci.getInvocation().getTargetOpts().triple);
698
699 if (triple.isAArch64())
700 return getAArch64VScaleRange(ci);
701 if (triple.isRISCV())
702 return getRISCVVScaleRange(ci);
703
704 // All other architectures that don't support scalable vectors (i.e. don't
705 // need vscale)
706 return std::nullopt;
707}
708
709// Lower the previously generated MLIR module into an LLVM IR module
710void CodeGenAction::generateLLVMIR() {
711 assert(mlirModule && "The MLIR module has not been generated yet.");
712
713 CompilerInstance &ci = this->getInstance();
714 CompilerInvocation &invoc = ci.getInvocation();
715 const CodeGenOptions &opts = invoc.getCodeGenOpts();
716 const auto &mathOpts = invoc.getLoweringOpts().getMathOptions();
717 llvm::OptimizationLevel level = mapToLevel(opts);
718 mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
719 mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
720
721 fir::support::loadDialects(*mlirCtx);
722 mlir::DialectRegistry registry;
723 fir::support::registerNonCodegenDialects(registry);
724 fir::support::addFIRExtensions(registry);
725 mlirCtx->appendDialectRegistry(registry);
726 fir::support::registerLLVMTranslation(*mlirCtx);
727
728 // Set-up the MLIR pass manager
729 mlir::PassManager pm((*mlirModule)->getName(),
730 mlir::OpPassManager::Nesting::Implicit);
731
732 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
733 pm.enableVerifier(/*verifyPasses=*/true);
734
735 MLIRToLLVMPassPipelineConfig config(level, opts, mathOpts);
736 fir::registerDefaultInlinerPass(config);
737
738 if (auto vsr = getVScaleRange(ci)) {
739 config.VScaleMin = vsr->first;
740 config.VScaleMax = vsr->second;
741 }
742
743 config.Reciprocals = opts.Reciprocals;
744 config.PreferVectorWidth = opts.PreferVectorWidth;
745
746 if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
747 Fortran::common::LanguageFeature::OpenMP))
748 config.EnableOpenMP = true;
749
750 if (ci.getInvocation().getLoweringOpts().getIntegerWrapAround())
751 config.NSWOnLoopVarInc = false;
752
753 config.ComplexRange = opts.getComplexRange();
754
755 // Create the pass pipeline
756 fir::createMLIRToLLVMPassPipeline(pm, config, getCurrentFile());
757 (void)mlir::applyPassManagerCLOptions(pm);
758
759 // run the pass manager
760 mlir::TimingScope timingScopeMLIRPasses = timingScopeRoot.nest(
761 mlir::TimingIdentifier::get(timingIdMLIRPasses, timingMgr));
762 pm.enableTiming(timingScopeMLIRPasses);
763 if (!mlir::succeeded(pm.run(*mlirModule))) {
764 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
765 clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
766 ci.getDiagnostics().Report(diagID);
767 }
768 timingScopeMLIRPasses.stop();
769
770 // Print final MLIR module, just before translation into LLVM IR, if
771 // -save-temps has been specified.
772 if (!saveMLIRTempFile(ci.getInvocation(), *mlirModule, getCurrentFile(),
773 "llvmir")) {
774 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
775 clang::DiagnosticsEngine::Error, "Saving MLIR temp file failed");
776 ci.getDiagnostics().Report(diagID);
777 return;
778 }
779
780 // Translate to LLVM IR
781 mlir::TimingScope timingScopeLLVMIRGen = timingScopeRoot.nest(
782 mlir::TimingIdentifier::get(timingIdLLVMIRGen, timingMgr));
783 std::optional<llvm::StringRef> moduleName = mlirModule->getName();
784 llvmModule = mlir::translateModuleToLLVMIR(
785 *mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
786
787 if (!llvmModule) {
788 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
789 clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
790 ci.getDiagnostics().Report(diagID);
791 return;
792 }
793
794 // Set PIC/PIE level LLVM module flags.
795 if (opts.PICLevel > 0) {
796 llvmModule->setPICLevel(static_cast<llvm::PICLevel::Level>(opts.PICLevel));
797 if (opts.IsPIE)
798 llvmModule->setPIELevel(
799 static_cast<llvm::PIELevel::Level>(opts.PICLevel));
800 }
801
802 const TargetOptions &targetOpts = ci.getInvocation().getTargetOpts();
803 const llvm::Triple triple(targetOpts.triple);
804
805 // Set mcmodel level LLVM module flags
806 std::optional<llvm::CodeModel::Model> cm = getCodeModel(opts.CodeModel);
807 if (cm.has_value()) {
808 llvmModule->setCodeModel(*cm);
809 if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) &&
810 triple.getArch() == llvm::Triple::x86_64) {
811 llvmModule->setLargeDataThreshold(opts.LargeDataThreshold);
812 }
813 }
814
815 if (triple.isRISCV() && !targetOpts.abi.empty())
816 llvmModule->addModuleFlag(
817 llvm::Module::Error, "target-abi",
818 llvm::MDString::get(llvmModule->getContext(), targetOpts.abi));
819
820 if (triple.isAMDGPU() ||
821 (triple.isSPIRV() && triple.getVendor() == llvm::Triple::AMD)) {
822 // Emit amdhsa_code_object_version module flag, which is code object version
823 // times 100.
824 if (opts.CodeObjectVersion != llvm::CodeObjectVersionKind::COV_None) {
825 llvmModule->addModuleFlag(llvm::Module::Error,
826 "amdhsa_code_object_version",
827 opts.CodeObjectVersion);
828 }
829 }
830}
831
832static std::unique_ptr<llvm::raw_pwrite_stream>
833getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
834 BackendActionTy action) {
835 switch (action) {
836 case BackendActionTy::Backend_EmitAssembly:
837 return ci.createDefaultOutputFile(
838 /*Binary=*/false, inFile, /*extension=*/"s");
839 case BackendActionTy::Backend_EmitLL:
840 return ci.createDefaultOutputFile(
841 /*Binary=*/false, inFile, /*extension=*/"ll");
842 case BackendActionTy::Backend_EmitFIR:
843 case BackendActionTy::Backend_EmitHLFIR:
844 return ci.createDefaultOutputFile(
845 /*Binary=*/false, inFile, /*extension=*/"mlir");
846 case BackendActionTy::Backend_EmitBC:
847 return ci.createDefaultOutputFile(
848 /*Binary=*/true, inFile, /*extension=*/"bc");
849 case BackendActionTy::Backend_EmitObj:
850 return ci.createDefaultOutputFile(
851 /*Binary=*/true, inFile, /*extension=*/"o");
852 }
853
854 llvm_unreachable("Invalid action!");
855}
856
857/// Generate target-specific machine-code or assembly file from the input LLVM
858/// module.
859///
860/// \param [in] diags Diagnostics engine for reporting errors
861/// \param [in] tm Target machine to aid the code-gen pipeline set-up
862/// \param [in] act Backend act to run (assembly vs machine-code generation)
863/// \param [in] llvmModule LLVM module to lower to assembly/machine-code
864/// \param [in] codeGenOpts options configuring codegen pipeline
865/// \param [out] os Output stream to emit the generated code to
866static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
867 llvm::TargetMachine &tm,
868 BackendActionTy act,
869 llvm::Module &llvmModule,
870 const CodeGenOptions &codeGenOpts,
871 llvm::raw_pwrite_stream &os) {
872 assert(((act == BackendActionTy::Backend_EmitObj) ||
873 (act == BackendActionTy::Backend_EmitAssembly)) &&
874 "Unsupported action");
875
876 // Set-up the pass manager, i.e create an LLVM code-gen pass pipeline.
877 // Currently only the legacy pass manager is supported.
878 // TODO: Switch to the new PM once it's available in the backend.
879 llvm::legacy::PassManager codeGenPasses;
880 codeGenPasses.add(
881 P: createTargetTransformInfoWrapperPass(TIRA: tm.getTargetIRAnalysis()));
882
883 llvm::Triple triple(llvmModule.getTargetTriple());
884 llvm::TargetLibraryInfoImpl *tlii =
885 llvm::driver::createTLII(triple, codeGenOpts.getVecLib());
886 codeGenPasses.add(P: new llvm::TargetLibraryInfoWrapperPass(*tlii));
887
888 llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly)
889 ? llvm::CodeGenFileType::AssemblyFile
890 : llvm::CodeGenFileType::ObjectFile;
891 if (tm.addPassesToEmitFile(codeGenPasses, os, nullptr, cgft)) {
892 unsigned diagID =
893 diags.getCustomDiagID(L: clang::DiagnosticsEngine::Error,
894 FormatString: "emission of this file type is not supported");
895 diags.Report(DiagID: diagID);
896 return;
897 }
898
899 // Run the passes
900 codeGenPasses.run(M&: llvmModule);
901
902 // Cleanup
903 delete tlii;
904}
905
906void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
907 CompilerInstance &ci = getInstance();
908 const CodeGenOptions &opts = ci.getInvocation().getCodeGenOpts();
909 clang::DiagnosticsEngine &diags = ci.getDiagnostics();
910 llvm::OptimizationLevel level = mapToLevel(opts);
911
912 llvm::TargetMachine *targetMachine = &ci.getTargetMachine();
913 // Create the analysis managers.
914 llvm::LoopAnalysisManager lam;
915 llvm::FunctionAnalysisManager fam;
916 llvm::CGSCCAnalysisManager cgam;
917 llvm::ModuleAnalysisManager mam;
918
919 // Create the pass manager builder.
920 llvm::PassInstrumentationCallbacks pic;
921 llvm::PipelineTuningOptions pto;
922 std::optional<llvm::PGOOptions> pgoOpt;
923
924 if (opts.hasProfileIRInstr()) {
925 // -fprofile-generate.
926 pgoOpt = llvm::PGOOptions(opts.InstrProfileOutput.empty()
927 ? llvm::driver::getDefaultProfileGenName()
928 : opts.InstrProfileOutput,
929 "", "", opts.MemoryProfileUsePath, nullptr,
930 llvm::PGOOptions::IRInstr,
931 llvm::PGOOptions::NoCSAction,
932 llvm::PGOOptions::ColdFuncOpt::Default, false,
933 /*PseudoProbeForProfiling=*/false, false);
934 } else if (opts.hasProfileIRUse()) {
935 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
936 llvm::vfs::getRealFileSystem();
937 // -fprofile-use.
938 auto CSAction = opts.hasProfileCSIRUse() ? llvm::PGOOptions::CSIRUse
939 : llvm::PGOOptions::NoCSAction;
940 pgoOpt = llvm::PGOOptions(
941 opts.ProfileInstrumentUsePath, "", opts.ProfileRemappingFile,
942 opts.MemoryProfileUsePath, VFS, llvm::PGOOptions::IRUse, CSAction,
943 llvm::PGOOptions::ColdFuncOpt::Default, false);
944 }
945
946 llvm::StandardInstrumentations si(llvmModule->getContext(),
947 opts.DebugPassManager);
948 si.registerCallbacks(pic, &mam);
949 if (ci.isTimingEnabled())
950 si.getTimePasses().setOutStream(ci.getTimingStreamLLVM());
951 pto.LoopUnrolling = opts.UnrollLoops;
952 pto.LoopInterchange = opts.InterchangeLoops;
953 pto.LoopInterleaving = opts.UnrollLoops;
954 pto.LoopVectorization = opts.VectorizeLoop;
955 pto.SLPVectorization = opts.VectorizeSLP;
956
957 llvm::PassBuilder pb(targetMachine, pto, pgoOpt, &pic);
958
959 // Attempt to load pass plugins and register their callbacks with PB.
960 for (auto &pluginFile : opts.LLVMPassPlugins) {
961 auto passPlugin = llvm::PassPlugin::Load(pluginFile);
962 if (passPlugin) {
963 passPlugin->registerPassBuilderCallbacks(pb);
964 } else {
965 diags.Report(clang::diag::err_fe_unable_to_load_plugin)
966 << pluginFile << passPlugin.takeError();
967 }
968 }
969 // Register static plugin extensions.
970#define HANDLE_EXTENSION(Ext) \
971 get##Ext##PluginInfo().RegisterPassBuilderCallbacks(pb);
972#include "llvm/Support/Extension.def"
973
974 // Register the target library analysis directly and give it a customized
975 // preset TLI depending on -fveclib
976 llvm::Triple triple(llvmModule->getTargetTriple());
977 llvm::TargetLibraryInfoImpl *tlii =
978 llvm::driver::createTLII(triple, opts.getVecLib());
979 fam.registerPass([&] { return llvm::TargetLibraryAnalysis(*tlii); });
980
981 // Register all the basic analyses with the managers.
982 pb.registerModuleAnalyses(mam);
983 pb.registerCGSCCAnalyses(cgam);
984 pb.registerFunctionAnalyses(fam);
985 pb.registerLoopAnalyses(lam);
986 pb.crossRegisterProxies(lam, fam, cgam, mam);
987
988 // Create the pass manager.
989 llvm::ModulePassManager mpm;
990 if (opts.PrepareForFullLTO)
991 mpm = pb.buildLTOPreLinkDefaultPipeline(level);
992 else if (opts.PrepareForThinLTO)
993 mpm = pb.buildThinLTOPreLinkDefaultPipeline(level);
994 else
995 mpm = pb.buildPerModuleDefaultPipeline(level);
996
997 if (action == BackendActionTy::Backend_EmitBC)
998 mpm.addPass(llvm::BitcodeWriterPass(os));
999 else if (action == BackendActionTy::Backend_EmitLL)
1000 mpm.addPass(llvm::PrintModulePass(os));
1001
1002 // FIXME: This should eventually be replaced by a first-class driver option.
1003 // This should be done for both flang and clang simultaneously.
1004 // Print a textual, '-passes=' compatible, representation of pipeline if
1005 // requested. In this case, don't run the passes. This mimics the behavior of
1006 // clang.
1007 if (llvm::PrintPipelinePasses) {
1008 mpm.printPipeline(llvm::outs(), [&pic](llvm::StringRef className) {
1009 auto passName = pic.getPassNameForClassName(className);
1010 return passName.empty() ? className : passName;
1011 });
1012 llvm::outs() << "\n";
1013 return;
1014 }
1015
1016 // Run the passes.
1017 mpm.run(*llvmModule, mam);
1018
1019 // Print the timers to the associated output stream and reset them.
1020 if (ci.isTimingEnabled())
1021 si.getTimePasses().print();
1022
1023 // Cleanup
1024 delete tlii;
1025}
1026
1027// This class handles optimization remark messages requested if
1028// any of -Rpass, -Rpass-analysis or -Rpass-missed flags were provided
1029class BackendRemarkConsumer : public llvm::DiagnosticHandler {
1030
1031 const CodeGenOptions &codeGenOpts;
1032 clang::DiagnosticsEngine &diags;
1033
1034public:
1035 BackendRemarkConsumer(clang::DiagnosticsEngine &diags,
1036 const CodeGenOptions &codeGenOpts)
1037 : codeGenOpts(codeGenOpts), diags(diags) {}
1038
1039 bool isAnalysisRemarkEnabled(llvm::StringRef passName) const override {
1040 return codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName);
1041 }
1042 bool isMissedOptRemarkEnabled(llvm::StringRef passName) const override {
1043 return codeGenOpts.OptimizationRemarkMissed.patternMatches(passName);
1044 }
1045 bool isPassedOptRemarkEnabled(llvm::StringRef passName) const override {
1046 return codeGenOpts.OptimizationRemark.patternMatches(passName);
1047 }
1048
1049 bool isAnyRemarkEnabled() const override {
1050 return codeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() ||
1051 codeGenOpts.OptimizationRemarkMissed.hasValidPattern() ||
1052 codeGenOpts.OptimizationRemark.hasValidPattern();
1053 }
1054
1055 void
1056 emitOptimizationMessage(const llvm::DiagnosticInfoOptimizationBase &diagInfo,
1057 unsigned diagID) {
1058 // We only support warnings and remarks.
1059 assert(diagInfo.getSeverity() == llvm::DS_Remark ||
1060 diagInfo.getSeverity() == llvm::DS_Warning);
1061
1062 std::string msg;
1063 llvm::raw_string_ostream msgStream(msg);
1064
1065 if (diagInfo.isLocationAvailable()) {
1066 // Clang contains a SourceManager class which handles loading
1067 // and caching of source files into memory and it can be used to
1068 // query SourceLocation data. The SourceLocation data is what is
1069 // needed here as it contains the full include stack which gives
1070 // line and column number as well as file name and location.
1071 // Since Flang doesn't have SourceManager, send file name and absolute
1072 // path through msgStream, to use for printing.
1073 msgStream << diagInfo.getLocationStr() << ";;"
1074 << diagInfo.getAbsolutePath() << ";;";
1075 }
1076
1077 msgStream << diagInfo.getMsg();
1078
1079 // Emit message.
1080 diags.Report(DiagID: diagID) << clang::AddFlagValue(diagInfo.getPassName()) << msg;
1081 }
1082
1083 void optimizationRemarkHandler(
1084 const llvm::DiagnosticInfoOptimizationBase &diagInfo) {
1085 auto passName = diagInfo.getPassName();
1086 if (diagInfo.isPassed()) {
1087 if (codeGenOpts.OptimizationRemark.patternMatches(passName))
1088 // Optimization remarks are active only if the -Rpass flag has a regular
1089 // expression that matches the name of the pass name in \p d.
1090 emitOptimizationMessage(
1091 diagInfo, diagID: clang::diag::remark_fe_backend_optimization_remark);
1092
1093 return;
1094 }
1095
1096 if (diagInfo.isMissed()) {
1097 if (codeGenOpts.OptimizationRemarkMissed.patternMatches(passName))
1098 // Missed optimization remarks are active only if the -Rpass-missed
1099 // flag has a regular expression that matches the name of the pass
1100 // name in \p d.
1101 emitOptimizationMessage(
1102 diagInfo,
1103 diagID: clang::diag::remark_fe_backend_optimization_remark_missed);
1104
1105 return;
1106 }
1107
1108 assert(diagInfo.isAnalysis() && "Unknown remark type");
1109
1110 bool shouldAlwaysPrint = false;
1111 auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(Val: &diagInfo);
1112 if (ora)
1113 shouldAlwaysPrint = ora->shouldAlwaysPrint();
1114
1115 if (shouldAlwaysPrint ||
1116 codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName))
1117 emitOptimizationMessage(
1118 diagInfo,
1119 diagID: clang::diag::remark_fe_backend_optimization_remark_analysis);
1120 }
1121
1122 bool handleDiagnostics(const llvm::DiagnosticInfo &di) override {
1123 switch (di.getKind()) {
1124 case llvm::DK_OptimizationRemark:
1125 optimizationRemarkHandler(diagInfo: llvm::cast<llvm::OptimizationRemark>(Val: di));
1126 break;
1127 case llvm::DK_OptimizationRemarkMissed:
1128 optimizationRemarkHandler(diagInfo: llvm::cast<llvm::OptimizationRemarkMissed>(Val: di));
1129 break;
1130 case llvm::DK_OptimizationRemarkAnalysis:
1131 optimizationRemarkHandler(
1132 diagInfo: llvm::cast<llvm::OptimizationRemarkAnalysis>(Val: di));
1133 break;
1134 case llvm::DK_MachineOptimizationRemark:
1135 optimizationRemarkHandler(
1136 diagInfo: llvm::cast<llvm::MachineOptimizationRemark>(Val: di));
1137 break;
1138 case llvm::DK_MachineOptimizationRemarkMissed:
1139 optimizationRemarkHandler(
1140 diagInfo: llvm::cast<llvm::MachineOptimizationRemarkMissed>(Val: di));
1141 break;
1142 case llvm::DK_MachineOptimizationRemarkAnalysis:
1143 optimizationRemarkHandler(
1144 diagInfo: llvm::cast<llvm::MachineOptimizationRemarkAnalysis>(Val: di));
1145 break;
1146 default:
1147 break;
1148 }
1149 return true;
1150 }
1151};
1152
1153void CodeGenAction::embedOffloadObjects() {
1154 CompilerInstance &ci = this->getInstance();
1155 const auto &cgOpts = ci.getInvocation().getCodeGenOpts();
1156
1157 for (llvm::StringRef offloadObject : cgOpts.OffloadObjects) {
1158 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> objectOrErr =
1159 llvm::MemoryBuffer::getFileOrSTDIN(offloadObject);
1160 if (std::error_code ec = objectOrErr.getError()) {
1161 auto diagID = ci.getDiagnostics().getCustomDiagID(
1162 clang::DiagnosticsEngine::Error, "could not open '%0' for embedding");
1163 ci.getDiagnostics().Report(diagID) << offloadObject;
1164 return;
1165 }
1166 llvm::embedBufferInModule(
1167 *llvmModule, **objectOrErr, ".llvm.offloading",
1168 llvm::Align(llvm::object::OffloadBinary::getAlignment()));
1169 }
1170}
1171
1172void CodeGenAction::linkBuiltinBCLibs() {
1173 auto options = clang::FileSystemOptions();
1174 clang::FileManager fileManager(options);
1175 CompilerInstance &ci = this->getInstance();
1176 const auto &cgOpts = ci.getInvocation().getCodeGenOpts();
1177
1178 std::vector<std::unique_ptr<llvm::Module>> modules;
1179
1180 // Load LLVM modules
1181 for (llvm::StringRef bcLib : cgOpts.BuiltinBCLibs) {
1182 auto BCBuf = fileManager.getBufferForFile(bcLib);
1183 if (!BCBuf) {
1184 auto diagID = ci.getDiagnostics().getCustomDiagID(
1185 clang::DiagnosticsEngine::Error, "could not open '%0' for linking");
1186 ci.getDiagnostics().Report(diagID) << bcLib;
1187 return;
1188 }
1189
1190 llvm::Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
1191 getOwningLazyBitcodeModule(std::move(*BCBuf), *llvmCtx);
1192 if (!ModuleOrErr) {
1193 auto diagID = ci.getDiagnostics().getCustomDiagID(
1194 clang::DiagnosticsEngine::Error, "error loading '%0' for linking");
1195 ci.getDiagnostics().Report(diagID) << bcLib;
1196 return;
1197 }
1198 modules.push_back(std::move(ModuleOrErr.get()));
1199 }
1200
1201 // Link modules and internalize functions
1202 for (auto &module : modules) {
1203 bool Err;
1204 Err = llvm::Linker::linkModules(
1205 *llvmModule, std::move(module), llvm::Linker::Flags::LinkOnlyNeeded,
1206 [](llvm::Module &M, const llvm::StringSet<> &GVS) {
1207 llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
1208 return !GV.hasName() || (GVS.count(GV.getName()) == 0);
1209 });
1210 });
1211 if (Err) {
1212 auto diagID = ci.getDiagnostics().getCustomDiagID(
1213 clang::DiagnosticsEngine::Error, "link error when linking '%0'");
1214 ci.getDiagnostics().Report(diagID) << module->getSourceFileName();
1215 return;
1216 }
1217 }
1218}
1219
1220static void reportOptRecordError(llvm::Error e, clang::DiagnosticsEngine &diags,
1221 const CodeGenOptions &codeGenOpts) {
1222 handleAllErrors(
1223 E: std::move(e),
1224 Handlers: [&](const llvm::LLVMRemarkSetupFileError &e) {
1225 diags.Report(DiagID: clang::diag::err_cannot_open_file)
1226 << codeGenOpts.OptRecordFile << e.message();
1227 },
1228 Handlers: [&](const llvm::LLVMRemarkSetupPatternError &e) {
1229 diags.Report(DiagID: clang::diag::err_drv_optimization_remark_pattern)
1230 << e.message() << codeGenOpts.OptRecordPasses;
1231 },
1232 Handlers: [&](const llvm::LLVMRemarkSetupFormatError &e) {
1233 diags.Report(DiagID: clang::diag::err_drv_optimization_remark_format)
1234 << codeGenOpts.OptRecordFormat;
1235 });
1236}
1237
1238void CodeGenAction::executeAction() {
1239 CompilerInstance &ci = this->getInstance();
1240
1241 clang::DiagnosticsEngine &diags = ci.getDiagnostics();
1242 const CodeGenOptions &codeGenOpts = ci.getInvocation().getCodeGenOpts();
1243 const TargetOptions &targetOpts = ci.getInvocation().getTargetOpts();
1244 Fortran::lower::LoweringOptions &loweringOpts =
1245 ci.getInvocation().getLoweringOpts();
1246 mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
1247 mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
1248
1249 // If the output stream is a file, generate it and define the corresponding
1250 // output stream. If a pre-defined output stream is available, we will use
1251 // that instead.
1252 //
1253 // NOTE: `os` is a smart pointer that will be destroyed at the end of this
1254 // method. However, it won't be written to until `codeGenPasses` is
1255 // destroyed. By defining `os` before `codeGenPasses`, we make sure that the
1256 // output stream won't be destroyed before it is written to. This only
1257 // applies when an output file is used (i.e. there is no pre-defined output
1258 // stream).
1259 // TODO: Revisit once the new PM is ready (i.e. when `codeGenPasses` is
1260 // updated to use it).
1261 std::unique_ptr<llvm::raw_pwrite_stream> os;
1262 if (ci.isOutputStreamNull()) {
1263 os = getOutputStream(ci, getCurrentFileOrBufferName(), action);
1264
1265 if (!os) {
1266 unsigned diagID = diags.getCustomDiagID(
1267 clang::DiagnosticsEngine::Error, "failed to create the output file");
1268 diags.Report(diagID);
1269 return;
1270 }
1271 }
1272
1273 if (action == BackendActionTy::Backend_EmitFIR) {
1274 if (loweringOpts.getLowerToHighLevelFIR()) {
1275 lowerHLFIRToFIR();
1276 }
1277 mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1278 return;
1279 }
1280
1281 if (action == BackendActionTy::Backend_EmitHLFIR) {
1282 assert(loweringOpts.getLowerToHighLevelFIR() &&
1283 "Lowering must have been configured to emit HLFIR");
1284 mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1285 return;
1286 }
1287
1288 // Generate an LLVM module if it's not already present (it will already be
1289 // present if the input file is an LLVM IR/BC file).
1290 if (!llvmModule)
1291 generateLLVMIR();
1292
1293 // This will already have been started in generateLLVMIR(). But we need to
1294 // continue operating on the module, so we continue timing it.
1295 mlir::TimingScope timingScopeLLVMIRGen = timingScopeRoot.nest(
1296 mlir::TimingIdentifier::get(timingIdLLVMIRGen, timingMgr));
1297
1298 // If generating the LLVM module failed, abort! No need for further error
1299 // reporting since generateLLVMIR() does this already.
1300 if (!llvmModule)
1301 return;
1302
1303 // Set the triple based on the targetmachine (this comes compiler invocation
1304 // and the command-line target option if specified, or the default if not
1305 // given on the command-line).
1306 llvm::TargetMachine &targetMachine = ci.getTargetMachine();
1307
1308 targetMachine.Options.MCOptions.AsmVerbose = targetOpts.asmVerbose;
1309
1310 const llvm::Triple &theTriple = targetMachine.getTargetTriple();
1311
1312 if (llvmModule->getTargetTriple() != theTriple) {
1313 diags.Report(clang::diag::warn_fe_override_module) << theTriple.str();
1314 }
1315
1316 // Always set the triple and data layout, to make sure they match and are set.
1317 // Note that this overwrites any datalayout stored in the LLVM-IR. This avoids
1318 // an assert for incompatible data layout when the code-generation happens.
1319 llvmModule->setTargetTriple(theTriple);
1320 llvmModule->setDataLayout(targetMachine.createDataLayout());
1321
1322 // Link in builtin bitcode libraries
1323 if (!codeGenOpts.BuiltinBCLibs.empty())
1324 linkBuiltinBCLibs();
1325
1326 // Embed offload objects specified with -fembed-offload-object
1327 if (!codeGenOpts.OffloadObjects.empty())
1328 embedOffloadObjects();
1329 timingScopeLLVMIRGen.stop();
1330
1331 BackendRemarkConsumer remarkConsumer(diags, codeGenOpts);
1332
1333 llvmModule->getContext().setDiagnosticHandler(
1334 std::make_unique<BackendRemarkConsumer>(remarkConsumer));
1335
1336 // write optimization-record
1337 llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
1338 setupLLVMOptimizationRemarks(
1339 llvmModule->getContext(), codeGenOpts.OptRecordFile,
1340 codeGenOpts.OptRecordPasses, codeGenOpts.OptRecordFormat,
1341 /*DiagnosticsWithHotness=*/false,
1342 /*DiagnosticsHotnessThreshold=*/0);
1343
1344 if (llvm::Error e = optRecordFileOrErr.takeError()) {
1345 reportOptRecordError(std::move(e), diags, codeGenOpts);
1346 return;
1347 }
1348
1349 std::unique_ptr<llvm::ToolOutputFile> optRecordFile =
1350 std::move(*optRecordFileOrErr);
1351
1352 if (optRecordFile) {
1353 optRecordFile->keep();
1354 optRecordFile->os().flush();
1355 }
1356
1357 // Run LLVM's middle-end (i.e. the optimizer).
1358 mlir::TimingScope timingScopeLLVMIRPasses = timingScopeRoot.nest(
1359 mlir::TimingIdentifier::get(timingIdLLVMIRPasses, timingMgr));
1360 runOptimizationPipeline(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1361 timingScopeLLVMIRPasses.stop();
1362
1363 if (action == BackendActionTy::Backend_EmitLL ||
1364 action == BackendActionTy::Backend_EmitBC) {
1365 // This action has effectively been completed in runOptimizationPipeline.
1366 return;
1367 }
1368
1369 // Run LLVM's backend and generate either assembly or machine code
1370 mlir::TimingScope timingScopeBackend = timingScopeRoot.nest(
1371 mlir::TimingIdentifier::get(timingIdBackend, timingMgr));
1372 if (action == BackendActionTy::Backend_EmitAssembly ||
1373 action == BackendActionTy::Backend_EmitObj) {
1374 generateMachineCodeOrAssemblyImpl(
1375 diags, targetMachine, action, *llvmModule, codeGenOpts,
1376 ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1377 if (timingMgr.isEnabled())
1378 llvm::reportAndResetTimings(&ci.getTimingStreamCodeGen());
1379 return;
1380 }
1381}
1382
1383void InitOnlyAction::executeAction() {
1384 CompilerInstance &ci = this->getInstance();
1385 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
1386 clang::DiagnosticsEngine::Warning,
1387 "Use `-init-only` for testing purposes only");
1388 ci.getDiagnostics().Report(diagID);
1389}
1390
1391void PluginParseTreeAction::executeAction() {}
1392
1393void DebugDumpPFTAction::executeAction() {
1394 dumpPreFIRTree(this->getInstance());
1395}
1396
1397Fortran::parser::Parsing &PluginParseTreeAction::getParsing() {
1398 return getInstance().getParsing();
1399}
1400
1401std::unique_ptr<llvm::raw_pwrite_stream>
1402PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") {
1403
1404 std::unique_ptr<llvm::raw_pwrite_stream> os{
1405 getInstance().createDefaultOutputFile(
1406 /*Binary=*/false, /*InFile=*/getCurrentFileOrBufferName(),
1407 extension)};
1408 return os;
1409}
1410

source code of flang/lib/Frontend/FrontendActions.cpp