diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -16,6 +16,7 @@ #include "mlir/IR/BuiltinOps.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/Module.h" +#include "llvm/Target/TargetMachine.h" #include namespace Fortran::frontend { @@ -170,17 +171,33 @@ //===----------------------------------------------------------------------===// // CodeGen Actions //===----------------------------------------------------------------------===// +/// Represents the type of "backend" action to perform by the corresponding +/// CodeGenAction. Note that from Flang's perspective, both LLVM and MLIR are +/// "backends" that are used for generating LLVM IR/BC, assembly files or +/// machine code. This enum captures "what" exactly one of these backends is to +/// do. The names are similar to what is used in Clang - this allows us to +/// maintain some level of consistency/similarity between the drivers. +enum class BackendActionTy { + Backend_EmitAssembly, ///< Emit native assembly files + Backend_EmitObj, ///< Emit native object files + Backend_EmitBC, ///< Emit LLVM bitcode files + Backend_EmitLL, ///< Emit human-readable LLVM assembly + Backend_EmitMLIR ///< Emit MLIR files +}; + /// Abstract base class for actions that generate code (MLIR, LLVM IR, assembly /// and machine code). Every action that inherits from this class will at /// least run the prescanning, parsing, semantic checks and lower the parse /// tree to an MLIR module. class CodeGenAction : public FrontendAction { - void ExecuteAction() override = 0; + void ExecuteAction() override; /// Runs prescan, parsing, sema and lowers to MLIR. bool BeginSourceFileAction() override; + void SetUpTargetMachine(); protected: + CodeGenAction(BackendActionTy act) : action{act} {}; /// @name MLIR /// { std::unique_ptr mlirModule; @@ -194,34 +211,38 @@ /// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it /// in CodeGenAction::llvmModule. void GenerateLLVMIR(); + + BackendActionTy action; + + std::unique_ptr TM; /// } +public: + ~CodeGenAction() override; }; class EmitMLIRAction : public CodeGenAction { - void ExecuteAction() override; +public: + EmitMLIRAction() : CodeGenAction(BackendActionTy::Backend_EmitMLIR) {} }; class EmitLLVMAction : public CodeGenAction { - void ExecuteAction() override; +public: + EmitLLVMAction() : CodeGenAction(BackendActionTy::Backend_EmitLL) {} }; class EmitLLVMBitcodeAction : public CodeGenAction { - void ExecuteAction() override; +public: + EmitLLVMBitcodeAction() : CodeGenAction(BackendActionTy::Backend_EmitBC) {} }; -class BackendAction : public CodeGenAction { +class EmitObjAction : public CodeGenAction { public: - enum class BackendActionTy { - Backend_EmitAssembly, ///< Emit native assembly files - Backend_EmitObj ///< Emit native object files - }; - - BackendAction(BackendActionTy act) : action{act} {}; - -private: - void ExecuteAction() override; + EmitObjAction() : CodeGenAction(BackendActionTy::Backend_EmitObj) {} +}; - BackendActionTy action; +class EmitAssemblyAction : public CodeGenAction { +public: + EmitAssemblyAction() : CodeGenAction(BackendActionTy::Backend_EmitAssembly) {} }; } // namespace Fortran::frontend diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -36,9 +36,11 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/IRReader/IRReader.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Target/TargetMachine.h" #include #include @@ -48,6 +50,7 @@ //===----------------------------------------------------------------------===// // Custom BeginSourceFileAction //===----------------------------------------------------------------------===// + bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); } bool PrescanAndParseAction::BeginSourceFileAction() { @@ -409,6 +412,12 @@ ci.semantics().DumpSymbolsSources(llvm::outs()); } +//===----------------------------------------------------------------------===// +// CodeGenActions +//===----------------------------------------------------------------------===// + +CodeGenAction::~CodeGenAction() = default; + #include "flang/Tools/CLOptions.inc" // Lower the previously generated MLIR module into an LLVM IR module @@ -451,36 +460,8 @@ } } -void EmitLLVMAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); - GenerateLLVMIR(); - - // If set, use the predefined outupt stream to print the generated module. - if (!ci.IsOutputStreamNull()) { - llvmModule->print( - ci.GetOutputStream(), /*AssemblyAnnotationWriter=*/nullptr); - return; - } - - // No predefined output stream was set. Create an output file and dump the - // generated module there. - std::unique_ptr os = ci.CreateDefaultOutputFile( - /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "ll"); - if (!os) { - unsigned diagID = ci.diagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Error, "failed to create the output file"); - ci.diagnostics().Report(diagID); - return; - } - llvmModule->print(*os, /*AssemblyAnnotationWriter=*/nullptr); -} - -void EmitLLVMBitcodeAction::ExecuteAction() { +void CodeGenAction::SetUpTargetMachine() { CompilerInstance &ci = this->instance(); - // Generate an LLVM module if it's not already present (it will already be - // present if the input file is an LLVM IR/BC file). - if (!llvmModule) - GenerateLLVMIR(); // Set the triple based on the CompilerInvocation set-up const std::string &theTriple = ci.invocation().targetOpts().triple; @@ -495,83 +476,103 @@ llvm::TargetRegistry::lookupTarget(theTriple, error); assert(theTarget && "Failed to create Target"); - // Create and configure `TargetMachine` - std::unique_ptr TM( - theTarget->createTargetMachine(theTriple, /*CPU=*/"", - /*Features=*/"", llvm::TargetOptions(), llvm::None)); + // Create `TargetMachine` + TM.reset(theTarget->createTargetMachine(theTriple, /*CPU=*/"", + /*Features=*/"", + llvm::TargetOptions(), llvm::None)); assert(TM && "Failed to create TargetMachine"); llvmModule->setDataLayout(TM->createDataLayout()); +} - // Generate an output file - std::unique_ptr os = ci.CreateDefaultOutputFile( - /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "bc"); - if (!os) { - unsigned diagID = ci.diagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Error, "failed to create the output file"); - ci.diagnostics().Report(diagID); - return; +static std::unique_ptr +GetOutputStream(CompilerInstance &ci, llvm::StringRef inFile, + BackendActionTy action) { + switch (action) { + case BackendActionTy::Backend_EmitAssembly: + return ci.CreateDefaultOutputFile( + /*Binary=*/false, inFile, /*extension=*/"s"); + case BackendActionTy::Backend_EmitLL: + return ci.CreateDefaultOutputFile( + /*Binary=*/false, inFile, /*extension=*/"ll"); + case BackendActionTy::Backend_EmitMLIR: + return ci.CreateDefaultOutputFile( + /*Binary=*/false, inFile, /*extension=*/"mlir"); + case BackendActionTy::Backend_EmitBC: + return ci.CreateDefaultOutputFile( + /*Binary=*/true, inFile, /*extension=*/"bc"); + case BackendActionTy::Backend_EmitObj: + return ci.CreateDefaultOutputFile( + /*Binary=*/true, inFile, /*extension=*/"o"); } - // Set-up the pass manager - llvm::ModulePassManager MPM; - llvm::ModuleAnalysisManager MAM; - llvm::PassBuilder PB(TM.get()); - PB.registerModuleAnalyses(MAM); - MPM.addPass(llvm::BitcodeWriterPass(*os)); - - // Run the passes - MPM.run(*llvmModule, MAM); + llvm_unreachable("Invalid action!"); } -void EmitMLIRAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); +/// Generate target-specific machine-code or assembly file from the input LLVM +/// module. +/// +/// \param [in] diags Diagnostics engine for reporting errors +/// \param [in] TM Target machine to aid the code-gen pipeline set-up +/// \param [in] act Backend act to run (assembly vs machine-code generation) +/// \param [in] llvmModule LLVM module to lower to assembly/machine-code +/// \param [out] os Output stream to emit the generated code to +static void GenerateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags, + llvm::TargetMachine &TM, + BackendActionTy act, + llvm::Module &llvmModule, + llvm::raw_pwrite_stream &os) { + assert((act == BackendActionTy::Backend_EmitObj) || + (act == BackendActionTy::Backend_EmitAssembly) && ""); + + // Set-up the pass manager, i.e create an LLVM code-gen pass pipeline. + // Currently only the legacy pass manager is supported. + // TODO: Switch to the new PM once it's available in the backend. + llvm::legacy::PassManager CodeGenPasses; + CodeGenPasses.add( + createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis())); - // Print the output. If a pre-defined output stream exists, dump the MLIR - // content there. - if (!ci.IsOutputStreamNull()) { - mlirModule->print(ci.GetOutputStream()); - return; - } + llvm::Triple triple(llvmModule.getTargetTriple()); + std::unique_ptr TLII = + std::make_unique(triple); + assert(TLII && "Failed to create TargetLibraryInfo"); + CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); - // ... otherwise, print to a file. - std::unique_ptr os{ci.CreateDefaultOutputFile( - /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "mlir")}; - if (!os) { - unsigned diagID = ci.diagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Error, "failed to create the output file"); - ci.diagnostics().Report(diagID); + llvm::CodeGenFileType cgft = (act == BackendActionTy::Backend_EmitAssembly) + ? llvm::CodeGenFileType::CGFT_AssemblyFile + : llvm::CodeGenFileType::CGFT_ObjectFile; + if (TM.addPassesToEmitFile(CodeGenPasses, os, nullptr, cgft)) { + unsigned diagID = + diags.getCustomDiagID(clang::DiagnosticsEngine::Error, + "emission of this file type is not supported"); + diags.Report(diagID); return; } - mlirModule->print(*os); + // Run the passes + CodeGenPasses.run(llvmModule); } -void BackendAction::ExecuteAction() { - CompilerInstance &ci = this->instance(); - // Generate an LLVM module if it's not already present (it will already be - // present if the input file is an LLVM IR/BC file). - if (!llvmModule) - GenerateLLVMIR(); - - // Set the triple based on the CompilerInvocation set-up - const std::string &theTriple = ci.invocation().targetOpts().triple; - if (llvmModule->getTargetTriple() != theTriple) { - ci.diagnostics().Report(clang::diag::warn_fe_override_module) << theTriple; - llvmModule->setTargetTriple(theTriple); - } +/// Generate LLVM byte code file from the input LLVM module. +/// +/// \param [in] TM Target machine to aid the code-gen pipeline set-up +/// \param [in] llvmModule LLVM module to lower to assembly/machine-code +/// \param [out] os Output stream to emit the generated code to +static void GenerateLLVMBCImpl(llvm::TargetMachine &TM, + llvm::Module &llvmModule, + llvm::raw_pwrite_stream &os) { + // Set-up the pass manager + llvm::ModulePassManager MPM; + llvm::ModuleAnalysisManager MAM; + llvm::PassBuilder PB(&TM); + PB.registerModuleAnalyses(MAM); + MPM.addPass(llvm::BitcodeWriterPass(os)); - // Create `Target` - std::string error; - const llvm::Target *theTarget = - llvm::TargetRegistry::lookupTarget(theTriple, error); - assert(theTarget && "Failed to create Target"); + // Run the passes + MPM.run(llvmModule, MAM); +} - // Create `TargetMachine` - std::unique_ptr TM( - theTarget->createTargetMachine(theTriple, /*CPU=*/"", - /*Features=*/"", llvm::TargetOptions(), llvm::None)); - assert(TM && "Failed to create TargetMachine"); - llvmModule->setDataLayout(TM->createDataLayout()); +void CodeGenAction::ExecuteAction() { + CompilerInstance &ci = this->instance(); // If the output stream is a file, generate it and define the corresponding // output stream. If a pre-defined output stream is available, we will use @@ -587,17 +588,8 @@ // updated to use it). std::unique_ptr os; if (ci.IsOutputStreamNull()) { - // Get the output buffer/file - switch (action) { - case BackendActionTy::Backend_EmitAssembly: - os = ci.CreateDefaultOutputFile( - /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "s"); - break; - case BackendActionTy::Backend_EmitObj: - os = ci.CreateDefaultOutputFile( - /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "o"); - break; - } + os = GetOutputStream(ci, GetCurrentFileOrBufferName(), action); + if (!os) { unsigned diagID = ci.diagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "failed to create the output file"); @@ -606,34 +598,35 @@ } } - // Create an LLVM code-gen pass pipeline. Currently only the legacy pass - // manager is supported. - // TODO: Switch to the new PM once it's available in the backend. - llvm::legacy::PassManager CodeGenPasses; - CodeGenPasses.add( - createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); - llvm::Triple triple(theTriple); + if (action == BackendActionTy::Backend_EmitMLIR) { + mlirModule->print(ci.IsOutputStreamNull() ? *os : ci.GetOutputStream()); + return; + } - std::unique_ptr TLII = - std::make_unique(triple); - assert(TLII && "Failed to create TargetLibraryInfo"); - CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); + // Generate an LLVM module if it's not already present (it will already be + // present if the input file is an LLVM IR/BC file). + if (!llvmModule) + GenerateLLVMIR(); - llvm::CodeGenFileType cgft = (action == BackendActionTy::Backend_EmitAssembly) - ? llvm::CodeGenFileType::CGFT_AssemblyFile - : llvm::CodeGenFileType::CGFT_ObjectFile; - if (TM->addPassesToEmitFile(CodeGenPasses, - ci.IsOutputStreamNull() ? *os : ci.GetOutputStream(), nullptr, - cgft)) { - unsigned diagID = - ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, - "emission of this file type is not supported"); - ci.diagnostics().Report(diagID); + if (action == BackendActionTy::Backend_EmitLL) { + llvmModule->print(ci.IsOutputStreamNull() ? *os : ci.GetOutputStream(), + /*AssemblyAnnotationWriter=*/nullptr); + return; + } + + SetUpTargetMachine(); + if (action == BackendActionTy::Backend_EmitBC) { + GenerateLLVMBCImpl(*TM, *llvmModule, *os); return; } - // Run the code-gen passes - CodeGenPasses.run(*llvmModule); + if (action == BackendActionTy::Backend_EmitAssembly || + action == BackendActionTy::Backend_EmitObj) { + GenerateMachineCodeOrAssemblyImpl( + ci.diagnostics(), *TM, action, *llvmModule, + ci.IsOutputStreamNull() ? *os : ci.GetOutputStream()); + return; + } } void InitOnlyAction::ExecuteAction() { diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -41,11 +41,9 @@ case EmitLLVMBitcode: return std::make_unique(); case EmitObj: - return std::make_unique( - BackendAction::BackendActionTy::Backend_EmitObj); + return std::make_unique(); case EmitAssembly: - return std::make_unique( - BackendAction::BackendActionTy::Backend_EmitAssembly); + return std::make_unique(); case DebugUnparse: return std::make_unique(); case DebugUnparseNoSema: