LLVM 24.0.0git
X86AvoidStoreForwardingBlocks.cpp
Go to the documentation of this file.
1//===- X86AvoidStoreForwardingBlocks.cpp - Avoid HW Store Forward Block ---===//
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// If a load follows a store and reloads data that the store has written to
10// memory, Intel microarchitectures can in many cases forward the data directly
11// from the store to the load, This "store forwarding" saves cycles by enabling
12// the load to directly obtain the data instead of accessing the data from
13// cache or memory.
14// A "store forward block" occurs in cases that a store cannot be forwarded to
15// the load. The most typical case of store forward block on Intel Core
16// microarchitecture that a small store cannot be forwarded to a large load.
17// The estimated penalty for a store forward block is ~13 cycles.
18//
19// This pass tries to recognize and handle cases where "store forward block"
20// is created by the compiler when lowering memcpy calls to a sequence
21// of a load and a store.
22//
23// The pass currently only handles cases where memcpy is lowered to
24// XMM/YMM registers, it tries to break the memcpy into smaller copies.
25// breaking the memcpy should be possible since there is no atomicity
26// guarantee for loads and stores to XMM/YMM.
27//
28// It could be better for performance to solve the problem by loading
29// to XMM/YMM then inserting the partial store before storing back from XMM/YMM
30// to memory, but this will result in a more conservative optimization since it
31// requires we prove that all memory accesses between the blocking store and the
32// load must alias/don't alias before we can move the store, whereas the
33// transformation done here is correct regardless to other memory accesses.
34//===----------------------------------------------------------------------===//
35
36#include "X86.h"
37#include "X86InstrInfo.h"
38#include "X86Subtarget.h"
48#include "llvm/IR/DebugLoc.h"
49#include "llvm/IR/Function.h"
51#include "llvm/MC/MCInstrDesc.h"
52
53using namespace llvm;
54
55#define DEBUG_TYPE "x86-avoid-sfb"
56
57namespace {
58
59using DisplacementSizeMap = std::map<int64_t, unsigned>;
60
61class X86AvoidSFBImpl {
62public:
63 X86AvoidSFBImpl(AliasAnalysis *AA) : AA(AA) {};
64 bool runOnMachineFunction(MachineFunction &MF);
65
66private:
67 MachineRegisterInfo *MRI = nullptr;
68 const X86InstrInfo *TII = nullptr;
69 const X86RegisterInfo *TRI = nullptr;
71 BlockedLoadsStoresPairs;
73 AliasAnalysis *AA = nullptr;
74
75 /// Returns couples of Load then Store to memory which look
76 /// like a memcpy.
77 void findPotentiallylBlockedCopies(MachineFunction &MF);
78 /// Break the memcpy's load and store into smaller copies
79 /// such that each memory load that was blocked by a smaller store
80 /// would now be copied separately.
81 void breakBlockedCopies(MachineInstr *LoadInst, MachineInstr *StoreInst,
82 const DisplacementSizeMap &BlockingStoresDispSizeMap);
83 /// Break a copy of size Size to smaller copies.
84 void buildCopies(int Size, MachineInstr *LoadInst, int64_t LdDispImm,
85 MachineInstr *StoreInst, int64_t StDispImm, int64_t Offset);
86
87 void buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode, int64_t LoadDisp,
88 MachineInstr *StoreInst, unsigned NStoreOpcode,
89 int64_t StoreDisp, unsigned Size, int64_t Offset);
90
91 bool alias(const MachineMemOperand &Op1, const MachineMemOperand &Op2) const;
92
93 unsigned getRegSizeInBytes(MachineInstr *Inst);
94};
95
96class X86AvoidSFBLegacy : public MachineFunctionPass {
97public:
98 static char ID;
99 X86AvoidSFBLegacy() : MachineFunctionPass(ID) {}
100
101 StringRef getPassName() const override {
102 return "X86 Avoid Store Forwarding Blocks";
103 }
104
105 bool runOnMachineFunction(MachineFunction &MF) override;
106
107 void getAnalysisUsage(AnalysisUsage &AU) const override {
111 }
112};
113
114} // end anonymous namespace
115
116char X86AvoidSFBLegacy::ID = 0;
117
118INITIALIZE_PASS_BEGIN(X86AvoidSFBLegacy, DEBUG_TYPE, "Machine code sinking",
119 false, false)
121INITIALIZE_PASS_END(X86AvoidSFBLegacy, DEBUG_TYPE, "Machine code sinking",
123
125 return new X86AvoidSFBLegacy();
126}
127
128static bool isXMMLoadOpcode(unsigned Opcode) {
129 return Opcode == X86::MOVUPSrm || Opcode == X86::MOVAPSrm ||
130 Opcode == X86::VMOVUPSrm || Opcode == X86::VMOVAPSrm ||
131 Opcode == X86::VMOVUPDrm || Opcode == X86::VMOVAPDrm ||
132 Opcode == X86::VMOVDQUrm || Opcode == X86::VMOVDQArm ||
133 Opcode == X86::VMOVUPSZ128rm || Opcode == X86::VMOVAPSZ128rm ||
134 Opcode == X86::VMOVUPDZ128rm || Opcode == X86::VMOVAPDZ128rm ||
135 Opcode == X86::VMOVDQU64Z128rm || Opcode == X86::VMOVDQA64Z128rm ||
136 Opcode == X86::VMOVDQU32Z128rm || Opcode == X86::VMOVDQA32Z128rm;
137}
138static bool isYMMLoadOpcode(unsigned Opcode) {
139 return Opcode == X86::VMOVUPSYrm || Opcode == X86::VMOVAPSYrm ||
140 Opcode == X86::VMOVUPDYrm || Opcode == X86::VMOVAPDYrm ||
141 Opcode == X86::VMOVDQUYrm || Opcode == X86::VMOVDQAYrm ||
142 Opcode == X86::VMOVUPSZ256rm || Opcode == X86::VMOVAPSZ256rm ||
143 Opcode == X86::VMOVUPDZ256rm || Opcode == X86::VMOVAPDZ256rm ||
144 Opcode == X86::VMOVDQU64Z256rm || Opcode == X86::VMOVDQA64Z256rm ||
145 Opcode == X86::VMOVDQU32Z256rm || Opcode == X86::VMOVDQA32Z256rm;
146}
147
148static bool isPotentialBlockedMemCpyLd(unsigned Opcode) {
149 return isXMMLoadOpcode(Opcode) || isYMMLoadOpcode(Opcode);
150}
151
152static bool isPotentialBlockedMemCpyPair(unsigned LdOpcode, unsigned StOpcode) {
153 switch (LdOpcode) {
154 case X86::MOVUPSrm:
155 case X86::MOVAPSrm:
156 return StOpcode == X86::MOVUPSmr || StOpcode == X86::MOVAPSmr;
157 case X86::VMOVUPSrm:
158 case X86::VMOVAPSrm:
159 return StOpcode == X86::VMOVUPSmr || StOpcode == X86::VMOVAPSmr;
160 case X86::VMOVUPDrm:
161 case X86::VMOVAPDrm:
162 return StOpcode == X86::VMOVUPDmr || StOpcode == X86::VMOVAPDmr;
163 case X86::VMOVDQUrm:
164 case X86::VMOVDQArm:
165 return StOpcode == X86::VMOVDQUmr || StOpcode == X86::VMOVDQAmr;
166 case X86::VMOVUPSZ128rm:
167 case X86::VMOVAPSZ128rm:
168 return StOpcode == X86::VMOVUPSZ128mr || StOpcode == X86::VMOVAPSZ128mr;
169 case X86::VMOVUPDZ128rm:
170 case X86::VMOVAPDZ128rm:
171 return StOpcode == X86::VMOVUPDZ128mr || StOpcode == X86::VMOVAPDZ128mr;
172 case X86::VMOVUPSYrm:
173 case X86::VMOVAPSYrm:
174 return StOpcode == X86::VMOVUPSYmr || StOpcode == X86::VMOVAPSYmr;
175 case X86::VMOVUPDYrm:
176 case X86::VMOVAPDYrm:
177 return StOpcode == X86::VMOVUPDYmr || StOpcode == X86::VMOVAPDYmr;
178 case X86::VMOVDQUYrm:
179 case X86::VMOVDQAYrm:
180 return StOpcode == X86::VMOVDQUYmr || StOpcode == X86::VMOVDQAYmr;
181 case X86::VMOVUPSZ256rm:
182 case X86::VMOVAPSZ256rm:
183 return StOpcode == X86::VMOVUPSZ256mr || StOpcode == X86::VMOVAPSZ256mr;
184 case X86::VMOVUPDZ256rm:
185 case X86::VMOVAPDZ256rm:
186 return StOpcode == X86::VMOVUPDZ256mr || StOpcode == X86::VMOVAPDZ256mr;
187 case X86::VMOVDQU64Z128rm:
188 case X86::VMOVDQA64Z128rm:
189 return StOpcode == X86::VMOVDQU64Z128mr || StOpcode == X86::VMOVDQA64Z128mr;
190 case X86::VMOVDQU32Z128rm:
191 case X86::VMOVDQA32Z128rm:
192 return StOpcode == X86::VMOVDQU32Z128mr || StOpcode == X86::VMOVDQA32Z128mr;
193 case X86::VMOVDQU64Z256rm:
194 case X86::VMOVDQA64Z256rm:
195 return StOpcode == X86::VMOVDQU64Z256mr || StOpcode == X86::VMOVDQA64Z256mr;
196 case X86::VMOVDQU32Z256rm:
197 case X86::VMOVDQA32Z256rm:
198 return StOpcode == X86::VMOVDQU32Z256mr || StOpcode == X86::VMOVDQA32Z256mr;
199 default:
200 return false;
201 }
202}
203
204static bool isPotentialBlockingStoreInst(unsigned Opcode, unsigned LoadOpcode) {
205 bool PBlock = false;
206 PBlock |= Opcode == X86::MOV64mr || Opcode == X86::MOV64mi32 ||
207 Opcode == X86::MOV32mr || Opcode == X86::MOV32mi ||
208 Opcode == X86::MOV16mr || Opcode == X86::MOV16mi ||
209 Opcode == X86::MOV8mr || Opcode == X86::MOV8mi;
210 if (isYMMLoadOpcode(LoadOpcode))
211 PBlock |= Opcode == X86::VMOVUPSmr || Opcode == X86::VMOVAPSmr ||
212 Opcode == X86::VMOVUPDmr || Opcode == X86::VMOVAPDmr ||
213 Opcode == X86::VMOVDQUmr || Opcode == X86::VMOVDQAmr ||
214 Opcode == X86::VMOVUPSZ128mr || Opcode == X86::VMOVAPSZ128mr ||
215 Opcode == X86::VMOVUPDZ128mr || Opcode == X86::VMOVAPDZ128mr ||
216 Opcode == X86::VMOVDQU64Z128mr ||
217 Opcode == X86::VMOVDQA64Z128mr ||
218 Opcode == X86::VMOVDQU32Z128mr || Opcode == X86::VMOVDQA32Z128mr;
219 return PBlock;
220}
221
222static const int MOV128SZ = 16;
223static const int MOV64SZ = 8;
224static const int MOV32SZ = 4;
225static const int MOV16SZ = 2;
226static const int MOV8SZ = 1;
227
228static unsigned getYMMtoXMMLoadOpcode(unsigned LoadOpcode) {
229 switch (LoadOpcode) {
230 case X86::VMOVUPSYrm:
231 case X86::VMOVAPSYrm:
232 return X86::VMOVUPSrm;
233 case X86::VMOVUPDYrm:
234 case X86::VMOVAPDYrm:
235 return X86::VMOVUPDrm;
236 case X86::VMOVDQUYrm:
237 case X86::VMOVDQAYrm:
238 return X86::VMOVDQUrm;
239 case X86::VMOVUPSZ256rm:
240 case X86::VMOVAPSZ256rm:
241 return X86::VMOVUPSZ128rm;
242 case X86::VMOVUPDZ256rm:
243 case X86::VMOVAPDZ256rm:
244 return X86::VMOVUPDZ128rm;
245 case X86::VMOVDQU64Z256rm:
246 case X86::VMOVDQA64Z256rm:
247 return X86::VMOVDQU64Z128rm;
248 case X86::VMOVDQU32Z256rm:
249 case X86::VMOVDQA32Z256rm:
250 return X86::VMOVDQU32Z128rm;
251 default:
252 llvm_unreachable("Unexpected Load Instruction Opcode");
253 }
254 return 0;
255}
256
257static unsigned getYMMtoXMMStoreOpcode(unsigned StoreOpcode) {
258 switch (StoreOpcode) {
259 case X86::VMOVUPSYmr:
260 case X86::VMOVAPSYmr:
261 return X86::VMOVUPSmr;
262 case X86::VMOVUPDYmr:
263 case X86::VMOVAPDYmr:
264 return X86::VMOVUPDmr;
265 case X86::VMOVDQUYmr:
266 case X86::VMOVDQAYmr:
267 return X86::VMOVDQUmr;
268 case X86::VMOVUPSZ256mr:
269 case X86::VMOVAPSZ256mr:
270 return X86::VMOVUPSZ128mr;
271 case X86::VMOVUPDZ256mr:
272 case X86::VMOVAPDZ256mr:
273 return X86::VMOVUPDZ128mr;
274 case X86::VMOVDQU64Z256mr:
275 case X86::VMOVDQA64Z256mr:
276 return X86::VMOVDQU64Z128mr;
277 case X86::VMOVDQU32Z256mr:
278 case X86::VMOVDQA32Z256mr:
279 return X86::VMOVDQU32Z128mr;
280 default:
281 llvm_unreachable("Unexpected Load Instruction Opcode");
282 }
283 return 0;
284}
285
286static int getAddrOffset(const MachineInstr *MI) {
287 int AddrOffset = X86II::getMemoryOperandIdx(MI->getDesc());
288 assert(AddrOffset >= 0 && "Expected a memory operand");
289 return AddrOffset;
290}
291
293 int AddrOffset = getAddrOffset(MI);
294 return MI->getOperand(AddrOffset + X86::AddrBaseReg);
295}
296
298 int AddrOffset = getAddrOffset(MI);
299 return MI->getOperand(AddrOffset + X86::AddrDisp);
300}
301
302// Relevant addressing modes contain only base register and immediate
303// displacement or frameindex and immediate displacement.
304// TODO: Consider expanding to other addressing modes in the future
306 int AddrOffset = getAddrOffset(MI);
308 const MachineOperand &Disp = getDispOperand(MI);
309 const MachineOperand &Scale = MI->getOperand(AddrOffset + X86::AddrScaleAmt);
310 const MachineOperand &Index = MI->getOperand(AddrOffset + X86::AddrIndexReg);
311 const MachineOperand &Segment = MI->getOperand(AddrOffset + X86::AddrSegmentReg);
312
313 if (!((Base.isReg() && Base.getReg() != X86::NoRegister) || Base.isFI()))
314 return false;
315 if (!Disp.isImm())
316 return false;
317 if (Scale.getImm() != 1)
318 return false;
319 if (!(Index.isReg() && Index.getReg() == X86::NoRegister))
320 return false;
321 if (!(Segment.isReg() && Segment.getReg() == X86::NoRegister))
322 return false;
323 return true;
324}
325
326// Collect potentially blocking stores.
327// Limit the number of instructions backwards we want to inspect
328// since the effect of store block won't be visible if the store
329// and load instructions have enough instructions in between to
330// keep the core busy.
332findPotentialBlockers(MachineInstr *LoadInst, unsigned InspectionLimit) {
333 SmallVector<MachineInstr *, 2> PotentialBlockers;
334 unsigned BlockCount = 0;
335 for (auto PBInst = std::next(MachineBasicBlock::reverse_iterator(LoadInst)),
336 E = LoadInst->getParent()->rend();
337 PBInst != E; ++PBInst) {
338 if (PBInst->isMetaInstruction())
339 continue;
340 BlockCount++;
341 if (BlockCount >= InspectionLimit)
342 break;
343 MachineInstr &MI = *PBInst;
344 if (MI.getDesc().isCall())
345 return PotentialBlockers;
346 PotentialBlockers.push_back(&MI);
347 }
348 // If we didn't get to the instructions limit try predecessing blocks.
349 // Ideally we should traverse the predecessor blocks in depth with some
350 // coloring algorithm, but for now let's just look at the first order
351 // predecessors.
352 if (BlockCount < InspectionLimit) {
354 int LimitLeft = InspectionLimit - BlockCount;
355 for (MachineBasicBlock *PMBB : MBB->predecessors()) {
356 int PredCount = 0;
357 for (MachineInstr &PBInst : llvm::reverse(*PMBB)) {
358 if (PBInst.isMetaInstruction())
359 continue;
360 PredCount++;
361 if (PredCount >= LimitLeft)
362 break;
363 if (PBInst.getDesc().isCall())
364 break;
365 PotentialBlockers.push_back(&PBInst);
366 }
367 }
368 }
369 return PotentialBlockers;
370}
371
372void X86AvoidSFBImpl::buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode,
373 int64_t LoadDisp, MachineInstr *StoreInst,
374 unsigned NStoreOpcode, int64_t StoreDisp,
375 unsigned Size, int64_t Offset) {
376 MachineOperand &LoadBase = getBaseOperand(LoadInst);
377 MachineOperand &StoreBase = getBaseOperand(StoreInst);
378 MachineBasicBlock *MBB = LoadInst->getParent();
379 MachineMemOperand *LMMO = *LoadInst->memoperands_begin();
380 MachineMemOperand *SMMO = *StoreInst->memoperands_begin();
381
382 Register Reg1 =
383 MRI->createVirtualRegister(TII->getRegClass(TII->get(NLoadOpcode), 0));
384 [[maybe_unused]] MachineInstr *NewLoad =
385 BuildMI(*MBB, LoadInst, LoadInst->getDebugLoc(), TII->get(NLoadOpcode),
386 Reg1)
387 .add(LoadBase)
388 .addImm(1)
389 .addReg(X86::NoRegister)
390 .addImm(LoadDisp)
391 .addReg(X86::NoRegister)
394 LLVM_DEBUG(NewLoad->dump());
395 // If the load and store are consecutive, use the loadInst location to
396 // reduce register pressure.
397 MachineInstr *StInst = StoreInst;
398 auto PrevInstrIt = prev_nodbg(MachineBasicBlock::instr_iterator(StoreInst),
399 MBB->instr_begin());
400 if (PrevInstrIt.getNodePtr() == LoadInst)
401 StInst = LoadInst;
402 [[maybe_unused]] MachineInstr *NewStore =
403 BuildMI(*MBB, StInst, StInst->getDebugLoc(), TII->get(NStoreOpcode))
404 .add(StoreBase)
405 .addImm(1)
406 .addReg(X86::NoRegister)
407 .addImm(StoreDisp)
408 .addReg(X86::NoRegister)
409 .addReg(Reg1)
412 LLVM_DEBUG(NewStore->dump());
413}
414
415void X86AvoidSFBImpl::buildCopies(int Size, MachineInstr *LoadInst,
416 int64_t LdDispImm, MachineInstr *StoreInst,
417 int64_t StDispImm, int64_t Offset) {
418 int LdDisp = LdDispImm;
419 int StDisp = StDispImm;
420 while (Size > 0) {
421 if ((Size - MOV128SZ >= 0) && isYMMLoadOpcode(LoadInst->getOpcode())) {
422 Size = Size - MOV128SZ;
423 buildCopy(LoadInst, getYMMtoXMMLoadOpcode(LoadInst->getOpcode()), LdDisp,
424 StoreInst, getYMMtoXMMStoreOpcode(StoreInst->getOpcode()),
425 StDisp, MOV128SZ, Offset);
426 LdDisp += MOV128SZ;
427 StDisp += MOV128SZ;
428 Offset += MOV128SZ;
429 continue;
430 }
431 if (Size - MOV64SZ >= 0) {
432 Size = Size - MOV64SZ;
433 buildCopy(LoadInst, X86::MOV64rm, LdDisp, StoreInst, X86::MOV64mr, StDisp,
434 MOV64SZ, Offset);
435 LdDisp += MOV64SZ;
436 StDisp += MOV64SZ;
437 Offset += MOV64SZ;
438 continue;
439 }
440 if (Size - MOV32SZ >= 0) {
441 Size = Size - MOV32SZ;
442 buildCopy(LoadInst, X86::MOV32rm, LdDisp, StoreInst, X86::MOV32mr, StDisp,
443 MOV32SZ, Offset);
444 LdDisp += MOV32SZ;
445 StDisp += MOV32SZ;
446 Offset += MOV32SZ;
447 continue;
448 }
449 if (Size - MOV16SZ >= 0) {
450 Size = Size - MOV16SZ;
451 buildCopy(LoadInst, X86::MOV16rm, LdDisp, StoreInst, X86::MOV16mr, StDisp,
452 MOV16SZ, Offset);
453 LdDisp += MOV16SZ;
454 StDisp += MOV16SZ;
455 Offset += MOV16SZ;
456 continue;
457 }
458 if (Size - MOV8SZ >= 0) {
459 Size = Size - MOV8SZ;
460 buildCopy(LoadInst, X86::MOV8rm, LdDisp, StoreInst, X86::MOV8mr, StDisp,
461 MOV8SZ, Offset);
462 LdDisp += MOV8SZ;
463 StDisp += MOV8SZ;
464 Offset += MOV8SZ;
465 continue;
466 }
467 }
468 assert(Size == 0 && "Wrong size division");
469}
470
471bool X86AvoidSFBImpl::alias(const MachineMemOperand &Op1,
472 const MachineMemOperand &Op2) const {
473 if (!Op1.getValue() || !Op2.getValue())
474 return true;
475
476 int64_t MinOffset = std::min(Op1.getOffset(), Op2.getOffset());
477 int64_t Overlapa = Op1.getSize().getValue() + Op1.getOffset() - MinOffset;
478 int64_t Overlapb = Op2.getSize().getValue() + Op2.getOffset() - MinOffset;
479
480 return !AA->isNoAlias(
481 MemoryLocation(Op1.getValue(), Overlapa, Op1.getAAInfo()),
482 MemoryLocation(Op2.getValue(), Overlapb, Op2.getAAInfo()));
483}
484
485void X86AvoidSFBImpl::findPotentiallylBlockedCopies(MachineFunction &MF) {
486 for (auto &MBB : MF)
487 for (auto &MI : MBB) {
488 if (!isPotentialBlockedMemCpyLd(MI.getOpcode()))
489 continue;
490 Register DefVR = MI.getOperand(0).getReg();
491 if (!MRI->hasOneNonDBGUse(DefVR))
492 continue;
493 for (MachineOperand &StoreMO :
495 MachineInstr &StoreMI = *StoreMO.getParent();
496 // Skip cases where the memcpy may overlap.
497 if (StoreMI.getParent() == MI.getParent() &&
498 isPotentialBlockedMemCpyPair(MI.getOpcode(), StoreMI.getOpcode()) &&
500 isRelevantAddressingMode(&StoreMI) &&
501 MI.hasOneMemOperand() && StoreMI.hasOneMemOperand()) {
502 // Don't split volatile or atomic accesses.
503 const MachineMemOperand *LMMO = *MI.memoperands_begin();
504 const MachineMemOperand *SMMO = *StoreMI.memoperands_begin();
505 if (LMMO->isVolatile() || LMMO->isAtomic() || SMMO->isVolatile() ||
506 SMMO->isAtomic())
507 continue;
508 if (!alias(*LMMO, *SMMO))
509 BlockedLoadsStoresPairs.push_back(std::make_pair(&MI, &StoreMI));
510 }
511 }
512 }
513}
514
515unsigned X86AvoidSFBImpl::getRegSizeInBytes(MachineInstr *LoadInst) {
516 const auto *TRC = TII->getRegClass(TII->get(LoadInst->getOpcode()), 0);
517 return TRI->getRegSizeInBits(*TRC) / 8;
518}
519
520void X86AvoidSFBImpl::breakBlockedCopies(
521 MachineInstr *LoadInst, MachineInstr *StoreInst,
522 const DisplacementSizeMap &BlockingStoresDispSizeMap) {
523 int64_t LdDispImm = getDispOperand(LoadInst).getImm();
524 int64_t StDispImm = getDispOperand(StoreInst).getImm();
525 int64_t Offset = 0;
526
527 int64_t LdDisp1 = LdDispImm;
528 int64_t LdDisp2 = 0;
529 int64_t StDisp1 = StDispImm;
530 int64_t StDisp2 = 0;
531 unsigned Size1 = 0;
532 unsigned Size2 = 0;
533 int64_t LdStDelta = StDispImm - LdDispImm;
534
535 for (auto DispSizePair : BlockingStoresDispSizeMap) {
536 LdDisp2 = DispSizePair.first;
537 StDisp2 = DispSizePair.first + LdStDelta;
538 Size2 = DispSizePair.second;
539 // Avoid copying overlapping areas.
540 if (LdDisp2 < LdDisp1) {
541 int OverlapDelta = LdDisp1 - LdDisp2;
542 LdDisp2 += OverlapDelta;
543 StDisp2 += OverlapDelta;
544 Size2 -= OverlapDelta;
545 }
546 Size1 = LdDisp2 - LdDisp1;
547
548 // Build a copy for the point until the current blocking store's
549 // displacement.
550 buildCopies(Size1, LoadInst, LdDisp1, StoreInst, StDisp1, Offset);
551 // Build a copy for the current blocking store.
552 buildCopies(Size2, LoadInst, LdDisp2, StoreInst, StDisp2, Offset + Size1);
553 LdDisp1 = LdDisp2 + Size2;
554 StDisp1 = StDisp2 + Size2;
555 Offset += Size1 + Size2;
556 }
557 unsigned Size3 = (LdDispImm + getRegSizeInBytes(LoadInst)) - LdDisp1;
558 buildCopies(Size3, LoadInst, LdDisp1, StoreInst, StDisp1, Offset);
559}
560
563 const MachineOperand &LoadBase = getBaseOperand(LoadInst);
564 const MachineOperand &StoreBase = getBaseOperand(StoreInst);
565 if (LoadBase.isReg() != StoreBase.isReg())
566 return false;
567 if (LoadBase.isReg())
568 return LoadBase.getReg() == StoreBase.getReg();
569 return LoadBase.getIndex() == StoreBase.getIndex();
570}
571
572static bool isBlockingStore(int64_t LoadDispImm, unsigned LoadSize,
573 int64_t StoreDispImm, unsigned StoreSize) {
574 return ((StoreDispImm >= LoadDispImm) &&
575 (StoreDispImm <= LoadDispImm + (LoadSize - StoreSize)));
576}
577
578// Keep track of all stores blocking a load
579static void
580updateBlockingStoresDispSizeMap(DisplacementSizeMap &BlockingStoresDispSizeMap,
581 int64_t DispImm, unsigned Size) {
582 auto [It, Inserted] = BlockingStoresDispSizeMap.try_emplace(DispImm, Size);
583 // Choose the smallest blocking store starting at this displacement.
584 if (!Inserted && It->second > Size)
585 It->second = Size;
586}
587
588// Remove blocking stores contained in each other.
589static void
590removeRedundantBlockingStores(DisplacementSizeMap &BlockingStoresDispSizeMap) {
591 if (BlockingStoresDispSizeMap.size() <= 1)
592 return;
593
595 for (auto DispSizePair : BlockingStoresDispSizeMap) {
596 int64_t CurrDisp = DispSizePair.first;
597 unsigned CurrSize = DispSizePair.second;
598 while (DispSizeStack.size()) {
599 int64_t PrevDisp = DispSizeStack.back().first;
600 unsigned PrevSize = DispSizeStack.back().second;
601 if (CurrDisp + CurrSize > PrevDisp + PrevSize)
602 break;
603 DispSizeStack.pop_back();
604 }
605 DispSizeStack.push_back(DispSizePair);
606 }
607 BlockingStoresDispSizeMap.clear();
608 for (auto Disp : DispSizeStack)
609 BlockingStoresDispSizeMap.insert(Disp);
610}
611
612bool X86AvoidSFBImpl::runOnMachineFunction(MachineFunction &MF) {
613 bool Changed = false;
614
615 const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
616 if (ST.getCLOpts().disable_avoid_SFB || !ST.is64Bit())
617 return false;
618
619 MRI = &MF.getRegInfo();
620 assert(MRI->isSSA() && "Expected MIR to be in SSA form");
621 TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
622 TRI = MF.getSubtarget<X86Subtarget>().getRegisterInfo();
623 LLVM_DEBUG(dbgs() << "Start X86AvoidStoreForwardBlocks\n";);
624 // Look for a load then a store to XMM/YMM which look like a memcpy
625 findPotentiallylBlockedCopies(MF);
626
627 for (auto LoadStoreInstPair : BlockedLoadsStoresPairs) {
628 MachineInstr *LoadInst = LoadStoreInstPair.first;
629 int64_t LdDispImm = getDispOperand(LoadInst).getImm();
630 DisplacementSizeMap BlockingStoresDispSizeMap;
631
632 SmallVector<MachineInstr *, 2> PotentialBlockers =
633 findPotentialBlockers(LoadInst, ST.getCLOpts().sfb_inspection_limit);
634 for (auto *PBInst : PotentialBlockers) {
635 if (!isPotentialBlockingStoreInst(PBInst->getOpcode(),
636 LoadInst->getOpcode()) ||
637 !isRelevantAddressingMode(PBInst) || !PBInst->hasOneMemOperand())
638 continue;
639 int64_t PBstDispImm = getDispOperand(PBInst).getImm();
640 unsigned PBstSize = (*PBInst->memoperands_begin())->getSize().getValue();
641 // This check doesn't cover all cases, but it will suffice for now.
642 // TODO: take branch probability into consideration, if the blocking
643 // store is in an unreached block, breaking the memcopy could lose
644 // performance.
645 if (hasSameBaseOpValue(LoadInst, PBInst) &&
646 isBlockingStore(LdDispImm, getRegSizeInBytes(LoadInst), PBstDispImm,
647 PBstSize))
648 updateBlockingStoresDispSizeMap(BlockingStoresDispSizeMap, PBstDispImm,
649 PBstSize);
650 }
651
652 if (BlockingStoresDispSizeMap.empty())
653 continue;
654
655 // We found a store forward block, break the memcpy's load and store
656 // into smaller copies such that each smaller store that was causing
657 // a store block would now be copied separately.
658 MachineInstr *StoreInst = LoadStoreInstPair.second;
659 LLVM_DEBUG(dbgs() << "Blocked load and store instructions: \n");
660 LLVM_DEBUG(LoadInst->dump());
661 LLVM_DEBUG(StoreInst->dump());
662 LLVM_DEBUG(dbgs() << "Replaced with:\n");
663 removeRedundantBlockingStores(BlockingStoresDispSizeMap);
664 breakBlockedCopies(LoadInst, StoreInst, BlockingStoresDispSizeMap);
665 ForRemoval.push_back(LoadInst);
666 ForRemoval.push_back(StoreInst);
667 }
668 for (auto *RemovedInst : ForRemoval) {
669 RemovedInst->eraseFromParent();
670 }
671 ForRemoval.clear();
672 BlockedLoadsStoresPairs.clear();
673 LLVM_DEBUG(dbgs() << "End X86AvoidStoreForwardBlocks\n";);
674
675 return Changed;
676}
677
678bool X86AvoidSFBLegacy::runOnMachineFunction(MachineFunction &MF) {
679 if (skipFunction(MF.getFunction()))
680 return false;
681 AliasAnalysis *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
682 X86AvoidSFBImpl Impl(AA);
683 return Impl.runOnMachineFunction(MF);
684}
685
686PreservedAnalyses
691 .getManager()
692 .getResult<AAManager>(MF.getFunction());
693 X86AvoidSFBImpl Impl(AA);
694 bool Changed = Impl.runOnMachineFunction(MF);
697}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define DEBUG_TYPE
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
MachineRegisterInfo & MRI
#define LLVM_DEBUG(...)
Definition Debug.h:119
static SmallVector< MachineInstr *, 2 > findPotentialBlockers(MachineInstr *LoadInst, unsigned InspectionLimit)
static unsigned getYMMtoXMMLoadOpcode(unsigned LoadOpcode)
static bool isPotentialBlockedMemCpyLd(unsigned Opcode)
static bool isPotentialBlockedMemCpyPair(unsigned LdOpcode, unsigned StOpcode)
static bool isPotentialBlockingStoreInst(unsigned Opcode, unsigned LoadOpcode)
static const int MOV64SZ
static const int MOV8SZ
static bool isXMMLoadOpcode(unsigned Opcode)
static int getAddrOffset(const MachineInstr *MI)
static bool isBlockingStore(int64_t LoadDispImm, unsigned LoadSize, int64_t StoreDispImm, unsigned StoreSize)
static bool isRelevantAddressingMode(MachineInstr *MI)
static void removeRedundantBlockingStores(DisplacementSizeMap &BlockingStoresDispSizeMap)
static const int MOV16SZ
static bool hasSameBaseOpValue(MachineInstr *LoadInst, MachineInstr *StoreInst)
static void updateBlockingStoresDispSizeMap(DisplacementSizeMap &BlockingStoresDispSizeMap, int64_t DispImm, unsigned Size)
static MachineOperand & getBaseOperand(MachineInstr *MI)
static unsigned getYMMtoXMMStoreOpcode(unsigned StoreOpcode)
static const int MOV32SZ
static MachineOperand & getDispOperand(MachineInstr *MI)
static bool isYMMLoadOpcode(unsigned Opcode)
static const int MOV128SZ
A manager for alias analyses.
A wrapper pass to provide the legacy pass manager access to a suitably prepared AAResults object.
bool isNoAlias(const MemoryLocation &LocA, const MemoryLocation &LocB)
A trivial helper function to check to see if the specified pointers are no-alias.
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
An instruction for reading from memory.
TypeSize getValue() const
Instructions::iterator instr_iterator
MachineInstrBundleIterator< MachineInstr, true > reverse_iterator
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
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.
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.
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags F, LLT MemTy, Align BaseAlignment, const MMOMetadata &Metadata=MMOMetadata(), SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
const MachineInstrBuilder & addReg(Register RegNo, RegState Flags={}, unsigned SubReg=0) const
Add a new virtual register operand.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & add(const MachineOperand &MO) const
const MachineInstrBuilder & addMemOperand(MachineMemOperand *MMO) const
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
bool hasOneMemOperand() const
Return true if this instruction has exactly one MachineMemOperand.
mmo_iterator memoperands_begin() const
Access to memory operands of the instruction.
const DebugLoc & getDebugLoc() const
Returns the debug location id of this MachineInstr.
LLVM_ABI void dump() const
A description of a memory reference used in the backend.
LocationSize getSize() const
Return the size in bytes of the memory reference.
bool isAtomic() const
Returns true if this operation has an atomic ordering requirement of unordered or higher,...
AAMDNodes getAAInfo() const
Return the AA tags for the memory reference.
const Value * getValue() const
Return the base address of the memory access.
int64_t getOffset() const
For normal values, this is a byte offset added to the base address.
MachineOperand class - Representation of each machine instruction operand.
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
bool isImm() const
isImm - Tests if this is a MO_Immediate operand.
Register getReg() const
getReg - Returns the register number.
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.
iterator_range< use_nodbg_iterator > use_nodbg_operands(Register Reg) const
LLVM_ABI Register createVirtualRegister(const TargetRegisterClass *RegClass, StringRef Name="")
createVirtualRegister - Create and return a new virtual register in the function with the specified r...
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
const ParentTy * getParent() const
Definition ilist_node.h:34
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
Definition Attributor.h:165
int getMemoryOperandIdx(const MCInstrDesc &Desc)
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
FunctionPass * createX86AvoidStoreForwardingBlocksLegacyPass()
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
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
auto reverse(ContainerTy &&C)
Definition STLExtras.h:408
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.
IterT prev_nodbg(IterT It, IterT Begin, bool SkipPseudoOp=true)
Decrement It, then continue decrementing it while it points to a debug instruction.