[llvm] LowerTypeTests: Fix quadratic complexity (try 2). (PR #136053)
Peter Collingbourne via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 16 16:51:25 PDT 2025
https://github.com/pcc updated https://github.com/llvm/llvm-project/pull/136053
>From 3e2a5ba93b47dc543cf53403287bb5e0a310c9a9 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter at pcc.me.uk>
Date: Wed, 16 Apr 2025 16:12:09 -0700
Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
=?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.6-beta.1
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 191 +++++++++++----------
1 file changed, 104 insertions(+), 87 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 7cf7d74acfcfa..604ca7f8973b4 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -352,6 +352,27 @@ 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;
+ 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
@@ -365,10 +386,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,
@@ -1669,61 +1688,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);
}
}
@@ -2339,39 +2352,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();
>From 3d2cd67dde35dbfa61cd7dff58dc3c2b4ab69166 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <peter at pcc.me.uk>
Date: Wed, 16 Apr 2025 16:51:11 -0700
Subject: [PATCH 2/2] Add comment
Created using spr 1.3.6-beta.1
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 604ca7f8973b4..38da7329bbd58 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -361,6 +361,9 @@ struct ScopedSaveAliaseesAndUsed {
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) {
More information about the llvm-commits
mailing list