1 | //===---------- AArch64CollectLOH.cpp - AArch64 collect LOH pass --*- C++ -*-=// |
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 contains a pass that collect the Linker Optimization Hint (LOH). |
10 | // This pass should be run at the very end of the compilation flow, just before |
11 | // assembly printer. |
12 | // To be useful for the linker, the LOH must be printed into the assembly file. |
13 | // |
14 | // A LOH describes a sequence of instructions that may be optimized by the |
15 | // linker. |
16 | // This same sequence cannot be optimized by the compiler because some of |
17 | // the information will be known at link time. |
18 | // For instance, consider the following sequence: |
19 | // L1: adrp xA, sym@PAGE |
20 | // L2: add xB, xA, sym@PAGEOFF |
21 | // L3: ldr xC, [xB, #imm] |
22 | // This sequence can be turned into: |
23 | // A literal load if sym@PAGE + sym@PAGEOFF + #imm - address(L3) is < 1MB: |
24 | // L3: ldr xC, sym+#imm |
25 | // It may also be turned into either the following more efficient |
26 | // code sequences: |
27 | // - If sym@PAGEOFF + #imm fits the encoding space of L3. |
28 | // L1: adrp xA, sym@PAGE |
29 | // L3: ldr xC, [xB, sym@PAGEOFF + #imm] |
30 | // - If sym@PAGE + sym@PAGEOFF - address(L1) < 1MB: |
31 | // L1: adr xA, sym |
32 | // L3: ldr xC, [xB, #imm] |
33 | // |
34 | // To be valid a LOH must meet all the requirements needed by all the related |
35 | // possible linker transformations. |
36 | // For instance, using the running example, the constraints to emit |
37 | // ".loh AdrpAddLdr" are: |
38 | // - L1, L2, and L3 instructions are of the expected type, i.e., |
39 | // respectively ADRP, ADD (immediate), and LD. |
40 | // - The result of L1 is used only by L2. |
41 | // - The register argument (xA) used in the ADD instruction is defined |
42 | // only by L1. |
43 | // - The result of L2 is used only by L3. |
44 | // - The base address (xB) in L3 is defined only L2. |
45 | // - The ADRP in L1 and the ADD in L2 must reference the same symbol using |
46 | // @PAGE/@PAGEOFF with no additional constants |
47 | // |
48 | // Currently supported LOHs are: |
49 | // * So called non-ADRP-related: |
50 | // - .loh AdrpAddLdr L1, L2, L3: |
51 | // L1: adrp xA, sym@PAGE |
52 | // L2: add xB, xA, sym@PAGEOFF |
53 | // L3: ldr xC, [xB, #imm] |
54 | // - .loh AdrpLdrGotLdr L1, L2, L3: |
55 | // L1: adrp xA, sym@GOTPAGE |
56 | // L2: ldr xB, [xA, sym@GOTPAGEOFF] |
57 | // L3: ldr xC, [xB, #imm] |
58 | // - .loh AdrpLdr L1, L3: |
59 | // L1: adrp xA, sym@PAGE |
60 | // L3: ldr xC, [xA, sym@PAGEOFF] |
61 | // - .loh AdrpAddStr L1, L2, L3: |
62 | // L1: adrp xA, sym@PAGE |
63 | // L2: add xB, xA, sym@PAGEOFF |
64 | // L3: str xC, [xB, #imm] |
65 | // - .loh AdrpLdrGotStr L1, L2, L3: |
66 | // L1: adrp xA, sym@GOTPAGE |
67 | // L2: ldr xB, [xA, sym@GOTPAGEOFF] |
68 | // L3: str xC, [xB, #imm] |
69 | // - .loh AdrpAdd L1, L2: |
70 | // L1: adrp xA, sym@PAGE |
71 | // L2: add xB, xA, sym@PAGEOFF |
72 | // For all these LOHs, L1, L2, L3 form a simple chain: |
73 | // L1 result is used only by L2 and L2 result by L3. |
74 | // L3 LOH-related argument is defined only by L2 and L2 LOH-related argument |
75 | // by L1. |
76 | // All these LOHs aim at using more efficient load/store patterns by folding |
77 | // some instructions used to compute the address directly into the load/store. |
78 | // |
79 | // * So called ADRP-related: |
80 | // - .loh AdrpAdrp L2, L1: |
81 | // L2: ADRP xA, sym1@PAGE |
82 | // L1: ADRP xA, sym2@PAGE |
83 | // L2 dominates L1 and xA is not redifined between L2 and L1 |
84 | // This LOH aims at getting rid of redundant ADRP instructions. |
85 | // |
86 | // The overall design for emitting the LOHs is: |
87 | // 1. AArch64CollectLOH (this pass) records the LOHs in the AArch64FunctionInfo. |
88 | // 2. AArch64AsmPrinter reads the LOHs from AArch64FunctionInfo and it: |
89 | // 1. Associates them a label. |
90 | // 2. Emits them in a MCStreamer (EmitLOHDirective). |
91 | // - The MCMachOStreamer records them into the MCAssembler. |
92 | // - The MCAsmStreamer prints them. |
93 | // - Other MCStreamers ignore them. |
94 | // 3. Closes the MCStreamer: |
95 | // - The MachObjectWriter gets them from the MCAssembler and writes |
96 | // them in the object file. |
97 | // - Other ObjectWriters ignore them. |
98 | //===----------------------------------------------------------------------===// |
99 | |
100 | #include "AArch64.h" |
101 | #include "AArch64MachineFunctionInfo.h" |
102 | #include "llvm/ADT/SmallSet.h" |
103 | #include "llvm/ADT/Statistic.h" |
104 | #include "llvm/CodeGen/MachineBasicBlock.h" |
105 | #include "llvm/CodeGen/MachineFunctionPass.h" |
106 | #include "llvm/CodeGen/MachineInstr.h" |
107 | #include "llvm/CodeGen/TargetRegisterInfo.h" |
108 | #include "llvm/Support/Debug.h" |
109 | #include "llvm/Support/ErrorHandling.h" |
110 | #include "llvm/Support/raw_ostream.h" |
111 | using namespace llvm; |
112 | |
113 | #define DEBUG_TYPE "aarch64-collect-loh" |
114 | |
115 | STATISTIC(NumADRPSimpleCandidate, |
116 | "Number of simplifiable ADRP dominate by another" ); |
117 | STATISTIC(NumADDToSTR, "Number of simplifiable STR reachable by ADD" ); |
118 | STATISTIC(NumLDRToSTR, "Number of simplifiable STR reachable by LDR" ); |
119 | STATISTIC(NumADDToLDR, "Number of simplifiable LDR reachable by ADD" ); |
120 | STATISTIC(NumLDRToLDR, "Number of simplifiable LDR reachable by LDR" ); |
121 | STATISTIC(NumADRPToLDR, "Number of simplifiable LDR reachable by ADRP" ); |
122 | STATISTIC(NumADRSimpleCandidate, "Number of simplifiable ADRP + ADD" ); |
123 | |
124 | #define AARCH64_COLLECT_LOH_NAME "AArch64 Collect Linker Optimization Hint (LOH)" |
125 | |
126 | namespace { |
127 | |
128 | struct AArch64CollectLOH : public MachineFunctionPass { |
129 | static char ID; |
130 | AArch64CollectLOH() : MachineFunctionPass(ID) {} |
131 | |
132 | bool runOnMachineFunction(MachineFunction &MF) override; |
133 | |
134 | MachineFunctionProperties getRequiredProperties() const override { |
135 | return MachineFunctionProperties().setNoVRegs(); |
136 | } |
137 | |
138 | StringRef getPassName() const override { return AARCH64_COLLECT_LOH_NAME; } |
139 | |
140 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
141 | MachineFunctionPass::getAnalysisUsage(AU); |
142 | AU.setPreservesAll(); |
143 | } |
144 | }; |
145 | |
146 | char AArch64CollectLOH::ID = 0; |
147 | |
148 | } // end anonymous namespace. |
149 | |
150 | INITIALIZE_PASS(AArch64CollectLOH, "aarch64-collect-loh" , |
151 | AARCH64_COLLECT_LOH_NAME, false, false) |
152 | |
153 | static bool canAddBePartOfLOH(const MachineInstr &MI) { |
154 | // Check immediate to see if the immediate is an address. |
155 | switch (MI.getOperand(i: 2).getType()) { |
156 | default: |
157 | return false; |
158 | case MachineOperand::MO_GlobalAddress: |
159 | case MachineOperand::MO_JumpTableIndex: |
160 | case MachineOperand::MO_ConstantPoolIndex: |
161 | case MachineOperand::MO_BlockAddress: |
162 | return true; |
163 | } |
164 | } |
165 | |
166 | /// Answer the following question: Can Def be one of the definition |
167 | /// involved in a part of a LOH? |
168 | static bool canDefBePartOfLOH(const MachineInstr &MI) { |
169 | // Accept ADRP, ADDLow and LOADGot. |
170 | switch (MI.getOpcode()) { |
171 | default: |
172 | return false; |
173 | case AArch64::ADRP: |
174 | return true; |
175 | case AArch64::ADDXri: |
176 | return canAddBePartOfLOH(MI); |
177 | case AArch64::LDRXui: |
178 | case AArch64::LDRWui: |
179 | // Check immediate to see if the immediate is an address. |
180 | switch (MI.getOperand(i: 2).getType()) { |
181 | default: |
182 | return false; |
183 | case MachineOperand::MO_GlobalAddress: |
184 | return MI.getOperand(i: 2).getTargetFlags() & AArch64II::MO_GOT; |
185 | } |
186 | } |
187 | } |
188 | |
189 | /// Check whether the given instruction can the end of a LOH chain involving a |
190 | /// store. |
191 | static bool isCandidateStore(const MachineInstr &MI, const MachineOperand &MO) { |
192 | switch (MI.getOpcode()) { |
193 | default: |
194 | return false; |
195 | case AArch64::STRBBui: |
196 | case AArch64::STRHHui: |
197 | case AArch64::STRBui: |
198 | case AArch64::STRHui: |
199 | case AArch64::STRWui: |
200 | case AArch64::STRXui: |
201 | case AArch64::STRSui: |
202 | case AArch64::STRDui: |
203 | case AArch64::STRQui: |
204 | // We can only optimize the index operand. |
205 | // In case we have str xA, [xA, #imm], this is two different uses |
206 | // of xA and we cannot fold, otherwise the xA stored may be wrong, |
207 | // even if #imm == 0. |
208 | return MO.getOperandNo() == 1 && |
209 | MI.getOperand(i: 0).getReg() != MI.getOperand(i: 1).getReg(); |
210 | } |
211 | } |
212 | |
213 | /// Check whether the given instruction can be the end of a LOH chain |
214 | /// involving a load. |
215 | static bool isCandidateLoad(const MachineInstr &MI) { |
216 | switch (MI.getOpcode()) { |
217 | default: |
218 | return false; |
219 | case AArch64::LDRSBWui: |
220 | case AArch64::LDRSBXui: |
221 | case AArch64::LDRSHWui: |
222 | case AArch64::LDRSHXui: |
223 | case AArch64::LDRSWui: |
224 | case AArch64::LDRBui: |
225 | case AArch64::LDRHui: |
226 | case AArch64::LDRWui: |
227 | case AArch64::LDRXui: |
228 | case AArch64::LDRSui: |
229 | case AArch64::LDRDui: |
230 | case AArch64::LDRQui: |
231 | return !(MI.getOperand(i: 2).getTargetFlags() & AArch64II::MO_GOT); |
232 | } |
233 | } |
234 | |
235 | /// Check whether the given instruction can load a literal. |
236 | static bool supportLoadFromLiteral(const MachineInstr &MI) { |
237 | switch (MI.getOpcode()) { |
238 | default: |
239 | return false; |
240 | case AArch64::LDRSWui: |
241 | case AArch64::LDRWui: |
242 | case AArch64::LDRXui: |
243 | case AArch64::LDRSui: |
244 | case AArch64::LDRDui: |
245 | case AArch64::LDRQui: |
246 | return true; |
247 | } |
248 | } |
249 | |
250 | /// Returns \p true if there are no non-debug instructions between \p First and |
251 | /// \p Second |
252 | static bool areInstructionsConsecutive(const MachineInstr *First, |
253 | const MachineInstr *Second) { |
254 | auto It = First->getIterator(); |
255 | auto EndIt = First->getParent()->instr_end(); |
256 | if (It == EndIt) |
257 | return false; |
258 | return next_nodbg(It, End: EndIt) == Second->getIterator(); |
259 | } |
260 | |
261 | /// Number of GPR registers tracked by mapRegToGPRIndex() |
262 | static const unsigned N_GPR_REGS = 31; |
263 | /// Map register number to index from 0-30. |
264 | static int mapRegToGPRIndex(MCRegister Reg) { |
265 | static_assert(AArch64::X28 - AArch64::X0 + 3 == N_GPR_REGS, "Number of GPRs" ); |
266 | static_assert(AArch64::W30 - AArch64::W0 + 1 == N_GPR_REGS, "Number of GPRs" ); |
267 | if (AArch64::X0 <= Reg && Reg <= AArch64::X28) |
268 | return Reg - AArch64::X0; |
269 | if (AArch64::W0 <= Reg && Reg <= AArch64::W30) |
270 | return Reg - AArch64::W0; |
271 | // TableGen gives "FP" and "LR" an index not adjacent to X28 so we have to |
272 | // handle them as special cases. |
273 | if (Reg == AArch64::FP) |
274 | return 29; |
275 | if (Reg == AArch64::LR) |
276 | return 30; |
277 | return -1; |
278 | } |
279 | |
280 | /// State tracked per register. |
281 | /// The main algorithm walks backwards over a basic block maintaining this |
282 | /// datastructure for each tracked general purpose register. |
283 | struct LOHInfo { |
284 | MCLOHType Type : 8; ///< "Best" type of LOH possible. |
285 | LLVM_PREFERRED_TYPE(bool) |
286 | unsigned IsCandidate : 1; ///< Possible LOH candidate. |
287 | LLVM_PREFERRED_TYPE(bool) |
288 | unsigned OneUser : 1; ///< Found exactly one user (yet). |
289 | LLVM_PREFERRED_TYPE(bool) |
290 | unsigned MultiUsers : 1; ///< Found multiple users. |
291 | const MachineInstr *MI0; ///< First instruction involved in the LOH. |
292 | const MachineInstr *MI1; ///< Second instruction involved in the LOH |
293 | /// (if any). |
294 | const MachineInstr *LastADRP; ///< Last ADRP in same register. |
295 | }; |
296 | |
297 | /// Update state \p Info given \p MI uses the tracked register. |
298 | static void handleUse(const MachineInstr &MI, const MachineOperand &MO, |
299 | LOHInfo &Info) { |
300 | // We have multiple uses if we already found one before. |
301 | if (Info.MultiUsers || Info.OneUser) { |
302 | Info.IsCandidate = false; |
303 | Info.MultiUsers = true; |
304 | return; |
305 | } |
306 | Info.OneUser = true; |
307 | |
308 | // Start new LOHInfo if applicable. |
309 | if (isCandidateLoad(MI)) { |
310 | Info.Type = MCLOH_AdrpLdr; |
311 | Info.IsCandidate = true; |
312 | Info.MI0 = &MI; |
313 | // Note that even this is AdrpLdr now, we can switch to a Ldr variant |
314 | // later. |
315 | } else if (isCandidateStore(MI, MO)) { |
316 | Info.Type = MCLOH_AdrpAddStr; |
317 | Info.IsCandidate = true; |
318 | Info.MI0 = &MI; |
319 | Info.MI1 = nullptr; |
320 | } else if (MI.getOpcode() == AArch64::ADDXri) { |
321 | Info.Type = MCLOH_AdrpAdd; |
322 | Info.IsCandidate = true; |
323 | Info.MI0 = &MI; |
324 | } else if ((MI.getOpcode() == AArch64::LDRXui || |
325 | MI.getOpcode() == AArch64::LDRWui) && |
326 | MI.getOperand(i: 2).getTargetFlags() & AArch64II::MO_GOT) { |
327 | Info.Type = MCLOH_AdrpLdrGot; |
328 | Info.IsCandidate = true; |
329 | Info.MI0 = &MI; |
330 | } |
331 | } |
332 | |
333 | /// Update state \p Info given the tracked register is clobbered. |
334 | static void handleClobber(LOHInfo &Info) { |
335 | Info.IsCandidate = false; |
336 | Info.OneUser = false; |
337 | Info.MultiUsers = false; |
338 | Info.LastADRP = nullptr; |
339 | } |
340 | |
341 | /// Update state \p Info given that \p MI is possibly the middle instruction |
342 | /// of an LOH involving 3 instructions. |
343 | static bool handleMiddleInst(const MachineInstr &MI, LOHInfo &DefInfo, |
344 | LOHInfo &OpInfo) { |
345 | if (!DefInfo.IsCandidate || (&DefInfo != &OpInfo && OpInfo.OneUser)) |
346 | return false; |
347 | // Copy LOHInfo for dest register to LOHInfo for source register. |
348 | if (&DefInfo != &OpInfo) { |
349 | OpInfo = DefInfo; |
350 | // Invalidate \p DefInfo because we track it in \p OpInfo now. |
351 | handleClobber(Info&: DefInfo); |
352 | } else |
353 | DefInfo.LastADRP = nullptr; |
354 | |
355 | // Advance state machine. |
356 | assert(OpInfo.IsCandidate && "Expect valid state" ); |
357 | if (MI.getOpcode() == AArch64::ADDXri && canAddBePartOfLOH(MI)) { |
358 | if (OpInfo.Type == MCLOH_AdrpLdr) { |
359 | OpInfo.Type = MCLOH_AdrpAddLdr; |
360 | OpInfo.IsCandidate = true; |
361 | OpInfo.MI1 = &MI; |
362 | return true; |
363 | } else if (OpInfo.Type == MCLOH_AdrpAddStr && OpInfo.MI1 == nullptr) { |
364 | OpInfo.Type = MCLOH_AdrpAddStr; |
365 | OpInfo.IsCandidate = true; |
366 | OpInfo.MI1 = &MI; |
367 | return true; |
368 | } |
369 | } else { |
370 | assert((MI.getOpcode() == AArch64::LDRXui || |
371 | MI.getOpcode() == AArch64::LDRWui) && |
372 | "Expect LDRXui or LDRWui" ); |
373 | assert((MI.getOperand(2).getTargetFlags() & AArch64II::MO_GOT) && |
374 | "Expected GOT relocation" ); |
375 | if (OpInfo.Type == MCLOH_AdrpAddStr && OpInfo.MI1 == nullptr) { |
376 | OpInfo.Type = MCLOH_AdrpLdrGotStr; |
377 | OpInfo.IsCandidate = true; |
378 | OpInfo.MI1 = &MI; |
379 | return true; |
380 | } else if (OpInfo.Type == MCLOH_AdrpLdr) { |
381 | OpInfo.Type = MCLOH_AdrpLdrGotLdr; |
382 | OpInfo.IsCandidate = true; |
383 | OpInfo.MI1 = &MI; |
384 | return true; |
385 | } |
386 | } |
387 | return false; |
388 | } |
389 | |
390 | /// Update state when seeing and ADRP instruction. |
391 | static void handleADRP(const MachineInstr &MI, AArch64FunctionInfo &AFI, |
392 | LOHInfo &Info, LOHInfo *LOHInfos) { |
393 | if (Info.LastADRP != nullptr) { |
394 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpAdrp:\n" |
395 | << '\t' << MI << '\t' << *Info.LastADRP); |
396 | AFI.addLOHDirective(Kind: MCLOH_AdrpAdrp, Args: {&MI, Info.LastADRP}); |
397 | ++NumADRPSimpleCandidate; |
398 | } |
399 | |
400 | // Produce LOH directive if possible. |
401 | if (Info.IsCandidate) { |
402 | switch (Info.Type) { |
403 | case MCLOH_AdrpAdd: { |
404 | // ADRPs and ADDs for this candidate may be split apart if using |
405 | // GlobalISel instead of pseudo-expanded. If that happens, the |
406 | // def register of the ADD may have a use in between. Adding an LOH in |
407 | // this case can cause the linker to rewrite the ADRP to write to that |
408 | // register, clobbering the use. |
409 | const MachineInstr *AddMI = Info.MI0; |
410 | int DefIdx = mapRegToGPRIndex(Reg: MI.getOperand(i: 0).getReg()); |
411 | int OpIdx = mapRegToGPRIndex(Reg: AddMI->getOperand(i: 0).getReg()); |
412 | LOHInfo DefInfo = LOHInfos[OpIdx]; |
413 | if (DefIdx != OpIdx && (DefInfo.OneUser || DefInfo.MultiUsers)) |
414 | break; |
415 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpAdd:\n" |
416 | << '\t' << MI << '\t' << *Info.MI0); |
417 | AFI.addLOHDirective(Kind: MCLOH_AdrpAdd, Args: {&MI, Info.MI0}); |
418 | ++NumADRSimpleCandidate; |
419 | break; |
420 | } |
421 | case MCLOH_AdrpLdr: |
422 | if (supportLoadFromLiteral(MI: *Info.MI0)) { |
423 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpLdr:\n" |
424 | << '\t' << MI << '\t' << *Info.MI0); |
425 | AFI.addLOHDirective(Kind: MCLOH_AdrpLdr, Args: {&MI, Info.MI0}); |
426 | ++NumADRPToLDR; |
427 | } |
428 | break; |
429 | case MCLOH_AdrpAddLdr: |
430 | // There is a possibility that the linker may try to rewrite: |
431 | // adrp x0, @sym@PAGE |
432 | // add x1, x0, @sym@PAGEOFF |
433 | // [x0 = some other def] |
434 | // ldr x2, [x1] |
435 | // ...into... |
436 | // adrp x0, @sym |
437 | // nop |
438 | // [x0 = some other def] |
439 | // ldr x2, [x0] |
440 | // ...if the offset to the symbol won't fit within a literal load. |
441 | // This causes the load to use the result of the adrp, which in this |
442 | // case has already been clobbered. |
443 | // FIXME: Implement proper liveness tracking for all registers. For now, |
444 | // don't emit the LOH if there are any instructions between the add and |
445 | // the ldr. |
446 | if (!areInstructionsConsecutive(First: Info.MI1, Second: Info.MI0)) |
447 | break; |
448 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpAddLdr:\n" |
449 | << '\t' << MI << '\t' << *Info.MI1 << '\t' |
450 | << *Info.MI0); |
451 | AFI.addLOHDirective(Kind: MCLOH_AdrpAddLdr, Args: {&MI, Info.MI1, Info.MI0}); |
452 | ++NumADDToLDR; |
453 | break; |
454 | case MCLOH_AdrpAddStr: |
455 | if (!Info.MI1) |
456 | break; |
457 | if (!areInstructionsConsecutive(First: Info.MI1, Second: Info.MI0)) |
458 | break; |
459 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpAddStr:\n" |
460 | << '\t' << MI << '\t' << *Info.MI1 << '\t' |
461 | << *Info.MI0); |
462 | AFI.addLOHDirective(Kind: MCLOH_AdrpAddStr, Args: {&MI, Info.MI1, Info.MI0}); |
463 | ++NumADDToSTR; |
464 | break; |
465 | case MCLOH_AdrpLdrGotLdr: |
466 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpLdrGotLdr:\n" |
467 | << '\t' << MI << '\t' << *Info.MI1 << '\t' |
468 | << *Info.MI0); |
469 | AFI.addLOHDirective(Kind: MCLOH_AdrpLdrGotLdr, Args: {&MI, Info.MI1, Info.MI0}); |
470 | ++NumLDRToLDR; |
471 | break; |
472 | case MCLOH_AdrpLdrGotStr: |
473 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpLdrGotStr:\n" |
474 | << '\t' << MI << '\t' << *Info.MI1 << '\t' |
475 | << *Info.MI0); |
476 | AFI.addLOHDirective(Kind: MCLOH_AdrpLdrGotStr, Args: {&MI, Info.MI1, Info.MI0}); |
477 | ++NumLDRToSTR; |
478 | break; |
479 | case MCLOH_AdrpLdrGot: |
480 | LLVM_DEBUG(dbgs() << "Adding MCLOH_AdrpLdrGot:\n" |
481 | << '\t' << MI << '\t' << *Info.MI0); |
482 | AFI.addLOHDirective(Kind: MCLOH_AdrpLdrGot, Args: {&MI, Info.MI0}); |
483 | break; |
484 | case MCLOH_AdrpAdrp: |
485 | llvm_unreachable("MCLOH_AdrpAdrp not used in state machine" ); |
486 | } |
487 | } |
488 | |
489 | handleClobber(Info); |
490 | Info.LastADRP = &MI; |
491 | } |
492 | |
493 | static void handleRegMaskClobber(const uint32_t *RegMask, MCPhysReg Reg, |
494 | LOHInfo *LOHInfos) { |
495 | if (!MachineOperand::clobbersPhysReg(RegMask, PhysReg: Reg)) |
496 | return; |
497 | int Idx = mapRegToGPRIndex(Reg); |
498 | if (Idx >= 0) |
499 | handleClobber(Info&: LOHInfos[Idx]); |
500 | } |
501 | |
502 | static void handleNormalInst(const MachineInstr &MI, LOHInfo *LOHInfos) { |
503 | // Handle defs and regmasks. |
504 | for (const MachineOperand &MO : MI.operands()) { |
505 | if (MO.isRegMask()) { |
506 | const uint32_t *RegMask = MO.getRegMask(); |
507 | for (MCPhysReg Reg : AArch64::GPR32RegClass) |
508 | handleRegMaskClobber(RegMask, Reg, LOHInfos); |
509 | for (MCPhysReg Reg : AArch64::GPR64RegClass) |
510 | handleRegMaskClobber(RegMask, Reg, LOHInfos); |
511 | continue; |
512 | } |
513 | if (!MO.isReg() || !MO.isDef()) |
514 | continue; |
515 | int Idx = mapRegToGPRIndex(Reg: MO.getReg()); |
516 | if (Idx < 0) |
517 | continue; |
518 | handleClobber(Info&: LOHInfos[Idx]); |
519 | } |
520 | // Handle uses. |
521 | |
522 | SmallSet<int, 4> UsesSeen; |
523 | for (const MachineOperand &MO : MI.uses()) { |
524 | if (!MO.isReg() || !MO.readsReg()) |
525 | continue; |
526 | int Idx = mapRegToGPRIndex(Reg: MO.getReg()); |
527 | if (Idx < 0) |
528 | continue; |
529 | |
530 | // Multiple uses of the same register within a single instruction don't |
531 | // count as MultiUser or block optimization. This is especially important on |
532 | // arm64_32, where any memory operation is likely to be an explicit use of |
533 | // xN and an implicit use of wN (the base address register). |
534 | if (UsesSeen.insert(V: Idx).second) |
535 | handleUse(MI, MO, Info&: LOHInfos[Idx]); |
536 | } |
537 | } |
538 | |
539 | bool AArch64CollectLOH::runOnMachineFunction(MachineFunction &MF) { |
540 | if (skipFunction(F: MF.getFunction())) |
541 | return false; |
542 | |
543 | LLVM_DEBUG(dbgs() << "********** AArch64 Collect LOH **********\n" |
544 | << "Looking in function " << MF.getName() << '\n'); |
545 | |
546 | LOHInfo LOHInfos[N_GPR_REGS]; |
547 | AArch64FunctionInfo &AFI = *MF.getInfo<AArch64FunctionInfo>(); |
548 | for (const MachineBasicBlock &MBB : MF) { |
549 | // Reset register tracking state. |
550 | memset(s: LOHInfos, c: 0, n: sizeof(LOHInfos)); |
551 | // Live-out registers are used. |
552 | for (const MachineBasicBlock *Succ : MBB.successors()) { |
553 | for (const auto &LI : Succ->liveins()) { |
554 | int RegIdx = mapRegToGPRIndex(Reg: LI.PhysReg); |
555 | if (RegIdx >= 0) |
556 | LOHInfos[RegIdx].OneUser = true; |
557 | } |
558 | } |
559 | |
560 | // Walk the basic block backwards and update the per register state machine |
561 | // in the process. |
562 | for (const MachineInstr &MI : |
563 | instructionsWithoutDebug(It: MBB.instr_rbegin(), End: MBB.instr_rend())) { |
564 | unsigned Opcode = MI.getOpcode(); |
565 | switch (Opcode) { |
566 | case AArch64::ADDXri: |
567 | case AArch64::LDRXui: |
568 | case AArch64::LDRWui: |
569 | if (canDefBePartOfLOH(MI)) { |
570 | const MachineOperand &Def = MI.getOperand(i: 0); |
571 | const MachineOperand &Op = MI.getOperand(i: 1); |
572 | assert(Def.isReg() && Def.isDef() && "Expected reg def" ); |
573 | assert(Op.isReg() && Op.isUse() && "Expected reg use" ); |
574 | int DefIdx = mapRegToGPRIndex(Reg: Def.getReg()); |
575 | int OpIdx = mapRegToGPRIndex(Reg: Op.getReg()); |
576 | if (DefIdx >= 0 && OpIdx >= 0 && |
577 | handleMiddleInst(MI, DefInfo&: LOHInfos[DefIdx], OpInfo&: LOHInfos[OpIdx])) |
578 | continue; |
579 | } |
580 | break; |
581 | case AArch64::ADRP: |
582 | const MachineOperand &Op0 = MI.getOperand(i: 0); |
583 | int Idx = mapRegToGPRIndex(Reg: Op0.getReg()); |
584 | if (Idx >= 0) { |
585 | handleADRP(MI, AFI, Info&: LOHInfos[Idx], LOHInfos); |
586 | continue; |
587 | } |
588 | break; |
589 | } |
590 | handleNormalInst(MI, LOHInfos); |
591 | } |
592 | } |
593 | |
594 | // Return "no change": The pass only collects information. |
595 | return false; |
596 | } |
597 | |
598 | FunctionPass *llvm::createAArch64CollectLOHPass() { |
599 | return new AArch64CollectLOH(); |
600 | } |
601 | |