[clang] [CIR] Use SymbolUserMap in applyReplacements to fix quadratic behavior (PR #195883)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 5 09:49:42 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: adams381
<details>
<summary>Changes</summary>
applyReplacements() previously called replaceAllSymbolUses() for each replacement, which walks the entire module every time — O(R × M) for R replacements and M operations. For C++ programs with heavy template instantiation (e.g., Eigen), this quadratic behavior dominated compile time.
Replace the per-replacement module walk with a single SymbolUserMap built once (O(M)), then use replaceAllUsesWith() which scopes each replacement to only the actual user operations. The debug-only verifyPointerTypeArgs helper is also updated to reuse the map.
Measured on Eigen's basicstuff.cpp (356 lines, heavy template instantiation): compile time dropped from 20m29s to 1m2s (20x speedup). CIR-to-classic ratio improved from 117x to 7.2x.
Made with [Cursor](https://cursor.com)
---
Full diff: https://github.com/llvm/llvm-project/pull/195883.diff
1 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+21-18)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index af8fd52bef017..db84967bd74a9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -33,6 +33,7 @@
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
#include "clang/CIR/MissingFeatures.h"
+#include "mlir/IR/SymbolTable.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -1648,15 +1649,10 @@ void CIRGenModule::addReplacement(StringRef name, mlir::Operation *op) {
}
#ifndef NDEBUG
-static bool verifyPointerTypeArgs(mlir::ModuleOp modOp, cir::FuncOp oldF,
- cir::FuncOp newF) {
- std::optional<mlir::SymbolTable::UseRange> optionalUseRange =
- oldF.getSymbolUses(modOp);
- if (!optionalUseRange)
- return true;
-
- for (const mlir::SymbolTable::SymbolUse &u : *optionalUseRange) {
- auto call = mlir::dyn_cast<cir::CallOp>(u.getUser());
+static bool verifyPointerTypeArgs(cir::FuncOp oldF, cir::FuncOp newF,
+ mlir::SymbolUserMap &userMap) {
+ for (mlir::Operation *user : userMap.getUsers(oldF)) {
+ auto call = mlir::dyn_cast<cir::CallOp>(user);
if (!call)
continue;
@@ -1672,6 +1668,15 @@ static bool verifyPointerTypeArgs(mlir::ModuleOp modOp, cir::FuncOp oldF,
#endif // NDEBUG
void CIRGenModule::applyReplacements() {
+ if (replacements.empty())
+ return;
+
+ // Build a symbol user map once — this walks the module O(M) one time.
+ // Previously, each replaceAllSymbolUses call walked the entire module,
+ // giving O(R × M) quadratic behavior for R replacements.
+ mlir::SymbolTableCollection symbolTableCollection;
+ mlir::SymbolUserMap userMap(symbolTableCollection, theModule);
+
for (auto &i : replacements) {
StringRef mangledName = i.first;
mlir::Operation *replacement = i.second;
@@ -1687,17 +1692,15 @@ void CIRGenModule::applyReplacements() {
continue;
}
- assert(verifyPointerTypeArgs(theModule, oldF, newF) &&
+ assert(verifyPointerTypeArgs(oldF, newF, userMap) &&
"call argument types do not match replacement function");
- // Replace old with new, but keep the old order.
- if (oldF.replaceAllSymbolUses(newF.getSymNameAttr(), theModule).failed())
- llvm_unreachable("internal error, cannot RAUW symbol");
- if (newF) {
- newF->moveBefore(oldF);
- eraseGlobalSymbol(oldF);
- oldF->erase();
- }
+ // Replace old with new, but keep the old order. Uses
+ // SymbolUserMap to touch only actual users, not the whole module.
+ userMap.replaceAllUsesWith(oldF, newF.getSymNameAttr());
+ newF->moveBefore(oldF);
+ eraseGlobalSymbol(oldF);
+ oldF->erase();
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/195883
More information about the cfe-commits
mailing list