[clang] [CIR] Lower .cir cc1 input to LLVM IR, assembly and objects (PR #232506)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 15 14:49:52 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Konstantinos Parasyris (koparasy)
<details>
<summary>Changes</summary>
Clang now supports lowering .cir cc1 input to LLVM IR, assembly, and objects.
The lowering does not execute the cir-to-cir passes; it only translates the input CIR to LLVM IR, assembly, and objects. This assumes that the input CIR is well-formed and valid and emitted the way -emit-cir emits this today.
There will be some future PR that enables emission earlier and allows the pipeline to apply CIR passes before lowering to LLVM IR, assembly, and objects.
---
Full diff: https://github.com/llvm/llvm-project/pull/232506.diff
9 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticCodeGenKinds.td (+3)
- (modified) clang/include/clang/Basic/DiagnosticFrontendKinds.td (-2)
- (modified) clang/include/clang/CIR/LowerToLLVM.h (+3)
- (modified) clang/lib/CIR/FrontendAction/CIRGenAction.cpp (+15-18)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+4-10)
- (modified) clang/test/CIR/Input/cir-input-invalid.cir (+5-2)
- (added) clang/test/CIR/Input/cir-input-lower-error.cir (+24)
- (added) clang/test/CIR/Input/cir-input-lower.cpp (+54)
- (removed) clang/test/CIR/Input/cir-input-unsupported.cir (-29)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticCodeGenKinds.td b/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
index c8b8bb38b20ea2..d01f8bcbf481be 100644
--- a/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCodeGenKinds.td
@@ -27,6 +27,9 @@ def err_cir_verification_failed_pre_passes : Error<
"CIR module verification error before running CIR-to-CIR passes">,
DefaultFatal;
+def err_cir_to_llvm_lowering_failed : Error<
+ "lowering ClangIR to LLVM IR failed">, DefaultFatal;
+
// Diagnostics relayed from MLIR-side passes/verifier through the
// clang::DiagnosticsEngine. The full message text comes from MLIR.
def err_cir_mlir_diagnostic : Error<"%0">;
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 66eeb08034bf4b..3df87215a0476d 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -146,8 +146,6 @@ def err_fe_action_not_available : Error<
"action %0 not compiled in">;
def err_fe_cir_not_built : Error<"clang IR support not available, rebuild "
"clang with -DCLANG_ENABLE_CIR=ON">;
-def err_fe_cir_input_unsupported : Error<
- "support for ClangIR input is not yet implemented">;
def err_invalid_cir : Error<"invalid ClangIR input">;
def err_cir_input_missing_triple : Error<
"missing target triple in ClangIR input">;
diff --git a/clang/include/clang/CIR/LowerToLLVM.h b/clang/include/clang/CIR/LowerToLLVM.h
index d97ac5265c282b..f746a5e211c3bc 100644
--- a/clang/include/clang/CIR/LowerToLLVM.h
+++ b/clang/include/clang/CIR/LowerToLLVM.h
@@ -28,6 +28,9 @@ class ModuleOp;
namespace cir {
namespace direct {
+/// Lowers \p mlirModule, a CIR module, to an LLVM module in \p llvmCtx.
+/// Returns null if lowering fails; the error has then been reported through
+/// the MLIR diagnostic engine of \p mlirModule's context.
std::unique_ptr<llvm::Module>
lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule,
llvm::LLVMContext &llvmCtx, bool enableOpenMP,
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index c1f7c5117c54bb..b0d35d0ddc56ad 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -173,6 +173,13 @@ static void emitCIRModuleOutput(
std::unique_ptr<llvm::Module> LLVMModule =
lowerFromCIRToLLVMIR(MlirModule, LLVMCtx, EnableOpenMP,
mlirSaveTempsOutFile, &CI.getVirtualFileSystem());
+ if (!LLVMModule) {
+ // Lowering errors are already routed through the MLIR diagnostic
+ // handler. Only emit the generic error if nothing was reported.
+ if (!CI.getDiagnostics().hasErrorOccurred())
+ CI.getDiagnostics().Report(diag::err_cir_to_llvm_lowering_failed);
+ return;
+ }
LLVMModule->setDataLayout(CI.getTarget().getDataLayoutString());
@@ -432,24 +439,14 @@ void CIRGenAction::ExecuteAction() {
return;
}
- switch (Action) {
- case OutputType::EmitCIR:
- // The CIR-to-CIR pipeline is not run: CIR printed by -emit-cir has already
- // 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:
- case OutputType::EmitAssembly:
- Diags.Report(diag::err_fe_cir_input_unsupported);
- break;
- }
+ // The CIR-to-CIR pipeline is not run: CIR printed by -emit-cir has already
+ // been through it, and its lowering passes are not idempotent.
+ // TODO: Decide from a pipeline-stage attribute on the module which passes
+ // still have to run, once -emit-cir can stop before ABI lowering.
+ BackendDiagnosticConsumer DiagConsumer(Diags, CI.getCodeGenOpts());
+ DiagConsumer.setSourceManager(&SM);
+ emitCIRModuleOutput(Action, CI, *Module, *Ctx, LinkModules, DiagConsumer,
+ std::move(OS));
}
static std::unique_ptr<raw_pwrite_stream>
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index ddfc28da80388d..60ad37198fcfbe 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -6441,11 +6441,10 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx,
(void)mlir::applyPassManagerCLOptions(pm);
- if (mlir::failed(pm.run(mlirModule))) {
- // FIXME: Handle any errors where they occurs and return a nullptr here.
- report_fatal_error(
- "The pass manager failed to lower CIR to LLVMIR dialect!");
- }
+ // The failing pass has already reported its error through the MLIR
+ // diagnostic engine.
+ if (mlir::failed(pm.run(mlirModule)))
+ return nullptr;
if (!mlirSaveTempsOutFile.empty()) {
std::error_code ec;
@@ -6465,11 +6464,6 @@ lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx,
std::unique_ptr<llvm::Module> llvmModule = mlir::translateModuleToLLVMIR(
mlirModule, llvmCtx, moduleName, /*disableVerification=*/false, fs);
- if (!llvmModule) {
- // FIXME: Handle any errors where they occurs and return a nullptr here.
- report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
- }
-
return llvmModule;
}
} // namespace direct
diff --git a/clang/test/CIR/Input/cir-input-invalid.cir b/clang/test/CIR/Input/cir-input-invalid.cir
index 1a791d843bb0a4..cc64546eed3580 100644
--- a/clang/test/CIR/Input/cir-input-invalid.cir
+++ b/clang/test/CIR/Input/cir-input-invalid.cir
@@ -1,6 +1,5 @@
// Invalid ClangIR input is diagnosed, not crashed on, before any output is
-// produced. Backend outputs, which are not supported from ClangIR input yet,
-// report the same errors.
+// produced. Backend outputs report the same errors.
// RUN: rm -rf %t && split-file %s %t
@@ -12,6 +11,8 @@
// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/verifier.cir -o - 2>&1 \
// RUN: | FileCheck %s --check-prefix=VERIFIER --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %t/verifier.cir -o /dev/null 2>&1 \
+// RUN: | FileCheck %s --check-prefix=VERIFIER --implicit-check-not=error:
// VERIFIER: verifier.cir:5:5: error: 'cir.return' op returns '!cir.int<s, 64>' but enclosing function returns '!cir.int<s, 32>'
// Ops in CIR emitted by CIRGen are located in the original source, which is not
@@ -33,6 +34,8 @@
// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/mismatch.cir -o - 2>&1 \
// RUN: | FileCheck %s --check-prefix=MISMATCH --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %t/mismatch.cir -o /dev/null 2>&1 \
+// RUN: | FileCheck %s --check-prefix=MISMATCH --implicit-check-not=error:
// MISMATCH: error: target triple 'aarch64-unknown-linux-gnu' of ClangIR input does not match the target 'x86_64-unknown-linux-gnu'
//--- syntax.cir
diff --git a/clang/test/CIR/Input/cir-input-lower-error.cir b/clang/test/CIR/Input/cir-input-lower-error.cir
new file mode 100644
index 00000000000000..2f135bdd0f9a39
--- /dev/null
+++ b/clang/test/CIR/Input/cir-input-lower-error.cir
@@ -0,0 +1,24 @@
+// Errors from lowering ClangIR input to LLVM IR are diagnosed, not crashed on.
+// The ctor region of @gs is only removed by LoweringPrepare, which is not run
+// on ClangIR input.
+
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - 2>&1 \
+// RUN: | FileCheck %s --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %s -o /dev/null 2>&1 \
+// RUN: | FileCheck %s --implicit-check-not=error:
+
+// No partial output is left behind.
+// RUN: rm -f %t.o && not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %s -o %t.o 2>/dev/null
+// RUN: not ls %t.o
+
+// CHECK: cir-input-lower-error.cir:[[@LINE+6]]:3: error: GlobalOp ctor and dtor regions should be removed in LoweringPrepare
+// CHECK: cir-input-lower-error.cir:[[@LINE+5]]:3: error: failed to legalize operation 'cir.global'
+
+!rec_S = !cir.struct<"S" {data !cir.int<s, 32>}>
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
+ cir.global external @gs = #cir.zero : !rec_S ctor {
+ %0 = cir.get_global @gs : !cir.ptr<!rec_S>
+ cir.call @_ZN1SC1Ev(%0) : (!cir.ptr<!rec_S>) -> ()
+ }
+}
diff --git a/clang/test/CIR/Input/cir-input-lower.cpp b/clang/test/CIR/Input/cir-input-lower.cpp
new file mode 100644
index 00000000000000..0459deed07c2db
--- /dev/null
+++ b/clang/test/CIR/Input/cir-input-lower.cpp
@@ -0,0 +1,54 @@
+// Lowering ClangIR input to LLVM IR, assembly, bitcode or an object gives the
+// same result as compiling the source directly. The CIR-to-CIR pipeline is not
+// run again on input printed by -emit-cir, whose lowering passes are not
+// idempotent; the code below exercises the calling convention, global
+// constructor and TLS wrapper lowering.
+
+// 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 -emit-llvm %s -o %t.direct.ll
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %t.cir -o %t.ll
+// RUN: diff %t.direct.ll %t.ll
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S %s -o %t.direct.s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -S %t.cir -o %t.s
+// RUN: diff %t.direct.s %t.s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-obj %s -o %t.direct.o
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %t.cir -o %t.o
+// RUN: cmp %t.direct.o %t.o
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm-bc %s -o %t.direct.bc
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm-bc %t.cir -o %t.bc
+// RUN: cmp %t.direct.bc %t.bc
+
+// Without -o, the output is named after the input.
+// RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir
+// RUN: cp %t.cir input.cir
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj input.cir
+// RUN: cmp %t.direct.o input.o
+
+// Code generation options still come from the command line.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -fclangir -emit-cir %s -o %t.O2.cir
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -fclangir -emit-llvm %s -o %t.O2.direct.ll
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -emit-llvm -x cir %t.O2.cir -o %t.O2.ll
+// RUN: diff %t.O2.direct.ll %t.O2.ll
+
+typedef float v2f __attribute__((vector_size(8)));
+
+v2f mk(float a) { return (v2f){a, a}; }
+
+struct S {
+ S();
+ ~S();
+ int v;
+};
+
+S gs;
+
+thread_local int tl = 3;
+
+int get() {
+ static S local;
+ return local.v + tl;
+}
diff --git a/clang/test/CIR/Input/cir-input-unsupported.cir b/clang/test/CIR/Input/cir-input-unsupported.cir
deleted file mode 100644
index b087871af1d04e..00000000000000
--- a/clang/test/CIR/Input/cir-input-unsupported.cir
+++ /dev/null
@@ -1,29 +0,0 @@
-// A .cir input is routed to the ClangIR frontend action instead of being
-// parsed as C. Only -emit-cir is supported from ClangIR input so far, so every
-// other CIR action reports a single diagnostic.
-
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -x cir %s -o - 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-obj -x cir %s -o /dev/null 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -S -x cir %s -o - 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm-bc -x cir %s -o /dev/null 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-
-// ClangIR input implies -fclangir.
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -x cir %s -o - 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-
-// The .cir extension selects ClangIR input without -x.
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %s -o /dev/null 2>&1 \
-// RUN: | FileCheck %s --implicit-check-not=error:
-
-// CHECK: error: support for ClangIR input is not yet implemented
-
-!s32i = !cir.int<s, 32>
-module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
- cir.func @f(%arg0: !s32i) -> !s32i {
- cir.return %arg0 : !s32i
- }
-}
``````````
</details>
https://github.com/llvm/llvm-project/pull/232506
More information about the cfe-commits
mailing list