https://github.com/SharmaRithik updated 
https://github.com/llvm/llvm-project/pull/208854

>From 81cb03bee21e28b5094dccbac9c918745c975780 Mon Sep 17 00:00:00 2001
From: SharmaRithik <[email protected]>
Date: Thu, 2 Jul 2026 00:00:26 +0000
Subject: [PATCH 1/3] [CIR] Add support for the IdiomRecognizer pass

This patch adds the IdiomRecognizer pass, which raises calls to known
standard library functions into dedicated operations that later passes
can optimize. The implementation follows the ClangIR incubator, and
upstream had only the pass skeleton and the frontend wiring.

The pass recognizes the standard find algorithm and raises it to the
new operation cir.std.find. A call is raised when its callee carries
the matching identity tag, the argument count and the types line up,
the callee is not variadic, and the call is not musttail.
LoweringPrepare lowers the operation back to the original call with
its attributes, so behavior never changes when no transform fires.

The tag is the new func_identity form under the func_info union on
cir.func. It holds one entry from an enum naming the entities the
recognizer knows, so the tag itself carries no names. CIRGen attaches
it by the plain name, the std membership with inline namespaces looked
through, and whether the function is free, so members, static members,
operators, and functions outside std never match, while the recognizer
checks the shape of each call.

The pass reads facts from the operations alone, so the AST requirement
and its wiring are gone, the pass is registered with cir-opt, and a
test drives it over parsed CIR assembly.
---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  |  43 ++++++-
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |   2 +
 .../include/clang/CIR/Dialect/IR/CIRStdOps.td |  53 +++++++++
 clang/include/clang/CIR/Dialect/Passes.h      |   1 -
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  20 ++++
 clang/lib/CIR/CodeGen/CIRGenModule.h          |   5 +
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |   4 +-
 .../Dialect/Transforms/IdiomRecognizer.cpp    | 111 +++++++++++-------
 .../Dialect/Transforms/LoweringPrepare.cpp    |  25 +++-
 clang/lib/CIR/Lowering/CIRPasses.cpp          |   2 +-
 clang/test/CIR/CodeGen/func-identity-attr.c   |  10 ++
 clang/test/CIR/CodeGen/func-identity-attr.cpp |  39 ++++++
 clang/test/CIR/IR/func-identity-attr.cir      |   8 ++
 .../Transforms/idiom-recognizer-guards.cpp    |  57 +++++++++
 .../test/CIR/Transforms/idiom-recognizer.cir  |  34 ++++++
 .../test/CIR/Transforms/idiom-recognizer.cpp  |  45 +++++++
 clang/tools/cir-opt/cir-opt.cpp               |   4 +
 17 files changed, 415 insertions(+), 48 deletions(-)
 create mode 100644 clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
 create mode 100644 clang/test/CIR/CodeGen/func-identity-attr.c
 create mode 100644 clang/test/CIR/CodeGen/func-identity-attr.cpp
 create mode 100644 clang/test/CIR/IR/func-identity-attr.cir
 create mode 100644 clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
 create mode 100644 clang/test/CIR/Transforms/idiom-recognizer.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 88e8f91f7e75c..21ba6c8c15623 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1296,11 +1296,52 @@ def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", 
"cxx_assign"> {
 // FuncInfoAttr
 
//===----------------------------------------------------------------------===//
 
+// The standard library entities the identity tag can name. Each entry
+// pairs with one raised operation in CIRStdOps.td.
+def CIR_KnownFuncKind : CIR_I32EnumAttr<"KnownFuncKind",
+    "known standard library entity", [
+  I32EnumAttrCase<"StdFind", 1, "std_find">,
+]> {
+  let genSpecializedAttr = 0;
+}
+
+def CIR_FuncIdentityAttr : CIR_Attr<"FuncIdentity", "func_identity"> {
+  let summary = "Identifies a function as a known standard library entity";
+  let description = [{
+    Names the standard library entity a function represents, so that
+    transformations can recognize calls to well known library functions
+    without decoding mangled symbol names.
+
+    The tag names the whole entity. For `std_find` that is the free
+    function named `find` in the `std` namespace, so a member function, a
+    static member, or an operator can never carry the tag. Inline
+    namespaces, such as the versioning namespace of libc++, count as part
+    of `std`. The tag never encodes signatures, and a function that
+    matches no known entity carries no attribute.
+
+    Example:
+    ```
+    #cir.func_identity<std_find>
+    ```
+  }];
+
+  let parameters = (ins
+    EnumParameter<CIR_KnownFuncKind>:$kind
+  );
+
+  let assemblyFormat = [{
+    `<` $kind `>`
+  }];
+
+  let canHaveIllegalCXXABIType = 0;
+}
+
 // The attributes accepted in the optional func_info slot on cir.func.
 def CIR_FuncInfoAttr : AnyAttrOf<[
   CIR_CXXCtorAttr,
   CIR_CXXDtorAttr,
-  CIR_CXXAssignAttr
+  CIR_CXXAssignAttr,
+  CIR_FuncIdentityAttr
 ], "function information attribute">;
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 762ef56248e4c..0a60514ee21fc 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -8658,4 +8658,6 @@ def CIR_LaunderOp : CIR_Op<"launder", 
[SameOperandsAndResultType]> {
   let llvmOp = "LaunderInvariantGroupOp";
 }
 
+include "clang/CIR/Dialect/IR/CIRStdOps.td"
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td 
b/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
new file mode 100644
index 0000000000000..5b724ff6320a5
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
@@ -0,0 +1,53 @@
+//===-- CIRStdOps.td - CIR standard library ops ------------*- tablegen 
-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Defines ops representing standard library calls, raised from plain calls
+/// by the cir-idiom-recognizer pass and lowered back by LoweringPrepare.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_CIR_DIALECT_IR_CIRSTDOPS_TD
+#define CLANG_CIR_DIALECT_IR_CIRSTDOPS_TD
+
+// knownKind names the CIR_KnownFuncKind entry whose tagged calls raise
+// to this operation.
+class CIR_StdOp<string functionName, dag args, dag res,
+                list<Trait> traits = [], string knownKind = "">
+    : CIR_Op<"std." # functionName, traits> {
+  let arguments = !con(args, (ins FlatSymbolRefAttr:$original_fn));
+  let results = res;
+  let hasLLVMLowering = false;
+
+  let extraClassDeclaration = [{
+    static constexpr unsigned getNumArgs() {
+      return }] # !size(args) # [{;
+    }
+    static llvm::StringRef getFunctionName() {
+      return "}] # functionName # [{";
+    }
+  }] # !if(!empty(knownKind), "", [{
+    static cir::KnownFuncKind getFuncKind() {
+      return cir::KnownFuncKind::}] # knownKind # [{;
+    }
+  }]);
+}
+
+def CIR_StdFindOp : CIR_StdOp<"find",
+  (ins CIR_AnyType:$first, CIR_AnyType:$last, CIR_AnyType:$pattern),
+  (outs CIR_AnyType:$result),
+  [AllTypesMatch<["first", "last", "result"]>], "StdFind"> {
+  let summary = "std::find()";
+  let assemblyFormat = [{
+    `(` $first `:` qualified(type($first)) `,`
+        $last `:` qualified(type($last)) `,`
+        $pattern `:` qualified(type($pattern)) `,`
+        $original_fn `)` `->` qualified(type($result)) attr-dict
+  }];
+}
+
+#endif // CLANG_CIR_DIALECT_IR_CIRSTDOPS_TD
diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 651a1319cfe5d..7d317879ad739 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -33,7 +33,6 @@ 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);
 std::unique_ptr<Pass> createLibOptPass();
 std::unique_ptr<Pass> createLibOptPass(clang::ASTContext *astCtx);
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 66a9dc9ea605c..de5a9a6a78a7b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3461,6 +3461,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, 
StringRef name,
     // Mark C++ special member functions (Constructor, Destructor etc.)
     setCXXSpecialMemberAttr(func, funcDecl);
 
