| //===- 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(); |
| BC.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); |
| } |
| } |
| } |
| |
| Error ADRRelaxationPass::runOnFunctions(BinaryContext &BC) { |
| if (!opts::AdrPassOpt || !BC.HasRelocations) |
| return Error::success(); |
| |
| ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) { |
| runOnFunction(BF); |
| }; |
| |
| ParallelUtilities::runOnEachFunction( |
| BC, ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFun, nullptr, |
| "ADRRelaxationPass"); |
| |
| if (PassFailed) |
| return createFatalBOLTError(""); |
| return Error::success(); |
| } |
| |
| } // end namespace bolt |
| } // end namespace llvm |