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