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