+    // Tag functions that match a known standard library entity.
+    setFuncIdentityAttr(func, funcDecl);
+
     if (!cgf)
       theModule.push_back(func);
 
@@ -3537,6 +3540,23 @@ void CIRGenModule::setCXXSpecialMemberAttr(
   }
 }
 
+void CIRGenModule::setFuncIdentityAttr(cir::FuncOp funcOp,
+                                       const clang::FunctionDecl *funcDecl) {
+  // Only a free std function with a plain identifier can match a known
+  // entity, so members, static members, and operators never take a tag.
+  // Inline namespaces, like the versioning namespace of libc++, count as
+  // part of std.
+  if (!funcDecl || !funcDecl->getIdentifier() || isa<CXXMethodDecl>(funcDecl) 
||
+      !funcDecl->isInStdNamespace())
+    return;
+
+  // The names and the tags come from CIRStdOps.td, and the recognizer
+  // checks the shape of each call.
+  if (funcDecl->getName() == cir::StdFindOp::getFunctionName())
+    funcOp.setFuncInfoAttr(cir::FuncIdentityAttr::get(
+        &getMLIRContext(), cir::StdFindOp::getFuncKind()));
+}
+
 static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
                                        cir::FuncOp funcOp, StringRef name) {
   // In Windows Itanium environments, try to mark runtime functions
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 144f8c7b9f3e7..4a4e42bb16195 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -802,6 +802,11 @@ class CIRGenModule : public CIRGenTypeCache {
   void setCXXSpecialMemberAttr(cir::FuncOp funcOp,
                                const clang::FunctionDecl *funcDecl);
 
+  /// Tag functions that match a known standard library entity, so passes
+  /// can recognize calls to them without the AST.
+  void setFuncIdentityAttr(cir::FuncOp funcOp,
+                           const clang::FunctionDecl *funcDecl);
+
   cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
                                     mlir::NamedAttrList extraAttrs = {},
                                     bool isLocal = false,
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp 
b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index c519229a02f26..60ad12b92cdb9 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2550,8 +2550,8 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, 
OperationState &state) {
     mlir::Attribute attr;
     if (parser.parseAttribute(attr).failed())
       return failure();
-    if (!mlir::isa<cir::CXXCtorAttr, cir::CXXDtorAttr, cir::CXXAssignAttr>(
-            attr))
+    if (!mlir::isa<cir::CXXCtorAttr, cir::CXXDtorAttr, cir::CXXAssignAttr,
+                   cir::FuncIdentityAttr>(attr))
       return parser.emitError(attrLoc,
                               "expected a function info attribute, got ")
              << attr;
diff --git a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp 
b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
index 4a8f931dd2d4d..ce234363ac6b1 100644
--- a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
@@ -13,21 +13,12 @@
 
//===----------------------------------------------------------------------===//
 
 #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"
+
+#include <utility>
 
 using namespace mlir;
 using namespace cir;
@@ -39,58 +30,94 @@ namespace mlir {
 
 namespace {
 
+// The raised operation requires matching operand types. The searched value
+// arrives by reference and must share the iterator type.
+template <typename TargetOp> bool operandTypesMatch(CallOp call);
+
+template <> bool operandTypesMatch<StdFindOp>(CallOp call) {
+  mlir::Type iterTy = call.getOperand(0).getType();
+  return iterTy == call.getOperand(1).getType() &&
+         iterTy == call.getOperand(2).getType() &&
+         iterTy == call->getResult(0).getType();
+}
+
+// Raises a direct cir.call to `TargetOp` when the callee carries the
+// matching identity tag.
+template <typename TargetOp> class StdRecognizer {
+  template <size_t... Indices>
+  static TargetOp buildCall(cir::CIRBaseBuilderTy &builder, CallOp call,
+                            std::index_sequence<Indices...>) {
+    return TargetOp::create(builder, call.getLoc(),
+                            call->getResult(0).getType(),
+                            call.getOperand(Indices)..., call.getCalleeAttr());
+  }
+
+public:
+  static bool raise(CallOp call, mlir::MLIRContext &context,
+                    mlir::SymbolTableCollection &symbolTables) {
+    // A musttail call must stay a call, so it is never raised.
+    constexpr unsigned numArgs = TargetOp::getNumArgs();
+    if (!call.getCallee() || call.getNumOperands() != numArgs ||
+        call->getNumResults() != 1 || call.getMusttail() ||
+        !operandTypesMatch<TargetOp>(call))
+      return false;
+
+    // Only a free std function with the right name carries the tag, so
+    // members, static members, and operators never match. The shape of the
+    // call is checked here, so a variadic callee never matches.
+    cir::FuncOp callee = call.resolveCalleeInTable(symbolTables);
+    if (!callee || callee.getFunctionType().isVarArg())
+      return false;
+    auto funcIdentity = mlir::dyn_cast_if_present<cir::FuncIdentityAttr>(
+        callee.getFuncInfoAttr());
+    if (!funcIdentity || funcIdentity.getKind() != TargetOp::getFuncKind())
+      return false;
+
+    cir::CIRBaseBuilderTy builder(context);
+    builder.setInsertionPointAfter(call.getOperation());
+    TargetOp op = buildCall(builder, call, 
std::make_index_sequence<numArgs>());
+    // The raised operation keeps every call attribute except the callee,
+    // which it carries as original_fn, so lowering back loses nothing.
+    for (mlir::NamedAttribute attr : call->getAttrs())
+      if (attr.getName() != "callee")
+        op->setAttr(attr.getName(), attr.getValue());
+    call.replaceAllUsesWith(op);
+    call.erase();
+    return true;
+  }
+};
+
 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; }
-
-  /// Tracks current module.
-  ModuleOp theModule;
+  void recognizeStandardLibraryCall(CallOp call,
+                                    mlir::SymbolTableCollection &symbolTables);
 };
 } // namespace
 
-void IdiomRecognizerPass::recognizeStandardLibraryCall(CallOp call) {
-  // To be implemented
+void IdiomRecognizerPass::recognizeStandardLibraryCall(
+    CallOp call, mlir::SymbolTableCollection &symbolTables) {
+  StdRecognizer<StdFindOp>::raise(call, getContext(), symbolTables);
 }
 
 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).
+  // The facts this pass reads live on the operations, so it needs no AST
+  // and also works on parsed CIR assembly.
+  mlir::SymbolTableCollection symbolTables;
 
-  assert(astCtx && "Missing ASTContext, please construct with the right ctor");
-  theModule = getOperation();
-
-  // Process call operations
-  theModule->walk([&](CallOp callOp) {
+  getOperation()->walk([&](CallOp callOp) {
     // Skip indirect calls.
     std::optional<llvm::StringRef> callee = callOp.getCallee();
     if (!callee)
       return;
 
-    recognizeStandardLibraryCall(callOp);
+    recognizeStandardLibraryCall(callOp, symbolTables);
   });
 }
 
 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);
-}
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index e50830cffa742..973dfe8667a97 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -98,6 +98,7 @@ struct LoweringPreparePass
   void lowerTrivialCopyCall(cir::CallOp op);
   void lowerStoreOfConstAggregate(cir::StoreOp op);
   void lowerLocalInitOp(cir::LocalInitOp op);
+  void lowerStdFindOp(cir::StdFindOp op);
 
   /// Return the FuncOp called by `callOp`.  Uses the cached `symbolTables`
   /// member to avoid the O(M) module-wide scan that the static
