[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