Skip to content

Conversation

vitalybuka
Copy link
Collaborator

@vitalybuka vitalybuka commented Sep 8, 2025

buildBitSet had a loop trough entire GlobalLayout to pickup matching offsets.

The patch maps all offsets to correspondign
TypeId, so we pass prepared list of offsets into
buildBitSet.

On one large internal binary, LowerTypeTests
took 58% of ThinLTO link time before the patch.
After the patch just 7% (absolute saving is 200s).

Created using spr 1.3.6
@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Vitaly Buka (vitalybuka)

Changes

buildBitSet had a loop trough entire GlobalLayout to pickup matching offsets.

The patch maps all offsets to correspondign
TypeId, so we pass prepare list of offsets into
buildBitSet.

On one large internal binary, LowerTypeTests
took 58% of ThinLTO link time before the patch.
After the patch just 7% (absolute saving is 200s).


Full diff: https://github.com/llvm/llvm-project/pull/157386.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Transforms/IPO/LowerTypeTests.h (+6-9)
  • (modified) llvm/lib/Transforms/IPO/LowerTypeTests.cpp (+36-27)
  • (modified) llvm/unittests/Transforms/IPO/LowerTypeTests.cpp (+1-4)
diff --git a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
index 253d13f81857b..a34cbaf72675b 100644
--- a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
+++ b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
@@ -64,15 +64,12 @@ struct BitSetBuilder {
   uint64_t Min = std::numeric_limits<uint64_t>::max();
   uint64_t Max = 0;
 
-  BitSetBuilder() = default;
-
-  void addOffset(uint64_t Offset) {
-    if (Min > Offset)
-      Min = Offset;
-    if (Max < Offset)
-      Max = Offset;
-
-    Offsets.push_back(Offset);
+  explicit BitSetBuilder(ArrayRef<uint64_t> Offsets) : Offsets(Offsets) {
+    if (!Offsets.empty()) {
+      auto [MinIt, MaxIt] = std::minmax_element(Offsets.begin(), Offsets.end());
+      Min = *MinIt;
+      Max = *MaxIt;
+    }
   }
 
   LLVM_ABI BitSetInfo build();
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 821a9d82ddb0d..be6cba38d8b3c 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -573,28 +573,11 @@ class LowerTypeTestsModule {
 };
 } // end anonymous namespace
 
-/// Build a bit set for TypeId using the object layouts in
-/// GlobalLayout.
-static BitSetInfo
-buildBitSet(Metadata *TypeId,
-            const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
-  BitSetBuilder BSB;
-
+/// Build a bit set for list of offsets.
+static BitSetInfo buildBitSet(ArrayRef<uint64_t> Offsets) {
   // Compute the byte offset of each address associated with this type
   // identifier.
-  for (const auto &GlobalAndOffset : GlobalLayout) {
-    for (MDNode *Type : GlobalAndOffset.first->types()) {
-      if (Type->getOperand(1) != TypeId)
-        continue;
-      uint64_t Offset =
-          cast<ConstantInt>(
-              cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
-              ->getZExtValue();
-      BSB.addOffset(GlobalAndOffset.second + Offset);
-    }
-  }
-
-  return BSB.build();
+  return BitSetBuilder(Offsets).build();
 }
 
 /// Build a test that bit BitOffset mod sizeof(Bits)*8 is set in
@@ -1161,21 +1144,47 @@ void LowerTypeTestsModule::importFunction(Function *F,
   F->setVisibility(Visibility);
 }
 
-void LowerTypeTestsModule::lowerTypeTestCalls(
-    ArrayRef<Metadata *> TypeIds, Constant *CombinedGlobalAddr,
-    const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
-  // For each type identifier in this disjoint set...
+static auto
+buildBitSets(ArrayRef<Metadata *> TypeIds,
+             const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
+  DenseMap<Metadata *, SmallVector<uint64_t, 16>> OffsetsByTypeID;
+  // Pre-populate the map with interesting type identifiers.
+  for (Metadata *TypeId : TypeIds)
+    OffsetsByTypeID[TypeId];
+  for (const auto &[Mem, MemOff] : GlobalLayout) {
+    for (MDNode *Type : Mem->types()) {
+      auto It = OffsetsByTypeID.find(Type->getOperand(1));
+      if (It == OffsetsByTypeID.end())
+        continue;
+      uint64_t Offset =
+          cast<ConstantInt>(
+              cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
+              ->getZExtValue();
+      It->second.push_back(MemOff + Offset);
+    }
+  }
+
+  SmallVector<std::pair<Metadata *, BitSetInfo>> BitSets;
+  BitSets.reserve(TypeIds.size());
   for (Metadata *TypeId : TypeIds) {
-    // Build the bitset.
-    BitSetInfo BSI = buildBitSet(TypeId, GlobalLayout);
+    BitSets.emplace_back(TypeId, buildBitSet(OffsetsByTypeID[TypeId]));
     LLVM_DEBUG({
       if (auto MDS = dyn_cast<MDString>(TypeId))
         dbgs() << MDS->getString() << ": ";
       else
         dbgs() << "<unnamed>: ";
-      BSI.print(dbgs());
+      BitSets.back().second.print(dbgs());
     });
+  }
+
+  return BitSets;
+}
 
+void LowerTypeTestsModule::lowerTypeTestCalls(
+    ArrayRef<Metadata *> TypeIds, Constant *CombinedGlobalAddr,
+    const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
+  // For each type identifier in this disjoint set...
+  for (const auto &[TypeId, BSI] : buildBitSets(TypeIds, GlobalLayout)) {
     ByteArrayInfo *BAI = nullptr;
     TypeIdLowering TIL;
 
diff --git a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
index fc41b036ffb6c..3d6b7efbccd91 100644
--- a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
@@ -51,10 +51,7 @@ TEST(LowerTypeTests, BitSetBuilder) {
   };
 
   for (auto &&T : BSBTests) {
-    BitSetBuilder BSB;
-    for (auto Offset : T.Offsets)
-      BSB.addOffset(Offset);
-
+    BitSetBuilder BSB(T.Offsets);
     BitSetInfo BSI = BSB.build();
 
     EXPECT_EQ(T.Bits, BSI.Bits);

@vitalybuka vitalybuka requested a review from pcc September 8, 2025 05:32
buildBitSets(ArrayRef<Metadata *> TypeIds,
const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
DenseMap<Metadata *, SmallVector<uint64_t, 16>> OffsetsByTypeID;
// Pre-populate the map with interesting type identifiers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary, all types mentioned in GlobalLayout should also be in TypeIds.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not.
In most cases unique counts are 1 vs 2
But there are cases like 11250 vs 886681

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, TypeIds only contains types referenced by llvm.type.test.

@vitalybuka vitalybuka requested a review from pcc September 11, 2025 01:05
buildBitSets(ArrayRef<Metadata *> TypeIds,
const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout) {
DenseMap<Metadata *, SmallVector<uint64_t, 16>> OffsetsByTypeID;
// Pre-populate the map with interesting type identifiers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, TypeIds only contains types referenced by llvm.type.test.

@vitalybuka vitalybuka merged commit 79012fc into main Sep 11, 2025
11 checks passed
@vitalybuka vitalybuka deleted the users/vitalybuka/spr/lowertypetests-optimize-buildbitset branch September 11, 2025 01:17
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 11, 2025

LLVM Buildbot has detected a new failure on builder lldb-x86_64-debian running on lldb-x86_64-debian while building llvm at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/30872

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: lang/cpp/static_member_type_depending_on_parent_size/TestStaticMemberTypeDependingOnParentSize.py (861 of 3188)
PASS: lldb-shell :: SymbolFile/NativePDB/incomplete-tag-type.test (862 of 3188)
PASS: lldb-api :: functionalities/multiple-slides/TestMultipleSlides.py (863 of 3188)
PASS: lldb-shell :: SymbolFile/DWARF/x86/find-basic-type.cpp (864 of 3188)
PASS: lldb-shell :: SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp (865 of 3188)
PASS: lldb-api :: commands/target/anon-struct/TestTargetVarAnonStruct.py (866 of 3188)
PASS: lldb-unit :: Core/./LLDBCoreTests/65/118 (867 of 3188)
PASS: lldb-api :: functionalities/gdb_remote_client/TestGDBRemotePlatformFile.py (868 of 3188)
PASS: lldb-api :: lang/c/find_struct_type/TestFindStructTypes.py (869 of 3188)
PASS: lldb-api :: lang/cpp/pointer_to_member_type_depending_on_parent_size/TestPointerToMemberTypeDependingOnParentSize.py (870 of 3188)
FAIL: lldb-api :: functionalities/postmortem/netbsd-core/TestNetBSDCore.py (871 of 3188)
******************** TEST 'lldb-api :: functionalities/postmortem/netbsd-core/TestNetBSDCore.py' FAILED ********************
Script:
--
/usr/bin/python3 /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./lib --env LLVM_INCLUDE_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/include --env LLVM_TOOLS_DIR=/home/worker/2.0.1/lldb-x86_64-debian/build/./bin --arch x86_64 --build-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex --lldb-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/worker/2.0.1/lldb-x86_64-debian/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/lldb --compiler /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/clang --dsymutil /home/worker/2.0.1/lldb-x86_64-debian/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./bin --lldb-obj-root /home/worker/2.0.1/lldb-x86_64-debian/build/tools/lldb --lldb-libs-dir /home/worker/2.0.1/lldb-x86_64-debian/build/./lib --cmake-build-type Release -t /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/functionalities/postmortem/netbsd-core -p TestNetBSDCore.py
--
Exit Code: -6

Command Output (stdout):
--
lldb version 22.0.0git (https://github.com/llvm/llvm-project.git revision 79012fcfd333040e517f82e235d114c3bc960a99)
  clang revision 79012fcfd333040e517f82e235d114c3bc960a99
  llvm revision 79012fcfd333040e517f82e235d114c3bc960a99
Skipping the following test categories: ['libc++', 'msvcstl', 'dsym', 'gmodules', 'debugserver', 'objc']

--
Command Output (stderr):
--
Change dir to: /home/worker/2.0.1/lldb-x86_64-debian/llvm-project/lldb/test/API/functionalities/postmortem/netbsd-core
runCmd: settings clear --all

output: 

runCmd: settings set symbols.enable-external-lookup false

output: 

runCmd: settings set target.inherit-tcc true

output: 

runCmd: settings set target.disable-aslr false

output: 

runCmd: settings set target.detach-on-error false

output: 


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants