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