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
58 "x86-disable-avoid-SFB", cl::Hidden,
59 cl::desc("X86: Disable Store Forwarding Blocks fixup."), cl::init(false));
60
62 "x86-sfb-inspection-limit",
63 cl::desc("X86: Number of instructions backward to "
64 "inspect for store forwarding blocks."),
65 cl::init(20), cl::Hidden);
66
67namespace {
68
69using DisplacementSizeMap = std::map<int64_t, unsigned>;
70
71class X86AvoidSFBImpl {
72public:
73 X86AvoidSFBImpl(AliasAnalysis *AA) : AA(AA) {};
74 bool runOnMachineFunction(MachineFunction &MF);
75
76private:
77 MachineRegisterInfo *MRI = nullptr;
78 const X86InstrInfo *TII = nullptr;
79 const X86RegisterInfo *TRI = nullptr;
81 BlockedLoadsStoresPairs;
82 SmallVector<MachineInstr *, 2> ForRemoval;
83 AliasAnalysis *AA = nullptr;
84
85 /// Returns couples of Load then Store to memory which look
86 /// like a memcpy.
87 void findPotentiallylBlockedCopies(MachineFunction &MF);
88 /// Break the memcpy's load and store into smaller copies
89 /// such that each memory load that was blocked by a smaller store
90 /// would now be copied separately.
91 void breakBlockedCopies(MachineInstr *LoadInst, MachineInstr *StoreInst,
92 const DisplacementSizeMap &BlockingStoresDispSizeMap);
93 /// Break a copy of size Size to smaller copies.
94 void buildCopies(int Size, MachineInstr *LoadInst, int64_t LdDispImm,
95 MachineInstr *StoreInst, int64_t StDispImm, int64_t Offset);
96
97 void buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode, int64_t LoadDisp,
98 MachineInstr *StoreInst, unsigned NStoreOpcode,
99 int64_t StoreDisp, unsigned Size, int64_t Offset);
100
101 bool alias(const MachineMemOperand &Op1, const MachineMemOperand &Op2) const;
102
103 unsigned getRegSizeInBytes(MachineInstr *Inst);
104};
105
106class X86AvoidSFBLegacy : public MachineFunctionPass {
107public:
108 static char ID;
109 X86AvoidSFBLegacy() : MachineFunctionPass(ID) {}
110
111 StringRef getPassName() const override {
112 return "X86 Avoid Store Forwarding Blocks";
113 }
114
115 bool runOnMachineFunction(MachineFunction &MF) override;
116
117 void getAnalysisUsage(AnalysisUsage &AU) const override {
119 AU.addRequired<AAResultsWrapperPass>();
120 AU.addPreserved<MachineRegisterClassInfoWrapperPass>();
121 }
122};
123
124} // end anonymous namespace
125
126char X86AvoidSFBLegacy::ID = 0;
127
128INITIALIZE_PASS_BEGIN(X86AvoidSFBLegacy, DEBUG_TYPE, "Machine code sinking",
129 false, false)
131INITIALIZE_PASS_END(X86AvoidSFBLegacy, DEBUG_TYPE, "Machine code sinking",
133
135 return new X86AvoidSFBLegacy();
136}
137
138static bool isXMMLoadOpcode(unsigned Opcode) {
139 return Opcode == X86::MOVUPSrm || Opcode == X86::MOVAPSrm ||
140 Opcode == X86::VMOVUPSrm || Opcode == X86::VMOVAPSrm ||
141 Opcode == X86::VMOVUPDrm || Opcode == X86::VMOVAPDrm ||
142 Opcode == X86::VMOVDQUrm || Opcode == X86::VMOVDQArm ||
143 Opcode == X86::VMOVUPSZ128rm || Opcode == X86::VMOVAPSZ128rm ||
144 Opcode == X86::VMOVUPDZ128rm || Opcode == X86::VMOVAPDZ128rm ||
145 Opcode == X86::VMOVDQU64Z128rm || Opcode == X86::VMOVDQA64Z128rm ||
146 Opcode == X86::VMOVDQU32Z128rm || Opcode == X86::VMOVDQA32Z128rm;
147}
148static bool isYMMLoadOpcode(unsigned Opcode) {
149 return Opcode == X86::VMOVUPSYrm || Opcode == X86::VMOVAPSYrm ||
150 Opcode == X86::VMOVUPDYrm || Opcode == X86::VMOVAPDYrm ||
151 Opcode == X86::VMOVDQUYrm || Opcode == X86::VMOVDQAYrm ||
152 Opcode == X86::VMOVUPSZ256rm || Opcode == X86::VMOVAPSZ256rm ||
153 Opcode == X86::VMOVUPDZ256rm || Opcode == X86::VMOVAPDZ256rm ||
154 Opcode == X86::VMOVDQU64Z256rm || Opcode == X86::VMOVDQA64Z256rm ||
155 Opcode == X86::VMOVDQU32Z256rm || Opcode == X86::VMOVDQA32Z256rm;
156}
157
158static bool isPotentialBlockedMemCpyLd(unsigned Opcode) {
159 return isXMMLoadOpcode(Opcode) || isYMMLoadOpcode(Opcode);
160}
161
162static bool isPotentialBlockedMemCpyPair(unsigned LdOpcode, unsigned StOpcode) {
163 switch (LdOpcode) {
164 case X86::MOVUPSrm:
165 case X86::MOVAPSrm:
166 return StOpcode == X86::MOVUPSmr || StOpcode == X86::MOVAPSmr;
167 case X86::VMOVUPSrm:
168 case X86::VMOVAPSrm:
169 return StOpcode == X86::VMOVUPSmr || StOpcode == X86::VMOVAPSmr;
170 case X86::VMOVUPDrm:
171 case X86::VMOVAPDrm:
172 return StOpcode == X86::VMOVUPDmr || StOpcode == X86::VMOVAPDmr;
173 case X86::VMOVDQUrm:
174 case X86::VMOVDQArm:
175 return StOpcode == X86::VMOVDQUmr || StOpcode == X86::VMOVDQAmr;
176 case X86::VMOVUPSZ128rm:
177 case X86::VMOVAPSZ128rm:
178 return StOpcode == X86::VMOVUPSZ128mr || StOpcode == X86::VMOVAPSZ128mr;
179 case X86::VMOVUPDZ128rm:
180 case X86::VMOVAPDZ128rm:
181 return StOpcode == X86::VMOVUPDZ128mr || StOpcode == X86::VMOVAPDZ128mr;
182 case X86::VMOVUPSYrm:
183 case X86::VMOVAPSYrm:
184 return StOpcode == X86::VMOVUPSYmr || StOpcode == X86::VMOVAPSYmr;
185 case X86::VMOVUPDYrm:
186 case X86::VMOVAPDYrm:
187 return StOpcode == X86::VMOVUPDYmr || StOpcode == X86::VMOVAPDYmr;
188 case X86::VMOVDQUYrm:
189 case X86::VMOVDQAYrm:
190 return StOpcode == X86::VMOVDQUYmr || StOpcode == X86::VMOVDQAYmr;
191 case X86::VMOVUPSZ256rm:
192 case X86::VMOVAPSZ256rm:
193 return StOpcode == X86::VMOVUPSZ256mr || StOpcode == X86::VMOVAPSZ256mr;
194 case X86::VMOVUPDZ256rm:
195 case X86::VMOVAPDZ256rm:
196 return StOpcode == X86::VMOVUPDZ256mr || StOpcode == X86::VMOVAPDZ256mr;
197 case X86::VMOVDQU64Z128rm:
198 case X86::VMOVDQA64Z128rm:
199 return StOpcode == X86::VMOVDQU64Z128mr || StOpcode == X86::VMOVDQA64Z128mr;
200 case X86::VMOVDQU32Z128rm:
201 case X86::VMOVDQA32Z128rm:
202 return StOpcode == X86::VMOVDQU32Z128mr || StOpcode == X86::VMOVDQA32Z128mr;
203 case X86::VMOVDQU64Z256rm:
204 case X86::VMOVDQA64Z256rm:
205 return StOpcode == X86::VMOVDQU64Z256mr || StOpcode == X86::VMOVDQA64Z256mr;
206 case X86::VMOVDQU32Z256rm:
207 case X86::VMOVDQA32Z256rm:
208 return StOpcode == X86::VMOVDQU32Z256mr || StOpcode == X86::VMOVDQA32Z256mr;
209 default:
210 return false;
211 }
212}
213
214static bool isPotentialBlockingStoreInst(unsigned Opcode, unsigned LoadOpcode) {
215 bool PBlock = false;
216 PBlock |= Opcode == X86::MOV64mr || Opcode == X86::MOV64mi32 ||
217 Opcode == X86::MOV32mr || Opcode == X86::MOV32mi ||
218 Opcode == X86::MOV16mr || Opcode == X86::MOV16mi ||
219 Opcode == X86::MOV8mr || Opcode == X86::MOV8mi;
220 if (isYMMLoadOpcode(LoadOpcode))
221 PBlock |= Opcode == X86::VMOVUPSmr || Opcode == X86::VMOVAPSmr ||
222 Opcode == X86::VMOVUPDmr || Opcode == X86::VMOVAPDmr ||
223 Opcode == X86::VMOVDQUmr || Opcode == X86::VMOVDQAmr ||
224 Opcode == X86::VMOVUPSZ128mr || Opcode == X86::VMOVAPSZ128mr ||
225 Opcode == X86::VMOVUPDZ128mr || Opcode == X86::VMOVAPDZ128mr ||
226 Opcode == X86::VMOVDQU64Z128mr ||
227 Opcode == X86::VMOVDQA64Z128mr ||
228 Opcode == X86::VMOVDQU32Z128mr || Opcode == X86::VMOVDQA32Z128mr;
229 return PBlock;
230}
231
232static const int MOV128SZ = 16;
233static const int MOV64SZ = 8;
234static const int MOV32SZ = 4;
235static const int MOV16SZ = 2;
236static const int MOV8SZ = 1;
237
238static unsigned getYMMtoXMMLoadOpcode(unsigned LoadOpcode) {
239 switch (LoadOpcode) {
240 case X86::VMOVUPSYrm:
241 case X86::VMOVAPSYrm:
242 return X86::VMOVUPSrm;
243 case X86::VMOVUPDYrm:
244 case X86::VMOVAPDYrm:
245 return X86::VMOVUPDrm;
246 case X86::VMOVDQUYrm:
247 case X86::VMOVDQAYrm:
248 return X86::VMOVDQUrm;
249 case X86::VMOVUPSZ256rm:
250 case X86::VMOVAPSZ256rm:
251 return X86::VMOVUPSZ128rm;
252 case X86::VMOVUPDZ256rm:
253 case X86::VMOVAPDZ256rm:
254 return X86::VMOVUPDZ128rm;
255 case X86::VMOVDQU64Z256rm:
256 case X86::VMOVDQA64Z256rm:
257 return X86::VMOVDQU64Z128rm;
258 case X86::VMOVDQU32Z256rm:
259 case X86::VMOVDQA32Z256rm:
260 return X86::VMOVDQU32Z128rm;
261 default:
262 llvm_unreachable("Unexpected Load Instruction Opcode");
263 }
264 return 0;
265}
266
267static unsigned getYMMtoXMMStoreOpcode(unsigned StoreOpcode) {
268 switch (StoreOpcode) {
269 case X86::VMOVUPSYmr:
270 case X86::VMOVAPSYmr:
271 return X86::VMOVUPSmr;
272 case X86::VMOVUPDYmr:
273 case X86::VMOVAPDYmr:
274 return X86::VMOVUPDmr;
275 case X86::VMOVDQUYmr:
276 case X86::VMOVDQAYmr:
277 return X86::VMOVDQUmr;
278 case X86::VMOVUPSZ256mr:
279 case X86::VMOVAPSZ256mr:
280 return X86::VMOVUPSZ128mr;
281 case X86::VMOVUPDZ256mr:
282 case X86::VMOVAPDZ256mr:
283 return X86::VMOVUPDZ128mr;
284 case X86::VMOVDQU64Z256mr:
285 case X86::VMOVDQA64Z256mr:
286 return X86::VMOVDQU64Z128mr;
287 case X86::VMOVDQU32Z256mr:
288 case X86::VMOVDQA32Z256mr:
289 return X86::VMOVDQU32Z128mr;
290 default:
291 llvm_unreachable("Unexpected Load Instruction Opcode");
292 }
293 return 0;
294}
295
296static int getAddrOffset(const MachineInstr *MI) {
297 const MCInstrDesc &Descl = MI->getDesc();
298 int AddrOffset = X86II::getMemoryOperandNo(Descl.TSFlags);
299 assert(AddrOffset != -1 && "Expected Memory Operand");
300 AddrOffset += X86II::getOperandBias(Descl);
301 return AddrOffset;
302}
303
305 int AddrOffset = getAddrOffset(MI);
306 return MI->getOperand(AddrOffset + X86::AddrBaseReg);
307}
308
310 int AddrOffset = getAddrOffset(MI);
311 return MI->getOperand(AddrOffset + X86::AddrDisp);
312}
313
314// Relevant addressing modes contain only base register and immediate
315// displacement or frameindex and immediate displacement.
316// TODO: Consider expanding to other addressing modes in the future
318 int AddrOffset = getAddrOffset(MI);
320 const MachineOperand &Disp = getDispOperand(MI);
321 const MachineOperand &Scale = MI->getOperand(AddrOffset + X86::AddrScaleAmt);
322 const MachineOperand &Index = MI->getOperand(AddrOffset + X86::AddrIndexReg);
323 const MachineOperand &Segment = MI->getOperand(AddrOffset + X86::AddrSegmentReg);
324
325 if (!((Base.isReg() && Base.getReg() != X86::NoRegister) || Base.isFI()))
326 return false;
327 if (!Disp.isImm())
328 return false;
329 if (Scale.getImm() != 1)
330 return false;
331 if (!(Index.isReg() && Index.getReg() == X86::NoRegister))
332 return false;
333 if (!(Segment.isReg() && Segment.getReg() == X86::NoRegister))
334 return false;
335 return true;
336}
337
338// Collect potentially blocking stores.
339// Limit the number of instructions backwards we want to inspect
340// since the effect of store block won't be visible if the store
341// and load instructions have enough instructions in between to
342// keep the core busy.
345 SmallVector<MachineInstr *, 2> PotentialBlockers;
346 unsigned BlockCount = 0;
347 const unsigned InspectionLimit = X86AvoidSFBInspectionLimit;
348 for (auto PBInst = std::next(MachineBasicBlock::reverse_iterator(LoadInst)),
349 E = LoadInst->getParent()->rend();
350 PBInst != E; ++PBInst) {
351 if (PBInst->isMetaInstruction())
352 continue;
353 BlockCount++;
354 if (BlockCount >= InspectionLimit)
355 break;
356 MachineInstr &MI = *PBInst;
357 if (MI.getDesc().isCall())
358 return PotentialBlockers;
359 PotentialBlockers.push_back(&MI);
360 }
361 // If we didn't get to the instructions limit try predecessing blocks.
362 // Ideally we should traverse the predecessor blocks in depth with some
363 // coloring algorithm, but for now let's just look at the first order
364 // predecessors.
365 if (BlockCount < InspectionLimit) {
367 int LimitLeft = InspectionLimit - BlockCount;
368 for (MachineBasicBlock *PMBB : MBB->predecessors()) {
369 int PredCount = 0;
370 for (MachineInstr &PBInst : llvm::reverse(*PMBB)) {
371 if (PBInst.isMetaInstruction())
372 continue;
373 PredCount++;
374 if (PredCount >= LimitLeft)
375 break;
376 if (PBInst.getDesc().isCall())
377 break;
378 PotentialBlockers.push_back(&PBInst);
379 }
380 }
381 }
382 return PotentialBlockers;
383}
384
385void X86AvoidSFBImpl::buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode,
386 int64_t LoadDisp, MachineInstr *StoreInst,
387 unsigned NStoreOpcode, int64_t StoreDisp,
388 unsigned Size, int64_t Offset) {
389 MachineOperand &LoadBase = getBaseOperand(LoadInst);
390 MachineOperand &StoreBase = getBaseOperand(StoreInst);
391 MachineBasicBlock *MBB = LoadInst->getParent();
392 MachineMemOperand *LMMO = *LoadInst->memoperands_begin();
393 MachineMemOperand *SMMO = *StoreInst->memoperands_begin();
394
395 Register Reg1 =
396 MRI->createVirtualRegister(TII->getRegClass(TII->get(NLoadOpcode), 0));
397 MachineInstr *NewLoad =
398 BuildMI(*MBB, LoadInst, LoadInst->getDebugLoc(), TII->get(NLoadOpcode),
399 Reg1)
400 .add(LoadBase)
401 .addImm(1)
402 .addReg(X86::NoRegister)
403 .addImm(LoadDisp)
404 .addReg(X86::NoRegister)
407 if (LoadBase.isReg())
408 getBaseOperand(NewLoad).setIsKill(false);
409 LLVM_DEBUG(NewLoad->dump());
410 // If the load and store are consecutive, use the loadInst location to
411 // reduce register pressure.
412 MachineInstr *StInst = StoreInst;
413 auto PrevInstrIt = prev_nodbg(MachineBasicBlock::instr_iterator(StoreInst),
414 MBB->instr_begin());
415 if (PrevInstrIt.getNodePtr() == LoadInst)
416 StInst = LoadInst;
417 MachineInstr *NewStore =
418 BuildMI(*MBB, StInst, StInst->getDebugLoc(), TII->get(NStoreOpcode))
419 .add(StoreBase)
420 .addImm(1)
421 .addReg(X86::NoRegister)
422 .addImm(StoreDisp)
423 .addReg(X86::NoRegister)
424 .addReg(Reg1)
427 if (StoreBase.isReg())
428 getBaseOperand(NewStore).setIsKill(false);
429 MachineOperand &StoreSrcVReg = StoreInst->getOperand(X86::AddrNumOperands);
430 assert(StoreSrcVReg.isReg() && "Expected virtual register");
431 NewStore->getOperand(X86::AddrNumOperands).setIsKill(StoreSrcVReg.isKill());
432 LLVM_DEBUG(NewStore->dump());
433}
434
435void X86AvoidSFBImpl::buildCopies(int Size, MachineInstr *LoadInst,
436 int64_t LdDispImm, MachineInstr *StoreInst,
437 int64_t StDispImm, int64_t Offset) {
438 int LdDisp = LdDispImm;
439 int StDisp = StDispImm;
440 while (Size > 0) {
441 if ((Size - MOV128SZ >= 0) && isYMMLoadOpcode(LoadInst->getOpcode())) {
442 Size = Size - MOV128SZ;
443 buildCopy(LoadInst, getYMMtoXMMLoadOpcode(LoadInst->getOpcode()), LdDisp,
444 StoreInst, getYMMtoXMMStoreOpcode(StoreInst->getOpcode()),
445 StDisp, MOV128SZ, Offset);
446 LdDisp += MOV128SZ;
447 StDisp += MOV128SZ;
448 Offset += MOV128SZ;
449 continue;
450 }
451 if (Size - MOV64SZ >= 0) {
452 Size = Size - MOV64SZ;
453 buildCopy(LoadInst, X86::MOV64rm, LdDisp, StoreInst, X86::MOV64mr, StDisp,
454 MOV64SZ, Offset);
455 LdDisp += MOV64SZ;
456 StDisp += MOV64SZ;
457 Offset += MOV64SZ;
458 continue;
459 }
460 if (Size - MOV32SZ >= 0) {
461 Size = Size - MOV32SZ;
462 buildCopy(LoadInst, X86::MOV32rm, LdDisp, StoreInst, X86::MOV32mr, StDisp,
463 MOV32SZ, Offset);
464 LdDisp += MOV32SZ;
465 StDisp += MOV32SZ;
466 Offset += MOV32SZ;
467 continue;
468 }
469 if (Size - MOV16SZ >= 0) {
470 Size = Size - MOV16SZ;
471 buildCopy(LoadInst, X86::MOV16rm, LdDisp, StoreInst, X86::MOV16mr, StDisp,
472 MOV16SZ, Offset);
473 LdDisp += MOV16SZ;
474 StDisp += MOV16SZ;
475 Offset += MOV16SZ;
476 continue;
477 }
478 if (Size - MOV8SZ >= 0) {
479 Size = Size - MOV8SZ;
480 buildCopy(LoadInst, X86::MOV8rm, LdDisp, StoreInst, X86::MOV8mr, StDisp,
481 MOV8SZ, Offset);
482 LdDisp += MOV8SZ;
483 StDisp += MOV8SZ;
484 Offset += MOV8SZ;
485 continue;
486 }
487 }
488 assert(Size == 0 && "Wrong size division");
489}
490
494 auto *StorePrevNonDbgInstr =
496 LoadInst->getParent()->instr_begin())
497 .getNodePtr();
498 if (LoadBase.isReg()) {
499 MachineInstr *LastLoad = LoadInst->getPrevNode();
500 // If the original load and store to xmm/ymm were consecutive
501 // then the partial copies were also created in
502 // a consecutive order to reduce register pressure,
503 // and the location of the last load is before the last store.
504 if (StorePrevNonDbgInstr == LoadInst)
505 LastLoad = LoadInst->getPrevNode()->getPrevNode();
506 getBaseOperand(LastLoad).setIsKill(LoadBase.isKill());
507 }
508 if (StoreBase.isReg()) {
509 MachineInstr *StInst = StoreInst;
510 if (StorePrevNonDbgInstr == LoadInst)
511 StInst = LoadInst;
512 getBaseOperand(StInst->getPrevNode()).setIsKill(StoreBase.isKill());
513 }
514}
515
516bool X86AvoidSFBImpl::alias(const MachineMemOperand &Op1,
517 const MachineMemOperand &Op2) const {
518 if (!Op1.getValue() || !Op2.getValue())
519 return true;
520
521 int64_t MinOffset = std::min(Op1.getOffset(), Op2.getOffset());
522 int64_t Overlapa = Op1.getSize().getValue() + Op1.getOffset() - MinOffset;
523 int64_t Overlapb = Op2.getSize().getValue() + Op2.getOffset() - MinOffset;
524
525 return !AA->isNoAlias(
526 MemoryLocation(Op1.getValue(), Overlapa, Op1.getAAInfo()),
527 MemoryLocation(Op2.getValue(), Overlapb, Op2.getAAInfo()));
528}
529
530void X86AvoidSFBImpl::findPotentiallylBlockedCopies(MachineFunction &MF) {
531 for (auto &MBB : MF)
532 for (auto &MI : MBB) {
533 if (!isPotentialBlockedMemCpyLd(MI.getOpcode()))
534 continue;
535 Register DefVR = MI.getOperand(0).getReg();
536 if (!MRI->hasOneNonDBGUse(DefVR))
537 continue;
538 for (MachineOperand &StoreMO :
540 MachineInstr &StoreMI = *StoreMO.getParent();
541 // Skip cases where the memcpy may overlap.
542 if (StoreMI.getParent() == MI.getParent() &&
543 isPotentialBlockedMemCpyPair(MI.getOpcode(), StoreMI.getOpcode()) &&
545 isRelevantAddressingMode(&StoreMI) &&
546 MI.hasOneMemOperand() && StoreMI.hasOneMemOperand()) {
547 // Don't split volatile or atomic accesses.
548 const MachineMemOperand *LMMO = *MI.memoperands_begin();
549 const MachineMemOperand *SMMO = *StoreMI.memoperands_begin();
550 if (LMMO->isVolatile() || LMMO->isAtomic() || SMMO->isVolatile() ||
551 SMMO->isAtomic())
552 continue;
553 if (!alias(*LMMO, *SMMO))
554 BlockedLoadsStoresPairs.push_back(std::make_pair(&MI, &StoreMI));
555 }
556 }
557 }
558}
559
560unsigned X86AvoidSFBImpl::getRegSizeInBytes(MachineInstr *LoadInst) {
561 const auto *TRC = TII->getRegClass(TII->get(LoadInst->getOpcode()), 0);
562 return TRI->getRegSizeInBits(*TRC) / 8;
563}
564
565void X86AvoidSFBImpl::breakBlockedCopies(
566 MachineInstr *LoadInst, MachineInstr *StoreInst,
567 const DisplacementSizeMap &BlockingStoresDispSizeMap) {
568 int64_t LdDispImm = getDispOperand(LoadInst).getImm();
569 int64_t StDispImm = getDispOperand(StoreInst).getImm();
570 int64_t Offset = 0;
571
572 int64_t LdDisp1 = LdDispImm;
573 int64_t LdDisp2 = 0;
574 int64_t StDisp1 = StDispImm;
575 int64_t StDisp2 = 0;
576 unsigned Size1 = 0;
577 unsigned Size2 = 0;
578 int64_t LdStDelta = StDispImm - LdDispImm;
579
580 for (auto DispSizePair : BlockingStoresDispSizeMap) {
581 LdDisp2 = DispSizePair.first;
582 StDisp2 = DispSizePair.first + LdStDelta;
583 Size2 = DispSizePair.second;
584 // Avoid copying overlapping areas.
585 if (LdDisp2 < LdDisp1) {
586 int OverlapDelta = LdDisp1 - LdDisp2;
587 LdDisp2 += OverlapDelta;
588 StDisp2 += OverlapDelta;
589 Size2 -= OverlapDelta;
590 }
591 Size1 = LdDisp2 - LdDisp1;
592
593 // Build a copy for the point until the current blocking store's
594 // displacement.
595 buildCopies(Size1, LoadInst, LdDisp1, StoreInst, StDisp1, Offset);
596 // Build a copy for the current blocking store.
597 buildCopies(Size2, LoadInst, LdDisp2, StoreInst, StDisp2, Offset + Size1);
598 LdDisp1 = LdDisp2 + Size2;
599 StDisp1 = StDisp2 + Size2;
600 Offset += Size1 + Size2;
601 }
602 unsigned Size3 = (LdDispImm + getRegSizeInBytes(LoadInst)) - LdDisp1;
603 buildCopies(Size3, LoadInst, LdDisp1, StoreInst, StDisp1, Offset);
604}
605
608 const MachineOperand &LoadBase = getBaseOperand(LoadInst);
609 const MachineOperand &StoreBase = getBaseOperand(StoreInst);
610 if (LoadBase.isReg() != StoreBase.isReg())
611 return false;
612 if (LoadBase.isReg())
613 return LoadBase.getReg() == StoreBase.getReg();
614 return LoadBase.getIndex() == StoreBase.getIndex();
615}
616
617static bool isBlockingStore(int64_t LoadDispImm, unsigned LoadSize,
618 int64_t StoreDispImm, unsigned StoreSize) {
619 return ((StoreDispImm >= LoadDispImm) &&
620 (StoreDispImm <= LoadDispImm + (LoadSize - StoreSize)));
621}
622
623// Keep track of all stores blocking a load
624static void
625updateBlockingStoresDispSizeMap(DisplacementSizeMap &BlockingStoresDispSizeMap,
626 int64_t DispImm, unsigned Size) {
627 auto [It, Inserted] = BlockingStoresDispSizeMap.try_emplace(DispImm, Size);
628 // Choose the smallest blocking store starting at this displacement.
629 if (!Inserted && It->second > Size)
630 It->second = Size;
631}
632
633// Remove blocking stores contained in each other.
634static void
635removeRedundantBlockingStores(DisplacementSizeMap &BlockingStoresDispSizeMap) {
636 if (BlockingStoresDispSizeMap.size() <= 1)
637 return;
638
640 for (auto DispSizePair : BlockingStoresDispSizeMap) {
641 int64_t CurrDisp = DispSizePair.first;
642 unsigned CurrSize = DispSizePair.second;
643 while (DispSizeStack.size()) {
644 int64_t PrevDisp = DispSizeStack.back().first;
645 unsigned PrevSize = DispSizeStack.back().second;
646 if (CurrDisp + CurrSize > PrevDisp + PrevSize)
647 break;
648 DispSizeStack.pop_back();
649 }
650 DispSizeStack.push_back(DispSizePair);
651 }
652 BlockingStoresDispSizeMap.clear();
653 for (auto Disp : DispSizeStack)
654 BlockingStoresDispSizeMap.insert(Disp);
655}
656
657bool X86AvoidSFBImpl::runOnMachineFunction(MachineFunction &MF) {
658 bool Changed = false;
659
661 !MF.getSubtarget<X86Subtarget>().is64Bit())
662 return false;
663
664 MRI = &MF.getRegInfo();
665 assert(MRI->isSSA() && "Expected MIR to be in SSA form");
666 TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
667 TRI = MF.getSubtarget<X86Subtarget>().getRegisterInfo();
668 LLVM_DEBUG(dbgs() << "Start X86AvoidStoreForwardBlocks\n";);
669 // Look for a load then a store to XMM/YMM which look like a memcpy
670 findPotentiallylBlockedCopies(MF);
671
672 for (auto LoadStoreInstPair : BlockedLoadsStoresPairs) {
673 MachineInstr *LoadInst = LoadStoreInstPair.first;
674 int64_t LdDispImm = getDispOperand(LoadInst).getImm();
675 DisplacementSizeMap BlockingStoresDispSizeMap;
676
677 SmallVector<MachineInstr *, 2> PotentialBlockers =
678 findPotentialBlockers(LoadInst);
679 for (auto *PBInst : PotentialBlockers) {
680 if (!isPotentialBlockingStoreInst(PBInst->getOpcode(),
681 LoadInst->getOpcode()) ||
682 !isRelevantAddressingMode(PBInst) || !PBInst->hasOneMemOperand())
683 continue;
684 int64_t PBstDispImm = getDispOperand(PBInst).getImm();
685 unsigned PBstSize = (*PBInst->memoperands_begin())->getSize().getValue();
686 // This check doesn't cover all cases, but it will suffice for now.
687 // TODO: take branch probability into consideration, if the blocking
688 // store is in an unreached block, breaking the memcopy could lose
689 // performance.
690 if (hasSameBaseOpValue(LoadInst, PBInst) &&
691 isBlockingStore(LdDispImm, getRegSizeInBytes(LoadInst), PBstDispImm,
692 PBstSize))
693 updateBlockingStoresDispSizeMap(BlockingStoresDispSizeMap, PBstDispImm,
694 PBstSize);
695 }
696
697 if (BlockingStoresDispSizeMap.empty())
698 continue;
699
700 // We found a store forward block, break the memcpy's load and store
701 // into smaller copies such that each smaller store that was causing
702 // a store block would now be copied separately.
703 MachineInstr *StoreInst = LoadStoreInstPair.second;
704 LLVM_DEBUG(dbgs() << "Blocked load and store instructions: \n");
705 LLVM_DEBUG(LoadInst->dump());
706 LLVM_DEBUG(StoreInst->dump());
707 LLVM_DEBUG(dbgs() << "Replaced with:\n");
708 removeRedundantBlockingStores(BlockingStoresDispSizeMap);
709 breakBlockedCopies(LoadInst, StoreInst, BlockingStoresDispSizeMap);
710 updateKillStatus(LoadInst, StoreInst);
711 ForRemoval.push_back(LoadInst);
712 ForRemoval.push_back(StoreInst);
713 }
714 for (auto *RemovedInst : ForRemoval) {
715 RemovedInst->eraseFromParent();
716 }
717 ForRemoval.clear();
718 BlockedLoadsStoresPairs.clear();
719 LLVM_DEBUG(dbgs() << "End X86AvoidStoreForwardBlocks\n";);
720
721 return Changed;
722}
723
724bool X86AvoidSFBLegacy::runOnMachineFunction(MachineFunction &MF) {
725 if (skipFunction(MF.getFunction()))
726 return false;
727 AliasAnalysis *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
728 X86AvoidSFBImpl Impl(AA);
729 return Impl.runOnMachineFunction(MF);
730}
731
732PreservedAnalyses
737 .getManager()
738 .getResult<AAManager>(MF.getFunction());
739 X86AvoidSFBImpl Impl(AA);
740 bool Changed = Impl.runOnMachineFunction(MF);
743}
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
#define LLVM_DEBUG(...)
Definition Debug.h:119
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 cl::opt< unsigned > X86AvoidSFBInspectionLimit("x86-sfb-inspection-limit", cl::desc("X86: Number of instructions backward to " "inspect for store forwarding blocks."), cl::init(20), cl::Hidden)
static bool isBlockingStore(int64_t LoadDispImm, unsigned LoadSize, int64_t StoreDispImm, unsigned StoreSize)
static bool isRelevantAddressingMode(MachineInstr *MI)
static cl::opt< bool > DisableX86AvoidStoreForwardBlocks("x86-disable-avoid-SFB", cl::Hidden, cl::desc("X86: Disable Store Forwarding Blocks fixup."), cl::init(false))
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 SmallVector< MachineInstr *, 2 > findPotentialBlockers(MachineInstr *LoadInst)
static void updateKillStatus(MachineInstr *LoadInst, MachineInstr *StoreInst)
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.
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:314
An instruction for reading from memory.
TypeSize getValue() const
Describe properties that are true of each instruction in the target description file.
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.
MachineMemOperand * getMachineMemOperand(MachinePointerInfo PtrInfo, MachineMemOperand::Flags f, LLT MemTy, Align base_alignment, const AAMDNodes &AAInfo=AAMDNodes(), const MDNode *Ranges=nullptr, SyncScope::ID SSID=SyncScope::System, AtomicOrdering Ordering=AtomicOrdering::NotAtomic, AtomicOrdering FailureOrdering=AtomicOrdering::NotAtomic)
getMachineMemOperand - Allocate a new MachineMemOperand.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
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
const MachineOperand & getOperand(unsigned i) const
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.
void setIsKill(bool Val=true)
Register getReg() const
getReg - Returns the register number.
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.
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
const ParentTy * getParent() const
Definition ilist_node.h:34
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
Abstract Attribute helper functions.
Definition Attributor.h:165
int getMemoryOperandNo(uint64_t TSFlags)
unsigned getOperandBias(const MCInstrDesc &Desc)
Compute whether all of the def operands are repeated in the uses and therefore should be skipped.
@ AddrNumOperands
Definition X86BaseInfo.h:36
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:578
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:633
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:407
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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.