Currently we have an optimization that if the ADR points to the same function we might skip it's relaxation. But it doesn't take into account that BF might be split, in such situation we still need to relax it. And just in case also relax if the initial BF size is >= 1MB. Fixes #71822
118 lines
3.9 KiB
C++
118 lines
3.9 KiB
C++
//===- bolt/Passes/ADRRelaxationPass.cpp ----------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the ADRRelaxationPass class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "bolt/Passes/ADRRelaxationPass.h"
|
|
#include "bolt/Core/ParallelUtilities.h"
|
|
#include "bolt/Utils/CommandLineOpts.h"
|
|
#include <iterator>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace opts {
|
|
extern cl::OptionCategory BoltCategory;
|
|
|
|
static cl::opt<bool>
|
|
AdrPassOpt("adr-relaxation",
|
|
cl::desc("Replace ARM non-local ADR instructions with ADRP"),
|
|
cl::init(true), cl::cat(BoltCategory), cl::ReallyHidden);
|
|
} // namespace opts
|
|
|
|
namespace llvm {
|
|
namespace bolt {
|
|
|
|
// We don't exit directly from runOnFunction since it would call ThreadPool
|
|
// destructor which might result in internal assert if we're not finished
|
|
// creating async jobs on the moment of exit. So we're finishing all parallel
|
|
// jobs and checking the exit flag after it.
|
|
static bool PassFailed = false;
|
|
|
|
void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) {
|
|
if (PassFailed)
|
|
return;
|
|
|
|
BinaryContext &BC = BF.getBinaryContext();
|
|
for (BinaryBasicBlock &BB : BF) {
|
|
for (auto It = BB.begin(); It != BB.end(); ++It) {
|
|
MCInst &Inst = *It;
|
|
if (!BC.MIB->isADR(Inst))
|
|
continue;
|
|
|
|
const MCSymbol *Symbol = BC.MIB->getTargetSymbol(Inst);
|
|
if (!Symbol)
|
|
continue;
|
|
|
|
if (BF.hasIslandsInfo()) {
|
|
BinaryFunction::IslandInfo &Islands = BF.getIslandInfo();
|
|
if (Islands.Symbols.count(Symbol) || Islands.ProxySymbols.count(Symbol))
|
|
continue;
|
|
}
|
|
|
|
// Don't relax adr if it points to the same function and it is not split
|
|
// and BF initial size is < 1MB.
|
|
const unsigned OneMB = 0x100000;
|
|
if (!BF.isSplit() && BF.getSize() < OneMB) {
|
|
BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol);
|
|
if (TargetBF && TargetBF == &BF)
|
|
continue;
|
|
}
|
|
|
|
MCPhysReg Reg;
|
|
BC.MIB->getADRReg(Inst, Reg);
|
|
int64_t Addend = BC.MIB->getTargetAddend(Inst);
|
|
InstructionListType Addr;
|
|
|
|
{
|
|
auto L = BC.scopeLock();
|
|
Addr = BC.MIB->materializeAddress(Symbol, BC.Ctx.get(), Reg, Addend);
|
|
}
|
|
|
|
if (It != BB.begin() && BC.MIB->isNoop(*std::prev(It))) {
|
|
It = BB.eraseInstruction(std::prev(It));
|
|
} else if (std::next(It) != BB.end() && BC.MIB->isNoop(*std::next(It))) {
|
|
BB.eraseInstruction(std::next(It));
|
|
} else if (!opts::StrictMode && !BF.isSimple()) {
|
|
// If the function is not simple, it may contain a jump table undetected
|
|
// by us. This jump table may use an offset from the branch instruction
|
|
// to land in the desired place. If we add new instructions, we
|
|
// invalidate this offset, so we have to rely on linker-inserted NOP to
|
|
// replace it with ADRP, and abort if it is not present.
|
|
auto L = BC.scopeLock();
|
|
errs() << formatv("BOLT-ERROR: Cannot relax adr in non-simple function "
|
|
"{0}. Use --strict option to override\n",
|
|
BF.getOneName());
|
|
PassFailed = true;
|
|
return;
|
|
}
|
|
It = BB.replaceInstruction(It, Addr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADRRelaxationPass::runOnFunctions(BinaryContext &BC) {
|
|
if (!opts::AdrPassOpt || !BC.HasRelocations)
|
|
return;
|
|
|
|
ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
|
|
runOnFunction(BF);
|
|
};
|
|
|
|
ParallelUtilities::runOnEachFunction(
|
|
BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, nullptr,
|
|
"ADRRelaxationPass");
|
|
|
|
if (PassFailed)
|
|
exit(1);
|
|
}
|
|
|
|
} // end namespace bolt
|
|
} // end namespace llvm
|