Skip to content

LowerTypeTests: Fix quadratic complexity (try 2). #136053

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 107 additions & 87 deletions llvm/lib/Transforms/IPO/LowerTypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,30 @@ struct ScopedSaveAliaseesAndUsed {
std::vector<std::pair<GlobalAlias *, Function *>> FunctionAliases;
std::vector<std::pair<GlobalIFunc *, Function *>> ResolverIFuncs;

// This function only removes functions from llvm.used and llvm.compiler.used.
// We cannot remove global variables because they need to follow RAUW, as
// they may be deleted by buildBitSetsFromGlobalVariables.
void collectAndEraseUsedFunctions(Module &M,
SmallVectorImpl<GlobalValue *> &Vec,
bool CompilerUsed) {
auto *GV = collectUsedGlobalVariables(M, Vec, CompilerUsed);
if (!GV)
return;
// There's no API to only remove certain array elements from
// llvm.used/llvm.compiler.used, so we remove all of them and add back only
// the non-functions.
GV->eraseFromParent();
auto NonFuncBegin =
std::stable_partition(Vec.begin(), Vec.end(), [](GlobalValue *GV) {
return isa<Function>(GV);
});
if (CompilerUsed)
appendToCompilerUsed(M, {NonFuncBegin, Vec.end()});
else
appendToUsed(M, {NonFuncBegin, Vec.end()});
Vec.resize(NonFuncBegin - Vec.begin());
}

ScopedSaveAliaseesAndUsed(Module &M) : M(M) {
// The users of this class want to replace all function references except
// for aliases and llvm.used/llvm.compiler.used with references to a jump
Expand All @@ -365,10 +389,8 @@ struct ScopedSaveAliaseesAndUsed {
// llvm.used/llvm.compiler.used and aliases, erase the used lists, let RAUW
// replace the aliasees and then set them back to their original values at
// the end.
if (GlobalVariable *GV = collectUsedGlobalVariables(M, Used, false))
GV->eraseFromParent();
if (GlobalVariable *GV = collectUsedGlobalVariables(M, CompilerUsed, true))
GV->eraseFromParent();
collectAndEraseUsedFunctions(M, Used, false);
collectAndEraseUsedFunctions(M, CompilerUsed, true);

for (auto &GA : M.aliases()) {
// FIXME: This should look past all aliases not just interposable ones,
Expand Down Expand Up @@ -1669,61 +1691,55 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative(

lowerTypeTestCalls(TypeIds, JumpTable, GlobalLayout);

{
ScopedSaveAliaseesAndUsed S(M);
// Build aliases pointing to offsets into the jump table, and replace
// references to the original functions with references to the aliases.
for (unsigned I = 0; I != Functions.size(); ++I) {
Function *F = cast<Function>(Functions[I]->getGlobal());
bool IsJumpTableCanonical = Functions[I]->isJumpTableCanonical();

// Build aliases pointing to offsets into the jump table, and replace
// references to the original functions with references to the aliases.
for (unsigned I = 0; I != Functions.size(); ++I) {
Function *F = cast<Function>(Functions[I]->getGlobal());
bool IsJumpTableCanonical = Functions[I]->isJumpTableCanonical();

Constant *CombinedGlobalElemPtr = ConstantExpr::getInBoundsGetElementPtr(
JumpTableType, JumpTable,
ArrayRef<Constant *>{ConstantInt::get(IntPtrTy, 0),
ConstantInt::get(IntPtrTy, I)});

const bool IsExported = Functions[I]->isExported();
if (!IsJumpTableCanonical) {
GlobalValue::LinkageTypes LT = IsExported
? GlobalValue::ExternalLinkage
: GlobalValue::InternalLinkage;
GlobalAlias *JtAlias = GlobalAlias::create(F->getValueType(), 0, LT,
F->getName() + ".cfi_jt",
CombinedGlobalElemPtr, &M);
if (IsExported)
JtAlias->setVisibility(GlobalValue::HiddenVisibility);
else
appendToUsed(M, {JtAlias});
}
Constant *CombinedGlobalElemPtr = ConstantExpr::getInBoundsGetElementPtr(
JumpTableType, JumpTable,
ArrayRef<Constant *>{ConstantInt::get(IntPtrTy, 0),
ConstantInt::get(IntPtrTy, I)});

const bool IsExported = Functions[I]->isExported();
if (!IsJumpTableCanonical) {
GlobalValue::LinkageTypes LT = IsExported ? GlobalValue::ExternalLinkage
: GlobalValue::InternalLinkage;
GlobalAlias *JtAlias = GlobalAlias::create(F->getValueType(), 0, LT,
F->getName() + ".cfi_jt",
CombinedGlobalElemPtr, &M);
if (IsExported)
JtAlias->setVisibility(GlobalValue::HiddenVisibility);
else
appendToUsed(M, {JtAlias});
}

if (IsExported) {
if (IsJumpTableCanonical)
ExportSummary->cfiFunctionDefs().emplace(F->getName());
else
ExportSummary->cfiFunctionDecls().emplace(F->getName());
}
if (IsExported) {
if (IsJumpTableCanonical)
ExportSummary->cfiFunctionDefs().emplace(F->getName());
else
ExportSummary->cfiFunctionDecls().emplace(F->getName());
}

if (!IsJumpTableCanonical) {
if (F->hasExternalWeakLinkage())
replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr,
IsJumpTableCanonical);
else
replaceCfiUses(F, CombinedGlobalElemPtr, IsJumpTableCanonical);
} else {
assert(F->getType()->getAddressSpace() == 0);

GlobalAlias *FAlias =
GlobalAlias::create(F->getValueType(), 0, F->getLinkage(), "",
CombinedGlobalElemPtr, &M);
FAlias->setVisibility(F->getVisibility());
FAlias->takeName(F);
if (FAlias->hasName())
F->setName(FAlias->getName() + ".cfi");
replaceCfiUses(F, FAlias, IsJumpTableCanonical);
if (!F->hasLocalLinkage())
F->setVisibility(GlobalVariable::HiddenVisibility);
}
if (!IsJumpTableCanonical) {
if (F->hasExternalWeakLinkage())
replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr,
IsJumpTableCanonical);
else
replaceCfiUses(F, CombinedGlobalElemPtr, IsJumpTableCanonical);
} else {
assert(F->getType()->getAddressSpace() == 0);

GlobalAlias *FAlias = GlobalAlias::create(
F->getValueType(), 0, F->getLinkage(), "", CombinedGlobalElemPtr, &M);
FAlias->setVisibility(F->getVisibility());
FAlias->takeName(F);
if (FAlias->hasName())
F->setName(FAlias->getName() + ".cfi");
replaceCfiUses(F, FAlias, IsJumpTableCanonical);
if (!F->hasLocalLinkage())
F->setVisibility(GlobalVariable::HiddenVisibility);
}
}

Expand Down Expand Up @@ -2339,39 +2355,43 @@ bool LowerTypeTestsModule::lower() {
if (GlobalClasses.empty())
return false;

// For each disjoint set we found...
for (const auto &C : GlobalClasses) {
if (!C->isLeader())
continue;

++NumTypeIdDisjointSets;
// Build the list of type identifiers in this disjoint set.
std::vector<Metadata *> TypeIds;
std::vector<GlobalTypeMember *> Globals;
std::vector<ICallBranchFunnel *> ICallBranchFunnels;
for (auto M : GlobalClasses.members(*C)) {
if (isa<Metadata *>(M))
TypeIds.push_back(cast<Metadata *>(M));
else if (isa<GlobalTypeMember *>(M))
Globals.push_back(cast<GlobalTypeMember *>(M));
else
ICallBranchFunnels.push_back(cast<ICallBranchFunnel *>(M));
}

// Order type identifiers by unique ID for determinism. This ordering is
// stable as there is a one-to-one mapping between metadata and unique IDs.
llvm::sort(TypeIds, [&](Metadata *M1, Metadata *M2) {
return TypeIdInfo[M1].UniqueId < TypeIdInfo[M2].UniqueId;
});
{
ScopedSaveAliaseesAndUsed S(M);
// For each disjoint set we found...
for (const auto &C : GlobalClasses) {
if (!C->isLeader())
continue;

// Same for the branch funnels.
llvm::sort(ICallBranchFunnels,
[&](ICallBranchFunnel *F1, ICallBranchFunnel *F2) {
return F1->UniqueId < F2->UniqueId;
});
++NumTypeIdDisjointSets;
// Build the list of type identifiers in this disjoint set.
std::vector<Metadata *> TypeIds;
std::vector<GlobalTypeMember *> Globals;
std::vector<ICallBranchFunnel *> ICallBranchFunnels;
for (auto M : GlobalClasses.members(*C)) {
if (isa<Metadata *>(M))
TypeIds.push_back(cast<Metadata *>(M));
else if (isa<GlobalTypeMember *>(M))
Globals.push_back(cast<GlobalTypeMember *>(M));
else
ICallBranchFunnels.push_back(cast<ICallBranchFunnel *>(M));
}

// Build bitsets for this disjoint set.
buildBitSetsFromDisjointSet(TypeIds, Globals, ICallBranchFunnels);
// Order type identifiers by unique ID for determinism. This ordering is
// stable as there is a one-to-one mapping between metadata and unique
// IDs.
llvm::sort(TypeIds, [&](Metadata *M1, Metadata *M2) {
return TypeIdInfo[M1].UniqueId < TypeIdInfo[M2].UniqueId;
});

// Same for the branch funnels.
llvm::sort(ICallBranchFunnels,
[&](ICallBranchFunnel *F1, ICallBranchFunnel *F2) {
return F1->UniqueId < F2->UniqueId;
});

// Build bitsets for this disjoint set.
buildBitSetsFromDisjointSet(TypeIds, Globals, ICallBranchFunnels);
}
}

allocateByteArrays();
Expand Down
Loading