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