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