[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