https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/232863
>From 586a80a94542620da7d681323353543f9e093d39 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <[email protected]> Date: Wed, 7 Oct 2026 17:12:42 -0700 Subject: [PATCH] [CIR] Memoize FuncType purity checks in the CIR dialect 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 goes from 6.3s to 3.0s and -emit-cir from 49.4s to 29.3s (asserts-enabled build); output is unchanged. Assisted-by: Muse Code (Muse). --- .../include/clang/CIR/Dialect/IR/CIRDialect.h | 2 ++ .../clang/CIR/Dialect/IR/CIRDialect.td | 8 ++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 27 +++++++++++++++++++ clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 9 ++----- clang/test/CIR/IR/invalid-func.cir | 10 +++++++ 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index d9018e93628b69..f28f2bf34422d0 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 429af17eb194b8..2d6fc2ce8e7d06 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -146,6 +146,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 f564eee0537057..4dfbfbb195ca46 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 1ec5687e17db01..c6296180cfb025 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 c4b0eb1fd0c3ef..da335f3a96c435 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}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
