[clang] [CIR] Add addition frontend actions (PR #127249)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 14 11:37:02 PST 2025


https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/127249

Add frontend actions to support emitting assembly, bitcode, and object files when compiling with ClangIR.

>From 62a33eb8f488bd2fcec8f155da2ec646b3562824 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 14 Feb 2025 11:25:08 -0800
Subject: [PATCH] [CIR] Add addition frontend actions

Add frontend actions to support emitting assembly, bitcode, and
object files when compiling with ClangIR.
---
 .../clang/CIR/Dialect/IR/CIRDialect.td        |  2 ++
 .../clang/CIR/FrontendAction/CIRGenAction.h   | 24 +++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  3 ++
 clang/lib/CIR/CodeGen/CIRGenModule.h          |  4 +++
 clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 29 ++++++++++++++++++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 11 +++++++
 .../ExecuteCompilerInvocation.cpp             | 21 ++++++++++++--
 clang/test/CIR/emit-actions.cpp               | 21 ++++++++++++++
 8 files changed, 111 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/emit-actions.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index 305a06427ed0e..73759cfa9c3c9 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -28,6 +28,8 @@ def CIR_Dialect : Dialect {
   let useDefaultTypePrinterParser = 0;
 
   let extraClassDeclaration = [{
+    static llvm::StringRef getTripleAttrName() { return "cir.triple"; }
+
     void registerAttributes();
     void registerTypes();
 
diff --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
index 5f9110bc83b89..99495f4718c5f 100644
--- a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
+++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
@@ -25,8 +25,11 @@ class CIRGenConsumer;
 class CIRGenAction : public clang::ASTFrontendAction {
 public:
   enum class OutputType {
+    EmitAssembly,
     EmitCIR,
     EmitLLVM,
+    EmitBC,
+    EmitObj,
   };
 
 private:
@@ -63,6 +66,27 @@ class EmitLLVMAction : public CIRGenAction {
   EmitLLVMAction(mlir::MLIRContext *MLIRCtx = nullptr);
 };
 
+class EmitBCAction : public CIRGenAction {
+  virtual void anchor();
+
+public:
+  EmitBCAction(mlir::MLIRContext *MLIRCtx = nullptr);
+};
+
+class EmitAssemblyAction : public CIRGenAction {
+  virtual void anchor();
+
+public:
+  EmitAssemblyAction(mlir::MLIRContext *MLIRCtx = nullptr);
+};
+
+class EmitObjAction : public CIRGenAction {
+  virtual void anchor();
+
+public:
+  EmitObjAction(mlir::MLIRContext *MLIRCtx = nullptr);
+};
+
 } // namespace cir
 
 #endif
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 2615ae382cb8b..cbecdf925aa5d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -52,6 +52,9 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext,
   DoubleTy = cir::DoubleType::get(&getMLIRContext());
   FP80Ty = cir::FP80Type::get(&getMLIRContext());
   FP128Ty = cir::FP128Type::get(&getMLIRContext());
+
+  theModule->setAttr(cir::CIRDialect::getTripleAttrName(),
+                     builder.getStringAttr(getTriple().str()));
 }
 
 mlir::Location CIRGenModule::getLoc(SourceLocation cLoc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 1c7ed63773900..29bb4036218e4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -21,7 +21,9 @@
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/MLIRContext.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/TargetParser/Triple.h"
 
 namespace clang {
 class ASTContext;
@@ -88,6 +90,8 @@ class CIRGenModule : public CIRGenTypeCache {
   void emitGlobalVarDefinition(const clang::VarDecl *vd,
                                bool isTentative = false);
 
+  const llvm::Triple &getTriple() const { return target.getTriple(); }
+
   /// Helpers to emit "not yet implemented" error diagnostics
   DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef);
 
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index eab6958ac8f6d..0f686a36b982b 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -27,8 +27,14 @@ getBackendActionFromOutputType(CIRGenAction::OutputType Action) {
     assert(false &&
            "Unsupported output type for getBackendActionFromOutputType!");
     break; // Unreachable, but fall through to report that
+  case CIRGenAction::OutputType::EmitAssembly:
+    return BackendAction::Backend_EmitAssembly;
+  case CIRGenAction::OutputType::EmitBC:
+    return BackendAction::Backend_EmitBC;
   case CIRGenAction::OutputType::EmitLLVM:
     return BackendAction::Backend_EmitLL;
+  case CIRGenAction::OutputType::EmitObj:
+    return BackendAction::Backend_EmitObj;
   }
   // We should only get here if a non-enum value is passed in or we went through
   // the assert(false) case above
@@ -84,7 +90,10 @@ class CIRGenConsumer : public clang::ASTConsumer {
         MlirModule->print(*OutputStream, Flags);
       }
       break;
-    case CIRGenAction::OutputType::EmitLLVM: {
+    case CIRGenAction::OutputType::EmitLLVM:
+    case CIRGenAction::OutputType::EmitBC:
+    case CIRGenAction::OutputType::EmitObj:
+    case CIRGenAction::OutputType::EmitAssembly: {
       llvm::LLVMContext LLVMCtx;
       std::unique_ptr<llvm::Module> LLVMModule =
           lowerFromCIRToLLVMIR(MlirModule, LLVMCtx);
@@ -111,10 +120,16 @@ static std::unique_ptr<raw_pwrite_stream>
 getOutputStream(CompilerInstance &CI, StringRef InFile,
                 CIRGenAction::OutputType Action) {
   switch (Action) {
+  case CIRGenAction::OutputType::EmitAssembly:
+    return CI.createDefaultOutputFile(false, InFile, "s");
   case CIRGenAction::OutputType::EmitCIR:
     return CI.createDefaultOutputFile(false, InFile, "cir");
   case CIRGenAction::OutputType::EmitLLVM:
     return CI.createDefaultOutputFile(false, InFile, "ll");
+  case CIRGenAction::OutputType::EmitBC:
+    return CI.createDefaultOutputFile(true, InFile, "bc");
+  case CIRGenAction::OutputType::EmitObj:
+    return CI.createDefaultOutputFile(true, InFile, "o");
   }
   llvm_unreachable("Invalid CIRGenAction::OutputType");
 }
@@ -132,6 +147,10 @@ CIRGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
   return Result;
 }
 
+void EmitAssemblyAction::anchor() {}
+EmitAssemblyAction::EmitAssemblyAction(mlir::MLIRContext *MLIRCtx)
+    : CIRGenAction(OutputType::EmitAssembly, MLIRCtx) {}
+
 void EmitCIRAction::anchor() {}
 EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
     : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {}
@@ -139,3 +158,11 @@ EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
 void EmitLLVMAction::anchor() {}
 EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx)
     : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {}
+
+void EmitBCAction::anchor() {}
+EmitBCAction::EmitBCAction(mlir::MLIRContext *MLIRCtx)
+    : CIRGenAction(OutputType::EmitBC, MLIRCtx) {}
+
+void EmitObjAction::anchor() {}
+EmitObjAction::EmitObjAction(mlir::MLIRContext *MLIRCtx)
+    : CIRGenAction(OutputType::EmitObj, MLIRCtx) {}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index d60a6b38b0c12..5915a9517b028 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -114,6 +114,8 @@ struct ConvertCIRToLLVMPass
   }
   void runOnOperation() final;
 
+  void processCIRAttrs(mlir::ModuleOp module);
+
   StringRef getDescription() const override {
     return "Convert the prepared CIR dialect module to LLVM dialect";
   }
@@ -271,6 +273,13 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
   });
 }
 
+void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) {
+  // Lower the module attributes to LLVM equivalents.
+  if (auto tripleAttr = module->getAttr(cir::CIRDialect::getTripleAttrName()))
+    module->setAttr(mlir::LLVM::LLVMDialect::getTargetTripleAttrName(),
+                    tripleAttr);
+}
+
 void ConvertCIRToLLVMPass::runOnOperation() {
   llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
 
@@ -283,6 +292,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
 
   patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl);
 
