xref: /freebsd/contrib/llvm-project/lld/MachO/Arch/ARM64.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
1fe6060f1SDimitry Andric //===- ARM64.cpp ----------------------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9fe6060f1SDimitry Andric #include "Arch/ARM64Common.h"
10fe6060f1SDimitry Andric #include "InputFiles.h"
11fe6060f1SDimitry Andric #include "Symbols.h"
12fe6060f1SDimitry Andric #include "SyntheticSections.h"
13fe6060f1SDimitry Andric #include "Target.h"
14fe6060f1SDimitry Andric 
15fe6060f1SDimitry Andric #include "lld/Common/ErrorHandler.h"
16*81ad6265SDimitry Andric #include "mach-o/compact_unwind_encoding.h"
17fe6060f1SDimitry Andric #include "llvm/ADT/SmallVector.h"
18fe6060f1SDimitry Andric #include "llvm/ADT/StringRef.h"
19fe6060f1SDimitry Andric #include "llvm/BinaryFormat/MachO.h"
20fe6060f1SDimitry Andric #include "llvm/Support/Endian.h"
21fe6060f1SDimitry Andric #include "llvm/Support/MathExtras.h"
22fe6060f1SDimitry Andric 
23fe6060f1SDimitry Andric using namespace llvm;
24fe6060f1SDimitry Andric using namespace llvm::MachO;
25fe6060f1SDimitry Andric using namespace llvm::support::endian;
26fe6060f1SDimitry Andric using namespace lld;
27fe6060f1SDimitry Andric using namespace lld::macho;
28fe6060f1SDimitry Andric 
29fe6060f1SDimitry Andric namespace {
30fe6060f1SDimitry Andric 
31fe6060f1SDimitry Andric struct ARM64 : ARM64Common {
32fe6060f1SDimitry Andric   ARM64();
33fe6060f1SDimitry Andric   void writeStub(uint8_t *buf, const Symbol &) const override;
34fe6060f1SDimitry Andric   void writeStubHelperHeader(uint8_t *buf) const override;
35*81ad6265SDimitry Andric   void writeStubHelperEntry(uint8_t *buf, const Symbol &,
36fe6060f1SDimitry Andric                             uint64_t entryAddr) const override;
37fe6060f1SDimitry Andric   const RelocAttrs &getRelocAttrs(uint8_t type) const override;
38fe6060f1SDimitry Andric   void populateThunk(InputSection *thunk, Symbol *funcSym) override;
39*81ad6265SDimitry Andric   void applyOptimizationHints(uint8_t *, const ConcatInputSection *,
40*81ad6265SDimitry Andric                               ArrayRef<uint64_t>) const override;
41fe6060f1SDimitry Andric };
42fe6060f1SDimitry Andric 
43fe6060f1SDimitry Andric } // namespace
44fe6060f1SDimitry Andric 
45fe6060f1SDimitry Andric // Random notes on reloc types:
46fe6060f1SDimitry Andric // ADDEND always pairs with BRANCH26, PAGE21, or PAGEOFF12
47fe6060f1SDimitry Andric // POINTER_TO_GOT: ld64 supports a 4-byte pc-relative form as well as an 8-byte
48fe6060f1SDimitry Andric // absolute version of this relocation. The semantics of the absolute relocation
49fe6060f1SDimitry Andric // are weird -- it results in the value of the GOT slot being written, instead
50fe6060f1SDimitry Andric // of the address. Let's not support it unless we find a real-world use case.
51fe6060f1SDimitry Andric 
52fe6060f1SDimitry Andric const RelocAttrs &ARM64::getRelocAttrs(uint8_t type) const {
53fe6060f1SDimitry Andric   static const std::array<RelocAttrs, 11> relocAttrsArray{{
54fe6060f1SDimitry Andric #define B(x) RelocAttrBits::x
55fe6060f1SDimitry Andric       {"UNSIGNED",
56fe6060f1SDimitry Andric        B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
57fe6060f1SDimitry Andric       {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
58fe6060f1SDimitry Andric       {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
59fe6060f1SDimitry Andric       {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},
60fe6060f1SDimitry Andric       {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},
61fe6060f1SDimitry Andric       {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},
62fe6060f1SDimitry Andric       {"GOT_LOAD_PAGEOFF12",
63fe6060f1SDimitry Andric        B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
64fe6060f1SDimitry Andric       {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
65fe6060f1SDimitry Andric       {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},
66fe6060f1SDimitry Andric       {"TLVP_LOAD_PAGEOFF12",
67fe6060f1SDimitry Andric        B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
68fe6060f1SDimitry Andric       {"ADDEND", B(ADDEND)},
69fe6060f1SDimitry Andric #undef B
70fe6060f1SDimitry Andric   }};
71fe6060f1SDimitry Andric   assert(type < relocAttrsArray.size() && "invalid relocation type");
72fe6060f1SDimitry Andric   if (type >= relocAttrsArray.size())
73fe6060f1SDimitry Andric     return invalidRelocAttrs;
74fe6060f1SDimitry Andric   return relocAttrsArray[type];
75fe6060f1SDimitry Andric }
76fe6060f1SDimitry Andric 
77fe6060f1SDimitry Andric static constexpr uint32_t stubCode[] = {
78fe6060f1SDimitry Andric     0x90000010, // 00: adrp  x16, __la_symbol_ptr@page
79fe6060f1SDimitry Andric     0xf9400210, // 04: ldr   x16, [x16, __la_symbol_ptr@pageoff]
80fe6060f1SDimitry Andric     0xd61f0200, // 08: br    x16
81fe6060f1SDimitry Andric };
82fe6060f1SDimitry Andric 
83fe6060f1SDimitry Andric void ARM64::writeStub(uint8_t *buf8, const Symbol &sym) const {
84fe6060f1SDimitry Andric   ::writeStub<LP64>(buf8, stubCode, sym);
85fe6060f1SDimitry Andric }
86fe6060f1SDimitry Andric 
87fe6060f1SDimitry Andric static constexpr uint32_t stubHelperHeaderCode[] = {
88fe6060f1SDimitry Andric     0x90000011, // 00: adrp  x17, _dyld_private@page
89fe6060f1SDimitry Andric     0x91000231, // 04: add   x17, x17, _dyld_private@pageoff
90fe6060f1SDimitry Andric     0xa9bf47f0, // 08: stp   x16/x17, [sp, #-16]!
91fe6060f1SDimitry Andric     0x90000010, // 0c: adrp  x16, dyld_stub_binder@page
92fe6060f1SDimitry Andric     0xf9400210, // 10: ldr   x16, [x16, dyld_stub_binder@pageoff]
93fe6060f1SDimitry Andric     0xd61f0200, // 14: br    x16
94fe6060f1SDimitry Andric };
95fe6060f1SDimitry Andric 
96fe6060f1SDimitry Andric void ARM64::writeStubHelperHeader(uint8_t *buf8) const {
97fe6060f1SDimitry Andric   ::writeStubHelperHeader<LP64>(buf8, stubHelperHeaderCode);
98fe6060f1SDimitry Andric }
99fe6060f1SDimitry Andric 
100fe6060f1SDimitry Andric static constexpr uint32_t stubHelperEntryCode[] = {
101fe6060f1SDimitry Andric     0x18000050, // 00: ldr  w16, l0
102fe6060f1SDimitry Andric     0x14000000, // 04: b    stubHelperHeader
103fe6060f1SDimitry Andric     0x00000000, // 08: l0: .long 0
104fe6060f1SDimitry Andric };
105fe6060f1SDimitry Andric 
106*81ad6265SDimitry Andric void ARM64::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,
107fe6060f1SDimitry Andric                                  uint64_t entryVA) const {
108fe6060f1SDimitry Andric   ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA);
109fe6060f1SDimitry Andric }
110fe6060f1SDimitry Andric 
111fe6060f1SDimitry Andric // A thunk is the relaxed variation of stubCode. We don't need the
112fe6060f1SDimitry Andric // extra indirection through a lazy pointer because the target address
113fe6060f1SDimitry Andric // is known at link time.
114fe6060f1SDimitry Andric static constexpr uint32_t thunkCode[] = {
115fe6060f1SDimitry Andric     0x90000010, // 00: adrp  x16, <thunk.ptr>@page
116fe6060f1SDimitry Andric     0x91000210, // 04: add   x16, [x16,<thunk.ptr>@pageoff]
117fe6060f1SDimitry Andric     0xd61f0200, // 08: br    x16
118fe6060f1SDimitry Andric };
119fe6060f1SDimitry Andric 
120fe6060f1SDimitry Andric void ARM64::populateThunk(InputSection *thunk, Symbol *funcSym) {
121fe6060f1SDimitry Andric   thunk->align = 4;
122fe6060f1SDimitry Andric   thunk->data = {reinterpret_cast<const uint8_t *>(thunkCode),
123fe6060f1SDimitry Andric                  sizeof(thunkCode)};
124fe6060f1SDimitry Andric   thunk->relocs.push_back({/*type=*/ARM64_RELOC_PAGEOFF12,
125fe6060f1SDimitry Andric                            /*pcrel=*/false, /*length=*/2,
126fe6060f1SDimitry Andric                            /*offset=*/4, /*addend=*/0,
127fe6060f1SDimitry Andric                            /*referent=*/funcSym});
128fe6060f1SDimitry Andric   thunk->relocs.push_back({/*type=*/ARM64_RELOC_PAGE21,
129fe6060f1SDimitry Andric                            /*pcrel=*/true, /*length=*/2,
130fe6060f1SDimitry Andric                            /*offset=*/0, /*addend=*/0,
131fe6060f1SDimitry Andric                            /*referent=*/funcSym});
132fe6060f1SDimitry Andric }
133fe6060f1SDimitry Andric 
134fe6060f1SDimitry Andric ARM64::ARM64() : ARM64Common(LP64()) {
135fe6060f1SDimitry Andric   cpuType = CPU_TYPE_ARM64;
136fe6060f1SDimitry Andric   cpuSubtype = CPU_SUBTYPE_ARM64_ALL;
137fe6060f1SDimitry Andric 
138fe6060f1SDimitry Andric   stubSize = sizeof(stubCode);
139fe6060f1SDimitry Andric   thunkSize = sizeof(thunkCode);
140349cc55cSDimitry Andric 
141349cc55cSDimitry Andric   // Branch immediate is two's complement 26 bits, which is implicitly
142349cc55cSDimitry Andric   // multiplied by 4 (since all functions are 4-aligned: The branch range
143349cc55cSDimitry Andric   // is -4*(2**(26-1))..4*(2**(26-1) - 1).
144349cc55cSDimitry Andric   backwardBranchRange = 128 * 1024 * 1024;
145349cc55cSDimitry Andric   forwardBranchRange = backwardBranchRange - 4;
146349cc55cSDimitry Andric 
147*81ad6265SDimitry Andric   modeDwarfEncoding = UNWIND_ARM64_MODE_DWARF;
148*81ad6265SDimitry Andric   subtractorRelocType = ARM64_RELOC_SUBTRACTOR;
149*81ad6265SDimitry Andric   unsignedRelocType = ARM64_RELOC_UNSIGNED;
150*81ad6265SDimitry Andric 
151fe6060f1SDimitry Andric   stubHelperHeaderSize = sizeof(stubHelperHeaderCode);
152fe6060f1SDimitry Andric   stubHelperEntrySize = sizeof(stubHelperEntryCode);
153fe6060f1SDimitry Andric }
154fe6060f1SDimitry Andric 
155*81ad6265SDimitry Andric namespace {
156*81ad6265SDimitry Andric struct Adrp {
157*81ad6265SDimitry Andric   uint32_t destRegister;
158*81ad6265SDimitry Andric };
159*81ad6265SDimitry Andric 
160*81ad6265SDimitry Andric struct Add {
161*81ad6265SDimitry Andric   uint8_t destRegister;
162*81ad6265SDimitry Andric   uint8_t srcRegister;
163*81ad6265SDimitry Andric   uint32_t addend;
164*81ad6265SDimitry Andric };
165*81ad6265SDimitry Andric 
166*81ad6265SDimitry Andric enum ExtendType { ZeroExtend = 1, Sign64 = 2, Sign32 = 3 };
167*81ad6265SDimitry Andric 
168*81ad6265SDimitry Andric struct Ldr {
169*81ad6265SDimitry Andric   uint8_t destRegister;
170*81ad6265SDimitry Andric   uint8_t baseRegister;
171*81ad6265SDimitry Andric   uint8_t size;
172*81ad6265SDimitry Andric   bool isFloat;
173*81ad6265SDimitry Andric   ExtendType extendType;
174*81ad6265SDimitry Andric   uint64_t offset;
175*81ad6265SDimitry Andric };
176*81ad6265SDimitry Andric 
177*81ad6265SDimitry Andric struct PerformedReloc {
178*81ad6265SDimitry Andric   const Reloc &rel;
179*81ad6265SDimitry Andric   uint64_t referentVA;
180*81ad6265SDimitry Andric };
181*81ad6265SDimitry Andric 
182*81ad6265SDimitry Andric class OptimizationHintContext {
183*81ad6265SDimitry Andric public:
184*81ad6265SDimitry Andric   OptimizationHintContext(uint8_t *buf, const ConcatInputSection *isec,
185*81ad6265SDimitry Andric                           ArrayRef<uint64_t> relocTargets)
186*81ad6265SDimitry Andric       : buf(buf), isec(isec), relocTargets(relocTargets),
187*81ad6265SDimitry Andric         relocIt(isec->relocs.rbegin()) {}
188*81ad6265SDimitry Andric 
189*81ad6265SDimitry Andric   void applyAdrpAdd(const OptimizationHint &);
190*81ad6265SDimitry Andric   void applyAdrpAdrp(const OptimizationHint &);
191*81ad6265SDimitry Andric   void applyAdrpLdr(const OptimizationHint &);
192*81ad6265SDimitry Andric 
193*81ad6265SDimitry Andric private:
194*81ad6265SDimitry Andric   uint8_t *buf;
195*81ad6265SDimitry Andric   const ConcatInputSection *isec;
196*81ad6265SDimitry Andric   ArrayRef<uint64_t> relocTargets;
197*81ad6265SDimitry Andric   std::vector<Reloc>::const_reverse_iterator relocIt;
198*81ad6265SDimitry Andric 
199*81ad6265SDimitry Andric   uint64_t getRelocTarget(const Reloc &);
200*81ad6265SDimitry Andric 
201*81ad6265SDimitry Andric   Optional<PerformedReloc> findPrimaryReloc(uint64_t offset);
202*81ad6265SDimitry Andric   Optional<PerformedReloc> findReloc(uint64_t offset);
203*81ad6265SDimitry Andric };
204*81ad6265SDimitry Andric } // namespace
205*81ad6265SDimitry Andric 
206*81ad6265SDimitry Andric static bool parseAdrp(uint32_t insn, Adrp &adrp) {
207*81ad6265SDimitry Andric   if ((insn & 0x9f000000) != 0x90000000)
208*81ad6265SDimitry Andric     return false;
209*81ad6265SDimitry Andric   adrp.destRegister = insn & 0x1f;
210*81ad6265SDimitry Andric   return true;
211*81ad6265SDimitry Andric }
212*81ad6265SDimitry Andric 
213*81ad6265SDimitry Andric static bool parseAdd(uint32_t insn, Add &add) {
214*81ad6265SDimitry Andric   if ((insn & 0xffc00000) != 0x91000000)
215*81ad6265SDimitry Andric     return false;
216*81ad6265SDimitry Andric   add.destRegister = insn & 0x1f;
217*81ad6265SDimitry Andric   add.srcRegister = (insn >> 5) & 0x1f;
218*81ad6265SDimitry Andric   add.addend = (insn >> 10) & 0xfff;
219*81ad6265SDimitry Andric   return true;
220*81ad6265SDimitry Andric }
221*81ad6265SDimitry Andric 
222*81ad6265SDimitry Andric static bool parseLdr(uint32_t insn, Ldr &ldr) {
223*81ad6265SDimitry Andric   ldr.destRegister = insn & 0x1f;
224*81ad6265SDimitry Andric   ldr.baseRegister = (insn >> 5) & 0x1f;
225*81ad6265SDimitry Andric   uint8_t size = insn >> 30;
226*81ad6265SDimitry Andric   uint8_t opc = (insn >> 22) & 3;
227*81ad6265SDimitry Andric 
228*81ad6265SDimitry Andric   if ((insn & 0x3fc00000) == 0x39400000) {
229*81ad6265SDimitry Andric     // LDR (immediate), LDRB (immediate), LDRH (immediate)
230*81ad6265SDimitry Andric     ldr.size = 1 << size;
231*81ad6265SDimitry Andric     ldr.extendType = ZeroExtend;
232*81ad6265SDimitry Andric     ldr.isFloat = false;
233*81ad6265SDimitry Andric   } else if ((insn & 0x3f800000) == 0x39800000) {
234*81ad6265SDimitry Andric     // LDRSB (immediate), LDRSH (immediate), LDRSW (immediate)
235*81ad6265SDimitry Andric     ldr.size = 1 << size;
236*81ad6265SDimitry Andric     ldr.extendType = static_cast<ExtendType>(opc);
237*81ad6265SDimitry Andric     ldr.isFloat = false;
238*81ad6265SDimitry Andric   } else if ((insn & 0x3f400000) == 0x3d400000) {
239*81ad6265SDimitry Andric     // LDR (immediate, SIMD&FP)
240*81ad6265SDimitry Andric     ldr.extendType = ZeroExtend;
241*81ad6265SDimitry Andric     ldr.isFloat = true;
242*81ad6265SDimitry Andric     if (size == 2 && opc == 1)
243*81ad6265SDimitry Andric       ldr.size = 4;
244*81ad6265SDimitry Andric     else if (size == 3 && opc == 1)
245*81ad6265SDimitry Andric       ldr.size = 8;
246*81ad6265SDimitry Andric     else if (size == 0 && opc == 3)
247*81ad6265SDimitry Andric       ldr.size = 16;
248*81ad6265SDimitry Andric     else
249*81ad6265SDimitry Andric       return false;
250*81ad6265SDimitry Andric   } else {
251*81ad6265SDimitry Andric     return false;
252*81ad6265SDimitry Andric   }
253*81ad6265SDimitry Andric   ldr.offset = ((insn >> 10) & 0xfff) * ldr.size;
254*81ad6265SDimitry Andric   return true;
255*81ad6265SDimitry Andric }
256*81ad6265SDimitry Andric 
257*81ad6265SDimitry Andric static void writeAdr(void *loc, uint32_t dest, int32_t delta) {
258*81ad6265SDimitry Andric   uint32_t opcode = 0x10000000;
259*81ad6265SDimitry Andric   uint32_t immHi = (delta & 0x001ffffc) << 3;
260*81ad6265SDimitry Andric   uint32_t immLo = (delta & 0x00000003) << 29;
261*81ad6265SDimitry Andric   write32le(loc, opcode | immHi | immLo | dest);
262*81ad6265SDimitry Andric }
263*81ad6265SDimitry Andric 
264*81ad6265SDimitry Andric static void writeNop(void *loc) { write32le(loc, 0xd503201f); }
265*81ad6265SDimitry Andric 
266*81ad6265SDimitry Andric static void writeLiteralLdr(void *loc, Ldr original, int32_t delta) {
267*81ad6265SDimitry Andric   uint32_t imm19 = (delta & 0x001ffffc) << 3;
268*81ad6265SDimitry Andric   uint32_t opcode = 0;
269*81ad6265SDimitry Andric   switch (original.size) {
270*81ad6265SDimitry Andric   case 4:
271*81ad6265SDimitry Andric     if (original.isFloat)
272*81ad6265SDimitry Andric       opcode = 0x1c000000;
273*81ad6265SDimitry Andric     else
274*81ad6265SDimitry Andric       opcode = original.extendType == Sign64 ? 0x98000000 : 0x18000000;
275*81ad6265SDimitry Andric     break;
276*81ad6265SDimitry Andric   case 8:
277*81ad6265SDimitry Andric     opcode = original.isFloat ? 0x5c000000 : 0x58000000;
278*81ad6265SDimitry Andric     break;
279*81ad6265SDimitry Andric   case 16:
280*81ad6265SDimitry Andric     opcode = 0x9c000000;
281*81ad6265SDimitry Andric     break;
282*81ad6265SDimitry Andric   default:
283*81ad6265SDimitry Andric     assert(false && "Invalid size for literal ldr");
284*81ad6265SDimitry Andric   }
285*81ad6265SDimitry Andric   write32le(loc, opcode | imm19 | original.destRegister);
286*81ad6265SDimitry Andric }
287*81ad6265SDimitry Andric 
288*81ad6265SDimitry Andric uint64_t OptimizationHintContext::getRelocTarget(const Reloc &reloc) {
289*81ad6265SDimitry Andric   size_t relocIdx = &reloc - isec->relocs.data();
290*81ad6265SDimitry Andric   return relocTargets[relocIdx];
291*81ad6265SDimitry Andric }
292*81ad6265SDimitry Andric 
293*81ad6265SDimitry Andric // Optimization hints are sorted in a monotonically increasing order by their
294*81ad6265SDimitry Andric // first address as are relocations (albeit in decreasing order), so if we keep
295*81ad6265SDimitry Andric // a pointer around to the last found relocation, we don't have to do a full
296*81ad6265SDimitry Andric // binary search every time.
297*81ad6265SDimitry Andric Optional<PerformedReloc>
298*81ad6265SDimitry Andric OptimizationHintContext::findPrimaryReloc(uint64_t offset) {
299*81ad6265SDimitry Andric   const auto end = isec->relocs.rend();
300*81ad6265SDimitry Andric   while (relocIt != end && relocIt->offset < offset)
301*81ad6265SDimitry Andric     ++relocIt;
302*81ad6265SDimitry Andric   if (relocIt == end || relocIt->offset != offset)
303*81ad6265SDimitry Andric     return None;
304*81ad6265SDimitry Andric   return PerformedReloc{*relocIt, getRelocTarget(*relocIt)};
305*81ad6265SDimitry Andric }
306*81ad6265SDimitry Andric 
307*81ad6265SDimitry Andric // The second and third addresses of optimization hints have no such
308*81ad6265SDimitry Andric // monotonicity as the first, so we search the entire range of relocations.
309*81ad6265SDimitry Andric Optional<PerformedReloc> OptimizationHintContext::findReloc(uint64_t offset) {
310*81ad6265SDimitry Andric   // Optimization hints often apply to successive relocations, so we check for
311*81ad6265SDimitry Andric   // that first before doing a full binary search.
312*81ad6265SDimitry Andric   auto end = isec->relocs.rend();
313*81ad6265SDimitry Andric   if (relocIt < end - 1 && (relocIt + 1)->offset == offset)
314*81ad6265SDimitry Andric     return PerformedReloc{*(relocIt + 1), getRelocTarget(*(relocIt + 1))};
315*81ad6265SDimitry Andric 
316*81ad6265SDimitry Andric   auto reloc = lower_bound(isec->relocs, offset,
317*81ad6265SDimitry Andric                            [](const Reloc &reloc, uint64_t offset) {
318*81ad6265SDimitry Andric                              return offset < reloc.offset;
319*81ad6265SDimitry Andric                            });
320*81ad6265SDimitry Andric 
321*81ad6265SDimitry Andric   if (reloc == isec->relocs.end() || reloc->offset != offset)
322*81ad6265SDimitry Andric     return None;
323*81ad6265SDimitry Andric   return PerformedReloc{*reloc, getRelocTarget(*reloc)};
324*81ad6265SDimitry Andric }
325*81ad6265SDimitry Andric 
326*81ad6265SDimitry Andric // Transforms a pair of adrp+add instructions into an adr instruction if the
327*81ad6265SDimitry Andric // target is within the +/- 1 MiB range allowed by the adr's 21 bit signed
328*81ad6265SDimitry Andric // immediate offset.
329*81ad6265SDimitry Andric //
330*81ad6265SDimitry Andric //   adrp xN, _foo@PAGE
331*81ad6265SDimitry Andric //   add  xM, xN, _foo@PAGEOFF
332*81ad6265SDimitry Andric // ->
333*81ad6265SDimitry Andric //   adr  xM, _foo
334*81ad6265SDimitry Andric //   nop
335*81ad6265SDimitry Andric void OptimizationHintContext::applyAdrpAdd(const OptimizationHint &hint) {
336*81ad6265SDimitry Andric   uint32_t ins1 = read32le(buf + hint.offset0);
337*81ad6265SDimitry Andric   uint32_t ins2 = read32le(buf + hint.offset0 + hint.delta[0]);
338*81ad6265SDimitry Andric   Adrp adrp;
339*81ad6265SDimitry Andric   if (!parseAdrp(ins1, adrp))
340*81ad6265SDimitry Andric     return;
341*81ad6265SDimitry Andric   Add add;
342*81ad6265SDimitry Andric   if (!parseAdd(ins2, add))
343*81ad6265SDimitry Andric     return;
344*81ad6265SDimitry Andric   if (adrp.destRegister != add.srcRegister)
345*81ad6265SDimitry Andric     return;
346*81ad6265SDimitry Andric 
347*81ad6265SDimitry Andric   Optional<PerformedReloc> rel1 = findPrimaryReloc(hint.offset0);
348*81ad6265SDimitry Andric   Optional<PerformedReloc> rel2 = findReloc(hint.offset0 + hint.delta[0]);
349*81ad6265SDimitry Andric   if (!rel1 || !rel2)
350*81ad6265SDimitry Andric     return;
351*81ad6265SDimitry Andric   if (rel1->referentVA != rel2->referentVA)
352*81ad6265SDimitry Andric     return;
353*81ad6265SDimitry Andric   int64_t delta = rel1->referentVA - rel1->rel.offset - isec->getVA();
354*81ad6265SDimitry Andric   if (delta >= (1 << 20) || delta < -(1 << 20))
355*81ad6265SDimitry Andric     return;
356*81ad6265SDimitry Andric 
357*81ad6265SDimitry Andric   writeAdr(buf + hint.offset0, add.destRegister, delta);
358*81ad6265SDimitry Andric   writeNop(buf + hint.offset0 + hint.delta[0]);
359*81ad6265SDimitry Andric }
360*81ad6265SDimitry Andric 
361*81ad6265SDimitry Andric // Transforms two adrp instructions into a single adrp if their referent
362*81ad6265SDimitry Andric // addresses are located on the same 4096 byte page.
363*81ad6265SDimitry Andric //
364*81ad6265SDimitry Andric //   adrp xN, _foo@PAGE
365*81ad6265SDimitry Andric //   adrp xN, _bar@PAGE
366*81ad6265SDimitry Andric // ->
367*81ad6265SDimitry Andric //   adrp xN, _foo@PAGE
368*81ad6265SDimitry Andric //   nop
369*81ad6265SDimitry Andric void OptimizationHintContext::applyAdrpAdrp(const OptimizationHint &hint) {
370*81ad6265SDimitry Andric   uint32_t ins1 = read32le(buf + hint.offset0);
371*81ad6265SDimitry Andric   uint32_t ins2 = read32le(buf + hint.offset0 + hint.delta[0]);
372*81ad6265SDimitry Andric   Adrp adrp1, adrp2;
373*81ad6265SDimitry Andric   if (!parseAdrp(ins1, adrp1) || !parseAdrp(ins2, adrp2))
374*81ad6265SDimitry Andric     return;
375*81ad6265SDimitry Andric   if (adrp1.destRegister != adrp2.destRegister)
376*81ad6265SDimitry Andric     return;
377*81ad6265SDimitry Andric 
378*81ad6265SDimitry Andric   Optional<PerformedReloc> rel1 = findPrimaryReloc(hint.offset0);
379*81ad6265SDimitry Andric   Optional<PerformedReloc> rel2 = findReloc(hint.offset0 + hint.delta[0]);
380*81ad6265SDimitry Andric   if (!rel1 || !rel2)
381*81ad6265SDimitry Andric     return;
382*81ad6265SDimitry Andric   if ((rel1->referentVA & ~0xfffULL) != (rel2->referentVA & ~0xfffULL))
383*81ad6265SDimitry Andric     return;
384*81ad6265SDimitry Andric 
385*81ad6265SDimitry Andric   writeNop(buf + hint.offset0 + hint.delta[0]);
386*81ad6265SDimitry Andric }
387*81ad6265SDimitry Andric 
388*81ad6265SDimitry Andric // Transforms a pair of adrp+ldr (immediate) instructions into an ldr (literal)
389*81ad6265SDimitry Andric // load from a PC-relative address if it is 4-byte aligned and within +/- 1 MiB,
390*81ad6265SDimitry Andric // as ldr can encode a signed 19-bit offset that gets multiplied by 4.
391*81ad6265SDimitry Andric //
392*81ad6265SDimitry Andric //   adrp xN, _foo@PAGE
393*81ad6265SDimitry Andric //   ldr  xM, [xN, _foo@PAGEOFF]
394*81ad6265SDimitry Andric // ->
395*81ad6265SDimitry Andric //   nop
396*81ad6265SDimitry Andric //   ldr  xM, _foo
397*81ad6265SDimitry Andric void OptimizationHintContext::applyAdrpLdr(const OptimizationHint &hint) {
398*81ad6265SDimitry Andric   uint32_t ins1 = read32le(buf + hint.offset0);
399*81ad6265SDimitry Andric   uint32_t ins2 = read32le(buf + hint.offset0 + hint.delta[0]);
400*81ad6265SDimitry Andric   Adrp adrp;
401*81ad6265SDimitry Andric   if (!parseAdrp(ins1, adrp))
402*81ad6265SDimitry Andric     return;
403*81ad6265SDimitry Andric   Ldr ldr;
404*81ad6265SDimitry Andric   if (!parseLdr(ins2, ldr))
405*81ad6265SDimitry Andric     return;
406*81ad6265SDimitry Andric   if (adrp.destRegister != ldr.baseRegister)
407*81ad6265SDimitry Andric     return;
408*81ad6265SDimitry Andric 
409*81ad6265SDimitry Andric   Optional<PerformedReloc> rel1 = findPrimaryReloc(hint.offset0);
410*81ad6265SDimitry Andric   Optional<PerformedReloc> rel2 = findReloc(hint.offset0 + hint.delta[0]);
411*81ad6265SDimitry Andric   if (!rel1 || !rel2)
412*81ad6265SDimitry Andric     return;
413*81ad6265SDimitry Andric   if (ldr.offset != (rel1->referentVA & 0xfff))
414*81ad6265SDimitry Andric     return;
415*81ad6265SDimitry Andric   if ((rel1->referentVA & 3) != 0)
416*81ad6265SDimitry Andric     return;
417*81ad6265SDimitry Andric   if (ldr.size == 1 || ldr.size == 2)
418*81ad6265SDimitry Andric     return;
419*81ad6265SDimitry Andric   int64_t delta = rel1->referentVA - rel2->rel.offset - isec->getVA();
420*81ad6265SDimitry Andric   if (delta >= (1 << 20) || delta < -(1 << 20))
421*81ad6265SDimitry Andric     return;
422*81ad6265SDimitry Andric 
423*81ad6265SDimitry Andric   writeNop(buf + hint.offset0);
424*81ad6265SDimitry Andric   writeLiteralLdr(buf + hint.offset0 + hint.delta[0], ldr, delta);
425*81ad6265SDimitry Andric }
426*81ad6265SDimitry Andric 
427*81ad6265SDimitry Andric void ARM64::applyOptimizationHints(uint8_t *buf, const ConcatInputSection *isec,
428*81ad6265SDimitry Andric                                    ArrayRef<uint64_t> relocTargets) const {
429*81ad6265SDimitry Andric   assert(isec);
430*81ad6265SDimitry Andric   assert(relocTargets.size() == isec->relocs.size());
431*81ad6265SDimitry Andric 
432*81ad6265SDimitry Andric   // Note: Some of these optimizations might not be valid when shared regions
433*81ad6265SDimitry Andric   // are in use. Will need to revisit this if splitSegInfo is added.
434*81ad6265SDimitry Andric 
435*81ad6265SDimitry Andric   OptimizationHintContext ctx1(buf, isec, relocTargets);
436*81ad6265SDimitry Andric   for (const OptimizationHint &hint : isec->optimizationHints) {
437*81ad6265SDimitry Andric     switch (hint.type) {
438*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_ADRP:
439*81ad6265SDimitry Andric       // This is done in another pass because the other optimization hints
440*81ad6265SDimitry Andric       // might cause its targets to be turned into NOPs.
441*81ad6265SDimitry Andric       break;
442*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_LDR:
443*81ad6265SDimitry Andric       ctx1.applyAdrpLdr(hint);
444*81ad6265SDimitry Andric       break;
445*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_ADD_LDR:
446*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_LDR_GOT_LDR:
447*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_ADD_STR:
448*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_LDR_GOT_STR:
449*81ad6265SDimitry Andric       // TODO: Implement these
450*81ad6265SDimitry Andric       break;
451*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_ADD:
452*81ad6265SDimitry Andric       ctx1.applyAdrpAdd(hint);
453*81ad6265SDimitry Andric       break;
454*81ad6265SDimitry Andric     case LOH_ARM64_ADRP_LDR_GOT:
455*81ad6265SDimitry Andric       // TODO: Implement this as well
456*81ad6265SDimitry Andric       break;
457*81ad6265SDimitry Andric     }
458*81ad6265SDimitry Andric   }
459*81ad6265SDimitry Andric 
460*81ad6265SDimitry Andric   OptimizationHintContext ctx2(buf, isec, relocTargets);
461*81ad6265SDimitry Andric   for (const OptimizationHint &hint : isec->optimizationHints)
462*81ad6265SDimitry Andric     if (hint.type == LOH_ARM64_ADRP_ADRP)
463*81ad6265SDimitry Andric       ctx2.applyAdrpAdrp(hint);
464*81ad6265SDimitry Andric }
465*81ad6265SDimitry Andric 
466fe6060f1SDimitry Andric TargetInfo *macho::createARM64TargetInfo() {
467fe6060f1SDimitry Andric   static ARM64 t;
468fe6060f1SDimitry Andric   return &t;
469fe6060f1SDimitry Andric }
470