1 | //===- bolt/Passes/ADRRelaxationPass.cpp ----------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements the ADRRelaxationPass class. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "bolt/Passes/ADRRelaxationPass.h" |
14 | #include "bolt/Core/ParallelUtilities.h" |
15 | #include "bolt/Utils/CommandLineOpts.h" |
16 | #include <iterator> |
17 | |
18 | using namespace llvm; |
19 | |
20 | namespace opts { |
21 | extern cl::OptionCategory BoltCategory; |
22 | |
23 | static cl::opt<bool> |
24 | AdrPassOpt("adr-relaxation" , |
25 | cl::desc("Replace ARM non-local ADR instructions with ADRP" ), |
26 | cl::init(Val: true), cl::cat(BoltCategory), cl::ReallyHidden); |
27 | } // namespace opts |
28 | |
29 | namespace llvm { |
30 | namespace bolt { |
31 | |
32 | // We don't exit directly from runOnFunction since it would call ThreadPool |
33 | // destructor which might result in internal assert if we're not finished |
34 | // creating async jobs on the moment of exit. So we're finishing all parallel |
35 | // jobs and checking the exit flag after it. |
36 | static bool PassFailed = false; |
37 | |
38 | void ADRRelaxationPass::runOnFunction(BinaryFunction &BF) { |
39 | if (PassFailed) |
40 | return; |
41 | |
42 | BinaryContext &BC = BF.getBinaryContext(); |
43 | for (BinaryBasicBlock &BB : BF) { |
44 | for (auto It = BB.begin(); It != BB.end(); ++It) { |
45 | MCInst &Inst = *It; |
46 | if (!BC.MIB->isADR(Inst)) |
47 | continue; |
48 | |
49 | const MCSymbol *Symbol = BC.MIB->getTargetSymbol(Inst); |
50 | if (!Symbol) |
51 | continue; |
52 | |
53 | if (BF.hasIslandsInfo()) { |
54 | BinaryFunction::IslandInfo &Islands = BF.getIslandInfo(); |
55 | if (Islands.Symbols.count(Ptr: Symbol) || Islands.ProxySymbols.count(Val: Symbol)) |
56 | continue; |
57 | } |
58 | |
59 | // Don't relax adr if it points to the same function and it is not split |
60 | // and BF initial size is < 1MB. |
61 | const unsigned OneMB = 0x100000; |
62 | if (!BF.isSplit() && BF.getSize() < OneMB) { |
63 | BinaryFunction *TargetBF = BC.getFunctionForSymbol(Symbol); |
64 | if (TargetBF && TargetBF == &BF) |
65 | continue; |
66 | } |
67 | |
68 | MCPhysReg Reg; |
69 | BC.MIB->getADRReg(Inst, RegName&: Reg); |
70 | int64_t Addend = BC.MIB->getTargetAddend(Inst); |
71 | InstructionListType Addr; |
72 | |
73 | { |
74 | auto L = BC.scopeLock(); |
75 | Addr = BC.MIB->materializeAddress(Target: Symbol, Ctx: BC.Ctx.get(), RegName: Reg, Addend); |
76 | } |
77 | |
78 | if (It != BB.begin() && BC.MIB->isNoop(Inst: *std::prev(x: It))) { |
79 | It = BB.eraseInstruction(II: std::prev(x: It)); |
80 | } else if (std::next(x: It) != BB.end() && BC.MIB->isNoop(Inst: *std::next(x: It))) { |
81 | BB.eraseInstruction(II: std::next(x: It)); |
82 | } else if (!opts::StrictMode && !BF.isSimple()) { |
83 | // If the function is not simple, it may contain a jump table undetected |
84 | // by us. This jump table may use an offset from the branch instruction |
85 | // to land in the desired place. If we add new instructions, we |
86 | // invalidate this offset, so we have to rely on linker-inserted NOP to |
87 | // replace it with ADRP, and abort if it is not present. |
88 | auto L = BC.scopeLock(); |
89 | BC.errs() << formatv( |
90 | Fmt: "BOLT-ERROR: Cannot relax adr in non-simple function " |
91 | "{0}. Use --strict option to override\n" , |
92 | Vals: BF.getOneName()); |
93 | PassFailed = true; |
94 | return; |
95 | } |
96 | It = BB.replaceInstruction(II: It, Replacement: Addr); |
97 | } |
98 | } |
99 | } |
100 | |
101 | Error ADRRelaxationPass::runOnFunctions(BinaryContext &BC) { |
102 | if (!opts::AdrPassOpt || !BC.HasRelocations) |
103 | return Error::success(); |
104 | |
105 | ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) { |
106 | runOnFunction(BF); |
107 | }; |
108 | |
109 | ParallelUtilities::runOnEachFunction( |
110 | BC, SchedPolicy: ParallelUtilities::SchedulingPolicy::SP_TRIVIAL, WorkFunction: WorkFun, SkipPredicate: nullptr, |
111 | LogName: "ADRRelaxationPass" ); |
112 | |
113 | if (PassFailed) |
114 | return createFatalBOLTError(S: "" ); |
115 | return Error::success(); |
116 | } |
117 | |
118 | } // end namespace bolt |
119 | } // end namespace llvm |
120 | |