@@ -2251,11 +2252,33 @@ void 
LoweringPreparePass::lowerStoreOfConstAggregate(cir::StoreOp op) {
     constOp.erase();
 }
 
+// Raised ops carry the original callee and its call attributes, so lowering
+// them back rebuilds an equivalent plain call.
+static void restoreCallAttrs(cir::CallOp call, mlir::Operation *raised) {
+  for (mlir::NamedAttribute attr : raised->getAttrs())
+    if (attr.getName() != "original_fn")
+      call->setAttr(attr.getName(), attr.getValue());
+}
+
+void LoweringPreparePass::lowerStdFindOp(cir::StdFindOp op) {
+  cir::CIRBaseBuilderTy builder(getContext());
+  builder.setInsertionPointAfter(op.getOperation());
+  cir::CallOp call = builder.createCallOp(
+      op.getLoc(), op.getOriginalFnAttr(), op.getType(),
+      mlir::ValueRange{op.getFirst(), op.getLast(), op.getPattern()});
+  restoreCallAttrs(call, op);
+
+  op.replaceAllUsesWith(call);
+  op.erase();
+}
+
 void LoweringPreparePass::runOnOp(mlir::Operation *op) {
   if (auto arrayCtor = dyn_cast<cir::ArrayCtor>(op)) {
     lowerArrayCtor(arrayCtor);
   } else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
     lowerArrayDtor(arrayDtor);
+  } else if (auto stdFind = mlir::dyn_cast<cir::StdFindOp>(op)) {
+    lowerStdFindOp(stdFind);
   } else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
     lowerCastOp(cast);
   } else if (auto complexConj = mlir::dyn_cast<cir::ComplexConjOp>(op)) {
@@ -2894,7 +2917,7 @@ void LoweringPreparePass::runOnOperation() {
                   cir::ComplexConjOp, cir::ComplexMulOp, cir::ComplexDivOp,
                   cir::DynamicCastOp, cir::FuncOp, cir::CallOp,
                   cir::GetGlobalOp, cir::GlobalOp, cir::StoreOp,
-                  cir::CmpThreeWayOp, cir::LocalInitOp>(op))
+                  cir::CmpThreeWayOp, cir::LocalInitOp, cir::StdFindOp>(op))
       opsToTransform.push_back(op);
   });
 
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp 
b/clang/lib/CIR/Lowering/CIRPasses.cpp
index f476ee04430cd..2866e5f15379c 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -33,7 +33,7 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, mlir::MLIRContext 
&mlirContext,
     pm.addPass(mlir::createCIRSimplifyPass());
 
   if (enableIdiomRecognizer)
-    pm.addPass(mlir::createIdiomRecognizerPass(&astContext));
+    pm.addPass(mlir::createIdiomRecognizerPass());
 
   if (enableLibOpt) {
     auto libOptPass = mlir::createLibOptPass();
diff --git a/clang/test/CIR/CodeGen/func-identity-attr.c 
b/clang/test/CIR/CodeGen/func-identity-attr.c
new file mode 100644
index 0000000000000..56e4493626d4d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/func-identity-attr.c
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
- | FileCheck %s
+
+char *find(char *first, char *last, int value);
+
+char *caller(char *first, char *last) { return find(first, last, 42); }
+
+// A C function named like the std one lives outside std, so it carries no
+// tag.
+// CHECK: cir.func{{.*}} @find
+// CHECK-NOT: func_identity
diff --git a/clang/test/CIR/CodeGen/func-identity-attr.cpp 
b/clang/test/CIR/CodeGen/func-identity-attr.cpp
new file mode 100644
index 0000000000000..75d1308740d1f
--- /dev/null
+++ b/clang/test/CIR/CodeGen/func-identity-attr.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck %s --input-file=%t.cir
+// RUN: FileCheck %s --input-file=%t.cir --check-prefix=TAG
+
+namespace std {
+inline namespace __1 {
+int *find(int *first, int *last, int value);
+}
+struct container {
+  int *find(int value);
+};
+struct traits {
+  static int *find(int *first);
+};
+}
+
+namespace other {
+int *find(int *first, int *last, int value);
+}
+
+int *std_call(int *first, int *last) { return std::find(first, last, 42); }
+// The free std find carries its tag, with inline namespaces looked
+// through.
+// CHECK-DAG: cir.func{{.*}} @_ZNSt3__14find{{.*}} 
func_info<#cir.func_identity<std_find>>
+
+struct S {
+  void operator()();
+};
+
+void other_calls(S &s, std::container &c, int *first, int *last) {
+  c.find(1);
+  std::traits::find(first);
+  other::find(first, last, 42);
+  s();
+}
+// Members, static members, operators, and functions outside std match no
+// entity, so the free std find above stays the only tagged function.
+// TAG-COUNT-1: #cir.func_identity
+// TAG-NOT: #cir.func_identity
diff --git a/clang/test/CIR/IR/func-identity-attr.cir 
b/clang/test/CIR/IR/func-identity-attr.cir
new file mode 100644
index 0000000000000..83924aaf0353f
--- /dev/null
+++ b/clang/test/CIR/IR/func-identity-attr.cir
@@ -0,0 +1,8 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+module {
+
+cir.func private @_ZSt4findv() func_info<#cir.func_identity<std_find>>
+// CHECK: cir.func private @_ZSt4findv() 
func_info<#cir.func_identity<std_find>>
+
+}
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
new file mode 100644
index 0000000000000..28ada39057fde
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--implicit-check-not=cir.std.
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -DVOID_RESULT 
-fclangir -clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--check-prefix=VOID --implicit-check-not=cir.std.
+
+// Each call satisfies every recognizer check except the one guard it pins,
+// and stays the call to the overload its comment names.
+
+#ifdef VOID_RESULT
+
+namespace std {
+// std::find returns the iterator, so a result is required.
+void find(char *first, char *last, const char &value);
+}
+
+void test_void_result(char *first, char *last, const char &value) {
+  std::find(first, last, value);
+}
+// VOID-LABEL: @_Z16test_void_result
+// VOID: cir.call @_ZSt4findPcS_RKc
+
+#else
+
+namespace std {
+// Variadic, only viable for the all-pointer call in test_variadic.
+char *find(char *first, ...);
+// Result type differs from the iterator type.
+int find(char *first, char *last, const char &value);
+// Searched value type differs from the element type.
+char *find(char *first, char *last, const int &value);
+// Wrong arity.
+char *find(char *first, char *last, const char &value, int n);
+}
+
+char *test_variadic(char *first, char *last, char *value) {
+  return std::find(first, last, value);
+}
+// CHECK-LABEL: @_Z13test_variadic
+// CHECK: cir.call @_ZSt4findPcz
+
+int test_result_type(char *first, char *last, const char &value) {
+  return std::find(first, last, value);
+}
+// CHECK-LABEL: @_Z16test_result_type
+// CHECK: cir.call @_ZSt4findPcS_RKc
+
+char *test_pattern_type(char *first, char *last, const int &value) {
+  return std::find(first, last, value);
+}
+// CHECK-LABEL: @_Z17test_pattern_type
+// CHECK: cir.call @_ZSt4findPcS_RKi
+
+char *test_arity(char *first, char *last, const char &value) {
+  return std::find(first, last, value, 1);
+}
+// CHECK-LABEL: @_Z10test_arity
+// CHECK: cir.call @_ZSt4findPcS_RKci
+
+#endif
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cir 
b/clang/test/CIR/Transforms/idiom-recognizer.cir
new file mode 100644
index 0000000000000..ed5a8f2d16a2e
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cir
@@ -0,0 +1,34 @@
+// RUN: cir-opt %s -cir-idiom-recognizer | FileCheck %s
+
+// The pass reads only the tag on the callee, so recognition also works on
+// parsed CIR assembly with no AST behind it.
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+cir.func private @_ZSt4findIPiiET_S1_S1_RKT0_(!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i> 
func_info<#cir.func_identity<std_find>>
+
+cir.func @caller(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>, %v: 
!cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+  %r = cir.call @_ZSt4findIPiiET_S1_S1_RKT0_(%a, %b, %v) : (!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>
+  cir.return %r : !cir.ptr<!s32i>
+}
+// CHECK: cir.std.find(%{{.*}}, %{{.*}}, %{{.*}}, @_ZSt4findIPiiET_S1_S1_RKT0_)
+
+// A musttail call must stay a call.
+cir.func @musttail_caller(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>, %v: 
!cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+  %r = cir.call @_ZSt4findIPiiET_S1_S1_RKT0_(%a, %b, %v) musttail : 
(!cir.ptr<!s32i>, !cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>
+  cir.return %r : !cir.ptr<!s32i>
+}
+// CHECK-LABEL: @musttail_caller
+// CHECK: cir.call @_ZSt4findIPiiET_S1_S1_RKT0_({{.*}}) musttail
+
+// An indirect call has no callee to resolve.
+cir.func @indirect_caller(%f: !cir.ptr<!cir.func<(!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>>>, %a: !cir.ptr<!s32i>, 
%b: !cir.ptr<!s32i>, %v: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
+  %r = cir.call %f(%a, %b, %v) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>>>, !cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>
+  cir.return %r : !cir.ptr<!s32i>
+}
+// CHECK-LABEL: @indirect_caller
+// CHECK-NOT: cir.std.find
+
+}
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer.cpp
index 28a2b502eb18a..0005568680865 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer.cpp
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cpp
@@ -1,2 +1,47 @@
 // RUN: %clang_cc1 -fclangir -emit-cir -mmlir --mlir-print-ir-after-all 
-clangir-enable-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
-check-prefix=CIR
 // CIR: IR Dump After IdiomRecognizer: cir-idiom-recognizer
+
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
--check-prefix=RAISED '--implicit-check-not=cir.call 
@_ZSt4findIPccET_S1_S1_RKT0_'
+// RUN: FileCheck %s --check-prefix=FINAL --input-file=%t.cir
+
+namespace std {
+template <class Iter, class T>
+__attribute__((pure)) Iter find(Iter, Iter, const T &) noexcept;
+}
+
+char *test_find(char *first, char *last, const char &value) {
+  return std::find(first, last, value);
+}
+// Raised, then lowered back in operand order with its call attributes.
+// RAISED: cir.std.find(
+// RAISED-SAME: @_ZSt4find
+// FINAL: %[[FIRST:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
+// FINAL: %[[LAST:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
+// FINAL: %[[VALUE:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
+// FINAL: cir.call @_ZSt4find{{.*}}(%[[FIRST]], %[[LAST]], %[[VALUE]])
+// FINAL-SAME: nothrow side_effect(pure)
+// FINAL-SAME: {llvm.noundef}
+// FINAL-SAME: -> (!cir.ptr<!s8i> {llvm.noundef})
+// FINAL-NOT: cir.call @_ZSt4find
+// FINAL-NOT: cir.std.
+
+// A function merely named like the std one is not raised.
+char *find(char *first, char *last, const char &value);
+char *test_non_std_find(char *first, char *last, const char &value) {
+  return find(first, last, value);
+}
+// RAISED: cir.call @_Z4find
+
+// A member function named find in std is not std::find. The types are chosen
+// so the call reaches the member exclusion itself.
+namespace std {
+struct string {
+  string *find(string *first, string *last);
+};
+}
+
+std::string *test_member_find(std::string &s, std::string *f, std::string *l) {
+  return s.find(f, l);
+}
+// RAISED: cir.call @_ZNSt6string4find
+// RAISED-NOT: cir.std.find
diff --git a/clang/tools/cir-opt/cir-opt.cpp b/clang/tools/cir-opt/cir-opt.cpp
index 6742985e149e8..22efa07cec8a0 100644
--- a/clang/tools/cir-opt/cir-opt.cpp
+++ b/clang/tools/cir-opt/cir-opt.cpp
@@ -67,6 +67,10 @@ int main(int argc, char **argv) {
     return mlir::createCXXABILoweringPass();
   });
 
+  ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
+    return mlir::createIdiomRecognizerPass();
+  });
+
   ::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
     return mlir::createCallConvLoweringPass();
   });

