[clang] e993b20 - [flang][driver] Add support for `-emit-llvm`

Andrzej Warzynski via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 17 04:13:14 PST 2022


Author: Andrzej Warzynski
Date: 2022-02-17T12:13:03Z
New Revision: e993b20c049d2f933831c26139f024e022f3d7fe

URL: https://github.com/llvm/llvm-project/commit/e993b20c049d2f933831c26139f024e022f3d7fe
DIFF: https://github.com/llvm/llvm-project/commit/e993b20c049d2f933831c26139f024e022f3d7fe.diff

LOG: [flang][driver] Add support for `-emit-llvm`

This patch adds support for the `-emit-llvm` option in the frontend
driver (i.e. `flang-new -fc1`). Similarly to Clang, `flang-new -fc1
-emit-llvm file.f` will generate a textual LLVM IR file.

Depends on D118985

Differential Revision: https://reviews.llvm.org/D119012

Added: 
    flang/test/Driver/emit-llvm.f90

Modified: 
    clang/include/clang/Driver/Options.td
    flang/include/flang/Frontend/FrontendActions.h
    flang/include/flang/Frontend/FrontendOptions.h
    flang/lib/Frontend/CompilerInvocation.cpp
    flang/lib/Frontend/FrontendActions.cpp
    flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
    flang/test/Driver/driver-help.f90
    flang/unittests/Frontend/FrontendActionTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index b81973155cae6..37a8e9b77bbfb 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1071,7 +1071,7 @@ def d_Flag : Flag<["-"], "d">, Group<d_Group>;
 def d_Joined : Joined<["-"], "d">, Group<d_Group>;
 def emit_ast : Flag<["-"], "emit-ast">,
   HelpText<"Emit Clang AST files for source inputs">;
-def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group<Action_Group>,
+def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option, FC1Option]>, Group<Action_Group>,
   HelpText<"Use the LLVM representation for assembler and object files">;
 def emit_interface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group<Action_Group>,
   HelpText<"Generate Interface Stub Files.">;

diff  --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h
index e3def74e0f417..6a9afd1afc5c0 100644
--- a/flang/include/flang/Frontend/FrontendActions.h
+++ b/flang/include/flang/Frontend/FrontendActions.h
@@ -13,6 +13,7 @@
 #include "flang/Semantics/semantics.h"
 
 #include "mlir/IR/BuiltinOps.h"
