[BOLT][AArch64] Always cover veneers in lite mode (#171534)

If a veneer is not disassembled in lite mode, the veneer elimination
pass will not recognize it as such and the call to such veneer will
remain unchanged.

Later, we may need to insert a new veneer for such code ending up with a
double veneer.

To avoid such suboptimal code generation, always disassemble veneers and
guarantee that they are converted to direct calls in BOLT.
This commit is contained in:
Maksim Panchenko
2025-12-10 09:54:37 -08:00
committed by GitHub
parent 3ece6626cb
commit 28ff941a2c
5 changed files with 71 additions and 5 deletions

View File

@@ -2582,6 +2582,10 @@ public:
/// Return true if the function is an AArch64 linker inserted veneer
bool isAArch64Veneer() const;
/// Return true if the function signature matches veneer or it was established
/// to be a veneer.
bool isPossibleVeneer() const;
virtual ~BinaryFunction();
};

View File

@@ -4829,6 +4829,11 @@ bool BinaryFunction::isAArch64Veneer() const {
return true;
}
bool BinaryFunction::isPossibleVeneer() const {
return BC.isAArch64() &&
(isAArch64Veneer() || getOneName().starts_with("__AArch64"));
}
void BinaryFunction::addRelocation(uint64_t Address, MCSymbol *Symbol,
uint32_t RelType, uint64_t Addend,
uint64_t Value) {

View File

@@ -29,10 +29,6 @@ static llvm::cl::opt<bool>
namespace llvm {
namespace bolt {
static bool isPossibleVeneer(const BinaryFunction &BF) {
return BF.isAArch64Veneer() || BF.getOneName().starts_with("__AArch64");
}
Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
if (!opts::EliminateVeneers || !BC.isAArch64())
return Error::success();
@@ -40,7 +36,7 @@ Error VeneerElimination::runOnFunctions(BinaryContext &BC) {
std::unordered_map<const MCSymbol *, const MCSymbol *> VeneerDestinations;
uint64_t NumEliminatedVeneers = 0;
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
if (!isPossibleVeneer(BF))
if (!BF.isPossibleVeneer())
continue;
if (BF.isIgnored())

View File

@@ -3374,6 +3374,11 @@ void RewriteInstance::selectFunctionsToProcess() {
if (mustSkip(Function))
return false;
// Include veneer functions as we want to replace veneer calls with direct
// ones.
if (Function.isPossibleVeneer())
return true;
// If the list is not empty, only process functions from the list.
if (!opts::ForceFunctionNames.empty() || !ForceFunctionsNR.empty()) {
// Regex check (-funcs and -funcs-file options).

View File

@@ -0,0 +1,56 @@
## Check that llvm-bolt correctly handles veneers in lite mode.
# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o
# RUN: link_fdata %s %t.o %t.fdata
# RUN: llvm-strip --strip-unneeded %t.o
# RUN: %clang %cflags %t.o -o %t.exe -nostdlib -Wl,-q
# RUN: llvm-bolt %t.exe -o %t.bolt --lite=1 --data %t.fdata \
# RUN: --print-normalized 2>&1 | FileCheck %s --check-prefix=CHECK-VENEER
## Constant islands at the end of functions foo(), bar(), and _start() make each
## one of them ~112MB in size. Thus the total code size exceeds 300MB.
.text
.global foo
.type foo, %function
foo:
bl _start
bl bar
ret
.space 0x7000000
.size foo, .-foo
.global bar
.type bar, %function
bar:
bl foo
bl _start
ret
.space 0x7000000
.size bar, .-bar
.global hot
.type hot, %function
hot:
# FDATA: 0 [unknown] 0 1 hot 0 0 100
bl foo
bl bar
bl _start
ret
.size hot, .-hot
## Check that BOLT sees the call to foo, not to its veneer.
# CHECK-VENEER-LABEL: Binary Function "hot"
# CHECK-VENEER: bl
# CHECK-VENEER-SAME: {{[[:space:]]foo[[:space:]]}}
.global _start
.type _start, %function
_start:
bl foo
bl bar
bl hot
ret
.space 0x7000000
.size _start, .-_start