>From 475f7f89ffa1f12042cad6e2a6181e6d3a40211e Mon Sep 17 00:00:00 2001
From: SharmaRithik <[email protected]>
Date: Mon, 13 Jul 2026 06:58:09 +0000
Subject: [PATCH 2/3] [CIR] Map std functions to a kind with a StringSwitch and
 lower them through one function

Lower every raised std operation through one function that rebuilds the call
from the original callee, the operands, and the result type, so no per operation
lowering function is needed as more operations are added.

Map the std name to its kind through a StringSwitch, with getFunctionName
returning a StringLiteral so the names stay defined once in CIRStdOps.td. Read
std membership from the record for a member, so members go through the same path
while only free functions carry a tag today.

Split the guards test into two files with no preprocessor branch, and add tests
for the inline versioning namespace of libc++ and for names outside std.
---
 .../include/clang/CIR/Dialect/IR/CIRStdOps.td |  2 +-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 35 ++++++++++++------
 .../Dialect/Transforms/LoweringPrepare.cpp    | 36 +++++++++----------
 .../idiom-recognizer-guards-void-result.cpp   | 14 ++++++++
 .../Transforms/idiom-recognizer-guards.cpp    | 29 +++------------
 .../idiom-recognizer-inline-namespace.cpp     | 17 +++++++++
 .../idiom-recognizer-namespaces.cpp           | 30 ++++++++++++++++
 .../test/CIR/Transforms/idiom-recognizer.cpp  |  3 ++
 8 files changed, 112 insertions(+), 54 deletions(-)
 create mode 100644 
clang/test/CIR/Transforms/idiom-recognizer-guards-void-result.cpp
 create mode 100644 
clang/test/CIR/Transforms/idiom-recognizer-inline-namespace.cpp
 create mode 100644 clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td 
b/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
index 5b724ff6320a5..608a958e5117c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRStdOps.td
@@ -27,7 +27,7 @@ class CIR_StdOp<string functionName, dag args, dag res,
     static constexpr unsigned getNumArgs() {
       return }] # !size(args) # [{;
     }
-    static llvm::StringRef getFunctionName() {
+    static llvm::StringLiteral getFunctionName() {
       return "}] # functionName # [{";
     }
   }] # !if(!empty(knownKind), "", [{
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index de5a9a6a78a7b..87e1b78e935df 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -37,6 +37,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/raw_ostream.h"
 
 #include "CIRGenFunctionInfo.h"
