[flang-commits] [flang] bb177ed - [flang][driver] Re-organise the code-gen actions (nfc)
Andrzej Warzynski via flang-commits
flang-commits at lists.llvm.org
Thu May 5 07:06:55 PDT 2022
Author: Andrzej Warzynski
Date: 2022-05-05T14:05:06Z
New Revision: bb177edc44f412f368c4c5983df15b7364fc3122
URL: https://github.com/llvm/llvm-project/commit/bb177edc44f412f368c4c5983df15b7364fc3122
DIFF: https://github.com/llvm/llvm-project/commit/bb177edc44f412f368c4c5983df15b7364fc3122.diff
LOG: [flang][driver] Re-organise the code-gen actions (nfc)
All frontend actions that generate code (MLIR, LLVM IR/BC,
Assembly/Object Code) are re-factored as essentially one action,
`CodeGenAction`, with minor specialisations. To facilate all this,
`CodeGenAction` is extended to hold `TargetMachine` and backend action
type (MLIR vs LLVM IR vs LLVM BC vs Assembly vs Object Code).
`CodeGenAction` is no longer a pure abstract class and the
corresponding `ExecuteAction` is implemented so that it covers all use
cases. All this allows a much better code re-use.
Key functionality is extracted into some helpful hooks:
* `SetUpTargetMachine`
* `GetOutputStream`
* `EmitObjectCodeHelper`
* `EmitBCHelper`
I hope that this clarifies the overall structure. I suspect that we may
need to revisit this again as the functionality grows in complexity.
Differential Revision: https://reviews.llvm.org/D124665
Added:
Modified:
flang/include/flang/Frontend/FrontendActions.h
flang/lib/Frontend/FrontendActions.cpp
flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index 4831b1ece955f..d2dce86affecf 100644
--- 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 <memory>
namespace Fortran::frontend {
@@ -170,17 +171,33 @@ class DebugDumpAllAction : public PrescanAndSemaDebugAction {
//===----------------------------------------------------------------------===//
// 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<mlir::ModuleOp> mlirModule;
@@ -194,34 +211,38 @@ class CodeGenAction : public FrontendAction {
/// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it
/// in CodeGenAction::llvmModule.
void GenerateLLVMIR();
+
+ BackendActionTy action;
+
+ std::unique_ptr<llvm::TargetMachine> 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
index d37d0ac037236..3965c49022716 100644
--- 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 <clang/Basic/Diagnostic.h>
#include <memory>
@@ -48,6 +50,7 @@ using namespace Fortran::frontend;
//===----------------------------------------------------------------------===//
// Custom BeginSourceFileAction
//===----------------------------------------------------------------------===//
+
bool PrescanAction::BeginSourceFileAction() { return RunPrescan(); }
bool PrescanAndParseAction::BeginSourceFileAction() {
@@ -409,6 +412,12 @@ void GetSymbolsSourcesAction::ExecuteAction() {
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 CodeGenAction::GenerateLLVMIR() {
}
}
-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<llvm::raw_ostream> 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,104 @@ void EmitLLVMBitcodeAction::ExecuteAction() {
llvm::TargetRegistry::lookupTarget(theTriple, error);
assert(theTarget && "Failed to create Target");
- // Create and configure `TargetMachine`
- std::unique_ptr<llvm::TargetMachine> 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<llvm::raw_ostream> 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<llvm::raw_pwrite_stream>
+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) &&
+ "Unsupported action");
+
+ // 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<llvm::TargetLibraryInfoImpl> TLII =
+ std::make_unique<llvm::TargetLibraryInfoImpl>(triple);
+ assert(TLII && "Failed to create TargetLibraryInfo");
+ CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII));
- // ... otherwise, print to a file.
- std::unique_ptr<llvm::raw_pwrite_stream> 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<llvm::TargetMachine> 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 +589,8 @@ void BackendAction::ExecuteAction() {
// updated to use it).
std::unique_ptr<llvm::raw_pwrite_stream> 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 +599,35 @@ void BackendAction::ExecuteAction() {
}
}
- // 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<llvm::TargetLibraryInfoImpl> TLII =
- std::make_unique<llvm::TargetLibraryInfoImpl>(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
index 8f5b3132f654d..7aef815d95d70 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -41,11 +41,9 @@ static std::unique_ptr<FrontendAction> CreateFrontendAction(
case EmitLLVMBitcode:
return std::make_unique<EmitLLVMBitcodeAction>();
case EmitObj:
- return std::make_unique<BackendAction>(
- BackendAction::BackendActionTy::Backend_EmitObj);
+ return std::make_unique<EmitObjAction>();
case EmitAssembly:
- return std::make_unique<BackendAction>(
- BackendAction::BackendActionTy::Backend_EmitAssembly);
+ return std::make_unique<EmitAssemblyAction>();
case DebugUnparse:
return std::make_unique<DebugUnparseAction>();
case DebugUnparseNoSema:
More information about the flang-commits
mailing list