LLVM 24.0.0git
LoongArchMergeBaseOffset.cpp
Go to the documentation of this file.
1//===---- LoongArchMergeBaseOffset.cpp - Optimise address calculations ----===//
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// Merge the offset of address calculation into the offset field
10// of instructions in a global address lowering sequence.
11//
12//===----------------------------------------------------------------------===//
13
14#include "LoongArch.h"
17#include "llvm/CodeGen/Passes.h"
19#include "llvm/Support/Debug.h"
21#include <optional>
22
23using namespace llvm;
24
25#define DEBUG_TYPE "loongarch-merge-base-offset"
26#define LoongArch_MERGE_BASE_OFFSET_NAME "LoongArch Merge Base Offset"
27
28namespace {
29
30class LoongArchMergeBaseOffsetOpt : public MachineFunctionPass {
31 const LoongArchSubtarget *ST = nullptr;
33
34public:
35 static char ID;
36 bool runOnMachineFunction(MachineFunction &Fn) override;
37 bool detectFoldable(MachineInstr &Hi20, MachineInstr *&Lo12,
38 MachineInstr *&Lo20, MachineInstr *&Hi12,
40 bool detectFoldable(MachineInstr &Hi20, MachineInstr *&Add,
41 MachineInstr *&Lo12);
42
43 bool detectAndFoldOffset(MachineInstr &Hi20, MachineInstr &Lo12,
44 MachineInstr *&Lo20, MachineInstr *&Hi12,
46 void foldOffset(MachineInstr &Hi20, MachineInstr &Lo12, MachineInstr *&Lo20,
48 int64_t Offset);
49 bool foldLargeOffset(MachineInstr &Hi20, MachineInstr &Lo12,
50 MachineInstr *&Lo20, MachineInstr *&Hi12,
51 MachineInstr *&Last, MachineInstr &TailAdd,
52 Register GAReg);
53
54 bool foldIntoMemoryOps(MachineInstr &Hi20, MachineInstr &Lo12,
55 MachineInstr *&Lo20, MachineInstr *&Hi12,
57
58 LoongArchMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}
59
60 MachineFunctionProperties getRequiredProperties() const override {
61 return MachineFunctionProperties().setIsSSA();
62 }
63
64 void getAnalysisUsage(AnalysisUsage &AU) const override {
65 AU.setPreservesCFG();
67 }
68
69 StringRef getPassName() const override {
71 }
72};
73} // end anonymous namespace
74
75char LoongArchMergeBaseOffsetOpt::ID = 0;
76INITIALIZE_PASS(LoongArchMergeBaseOffsetOpt, DEBUG_TYPE,
78
79static inline bool isPCAddLo(unsigned Flags) {
80 switch (Flags) {
87 return true;
88 }
89 return false;
90}
91
92// Detect either of the patterns:
93//
94// 1. (small/medium):
95// pcaxxu12i vreg1, %pc_hi20(s)
96// addi.d vreg2, vreg1, %pc_lo12(s)
97//
98// 2. (large):
99// pcalau12i vreg1, %pc_hi20(s)
100// addi.d vreg2, $zero, %pc_lo12(s)
101// lu32i.d vreg3, vreg2, %pc64_lo20(s)
102// lu52i.d vreg4, vreg3, %pc64_hi12(s)
103// add.d vreg5, vreg4, vreg1
104
105// The pattern is only accepted if:
106// 1) For small and medium pattern, the first instruction has only one use,
107// which is the ADDI.
108// 2) For large pattern, the first four instructions each have only one use,
109// and the user of the fourth instruction is ADD.
110// 3) The address operands have the appropriate type, reflecting the
111// lowering of a global address or constant pool using the pattern.
112// 4) The offset value in the Global Address or Constant Pool is 0.
113bool LoongArchMergeBaseOffsetOpt::detectFoldable(MachineInstr &Hi20,
114 MachineInstr *&Lo12,
115 MachineInstr *&Lo20,
116 MachineInstr *&Hi12,
117 MachineInstr *&Last) {
118 if (Hi20.getOpcode() != LoongArch::PCALAU12I &&
119 Hi20.getOpcode() != LoongArch::PCADDU12I)
120 return false;
121
122 const MachineOperand &Hi20Op1 = Hi20.getOperand(1);
125 return false;
126
127 auto isGlobalOrCPIOrBlockAddress = [](const MachineOperand &Op) {
128 return Op.isGlobal() || Op.isCPI() || Op.isBlockAddress();
129 };
130
131 if (!isGlobalOrCPIOrBlockAddress(Hi20Op1) || Hi20Op1.getOffset() != 0)
132 return false;
133
134 Register HiDestReg = Hi20.getOperand(0).getReg();
135 if (!MRI->hasOneUse(HiDestReg))
136 return false;
137
138 MachineInstr *UseInst = &*MRI->use_instr_begin(HiDestReg);
139 if (UseInst->getOpcode() != LoongArch::ADD_D) {
140 Lo12 = UseInst;
141 if ((ST->is64Bit() && Lo12->getOpcode() != LoongArch::ADDI_D) ||
142 (!ST->is64Bit() && Lo12->getOpcode() != LoongArch::ADDI_W))
143 return false;
144 } else {
145 assert(ST->is64Bit());
146 Last = UseInst;
147
148 Register LastOp1Reg = Last->getOperand(1).getReg();
149 if (!LastOp1Reg.isVirtual())
150 return false;
151 Hi12 = MRI->getVRegDef(LastOp1Reg);
152 const MachineOperand &Hi12Op2 = Hi12->getOperand(2);
154 return false;
155 if (!isGlobalOrCPIOrBlockAddress(Hi12Op2) || Hi12Op2.getOffset() != 0)
156 return false;
157 if (!MRI->hasOneUse(Hi12->getOperand(0).getReg()))
158 return false;
159
160 Lo20 = MRI->getVRegDef(Hi12->getOperand(1).getReg());
161 const MachineOperand &Lo20Op2 = Lo20->getOperand(2);
163 return false;
164 if (!isGlobalOrCPIOrBlockAddress(Lo20Op2) || Lo20Op2.getOffset() != 0)
165 return false;
166 if (!MRI->hasOneUse(Lo20->getOperand(0).getReg()))
167 return false;
168
169 Lo12 = MRI->getVRegDef(Lo20->getOperand(1).getReg());
170 if (!MRI->hasOneUse(Lo12->getOperand(0).getReg()))
171 return false;
172 }
173
174 const MachineOperand &Lo12Op2 = Lo12->getOperand(2);
175 assert(Hi20.getOpcode() == LoongArch::PCALAU12I ||
176 Hi20.getOpcode() == LoongArch::PCADDU12I);
178 !isPCAddLo(LoongArchII::getDirectFlags(Lo12Op2))) ||
179 !(isGlobalOrCPIOrBlockAddress(Lo12Op2) || Lo12Op2.isMCSymbol()) ||
180 Lo12Op2.getOffset() != 0)
181 return false;
182
183 if (Hi20Op1.isGlobal()) {
184 LLVM_DEBUG(dbgs() << " Found lowered global address: "
185 << *Hi20Op1.getGlobal() << "\n");
186 } else if (Hi20Op1.isBlockAddress()) {
187 LLVM_DEBUG(dbgs() << " Found lowered basic address: "
188 << *Hi20Op1.getBlockAddress() << "\n");
189 } else if (Hi20Op1.isCPI()) {
190 LLVM_DEBUG(dbgs() << " Found lowered constant pool: " << Hi20Op1.getIndex()
191 << "\n");
192 }
193
194 return true;
195}
196
197// Detect the pattern:
198//
199// (small/medium):
200// lu12i.w vreg1, %le_hi20_r(s)
201// add.w/d vreg2, vreg1, r2, %le_add_r(s)
202// addi.w/d vreg3, vreg2, %le_lo12_r(s)
203
204// The pattern is only accepted if:
205// 1) The first instruction has only one use, which is the PseudoAddTPRel.
206// The second instruction has only one use, which is the ADDI. The
207// second instruction's last operand is the tp register.
208// 2) The address operands have the appropriate type, reflecting the
209// lowering of a thread_local global address using the pattern.
210// 3) The offset value in the ThreadLocal Global Address is 0.
211bool LoongArchMergeBaseOffsetOpt::detectFoldable(MachineInstr &Hi20,
212 MachineInstr *&Add,
213 MachineInstr *&Lo12) {
214 if (Hi20.getOpcode() != LoongArch::LU12I_W)
215 return false;
216
217 auto isGlobalOrCPI = [](const MachineOperand &Op) {
218 return Op.isGlobal() || Op.isCPI();
219 };
220
221 const MachineOperand &Hi20Op1 = Hi20.getOperand(1);
223 !isGlobalOrCPI(Hi20Op1) || Hi20Op1.getOffset() != 0)
224 return false;
225
226 Register HiDestReg = Hi20.getOperand(0).getReg();
227 if (!MRI->hasOneUse(HiDestReg))
228 return false;
229
230 Add = &*MRI->use_instr_begin(HiDestReg);
231 if ((ST->is64Bit() && Add->getOpcode() != LoongArch::PseudoAddTPRel_D) ||
232 (!ST->is64Bit() && Add->getOpcode() != LoongArch::PseudoAddTPRel_W))
233 return false;
234
235 if (Add->getOperand(2).getReg() != LoongArch::R2)
236 return false;
237
238 const MachineOperand &AddOp3 = Add->getOperand(3);
240 !(isGlobalOrCPI(AddOp3) || AddOp3.isMCSymbol()) ||
241 AddOp3.getOffset() != 0)
242 return false;
243
244 Register AddDestReg = Add->getOperand(0).getReg();
245 if (!MRI->hasOneUse(AddDestReg))
246 return false;
247
248 Lo12 = &*MRI->use_instr_begin(AddDestReg);
249 if ((ST->is64Bit() && Lo12->getOpcode() != LoongArch::ADDI_D) ||
250 (!ST->is64Bit() && Lo12->getOpcode() != LoongArch::ADDI_W))
251 return false;
252
253 const MachineOperand &Lo12Op2 = Lo12->getOperand(2);
255 !(isGlobalOrCPI(Lo12Op2) || Lo12Op2.isMCSymbol()) ||
256 Lo12Op2.getOffset() != 0)
257 return false;
258
259 if (Hi20Op1.isGlobal()) {
260 LLVM_DEBUG(dbgs() << " Found lowered global address: "
261 << *Hi20Op1.getGlobal() << "\n");
262 } else if (Hi20Op1.isCPI()) {
263 LLVM_DEBUG(dbgs() << " Found lowered constant pool: " << Hi20Op1.getIndex()
264 << "\n");
265 }
266
267 return true;
268}
269
270// Update the offset in Hi20, (Add), Lo12, (Lo20 and Hi12) instructions.
271// Delete the tail instruction and update all the uses to use the
272// output from Last.
273void LoongArchMergeBaseOffsetOpt::foldOffset(
274 MachineInstr &Hi20, MachineInstr &Lo12, MachineInstr *&Lo20,
275 MachineInstr *&Hi12, MachineInstr *&Last, MachineInstr &Tail,
276 int64_t Offset) {
277 // Put the offset back in Hi and the Lo
278 MachineOperand &Lo12Op2 = Lo12.getOperand(2);
279 Hi20.getOperand(1).setOffset(Offset);
280 if (!isPCAddLo(LoongArchII::getDirectFlags(Lo12Op2)))
281 Lo12Op2.setOffset(Offset);
282 if (Lo20 && Hi12) {
283 Lo20->getOperand(2).setOffset(Offset);
284 Hi12->getOperand(2).setOffset(Offset);
285 }
286
287 // For tls-le, offset of the second PseudoAddTPRel instr should also be
288 // updated.
289 MachineInstr *Add = &*MRI->use_instr_begin(Hi20.getOperand(0).getReg());
290 if (Hi20.getOpcode() == LoongArch::LU12I_W)
292
293 // Delete the tail instruction.
294 MachineInstr *Def = Last ? Last : &Lo12;
295 MRI->constrainRegClass(Def->getOperand(0).getReg(),
296 MRI->getRegClass(Tail.getOperand(0).getReg()));
297 MRI->replaceRegWith(Tail.getOperand(0).getReg(), Def->getOperand(0).getReg());
298 Tail.eraseFromParent();
299
300 LLVM_DEBUG(dbgs() << " Merged offset " << Offset << " into base.\n"
301 << " " << Hi20;);
302 if (Hi20.getOpcode() == LoongArch::LU12I_W) {
303 LLVM_DEBUG(dbgs() << " " << *Add;);
304 }
305 LLVM_DEBUG(dbgs() << " " << Lo12;);
306 if (Lo20 && Hi12) {
307 LLVM_DEBUG(dbgs() << " " << *Lo20 << " " << *Hi12;);
308 }
309}
310
311// Detect patterns for large offsets that are passed into an ADD instruction.
312// If the pattern is found, updates the offset in Hi20, (Add), Lo12,
313// (Lo20 and Hi12) instructions and deletes TailAdd and the instructions that
314// produced the offset.
315//
316// (The instructions marked with "!" are not necessarily present)
317//
318// Base address lowering is of the form:
319// 1) pcala:
320// Hi20: pcaxxu12i vreg1, %pc_hi20(s)
321// +--- Lo12: addi.d vreg2, vreg1, %pc_lo12(s)
322// | Lo20: lu32i.d vreg2, %pc64_lo20(s) !
323// +--- Hi12: lu52i.d vreg2, vreg2, %pc64_hi12(s) !
324// |
325// | 2) tls-le:
326// | Hi20: lu12i.w vreg1, %le_hi20_r(s)
327// | Add: add.w/d vreg1, vreg1, r2, %le_add_r(s)
328// +--- Lo12: addi.w/d vreg2, vreg1, %le_lo12_r(s)
329// |
330// | The large offset can be one of the forms:
331// |
332// +-> 1) Offset that has non zero bits in Hi20 and Lo12 bits:
333// | OffsetHi20: lu12i.w vreg3, 4
334// | OffsetLo12: ori voff, vreg3, 188 ------------------+
335// | |
336// +-> 2) Offset that has non zero bits in Hi20 bits only: |
337// | OffsetHi20: lu12i.w voff, 128 ------------------+
338// | |
339// +-> 3) Offset that has non zero bits in Lo20 bits: |
340// | OffsetHi20: lu12i.w vreg3, 121 ! |
341// | OffsetLo12: ori voff, vreg3, 122 ! |
342// | OffsetLo20: lu32i.d voff, 123 ------------------+
343// +-> 4) Offset that has non zero bits in Hi12 bits: |
344// OffsetHi20: lu12i.w vreg3, 121 ! |
345// OffsetLo12: ori voff, vreg3, 122 ! |
346// OffsetLo20: lu32i.d vreg3, 123 ! |
347// OffsetHi12: lu52i.d voff, vrg3, 124 ------------------+
348// |
349// TailAdd: add.d vreg4, vreg2, voff <------------------+
350//
351bool LoongArchMergeBaseOffsetOpt::foldLargeOffset(
352 MachineInstr &Hi20, MachineInstr &Lo12, MachineInstr *&Lo20,
353 MachineInstr *&Hi12, MachineInstr *&Last, MachineInstr &TailAdd,
354 Register GAReg) {
355 assert((TailAdd.getOpcode() == LoongArch::ADD_W ||
356 TailAdd.getOpcode() == LoongArch::ADD_D) &&
357 "Expected ADD instruction!");
358 Register Rs = TailAdd.getOperand(1).getReg();
359 Register Rt = TailAdd.getOperand(2).getReg();
360 Register Reg = Rs == GAReg ? Rt : Rs;
361 SmallVector<MachineInstr *, 4> Instrs;
362 int64_t Offset = 0;
363 int64_t Mask = -1;
364
365 // This can point to one of [ORI, LU12I.W, LU32I.D, LU52I.D]:
366 for (int i = 0; i < 4; i++) {
367 // Handle Reg is R0.
368 if (Reg == LoongArch::R0)
369 break;
370
371 // Can't fold if the register has more than one non-debug use.
372 if (!Reg.isVirtual() || !MRI->hasOneNonDBGUse(Reg))
373 return false;
374
375 MachineInstr *Curr = MRI->getVRegDef(Reg);
376 if (!Curr)
377 break;
378
379 switch (Curr->getOpcode()) {
380 default:
381 // Can't fold if the instruction opcode is unexpected.
382 return false;
383 case LoongArch::ORI: {
384 MachineOperand ImmOp = Curr->getOperand(2);
386 return false;
387 Offset += ImmOp.getImm();
388 Reg = Curr->getOperand(1).getReg();
389 Instrs.push_back(Curr);
390 break;
391 }
392 case LoongArch::LU12I_W: {
393 MachineOperand ImmOp = Curr->getOperand(1);
395 return false;
396 Offset += SignExtend64<32>(ImmOp.getImm() << 12) & Mask;
397 Reg = LoongArch::R0;
398 Instrs.push_back(Curr);
399 break;
400 }
401 case LoongArch::LU32I_D: {
402 MachineOperand ImmOp = Curr->getOperand(2);
403 if (ImmOp.getTargetFlags() != LoongArchII::MO_None || !Lo20)
404 return false;
405 Offset += SignExtend64<52>(ImmOp.getImm() << 32) & Mask;
406 Mask ^= 0x000FFFFF00000000ULL;
407 Reg = Curr->getOperand(1).getReg();
408 Instrs.push_back(Curr);
409 break;
410 }
411 case LoongArch::LU52I_D: {
412 MachineOperand ImmOp = Curr->getOperand(2);
413 if (ImmOp.getTargetFlags() != LoongArchII::MO_None || !Hi12)
414 return false;
415 Offset += ImmOp.getImm() << 52;
416 Mask ^= 0xFFF0000000000000ULL;
417 Reg = Curr->getOperand(1).getReg();
418 Instrs.push_back(Curr);
419 break;
420 }
421 }
422 }
423
424 // Can't fold if the offset is not extracted.
425 if (!Offset)
426 return false;
427
428 foldOffset(Hi20, Lo12, Lo20, Hi12, Last, TailAdd, Offset);
429 LLVM_DEBUG(dbgs() << " Offset Instrs:\n");
430 for (auto I : Instrs) {
431 LLVM_DEBUG(dbgs() << " " << *I);
432 // The offset instruction is about to be erased, so its debug users can no
433 // longer be described.
434 MRI->markUsesInDebugValueAsUndef(I->getOperand(0).getReg());
435 I->eraseFromParent();
436 }
437
438 return true;
439}
440
441bool LoongArchMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &Hi20,
442 MachineInstr &Lo12,
443 MachineInstr *&Lo20,
444 MachineInstr *&Hi12,
445 MachineInstr *&Last) {
446 Register DestReg =
447 Last ? Last->getOperand(0).getReg() : Lo12.getOperand(0).getReg();
448
449 // Look for arithmetic instructions we can get an offset from.
450 // We might be able to remove the arithmetic instructions by folding the
451 // offset into the PCAXXU12I+(ADDI/ADDI+LU32I+LU52I) or
452 // LU12I_W+PseudoAddTPRel+ADDI.
453 if (!MRI->hasOneUse(DestReg))
454 return false;
455
456 // DestReg has only one use.
457 MachineInstr &Tail = *MRI->use_instr_begin(DestReg);
458 switch (Tail.getOpcode()) {
459 default:
460 LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:"
461 << Tail);
462 break;
463 case LoongArch::ADDI_W:
464 if (ST->is64Bit())
465 return false;
466 [[fallthrough]];
467 case LoongArch::ADDI_D:
468 case LoongArch::ADDU16I_D: {
469 // Offset is simply an immediate operand.
470 int64_t Offset = Tail.getOperand(2).getImm();
471 if (Tail.getOpcode() == LoongArch::ADDU16I_D)
473
474 // We might have two ADDIs in a row.
475 Register TailDestReg = Tail.getOperand(0).getReg();
476 if (MRI->hasOneUse(TailDestReg)) {
477 MachineInstr &TailTail = *MRI->use_instr_begin(TailDestReg);
478 if (ST->is64Bit() && TailTail.getOpcode() == LoongArch::ADDI_W)
479 return false;
480 if (TailTail.getOpcode() == LoongArch::ADDI_W ||
481 TailTail.getOpcode() == LoongArch::ADDI_D) {
482 Offset += TailTail.getOperand(2).getImm();
483 LLVM_DEBUG(dbgs() << " Offset Instrs: " << Tail << TailTail);
484 foldOffset(Hi20, Lo12, Lo20, Hi12, Last, TailTail, Offset);
485 Tail.eraseFromParent();
486 return true;
487 }
488 }
489
490 LLVM_DEBUG(dbgs() << " Offset Instr: " << Tail);
491 foldOffset(Hi20, Lo12, Lo20, Hi12, Last, Tail, Offset);
492 return true;
493 }
494 case LoongArch::ADD_W:
495 if (ST->is64Bit())
496 return false;
497 [[fallthrough]];
498 case LoongArch::ADD_D:
499 // The offset is too large to fit in the immediate field of ADDI.
500 return foldLargeOffset(Hi20, Lo12, Lo20, Hi12, Last, Tail, DestReg);
501 break;
502 }
503
504 return false;
505}
506
507// Memory access opcode mapping for transforms.
508static unsigned getNewOpc(unsigned Op, bool isLarge) {
509 switch (Op) {
510 case LoongArch::LD_B:
511 return isLarge ? LoongArch::LDX_B : LoongArch::LD_B;
512 case LoongArch::LD_H:
513 return isLarge ? LoongArch::LDX_H : LoongArch::LD_H;
514 case LoongArch::LD_W:
515 case LoongArch::LDPTR_W:
516 return isLarge ? LoongArch::LDX_W : LoongArch::LD_W;
517 case LoongArch::LD_D:
518 case LoongArch::LDPTR_D:
519 return isLarge ? LoongArch::LDX_D : LoongArch::LD_D;
520 case LoongArch::LD_BU:
521 return isLarge ? LoongArch::LDX_BU : LoongArch::LD_BU;
522 case LoongArch::LD_HU:
523 return isLarge ? LoongArch::LDX_HU : LoongArch::LD_HU;
524 case LoongArch::LD_WU:
525 return isLarge ? LoongArch::LDX_WU : LoongArch::LD_WU;
526 case LoongArch::FLD_S:
527 return isLarge ? LoongArch::FLDX_S : LoongArch::FLD_S;
528 case LoongArch::FLD_D:
529 return isLarge ? LoongArch::FLDX_D : LoongArch::FLD_D;
530 case LoongArch::VLD:
531 return isLarge ? LoongArch::VLDX : LoongArch::VLD;
532 case LoongArch::XVLD:
533 return isLarge ? LoongArch::XVLDX : LoongArch::XVLD;
534 case LoongArch::VLDREPL_B:
535 return LoongArch::VLDREPL_B;
536 case LoongArch::XVLDREPL_B:
537 return LoongArch::XVLDREPL_B;
538 case LoongArch::ST_B:
539 return isLarge ? LoongArch::STX_B : LoongArch::ST_B;
540 case LoongArch::ST_H:
541 return isLarge ? LoongArch::STX_H : LoongArch::ST_H;
542 case LoongArch::ST_W:
543 case LoongArch::STPTR_W:
544 return isLarge ? LoongArch::STX_W : LoongArch::ST_W;
545 case LoongArch::ST_D:
546 case LoongArch::STPTR_D:
547 return isLarge ? LoongArch::STX_D : LoongArch::ST_D;
548 case LoongArch::FST_S:
549 return isLarge ? LoongArch::FSTX_S : LoongArch::FST_S;
550 case LoongArch::FST_D:
551 return isLarge ? LoongArch::FSTX_D : LoongArch::FST_D;
552 case LoongArch::VST:
553 return isLarge ? LoongArch::VSTX : LoongArch::VST;
554 case LoongArch::XVST:
555 return isLarge ? LoongArch::XVSTX : LoongArch::XVST;
556 default:
557 llvm_unreachable("Unexpected opcode for replacement");
558 }
559}
560
561bool LoongArchMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi20,
562 MachineInstr &Lo12,
563 MachineInstr *&Lo20,
564 MachineInstr *&Hi12,
565 MachineInstr *&Last) {
566 Register DestReg =
567 Last ? Last->getOperand(0).getReg() : Lo12.getOperand(0).getReg();
568
569 // If all the uses are memory ops with the same offset, we can transform:
570 //
571 // 1. (small/medium):
572 // 1.1. pcala
573 // pcaxxu12i vreg1, %pc_hi20(s)
574 // addi.d vreg2, vreg1, %pc_lo12(s)
575 // ld.w vreg3, 8(vreg2)
576 //
577 // =>
578 //
579 // pcalxx12i vreg1, %pc_hi20(s+8)
580 // ld.w vreg3, vreg1, %pc_lo12(s+8)(vreg1)
581 //
582 // 1.2. tls-le
583 // lu12i.w vreg1, %le_hi20_r(s)
584 // add.w/d vreg2, vreg1, r2, %le_add_r(s)
585 // addi.w/d vreg3, vreg2, %le_lo12_r(s)
586 // ld.w vreg4, 8(vreg3)
587 //
588 // =>
589 //
590 // lu12i.w vreg1, %le_hi20_r(s+8)
591 // add.w/d vreg2, vreg1, r2, %le_add_r(s+8)
592 // ld.w vreg4, vreg2, %le_lo12_r(s+8)(vreg2)
593 //
594 // 2. (large):
595 // pcalau12i vreg1, %pc_hi20(s)
596 // addi.d vreg2, $zero, %pc_lo12(s)
597 // lu32i.d vreg3, vreg2, %pc64_lo20(s)
598 // lu52i.d vreg4, vreg3, %pc64_hi12(s)
599 // add.d vreg5, vreg4, vreg1
600 // ld.w vreg6, 8(vreg5)
601 //
602 // =>
603 //
604 // pcalau12i vreg1, %pc_hi20(s+8)
605 // addi.d vreg2, $zero, %pc_lo12(s+8)
606 // lu32i.d vreg3, vreg2, %pc64_lo20(s+8)
607 // lu52i.d vreg4, vreg3, %pc64_hi12(s+8)
608 // ldx.w vreg6, vreg4, vreg1
609
610 std::optional<int64_t> CommonOffset;
611 DenseMap<const MachineInstr *, SmallVector<unsigned>>
612 InlineAsmMemoryOpIndexesMap;
613 for (const MachineInstr &UseMI : MRI->use_nodbg_instructions(DestReg)) {
614 switch (UseMI.getOpcode()) {
615 default:
616 LLVM_DEBUG(dbgs() << "Not a load or store instruction: " << UseMI);
617 return false;
618 case LoongArch::VLDREPL_B:
619 case LoongArch::XVLDREPL_B:
620 // We can't do this for large pattern.
621 if (Last)
622 return false;
623 [[fallthrough]];
624 case LoongArch::LD_B:
625 case LoongArch::LD_H:
626 case LoongArch::LD_W:
627 case LoongArch::LD_D:
628 case LoongArch::LD_BU:
629 case LoongArch::LD_HU:
630 case LoongArch::LD_WU:
631 case LoongArch::LDPTR_W:
632 case LoongArch::LDPTR_D:
633 case LoongArch::FLD_S:
634 case LoongArch::FLD_D:
635 case LoongArch::VLD:
636 case LoongArch::XVLD:
637 case LoongArch::ST_B:
638 case LoongArch::ST_H:
639 case LoongArch::ST_W:
640 case LoongArch::ST_D:
641 case LoongArch::STPTR_W:
642 case LoongArch::STPTR_D:
643 case LoongArch::FST_S:
644 case LoongArch::FST_D:
645 case LoongArch::VST:
646 case LoongArch::XVST: {
647 if (UseMI.getOperand(1).isFI())
648 return false;
649 // Register defined by Lo should not be the value register.
650 if (DestReg == UseMI.getOperand(0).getReg())
651 return false;
652 assert(DestReg == UseMI.getOperand(1).getReg() &&
653 "Expected base address use");
654 // All load/store instructions must use the same offset.
655 int64_t Offset = UseMI.getOperand(2).getImm();
656 if (CommonOffset && Offset != CommonOffset)
657 return false;
658 CommonOffset = Offset;
659 break;
660 }
661 case LoongArch::INLINEASM:
662 case LoongArch::INLINEASM_BR: {
663 // We can't do this for large pattern.
664 if (Last)
665 return false;
666 SmallVector<unsigned> InlineAsmMemoryOpIndexes;
667 unsigned NumOps = 0;
668 for (unsigned I = InlineAsm::MIOp_FirstOperand;
669 I < UseMI.getNumOperands(); I += 1 + NumOps) {
670 const MachineOperand &FlagsMO = UseMI.getOperand(I);
671 // Should be an imm.
672 if (!FlagsMO.isImm())
673 continue;
674
675 const InlineAsm::Flag Flags(FlagsMO.getImm());
676 NumOps = Flags.getNumOperandRegisters();
677
678 // Memory constraints have two operands.
679 if (NumOps != 2 || !Flags.isMemKind()) {
680 // If the register is used by something other than a memory contraint,
681 // we should not fold.
682 for (unsigned J = 0; J < NumOps; ++J) {
683 const MachineOperand &MO = UseMI.getOperand(I + 1 + J);
684 if (MO.isReg() && MO.getReg() == DestReg)
685 return false;
686 }
687 continue;
688 }
689
690 // We can only do this for constraint m.
691 if (Flags.getMemoryConstraintID() != InlineAsm::ConstraintCode::m)
692 return false;
693
694 const MachineOperand &AddrMO = UseMI.getOperand(I + 1);
695 if (!AddrMO.isReg() || AddrMO.getReg() != DestReg)
696 continue;
697
698 const MachineOperand &OffsetMO = UseMI.getOperand(I + 2);
699 if (!OffsetMO.isImm())
700 continue;
701
702 // All inline asm memory operands must use the same offset.
703 int64_t Offset = OffsetMO.getImm();
704 if (CommonOffset && Offset != CommonOffset)
705 return false;
706 CommonOffset = Offset;
707 InlineAsmMemoryOpIndexes.push_back(I + 1);
708 }
709 InlineAsmMemoryOpIndexesMap.insert(
710 std::make_pair(&UseMI, InlineAsmMemoryOpIndexes));
711 break;
712 }
713 }
714 }
715
716 if (!CommonOffset)
717 return false;
718
719 // We found a common offset.
720 // Update the offsets in global address lowering.
721 // We may have already folded some arithmetic so we need to add to any
722 // existing offset.
723 int64_t NewOffset = Hi20.getOperand(1).getOffset() + *CommonOffset;
724 // LA32 ignores the upper 32 bits.
725 if (!ST->is64Bit())
726 NewOffset = SignExtend64<32>(NewOffset);
727 // We can only fold simm32 offsets.
728 if (!isInt<32>(NewOffset))
729 return false;
730
731 // Folding changes the value represented by DestReg, so its debug users can
732 // no longer describe the same address.
733 MRI->markUsesInDebugValueAsUndef(DestReg);
734
735 // If optimized by this pass successfully, MO_RELAX bitmask target-flag should
736 // be removed from the pcala code sequence. Code sequence of tls-le can still
737 // be relaxed after being optimized.
738 //
739 // For example:
740 // pcaxxu12i $a0, %pc_hi20(symbol)
741 // addi.d $a0, $a0, %pc_lo12(symbol)
742 // ld.w $a0, $a0, 0
743 //
744 // =>
745 //
746 // pcaxxu12i $a0, %pc_hi20(symbol)
747 // ld.w $a0, $a0, %pc_lo12(symbol)
748 //
749 // Code sequence optimized before can be relax by linker. But after being
750 // optimized, it cannot be relaxed any more. So MO_RELAX flag should not be
751 // carried by them.
752 Hi20.getOperand(1).setOffset(NewOffset);
753 MachineOperand &ImmOp = Lo12.getOperand(2);
754 if (!isPCAddLo(LoongArchII::getDirectFlags(ImmOp)))
755 ImmOp.setOffset(NewOffset);
756 if (Lo20 && Hi12) {
757 Lo20->getOperand(2).setOffset(NewOffset);
758 Hi12->getOperand(2).setOffset(NewOffset);
759 }
760 if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
761 Hi20.getOpcode() == LoongArch::PCALAU12I) {
765 } else if (Hi20.getOpcode() == LoongArch::LU12I_W) {
766 MachineInstr *Add = &*MRI->use_instr_begin(Hi20.getOperand(0).getReg());
767 Add->getOperand(3).setOffset(NewOffset);
768 }
769
770 // Update the immediate in the load/store instructions to add the offset.
771 const LoongArchInstrInfo &TII = *ST->getInstrInfo();
772 for (MachineInstr &UseMI :
774 if (UseMI.getOpcode() == LoongArch::INLINEASM ||
775 UseMI.getOpcode() == LoongArch::INLINEASM_BR) {
776 auto &InlineAsmMemoryOpIndexes = InlineAsmMemoryOpIndexesMap[&UseMI];
777 for (unsigned I : InlineAsmMemoryOpIndexes) {
778 MachineOperand &MO = UseMI.getOperand(I + 1);
779 switch (ImmOp.getType()) {
781 MO.ChangeToGA(ImmOp.getGlobal(), ImmOp.getOffset(),
783 break;
785 MO.ChangeToMCSymbol(ImmOp.getMCSymbol(),
787 MO.setOffset(ImmOp.getOffset());
788 break;
790 MO.ChangeToBA(ImmOp.getBlockAddress(), ImmOp.getOffset(),
792 break;
794 MO.ChangeToCPI(ImmOp.getIndex(), ImmOp.getOffset(),
796 break;
797 default:
798 report_fatal_error("unsupported machine operand type");
799 break;
800 }
801 }
802 } else {
803 UseMI.setDesc(TII.get(getNewOpc(UseMI.getOpcode(), Last)));
804 if (Last) {
805 UseMI.removeOperand(2);
806 UseMI.removeOperand(1);
807 UseMI.addOperand(Last->getOperand(1));
808 UseMI.addOperand(Last->getOperand(2));
809 UseMI.getOperand(1).setIsKill(false);
810 UseMI.getOperand(2).setIsKill(false);
811 } else {
812 UseMI.removeOperand(2);
813 UseMI.addOperand(ImmOp);
814 }
815 }
816 }
817
818 if (Last) {
819 Last->eraseFromParent();
820 return true;
821 }
822
823 if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
824 Hi20.getOpcode() == LoongArch::PCALAU12I) {
825 MRI->replaceRegWith(Lo12.getOperand(0).getReg(),
826 Hi20.getOperand(0).getReg());
827 } else if (Hi20.getOpcode() == LoongArch::LU12I_W) {
828 MachineInstr *Add = &*MRI->use_instr_begin(Hi20.getOperand(0).getReg());
829 MRI->replaceRegWith(Lo12.getOperand(0).getReg(),
830 Add->getOperand(0).getReg());
831 }
832 Lo12.eraseFromParent();
833 return true;
834}
835
836bool LoongArchMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
837 if (skipFunction(Fn.getFunction()))
838 return false;
839
840 ST = &Fn.getSubtarget<LoongArchSubtarget>();
841
842 bool MadeChange = false;
843 MRI = &Fn.getRegInfo();
844 for (MachineBasicBlock &MBB : Fn) {
845 LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
846 for (MachineInstr &Hi20 : MBB) {
847 MachineInstr *Lo12 = nullptr;
848 MachineInstr *Lo20 = nullptr;
849 MachineInstr *Hi12 = nullptr;
850 MachineInstr *Last = nullptr;
851 if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
852 Hi20.getOpcode() == LoongArch::PCALAU12I) {
853 // Detect foldable pcala code sequence in small/medium/large code model.
854 if (!detectFoldable(Hi20, Lo12, Lo20, Hi12, Last))
855 continue;
856 } else if (Hi20.getOpcode() == LoongArch::LU12I_W) {
857 MachineInstr *Add = nullptr;
858 // Detect foldable tls-le code sequence in small/medium code model.
859 if (!detectFoldable(Hi20, Add, Lo12))
860 continue;
861 } else {
862 continue;
863 }
864 // For tls-le, we do not pass the second PseudoAddTPRel instr in order to
865 // reuse the existing hooks and the last three paramaters should always be
866 // nullptr.
867 MadeChange |= detectAndFoldOffset(Hi20, *Lo12, Lo20, Hi12, Last);
868 MadeChange |= foldIntoMemoryOps(Hi20, *Lo12, Lo20, Hi12, Last);
869 }
870 }
871
872 return MadeChange;
873}
874
875/// Returns an instance of the Merge Base Offset Optimization pass.
877 return new LoongArchMergeBaseOffsetOpt();
878}
MachineInstrBuilder & UseMI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
always inline
#define DEBUG_TYPE
const HexagonInstrInfo * TII
const size_t AbstractManglingParser< Derived, Alloc >::NumOps
static unsigned getNewOpc(unsigned Op, bool isLarge)
#define LoongArch_MERGE_BASE_OFFSET_NAME
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
#define LLVM_DEBUG(...)
Definition Debug.h:119
Represent the analysis usage information of a pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Definition DenseMap.h:827
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
const LoongArchInstrInfo * getInstrInfo() const override
LLVM_ABI StringRef getName() const
Return the name of the corresponding LLVM basic block, or an empty string.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
Properties which a MachineFunction may have at a given point in time.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
Register getReg(unsigned Idx) const
Get the register for the operand index.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
const GlobalValue * getGlobal() const
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
bool isCPI() const
isCPI - Tests if this is a MO_ConstantPoolIndex operand.
LLVM_ABI void ChangeToMCSymbol(MCSymbol *Sym, unsigned TargetFlags=0)
ChangeToMCSymbol - Replace this operand with a new MC symbol operand.
bool isImm() const
isImm - Tests if this is a MO_Immediate operand.
LLVM_ABI void ChangeToGA(const GlobalValue *GV, int64_t Offset, unsigned TargetFlags=0)
ChangeToGA - Replace this operand with a new global address operand.
LLVM_ABI void ChangeToBA(const BlockAddress *BA, int64_t Offset, unsigned TargetFlags=0)
ChangeToBA - Replace this operand with a new block address operand.
const BlockAddress * getBlockAddress() const
void setOffset(int64_t Offset)
unsigned getTargetFlags() const
bool isGlobal() const
isGlobal - Tests if this is a MO_GlobalAddress operand.
MachineOperandType getType() const
getType - Returns the MachineOperandType for this operand.
bool isBlockAddress() const
isBlockAddress - Tests if this is a MO_BlockAddress operand.
Register getReg() const
getReg - Returns the register number.
void setTargetFlags(unsigned F)
LLVM_ABI void ChangeToCPI(unsigned Idx, int Offset, unsigned TargetFlags=0)
ChangeToCPI - Replace this operand with a new constant pool index operand.
MCSymbol * getMCSymbol() const
@ MO_ConstantPoolIndex
Address of indexed Constant in Constant Pool.
@ MO_MCSymbol
MCSymbol reference (for debug/eh info)
@ MO_GlobalAddress
Address of a global value.
@ MO_BlockAddress
Address of a basic block.
int64_t getOffset() const
Return the offset from the symbol in this operand.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool hasOneNonDBGUse(Register RegNo) const
hasOneNonDBGUse - Return true if there is exactly one non-Debug use of the specified register.
LLVM_ABI void markUsesInDebugValueAsUndef(Register Reg) const
markUsesInDebugValueAsUndef - Mark every DBG_VALUE referencing the specified register as undefined wh...
const TargetRegisterClass * getRegClass(Register Reg) const
Return the register class of the specified virtual register.
LLVM_ABI LLVM_READONLY MachineInstr * getVRegDef(Register Reg) const
getVRegDef - Return the machine instr that defines the specified virtual register or null if none is ...
use_instr_iterator use_instr_begin(Register RegNo) const
bool hasOneUse(Register RegNo) const
hasOneUse - Return true if there is exactly one instruction using the specified register.
iterator_range< use_instr_nodbg_iterator > use_nodbg_instructions(Register Reg) const
LLVM_ABI const TargetRegisterClass * constrainRegClass(Register Reg, const TargetRegisterClass *RC, unsigned MinNumRegs=0)
constrainRegClass - Constrain the register class of the specified virtual register to be a common sub...
LLVM_ABI void replaceRegWith(Register FromReg, Register ToReg)
replaceRegWith - Replace all instances of FromReg with ToReg in the machine function.
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
Definition Register.h:79
void push_back(const T &Elt)
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
@ Tail
Attemps to make calls as fast as possible while guaranteeing that tail call optimization can always b...
Definition CallingConv.h:76
static unsigned getDirectFlags(const MachineOperand &MO)
NodeAddr< DefNode * > Def
Definition RDFGraph.h:384
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:166
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
@ Add
Sum of integers.
DWARFExpression::Operation Op
constexpr int64_t SignExtend64(uint64_t x)
Sign-extend the number in the bottom B bits of X to a 64-bit integer.
Definition MathExtras.h:567
FunctionPass * createLoongArchMergeBaseOffsetOptPass()
Returns an instance of the Merge Base Offset Optimization pass.