From 4c17f9b68426f1a01989bfc185ff50174d53f271 Mon Sep 17 00:00:00 2001 From: YongKang Zhu Date: Thu, 4 Dec 2025 07:33:42 -0800 Subject: [PATCH] [BOLT] Make safe ICF work with relative vtable (#170629) To support relative vtable, we divide each vtable entry address by 4 instead of 8 when tracking addresses covered by vtable using bitvector. This guarantees accuracy, saves the trouble of checking for each vtable whether it is relative, and is also necessary for libraries for which some contributing compilation units are compiled with relative vtable while others are not. --- .../bolt/Passes/IdenticalCodeFolding.h | 6 +++-- bolt/lib/Passes/IdenticalCodeFolding.cpp | 3 ++- bolt/test/safe-icf-relative-vtable.cpp | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 bolt/test/safe-icf-relative-vtable.cpp diff --git a/bolt/include/bolt/Passes/IdenticalCodeFolding.h b/bolt/include/bolt/Passes/IdenticalCodeFolding.h index f59e75c61860..1664c69de375 100644 --- a/bolt/include/bolt/Passes/IdenticalCodeFolding.h +++ b/bolt/include/bolt/Passes/IdenticalCodeFolding.h @@ -37,17 +37,19 @@ public: Error runOnFunctions(BinaryContext &BC) override; private: + static constexpr uint64_t VTableAddressGranularity = 4; + /// Bit vector of memory addresses of vtables. llvm::SparseBitVector<> VTableBitVector; /// Return true if the memory address is in a vtable. bool isAddressInVTable(uint64_t Address) const { - return VTableBitVector.test(Address / 8); + return VTableBitVector.test(Address / VTableAddressGranularity); } /// Mark memory address of a vtable as used. void setAddressUsedInVTable(uint64_t Address) { - VTableBitVector.set(Address / 8); + VTableBitVector.set(Address / VTableAddressGranularity); } /// Scan symbol table and mark memory addresses of diff --git a/bolt/lib/Passes/IdenticalCodeFolding.cpp b/bolt/lib/Passes/IdenticalCodeFolding.cpp index 9a8f79ea7de0..c5c33b7cde8d 100644 --- a/bolt/lib/Passes/IdenticalCodeFolding.cpp +++ b/bolt/lib/Passes/IdenticalCodeFolding.cpp @@ -380,7 +380,8 @@ void IdenticalCodeFolding::initVTableReferences(const BinaryContext &BC) { if (!Data->getName().starts_with("_ZTV") && // vtable !Data->getName().starts_with("_ZTCN")) // construction vtable continue; - for (uint64_t I = Address, End = I + Data->getSize(); I < End; I += 8) + for (uint64_t I = Address, End = I + Data->getSize(); I < End; + I += VTableAddressGranularity) setAddressUsedInVTable(I); } } diff --git a/bolt/test/safe-icf-relative-vtable.cpp b/bolt/test/safe-icf-relative-vtable.cpp new file mode 100644 index 000000000000..59ddcc656a5d --- /dev/null +++ b/bolt/test/safe-icf-relative-vtable.cpp @@ -0,0 +1,26 @@ +// Test safe ICF works with binaries that contain relative vtable. + +// REQUIRES: system-linux,asserts + +// RUN: %clang %cxxflags -o %t.so %s -Wl,-q -fno-rtti +// RUN: llvm-bolt %t.so -o %t.bolt --no-threads --icf=safe \ +// RUN: --debug-only=bolt-icf 2>&1 | FileCheck %s + +// RUN: %clang %cxxflags -o %t.so %s -Wl,-q -fno-rtti \ +// RUN: -fexperimental-relative-c++-abi-vtables +// RUN: llvm-bolt %t.so -o %t.bolt --no-threads --icf=safe \ +// RUN: --debug-only=bolt-icf 2>&1 | FileCheck %s + +// CHECK: folding {{.*bar.*}} into {{.*foo.*}} +// CHECK-NOT: skipping function with reference taken {{.*bar.*}} + +class TT { +public: + virtual int foo(int a) { return ++a; } + virtual int bar(int a) { return ++a; } +}; + +int main() { + TT T; + return T.foo(0) + T.bar(1); +}