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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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