@@ -3542,19 +3543,31 @@ void CIRGenModule::setCXXSpecialMemberAttr(
 
 void CIRGenModule::setFuncIdentityAttr(cir::FuncOp funcOp,
                                        const clang::FunctionDecl *funcDecl) {
-  // Only a free std function with a plain identifier can match a known
-  // entity, so members, static members, and operators never take a tag.
-  // Inline namespaces, like the versioning namespace of libc++, count as
-  // part of std.
-  if (!funcDecl || !funcDecl->getIdentifier() || isa<CXXMethodDecl>(funcDecl) 
||
-      !funcDecl->isInStdNamespace())
+  // A known entity is named by a plain identifier in std. For a member the
+  // record decides std membership. Inline namespaces, like the versioning
+  // namespace of libc++, count as part of std.
+  if (!funcDecl || !funcDecl->getIdentifier())
+    return;
+  const auto *method = dyn_cast<CXXMethodDecl>(funcDecl);
+  bool inStdNamespace = method ? method->getParent()->isInStdNamespace()
+                               : funcDecl->isInStdNamespace();
+  if (!inStdNamespace)
+    return;
+
+  // The names and the tags come from CIRStdOps.td, and the recognizer checks
+  // the shape of each call. Only free functions name a known entity today, so
+  // a member like char_traits::find never shares the tag of the free 
std::find.
+  std::optional<cir::KnownFuncKind> kind;
+  if (!method)
+    kind = llvm::StringSwitch<std::optional<cir::KnownFuncKind>>(
+               funcDecl->getName())
+               .Case(cir::StdFindOp::getFunctionName(),
+                     cir::StdFindOp::getFuncKind())
+               .Default(std::nullopt);
+  if (!kind)
     return;
 
-  // The names and the tags come from CIRStdOps.td, and the recognizer
-  // checks the shape of each call.
-  if (funcDecl->getName() == cir::StdFindOp::getFunctionName())
-    funcOp.setFuncInfoAttr(cir::FuncIdentityAttr::get(
-        &getMLIRContext(), cir::StdFindOp::getFuncKind()));
+  funcOp.setFuncInfoAttr(cir::FuncIdentityAttr::get(&getMLIRContext(), *kind));
 }
 
 static void setWindowsItaniumDLLImport(CIRGenModule &cgm, bool isLocal,
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 973dfe8667a97..3d956240a53f0 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -98,7 +98,7 @@ struct LoweringPreparePass
   void lowerTrivialCopyCall(cir::CallOp op);
   void lowerStoreOfConstAggregate(cir::StoreOp op);
   void lowerLocalInitOp(cir::LocalInitOp op);
-  void lowerStdFindOp(cir::StdFindOp op);
+  void lowerStdOp(mlir::Operation *op);
 
   /// Return the FuncOp called by `callOp`.  Uses the cached `symbolTables`
   /// member to avoid the O(M) module-wide scan that the static
@@ -2252,24 +2252,24 @@ void 
LoweringPreparePass::lowerStoreOfConstAggregate(cir::StoreOp op) {
     constOp.erase();
 }
 
-// Raised ops carry the original callee and its call attributes, so lowering
-// them back rebuilds an equivalent plain call.
-static void restoreCallAttrs(cir::CallOp call, mlir::Operation *raised) {
-  for (mlir::NamedAttribute attr : raised->getAttrs())
-    if (attr.getName() != "original_fn")
-      call->setAttr(attr.getName(), attr.getValue());
-}
-
-void LoweringPreparePass::lowerStdFindOp(cir::StdFindOp op) {
+// Every raised operation carries the original callee, the operands, and the
+// attributes of the call, so this one function lowers any of them back to an
+// equivalent plain call.
+void LoweringPreparePass::lowerStdOp(mlir::Operation *op) {
   cir::CIRBaseBuilderTy builder(getContext());
-  builder.setInsertionPointAfter(op.getOperation());
+  builder.setInsertionPointAfter(op);
+  mlir::Type resultType;
+  if (op->getNumResults())
+    resultType = op->getResult(0).getType();
   cir::CallOp call = builder.createCallOp(
-      op.getLoc(), op.getOriginalFnAttr(), op.getType(),
-      mlir::ValueRange{op.getFirst(), op.getLast(), op.getPattern()});
-  restoreCallAttrs(call, op);
+      op->getLoc(), op->getAttrOfType<mlir::FlatSymbolRefAttr>("original_fn"),
+      resultType, op->getOperands());
+  for (mlir::NamedAttribute attr : op->getAttrs())
+    if (attr.getName() != "original_fn")
+      call->setAttr(attr.getName(), attr.getValue());
 
-  op.replaceAllUsesWith(call);
-  op.erase();
+  op->replaceAllUsesWith(call);
+  op->erase();
 }
 
 void LoweringPreparePass::runOnOp(mlir::Operation *op) {
@@ -2277,8 +2277,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
     lowerArrayCtor(arrayCtor);
   } else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
     lowerArrayDtor(arrayDtor);
-  } else if (auto stdFind = mlir::dyn_cast<cir::StdFindOp>(op)) {
-    lowerStdFindOp(stdFind);
+  } else if (mlir::isa<cir::StdFindOp>(op)) {
+    lowerStdOp(op);
   } else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
     lowerCastOp(cast);
   } else if (auto complexConj = mlir::dyn_cast<cir::ComplexConjOp>(op)) {
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-guards-void-result.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-guards-void-result.cpp
new file mode 100644
index 0000000000000..fd72b75083068
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer-guards-void-result.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--implicit-check-not=cir.std.
+
+// std::find returns the iterator, so a result is required. A void result
+// fails the recognizer's result check and the call stays.
+
+namespace std {
+void find(char *first, char *last, const char &value);
+}
+
+void test_void_result(char *first, char *last, const char &value) {
+  std::find(first, last, value);
+}
+// CHECK-LABEL: @_Z16test_void_result
+// CHECK: cir.call @_ZSt4findPcS_RKc
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
index 28ada39057fde..ae9b9db64bf18 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
+++ b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
@@ -1,23 +1,4 @@
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--implicit-check-not=cir.std.
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -DVOID_RESULT 
-fclangir -clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--check-prefix=VOID --implicit-check-not=cir.std.
-
-// Each call satisfies every recognizer check except the one guard it pins,
-// and stays the call to the overload its comment names.
-
-#ifdef VOID_RESULT
-
-namespace std {
-// std::find returns the iterator, so a result is required.
-void find(char *first, char *last, const char &value);
-}
-
-void test_void_result(char *first, char *last, const char &value) {
-  std::find(first, last, value);
-}
-// VOID-LABEL: @_Z16test_void_result
-// VOID: cir.call @_ZSt4findPcS_RKc
-
-#else
 
 namespace std {
 // Variadic, only viable for the all-pointer call in test_variadic.
@@ -26,7 +7,9 @@ char *find(char *first, ...);
 int find(char *first, char *last, const char &value);
 // Searched value type differs from the element type.
 char *find(char *first, char *last, const int &value);
-// Wrong arity.
+// Wrong argument count. The C++17 std::find overload taking an ExecutionPolicy
+// is also declined here, since it too differs from the recognized three
+// argument shape.
 char *find(char *first, char *last, const char &value, int n);
 }
 
@@ -48,10 +31,8 @@ char *test_pattern_type(char *first, char *last, const int 
&value) {
 // CHECK-LABEL: @_Z17test_pattern_type
 // CHECK: cir.call @_ZSt4findPcS_RKi
 
-char *test_arity(char *first, char *last, const char &value) {
+char *test_wrong_arg_count(char *first, char *last, const char &value) {
   return std::find(first, last, value, 1);
 }
-// CHECK-LABEL: @_Z10test_arity
+// CHECK-LABEL: @_Z20test_wrong_arg_count
 // CHECK: cir.call @_ZSt4findPcS_RKci
-
-#endif
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-inline-namespace.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-inline-namespace.cpp
new file mode 100644
index 0000000000000..5eb146ba2f3b7
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer-inline-namespace.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s
+
+// The versioning namespace of libc++ is inline, so std::find resolves through
+// it and the call is still tagged and raised.
+
+namespace std {
+inline namespace __1 {
+template <class Iter, class T>
+Iter find(Iter, Iter, const T &);
+}
+}
+
+char *test_inline_namespace(char *first, char *last, const char &value) {
+  return std::find(first, last, value);
+}
+// CHECK-LABEL: @_Z21test_inline_namespace
+// CHECK: cir.std.find
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp
new file mode 100644
index 0000000000000..a5e07c1e1e1ea
--- /dev/null
+++ b/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--implicit-check-not=cir.std.
+
+// std membership is fixed when the tag is set, so a find outside std is never
+// tagged and never raised.
+
+// A nested namespace that is not inline is not std.
+namespace std {
+namespace another_ns {
+template <class Iter, class T>
+Iter find(Iter, Iter, const T &);
+}
+}
+
+char *test_nested_namespace(char *first, char *last, const char &value) {
+  return std::another_ns::find(first, last, value);
+}
+// CHECK-LABEL: @_Z21test_nested_namespace
+// CHECK: cir.call
+
+// An anonymous namespace function is not std::find.
+namespace {
+template <class Iter, class T>
+Iter find(Iter, Iter, const T &);
+}
+
+char *test_anonymous_namespace(char *first, char *last, const char &value) {
+  return find(first, last, value);
+}
+// CHECK-LABEL: @_Z24test_anonymous_namespace
+// CHECK: cir.call
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer.cpp
index 0005568680865..b38cd76f9bfc4 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer.cpp
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -fclangir -emit-cir -mmlir --mlir-print-ir-after-all 
-clangir-enable-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
-check-prefix=CIR
 // CIR: IR Dump After IdiomRecognizer: cir-idiom-recognizer
 
+// The implicit-check-not on the RAISED run makes the original std::find call 
an
+// error anywhere in the post-pass dump, so the test only passes if that call 
was
+// raised to cir.std.find rather than left in place.
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
--check-prefix=RAISED '--implicit-check-not=cir.call 
@_ZSt4findIPccET_S1_S1_RKT0_'
 // RUN: FileCheck %s --check-prefix=FINAL --input-file=%t.cir
 

>From 834af3615f2d9ec124fea06415f0ada2e80fb13d Mon Sep 17 00:00:00 2001
From: SharmaRithik <[email protected]>
Date: Tue, 14 Jul 2026 01:05:34 +0000
Subject: [PATCH 3/3] [CIR] Spell the identity as std::find, tighten the
 recognizer and tests

Print the identity spelling as std::find. It is not a bare identifier,
so a quoting printer serializes it and the enum parser reads it back.

Fold the two func_info setters into one setFuncInfoAttr. The special
member forms come first and return, and a known standard library entity
is the fall through, so a member and a free function never both fire.

Move the operand and result count checks into the per operation shape
check, now signatureMatches, so each check validates its own shape
before it reads the operands.

Tighten the tests. The guards pin the full mangled name, the lowered
call is checked with its operands bound to their named allocas so a
reordering would fail, the declined cases assert both the non raise and
the survival, and the parsed CIR test asserts the operand order. The
namespace cases join the guards file, since they decline for the same
reason.
---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  | 10 +++--
 clang/lib/CIR/CodeGen/CIRGenClass.cpp         |  2 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  4 +-
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 21 ++++------
 clang/lib/CIR/CodeGen/CIRGenModule.h          | 13 +++----
 .../Dialect/Transforms/IdiomRecognizer.cpp    | 21 +++++-----
 clang/test/CIR/CodeGen/func-identity-attr.cpp |  2 +-
 clang/test/CIR/IR/func-identity-attr.cir      |  4 +-
 .../Transforms/idiom-recognizer-guards.cpp    | 29 ++++++++++++++
 .../idiom-recognizer-namespaces.cpp           | 30 ---------------
 .../test/CIR/Transforms/idiom-recognizer.cir  |  6 ++-
 .../test/CIR/Transforms/idiom-recognizer.cpp  | 38 +++++++++++--------
 12 files changed, 94 insertions(+), 86 deletions(-)
 delete mode 100644 clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 21ba6c8c15623..bf91a1831eeff 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1300,9 +1300,13 @@ def CIR_CXXAssignAttr : CIR_Attr<"CXXAssign", 
"cxx_assign"> {
 // pairs with one raised operation in CIRStdOps.td.
 def CIR_KnownFuncKind : CIR_I32EnumAttr<"KnownFuncKind",
     "known standard library entity", [
-  I32EnumAttrCase<"StdFind", 1, "std_find">,
+  I32EnumAttrCase<"StdFind", 1, "std::find">,
 ]> {
   let genSpecializedAttr = 0;
+  // A name like std::find is not a bare identifier, so print it as a quoted
+  // string. The enum parser already reads a quoted string back.
+  let parameterPrinter =
+      "$_printer.printKeywordOrString(" # symbolToStringFnName # "($_self))";
 }
 
 def CIR_FuncIdentityAttr : CIR_Attr<"FuncIdentity", "func_identity"> {
@@ -1312,7 +1316,7 @@ def CIR_FuncIdentityAttr : CIR_Attr<"FuncIdentity", 
"func_identity"> {
     transformations can recognize calls to well known library functions
     without decoding mangled symbol names.
 
-    The tag names the whole entity. For `std_find` that is the free
+    The tag names the whole entity. For `std::find` that is the free
     function named `find` in the `std` namespace, so a member function, a
     static member, or an operator can never carry the tag. Inline
     namespaces, such as the versioning namespace of libc++, count as part
@@ -1321,7 +1325,7 @@ def CIR_FuncIdentityAttr : CIR_Attr<"FuncIdentity", 
"func_identity"> {
 
     Example:
     ```
-    #cir.func_identity<std_find>
+    #cir.func_identity<"std::find">
     ```
   }];
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index eb6490973da75..96ece6703a512 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -896,7 +896,7 @@ void 
CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) {
          "Body of an implicit assignment operator should be compound stmt.");
   const auto *rootCS = cast<CompoundStmt>(rootS);
 
-  cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), assignOp);
+  cgm.setFuncInfoAttr(cast<cir::FuncOp>(curFn), assignOp);
 
   assert(!cir::MissingFeatures::incrementProfileCounter());
   assert(!cir::MissingFeatures::runCleanupsScope());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index d0aedc0689404..bbcf5b6c38899 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -813,7 +813,7 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList 
&args) {
           ctorType == Ctor_Complete) &&
          "can only generate complete ctor for this ABI");
 
