[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.
This commit is contained in:
YongKang Zhu 2025-12-04 07:33:42 -08:00 committed by GitHub
parent 09b0885569
commit 4c17f9b684
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 3 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);
}