[clang] [CIR] Parse .cir cc1 input and support -emit-cir from it (PR #228288)
Konstantinos Parasyris via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 4 10:54:41 PDT 2026
https://github.com/koparasy updated https://github.com/llvm/llvm-project/pull/228288
>From 9c28ee300e15f75c989db556cd97b6e555a9ee38 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Thu, 1 Oct 2026 17:41:16 -0700
Subject: [PATCH 1/3] [clang][CIR] Parse .cir cc1 input and support -emit-cir
from it
Fill in the Language::CIR path of CIRGenAction::ExecuteAction: register
the ClangIR dialects, parse and verify the main file into a ModuleOp
with MLIR diagnostics routed through CIRDiagnosticHandler, and require
the module's cir.triple to match the target triple. A mismatch is an
error rather than an override, because CIR emitted by -emit-cir has
already been lowered for the target ABI of its own triple.
It is still unclear whether we should disallow any overrides of module
CIRGen attribues. At this point in time, I opt to go with a conservative
approach.
The CIR-to-CIR pipeline is not run, because -emit-cir output has already
been through it and its lowering passes are not idempotent; recording
the pipeline stage on the module is left for a follow-up. Other outputs
still report that ClangIR input is not yet supported.
`registerAllDialects`` now registers the LLVM dialect. The data layout
spec that CIRGen attaches to every module uses LLVM pointer types as
keys, so parsing CIR needs it. Until now every CIR tool registered it on
top of registerAllDialects, and the CIRGen context only got it as a
dependency of the eagerly loaded OpenACC and OpenMP dialects.
Note: Parse errors point into the .cir input. Verifier errors on CIR emitted
by CIRGen carry locations in the original source file, which is not
loaded in the SourceManager, so they are reported without a location for
now; a TODO records the open question of where they should point and how
to best handle this.
The round-trip tests for C and C++ check that the re-emitted CIR is
byte-identical to the original.
Part of https://github.com/llvm/llvm-project/issues/227917.
---
.../clang/Basic/DiagnosticFrontendKinds.td | 5 +
clang/lib/CIR/CMakeLists.txt | 1 +
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 92 +++++++++++++++++--
clang/lib/CIR/FrontendAction/CMakeLists.txt | 2 +
clang/lib/CIR/RegisterAllDialects.cpp | 6 +-
clang/test/CIR/Input/cir-input-emit-cir.c | 29 ++++++
clang/test/CIR/Input/cir-input-emit-cir.cpp | 30 ++++++
clang/test/CIR/Input/cir-input-invalid.cir | 53 +++++++++++
.../test/CIR/Input/cir-input-unsupported.cir | 8 +-
9 files changed, 212 insertions(+), 14 deletions(-)
create mode 100644 clang/test/CIR/Input/cir-input-emit-cir.c
create mode 100644 clang/test/CIR/Input/cir-input-emit-cir.cpp
create mode 100644 clang/test/CIR/Input/cir-input-invalid.cir
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 840bade066430..2980d42561675 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -148,6 +148,11 @@ 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">;
+def err_cir_input_triple_mismatch : Error<
+ "target triple '%0' of ClangIR input does not match the target '%1'">;
def err_fe_invalid_multiple_actions : Error<
"'%0' action ignored; '%1' action specified previously">;
def err_fe_invalid_alignment : Error<
diff --git a/clang/lib/CIR/CMakeLists.txt b/clang/lib/CIR/CMakeLists.txt
index eebb1131f914c..6f9f4bb941aad 100644
--- a/clang/lib/CIR/CMakeLists.txt
+++ b/clang/lib/CIR/CMakeLists.txt
@@ -15,4 +15,5 @@ add_clang_library(CIRRegisterAllDialects
CIROpenACCSupport
CIROpenMPSupport
MLIRDLTIDialect
+ MLIRLLVMDialect
)
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 840095c1d0533..5c8b8e0be6a75 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -8,13 +8,17 @@
#include "clang/CIR/FrontendAction/CIRGenAction.h"
#include "CIRDiagnosticHandler.h"
+#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OwningOpRef.h"
+#include "mlir/Parser/Parser.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/DiagnosticCodeGen.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/CIR/CIRGenerator.h"
#include "clang/CIR/CIRToCIRPasses.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/InitAllDialects.h"
#include "clang/CIR/LowerToLLVM.h"
#include "clang/CodeGen/BackendUtil.h"
#include "clang/CodeGen/ModuleLinker.h"
@@ -70,6 +74,14 @@ lowerFromCIRToLLVMIR(mlir::ModuleOp MLIRModule, llvm::LLVMContext &LLVMCtx,
mlirSaveTempsOutFile, fs);
}
+// Print \p MLIRModule the way -emit-cir does, so that CIR emitted from source
+// and CIR printed back from ClangIR input use the same form.
+static void printCIRModule(mlir::ModuleOp MLIRModule, raw_ostream &OS) {
+ mlir::OpPrintingFlags Flags;
+ Flags.enableDebugInfo(/*enable=*/true, /*prettyForm=*/false);
+ MLIRModule->print(OS, Flags);
+}
+
class CIRGenConsumer : public clang::ASTConsumer {
virtual void anchor();
@@ -179,11 +191,8 @@ class CIRGenConsumer : public clang::ASTConsumer {
switch (Action) {
case CIRGenAction::OutputType::EmitCIR:
- if (OutputStream && MlirModule) {
- mlir::OpPrintingFlags Flags;
- Flags.enableDebugInfo(/*enable=*/true, /*prettyForm=*/false);
- MlirModule->print(*OutputStream, Flags);
- }
+ if (OutputStream && MlirModule)
+ printCIRModule(MlirModule, *OutputStream);
break;
case CIRGenAction::OutputType::EmitLLVM:
case CIRGenAction::OutputType::EmitBC:
@@ -355,15 +364,82 @@ bool CIRGenAction::BeginSourceFileAction(CompilerInstance &CI) {
return ASTFrontendAction::BeginSourceFileAction(CI);
}
+static std::unique_ptr<raw_pwrite_stream>
+getOutputStream(CompilerInstance &CI, StringRef InFile,
+ CIRGenAction::OutputType Action);
+
void CIRGenAction::ExecuteAction() {
if (getCurrentFileKind().getLanguage() != Language::CIR) {
ASTFrontendAction::ExecuteAction();
return;
}
- // TODO: Parse the ClangIR input and emit the requested output.
- getCompilerInstance().getDiagnostics().Report(
- diag::err_fe_cir_input_unsupported);
+ CompilerInstance &CI = getCompilerInstance();
+ DiagnosticsEngine &Diags = CI.getDiagnostics();
+ SourceManager &SM = CI.getSourceManager();
+
+ std::unique_ptr<raw_pwrite_stream> OS = CI.takeOutputStream();
+ if (!OS)
+ OS = getOutputStream(CI, getCurrentFileOrBufferName(), Action);
+ if (!OS)
+ return;
+
+ std::optional<llvm::MemoryBufferRef> MainFile =
+ SM.getBufferOrNone(SM.getMainFileID());
+ if (!MainFile)
+ return;
+
+ mlir::MLIRContext MLIRContext;
+ cir::registerAllDialects(MLIRContext);
+
+ // Route parser and verifier errors through clang's diagnostics. Parse
+ // errors point into the .cir input. Verifier errors use the location of
+ // the failing op, which for CIR emitted by CIRGen is a location in the
+ // original source file; that file is not loaded in the SourceManager, so
+ // such errors are currently reported without a location.
+ // TODO: Decide where errors in CIRGen-produced input should point: at the
+ // .cir text, or at the original source.
+ CIRDiagnosticHandler DiagHandler(&MLIRContext, Diags, SM,
+ CI.getFileManager());
+
+ mlir::OwningOpRef<mlir::ModuleOp> Module =
+ mlir::parseSourceString<mlir::ModuleOp>(MainFile->getBuffer(),
+ mlir::ParserConfig(&MLIRContext),
+ MainFile->getBufferIdentifier());
+ if (!Module) {
+ if (!Diags.hasErrorOccurred())
+ Diags.Report(diag::err_invalid_cir);
+ return;
+ }
+
+ // CIR is lowered for the target ABI of its own triple, so a module emitted
+ // for a different target cannot be retargeted by overriding its triple.
+ auto ModuleTriple = mlir::dyn_cast_if_present<mlir::StringAttr>(
+ (*Module)->getAttr(cir::CIRDialect::getTripleAttrName()));
+ if (!ModuleTriple) {
+ Diags.Report(diag::err_cir_input_missing_triple);
+ return;
+ }
+ const std::string &TargetTriple = CI.getTarget().getTriple().str();
+ if (ModuleTriple.getValue() != TargetTriple) {
+ Diags.Report(diag::err_cir_input_triple_mismatch)
+ << ModuleTriple.getValue() << TargetTriple;
+ 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::EmitLLVM:
+ case OutputType::EmitBC:
+ case OutputType::EmitObj:
+ case OutputType::EmitAssembly:
+ Diags.Report(diag::err_fe_cir_input_unsupported);
+ break;
+ }
}
static std::unique_ptr<raw_pwrite_stream>
diff --git a/clang/lib/CIR/FrontendAction/CMakeLists.txt b/clang/lib/CIR/FrontendAction/CMakeLists.txt
index 4fd0e01f71ed0..be46ce6965931 100644
--- a/clang/lib/CIR/FrontendAction/CMakeLists.txt
+++ b/clang/lib/CIR/FrontendAction/CMakeLists.txt
@@ -24,6 +24,8 @@ add_clang_library(clangCIRFrontendAction
clangCIRLoweringDirectToLLVM
clangCodeGen
clangCodeGenUtils
+ CIRRegisterAllDialects
MLIRCIR
MLIRIR
+ MLIRParser
)
diff --git a/clang/lib/CIR/RegisterAllDialects.cpp b/clang/lib/CIR/RegisterAllDialects.cpp
index 8c2961bdebbaa..73effa12b3192 100644
--- a/clang/lib/CIR/RegisterAllDialects.cpp
+++ b/clang/lib/CIR/RegisterAllDialects.cpp
@@ -9,6 +9,7 @@
#include "clang/CIR/InitAllDialects.h"
#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/BuiltinDialect.h"
@@ -21,8 +22,11 @@
namespace cir {
void registerAllDialects(mlir::DialectRegistry ®istry) {
+ // The LLVM dialect is needed to parse CIR: data layout specs use LLVM
+ // pointer types as keys.
registry.insert<mlir::BuiltinDialect, cir::CIRDialect, mlir::DLTIDialect,
- mlir::omp::OpenMPDialect, mlir::acc::OpenACCDialect>();
+ mlir::LLVM::LLVMDialect, mlir::omp::OpenMPDialect,
+ mlir::acc::OpenACCDialect>();
// Register extensions to integrate CIR types with OpenACC and OpenMP.
cir::omp::registerOpenMPExtensions(registry);
cir::acc::registerOpenACCExtensions(registry);
diff --git a/clang/test/CIR/Input/cir-input-emit-cir.c b/clang/test/CIR/Input/cir-input-emit-cir.c
new file mode 100644
index 0000000000000..29a78b553ebe8
--- /dev/null
+++ b/clang/test/CIR/Input/cir-input-emit-cir.c
@@ -0,0 +1,29 @@
+// Printing parsed ClangIR input with -emit-cir reproduces the CIR it was
+// emitted as.
+
+// 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 -emit-cir %t.cir -o %t2.cir
+// RUN: diff %t.cir %t2.cir
+
+// Without -o, the output is named after the input with a .cir extension.
+// RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir
+// RUN: cp %t.cir input.txt
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir -x cir input.txt
+// RUN: diff %t.cir input.cir
+
+struct Point {
+ int x;
+ int y;
+};
+
+struct Point origin = {1, 2};
+int counter;
+
+static int square(int v) { return v * v; }
+
+int add(int a, int b) { return a + b; }
+
+int sumSquares(struct Point *p) {
+ counter++;
+ return add(square(p->x), square(p->y));
+}
diff --git a/clang/test/CIR/Input/cir-input-emit-cir.cpp b/clang/test/CIR/Input/cir-input-emit-cir.cpp
new file mode 100644
index 0000000000000..b13c46f879a18
--- /dev/null
+++ b/clang/test/CIR/Input/cir-input-emit-cir.cpp
@@ -0,0 +1,30 @@
+// Printing parsed ClangIR input with -emit-cir reproduces the CIR it was
+// emitted as, including C++ constructs such as vtables, dynamic and
+// thread-local initialization, and guarded static locals.
+
+// 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 -emit-cir %t.cir -o %t2.cir
+// RUN: diff %t.cir %t2.cir
+
+struct Base {
+ virtual ~Base();
+ virtual int get() const { return 1; }
+};
+
+struct Derived : Base {
+ int v;
+ Derived(int v) : v(v) {}
+ ~Derived() override {}
+ int get() const override { return v; }
+};
+
+int compute();
+int dynInit = compute();
+thread_local int tls = 7;
+
+int useStatic() {
+ static Derived d(compute());
+ return d.get() + tls;
+}
+
+Base *make() { return new Derived(3); }
diff --git a/clang/test/CIR/Input/cir-input-invalid.cir b/clang/test/CIR/Input/cir-input-invalid.cir
new file mode 100644
index 0000000000000..bfd8fd14c7d4f
--- /dev/null
+++ b/clang/test/CIR/Input/cir-input-invalid.cir
@@ -0,0 +1,53 @@
+// 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.
+
+// RUN: rm -rf %t && split-file %s %t
+
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/syntax.cir -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=SYNTAX --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %t/syntax.cir -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=SYNTAX --implicit-check-not=error:
+// SYNTAX: syntax.cir:2:15: error: expected non-function type
+
+// 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:
+// VERIFIER: verifier.cir:5:5: error: 'cir.return' op returns '!cir.int<s, 64>' but enclosing function returns '!cir.int<s, 32>'
+
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/no-triple.cir -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=NO-TRIPLE --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/int-triple.cir -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=NO-TRIPLE --implicit-check-not=error:
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-obj %t/no-triple.cir -o /dev/null 2>&1 \
+// RUN: | FileCheck %s --check-prefix=NO-TRIPLE --implicit-check-not=error:
+// NO-TRIPLE: error: missing target triple in ClangIR input
+
+// 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:
+// MISMATCH: error: target triple 'aarch64-unknown-linux-gnu' of ClangIR input does not match the target 'x86_64-unknown-linux-gnu'
+
+//--- syntax.cir
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @f( {
+}
+
+//--- verifier.cir
+!s32i = !cir.int<s, 32>
+!s64i = !cir.int<s, 64>
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @f(%arg0: !s64i) -> !s32i {
+ cir.return %arg0 : !s64i
+ }
+}
+
+//--- no-triple.cir
+module {
+}
+
+//--- int-triple.cir
+module attributes {cir.triple = 42} {
+}
+
+//--- mismatch.cir
+module attributes {cir.triple = "aarch64-unknown-linux-gnu"} {
+}
diff --git a/clang/test/CIR/Input/cir-input-unsupported.cir b/clang/test/CIR/Input/cir-input-unsupported.cir
index 2d632d893d564..b087871af1d04 100644
--- a/clang/test/CIR/Input/cir-input-unsupported.cir
+++ b/clang/test/CIR/Input/cir-input-unsupported.cir
@@ -1,6 +1,6 @@
// A .cir input is routed to the ClangIR frontend action instead of being
-// parsed as C. Consuming ClangIR input is not implemented yet, so every CIR
-// action reports a single diagnostic.
+// 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:
@@ -10,8 +10,6 @@
// 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:
-// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -x cir %s -o - 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 \
@@ -24,7 +22,7 @@
// CHECK: error: support for ClangIR input is not yet implemented
!s32i = !cir.int<s, 32>
-module {
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
cir.func @f(%arg0: !s32i) -> !s32i {
cir.return %arg0 : !s32i
}
>From 74d163aad63dd10fc72e018c523d2e3ce041d806 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Fri, 2 Oct 2026 15:36:35 -0700
Subject: [PATCH 2/3] [CIR] Keep unattachable MLIR diagnostic locations as text
---
.../FrontendAction/CIRDiagnosticHandler.cpp | 39 ++++++++++++++++++-
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 2 +-
clang/test/CIR/Input/cir-input-invalid.cir | 14 +++++++
3 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/FrontendAction/CIRDiagnosticHandler.cpp b/clang/lib/CIR/FrontendAction/CIRDiagnosticHandler.cpp
index 95f9e9a058e1f..952b940cb63d1 100644
--- a/clang/lib/CIR/FrontendAction/CIRDiagnosticHandler.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRDiagnosticHandler.cpp
@@ -19,9 +19,12 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
+#include <optional>
+#include <string>
namespace cir {
@@ -65,6 +68,28 @@ clang::SourceLocation CIRDiagnosticHandler::translateLoc(mlir::Location loc) {
return clang::SourceLocation();
}
+// Returns the first file/line/column location with a known line nested in
+// \p loc, walking the same wrappers as translateLoc.
+static std::optional<mlir::FileLineColLoc>
+findFileLineColLoc(mlir::Location loc) {
+ if (auto file = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) {
+ if (file.getLine() == 0 || file.getColumn() == 0)
+ return std::nullopt;
+ return file;
+ }
+ if (auto fused = mlir::dyn_cast<mlir::FusedLoc>(loc)) {
+ for (mlir::Location child : fused.getLocations())
+ if (std::optional<mlir::FileLineColLoc> file = findFileLineColLoc(child))
+ return file;
+ return std::nullopt;
+ }
+ if (auto callsite = mlir::dyn_cast<mlir::CallSiteLoc>(loc))
+ return findFileLineColLoc(callsite.getCallee());
+ if (auto named = mlir::dyn_cast<mlir::NameLoc>(loc))
+ return findFileLineColLoc(named.getChildLoc());
+ return std::nullopt;
+}
+
void CIRDiagnosticHandler::emit(mlir::Diagnostic &diag, bool isNote) {
unsigned diagID;
if (isNote) {
@@ -87,7 +112,19 @@ void CIRDiagnosticHandler::emit(mlir::Diagnostic &diag, bool isNote) {
"severity; notes arrive via getNotes()");
}
}
- Diags.Report(translateLoc(diag.getLocation()), diagID) << diag.str();
+ clang::SourceLocation loc = translateLoc(diag.getLocation());
+ std::string message = diag.str();
+ // A location in a file that is not loaded in the SourceManager, such as the
+ // original source of parsed ClangIR input, cannot be attached to the
+ // diagnostic. Keep it as text so the user can still find the code.
+ if (loc.isInvalid())
+ if (std::optional<mlir::FileLineColLoc> file =
+ findFileLineColLoc(diag.getLocation()))
+ message = (llvm::Twine(file->getFilename().getValue()) + ":" +
+ llvm::Twine(file->getLine()) + ":" +
+ llvm::Twine(file->getColumn()) + ": " + message)
+ .str();
+ Diags.Report(loc, diagID) << message;
}
mlir::LogicalResult CIRDiagnosticHandler::handle(mlir::Diagnostic &diag) {
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index 5c8b8e0be6a75..627e659f62ba8 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -396,7 +396,7 @@ void CIRGenAction::ExecuteAction() {
// errors point into the .cir input. Verifier errors use the location of
// the failing op, which for CIR emitted by CIRGen is a location in the
// original source file; that file is not loaded in the SourceManager, so
- // such errors are currently reported without a location.
+ // the handler reports such errors with the original location as text.
// TODO: Decide where errors in CIRGen-produced input should point: at the
// .cir text, or at the original source.
CIRDiagnosticHandler DiagHandler(&MLIRContext, Diags, SM,
diff --git a/clang/test/CIR/Input/cir-input-invalid.cir b/clang/test/CIR/Input/cir-input-invalid.cir
index bfd8fd14c7d4f..1a791d843bb0a 100644
--- a/clang/test/CIR/Input/cir-input-invalid.cir
+++ b/clang/test/CIR/Input/cir-input-invalid.cir
@@ -14,6 +14,15 @@
// 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
+// loaded for ClangIR input; the location is kept as text in the message.
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %t/src.c -o - \
+// RUN: | sed -E 's/cir\.return %[0-9]+ : !s32i/cir.return/' > %t/src-verifier.cir
+// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/src-verifier.cir -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=SRC-VERIFIER --implicit-check-not=error:
+// SRC-VERIFIER: error: {{.*}}src.c:2:3: 'cir.return' op returns '!cir.void' but enclosing function returns '!cir.int<s, 32>'
+// SRC-VERIFIER: note: {{.*}}src.c:2:3: see current operation
+
// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/no-triple.cir -o - 2>&1 \
// RUN: | FileCheck %s --check-prefix=NO-TRIPLE --implicit-check-not=error:
// RUN: not %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %t/int-triple.cir -o - 2>&1 \
@@ -40,6 +49,11 @@ module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
}
}
+//--- src.c
+int f(int a) {
+ return a;
+}
+
//--- no-triple.cir
module {
}
>From f3458ec897ed383d82d13c7e0651800f76032db4 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <konstantinos.parasyris at intel.com>
Date: Sun, 4 Oct 2026 10:53:00 -0700
Subject: [PATCH 3/3] Fix tests
---
clang/unittests/CIR/CIRDiagnosticHandlerTest.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang/unittests/CIR/CIRDiagnosticHandlerTest.cpp b/clang/unittests/CIR/CIRDiagnosticHandlerTest.cpp
index bf3cdc36dc0a7..98119c090d866 100644
--- a/clang/unittests/CIR/CIRDiagnosticHandlerTest.cpp
+++ b/clang/unittests/CIR/CIRDiagnosticHandlerTest.cpp
@@ -177,13 +177,15 @@ TEST_F(CIRDiagnosticHandlerTest, ZeroLineColumnFallsBackToInvalidLoc) {
EXPECT_EQ(Consumer->Diags[0].Message, "module-level");
}
-TEST_F(CIRDiagnosticHandlerTest, FileNotInManagerFallsBack) {
+TEST_F(CIRDiagnosticHandlerTest, FileNotInManagerKeepsLocationAsText) {
+ // A file the SourceManager does not know cannot be attached to the
+ // diagnostic, so the handler prefixes the message with file:line:col.
cir::CIRDiagnosticHandler Handler(&MLIRCtx, Diags, SrcMgr, FileMgr);
mlir::emitError(fileLoc(1, 1, "/not/registered.c")) << "stray";
ASSERT_EQ(Consumer->Diags.size(), 1u);
EXPECT_FALSE(Consumer->Diags[0].HasLoc);
- EXPECT_EQ(Consumer->Diags[0].Message, "stray");
+ EXPECT_EQ(Consumer->Diags[0].Message, "/not/registered.c:1:1: stray");
}
TEST_F(CIRDiagnosticHandlerTest, FusedLocResolvesToFirstTranslatableChild) {
More information about the cfe-commits
mailing list