[CIR] Eliminate SymbolTable::lookupSymbolIn hotspots (#193362)
mlir::SymbolTable::lookupSymbolIn is O(n) per lookup, so cumulative
symbol lookups during CIRGen are O(n^2) in the number of global symbols.
On template-heavy translation units this becomes a significant
compile-time hotspot.
Replace the SymbolTable lookup path with a per-CIRGenModule DenseMap
cache keyed by symbol name, giving O(1) lookups.
On a synthetic template-heavy stress test, end-to-end compile time on
`clang -fclangir -S -emit-llvm -O0` improves by ~11% on a 33K-LOC input
(5.86s -> 5.21s) and ~16% on a 67K-LOC input (16.09s -> 13.52s). The
super-linear growth of the win with input size confirms the O(n^2) ->
O(n) effect.
Similar to previous compile time fix, repro shape (scale records and
template instantiations into the hundreds/thousands to amplify):
template <typename T, int N> struct Box { T head; Box<T, N-1> tail; };
template <typename T> struct Box<T, 0> { T head; };
struct R0; struct R1;
struct R0 { int v; Box<int, 4> b; R1* next; };
struct R1 { long v; Box<long, 4> b; R0* next; };
int f0(R0*); int f1(R1*);
int f0(R0* r) { return r->v + (r->next ? f1(r->next) : 0); }
int f1(R1* r) { return (int)r->v + (r->next ? f0(r->next) : 0); }
This commit is contained in:
committed by
GitHub
parent
10fe2e3d68
commit
20a62a41c2
@@ -450,6 +450,7 @@ CIRGenModule::getOrCreateStaticVarDecl(const VarDecl &d,
|
||||
|
||||
cir::GlobalOp gv = builder.createVersionedGlobal(
|
||||
getModule(), getLoc(d.getLocation()), name, lty, false, linkage);
|
||||
insertGlobalSymbol(gv);
|
||||
// TODO(cir): infer visibility from linkage in global op builder.
|
||||
gv.setVisibility(getMLIRVisibilityFromCIRLinkage(linkage));
|
||||
gv.setInitialValueAttr(init);
|
||||
@@ -545,6 +546,7 @@ Address CIRGenModule::createUnnamedGlobalFrom(const VarDecl &d,
|
||||
cir::GlobalOp gv = builder.createVersionedGlobal(
|
||||
getModule(), getLoc(d.getLocation()), name, ty, isConstant,
|
||||
cir::GlobalLinkageKind::PrivateLinkage);
|
||||
insertGlobalSymbol(gv);
|
||||
// TODO(cir): infer visibility from linkage in global op builder.
|
||||
gv.setVisibility(getMLIRVisibilityFromCIRLinkage(
|
||||
cir::GlobalLinkageKind::PrivateLinkage));
|
||||
|
||||
@@ -2077,6 +2077,7 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) {
|
||||
|
||||
clone = cir::FuncOp::create(builder, calleeFunc.getLoc(), fdInlineName,
|
||||
calleeFunc.getFunctionType());
|
||||
cgm.insertGlobalSymbol(clone);
|
||||
clone.setLinkageAttr(cir::GlobalLinkageKindAttr::get(
|
||||
&cgm.getMLIRContext(), cir::GlobalLinkageKind::InternalLinkage));
|
||||
clone.setSymVisibility("private");
|
||||
|
||||
@@ -683,6 +683,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
|
||||
builder.setInsertionPoint(fn);
|
||||
clone = cir::FuncOp::create(builder, fn.getLoc(), fdInlineName,
|
||||
fn.getFunctionType());
|
||||
cgm.insertGlobalSymbol(clone);
|
||||
clone.setLinkage(cir::GlobalLinkageKind::InternalLinkage);
|
||||
clone.setSymVisibility("private");
|
||||
clone.setInlineKind(cir::InlineKind::AlwaysInline);
|
||||
@@ -707,6 +708,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
|
||||
.replaceAllSymbolUses(fn.getSymNameAttr(), cgm.getModule())
|
||||
.failed())
|
||||
llvm_unreachable("Failed to replace inline builtin symbol uses");
|
||||
cgm.eraseGlobalSymbol(inlineFn);
|
||||
inlineFn.erase();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1198,8 +1198,7 @@ CIRGenItaniumRTTIBuilder::getAddrOfExternalRTTIDescriptor(mlir::Location loc,
|
||||
CIRGenBuilderTy &builder = cgm.getBuilder();
|
||||
|
||||
// Look for an existing global.
|
||||
cir::GlobalOp gv = dyn_cast_or_null<cir::GlobalOp>(
|
||||
mlir::SymbolTable::lookupSymbolIn(cgm.getModule(), name));
|
||||
cir::GlobalOp gv = dyn_cast_or_null<cir::GlobalOp>(cgm.getGlobalValue(name));
|
||||
|
||||
if (!gv) {
|
||||
// Create a new global variable.
|
||||
@@ -1428,8 +1427,7 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(mlir::Location loc,
|
||||
llvm::raw_svector_ostream out(name);
|
||||
cgm.getCXXABI().getMangleContext().mangleCXXRTTI(ty, out);
|
||||
|
||||
auto oldGV = dyn_cast_or_null<cir::GlobalOp>(
|
||||
mlir::SymbolTable::lookupSymbolIn(cgm.getModule(), name));
|
||||
auto oldGV = dyn_cast_or_null<cir::GlobalOp>(cgm.getGlobalValue(name));
|
||||
|
||||
if (oldGV && !oldGV.isDeclaration()) {
|
||||
assert(!oldGV.hasAvailableExternallyLinkage() &&
|
||||
@@ -1604,8 +1602,7 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
|
||||
cgm.getCXXABI().getMangleContext().mangleCXXRTTI(ty, out);
|
||||
|
||||
// Create new global and search for an existing global.
|
||||
auto oldGV = dyn_cast_or_null<cir::GlobalOp>(
|
||||
mlir::SymbolTable::lookupSymbolIn(cgm.getModule(), name));
|
||||
auto oldGV = dyn_cast_or_null<cir::GlobalOp>(cgm.getGlobalValue(name));
|
||||
|
||||
cir::GlobalOp gv =
|
||||
CIRGenModule::createGlobalOp(cgm, loc, name, init.getType(),
|
||||
@@ -1627,6 +1624,7 @@ mlir::Attribute CIRGenItaniumRTTIBuilder::buildTypeInfo(
|
||||
cgm.errorNYI("buildTypeInfo: old GV !use_empty");
|
||||
return {};
|
||||
}
|
||||
cgm.eraseGlobalSymbol(oldGV);
|
||||
oldGV->erase();
|
||||
}
|
||||
|
||||
|
||||
@@ -380,8 +380,7 @@ void CIRGenModule::emitGlobalDecl(const clang::GlobalDecl &d) {
|
||||
// TODO: Not sure what to map this to for MLIR
|
||||
mlir::Operation *globalValueOp = op;
|
||||
if (auto gv = dyn_cast<cir::GetGlobalOp>(op)) {
|
||||
globalValueOp =
|
||||
mlir::SymbolTable::lookupSymbolIn(getModule(), gv.getNameAttr());
|
||||
globalValueOp = getGlobalValue(gv.getName());
|
||||
assert(globalValueOp && "expected a valid global op");
|
||||
}
|
||||
|
||||
@@ -663,7 +662,8 @@ void CIRGenModule::handleCXXStaticMemberVarInstantiation(VarDecl *vd) {
|
||||
}
|
||||
|
||||
mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) {
|
||||
return mlir::SymbolTable::lookupSymbolIn(theModule, name);
|
||||
auto it = symbolLookupCache.find(name);
|
||||
return it != symbolLookupCache.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
cir::GlobalOp
|
||||
@@ -699,6 +699,7 @@ CIRGenModule::createGlobalOp(CIRGenModule &cgm, mlir::Location loc,
|
||||
mlir::SymbolTable::setSymbolVisibility(
|
||||
g, mlir::SymbolTable::Visibility::Private);
|
||||
}
|
||||
cgm.symbolLookupCache[g.getSymNameAttr()] = g;
|
||||
return g;
|
||||
}
|
||||
|
||||
@@ -944,6 +945,7 @@ void CIRGenModule::replaceGlobal(cir::GlobalOp oldGV, cir::GlobalOp newGV) {
|
||||
// erased) operation, which would leave them detached from the module.
|
||||
if (lastGlobalOp == oldGV)
|
||||
lastGlobalOp = newGV;
|
||||
eraseGlobalSymbol(oldGV);
|
||||
oldGV.erase();
|
||||
}
|
||||
|
||||
@@ -1598,6 +1600,7 @@ void CIRGenModule::applyReplacements() {
|
||||
llvm_unreachable("internal error, cannot RAUW symbol");
|
||||
if (newF) {
|
||||
newF->moveBefore(oldF);
|
||||
eraseGlobalSymbol(oldF);
|
||||
oldF->erase();
|
||||
}
|
||||
}
|
||||
@@ -1606,8 +1609,7 @@ void CIRGenModule::applyReplacements() {
|
||||
cir::GlobalOp CIRGenModule::createOrReplaceCXXRuntimeVariable(
|
||||
mlir::Location loc, StringRef name, mlir::Type ty,
|
||||
cir::GlobalLinkageKind linkage, clang::CharUnits alignment) {
|
||||
auto gv = mlir::dyn_cast_or_null<cir::GlobalOp>(
|
||||
mlir::SymbolTable::lookupSymbolIn(theModule, name));
|
||||
auto gv = mlir::dyn_cast_or_null<cir::GlobalOp>(getGlobalValue(name));
|
||||
|
||||
if (gv) {
|
||||
// Check if the variable has the right type.
|
||||
@@ -1928,7 +1930,7 @@ std::string CIRGenModule::getUniqueGlobalName(const std::string &baseName) {
|
||||
std::string result =
|
||||
baseName + "." + std::to_string(cgGlobalNames[baseName]++);
|
||||
// There should not be any symbol with this name in the module.
|
||||
assert(!mlir::SymbolTable::lookupSymbolIn(theModule, result));
|
||||
assert(!getGlobalValue(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2954,6 +2956,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
|
||||
replaceUsesOfNonProtoTypeWithRealFunction(entry, funcOp);
|
||||
|
||||
// Obliterate no-proto declaration.
|
||||
eraseGlobalSymbol(entry);
|
||||
entry->erase();
|
||||
}
|
||||
|
||||
@@ -3036,6 +3039,8 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
|
||||
|
||||
func = cir::FuncOp::create(builder, loc, name, funcType);
|
||||
|
||||
symbolLookupCache[func.getSymNameAttr()] = func;
|
||||
|
||||
assert(!cir::MissingFeatures::opFuncAstDeclAttr());
|
||||
|
||||
if (funcDecl && !funcDecl->hasPrototype())
|
||||
@@ -3312,6 +3317,7 @@ void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
|
||||
// function declaration.
|
||||
assert(cast<cir::FuncOp>(op).getFunctionType() == alias.getFunctionType() &&
|
||||
"declaration exists with different type");
|
||||
eraseGlobalSymbol(op);
|
||||
op->erase();
|
||||
} else {
|
||||
// Name already set by createCIRFunction
|
||||
@@ -3556,6 +3562,7 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *mte,
|
||||
mlir::Operation *&entry = materializedGlobalTemporaryMap[mte];
|
||||
if (entry) {
|
||||
entry->replaceAllUsesWith(cv);
|
||||
eraseGlobalSymbol(entry);
|
||||
entry->erase();
|
||||
}
|
||||
entry = cv;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
|
||||
@@ -226,8 +227,31 @@ public:
|
||||
llvm::DenseMap<const Decl *, cir::GlobalOp> staticLocalDeclMap;
|
||||
llvm::DenseMap<const VarDecl *, cir::GlobalOp> initializerConstants;
|
||||
|
||||
/// Cache for O(1) symbol lookups by name, replacing the O(N) linear scan
|
||||
/// in SymbolTable::lookupSymbolIn that getGlobalValue used previously.
|
||||
llvm::StringMap<mlir::Operation *> symbolLookupCache;
|
||||
|
||||
mlir::Operation *getGlobalValue(llvm::StringRef ref);
|
||||
|
||||
/// O(1) lookup of a FuncOp by name in the symbol cache.
|
||||
/// Returns nullptr if the name is not found or is not a FuncOp.
|
||||
cir::FuncOp lookupFuncOp(llvm::StringRef name) {
|
||||
auto *op = getGlobalValue(name);
|
||||
return op ? mlir::dyn_cast<cir::FuncOp>(op) : cir::FuncOp{};
|
||||
}
|
||||
|
||||
void insertGlobalSymbol(mlir::Operation *op) {
|
||||
if (auto sym = mlir::dyn_cast<mlir::SymbolOpInterface>(op))
|
||||
symbolLookupCache[sym.getName()] = op;
|
||||
}
|
||||
void eraseGlobalSymbol(mlir::Operation *op) {
|
||||
if (auto sym = mlir::dyn_cast<mlir::SymbolOpInterface>(op)) {
|
||||
auto it = symbolLookupCache.find(sym.getName());
|
||||
if (it != symbolLookupCache.end() && it->second == op)
|
||||
symbolLookupCache.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
cir::GlobalOp getStaticLocalDeclAddress(const VarDecl *d) {
|
||||
return staticLocalDeclMap[d];
|
||||
}
|
||||
|
||||
@@ -900,10 +900,12 @@ cir::FuncOp CIRGenVTables::maybeEmitThunk(GlobalDecl gd,
|
||||
assert(oldThunkFn.isDeclaration() && "Shouldn't replace non-declaration");
|
||||
|
||||
// Remove the name from the old thunk function and get a new thunk.
|
||||
cgm.eraseGlobalSymbol(oldThunkFn);
|
||||
oldThunkFn.setName(StringRef());
|
||||
thunkFn =
|
||||
cir::FuncOp::create(cgm.getBuilder(), thunk->getLoc(), name.str(),
|
||||
thunkFnTy, cir::GlobalLinkageKind::ExternalLinkage);
|
||||
cgm.insertGlobalSymbol(thunkFn);
|
||||
cgm.setCIRFunctionAttributes(md, fnInfo, thunkFn, /*isThunk=*/false);
|
||||
|
||||
if (!oldThunkFn->use_empty())
|
||||
|
||||
Reference in New Issue
Block a user