-  cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), ctor);
+  cgm.setFuncInfoAttr(cast<cir::FuncOp>(curFn), ctor);
 
   if (ctorType == Ctor_Complete && isConstructorDelegationValid(ctor) &&
       cgm.getTarget().getCXXABI().hasConstructorVariants()) {
@@ -870,7 +870,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList 
&args) {
   const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl());
   CXXDtorType dtorType = curGD.getDtorType();
 
-  cgm.setCXXSpecialMemberAttr(cast<cir::FuncOp>(curFn), dtor);
+  cgm.setFuncInfoAttr(cast<cir::FuncOp>(curFn), dtor);
 
   // For an abstract class, non-base destructors are never used (and can't
   // be emitted in general, because vbase dtors may not have been validated
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 87e1b78e935df..75b4b6968604a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3459,11 +3459,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, 
StringRef name,
 
     assert(!cir::MissingFeatures::opFuncExtraAttrs());
 
-    // Mark C++ special member functions (Constructor, Destructor etc.)
-    setCXXSpecialMemberAttr(func, funcDecl);
-
-    // Tag functions that match a known standard library entity.
-    setFuncIdentityAttr(func, funcDecl);
+    // Record the func_info tag, a C++ special member form or a known standard
+    // library entity.
+    setFuncInfoAttr(func, funcDecl);
 
     if (!cgf)
       theModule.push_back(func);
