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 #include "flang/Frontend/FrontendActions.h"
10 #include "flang/Common/default-kinds.h"
11 #include "flang/Frontend/CompilerInstance.h"
12 #include "flang/Frontend/FrontendOptions.h"
13 #include "flang/Frontend/PreprocessorOptions.h"
14 #include "flang/Lower/Bridge.h"
15 #include "flang/Lower/PFTBuilder.h"
16 #include "flang/Lower/Support/Verifier.h"
17 #include "flang/Optimizer/Support/FIRContext.h"
18 #include "flang/Optimizer/Support/InitFIR.h"
19 #include "flang/Optimizer/Support/KindMapping.h"
20 #include "flang/Optimizer/Support/Utils.h"
21 #include "flang/Parser/dump-parse-tree.h"
22 #include "flang/Parser/parsing.h"
23 #include "flang/Parser/provenance.h"
24 #include "flang/Parser/source.h"
25 #include "flang/Parser/unparse.h"
26 #include "flang/Semantics/runtime-type-info.h"
27 #include "flang/Semantics/semantics.h"
28 #include "flang/Semantics/unparse-with-symbols.h"
29 
30 #include "mlir/IR/Dialect.h"
31 #include "mlir/Pass/PassManager.h"
32 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
33 #include "llvm/ADT/StringRef.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include <clang/Basic/Diagnostic.h>
36 #include <memory>
37 
38 using namespace Fortran::frontend;
39 
40 //===----------------------------------------------------------------------===//
41 // Custom BeginSourceFileAction
42 //===----------------------------------------------------------------------===//
43 bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); }
44 
45 bool PrescanAndParseAction::BeginSourceFileAction() {
46   return RunPrescan() && RunParse();
47 }
48 
49 bool PrescanAndSemaAction::BeginSourceFileAction() {
50   return RunPrescan() && RunParse() && RunSemanticChecks() &&
51       GenerateRtTypeTables();
52 }
53 
54 bool PrescanAndSemaDebugAction::BeginSourceFileAction() {
55   // This is a "debug" action for development purposes. To facilitate this, the
56   // semantic checks are made to succeed unconditionally to prevent this action
57   // from exiting early (i.e. in the presence of semantic errors). We should
58   // never do this in actions intended for end-users or otherwise regular
59   // compiler workflows!
60   return RunPrescan() && RunParse() && (RunSemanticChecks() || true) &&
61       (GenerateRtTypeTables() || true);
62 }
63 
64 bool CodeGenAction::BeginSourceFileAction() {
65   bool res = RunPrescan() && RunParse() && RunSemanticChecks();
66   if (!res)
67     return res;
68 
69   CompilerInstance &ci = this->instance();
70 
71   // Load the MLIR dialects required by Flang
72   mlir::DialectRegistry registry;
73   mlirCtx = std::make_unique<mlir::MLIRContext>(registry);
74   fir::support::registerNonCodegenDialects(registry);
75   fir::support::loadNonCodegenDialects(*mlirCtx);
76 
77   // Create a LoweringBridge
78   const common::IntrinsicTypeDefaultKinds &defKinds =
79       ci.invocation().semanticsContext().defaultKinds();
80   fir::KindMapping kindMap(mlirCtx.get(),
81       llvm::ArrayRef<fir::KindTy>{fir::fromDefaultKinds(defKinds)});
82   lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create(*mlirCtx,
83       defKinds, ci.invocation().semanticsContext().intrinsics(),
84       ci.parsing().allCooked(), ci.invocation().targetOpts().triple, kindMap);
85 
86   // Create a parse tree and lower it to FIR
87   Fortran::parser::Program &parseTree{*ci.parsing().parseTree()};
88   lb.lower(parseTree, ci.invocation().semanticsContext());
89   mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
90 
91   // Run the default passes.
92   mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
93   pm.enableVerifier(/*verifyPasses=*/true);
94   pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
95 
96   if (mlir::failed(pm.run(*mlirModule))) {
97     unsigned diagID =
98         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
99             "verification of lowering to FIR failed");
100     ci.diagnostics().Report(diagID);
101     return false;
102   }
103 
104   return true;
105 }
106 
107 //===----------------------------------------------------------------------===//
108 // Custom ExecuteAction
109 //===----------------------------------------------------------------------===//
110 void InputOutputTestAction::ExecuteAction() {
111   CompilerInstance &ci = instance();
112 
113   // Create a stream for errors
114   std::string buf;
115   llvm::raw_string_ostream error_stream{buf};
116 
117   // Read the input file
118   Fortran::parser::AllSources &allSources{ci.allSources()};
119   std::string path{GetCurrentFileOrBufferName()};
120   const Fortran::parser::SourceFile *sf;
121   if (path == "-")
122     sf = allSources.ReadStandardInput(error_stream);
123   else
124     sf = allSources.Open(path, error_stream, std::optional<std::string>{"."s});
125   llvm::ArrayRef<char> fileContent = sf->content();
126 
127   // Output file descriptor to receive the contents of the input file.
128   std::unique_ptr<llvm::raw_ostream> os;
129 
130   // Copy the contents from the input file to the output file
131   if (!ci.IsOutputStreamNull()) {
132     // An output stream (outputStream_) was set earlier
133     ci.WriteOutputStream(fileContent.data());
134   } else {
135     // No pre-set output stream - create an output file
136     os = ci.CreateDefaultOutputFile(
137         /*binary=*/true, GetCurrentFileOrBufferName(), "txt");
138     if (!os)
139       return;
140     (*os) << fileContent.data();
141   }
142 }
143 
144 void PrintPreprocessedAction::ExecuteAction() {
145   std::string buf;
146   llvm::raw_string_ostream outForPP{buf};
147 
148   // Format or dump the prescanner's output
149   CompilerInstance &ci = this->instance();
150   if (ci.invocation().preprocessorOpts().noReformat) {
151     ci.parsing().DumpCookedChars(outForPP);
152   } else {
153     ci.parsing().EmitPreprocessedSource(
154         outForPP, !ci.invocation().preprocessorOpts().noLineDirectives);
155   }
156 
157   // Print diagnostics from the prescanner
158   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
159 
160   // If a pre-defined output stream exists, dump the preprocessed content there
161   if (!ci.IsOutputStreamNull()) {
162     // Send the output to the pre-defined output buffer.
163     ci.WriteOutputStream(outForPP.str());
164     return;
165   }
166 
167   // Create a file and save the preprocessed output there
168   std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
169       /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName())};
170   if (!os) {
171     return;
172   }
173 
174   (*os) << outForPP.str();
175 }
176 
177 void DebugDumpProvenanceAction::ExecuteAction() {
178   this->instance().parsing().DumpProvenance(llvm::outs());
179 }
180 
181 void ParseSyntaxOnlyAction::ExecuteAction() {
182 }
183 
184 void DebugUnparseNoSemaAction::ExecuteAction() {
185   auto &invoc = this->instance().invocation();
186   auto &parseTree{instance().parsing().parseTree()};
187 
188   // TODO: Options should come from CompilerInvocation
189   Unparse(llvm::outs(), *parseTree,
190       /*encoding=*/Fortran::parser::Encoding::UTF_8,
191       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
192       /*preStatement=*/nullptr,
193       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
194 }
195 
196 void DebugUnparseAction::ExecuteAction() {
197   auto &invoc = this->instance().invocation();
198   auto &parseTree{instance().parsing().parseTree()};
199 
200   CompilerInstance &ci = this->instance();
201   auto os{ci.CreateDefaultOutputFile(
202       /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName())};
203 
204   // TODO: Options should come from CompilerInvocation
205   Unparse(*os, *parseTree,
206       /*encoding=*/Fortran::parser::Encoding::UTF_8,
207       /*capitalizeKeywords=*/true, /*backslashEscapes=*/false,
208       /*preStatement=*/nullptr,
209       invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr);
210 
211   // Report fatal semantic errors
212   reportFatalSemanticErrors();
213 }
214 
215 void DebugUnparseWithSymbolsAction::ExecuteAction() {
216   auto &parseTree{*instance().parsing().parseTree()};
217 
218   Fortran::semantics::UnparseWithSymbols(
219       llvm::outs(), parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8);
220 
221   // Report fatal semantic errors
222   reportFatalSemanticErrors();
223 }
224 
225 void DebugDumpSymbolsAction::ExecuteAction() {
226   CompilerInstance &ci = this->instance();
227 
228   if (!ci.getRtTyTables().schemata) {
229     unsigned DiagID =
230         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
231             "could not find module file for __fortran_type_info");
232     ci.diagnostics().Report(DiagID);
233     llvm::errs() << "\n";
234     return;
235   }
236 
237   // Dump symbols
238   ci.semantics().DumpSymbols(llvm::outs());
239 }
240 
241 void DebugDumpAllAction::ExecuteAction() {
242   CompilerInstance &ci = this->instance();
243 
244   // Dump parse tree
245   auto &parseTree{instance().parsing().parseTree()};
246   llvm::outs() << "========================";
247   llvm::outs() << " Flang: parse tree dump ";
248   llvm::outs() << "========================\n";
249   Fortran::parser::DumpTree(
250       llvm::outs(), parseTree, &ci.invocation().asFortran());
251 
252   if (!ci.getRtTyTables().schemata) {
253     unsigned DiagID =
254         ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error,
255             "could not find module file for __fortran_type_info");
256     ci.diagnostics().Report(DiagID);
257     llvm::errs() << "\n";
258     return;
259   }
260 
261   // Dump symbols
262   llvm::outs() << "=====================";
263   llvm::outs() << " Flang: symbols dump ";
264   llvm::outs() << "=====================\n";
265   ci.semantics().DumpSymbols(llvm::outs());
266 }
267 
268 void DebugDumpParseTreeNoSemaAction::ExecuteAction() {
269   auto &parseTree{instance().parsing().parseTree()};
270 
271   // Dump parse tree
272   Fortran::parser::DumpTree(
273       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
274 }
275 
276 void DebugDumpParseTreeAction::ExecuteAction() {
277   auto &parseTree{instance().parsing().parseTree()};
278 
279   // Dump parse tree
280   Fortran::parser::DumpTree(
281       llvm::outs(), parseTree, &this->instance().invocation().asFortran());
282 
283   // Report fatal semantic errors
284   reportFatalSemanticErrors();
285 }
286 
287 void DebugMeasureParseTreeAction::ExecuteAction() {
288   CompilerInstance &ci = this->instance();
289 
290   // Parse. In case of failure, report and return.
291   ci.parsing().Parse(llvm::outs());
292 
293   if (!ci.parsing().messages().empty() &&
294       (ci.invocation().warnAsErr() ||
295           ci.parsing().messages().AnyFatalError())) {
296     unsigned diagID = ci.diagnostics().getCustomDiagID(
297         clang::DiagnosticsEngine::Error, "Could not parse %0");
298     ci.diagnostics().Report(diagID) << GetCurrentFileOrBufferName();
299 
300     ci.parsing().messages().Emit(
301         llvm::errs(), this->instance().allCookedSources());
302     return;
303   }
304 
305   // Report the diagnostics from parsing
306   ci.parsing().messages().Emit(llvm::errs(), ci.allCookedSources());
307 
308   auto &parseTree{*ci.parsing().parseTree()};
309 
310   // Measure the parse tree
311   MeasurementVisitor visitor;
312   Fortran::parser::Walk(parseTree, visitor);
313   llvm::outs() << "Parse tree comprises " << visitor.objects
314                << " objects and occupies " << visitor.bytes
315                << " total bytes.\n";
316 }
317 
318 void DebugPreFIRTreeAction::ExecuteAction() {
319   CompilerInstance &ci = this->instance();
320   // Report and exit if fatal semantic errors are present
321   if (reportFatalSemanticErrors()) {
322     return;
323   }
324 
325   auto &parseTree{*ci.parsing().parseTree()};
326 
327   // Dump pre-FIR tree
328   if (auto ast{Fortran::lower::createPFT(
329           parseTree, ci.invocation().semanticsContext())}) {
330     Fortran::lower::dumpPFT(llvm::outs(), *ast);
331   } else {
332     unsigned diagID = ci.diagnostics().getCustomDiagID(
333         clang::DiagnosticsEngine::Error, "Pre FIR Tree is NULL.");
334     ci.diagnostics().Report(diagID);
335   }
336 }
337 
338 void DebugDumpParsingLogAction::ExecuteAction() {
339   CompilerInstance &ci = this->instance();
340 
341   ci.parsing().Parse(llvm::errs());
342   ci.parsing().DumpParsingLog(llvm::outs());
343 }
344 
345 void GetDefinitionAction::ExecuteAction() {
346   CompilerInstance &ci = this->instance();
347 
348   // Report and exit if fatal semantic errors are present
349   if (reportFatalSemanticErrors()) {
350     return;
351   }
352 
353   parser::AllCookedSources &cs = ci.allCookedSources();
354   unsigned diagID = ci.diagnostics().getCustomDiagID(
355       clang::DiagnosticsEngine::Error, "Symbol not found");
356 
357   auto gdv = ci.invocation().frontendOpts().getDefVals;
358   auto charBlock{cs.GetCharBlockFromLineAndColumns(
359       gdv.line, gdv.startColumn, gdv.endColumn)};
360   if (!charBlock) {
361     ci.diagnostics().Report(diagID);
362     return;
363   }
364 
365   llvm::outs() << "String range: >" << charBlock->ToString() << "<\n";
366 
367   auto *symbol{ci.invocation()
368                    .semanticsContext()
369                    .FindScope(*charBlock)
370                    .FindSymbol(*charBlock)};
371   if (!symbol) {
372     ci.diagnostics().Report(diagID);
373     return;
374   }
375 
376   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
377 
378   auto sourceInfo{cs.GetSourcePositionRange(symbol->name())};
379   if (!sourceInfo) {
380     llvm_unreachable(
381         "Failed to obtain SourcePosition."
382         "TODO: Please, write a test and replace this with a diagnostic!");
383     return;
384   }
385 
386   llvm::outs() << "Found symbol name: " << symbol->name().ToString() << "\n";
387   llvm::outs() << symbol->name().ToString() << ": "
388                << sourceInfo->first.file.path() << ", "
389                << sourceInfo->first.line << ", " << sourceInfo->first.column
390                << "-" << sourceInfo->second.column << "\n";
391 }
392 
393 void GetSymbolsSourcesAction::ExecuteAction() {
394   CompilerInstance &ci = this->instance();
395 
396   // Report and exit if fatal semantic errors are present
397   if (reportFatalSemanticErrors()) {
398     return;
399   }
400 
401   ci.semantics().DumpSymbolsSources(llvm::outs());
402 }
403 
404 #include "flang/Tools/CLOptions.inc"
405 
406 // Lower the previously generated MLIR module into an LLVM IR module
407 void CodeGenAction::GenerateLLVMIR() {
408   assert(mlirModule && "The MLIR module has not been generated yet.");
409 
410   CompilerInstance &ci = this->instance();
411 
412   fir::support::loadDialects(*mlirCtx);
413   fir::support::registerLLVMTranslation(*mlirCtx);
414 
415   // Set-up the MLIR pass manager
416   mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
417 
418   pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
419   pm.enableVerifier(/*verifyPasses=*/true);
420   mlir::PassPipelineCLParser passPipeline("", "Compiler passes to run");
421 
422   // Create the pass pipeline
423   fir::createMLIRToLLVMPassPipeline(pm);
424 
425   // Run the pass manager
426   if (!mlir::succeeded(pm.run(*mlirModule))) {
427     unsigned diagID = ci.diagnostics().getCustomDiagID(
428         clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
429     ci.diagnostics().Report(diagID);
430   }
431 
432   // Translate to LLVM IR
433   llvm::Optional<llvm::StringRef> moduleName = mlirModule->getName();
434   llvmCtx = std::make_unique<llvm::LLVMContext>();
435   llvmModule = mlir::translateModuleToLLVMIR(
436       *mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
437 
438   if (!llvmModule) {
439     unsigned diagID = ci.diagnostics().getCustomDiagID(
440         clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
441     ci.diagnostics().Report(diagID);
442     return;
443   }
444 }
445 
446 void EmitLLVMAction::ExecuteAction() {
447   CompilerInstance &ci = this->instance();
448   GenerateLLVMIR();
449 
450   // If set, use the predefined outupt stream to print the generated module.
451   if (!ci.IsOutputStreamNull()) {
452     llvmModule->print(
453         ci.GetOutputStream(), /*AssemblyAnnotationWriter=*/nullptr);
454     return;
455   }
456 
457   // No predefined output stream was set. Create an output file and dump the
458   // generated module there.
459   std::unique_ptr<llvm::raw_ostream> os = ci.CreateDefaultOutputFile(
460       /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "ll");
461   if (!os) {
462     unsigned diagID = ci.diagnostics().getCustomDiagID(
463         clang::DiagnosticsEngine::Error, "failed to create the output file");
464     ci.diagnostics().Report(diagID);
465     return;
466   }
467   llvmModule->print(*os, /*AssemblyAnnotationWriter=*/nullptr);
468 }
469 
470 void EmitMLIRAction::ExecuteAction() {
471   CompilerInstance &ci = this->instance();
472 
473   // Print the output. If a pre-defined output stream exists, dump the MLIR
474   // content there.
475   if (!ci.IsOutputStreamNull()) {
476     mlirModule->print(ci.GetOutputStream());
477     return;
478   }
479 
480   // ... otherwise, print to a file.
481   std::unique_ptr<llvm::raw_pwrite_stream> os{ci.CreateDefaultOutputFile(
482       /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "mlir")};
483   if (!os) {
484     unsigned diagID = ci.diagnostics().getCustomDiagID(
485         clang::DiagnosticsEngine::Error, "failed to create the output file");
486     ci.diagnostics().Report(diagID);
487     return;
488   }
489 
490   mlirModule->print(*os);
491 }
492 
493 void EmitObjAction::ExecuteAction() {
494   CompilerInstance &ci = this->instance();
495   unsigned DiagID = ci.diagnostics().getCustomDiagID(
496       clang::DiagnosticsEngine::Error, "code-generation is not available yet");
497   ci.diagnostics().Report(DiagID);
498 }
499 
500 void InitOnlyAction::ExecuteAction() {
501   CompilerInstance &ci = this->instance();
502   unsigned DiagID =
503       ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning,
504           "Use `-init-only` for testing purposes only");
505   ci.diagnostics().Report(DiagID);
506 }
507 
508 void PluginParseTreeAction::ExecuteAction() {}
509