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