@@ -3507,8 +3505,8 @@ static cir::AssignKind getAssignKindFromDecl(const 
CXXMethodDecl *method) {
   llvm_unreachable("not a copy or move assignment operator");
 }
 
-void CIRGenModule::setCXXSpecialMemberAttr(
-    cir::FuncOp funcOp, const clang::FunctionDecl *funcDecl) {
+void CIRGenModule::setFuncInfoAttr(cir::FuncOp funcOp,
+                                   const clang::FunctionDecl *funcDecl) {
   if (!funcDecl)
     return;
 
@@ -3539,16 +3537,13 @@ void CIRGenModule::setCXXSpecialMemberAttr(
     funcOp.setFuncInfoAttr(cxxAssign);
     return;
   }
-}
 
-void CIRGenModule::setFuncIdentityAttr(cir::FuncOp funcOp,
-                                       const clang::FunctionDecl *funcDecl) {
-  // A known entity is named by a plain identifier in std. For a member the
+  // Otherwise tag a function that matches a known standard library entity. A
+  // known entity is named by a plain identifier in std. For a member the
   // record decides std membership. Inline namespaces, like the versioning
   // namespace of libc++, count as part of std.
-  if (!funcDecl || !funcDecl->getIdentifier())
+  if (!funcDecl->getIdentifier())
     return;
-  const auto *method = dyn_cast<CXXMethodDecl>(funcDecl);
   bool inStdNamespace = method ? method->getParent()->isInStdNamespace()
                                : funcDecl->isInStdNamespace();
   if (!inStdNamespace)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 4a4e42bb16195..6b656e34bf171 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -798,14 +798,11 @@ class CIRGenModule : public CIRGenTypeCache {
                                        cir::FuncType ty,
                                        const clang::FunctionDecl *fd);
 
-  /// Mark the function as a special member (e.g. constructor, destructor)
-  void setCXXSpecialMemberAttr(cir::FuncOp funcOp,
-                               const clang::FunctionDecl *funcDecl);
-
-  /// Tag functions that match a known standard library entity, so passes
-  /// can recognize calls to them without the AST.
-  void setFuncIdentityAttr(cir::FuncOp funcOp,
-                           const clang::FunctionDecl *funcDecl);
+  /// Record the func_info tag for a function, either a C++ special member
+  /// form (constructor, destructor, assignment) or a known standard library
+  /// entity that passes can recognize without the AST.
+  void setFuncInfoAttr(cir::FuncOp funcOp,
+                       const clang::FunctionDecl *funcDecl);
 
   cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name,
                                     mlir::NamedAttrList extraAttrs = {},
diff --git a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp 
b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
index ce234363ac6b1..030a6bed68b99 100644
--- a/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/IdiomRecognizer.cpp
@@ -30,11 +30,15 @@ namespace mlir {
 
 namespace {
 
-// The raised operation requires matching operand types. The searched value
-// arrives by reference and must share the iterator type.
-template <typename TargetOp> bool operandTypesMatch(CallOp call);
-
-template <> bool operandTypesMatch<StdFindOp>(CallOp call) {
+// A call matches when its shape fits the raised operation, the operand and
+// result counts first and then the operand types. The searched value arrives
+// by reference and must share the iterator type.
+template <typename TargetOp> bool signatureMatches(CallOp call);
+
+template <> bool signatureMatches<StdFindOp>(CallOp call) {
+  if (call.getNumOperands() != StdFindOp::getNumArgs() ||
+      call->getNumResults() != 1)
+    return false;
   mlir::Type iterTy = call.getOperand(0).getType();
   return iterTy == call.getOperand(1).getType() &&
          iterTy == call.getOperand(2).getType() &&
@@ -56,10 +60,8 @@ template <typename TargetOp> class StdRecognizer {
   static bool raise(CallOp call, mlir::MLIRContext &context,
                     mlir::SymbolTableCollection &symbolTables) {
     // A musttail call must stay a call, so it is never raised.
-    constexpr unsigned numArgs = TargetOp::getNumArgs();
-    if (!call.getCallee() || call.getNumOperands() != numArgs ||
-        call->getNumResults() != 1 || call.getMusttail() ||
-        !operandTypesMatch<TargetOp>(call))
+    if (!call.getCallee() || call.getMusttail() ||
+        !signatureMatches<TargetOp>(call))
       return false;
 
     // Only a free std function with the right name carries the tag, so
@@ -75,6 +77,7 @@ template <typename TargetOp> class StdRecognizer {
 
     cir::CIRBaseBuilderTy builder(context);
     builder.setInsertionPointAfter(call.getOperation());
+    constexpr unsigned numArgs = TargetOp::getNumArgs();
     TargetOp op = buildCall(builder, call, 
std::make_index_sequence<numArgs>());
     // The raised operation keeps every call attribute except the callee,
     // which it carries as original_fn, so lowering back loses nothing.
diff --git a/clang/test/CIR/CodeGen/func-identity-attr.cpp 
b/clang/test/CIR/CodeGen/func-identity-attr.cpp
index 75d1308740d1f..468d1664717b8 100644
--- a/clang/test/CIR/CodeGen/func-identity-attr.cpp
+++ b/clang/test/CIR/CodeGen/func-identity-attr.cpp
@@ -21,7 +21,7 @@ int *find(int *first, int *last, int value);
 int *std_call(int *first, int *last) { return std::find(first, last, 42); }
 // The free std find carries its tag, with inline namespaces looked
 // through.
-// CHECK-DAG: cir.func{{.*}} @_ZNSt3__14find{{.*}} 
func_info<#cir.func_identity<std_find>>
+// CHECK: cir.func{{.*}} @_ZNSt3__14find{{.*}} 
func_info<#cir.func_identity<"std::find">>
 
 struct S {
   void operator()();
diff --git a/clang/test/CIR/IR/func-identity-attr.cir 
b/clang/test/CIR/IR/func-identity-attr.cir
index 83924aaf0353f..0e338112cb38d 100644
--- a/clang/test/CIR/IR/func-identity-attr.cir
+++ b/clang/test/CIR/IR/func-identity-attr.cir
@@ -2,7 +2,7 @@
 
 module {
 
-cir.func private @_ZSt4findv() func_info<#cir.func_identity<std_find>>
-// CHECK: cir.func private @_ZSt4findv() 
func_info<#cir.func_identity<std_find>>
+cir.func private @_ZSt4findv() func_info<#cir.func_identity<"std::find">>
+// CHECK: cir.func private @_ZSt4findv() 
func_info<#cir.func_identity<"std::find">>
 
 }
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
index ae9b9db64bf18..11612371e023e 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
+++ b/clang/test/CIR/Transforms/idiom-recognizer-guards.cpp
@@ -36,3 +36,32 @@ char *test_wrong_arg_count(char *first, char *last, const 
char &value) {
 }
 // CHECK-LABEL: @_Z20test_wrong_arg_count
 // CHECK: cir.call @_ZSt4findPcS_RKci
+
+// std membership is fixed when the tag is set, so a find outside std is never
+// tagged and never raised, whatever the call shape.
+
+// A nested namespace that is not inline is not std.
+namespace std {
+namespace another_ns {
+template <class Iter, class T>
+Iter find(Iter, Iter, const T &);
+}
+}
+
+char *test_nested_namespace(char *first, char *last, const char &value) {
+  return std::another_ns::find(first, last, value);
+}
+// CHECK-LABEL: @_Z21test_nested_namespace
+// CHECK: cir.call
+
+// An anonymous namespace function is not std::find.
+namespace {
+template <class Iter, class T>
+Iter find(Iter, Iter, const T &);
+}
+
+char *test_anonymous_namespace(char *first, char *last, const char &value) {
+  return find(first, last, value);
+}
+// CHECK-LABEL: @_Z24test_anonymous_namespace
+// CHECK: cir.call
diff --git a/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp
deleted file mode 100644
index a5e07c1e1e1ea..0000000000000
--- a/clang/test/CIR/Transforms/idiom-recognizer-namespaces.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o /dev/null 2>&1 | FileCheck %s 
--implicit-check-not=cir.std.
-
-// std membership is fixed when the tag is set, so a find outside std is never
-// tagged and never raised.
-
-// A nested namespace that is not inline is not std.
-namespace std {
-namespace another_ns {
-template <class Iter, class T>
-Iter find(Iter, Iter, const T &);
-}
-}
-
-char *test_nested_namespace(char *first, char *last, const char &value) {
-  return std::another_ns::find(first, last, value);
-}
-// CHECK-LABEL: @_Z21test_nested_namespace
-// CHECK: cir.call
-
-// An anonymous namespace function is not std::find.
-namespace {
-template <class Iter, class T>
-Iter find(Iter, Iter, const T &);
-}
-
-char *test_anonymous_namespace(char *first, char *last, const char &value) {
-  return find(first, last, value);
-}
-// CHECK-LABEL: @_Z24test_anonymous_namespace
-// CHECK: cir.call
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cir 
b/clang/test/CIR/Transforms/idiom-recognizer.cir
index ed5a8f2d16a2e..7e9507f13c023 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer.cir
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cir
@@ -7,13 +7,15 @@
 
 module {
 
-cir.func private @_ZSt4findIPiiET_S1_S1_RKT0_(!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i> 
func_info<#cir.func_identity<std_find>>
+cir.func private @_ZSt4findIPiiET_S1_S1_RKT0_(!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i> 
func_info<#cir.func_identity<"std::find">>
 
 cir.func @caller(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>, %v: 
!cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
   %r = cir.call @_ZSt4findIPiiET_S1_S1_RKT0_(%a, %b, %v) : (!cir.ptr<!s32i>, 
!cir.ptr<!s32i>, !cir.ptr<!s32i>) -> !cir.ptr<!s32i>
   cir.return %r : !cir.ptr<!s32i>
 }
-// CHECK: cir.std.find(%{{.*}}, %{{.*}}, %{{.*}}, @_ZSt4findIPiiET_S1_S1_RKT0_)
+// The operands are threaded into the raised op in order, first to last.
+// CHECK: cir.func @caller(%[[A:.*]]: !cir.ptr<!s32i>, %[[B:.*]]: 
!cir.ptr<!s32i>, %[[V:.*]]: !cir.ptr<!s32i>)
+// CHECK: cir.std.find(%[[A]] :{{.*}}, %[[B]] :{{.*}}, %[[V]] :{{.*}}, 
@_ZSt4findIPiiET_S1_S1_RKT0_)
 
 // A musttail call must stay a call.
 cir.func @musttail_caller(%a: !cir.ptr<!s32i>, %b: !cir.ptr<!s32i>, %v: 
!cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
diff --git a/clang/test/CIR/Transforms/idiom-recognizer.cpp 
b/clang/test/CIR/Transforms/idiom-recognizer.cpp
index b38cd76f9bfc4..f60d10f2ae4b1 100644
--- a/clang/test/CIR/Transforms/idiom-recognizer.cpp
+++ b/clang/test/CIR/Transforms/idiom-recognizer.cpp
@@ -1,11 +1,12 @@
 // RUN: %clang_cc1 -fclangir -emit-cir -mmlir --mlir-print-ir-after-all 
-clangir-enable-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
-check-prefix=CIR
 // CIR: IR Dump After IdiomRecognizer: cir-idiom-recognizer
 
-// The implicit-check-not on the RAISED run makes the original std::find call 
an
-// error anywhere in the post-pass dump, so the test only passes if that call 
was
-// raised to cir.std.find rather than left in place.
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
--check-prefix=RAISED '--implicit-check-not=cir.call 
@_ZSt4findIPccET_S1_S1_RKT0_'
-// RUN: FileCheck %s --check-prefix=FINAL --input-file=%t.cir
+// The implicit-check-not on the RAISED run makes any surviving std::find call
+// an error in the post-pass dump, so the test only passes if it was raised to
+// cir.std.find. The FINAL run checks the lowered output and its 
implicit-check-not
+// proves no raised operation leaked past LoweringPrepare.
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-clangir-enable-idiom-recognizer -emit-cir -mmlir 
--mlir-print-ir-after=cir-idiom-recognizer %s -o %t.cir 2>&1 | FileCheck %s 
--check-prefix=RAISED '--implicit-check-not=cir.call @_ZSt4find'
+// RUN: FileCheck %s --check-prefix=FINAL --input-file=%t.cir 
--implicit-check-not=cir.std.
 
 namespace std {
 template <class Iter, class T>
@@ -15,25 +16,31 @@ __attribute__((pure)) Iter find(Iter, Iter, const T &) 
noexcept;
 char *test_find(char *first, char *last, const char &value) {
   return std::find(first, last, value);
 }
-// Raised, then lowered back in operand order with its call attributes.
+// Raised to cir.std.find, then lowered back to the exact same call with its
+// operands in source order and its attributes.
 // RAISED: cir.std.find(
-// RAISED-SAME: @_ZSt4find
-// FINAL: %[[FIRST:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
-// FINAL: %[[LAST:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
-// FINAL: %[[VALUE:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!s8i>>
-// FINAL: cir.call @_ZSt4find{{.*}}(%[[FIRST]], %[[LAST]], %[[VALUE]])
+// RAISED-SAME: @_ZSt4findIPccET_S1_S1_RKT0_
+// FINAL: %[[FIRST_ADDR:.*]] = cir.alloca "first"
+// FINAL: %[[LAST_ADDR:.*]] = cir.alloca "last"
+// FINAL: %[[VALUE_ADDR:.*]] = cir.alloca "value"
+// FINAL: %[[FIRST:.*]] = cir.load{{.*}} %[[FIRST_ADDR]] :
+// FINAL: %[[LAST:.*]] = cir.load{{.*}} %[[LAST_ADDR]] :
+// FINAL: %[[VALUE:.*]] = cir.load{{.*}} %[[VALUE_ADDR]] :
+// FINAL: cir.call @_ZSt4findIPccET_S1_S1_RKT0_(%[[FIRST]], %[[LAST]], 
%[[VALUE]])
 // FINAL-SAME: nothrow side_effect(pure)
 // FINAL-SAME: {llvm.noundef}
 // FINAL-SAME: -> (!cir.ptr<!s8i> {llvm.noundef})
 // FINAL-NOT: cir.call @_ZSt4find
-// FINAL-NOT: cir.std.
 
-// A function merely named like the std one is not raised.
+// A function merely named like the std one is not raised, and it survives the
+// whole pipeline as the same plain call.
 char *find(char *first, char *last, const char &value);
 char *test_non_std_find(char *first, char *last, const char &value) {
   return find(first, last, value);
 }
-// RAISED: cir.call @_Z4find
+// RAISED: cir.call @_Z4findPcS_RKc
+// RAISED-NOT: cir.std.find
+// FINAL: cir.call @_Z4findPcS_RKc
 
 // A member function named find in std is not std::find. The types are chosen
 // so the call reaches the member exclusion itself.
@@ -46,5 +53,6 @@ struct string {
 std::string *test_member_find(std::string &s, std::string *f, std::string *l) {
   return s.find(f, l);
 }
-// RAISED: cir.call @_ZNSt6string4find
+// RAISED: cir.call @_ZNSt6string4findEPS_S0_
 // RAISED-NOT: cir.std.find
+// FINAL: cir.call @_ZNSt6string4findEPS_S0_

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

Reply via email to