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