[ORC] Fail materialization in tasks that are destroyed before running.
[llvm-project.git] / flang / lib / Frontend / FrontendActions.cpp
blob310cd650349c7afb9eac611f56a55a2331c248f3
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/
11 //===----------------------------------------------------------------------===//
13 #include "flang/Frontend/FrontendActions.h"
14 #include "flang/Common/default-kinds.h"
15 #include "flang/Frontend/CompilerInstance.h"
16 #include "flang/Frontend/CompilerInvocation.h"
17 #include "flang/Frontend/FrontendOptions.h"
18 #include "flang/Frontend/PreprocessorOptions.h"
19 #include "flang/Lower/Bridge.h"
20 #include "flang/Lower/PFTBuilder.h"
21 #include "flang/Lower/Support/Verifier.h"
22 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
23 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
24 #include "flang/Optimizer/Passes/Pipelines.h"
25 #include "flang/Optimizer/Support/DataLayout.h"
26 #include "flang/Optimizer/Support/InitFIR.h"
27 #include "flang/Optimizer/Support/Utils.h"
28 #include "flang/Optimizer/Transforms/Passes.h"
29 #include "flang/Parser/dump-parse-tree.h"
30 #include "flang/Parser/parsing.h"
31 #include "flang/Parser/provenance.h"
32 #include "flang/Parser/source.h"
33 #include "flang/Parser/unparse.h"
34 #include "flang/Semantics/runtime-type-info.h"
35 #include "flang/Semantics/semantics.h"
36 #include "flang/Semantics/unparse-with-symbols.h"
37 #include "flang/Tools/CrossToolHelpers.h"
39 #include "mlir/IR/Dialect.h"
40 #include "mlir/Parser/Parser.h"
41 #include "mlir/Pass/PassManager.h"
42 #include "mlir/Support/LLVM.h"
43 #include "mlir/Target/LLVMIR/Import.h"
44 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
45 #include "clang/Basic/Diagnostic.h"
46 #include "clang/Basic/DiagnosticFrontend.h"
47 #include "clang/Basic/FileManager.h"
48 #include "clang/Basic/FileSystemOptions.h"
49 #include "clang/Driver/DriverDiagnostic.h"
50 #include "llvm/ADT/SmallString.h"
51 #include "llvm/ADT/StringRef.h"
52 #include "llvm/Analysis/TargetLibraryInfo.h"
53 #include "llvm/Analysis/TargetTransformInfo.h"
54 #include "llvm/Bitcode/BitcodeWriterPass.h"
55 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
56 #include "llvm/IR/LLVMRemarkStreamer.h"
57 #include "llvm/IR/LegacyPassManager.h"
58 #include "llvm/IR/Verifier.h"
59 #include "llvm/IRPrinter/IRPrintingPasses.h"
60 #include "llvm/IRReader/IRReader.h"
61 #include "llvm/Linker/Linker.h"
62 #include "llvm/Object/OffloadBinary.h"
63 #include "llvm/Passes/PassBuilder.h"
64 #include "llvm/Passes/PassPlugin.h"
65 #include "llvm/Passes/StandardInstrumentations.h"
66 #include "llvm/Support/AMDGPUAddrSpace.h"
67 #include "llvm/Support/Error.h"
68 #include "llvm/Support/ErrorHandling.h"
69 #include "llvm/Support/FileSystem.h"
70 #include "llvm/Support/Path.h"
71 #include "llvm/Support/SourceMgr.h"
72 #include "llvm/Support/ToolOutputFile.h"
73 #include "llvm/Target/TargetMachine.h"
74 #include "llvm/TargetParser/RISCVISAInfo.h"
75 #include "llvm/TargetParser/RISCVTargetParser.h"
76 #include "llvm/Transforms/IPO/Internalize.h"
77 #include "llvm/Transforms/Utils/ModuleUtils.h"
78 #include <memory>
79 #include <system_error>
81 namespace llvm {
82 extern cl::opt<bool> PrintPipelinePasses;
83 } // namespace llvm
85 using namespace Fortran::frontend;
87 // Declare plugin extension function declarations.
88 #define HANDLE_EXTENSION(Ext) \
89 llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
90 #include "llvm/Support/Extension.def"
92 /// Save the given \c mlirModule to a temporary .mlir file, in a location
93 /// decided by the -save-temps flag. No files are produced if the flag is not
94 /// specified.
95 static bool saveMLIRTempFile(const CompilerInvocation &ci,
96 mlir::ModuleOp mlirModule,
97 llvm::StringRef inputFile,
98 llvm::StringRef outputTag) {
99 if (!ci.getCodeGenOpts().SaveTempsDir.has_value())
100 return true;
102 const llvm::StringRef compilerOutFile = ci.getFrontendOpts().outputFile;
103 const llvm::StringRef saveTempsDir = ci.getCodeGenOpts().SaveTempsDir.value();
104 auto dir = llvm::StringSwitch<llvm::StringRef>(saveTempsDir)
105 .Case("cwd", "")
106 .Case("obj", llvm::sys::path::parent_path(compilerOutFile))
107 .Default(saveTempsDir);
109 // Build path from the compiler output file name, triple, cpu and OpenMP
110 // information
111 llvm::SmallString<256> path(dir);
112 llvm::sys::path::append(path, llvm::sys::path::stem(inputFile) + "-" +
113 outputTag + ".mlir");
115 std::error_code ec;
116 llvm::ToolOutputFile out(path, ec, llvm::sys::fs::OF_Text);
117 if (ec)
118 return false;
120 mlirModule->print(out.os());
121 out.os().close();
122 out.keep();
124 return true;
127 //===----------------------------------------------------------------------===//
128 // Custom BeginSourceFileAction
129 //===----------------------------------------------------------------------===//
131 bool PrescanAction::beginSourceFileAction() { return runPrescan(); }
133 bool PrescanAndParseAction::beginSourceFileAction() {
134 return runPrescan() && runParse(/*emitMessages=*/true);
137 bool PrescanAndSemaAction::beginSourceFileAction() {
138 return runPrescan() && runParse(/*emitMessages=*/false) &&
139 runSemanticChecks() && generateRtTypeTables();
142 bool PrescanAndSemaDebugAction::beginSourceFileAction() {
143 // This is a "debug" action for development purposes. To facilitate this, the
144 // semantic checks are made to succeed unconditionally to prevent this action
145 // from exiting early (i.e. in the presence of semantic errors). We should
146 // never do this in actions intended for end-users or otherwise regular
147 // compiler workflows!
148 return runPrescan() && runParse(/*emitMessages=*/false) &&
149 (runSemanticChecks() || true) && (generateRtTypeTables() || true);
152 static void addDependentLibs(mlir::ModuleOp mlirModule, CompilerInstance &ci) {
153 const std::vector<std::string> &libs =
154 ci.getInvocation().getCodeGenOpts().DependentLibs;
155 if (libs.empty()) {
156 return;
158 // dependent-lib is currently only supported on Windows, so the list should be
159 // empty on non-Windows platforms
160 assert(
161 llvm::Triple(ci.getInvocation().getTargetOpts().triple).isOSWindows() &&
162 "--dependent-lib is only supported on Windows");
163 // Add linker options specified by --dependent-lib
164 auto builder = mlir::OpBuilder(mlirModule.getRegion());
165 for (const std::string &lib : libs) {
166 builder.create<mlir::LLVM::LinkerOptionsOp>(
167 mlirModule.getLoc(), builder.getStrArrayAttr({"/DEFAULTLIB:" + lib}));
171 // Add to MLIR code target specific items which are dependent on target
172 // configuration specified by the user.
173 // Clang equivalent function: AMDGPUTargetCodeGenInfo::emitTargetGlobals
174 static void addAMDGPUSpecificMLIRItems(mlir::ModuleOp mlirModule,
175 CompilerInstance &ci) {
176 const TargetOptions &targetOpts = ci.getInvocation().getTargetOpts();
177 const llvm::Triple triple(targetOpts.triple);
178 const llvm::StringRef codeObjectVersionGlobalOpName = "__oclc_ABI_version";
180 if (!triple.isAMDGPU()) {
181 return;
183 const CodeGenOptions &codeGenOpts = ci.getInvocation().getCodeGenOpts();
184 if (codeGenOpts.CodeObjectVersion == llvm::CodeObjectVersionKind::COV_None) {
185 return;
188 mlir::IRRewriter builder(mlirModule.getContext());
189 unsigned oclcABIVERsion = codeGenOpts.CodeObjectVersion;
190 auto int32Type = builder.getI32Type();
192 std::optional<mlir::LLVM::GlobalOp> originalGV;
194 mlirModule.walk([&originalGV, codeObjectVersionGlobalOpName](
195 mlir::LLVM::GlobalOp globalOp) {
196 if (globalOp.getName() == codeObjectVersionGlobalOpName)
197 originalGV = globalOp;
199 if (originalGV.has_value()) {
200 mlir::LLVM::GlobalOp originalGVOp = originalGV.value();
201 if (originalGVOp.getLinkage() != mlir::LLVM::Linkage::External) {
202 return;
204 // Update the variable if it is already present in MLIR but it was marked
205 // as external linkage variable
206 originalGVOp.setLinkage(mlir::LLVM::Linkage::WeakODR);
207 originalGVOp.setValueAttr(
208 builder.getIntegerAttr(int32Type, oclcABIVERsion));
209 originalGVOp.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Local);
210 originalGVOp.setAddrSpace(llvm::AMDGPUAS::CONSTANT_ADDRESS);
211 originalGVOp.setVisibility_(mlir::LLVM::Visibility::Hidden);
212 return;
215 mlir::LLVM::GlobalOp covInfo = builder.create<mlir::LLVM::GlobalOp>(
216 /* Location */ mlirModule.getLoc(), /* Type */ int32Type,
217 /* IsConstant */ true, /* Linkage */ mlir::LLVM::Linkage::WeakODR,
218 /* Name */ codeObjectVersionGlobalOpName,
219 /* Value */ builder.getIntegerAttr(int32Type, oclcABIVERsion));
220 covInfo.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Local);
221 covInfo.setAddrSpace(llvm::AMDGPUAS::CONSTANT_ADDRESS);
222 covInfo.setVisibility_(mlir::LLVM::Visibility::Hidden);
223 builder.setInsertionPointToStart(mlirModule.getBody());
224 builder.insert(covInfo);
227 bool CodeGenAction::beginSourceFileAction() {
228 llvmCtx = std::make_unique<llvm::LLVMContext>();
229 CompilerInstance &ci = this->getInstance();
231 // If the input is an LLVM file, just parse it and return.
232 if (this->getCurrentInput().getKind().getLanguage() == Language::LLVM_IR) {
233 llvm::SMDiagnostic err;
234 llvmModule = llvm::parseIRFile(getCurrentInput().getFile(), err, *llvmCtx);
235 if (!llvmModule || llvm::verifyModule(*llvmModule, &llvm::errs())) {
236 err.print("flang", llvm::errs());
237 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
238 clang::DiagnosticsEngine::Error, "Could not parse IR");
239 ci.getDiagnostics().Report(diagID);
240 return false;
243 return true;
246 // Load the MLIR dialects required by Flang
247 mlir::DialectRegistry registry;
248 mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
249 fir::support::registerNonCodegenDialects(registry);
250 fir::support::loadNonCodegenDialects(*mlirCtx);
251 fir::support::loadDialects(*mlirCtx);
252 fir::support::registerLLVMTranslation(*mlirCtx);
254 const llvm::TargetMachine &targetMachine = ci.getTargetMachine();
256 // If the input is an MLIR file, just parse it and return.
257 if (this->getCurrentInput().getKind().getLanguage() == Language::MLIR) {
258 llvm::SourceMgr sourceMgr;
259 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> fileOrErr =
260 llvm::MemoryBuffer::getFileOrSTDIN(getCurrentInput().getFile());
261 sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc());
262 mlir::OwningOpRef<mlir::ModuleOp> module =
263 mlir::parseSourceFile<mlir::ModuleOp>(sourceMgr, mlirCtx.get());
265 if (!module || mlir::failed(module->verifyInvariants())) {
266 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
267 clang::DiagnosticsEngine::Error, "Could not parse FIR");
268 ci.getDiagnostics().Report(diagID);
269 return false;
272 mlirModule = std::move(module);
273 const llvm::DataLayout &dl = targetMachine.createDataLayout();
274 fir::support::setMLIRDataLayout(*mlirModule, dl);
275 return true;
278 // Otherwise, generate an MLIR module from the input Fortran source
279 if (getCurrentInput().getKind().getLanguage() != Language::Fortran) {
280 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
281 clang::DiagnosticsEngine::Error,
282 "Invalid input type - expecting a Fortran file");
283 ci.getDiagnostics().Report(diagID);
284 return false;
286 bool res = runPrescan() && runParse(/*emitMessages=*/false) &&
287 runSemanticChecks() && generateRtTypeTables();
288 if (!res)
289 return res;
291 // Create a LoweringBridge
292 const common::IntrinsicTypeDefaultKinds &defKinds =
293 ci.getSemanticsContext().defaultKinds();
294 fir::KindMapping kindMap(mlirCtx.get(), llvm::ArrayRef<fir::KindTy>{
295 fir::fromDefaultKinds(defKinds)});
296 lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(
297 *mlirCtx, ci.getSemanticsContext(), defKinds,
298 ci.getSemanticsContext().intrinsics(),
299 ci.getSemanticsContext().targetCharacteristics(),
300 ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
301 kindMap, ci.getInvocation().getLoweringOpts(),
302 ci.getInvocation().getFrontendOpts().envDefaults,
303 ci.getInvocation().getFrontendOpts().features, targetMachine,
304 ci.getInvocation().getTargetOpts(), ci.getInvocation().getCodeGenOpts());
306 if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
307 Fortran::common::LanguageFeature::OpenMP)) {
308 setOffloadModuleInterfaceAttributes(lb.getModule(),
309 ci.getInvocation().getLangOpts());
310 setOpenMPVersionAttribute(lb.getModule(),
311 ci.getInvocation().getLangOpts().OpenMPVersion);
314 // Create a parse tree and lower it to FIR
315 Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()};
316 lb.lower(parseTree, ci.getSemanticsContext());
318 // Fetch module from lb, so we can set
319 mlirModule = lb.getModuleAndRelease();
321 // Add target specific items like dependent libraries, target specific
322 // constants etc.
323 addDependentLibs(*mlirModule, ci);
324 addAMDGPUSpecificMLIRItems(*mlirModule, ci);
326 // run the default passes.
327 mlir::PassManager pm((*mlirModule)->getName(),
328 mlir::OpPassManager::Nesting::Implicit);
329 (void)mlir::applyPassManagerCLOptions(pm);
330 // Add OpenMP-related passes
331 // WARNING: These passes must be run immediately after the lowering to ensure
332 // that the FIR is correct with respect to OpenMP operations/attributes.
333 if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
334 Fortran::common::LanguageFeature::OpenMP)) {
335 bool isDevice = false;
336 if (auto offloadMod = llvm::dyn_cast<mlir::omp::OffloadModuleInterface>(
337 mlirModule->getOperation()))
338 isDevice = offloadMod.getIsTargetDevice();
339 // WARNING: This pipeline must be run immediately after the lowering to
340 // ensure that the FIR is correct with respect to OpenMP operations/
341 // attributes.
342 fir::createOpenMPFIRPassPipeline(pm, isDevice);
345 pm.enableVerifier(/*verifyPasses=*/true);
346 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
348 if (mlir::failed(pm.run(*mlirModule))) {
349 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
350 clang::DiagnosticsEngine::Error,
351 "verification of lowering to FIR failed");
352 ci.getDiagnostics().Report(diagID);
353 return false;
356 // Print initial full MLIR module, before lowering or transformations, if
357 // -save-temps has been specified.
358 if (!saveMLIRTempFile(ci.getInvocation(), *mlirModule, getCurrentFile(),
359 "fir")) {
360 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
361 clang::DiagnosticsEngine::Error, "Saving MLIR temp file failed");
362 ci.getDiagnostics().Report(diagID);
363 return false;
366 return true;
369 //===----------------------------------------------------------------------===//
370 // Custom ExecuteAction
371 //===----------------------------------------------------------------------===//
372 void InputOutputTestAction::executeAction() {
373 CompilerInstance &ci = getInstance();
375 // Create a stream for errors
376 std::string buf;
377 llvm::raw_string_ostream errorStream{buf};
379 // Read the input file
380 Fortran::parser::AllSources &allSources{ci.getAllSources()};
381 std::string path{getCurrentFileOrBufferName()};
382 const Fortran::parser::SourceFile *sf;
383 if (path == "-")
384 sf = allSources.ReadStandardInput(errorStream);
385 else
386 sf = allSources.Open(path, errorStream, std::optional<std::string>{"."s});
387 llvm::ArrayRef<char> fileContent = sf->content();
389 // Output file descriptor to receive the contents of the input file.
390 std::unique_ptr<llvm::raw_ostream> os;
392 // Copy the contents from the input file to the output file
393 if (!ci.isOutputStreamNull()) {
394 // An output stream (outputStream_) was set earlier
395 ci.writeOutputStream(fileContent.data());
396 } else {
397 // No pre-set output stream - create an output file
398 os = ci.createDefaultOutputFile(
399 /*binary=*/true, getCurrentFileOrBufferName(), "txt");
400 if (!os)
401 return;
402 (*os) << fileContent.data();
406 void PrintPreprocessedAction::executeAction() {
407 std::string buf;
408 llvm::raw_string_ostream outForPP{buf};
410 // Format or dump the prescanner's output
411 CompilerInstance &ci = this->getInstance();
412 if (ci.getInvocation().getPreprocessorOpts().showMacros) {
413 ci.getParsing().EmitPreprocessorMacros(outForPP);
414 } else if (ci.getInvocation().getPreprocessorOpts().noReformat) {
415 ci.getParsing().DumpCookedChars(outForPP);
416 } else {
417 ci.getParsing().EmitPreprocessedSource(
418 outForPP, !ci.getInvocation().getPreprocessorOpts().noLineDirectives);
421 // Print getDiagnostics from the prescanner
422 ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
424 // If a pre-defined output stream exists, dump the preprocessed content there
425 if (!ci.isOutputStreamNull()) {
426 // Send the output to the pre-defined output buffer.
427 ci.writeOutputStream(buf);
428 return;
431 // Create a file and save the preprocessed output there
432 std::unique_ptr<llvm::raw_pwrite_stream> os{ci.createDefaultOutputFile(
433 /*Binary=*/true, /*InFile=*/getCurrentFileOrBufferName())};
434 if (!os) {
435 return;
438 (*os) << buf;
441 void DebugDumpProvenanceAction::executeAction() {
442 this->getInstance().getParsing().DumpProvenance(llvm::outs());
445 void ParseSyntaxOnlyAction::executeAction() {}
447 void DebugUnparseNoSemaAction::executeAction() {
448 auto &invoc = this->getInstance().getInvocation();
449 auto &parseTree{getInstance().getParsing().parseTree()};
451 // TODO: Options should come from CompilerInvocation
452 Unparse(llvm::outs(), *parseTree,
453 /*encoding=*/Fortran::parser::Encoding::UTF_8,
454 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
455 /*preStatement=*/nullptr,
456 invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
457 : nullptr);
460 void DebugUnparseAction::executeAction() {
461 auto &invoc = this->getInstance().getInvocation();
462 auto &parseTree{getInstance().getParsing().parseTree()};
464 CompilerInstance &ci = this->getInstance();
465 auto os{ci.createDefaultOutputFile(
466 /*Binary=*/false, /*InFile=*/getCurrentFileOrBufferName())};
468 // TODO: Options should come from CompilerInvocation
469 Unparse(*os, *parseTree,
470 /*encoding=*/Fortran::parser::Encoding::UTF_8,
471 /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
472 /*preStatement=*/nullptr,
473 invoc.getUseAnalyzedObjectsForUnparse() ? &invoc.getAsFortran()
474 : nullptr);
476 // Report fatal semantic errors
477 reportFatalSemanticErrors();
480 void DebugUnparseWithSymbolsAction::executeAction() {
481 auto &parseTree{*getInstance().getParsing().parseTree()};
483 Fortran::semantics::UnparseWithSymbols(
484 llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
486 // Report fatal semantic errors
487 reportFatalSemanticErrors();
490 void DebugUnparseWithModulesAction::executeAction() {
491 auto &parseTree{*getInstance().getParsing().parseTree()};
492 CompilerInstance &ci{getInstance()};
493 Fortran::semantics::UnparseWithModules(
494 llvm::outs(), ci.getSemantics().context(), parseTree,
495 /*encoding=*/Fortran::parser::Encoding::UTF_8);
496 reportFatalSemanticErrors();
499 void DebugDumpSymbolsAction::executeAction() {
500 CompilerInstance &ci = this->getInstance();
502 if (!ci.getRtTyTables().schemata) {
503 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
504 clang::DiagnosticsEngine::Error,
505 "could not find module file for __fortran_type_info");
506 ci.getDiagnostics().Report(diagID);
507 llvm::errs() << "\n";
508 return;
511 // Dump symbols
512 ci.getSemantics().DumpSymbols(llvm::outs());
515 void DebugDumpAllAction::executeAction() {
516 CompilerInstance &ci = this->getInstance();
518 // Dump parse tree
519 auto &parseTree{getInstance().getParsing().parseTree()};
520 llvm::outs() << "========================";
521 llvm::outs() << " Flang: parse tree dump ";
522 llvm::outs() << "========================\n";
523 Fortran::parser::DumpTree(llvm::outs(), parseTree,
524 &ci.getInvocation().getAsFortran());
526 if (!ci.getRtTyTables().schemata) {
527 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
528 clang::DiagnosticsEngine::Error,
529 "could not find module file for __fortran_type_info");
530 ci.getDiagnostics().Report(diagID);
531 llvm::errs() << "\n";
532 return;
535 // Dump symbols
536 llvm::outs() << "=====================";
537 llvm::outs() << " Flang: symbols dump ";
538 llvm::outs() << "=====================\n";
539 ci.getSemantics().DumpSymbols(llvm::outs());
542 void DebugDumpParseTreeNoSemaAction::executeAction() {
543 auto &parseTree{getInstance().getParsing().parseTree()};
545 // Dump parse tree
546 Fortran::parser::DumpTree(
547 llvm::outs(), parseTree,
548 &this->getInstance().getInvocation().getAsFortran());
551 void DebugDumpParseTreeAction::executeAction() {
552 auto &parseTree{getInstance().getParsing().parseTree()};
554 // Dump parse tree
555 Fortran::parser::DumpTree(
556 llvm::outs(), parseTree,
557 &this->getInstance().getInvocation().getAsFortran());
559 // Report fatal semantic errors
560 reportFatalSemanticErrors();
563 void DebugMeasureParseTreeAction::executeAction() {
564 CompilerInstance &ci = this->getInstance();
566 // Parse. In case of failure, report and return.
567 ci.getParsing().Parse(llvm::outs());
569 if ((ci.getParsing().parseTree().has_value() &&
570 !ci.getParsing().consumedWholeFile()) ||
571 (!ci.getParsing().messages().empty() &&
572 (ci.getInvocation().getWarnAsErr() ||
573 ci.getParsing().messages().AnyFatalError()))) {
574 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
575 clang::DiagnosticsEngine::Error, "Could not parse %0");
576 ci.getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();
578 ci.getParsing().messages().Emit(llvm::errs(),
579 this->getInstance().getAllCookedSources());
580 return;
583 // Report the getDiagnostics from parsing
584 ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources());
586 auto &parseTree{*ci.getParsing().parseTree()};
588 // Measure the parse tree
589 MeasurementVisitor visitor;
590 Fortran::parser::Walk(parseTree, visitor);
591 llvm::outs() << "Parse tree comprises " << visitor.objects
592 << " objects and occupies " << visitor.bytes
593 << " total bytes.\n";
596 void DebugPreFIRTreeAction::executeAction() {
597 CompilerInstance &ci = this->getInstance();
598 // Report and exit if fatal semantic errors are present
599 if (reportFatalSemanticErrors()) {
600 return;
603 auto &parseTree{*ci.getParsing().parseTree()};
605 // Dump pre-FIR tree
606 if (auto ast{
607 Fortran::lower::createPFT(parseTree, ci.getSemanticsContext())}) {
608 Fortran::lower::dumpPFT(llvm::outs(), *ast);
609 } else {
610 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
611 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
612 ci.getDiagnostics().Report(diagID);
616 void DebugDumpParsingLogAction::executeAction() {
617 CompilerInstance &ci = this->getInstance();
619 ci.getParsing().Parse(llvm::errs());
620 ci.getParsing().DumpParsingLog(llvm::outs());
623 void GetDefinitionAction::executeAction() {
624 CompilerInstance &ci = this->getInstance();
626 // Report and exit if fatal semantic errors are present
627 if (reportFatalSemanticErrors()) {
628 return;
631 parser::AllCookedSources &cs = ci.getAllCookedSources();
632 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
633 clang::DiagnosticsEngine::Error, "Symbol not found");
635 auto gdv = ci.getInvocation().getFrontendOpts().getDefVals;
636 auto charBlock{cs.GetCharBlockFromLineAndColumns(gdv.line, gdv.startColumn,
637 gdv.endColumn)};
638 if (!charBlock) {
639 ci.getDiagnostics().Report(diagID);
640 return;
643 llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
645 auto *symbol{
646 ci.getSemanticsContext().FindScope(*charBlock).FindSymbol(*charBlock)};
647 if (!symbol) {
648 ci.getDiagnostics().Report(diagID);
649 return;
652 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
654 auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
655 if (!sourceInfo) {
656 llvm_unreachable(
657 "Failed to obtain SourcePosition."
658 "TODO: Please, write a test and replace this with a diagnostic!");
659 return;
662 llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
663 llvm::outs() << symbol->name().ToString() << ": " << sourceInfo->first.path
664 << ", " << sourceInfo->first.line << ", "
665 << sourceInfo->first.column << "-" << sourceInfo->second.column
666 << "\n";
669 void GetSymbolsSourcesAction::executeAction() {
670 CompilerInstance &ci = this->getInstance();
672 // Report and exit if fatal semantic errors are present
673 if (reportFatalSemanticErrors()) {
674 return;
677 ci.getSemantics().DumpSymbolsSources(llvm::outs());
680 //===----------------------------------------------------------------------===//
681 // CodeGenActions
682 //===----------------------------------------------------------------------===//
684 CodeGenAction::~CodeGenAction() = default;
686 static llvm::OptimizationLevel
687 mapToLevel(const Fortran::frontend::CodeGenOptions &opts) {
688 switch (opts.OptimizationLevel) {
689 default:
690 llvm_unreachable("Invalid optimization level!");
691 case 0:
692 return llvm::OptimizationLevel::O0;
693 case 1:
694 return llvm::OptimizationLevel::O1;
695 case 2:
696 return llvm::OptimizationLevel::O2;
697 case 3:
698 return llvm::OptimizationLevel::O3;
702 // Lower using HLFIR then run the FIR to HLFIR pipeline
703 void CodeGenAction::lowerHLFIRToFIR() {
704 assert(mlirModule && "The MLIR module has not been generated yet.");
706 CompilerInstance &ci = this->getInstance();
707 auto opts = ci.getInvocation().getCodeGenOpts();
708 llvm::OptimizationLevel level = mapToLevel(opts);
710 fir::support::loadDialects(*mlirCtx);
712 // Set-up the MLIR pass manager
713 mlir::PassManager pm((*mlirModule)->getName(),
714 mlir::OpPassManager::Nesting::Implicit);
716 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
717 pm.enableVerifier(/*verifyPasses=*/true);
719 // Create the pass pipeline
720 fir::createHLFIRToFIRPassPipeline(
722 ci.getInvocation().getFrontendOpts().features.IsEnabled(
723 Fortran::common::LanguageFeature::OpenMP),
724 level);
725 (void)mlir::applyPassManagerCLOptions(pm);
727 if (!mlir::succeeded(pm.run(*mlirModule))) {
728 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
729 clang::DiagnosticsEngine::Error, "Lowering to FIR failed");
730 ci.getDiagnostics().Report(diagID);
734 static std::optional<std::pair<unsigned, unsigned>>
735 getAArch64VScaleRange(CompilerInstance &ci) {
736 const auto &langOpts = ci.getInvocation().getLangOpts();
738 if (langOpts.VScaleMin || langOpts.VScaleMax)
739 return std::pair<unsigned, unsigned>(
740 langOpts.VScaleMin ? langOpts.VScaleMin : 1, langOpts.VScaleMax);
742 std::string featuresStr = ci.getTargetFeatures();
743 if (featuresStr.find("+sve") != std::string::npos)
744 return std::pair<unsigned, unsigned>(1, 16);
746 return std::nullopt;
749 static std::optional<std::pair<unsigned, unsigned>>
750 getRISCVVScaleRange(CompilerInstance &ci) {
751 const auto &langOpts = ci.getInvocation().getLangOpts();
752 const auto targetOpts = ci.getInvocation().getTargetOpts();
753 const llvm::Triple triple(targetOpts.triple);
755 auto parseResult = llvm::RISCVISAInfo::parseFeatures(
756 triple.isRISCV64() ? 64 : 32, targetOpts.featuresAsWritten);
757 if (!parseResult) {
758 std::string buffer;
759 llvm::raw_string_ostream outputErrMsg(buffer);
760 handleAllErrors(parseResult.takeError(), [&](llvm::StringError &errMsg) {
761 outputErrMsg << errMsg.getMessage();
763 ci.getDiagnostics().Report(clang::diag::err_invalid_feature_combination)
764 << buffer;
765 return std::nullopt;
768 llvm::RISCVISAInfo *const isaInfo = parseResult->get();
770 // RISCV::RVVBitsPerBlock is 64.
771 unsigned vscaleMin = isaInfo->getMinVLen() / llvm::RISCV::RVVBitsPerBlock;
773 if (langOpts.VScaleMin || langOpts.VScaleMax) {
774 // Treat Zvl*b as a lower bound on vscale.
775 vscaleMin = std::max(vscaleMin, langOpts.VScaleMin);
776 unsigned vscaleMax = langOpts.VScaleMax;
777 if (vscaleMax != 0 && vscaleMax < vscaleMin)
778 vscaleMax = vscaleMin;
779 return std::pair<unsigned, unsigned>(vscaleMin ? vscaleMin : 1, vscaleMax);
782 if (vscaleMin > 0) {
783 unsigned vscaleMax = isaInfo->getMaxVLen() / llvm::RISCV::RVVBitsPerBlock;
784 return std::make_pair(vscaleMin, vscaleMax);
787 return std::nullopt;
790 // TODO: We should get this from TargetInfo. However, that depends on
791 // too much of clang, so for now, replicate the functionality.
792 static std::optional<std::pair<unsigned, unsigned>>
793 getVScaleRange(CompilerInstance &ci) {
794 const llvm::Triple triple(ci.getInvocation().getTargetOpts().triple);
796 if (triple.isAArch64())
797 return getAArch64VScaleRange(ci);
798 if (triple.isRISCV())
799 return getRISCVVScaleRange(ci);
801 // All other architectures that don't support scalable vectors (i.e. don't
802 // need vscale)
803 return std::nullopt;
806 // Lower the previously generated MLIR module into an LLVM IR module
807 void CodeGenAction::generateLLVMIR() {
808 assert(mlirModule && "The MLIR module has not been generated yet.");
810 CompilerInstance &ci = this->getInstance();
811 auto opts = ci.getInvocation().getCodeGenOpts();
812 auto mathOpts = ci.getInvocation().getLoweringOpts().getMathOptions();
813 llvm::OptimizationLevel level = mapToLevel(opts);
815 fir::support::loadDialects(*mlirCtx);
816 mlir::DialectRegistry registry;
817 fir::support::registerNonCodegenDialects(registry);
818 fir::support::addFIRExtensions(registry);
819 mlirCtx->appendDialectRegistry(registry);
820 fir::support::registerLLVMTranslation(*mlirCtx);
822 // Set-up the MLIR pass manager
823 mlir::PassManager pm((*mlirModule)->getName(),
824 mlir::OpPassManager::Nesting::Implicit);
826 pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
827 pm.enableVerifier(/*verifyPasses=*/true);
829 MLIRToLLVMPassPipelineConfig config(level, opts, mathOpts);
830 fir::registerDefaultInlinerPass(config);
832 if (auto vsr = getVScaleRange(ci)) {
833 config.VScaleMin = vsr->first;
834 config.VScaleMax = vsr->second;
837 if (ci.getInvocation().getFrontendOpts().features.IsEnabled(
838 Fortran::common::LanguageFeature::OpenMP))
839 config.EnableOpenMP = true;
841 if (ci.getInvocation().getLoweringOpts().getIntegerWrapAround())
842 config.NSWOnLoopVarInc = false;
844 // Create the pass pipeline
845 fir::createMLIRToLLVMPassPipeline(pm, config, getCurrentFile());
846 (void)mlir::applyPassManagerCLOptions(pm);
848 // run the pass manager
849 if (!mlir::succeeded(pm.run(*mlirModule))) {
850 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
851 clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
852 ci.getDiagnostics().Report(diagID);
855 // Print final MLIR module, just before translation into LLVM IR, if
856 // -save-temps has been specified.
857 if (!saveMLIRTempFile(ci.getInvocation(), *mlirModule, getCurrentFile(),
858 "llvmir")) {
859 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
860 clang::DiagnosticsEngine::Error, "Saving MLIR temp file failed");
861 ci.getDiagnostics().Report(diagID);
862 return;
865 // Translate to LLVM IR
866 std::optional<llvm::StringRef> moduleName = mlirModule->getName();
867 llvmModule = mlir::translateModuleToLLVMIR(
868 *mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
870 if (!llvmModule) {
871 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
872 clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
873 ci.getDiagnostics().Report(diagID);
874 return;
877 // Set PIC/PIE level LLVM module flags.
878 if (opts.PICLevel > 0) {
879 llvmModule->setPICLevel(static_cast<llvm::PICLevel::Level>(opts.PICLevel));
880 if (opts.IsPIE)
881 llvmModule->setPIELevel(
882 static_cast<llvm::PIELevel::Level>(opts.PICLevel));
885 // Set mcmodel level LLVM module flags
886 std::optional<llvm::CodeModel::Model> cm = getCodeModel(opts.CodeModel);
887 if (cm.has_value()) {
888 const llvm::Triple triple(ci.getInvocation().getTargetOpts().triple);
889 llvmModule->setCodeModel(*cm);
890 if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) &&
891 triple.getArch() == llvm::Triple::x86_64) {
892 llvmModule->setLargeDataThreshold(opts.LargeDataThreshold);
897 static std::unique_ptr<llvm::raw_pwrite_stream>
898 getOutputStream(CompilerInstance &ci, llvm::StringRef inFile,
899 BackendActionTy action) {
900 switch (action) {
901 case BackendActionTy::Backend_EmitAssembly:
902 return ci.createDefaultOutputFile(
903 /*Binary=*/false, inFile, /*extension=*/"s");
904 case BackendActionTy::Backend_EmitLL:
905 return ci.createDefaultOutputFile(
906 /*Binary=*/false, inFile, /*extension=*/"ll");
907 case BackendActionTy::Backend_EmitFIR:
908 case BackendActionTy::Backend_EmitHLFIR:
909 return ci.createDefaultOutputFile(
910 /*Binary=*/false, inFile, /*extension=*/"mlir");
911 case BackendActionTy::Backend_EmitBC:
912 return ci.createDefaultOutputFile(
913 /*Binary=*/true, inFile, /*extension=*/"bc");
914 case BackendActionTy::Backend_EmitObj:
915 return ci.createDefaultOutputFile(
916 /*Binary=*/true, inFile, /*extension=*/"o");
919 llvm_unreachable("Invalid action!");
922 /// Generate target-specific machine-code or assembly file from the input LLVM
923 /// module.
925 /// \param [in] diags Diagnostics engine for reporting errors
926 /// \param [in] tm Target machine to aid the code-gen pipeline set-up
927 /// \param [in] act Backend act to run (assembly vs machine-code generation)
928 /// \param [in] llvmModule LLVM module to lower to assembly/machine-code
929 /// \param [in] codeGenOpts options configuring codegen pipeline
930 /// \param [out] os Output stream to emit the generated code to
931 static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
932 llvm::TargetMachine &tm,
933 BackendActionTy act,
934 llvm::Module &llvmModule,
935 const CodeGenOptions &codeGenOpts,
936 llvm::raw_pwrite_stream &os) {
937 assert(((act == BackendActionTy::Backend_EmitObj) ||
938 (act == BackendActionTy::Backend_EmitAssembly)) &&
939 "Unsupported action");
941 // Set-up the pass manager, i.e create an LLVM code-gen pass pipeline.
942 // Currently only the legacy pass manager is supported.
943 // TODO: Switch to the new PM once it's available in the backend.
944 llvm::legacy::PassManager codeGenPasses;
945 codeGenPasses.add(
946 createTargetTransformInfoWrapperPass(tm.getTargetIRAnalysis()));
948 llvm::Triple triple(llvmModule.getTargetTriple());
949 llvm::TargetLibraryInfoImpl *tlii =
950 llvm::driver::createTLII(triple, codeGenOpts.getVecLib());
951 codeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*tlii));
953 llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly)
954 ? llvm::CodeGenFileType::AssemblyFile
955 : llvm::CodeGenFileType::ObjectFile;
956 if (tm.addPassesToEmitFile(codeGenPasses, os, nullptr, cgft)) {
957 unsigned diagID =
958 diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
959 "emission of this file type is not supported");
960 diags.Report(diagID);
961 return;
964 // Run the passes
965 codeGenPasses.run(llvmModule);
967 // Cleanup
968 delete tlii;
971 void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
972 auto opts = getInstance().getInvocation().getCodeGenOpts();
973 auto &diags = getInstance().getDiagnostics();
974 llvm::OptimizationLevel level = mapToLevel(opts);
976 llvm::TargetMachine *targetMachine = &getInstance().getTargetMachine();
977 // Create the analysis managers.
978 llvm::LoopAnalysisManager lam;
979 llvm::FunctionAnalysisManager fam;
980 llvm::CGSCCAnalysisManager cgam;
981 llvm::ModuleAnalysisManager mam;
983 // Create the pass manager builder.
984 llvm::PassInstrumentationCallbacks pic;
985 llvm::PipelineTuningOptions pto;
986 std::optional<llvm::PGOOptions> pgoOpt;
987 llvm::StandardInstrumentations si(llvmModule->getContext(),
988 opts.DebugPassManager);
989 si.registerCallbacks(pic, &mam);
990 llvm::PassBuilder pb(targetMachine, pto, pgoOpt, &pic);
992 // Attempt to load pass plugins and register their callbacks with PB.
993 for (auto &pluginFile : opts.LLVMPassPlugins) {
994 auto passPlugin = llvm::PassPlugin::Load(pluginFile);
995 if (passPlugin) {
996 passPlugin->registerPassBuilderCallbacks(pb);
997 } else {
998 diags.Report(clang::diag::err_fe_unable_to_load_plugin)
999 << pluginFile << passPlugin.takeError();
1002 // Register static plugin extensions.
1003 #define HANDLE_EXTENSION(Ext) \
1004 get##Ext##PluginInfo().RegisterPassBuilderCallbacks(pb);
1005 #include "llvm/Support/Extension.def"
1007 // Register the target library analysis directly and give it a customized
1008 // preset TLI depending on -fveclib
1009 llvm::Triple triple(llvmModule->getTargetTriple());
1010 llvm::TargetLibraryInfoImpl *tlii =
1011 llvm::driver::createTLII(triple, opts.getVecLib());
1012 fam.registerPass([&] { return llvm::TargetLibraryAnalysis(*tlii); });
1014 // Register all the basic analyses with the managers.
1015 pb.registerModuleAnalyses(mam);
1016 pb.registerCGSCCAnalyses(cgam);
1017 pb.registerFunctionAnalyses(fam);
1018 pb.registerLoopAnalyses(lam);
1019 pb.crossRegisterProxies(lam, fam, cgam, mam);
1021 // Create the pass manager.
1022 llvm::ModulePassManager mpm;
1023 if (opts.PrepareForFullLTO)
1024 mpm = pb.buildLTOPreLinkDefaultPipeline(level);
1025 else if (opts.PrepareForThinLTO)
1026 mpm = pb.buildThinLTOPreLinkDefaultPipeline(level);
1027 else
1028 mpm = pb.buildPerModuleDefaultPipeline(level);
1030 if (action == BackendActionTy::Backend_EmitBC)
1031 mpm.addPass(llvm::BitcodeWriterPass(os));
1032 else if (action == BackendActionTy::Backend_EmitLL)
1033 mpm.addPass(llvm::PrintModulePass(os));
1035 // FIXME: This should eventually be replaced by a first-class driver option.
1036 // This should be done for both flang and clang simultaneously.
1037 // Print a textual, '-passes=' compatible, representation of pipeline if
1038 // requested. In this case, don't run the passes. This mimics the behavior of
1039 // clang.
1040 if (llvm::PrintPipelinePasses) {
1041 mpm.printPipeline(llvm::outs(), [&pic](llvm::StringRef className) {
1042 auto passName = pic.getPassNameForClassName(className);
1043 return passName.empty() ? className : passName;
1045 llvm::outs() << "\n";
1046 return;
1049 // Run the passes.
1050 mpm.run(*llvmModule, mam);
1052 // Cleanup
1053 delete tlii;
1056 // This class handles optimization remark messages requested if
1057 // any of -Rpass, -Rpass-analysis or -Rpass-missed flags were provided
1058 class BackendRemarkConsumer : public llvm::DiagnosticHandler {
1060 const CodeGenOptions &codeGenOpts;
1061 clang::DiagnosticsEngine &diags;
1063 public:
1064 BackendRemarkConsumer(clang::DiagnosticsEngine &diags,
1065 const CodeGenOptions &codeGenOpts)
1066 : codeGenOpts(codeGenOpts), diags(diags) {}
1068 bool isAnalysisRemarkEnabled(llvm::StringRef passName) const override {
1069 return codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName);
1071 bool isMissedOptRemarkEnabled(llvm::StringRef passName) const override {
1072 return codeGenOpts.OptimizationRemarkMissed.patternMatches(passName);
1074 bool isPassedOptRemarkEnabled(llvm::StringRef passName) const override {
1075 return codeGenOpts.OptimizationRemark.patternMatches(passName);
1078 bool isAnyRemarkEnabled() const override {
1079 return codeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() ||
1080 codeGenOpts.OptimizationRemarkMissed.hasValidPattern() ||
1081 codeGenOpts.OptimizationRemark.hasValidPattern();
1084 void
1085 emitOptimizationMessage(const llvm::DiagnosticInfoOptimizationBase &diagInfo,
1086 unsigned diagID) {
1087 // We only support warnings and remarks.
1088 assert(diagInfo.getSeverity() == llvm::DS_Remark ||
1089 diagInfo.getSeverity() == llvm::DS_Warning);
1091 std::string msg;
1092 llvm::raw_string_ostream msgStream(msg);
1094 if (diagInfo.isLocationAvailable()) {
1095 // Clang contains a SourceManager class which handles loading
1096 // and caching of source files into memory and it can be used to
1097 // query SourceLocation data. The SourceLocation data is what is
1098 // needed here as it contains the full include stack which gives
1099 // line and column number as well as file name and location.
1100 // Since Flang doesn't have SourceManager, send file name and absolute
1101 // path through msgStream, to use for printing.
1102 msgStream << diagInfo.getLocationStr() << ";;"
1103 << diagInfo.getAbsolutePath() << ";;";
1106 msgStream << diagInfo.getMsg();
1108 // Emit message.
1109 diags.Report(diagID) << clang::AddFlagValue(diagInfo.getPassName()) << msg;
1112 void optimizationRemarkHandler(
1113 const llvm::DiagnosticInfoOptimizationBase &diagInfo) {
1114 auto passName = diagInfo.getPassName();
1115 if (diagInfo.isPassed()) {
1116 if (codeGenOpts.OptimizationRemark.patternMatches(passName))
1117 // Optimization remarks are active only if the -Rpass flag has a regular
1118 // expression that matches the name of the pass name in \p d.
1119 emitOptimizationMessage(
1120 diagInfo, clang::diag::remark_fe_backend_optimization_remark);
1122 return;
1125 if (diagInfo.isMissed()) {
1126 if (codeGenOpts.OptimizationRemarkMissed.patternMatches(passName))
1127 // Missed optimization remarks are active only if the -Rpass-missed
1128 // flag has a regular expression that matches the name of the pass
1129 // name in \p d.
1130 emitOptimizationMessage(
1131 diagInfo,
1132 clang::diag::remark_fe_backend_optimization_remark_missed);
1134 return;
1137 assert(diagInfo.isAnalysis() && "Unknown remark type");
1139 bool shouldAlwaysPrint = false;
1140 auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(&diagInfo);
1141 if (ora)
1142 shouldAlwaysPrint = ora->shouldAlwaysPrint();
1144 if (shouldAlwaysPrint ||
1145 codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName))
1146 emitOptimizationMessage(
1147 diagInfo,
1148 clang::diag::remark_fe_backend_optimization_remark_analysis);
1151 bool handleDiagnostics(const llvm::DiagnosticInfo &di) override {
1152 switch (di.getKind()) {
1153 case llvm::DK_OptimizationRemark:
1154 optimizationRemarkHandler(llvm::cast<llvm::OptimizationRemark>(di));
1155 break;
1156 case llvm::DK_OptimizationRemarkMissed:
1157 optimizationRemarkHandler(llvm::cast<llvm::OptimizationRemarkMissed>(di));
1158 break;
1159 case llvm::DK_OptimizationRemarkAnalysis:
1160 optimizationRemarkHandler(
1161 llvm::cast<llvm::OptimizationRemarkAnalysis>(di));
1162 break;
1163 case llvm::DK_MachineOptimizationRemark:
1164 optimizationRemarkHandler(
1165 llvm::cast<llvm::MachineOptimizationRemark>(di));
1166 break;
1167 case llvm::DK_MachineOptimizationRemarkMissed:
1168 optimizationRemarkHandler(
1169 llvm::cast<llvm::MachineOptimizationRemarkMissed>(di));
1170 break;
1171 case llvm::DK_MachineOptimizationRemarkAnalysis:
1172 optimizationRemarkHandler(
1173 llvm::cast<llvm::MachineOptimizationRemarkAnalysis>(di));
1174 break;
1175 default:
1176 break;
1178 return true;
1182 void CodeGenAction::embedOffloadObjects() {
1183 CompilerInstance &ci = this->getInstance();
1184 const auto &cgOpts = ci.getInvocation().getCodeGenOpts();
1186 for (llvm::StringRef offloadObject : cgOpts.OffloadObjects) {
1187 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> objectOrErr =
1188 llvm::MemoryBuffer::getFileOrSTDIN(offloadObject);
1189 if (std::error_code ec = objectOrErr.getError()) {
1190 auto diagID = ci.getDiagnostics().getCustomDiagID(
1191 clang::DiagnosticsEngine::Error, "could not open '%0' for embedding");
1192 ci.getDiagnostics().Report(diagID) << offloadObject;
1193 return;
1195 llvm::embedBufferInModule(
1196 *llvmModule, **objectOrErr, ".llvm.offloading",
1197 llvm::Align(llvm::object::OffloadBinary::getAlignment()));
1201 void CodeGenAction::linkBuiltinBCLibs() {
1202 auto options = clang::FileSystemOptions();
1203 clang::FileManager fileManager(options);
1204 CompilerInstance &ci = this->getInstance();
1205 const auto &cgOpts = ci.getInvocation().getCodeGenOpts();
1207 std::vector<std::unique_ptr<llvm::Module>> modules;
1209 // Load LLVM modules
1210 for (llvm::StringRef bcLib : cgOpts.BuiltinBCLibs) {
1211 auto BCBuf = fileManager.getBufferForFile(bcLib);
1212 if (!BCBuf) {
1213 auto diagID = ci.getDiagnostics().getCustomDiagID(
1214 clang::DiagnosticsEngine::Error, "could not open '%0' for linking");
1215 ci.getDiagnostics().Report(diagID) << bcLib;
1216 return;
1219 llvm::Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
1220 getOwningLazyBitcodeModule(std::move(*BCBuf), *llvmCtx);
1221 if (!ModuleOrErr) {
1222 auto diagID = ci.getDiagnostics().getCustomDiagID(
1223 clang::DiagnosticsEngine::Error, "error loading '%0' for linking");
1224 ci.getDiagnostics().Report(diagID) << bcLib;
1225 return;
1227 modules.push_back(std::move(ModuleOrErr.get()));
1230 // Link modules and internalize functions
1231 for (auto &module : modules) {
1232 bool Err;
1233 Err = llvm::Linker::linkModules(
1234 *llvmModule, std::move(module), llvm::Linker::Flags::LinkOnlyNeeded,
1235 [](llvm::Module &M, const llvm::StringSet<> &GVS) {
1236 llvm::internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
1237 return !GV.hasName() || (GVS.count(GV.getName()) == 0);
1240 if (Err) {
1241 auto diagID = ci.getDiagnostics().getCustomDiagID(
1242 clang::DiagnosticsEngine::Error, "link error when linking '%0'");
1243 ci.getDiagnostics().Report(diagID) << module->getSourceFileName();
1244 return;
1249 static void reportOptRecordError(llvm::Error e, clang::DiagnosticsEngine &diags,
1250 const CodeGenOptions &codeGenOpts) {
1251 handleAllErrors(
1252 std::move(e),
1253 [&](const llvm::LLVMRemarkSetupFileError &e) {
1254 diags.Report(clang::diag::err_cannot_open_file)
1255 << codeGenOpts.OptRecordFile << e.message();
1257 [&](const llvm::LLVMRemarkSetupPatternError &e) {
1258 diags.Report(clang::diag::err_drv_optimization_remark_pattern)
1259 << e.message() << codeGenOpts.OptRecordPasses;
1261 [&](const llvm::LLVMRemarkSetupFormatError &e) {
1262 diags.Report(clang::diag::err_drv_optimization_remark_format)
1263 << codeGenOpts.OptRecordFormat;
1267 void CodeGenAction::executeAction() {
1268 CompilerInstance &ci = this->getInstance();
1270 clang::DiagnosticsEngine &diags = ci.getDiagnostics();
1271 const CodeGenOptions &codeGenOpts = ci.getInvocation().getCodeGenOpts();
1272 Fortran::lower::LoweringOptions &loweringOpts =
1273 ci.getInvocation().getLoweringOpts();
1275 // If the output stream is a file, generate it and define the corresponding
1276 // output stream. If a pre-defined output stream is available, we will use
1277 // that instead.
1279 // NOTE: `os` is a smart pointer that will be destroyed at the end of this
1280 // method. However, it won't be written to until `codeGenPasses` is
1281 // destroyed. By defining `os` before `codeGenPasses`, we make sure that the
1282 // output stream won't be destroyed before it is written to. This only
1283 // applies when an output file is used (i.e. there is no pre-defined output
1284 // stream).
1285 // TODO: Revisit once the new PM is ready (i.e. when `codeGenPasses` is
1286 // updated to use it).
1287 std::unique_ptr<llvm::raw_pwrite_stream> os;
1288 if (ci.isOutputStreamNull()) {
1289 os = getOutputStream(ci, getCurrentFileOrBufferName(), action);
1291 if (!os) {
1292 unsigned diagID = diags.getCustomDiagID(
1293 clang::DiagnosticsEngine::Error, "failed to create the output file");
1294 diags.Report(diagID);
1295 return;
1299 if (action == BackendActionTy::Backend_EmitFIR) {
1300 if (loweringOpts.getLowerToHighLevelFIR()) {
1301 lowerHLFIRToFIR();
1303 mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1304 return;
1307 if (action == BackendActionTy::Backend_EmitHLFIR) {
1308 assert(loweringOpts.getLowerToHighLevelFIR() &&
1309 "Lowering must have been configured to emit HLFIR");
1310 mlirModule->print(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1311 return;
1314 // Generate an LLVM module if it's not already present (it will already be
1315 // present if the input file is an LLVM IR/BC file).
1316 if (!llvmModule)
1317 generateLLVMIR();
1319 // If generating the LLVM module failed, abort! No need for further error
1320 // reporting since generateLLVMIR() does this already.
1321 if (!llvmModule)
1322 return;
1324 // Set the triple based on the targetmachine (this comes compiler invocation
1325 // and the command-line target option if specified, or the default if not
1326 // given on the command-line).
1327 llvm::TargetMachine &targetMachine = ci.getTargetMachine();
1329 const std::string &theTriple = targetMachine.getTargetTriple().str();
1331 if (llvmModule->getTargetTriple() != theTriple) {
1332 diags.Report(clang::diag::warn_fe_override_module) << theTriple;
1335 // Always set the triple and data layout, to make sure they match and are set.
1336 // Note that this overwrites any datalayout stored in the LLVM-IR. This avoids
1337 // an assert for incompatible data layout when the code-generation happens.
1338 llvmModule->setTargetTriple(theTriple);
1339 llvmModule->setDataLayout(targetMachine.createDataLayout());
1341 // Link in builtin bitcode libraries
1342 if (!codeGenOpts.BuiltinBCLibs.empty())
1343 linkBuiltinBCLibs();
1345 // Embed offload objects specified with -fembed-offload-object
1346 if (!codeGenOpts.OffloadObjects.empty())
1347 embedOffloadObjects();
1349 BackendRemarkConsumer remarkConsumer(diags, codeGenOpts);
1351 llvmModule->getContext().setDiagnosticHandler(
1352 std::make_unique<BackendRemarkConsumer>(remarkConsumer));
1354 // write optimization-record
1355 llvm::Expected<std::unique_ptr<llvm::ToolOutputFile>> optRecordFileOrErr =
1356 setupLLVMOptimizationRemarks(
1357 llvmModule->getContext(), codeGenOpts.OptRecordFile,
1358 codeGenOpts.OptRecordPasses, codeGenOpts.OptRecordFormat,
1359 /*DiagnosticsWithHotness=*/false,
1360 /*DiagnosticsHotnessThreshold=*/0);
1362 if (llvm::Error e = optRecordFileOrErr.takeError()) {
1363 reportOptRecordError(std::move(e), diags, codeGenOpts);
1364 return;
1367 std::unique_ptr<llvm::ToolOutputFile> optRecordFile =
1368 std::move(*optRecordFileOrErr);
1370 if (optRecordFile) {
1371 optRecordFile->keep();
1372 optRecordFile->os().flush();
1375 // Run LLVM's middle-end (i.e. the optimizer).
1376 runOptimizationPipeline(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1378 if (action == BackendActionTy::Backend_EmitLL ||
1379 action == BackendActionTy::Backend_EmitBC) {
1380 // This action has effectively been completed in runOptimizationPipeline.
1381 return;
1384 // Run LLVM's backend and generate either assembly or machine code
1385 if (action == BackendActionTy::Backend_EmitAssembly ||
1386 action == BackendActionTy::Backend_EmitObj) {
1387 generateMachineCodeOrAssemblyImpl(
1388 diags, targetMachine, action, *llvmModule, codeGenOpts,
1389 ci.isOutputStreamNull() ? *os : ci.getOutputStream());
1390 return;
1394 void InitOnlyAction::executeAction() {
1395 CompilerInstance &ci = this->getInstance();
1396 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
1397 clang::DiagnosticsEngine::Warning,
1398 "Use `-init-only` for testing purposes only");
1399 ci.getDiagnostics().Report(diagID);
1402 void PluginParseTreeAction::executeAction() {}
1404 void DebugDumpPFTAction::executeAction() {
1405 CompilerInstance &ci = this->getInstance();
1407 if (auto ast = Fortran::lower::createPFT(*ci.getParsing().parseTree(),
1408 ci.getSemantics().context())) {
1409 Fortran::lower::dumpPFT(llvm::outs(), *ast);
1410 return;
1413 unsigned diagID = ci.getDiagnostics().getCustomDiagID(
1414 clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
1415 ci.getDiagnostics().Report(diagID);
1418 Fortran::parser::Parsing &PluginParseTreeAction::getParsing() {
1419 return getInstance().getParsing();
1422 std::unique_ptr<llvm::raw_pwrite_stream>
1423 PluginParseTreeAction::createOutputFile(llvm::StringRef extension = "") {
1425 std::unique_ptr<llvm::raw_pwrite_stream> os{
1426 getInstance().createDefaultOutputFile(
1427 /*Binary=*/false, /*InFile=*/getCurrentFileOrBufferName(),
1428 extension)};
1429 return os;