+  processCIRAttrs(module);
+
   mlir::ConversionTarget target(getContext());
   target.addLegalOp<mlir::ModuleOp>();
   target.addLegalDialect<mlir::LLVM::LLVMDialect>();
diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index c8d004163b96d..bb3bb0aac78bf 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -62,8 +62,18 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
     return std::make_unique<DumpCompilerOptionsAction>();
   case DumpRawTokens:          return std::make_unique<DumpRawTokensAction>();
   case DumpTokens:             return std::make_unique<DumpTokensAction>();
-  case EmitAssembly:           return std::make_unique<EmitAssemblyAction>();
-  case EmitBC:                 return std::make_unique<EmitBCAction>();
+  case EmitAssembly:
+#if CLANG_ENABLE_CIR
+    if (UseCIR)
+      return std::make_unique<cir::EmitAssemblyAction>();
+#endif
+    return std::make_unique<EmitAssemblyAction>();
+  case EmitBC:
+#if CLANG_ENABLE_CIR
+    if (UseCIR)
+      return std::make_unique<cir::EmitBCAction>();
+#endif
+    return std::make_unique<EmitBCAction>();
   case EmitCIR:
 #if CLANG_ENABLE_CIR
     return std::make_unique<cir::EmitCIRAction>();
@@ -80,7 +90,12 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
   }
   case EmitLLVMOnly:           return std::make_unique<EmitLLVMOnlyAction>();
   case EmitCodeGenOnly:        return std::make_unique<EmitCodeGenOnlyAction>();
-  case EmitObj:                return std::make_unique<EmitObjAction>();
+  case EmitObj:
+#if CLANG_ENABLE_CIR
+    if (UseCIR)
+      return std::make_unique<cir::EmitObjAction>();
+#endif
+    return std::make_unique<EmitObjAction>();
   case ExtractAPI:
     return std::make_unique<ExtractAPIAction>();
   case FixIt:                  return std::make_unique<FixItAction>();
diff --git a/clang/test/CIR/emit-actions.cpp b/clang/test/CIR/emit-actions.cpp
new file mode 100644
index 0000000000000..94ddf23b34753
--- /dev/null
+++ b/clang/test/CIR/emit-actions.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S %s -o - | FileCheck %s -check-prefix=ASM
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm-bc %s -o %t.bc
+// RUN: llvm-dis %t.bc -o - | FileCheck %s -check-prefix=BC
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-obj %s -o %t.o
+// RUN: llvm-objdump -t %t.o | FileCheck %s -check-prefix=OBJ
+
+// TODO: Make this test target-independent
+// REQUIRES: x86-registered-target
+
+int x = 1;
+
+// BC: @x = dso_local global i32 1
+
+// ASM: x:
+// ASM: .long   1
+// ASM: .size   x, 4
+
+// OBJ: .data
+// OBJ-SAME: x



More information about the cfe-commits mailing list