[clang] [CIR] Initial upstreaming of idiom recognizer pass (PR #172486)
Hendrik Hübner via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 15 04:35:16 PST 2026
https://github.com/HendrikHuebner updated https://github.com/llvm/llvm-project/pull/172486
>From b3dc20805532db4e2f8ddeb6c959cecbf0de912b Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Tue, 16 Dec 2025 15:02:32 +0100
Subject: [PATCH 1/3] Add idiom recognizer pass
---
clang/include/clang/CIR/Dialect/Passes.h | 2 +
clang/include/clang/CIR/Dialect/Passes.td | 11 +++
.../lib/CIR/Dialect/Transforms/CMakeLists.txt | 1 +
.../Dialect/Transforms/IdiomRecognizer.cpp | 69 +++++++++++++++++++
clang/lib/CIR/Lowering/CIRPasses.cpp | 1 +
.../test/CIR/Transforms/idiom-recognizer.cpp | 2 +
6 files changed, 86 insertions(+)
create mode 100644 clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
create mode 100644 clang/test/CIR/Transforms/idiom-recognizer.cpp
diff --git a/clang/include/clang/CIR/Dialect/Passes.h b/clang/include/clang/CIR/Dialect/Passes.h
index 161f365bb1402..7b57b00f7da8c 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -30,6 +30,8 @@ std::unique_ptr<Pass> createHoistAllocasPass();
std::unique_ptr<Pass> createLoweringPreparePass();
std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
std::unique_ptr<Pass> createGotoSolverPass();
+std::unique_ptr<Pass> createIdiomRecognizerPass();
+std::unique_ptr<Pass> createIdiomRecognizerPass(clang::ASTContext *astCtx);
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index ef5308ffffd19..0d1e15903b509 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -161,4 +161,15 @@ def LoweringPrepare : Pass<"cir-lowering-prepare"> {
let dependentDialects = ["cir::CIRDialect"];
}
+def IdiomRecognizer : Pass<"cir-idiom-recognizer"> {
+ let summary = "Raise calls to C/C++ libraries to CIR operations";
+ let description = [{
+ This pass recognize idiomatic C++ usage and incorporate C++ standard
+ containers, library functions calls, and types into CIR operation,
+ attributes and types.
+ }];
+ let constructor = "mlir::createIdiomRecognizerPass()";
+ let dependentDialects = ["cir::CIRDialect"];
+}
+
#endif // CLANG_CIR_DIALECT_PASSES_TD
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 1ace2d8634b40..3fe6c5f9ecb55 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -9,6 +9,7 @@ add_clang_library(MLIRCIRTransforms
HoistAllocas.cpp
LoweringPrepare.cpp
GotoSolver.cpp
+ IdiomRecognizer.cpp
DEPENDS
MLIRCIRPassIncGen
diff --git a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
new file mode 100644
index 0000000000000..ff1110f86ce8f
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
@@ -0,0 +1,69 @@
+//===- IdiomRecognizer.cpp - pareparation work for LLVM lowering ----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass is responsible for recognizing idioms (such as uses of functions
+// and types to the C/C++ standard library) and replacing them with Clang IR
+// operators for later optimization.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassDetail.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/Region.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Basic/Module.h"
+#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/Passes.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/Path.h"
+
+using namespace mlir;
+using namespace cir;
+
+namespace mlir {
+#define GEN_PASS_DEF_IDIOMRECOGNIZER
+#include "clang/CIR/Dialect/Passes.h.inc"
+} // namespace mlir
+
+namespace {
+
+struct IdiomRecognizerPass : public impl::IdiomRecognizerBase<IdiomRecognizerPass> {
+ IdiomRecognizerPass() = default;
+ void runOnOperation() override;
+
+ clang::ASTContext *astCtx;
+ void setASTContext(clang::ASTContext *c) { astCtx = c; }
+
+ /// Tracks current module.
+ ModuleOp theModule;
+};
+} // namespace
+
+void IdiomRecognizerPass::runOnOperation() {
+ auto *op = getOperation();
+ if (isa<::mlir::ModuleOp>(op))
+ theModule = cast<::mlir::ModuleOp>(op);
+}
+
+std::unique_ptr<Pass> mlir::createIdiomRecognizerPass() {
+ return std::make_unique<IdiomRecognizerPass>();
+}
+
+std::unique_ptr<Pass>
+mlir::createIdiomRecognizerPass(clang::ASTContext *astCtx) {
+ auto pass = std::make_unique<IdiomRecognizerPass>();
+ pass->setASTContext(astCtx);
+ return std::move(pass);
+}
\ No newline at end of file
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 589c2c524c305..9c91ac51bebd7 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -31,6 +31,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
if (enableCIRSimplify)
pm.addPass(mlir::createCIRSimplifyPass());
+ pm.addPass(mlir::createIdiomRecognizerPass());
pm.addPass(mlir::createTargetLoweringPass());
pm.addPass(mlir::createCXXABILoweringPass());
pm.addPass(mlir::createLoweringPreparePass(&astContext));
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cpp b/clang/test/CIR/Transforms/idiom-recognizer.cpp
new file mode 100644
index 0000000000000..b309d813ace40
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cpp
@@ -0,0 +1,2 @@
+// RUN: %clang_cc1 -fclangir -emit-cir -mmlir --mlir-print-ir-after-all %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=CIR
+// CIR: IR Dump After IdiomRecognizer (cir-idiom-recognizer)
>From bf20acfccaa63cc64ce076900a94741e880917d5 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Fri, 19 Dec 2025 22:05:40 +0100
Subject: [PATCH 2/3] fmt
---
clang/include/clang/CIR/Dialect/Passes.td | 8 +++---
.../Dialect/Transforms/IdiomRecognizer.cpp | 28 +++++++++++++++----
2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Passes.td b/clang/include/clang/CIR/Dialect/Passes.td
index 0d1e15903b509..0d60d5e33dd4b 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -161,12 +161,12 @@ def LoweringPrepare : Pass<"cir-lowering-prepare"> {
let dependentDialects = ["cir::CIRDialect"];
}
-def IdiomRecognizer : Pass<"cir-idiom-recognizer"> {
+def IdiomRecognizer : Pass<"cir-idiom-recognizer", "mlir::ModuleOp"> {
let summary = "Raise calls to C/C++ libraries to CIR operations";
let description = [{
- This pass recognize idiomatic C++ usage and incorporate C++ standard
- containers, library functions calls, and types into CIR operation,
- attributes and types.
+ This pass recognizes idiomatic C++ usage and captures information about C++
+ standard library containers, library functions calls, and types into CIR
+ operations, attributes and types.
}];
let constructor = "mlir::createIdiomRecognizerPass()";
let dependentDialects = ["cir::CIRDialect"];
diff --git a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
index ff1110f86ce8f..92c031a5a51e1 100644
--- a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
@@ -1,4 +1,4 @@
-//===- IdiomRecognizer.cpp - pareparation work for LLVM lowering ----------===//
+//===- IdiomRecognizer.cpp - recognizing and raising idioms to CIR --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -39,10 +39,14 @@ namespace mlir {
namespace {
-struct IdiomRecognizerPass : public impl::IdiomRecognizerBase<IdiomRecognizerPass> {
+struct IdiomRecognizerPass
+ : public impl::IdiomRecognizerBase<IdiomRecognizerPass> {
IdiomRecognizerPass() = default;
+
void runOnOperation() override;
+ void recognizeStandardLibraryCall(CallOp call);
+
clang::ASTContext *astCtx;
void setASTContext(clang::ASTContext *c) { astCtx = c; }
@@ -51,10 +55,22 @@ struct IdiomRecognizerPass : public impl::IdiomRecognizerBase<IdiomRecognizerPas
};
} // namespace
+void IdiomRecognizerPass::recognizeStandardLibraryCall(CallOp call) {
+ // To be implemented
+}
+
void IdiomRecognizerPass::runOnOperation() {
- auto *op = getOperation();
- if (isa<::mlir::ModuleOp>(op))
- theModule = cast<::mlir::ModuleOp>(op);
+ theModule = getOperation();
+
+ // Process call operations
+ theModule->walk([&](CallOp callOp) {
+ // Skip indirect calls.
+ std::optional<llvm::StringRef> callee = callOp.getCallee();
+ if (!callee)
+ return;
+
+ recognizeStandardLibraryCall(callOp);
+ });
}
std::unique_ptr<Pass> mlir::createIdiomRecognizerPass() {
@@ -66,4 +82,4 @@ mlir::createIdiomRecognizerPass(clang::ASTContext *astCtx) {
auto pass = std::make_unique<IdiomRecognizerPass>();
pass->setASTContext(astCtx);
return std::move(pass);
-}
\ No newline at end of file
+}
>From b1a2ac27516943dcfd705c3de6cbc7bf86a11ea4 Mon Sep 17 00:00:00 2001
From: hhuebner <hendrik.huebner18 at gmail.com>
Date: Sun, 15 Feb 2026 13:34:48 +0100
Subject: [PATCH 3/3] Add CLI flag
---
clang/include/clang/CIR/CIRToCIRPasses.h | 1 +
clang/include/clang/Frontend/FrontendOptions.h | 8 ++++++--
clang/include/clang/Options/Options.td | 5 +++++
clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp | 11 +++++++++++
clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 1 +
clang/lib/CIR/Lowering/CIRPasses.cpp | 5 ++++-
6 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/CIR/CIRToCIRPasses.h b/clang/include/clang/CIR/CIRToCIRPasses.h
index 4a23790ee8b76..c211df44c641b 100644
--- a/clang/include/clang/CIR/CIRToCIRPasses.h
+++ b/clang/include/clang/CIR/CIRToCIRPasses.h
@@ -33,6 +33,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
mlir::MLIRContext &mlirCtx,
clang::ASTContext &astCtx,
bool enableVerifier,
+ bool enableIdiomRecognizer,
bool enableCIRSimplify);
} // namespace cir
diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h
index ba7da56cb9fce..9a7fdf5cbcf06 100644
--- a/clang/include/clang/Frontend/FrontendOptions.h
+++ b/clang/include/clang/Frontend/FrontendOptions.h
@@ -422,6 +422,10 @@ class FrontendOptions {
LLVM_PREFERRED_TYPE(bool)
unsigned ClangIRDisableCIRVerifier : 1;
+ /// Enable Clang IR (CIR) idiom recognizer
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned ClangIREnableIdiomRecognizer : 1;
+
CodeCompleteOptions CodeCompleteOpts;
/// Specifies the output format of the AST.
@@ -552,8 +556,8 @@ class FrontendOptions {
EmitSymbolGraphSymbolLabelsForTesting(false),
EmitPrettySymbolGraphs(false), GenReducedBMI(false),
UseClangIRPipeline(false), ClangIRDisablePasses(false),
- ClangIRDisableCIRVerifier(false), TimeTraceGranularity(500),
- TimeTraceVerbose(false) {}
+ ClangIRDisableCIRVerifier(false), ClangIREnableIdiomRecognizer(false),
+ TimeTraceGranularity(500), TimeTraceVerbose(false) {}
/// getInputKindForExtension - Return the appropriate input kind for a file
/// extension. For example, "c" would return Language::C.
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index a274017953b1d..aa878addd9b9b 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3253,6 +3253,11 @@ def clangir_disable_verifier : Flag<["-"], "clangir-disable-verifier">,
HelpText<"ClangIR: Disable MLIR module verifier">,
MarshallingInfoFlag<FrontendOpts<"ClangIRDisableCIRVerifier">>;
+def clangir_enable_idiom_recognizer : Flag<["-"], "clangir-enable-idiom-recognizer">,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"ClangIR: Enable Idiom Recognizer pass">,
+ MarshallingInfoFlag<FrontendOpts<"ClangIREnableIdiomRecognizer">>;
+
defm clangir : BoolFOption<"clangir",
FrontendOpts<"UseClangIRPipeline">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Use the ClangIR pipeline to compile">,
diff --git a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
index 92c031a5a51e1..4a8f931dd2d4d 100644
--- a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
@@ -60,6 +60,17 @@ void IdiomRecognizerPass::recognizeStandardLibraryCall(CallOp call) {
}
void IdiomRecognizerPass::runOnOperation() {
+ // The AST context will be used to provide additional information such as
+ // namespaces and template parameter lists that are lost after lowering to
+ // CIR. This information is necessary to recognize many idioms, such as calls
+ // to standard library functions.
+
+ // For now, the AST will be required to allow for faster prototyping and
+ // exploring of new optimizations. In the future, it may be preferable to
+ // make it optional to reduce memory pressure and allow this pass to run
+ // on standalone CIR assembly (Possibly generated from non-Clang front ends).
+
+ assert(astCtx && "Missing ASTContext, please construct with the right ctor");
theModule = getOperation();
// Process call operations
diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
index daec8ae409e0f..e2d883a6b3a55 100644
--- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
+++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp
@@ -117,6 +117,7 @@ class CIRGenConsumer : public clang::ASTConsumer {
// Setup and run CIR pipeline.
if (runCIRToCIRPasses(MlirModule, MlirCtx, C,
!FEOptions.ClangIRDisableCIRVerifier,
+ FEOptions.ClangIREnableIdiomRecognizer,
CGO.OptimizationLevel > 0)
.failed()) {
CI.getDiagnostics().Report(diag::err_cir_to_cir_transform_failed);
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 9c91ac51bebd7..8ca0263f67ad1 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -21,6 +21,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
mlir::MLIRContext &mlirContext,
clang::ASTContext &astContext,
bool enableVerifier,
+ bool enableIdiomRecognizer,
bool enableCIRSimplify) {
llvm::TimeTraceScope scope("CIR To CIR Passes");
@@ -31,7 +32,9 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
if (enableCIRSimplify)
pm.addPass(mlir::createCIRSimplifyPass());
- pm.addPass(mlir::createIdiomRecognizerPass());
+ if (enableIdiomRecognizer)
+ pm.addPass(mlir::createIdiomRecognizerPass());
+
pm.addPass(mlir::createTargetLoweringPass());
pm.addPass(mlir::createCXXABILoweringPass());
pm.addPass(mlir::createLoweringPreparePass(&astContext));
More information about the cfe-commits
mailing list