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 BackendRemarkConsumer : public llvm::DiagnosticHandler { |
1006 | |
1007 | const CodeGenOptions &codeGenOpts; |
1008 | clang::DiagnosticsEngine &diags; |
1009 | |
1010 | public: |
1011 | BackendRemarkConsumer(clang::DiagnosticsEngine &diags, |
1012 | const CodeGenOptions &codeGenOpts) |
1013 | : codeGenOpts(codeGenOpts), diags(diags) {} |
1014 | |
1015 | bool isAnalysisRemarkEnabled(llvm::StringRef passName) const override { |
1016 | return codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName); |
1017 | } |
1018 | bool isMissedOptRemarkEnabled(llvm::StringRef passName) const override { |
1019 | return codeGenOpts.OptimizationRemarkMissed.patternMatches(passName); |
1020 | } |
1021 | bool isPassedOptRemarkEnabled(llvm::StringRef passName) const override { |
1022 | return codeGenOpts.OptimizationRemark.patternMatches(passName); |
1023 | } |
1024 | |
1025 | bool isAnyRemarkEnabled() const override { |
1026 | return codeGenOpts.OptimizationRemarkAnalysis.hasValidPattern() || |
1027 | codeGenOpts.OptimizationRemarkMissed.hasValidPattern() || |
1028 | codeGenOpts.OptimizationRemark.hasValidPattern(); |
1029 | } |
1030 | |
1031 | void |
1032 | emitOptimizationMessage(const llvm::DiagnosticInfoOptimizationBase &diagInfo, |
1033 | unsigned diagID) { |
1034 | // We only support warnings and remarks. |
1035 | assert(diagInfo.getSeverity() == llvm::DS_Remark || |
1036 | diagInfo.getSeverity() == llvm::DS_Warning); |
1037 | |
1038 | std::string msg; |
1039 | llvm::raw_string_ostream msgStream(msg); |
1040 | |
1041 | if (diagInfo.isLocationAvailable()) { |
1042 | // Clang contains a SourceManager class which handles loading |
1043 | // and caching of source files into memory and it can be used to |
1044 | // query SourceLocation data. The SourceLocation data is what is |
1045 | // needed here as it contains the full include stack which gives |
1046 | // line and column number as well as file name and location. |
1047 | // Since Flang doesn't have SourceManager, send file name and absolute |
1048 | // path through msgStream, to use for printing. |
1049 | msgStream << diagInfo.getLocationStr() << ";;" |
1050 | << diagInfo.getAbsolutePath() << ";;"; |
1051 | } |
1052 | |
1053 | msgStream << diagInfo.getMsg(); |
1054 | |
1055 | // Emit message. |
1056 | diags.Report(DiagID: diagID) << clang::AddFlagValue(diagInfo.getPassName()) << msg; |
1057 | } |
1058 | |
1059 | void optimizationRemarkHandler( |
1060 | const llvm::DiagnosticInfoOptimizationBase &diagInfo) { |
1061 | auto passName = diagInfo.getPassName(); |
1062 | if (diagInfo.isPassed()) { |
1063 | if (codeGenOpts.OptimizationRemark.patternMatches(passName)) |
1064 | // Optimization remarks are active only if the -Rpass flag has a regular |
1065 | // expression that matches the name of the pass name in \p d. |
1066 | emitOptimizationMessage( |
1067 | diagInfo, clang::diag::remark_fe_backend_optimization_remark); |
1068 | |
1069 | return; |
1070 | } |
1071 | |
1072 | if (diagInfo.isMissed()) { |
1073 | if (codeGenOpts.OptimizationRemarkMissed.patternMatches(passName)) |
1074 | // Missed optimization remarks are active only if the -Rpass-missed |
1075 | // flag has a regular expression that matches the name of the pass |
1076 | // name in \p d. |
1077 | emitOptimizationMessage( |
1078 | diagInfo, |
1079 | clang::diag::remark_fe_backend_optimization_remark_missed); |
1080 | |
1081 | return; |
1082 | } |
1083 | |
1084 | assert(diagInfo.isAnalysis() && "Unknown remark type"); |
1085 | |
1086 | bool shouldAlwaysPrint = false; |
1087 | auto *ora = llvm::dyn_cast<llvm::OptimizationRemarkAnalysis>(Val: &diagInfo); |
1088 | if (ora) |
1089 | shouldAlwaysPrint = ora->shouldAlwaysPrint(); |
1090 | |
1091 | if (shouldAlwaysPrint || |
1092 | codeGenOpts.OptimizationRemarkAnalysis.patternMatches(passName)) |
1093 | emitOptimizationMessage( |
1094 | diagInfo, |
1095 | clang::diag::remark_fe_backend_optimization_remark_analysis); |
1096 | } |
1097 | |
1098 | bool handleDiagnostics(const llvm::DiagnosticInfo &di) override { |
1099 | switch (di.getKind()) { |
1100 | case llvm::DK_OptimizationRemark: |
1101 | optimizationRemarkHandler(diagInfo: llvm::cast<llvm::OptimizationRemark>(Val: di)); |
1102 | break; |
1103 | case llvm::DK_OptimizationRemarkMissed: |
1104 | optimizationRemarkHandler(diagInfo: llvm::cast<llvm::OptimizationRemarkMissed>(Val: di)); |
1105 | break; |
1106 | case llvm::DK_OptimizationRemarkAnalysis: |
1107 | optimizationRemarkHandler( |
1108 | diagInfo: llvm::cast<llvm::OptimizationRemarkAnalysis>(Val: di)); |
1109 | break; |
1110 | case llvm::DK_MachineOptimizationRemark: |
1111 | optimizationRemarkHandler( |
1112 | diagInfo: llvm::cast<llvm::MachineOptimizationRemark>(Val: di)); |
1113 | break; |
1114 | case llvm::DK_MachineOptimizationRemarkMissed: |
1115 | optimizationRemarkHandler( |
1116 | diagInfo: llvm::cast<llvm::MachineOptimizationRemarkMissed>(Val: di)); |
1117 | break; |
1118 | case llvm::DK_MachineOptimizationRemarkAnalysis: |
1119 | optimizationRemarkHandler( |
1120 | diagInfo: llvm::cast<llvm::MachineOptimizationRemarkAnalysis>(Val: di)); |
1121 | break; |
1122 | default: |
1123 | break; |
1124 | } |
1125 | return true; |
1126 | } |
1127 | }; |
1128 | |
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 |
Definitions
- timingIdParse
- timingIdMLIRGen
- timingIdMLIRPasses
- timingIdLLVMIRGen
- timingIdLLVMIRPasses
- timingIdBackend
- saveMLIRTempFile
- addDependentLibs
- mapToLevel
- getAArch64VScaleRange
- getRISCVVScaleRange
- getVScaleRange
- getOutputStream
- generateMachineCodeOrAssemblyImpl
- BackendRemarkConsumer
- BackendRemarkConsumer
- isAnalysisRemarkEnabled
- isMissedOptRemarkEnabled
- isPassedOptRemarkEnabled
- isAnyRemarkEnabled
- emitOptimizationMessage
- optimizationRemarkHandler
- handleDiagnostics
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more