+#include "llvm/IR/Module.h"
 #include <memory>
 
 namespace Fortran::frontend {
@@ -163,12 +164,25 @@ class CodeGenAction : public FrontendAction {
   std::unique_ptr<mlir::ModuleOp> mlirModule;
   std::unique_ptr<mlir::MLIRContext> mlirCtx;
   /// }
+
+  /// @name LLVM IR
+  std::unique_ptr<llvm::LLVMContext> llvmCtx;
+  std::unique_ptr<llvm::Module> llvmModule;
+
+  /// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it
+  /// in CodeGenAction::llvmModule.
+  void GenerateLLVMIR();
+  /// }
 };
 
 class EmitMLIRAction : public CodeGenAction {
   void ExecuteAction() override;
 };
 
+class EmitLLVMAction : public CodeGenAction {
+  void ExecuteAction() override;
+};
+
 class EmitObjAction : public CodeGenAction {
   void ExecuteAction() override;
 };

diff  --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h
index 0ff8d0a758873..060910e3d67cd 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -34,6 +34,9 @@ enum ActionKind {
   /// Emit a .mlir file
   EmitMLIR,
 
+  /// Emit an .ll file
+  EmitLLVM,
+
   /// Emit a .o file.
   EmitObj,
 
@@ -84,9 +87,6 @@ enum ActionKind {
 
   /// Run a plugin action
   PluginAction
-
-  /// TODO: RunPreprocessor, EmitLLVM, EmitLLVMOnly,
-  /// EmitCodeGenOnly, EmitAssembly, (...)
 };
 
 /// \param suffix The file extension

diff  --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index af59cb6636b3a..7507b0091e13c 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -137,6 +137,9 @@ static bool ParseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
     case clang::driver::options::OPT_emit_mlir:
       opts.programAction = EmitMLIR;
       break;
+    case clang::driver::options::OPT_emit_llvm:
+      opts.programAction = EmitLLVM;
+      break;
     case clang::driver::options::OPT_emit_obj:
       opts.programAction = EmitObj;
       break;

diff  --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index d981faaa84980..43ab3f689522d 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -14,6 +14,7 @@
 #include "flang/Lower/Bridge.h"
 #include "flang/Lower/PFTBuilder.h"
 #include "flang/Lower/Support/Verifier.h"
+#include "flang/Optimizer/Support/FIRContext.h"
 #include "flang/Optimizer/Support/InitFIR.h"
 #include "flang/Optimizer/Support/KindMapping.h"
 #include "flang/Optimizer/Support/Utils.h"
@@ -28,6 +29,7 @@
 
 #include "mlir/IR/Dialect.h"
 #include "mlir/Pass/PassManager.h"
+#include "mlir/Target/LLVMIR/ModuleTranslation.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <clang/Basic/Diagnostic.h>
@@ -407,6 +409,72 @@ void GetSymbolsSourcesAction::ExecuteAction() {
   ci.semantics().DumpSymbolsSources(llvm::outs());
 }
 
+#include "flang/Tools/CLOptions.inc"
+
+// Lower the previously generated MLIR module into an LLVM IR module
+void CodeGenAction::GenerateLLVMIR() {
+  assert(mlirModule && "The MLIR module has not been generated yet.");
+
+  CompilerInstance &ci = this->instance();
+
+  fir::support::loadDialects(*mlirCtx);
+  fir::support::registerLLVMTranslation(*mlirCtx);
+
+  // Set-up the MLIR pass manager
+  mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit);
+
+  pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
+  pm.enableVerifier(/*verifyPasses=*/true);
+  mlir::PassPipelineCLParser passPipeline("", "Compiler passes to run");
+
+  // Create the pass pipeline
+  fir::createMLIRToLLVMPassPipeline(pm);
+
+  // Run the pass manager
+  if (!mlir::succeeded(pm.run(*mlirModule))) {
+    unsigned diagID = ci.diagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
+    ci.diagnostics().Report(diagID);
+  }
+
+  // Translate to LLVM IR
+  llvm::Optional<llvm::StringRef> moduleName = mlirModule->getName();
+  llvmCtx = std::make_unique<llvm::LLVMContext>();
+  llvmModule = mlir::translateModuleToLLVMIR(
+      *mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
+
+  if (!llvmModule) {
+    unsigned diagID = ci.diagnostics().getCustomDiagID(
+        clang::DiagnosticsEngine::Error, "failed to create the LLVM module");
+    ci.diagnostics().Report(diagID);
+    return;
+  }
+}
+
+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 EmitMLIRAction::ExecuteAction() {
   CompilerInstance &ci = this->instance();
 

diff  --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index d69242f58666b..d5c15b1c7b567 100644
--- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -35,6 +35,8 @@ static std::unique_ptr<FrontendAction> CreateFrontendBaseAction(
     return std::make_unique<ParseSyntaxOnlyAction>();
   case EmitMLIR:
     return std::make_unique<EmitMLIRAction>();
+  case EmitLLVM:
+    return std::make_unique<EmitLLVMAction>();
   case EmitObj:
     return std::make_unique<EmitObjAction>();
   case DebugUnparse:

diff  --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90
index 622548d353411..73d4697591919 100644
--- a/flang/test/Driver/driver-help.f90
+++ b/flang/test/Driver/driver-help.f90
@@ -65,6 +65,7 @@
 ! HELP-FC1-NEXT:OPTIONS:
 ! HELP-FC1-NEXT: -cpp                   Enable predefined and command line preprocessor macros
 ! HELP-FC1-NEXT: -D <macro>=<value>     Define <macro> to <value> (or 1 if <value> omitted)
+! HELP-FC1-NEXT: -emit-llvm Use the LLVM representation for assembler and object files
 ! HELP-FC1-NEXT: -emit-mlir Build the parse tree, then lower it to MLIR
 ! HELP-FC1-NEXT: -emit-obj Emit native object files
 ! HELP-FC1-NEXT: -E                     Only run the preprocessor

diff  --git a/flang/test/Driver/emit-llvm.f90 b/flang/test/Driver/emit-llvm.f90
new file mode 100644
index 0000000000000..c62680d6b5fbc
--- /dev/null
+++ b/flang/test/Driver/emit-llvm.f90
@@ -0,0 +1,22 @@
+! Test the `-emit-llvm` option
+
+! UNSUPPORTED: system-windows
+! Windows is currently not supported in flang/lib/Optimizer/CodeGen/Target.cpp
+
+!------------
+! RUN COMMAND
+!------------
+! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s
+
+!----------------
+! EXPECTED OUTPUT
+!----------------
+! CHECK: ; ModuleID = 'FIRModule'
+! CHECK: define void @_QQmain()
+! CHECK-NEXT:  ret void
+! CHECK-NEXT: }
+
+!------
+! INPUT
+!------
+end program

diff  --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp
index 249392eb22882..81a57be1fad01 100644
--- a/flang/unittests/Frontend/FrontendActionTest.cpp
+++ b/flang/unittests/Frontend/FrontendActionTest.cpp
@@ -161,4 +161,31 @@ TEST_F(FrontendActionTest, ParseSyntaxOnly) {
           .contains(
               ":1:14: error: IF statement is not allowed in IF statement\n"));
 }
+
+TEST_F(FrontendActionTest, EmitLLVM) {
+  // Populate the input file with the pre-defined input and flush it.
+  *(inputFileOs_) << "end program";
+  inputFileOs_.reset();
+
+  // Set-up the action kind.
+  compInst_.invocation().frontendOpts().programAction = EmitLLVM;
+  compInst_.invocation().preprocessorOpts().noReformat = true;
+
+  // Set-up the output stream. We are using output buffer wrapped as an output
+  // stream, as opposed to an actual file (or a file descriptor).
+  llvm::SmallVector<char> outputFileBuffer;
+  std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
+      new llvm::raw_svector_ostream(outputFileBuffer));
+  compInst_.set_outputStream(std::move(outputFileStream));
+
+  // Execute the action.
+  bool success = ExecuteCompilerInvocation(&compInst_);
+
+  // Validate the expected output.
+  EXPECT_TRUE(success);
+  EXPECT_TRUE(!outputFileBuffer.empty());
+
+  EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
+                  .contains("define void @_QQmain()"));
+}
 } // namespace


        


More information about the cfe-commits mailing list