[clang] 1d6f5b0 - [CIR] Memoize FuncType purity checks in the CIR dialect (#232863)

via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 19 10:58:08 PDT 2026


Author: Bruno Cardoso Lopes
Date: 2026-10-19T10:58:03-07:00
New Revision: 1d6f5b0fc795b4d22379fbf9d44f5e892bea48bf

URL: https://github.com/llvm/llvm-project/commit/1d6f5b0fc795b4d22379fbf9d44f5e892bea48bf
DIFF: https://github.com/llvm/llvm-project/commit/1d6f5b0fc795b4d22379fbf9d44f5e892bea48bf.diff

LOG: [CIR] Memoize FuncType purity checks in the CIR dialect (#232863)

FuncType::verify walks every type in the signature to prove it only
contains CIR types, and it runs on every FuncType construction in
asserts-enabled builds. Signatures share the same record types over and
over, so the same graphs were re-walked thousands of times per
translation unit (15% of CXXABILowering).

Memoize "purity" per type in the CIR dialect object: the dialect gives
the cache exactly the context lifetime, and a mutex guards the map
(computing outside the lock is safe since two threads store the same
value). Records gain members when completed, so a 'pure' answer is only
cached when the walk saw no incomplete record; 'impure' answers are
always stable.

CXXABILowering on clang/lib/Sema/SemaExpr.cpp (selfhosting) goes from
6.3s to 3.0s and `-emit-cir` from 49.4s to 29.3s (asserts-enabled
build); output is unchanged.

Added: 
    

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRDialect.h
    clang/include/clang/CIR/Dialect/IR/CIRDialect.td
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp
    clang/lib/CIR/Dialect/IR/CIRTypes.cpp
    clang/test/CIR/IR/invalid-func.cir

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
index d9018e93628b6..f28f2bf34422d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h
@@ -34,6 +34,8 @@
 #include "clang/CIR/Interfaces/CIROpInterfaces.h"
 #include "clang/CIR/MissingFeatures.h"
 
+#include <mutex>
+
 using BuilderCallbackRef =
     llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>;
 using BuilderOpStateCallbackRef = llvm::function_ref<void(

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
index cd6601c213ba9..5bd26c6fa9b70 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td
@@ -147,6 +147,14 @@ def CIR_Dialect : Dialect {
     mlir::Type parseType(mlir::DialectAsmParser &parser) const override;
     void printType(mlir::Type type,
                    mlir::DialectAsmPrinter &printer) const override;
+
+    /// Returns true if \p ty and every type nested in it are CIR types.
+    /// Memoized, since FuncType verification re-queries the same records.
+    bool isPureCIRType(mlir::Type ty) const;
+
+  private:
+    mutable llvm::DenseMap<mlir::Type, bool> pureCIRTypeCache;
+    mutable std::mutex pureCIRTypeCacheMutex;
   }];
 }
 

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index f564eee053705..4dfbfbb195ca4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -253,6 +253,33 @@ static LogicalResult verifyOffloadContainer(mlir::Operation *op) {
   return success();
 }
 
+bool cir::CIRDialect::isPureCIRType(mlir::Type ty) const {
+  assert(ty && "isPureCIRType requires a non-null type");
+  {
+    std::lock_guard<std::mutex> lock(pureCIRTypeCacheMutex);
+    auto it = pureCIRTypeCache.find(ty);
+    if (it != pureCIRTypeCache.end())
+      return it->second;
+  }
+  // Records gain members when completed, so a "pure" answer is only stable
+  // when the walk saw no incomplete record. "Impure" answers stay stable.
+  bool sawIncompleteRecord = false;
+  bool result = !ty.walk([&](mlir::Type t) {
+                     if (!t)
+                       return mlir::WalkResult::advance();
+                     if (auto rec = mlir::dyn_cast<cir::RecordType>(t))
+                       sawIncompleteRecord |= rec.isIncomplete();
+                     return mlir::isa<cir::CIRDialect>(t.getDialect())
+                                ? mlir::WalkResult::advance()
+                                : mlir::WalkResult::interrupt();
+                   }).wasInterrupted();
+  if (!result || !sawIncompleteRecord) {
+    std::lock_guard<std::mutex> lock(pureCIRTypeCacheMutex);
+    pureCIRTypeCache[ty] = result;
+  }
+  return result;
+}
+
 LogicalResult
 cir::CIRDialect::verifyOperationAttribute(mlir::Operation *op,
                                           mlir::NamedAttribute attr) {

diff  --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 1ec5687e17db0..c6296180cfb02 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -64,13 +64,8 @@ cir::FPTypeInterface cir::getFloatingPointType(const llvm::fltSemantics &sem,
 static bool isPureCIRType(mlir::Type ty) {
   if (!ty)
     return true;
-  return !ty.walk([](mlir::Type t) {
-              if (!t)
-                return mlir::WalkResult::advance();
-              return mlir::isa<cir::CIRDialect>(t.getDialect())
-                         ? mlir::WalkResult::advance()
-                         : mlir::WalkResult::interrupt();
-            }).wasInterrupted();
+  return ty.getContext()->getOrLoadDialect<cir::CIRDialect>()->isPureCIRType(
+      ty);
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/clang/test/CIR/IR/invalid-func.cir b/clang/test/CIR/IR/invalid-func.cir
index c4b0eb1fd0c3e..da335f3a96c43 100644
--- a/clang/test/CIR/IR/invalid-func.cir
+++ b/clang/test/CIR/IR/invalid-func.cir
@@ -109,3 +109,13 @@ module {
   cir.func comdat preferred_alignment 0 @f() { // expected-error {{expected '('}}
     cir.return
 }
+
+// -----
+
+// The inner function type is built while S is still incomplete; the purity
+// cache must not keep that answer once S completes with a non-CIR member.
+!rec_S = !cir.struct<"S" {data !cir.ptr<!cir.func<(!cir.ptr<!cir.struct<"S">>)>>, data i32}>
+
+module {
+  cir.func private @l0(!cir.ptr<!rec_S>) // expected-error {{expected all types in the function signature to be CIR types}}
+}


        


More information about the cfe-commits mailing list