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