Author: Bruno Cardoso Lopes
Date: 2026-10-08T13:54:00-07:00
New Revision: 986f4954120aae80201ff3187853c69db594d98c

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

LOG: [CIR] Enable bytecode emission through the driver and cc1 (#229607)

Teach the driver to serialize CIR directly to bytecode: new EmitCIRBC
frontend action kind, `TY_CIRBC` (.cirbc) driver type, and
EmitCIRBCAction writing the in-memory module with
`mlir::writeBytecodeToFile` instead of printing text. `.cirbc` inputs
map to the CIR language like .cir.

Tests cover cc1 emission with magic-byte and read-back pins, .cir-input
and .cirbc-input round trips, and driver forwarding, phase selection,
default output name, and flag-implied pipeline selection.

I started proper bytecode encoding support in #229586, so this is a step
into using that through the driver.

Added: 
    clang/test/CIR/Driver/emit-cir-bc.c

Modified: 
    clang/include/clang/Basic/DiagnosticCodeGenKinds.td
    clang/include/clang/CIR/FrontendAction/CIRGenAction.h
    clang/include/clang/Driver/Types.def
    clang/include/clang/Frontend/FrontendOptions.h
    clang/include/clang/Options/Options.td
    clang/lib/CIR/FrontendAction/CIRGenAction.cpp
    clang/lib/CIR/FrontendAction/CMakeLists.txt
    clang/lib/Driver/Driver.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Driver/Types.cpp
    clang/lib/Frontend/CompilerInvocation.cpp
    clang/lib/Frontend/FrontendOptions.cpp
    clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
    clang/test/CIR/emit-actions.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticCodeGenKinds.td 
b/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
index f135cc5df30574..c8b8bb38b20ea2 100644
--- a/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
@@ -20,6 +20,9 @@ let Component = "CodeGen" in {
 def err_cir_to_cir_transform_failed : Error<
     "CIR-to-CIR transformation failed">, DefaultFatal;
 
+def err_cir_bc_write_failed : Error<
+    "could not write ClangIR bytecode to output stream">, DefaultFatal;
+
 def err_cir_verification_failed_pre_passes : Error<
     "CIR module verification error before running CIR-to-CIR passes">,
     DefaultFatal;

diff  --git a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h 
b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
index f7ca27fa6b5a24..9f8bad248ea47e 100644
--- a/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
+++ b/clang/include/clang/CIR/FrontendAction/CIRGenAction.h
@@ -33,6 +33,7 @@ class CIRGenAction : public clang::ASTFrontendAction {
   enum class OutputType {
     EmitAssembly,
     EmitCIR,
+    EmitCIRBC,
     EmitLLVM,
     EmitBC,
     EmitObj,
@@ -74,6 +75,13 @@ class EmitCIRAction : public CIRGenAction {
   EmitCIRAction(mlir::MLIRContext *MLIRCtx = nullptr);
 };
 
+class EmitCIRBCAction : public CIRGenAction {
+  virtual void anchor();
+
+public:
+  EmitCIRBCAction(mlir::MLIRContext *MLIRCtx = nullptr);
+};
+
 class EmitLLVMAction : public CIRGenAction {
   virtual void anchor();
 

diff  --git a/clang/include/clang/Driver/Types.def 
b/clang/include/clang/Driver/Types.def
index 38ccdaaebe717e..dd9fe5053c8fa7 100644
--- a/clang/include/clang/Driver/Types.def
+++ b/clang/include/clang/Driver/Types.def
@@ -104,6 +104,7 @@ TYPE("lto-ir",                   LTO_IR,       INVALID,     
    "s",      phases
 TYPE("lto-bc",                   LTO_BC,       INVALID,         "o",      
phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 TYPE("cir",                      CIR,          INVALID,         "cir",    
phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cir-bc",                   CIRBC,        INVALID,         "cirbc",  
phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 // Misc.
 TYPE("ast",                      AST,          INVALID,         "ast",    
phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 TYPE("ifs",                      IFS,          INVALID,         "ifs",    
phases::IfsMerge)

diff  --git a/clang/include/clang/Frontend/FrontendOptions.h 
b/clang/include/clang/Frontend/FrontendOptions.h
index cb32509aa09e2c..7e2b0936525662 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -68,6 +68,9 @@ enum ActionKind {
   /// Emit a .cir file
   EmitCIR,
 
+  /// Emit a .cirbc file (ClangIR bytecode).
+  EmitCIRBC,
+
   /// Emit a .ll file.
   EmitLLVM,
 

diff  --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 2e6905a0d3065c..160c647286aac9 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3557,6 +3557,9 @@ defm clangir : BoolFOption<"clangir",
   BothFlags<[], [ClangOption, CC1Option], "">>;
 def emit_cir : Flag<["-"], "emit-cir">, Visibility<[ClangOption, CC1Option]>,
   Group<Action_Group>, HelpText<"Build ASTs and then lower to ClangIR">;
+def emit_cir_bc : Flag<["-"], "emit-cir-bc">,
+  Visibility<[CC1Option]>, Group<Action_Group>,
+  HelpText<"Build ASTs and then emit ClangIR bytecode">;
 /// ClangIR-specific options - END
 
 def flto_EQ : Joined<["-"], "flto=">,

diff  --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp 
b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 627e659f62ba8c..71a7a17146af3d 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -8,6 +8,7 @@
 
 #include "clang/CIR/FrontendAction/CIRGenAction.h"
 #include "CIRDiagnosticHandler.h"
+#include "mlir/Bytecode/BytecodeWriter.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/MLIRContext.h"
 #include "mlir/IR/OwningOpRef.h"
@@ -48,6 +49,7 @@ static BackendAction
 getBackendActionFromOutputType(CIRGenAction::OutputType Action) {
   switch (Action) {
   case CIRGenAction::OutputType::EmitCIR:
+  case CIRGenAction::OutputType::EmitCIRBC:
     assert(false &&
            "Unsupported output type for getBackendActionFromOutputType!");
     break; // Unreachable, but fall through to report that
@@ -194,6 +196,12 @@ class CIRGenConsumer : public clang::ASTConsumer {
       if (OutputStream && MlirModule)
         printCIRModule(MlirModule, *OutputStream);
       break;
+    case CIRGenAction::OutputType::EmitCIRBC:
+      if (OutputStream && MlirModule &&
+          failed(mlir::writeBytecodeToFile(MlirModule, *OutputStream)) &&
+          !CI.getDiagnostics().hasErrorOccurred())
+        CI.getDiagnostics().Report(diag::err_cir_bc_write_failed);
+      break;
     case CIRGenAction::OutputType::EmitLLVM:
     case CIRGenAction::OutputType::EmitBC:
     case CIRGenAction::OutputType::EmitObj:
@@ -433,6 +441,11 @@ void CIRGenAction::ExecuteAction() {
     // been through it, and its lowering passes are not idempotent.
     printCIRModule(*Module, *OS);
     break;
+  case OutputType::EmitCIRBC:
+    if (failed(mlir::writeBytecodeToFile(*Module, *OS)) &&
+        !Diags.hasErrorOccurred())
+      Diags.Report(diag::err_cir_bc_write_failed);
+    break;
   case OutputType::EmitLLVM:
   case OutputType::EmitBC:
   case OutputType::EmitObj:
@@ -450,6 +463,8 @@ getOutputStream(CompilerInstance &CI, StringRef InFile,
     return CI.createDefaultOutputFile(false, InFile, "s");
   case CIRGenAction::OutputType::EmitCIR:
     return CI.createDefaultOutputFile(false, InFile, "cir");
+  case CIRGenAction::OutputType::EmitCIRBC:
+    return CI.createDefaultOutputFile(true, InFile, "cirbc");
   case CIRGenAction::OutputType::EmitLLVM:
     return CI.createDefaultOutputFile(false, InFile, "ll");
   case CIRGenAction::OutputType::EmitBC:
@@ -481,6 +496,10 @@ void EmitCIRAction::anchor() {}
 EmitCIRAction::EmitCIRAction(mlir::MLIRContext *MLIRCtx)
     : CIRGenAction(OutputType::EmitCIR, MLIRCtx) {}
 
+void EmitCIRBCAction::anchor() {}
+EmitCIRBCAction::EmitCIRBCAction(mlir::MLIRContext *MLIRCtx)
+    : CIRGenAction(OutputType::EmitCIRBC, MLIRCtx) {}
+
 void EmitLLVMAction::anchor() {}
 EmitLLVMAction::EmitLLVMAction(mlir::MLIRContext *MLIRCtx)
     : CIRGenAction(OutputType::EmitLLVM, MLIRCtx) {}

diff  --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt 
b/clang/lib/CIR/FrontendAction/CMakeLists.txt
index be46ce69659316..d19496939af60b 100644
--- a/clang/lib/CIR/FrontendAction/CMakeLists.txt
+++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt
@@ -26,6 +26,7 @@ add_clang_library(clangCIRFrontendAction
   clangCodeGenUtils
   CIRRegisterAllDialects
   MLIRCIR
+  MLIRBytecodeWriter
   MLIRIR
   MLIRParser
   )

diff  --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 006c3d2f0044d2..a5bd672b8de478 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4452,8 +4452,13 @@ Action *Driver::ConstructPhaseAction(
       return C.MakeAction<AnalyzeJobAction>(Input, types::TY_Plist);
     if (Args.hasArg(options::OPT_emit_ast))
       return C.MakeAction<CompileJobAction>(Input, types::TY_AST);
-    if (Args.hasArg(options::OPT_emit_cir))
+    if (Args.hasArg(options::OPT_emit_cir)) {
+      // Like -emit-llvm, -c selects the binary form; bare -emit-cir and
+      // -emit-cir -S keep emitting text.
+      if (Args.hasArg(options::OPT_c) && !Args.hasArg(options::OPT_S))
+        return C.MakeAction<CompileJobAction>(Input, types::TY_CIRBC);
       return C.MakeAction<CompileJobAction>(Input, types::TY_CIR);
+    }
     if (Args.hasArg(options::OPT_module_file_info))
       return C.MakeAction<CompileJobAction>(Input, types::TY_ModuleFile);
     if (Args.hasArg(options::OPT_verify_pch))

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 2325e7cc552ef9..4c8530f01fd9a8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -357,6 +357,9 @@ static void addDashXForInput(const ArgList &Args, const 
InputInfo &Input,
     case types::TY_PP_CXXModule:
       ClangType = "c++-cpp-output";
       break;
+    case types::TY_CIRBC:
+      ClangType = "cir";
+      break;
     default:
       ClangType = types::getTypeName(Input.getType());
       break;
@@ -5582,6 +5585,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       rewriteKind = RK_Fragile;
     } else if (JA.getType() == types::TY_CIR) {
       CmdArgs.push_back("-emit-cir");
+    } else if (JA.getType() == types::TY_CIRBC) {
+      CmdArgs.push_back("-emit-cir-bc");
     } else if (JA.getType() == types::TY_Image && IsAMDSPIRVForHIPDevice) {
       CmdArgs.push_back("-emit-obj");
     } else {

diff  --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp
index 4cca8acd515d7b..a3acdc97283084 100644
--- a/clang/lib/Driver/Types.cpp
+++ b/clang/lib/Driver/Types.cpp
@@ -171,6 +171,8 @@ bool types::isAcceptedByClang(ID Id) {
   case TY_PP_CXXStdModule:
   case TY_AST: case TY_ModuleFile: case TY_PCH:
   case TY_LLVM_IR: case TY_LLVM_BC:
+  case TY_CIR:
+  case TY_CIRBC:
   case TY_API_INFO:
     return true;
   }
@@ -367,6 +369,8 @@ types::ID types::lookupTypeForExtension(llvm::StringRef 
Ext) {
       .Case("CPP", TY_CXX)
       .Case("c++", TY_CXX)
       .Case("C++", TY_CXX)
+      .Case("cir", TY_CIR)
+      .Case("cirbc", TY_CIRBC)
       .Case("cui", TY_PP_CUDA)
       .Case("cxx", TY_CXX)
       .Case("CXX", TY_CXX)

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 3d652a0304e21b..824b5d195098de 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2816,6 +2816,7 @@ static const auto &getFrontendActionTable() {
       {frontend::EmitAssembly, OPT_S},
       {frontend::EmitBC, OPT_emit_llvm_bc},
       {frontend::EmitCIR, OPT_emit_cir},
+      {frontend::EmitCIRBC, OPT_emit_cir_bc},
       {frontend::EmitHTML, OPT_emit_html},
       {frontend::EmitLLVM, OPT_emit_llvm},
       {frontend::EmitLLVMOnly, OPT_emit_llvm_only},
@@ -3179,7 +3180,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, 
ArgList &Args,
   if (Opts.ProgramAction != frontend::GenerateModule && Opts.IsSystemModule)
     Diags.Report(diag::err_drv_argument_only_allowed_with) << "-fsystem-module"
                                                            << "-emit-module";
-  if (Args.hasArg(OPT_emit_cir))
+  if (Args.hasArg(OPT_emit_cir, OPT_emit_cir_bc))
     Opts.UseClangIRPipeline = true;
 
 #if CLANG_ENABLE_CIR
@@ -4748,6 +4749,7 @@ static bool 
isStrictlyPreprocessorAction(frontend::ActionKind Action) {
   case frontend::EmitAssembly:
   case frontend::EmitBC:
   case frontend::EmitCIR:
+  case frontend::EmitCIRBC:
   case frontend::EmitHTML:
   case frontend::EmitLLVM:
   case frontend::EmitLLVMOnly:
@@ -4789,6 +4791,7 @@ static bool isCodeGenAction(frontend::ActionKind Action) {
   case frontend::EmitAssembly:
   case frontend::EmitBC:
   case frontend::EmitCIR:
+  case frontend::EmitCIRBC:
   case frontend::EmitHTML:
   case frontend::EmitLLVM:
   case frontend::EmitLLVMOnly:

diff  --git a/clang/lib/Frontend/FrontendOptions.cpp 
b/clang/lib/Frontend/FrontendOptions.cpp
index fb178b6b942e21..32db5306e35b51 100644
--- a/clang/lib/Frontend/FrontendOptions.cpp
+++ b/clang/lib/Frontend/FrontendOptions.cpp
@@ -35,6 +35,6 @@ InputKind FrontendOptions::getInputKindForExtension(StringRef 
Extension) {
       .Case("hip", Language::HIP)
       .Cases({"ll", "bc"}, Language::LLVM_IR)
       .Case("hlsl", Language::HLSL)
-      .Case("cir", Language::CIR)
+      .Cases({"cir", "cirbc"}, Language::CIR)
       .Default(Language::Unknown);
 }

diff  --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp 
b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 24e699b42ca7fa..48d9e0acd3a781 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -56,10 +56,11 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
 
   unsigned UseCIR = CI.getFrontendOpts().UseClangIRPipeline;
   frontend::ActionKind Act = CI.getFrontendOpts().ProgramAction;
-  bool EmitsCIR = Act == EmitCIR;
+  bool EmitsCIR = Act == EmitCIR || Act == EmitCIRBC;
 
   if (!UseCIR && EmitsCIR)
-    llvm::report_fatal_error("-emit-cir and only valid when using -fclangir");
+    llvm::report_fatal_error(
+        "-emit-cir and -emit-cir-bc only valid when using -fclangir");
 
   switch (CI.getFrontendOpts().ProgramAction) {
   case ASTDeclList:            return std::make_unique<ASTDeclListAction>();
@@ -88,6 +89,13 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
 #else
     CI.getDiagnostics().Report(diag::err_fe_cir_not_built);
     return nullptr;
+#endif
+  case EmitCIRBC:
+#if CLANG_ENABLE_CIR
+    return std::make_unique<cir::EmitCIRBCAction>();
+#else
+    CI.getDiagnostics().Report(diag::err_fe_cir_not_built);
+    return nullptr;
 #endif
   case EmitHTML:               return std::make_unique<HTMLPrintAction>();
   case EmitLLVM: {

diff  --git a/clang/test/CIR/Driver/emit-cir-bc.c 
b/clang/test/CIR/Driver/emit-cir-bc.c
new file mode 100644
index 00000000000000..78bc4a3b9a6c14
--- /dev/null
+++ b/clang/test/CIR/Driver/emit-cir-bc.c
@@ -0,0 +1,28 @@
+// -emit-cir follows the -emit-llvm model: -c selects the binary form,
+// bare -emit-cir and -emit-cir -S keep emitting text. -emit-cir-bc is
+// cc1-only.
+
+// RUN: %clang -### -fclangir -emit-cir -c %s 2>&1 | FileCheck %s 
--check-prefix=BC
+// BC: "-cc1"
+// BC-SAME: "-emit-cir-bc"
+// BC-SAME: "-o" "emit-cir-bc.cirbc"
+
+// RUN: %clang -ccc-print-phases -fclangir -emit-cir -c %s 2>&1 | FileCheck %s 
--check-prefix=PHASES
+// PHASES: compiler, {{.*}}, cir-bc
+
+// RUN: %clang -### -fclangir -emit-cir %s 2>&1 | FileCheck %s 
--check-prefix=TEXT
+// RUN: %clang -### -fclangir -emit-cir -S %s 2>&1 | FileCheck %s 
--check-prefix=TEXT
+// TEXT: "-cc1"
+// TEXT-SAME: "-emit-cir"
+// TEXT-SAME: "-o" "emit-cir-bc.cir"
+
+// RUN: not %clang -fclangir -emit-cir-bc %s 2>&1 | FileCheck %s 
--check-prefix=REJECT
+// REJECT: unknown argument '-emit-cir-bc'
+
+// The driver accepts .cirbc inputs, so bytecode round-trips through it.
+// RUN: %clang -fclangir -emit-cir -c %s -o %t.cirbc
+// RUN: %clang -fclangir -x cir-bc -emit-cir %t.cirbc -o - | FileCheck %s 
--check-prefix=ROUNDTRIP
+// RUN: %clang -fclangir -emit-cir %t.cirbc -o - | FileCheck %s 
--check-prefix=ROUNDTRIP
+// ROUNDTRIP: cir.func {{.*}}@foo()
+
+void foo() {}

diff  --git a/clang/test/CIR/emit-actions.cpp b/clang/test/CIR/emit-actions.cpp
index 3cfb3d32e8e2c0..2e9e4d961dfabc 100644
--- a/clang/test/CIR/emit-actions.cpp
+++ b/clang/test/CIR/emit-actions.cpp
@@ -6,6 +6,17 @@
 // 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
 
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir-bc %s 
-o %t.cirbc
+// RUN: od -An -c -N4 %t.cirbc | FileCheck %s --check-prefix=CIRBC-MAGIC
+// RUN: not grep -q "cir.global" %t.cirbc
+// RUN: cir-opt %t.cirbc | FileCheck %s -check-prefix=CIRBC
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -x cir 
-emit-cir-bc %t.cir -o %t2.cirbc
+// RUN: cir-opt %t2.cirbc | FileCheck %s --check-prefix=CIRBC
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir 
%t.cirbc -o - | FileCheck %s --check-prefix=CIRBC
+
 // TODO: Make this test target-independent
 // REQUIRES: x86-registered-target
 
@@ -19,3 +30,7 @@ int x = 1;
 
 // OBJ: .data
 // OBJ-SAME: x
+
+// CIRBC-MAGIC: M L 357 R
+
+// CIRBC: cir.global {{.*}}@x = #cir.int<1> : !s32i


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to