[X86] Reduce Store Forward Block issues in HW - Recommit after fixing Bug 36346

If a load follows a store and reloads data that the store has written to memory, Intel microarchitectures can in many cases forward the data directly from the store to the load, This "store forwarding" saves cycles by enabling the load to directly obtain the data instead of accessing the data from cache or memory.
A "store forward block" occurs in cases that a store cannot be forwarded to the load. The most typical case of store forward block on Intel Core microarchiticutre that a small store cannot be forwarded to a large load.
The estimated penalty for a store forward block is ~13 cycles.

This pass tries to recognize and handle cases where "store forward block" is created by the compiler when lowering memcpy calls to a sequence
of a load and a store.

The pass currently only handles cases where memcpy is lowered to XMM/YMM registers, it tries to break the memcpy into smaller copies.
breaking the memcpy should be possible since there is no atomicity guarantee for loads and stores to XMM/YMM.

Differential revision: https://reviews.llvm.org/D41330

Change-Id: Ib48836ccdf6005989f7d4466fa2035b7b04415d9
llvm-svn: 328973
This commit is contained in:
Lama Saba 2018-04-02 13:48:28 +00:00
parent 6fd62feff8
commit 927468309f
7 changed files with 2742 additions and 0 deletions

View File

@ -31,6 +31,7 @@ set(sources
X86FastISel.cpp
X86FixupBWInsts.cpp
X86FixupLEAs.cpp
X86AvoidStoreForwardingBlocks.cpp
X86FixupSetCC.cpp
X86FloatingPoint.cpp
X86FrameLowering.cpp

View File

@ -70,6 +70,9 @@ FunctionPass *createX86OptimizeLEAs();
/// Return a pass that transforms setcc + movzx pairs into xor + setcc.
FunctionPass *createX86FixupSetCC();
/// Return a pass that avoids creating store forward block issues in the hardware.
FunctionPass *createX86AvoidStoreForwardingBlocks();
/// Return a pass that expands WinAlloca pseudo-instructions.
FunctionPass *createX86WinAllocaExpander();

View File

@ -0,0 +1,722 @@
//===- X86AvoidStoreForwardingBlockis.cpp - Avoid HW Store Forward Block --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// If a load follows a store and reloads data that the store has written to
// memory, Intel microarchitectures can in many cases forward the data directly
// from the store to the load, This "store forwarding" saves cycles by enabling
// the load to directly obtain the data instead of accessing the data from
// cache or memory.
// A "store forward block" occurs in cases that a store cannot be forwarded to
// the load. The most typical case of store forward block on Intel Core
// microarchitecture that a small store cannot be forwarded to a large load.
// The estimated penalty for a store forward block is ~13 cycles.
//
// This pass tries to recognize and handle cases where "store forward block"
// is created by the compiler when lowering memcpy calls to a sequence
// of a load and a store.
//
// The pass currently only handles cases where memcpy is lowered to
// XMM/YMM registers, it tries to break the memcpy into smaller copies.
// breaking the memcpy should be possible since there is no atomicity
// guarantee for loads and stores to XMM/YMM.
//
// It could be better for performance to solve the problem by loading
// to XMM/YMM then inserting the partial store before storing back from XMM/YMM
// to memory, but this will result in a more conservative optimization since it
// requires we prove that all memory accesses between the blocking store and the
// load must alias/don't alias before we can move the store, whereas the
// transformation done here is correct regardless to other memory accesses.
//===----------------------------------------------------------------------===//
#include "X86InstrInfo.h"
#include "X86Subtarget.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/MCInstrDesc.h"
using namespace llvm;
#define DEBUG_TYPE "x86-avoid-SFB"
namespace llvm {
void initializeX86AvoidSFBPassPass(PassRegistry &);
} // end namespace llvm
static cl::opt<bool> DisableX86AvoidStoreForwardBlocks(
"x86-disable-avoid-SFB", cl::Hidden,
cl::desc("X86: Disable Store Forwarding Blocks fixup."), cl::init(false));
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);
namespace {
using DisplacementSizeMap = std::map<int64_t, unsigned>;
class X86AvoidSFBPass : public MachineFunctionPass {
public:
static char ID;
X86AvoidSFBPass() : MachineFunctionPass(ID) {
initializeX86AvoidSFBPassPass(*PassRegistry::getPassRegistry());
}
StringRef getPassName() const override {
return "X86 Avoid Store Forwarding Blocks";
}
bool runOnMachineFunction(MachineFunction &MF) override;
void getAnalysisUsage(AnalysisUsage &AU) const override {
MachineFunctionPass::getAnalysisUsage(AU);
AU.addRequired<AAResultsWrapperPass>();
}
private:
MachineRegisterInfo *MRI;
const X86InstrInfo *TII;
const X86RegisterInfo *TRI;
SmallVector<std::pair<MachineInstr *, MachineInstr *>, 2>
BlockedLoadsStoresPairs;
SmallVector<MachineInstr *, 2> ForRemoval;
AliasAnalysis *AA;
/// \brief Returns couples of Load then Store to memory which look
/// like a memcpy.
void findPotentiallylBlockedCopies(MachineFunction &MF);
/// \brief Break the memcpy's load and store into smaller copies
/// such that each memory load that was blocked by a smaller store
/// would now be copied separately.
void breakBlockedCopies(MachineInstr *LoadInst, MachineInstr *StoreInst,
const DisplacementSizeMap &BlockingStoresDispSizeMap);
/// \brief Break a copy of size Size to smaller copies.
void buildCopies(int Size, MachineInstr *LoadInst, int64_t LdDispImm,
MachineInstr *StoreInst, int64_t StDispImm,
int64_t LMMOffset, int64_t SMMOffset);
void buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode, int64_t LoadDisp,
MachineInstr *StoreInst, unsigned NStoreOpcode,
int64_t StoreDisp, unsigned Size, int64_t LMMOffset,
int64_t SMMOffset);
bool alias(const MachineMemOperand &Op1, const MachineMemOperand &Op2) const;
unsigned getRegSizeInBytes(MachineInstr *Inst);
};
} // end anonymous namespace
char X86AvoidSFBPass::ID = 0;
INITIALIZE_PASS_BEGIN(X86AvoidSFBPass, DEBUG_TYPE, "Machine code sinking",
false, false)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_END(X86AvoidSFBPass, DEBUG_TYPE, "Machine code sinking", false,
false)
FunctionPass *llvm::createX86AvoidStoreForwardingBlocks() {
return new X86AvoidSFBPass();
}
static bool isXMMLoadOpcode(unsigned Opcode) {
return Opcode == X86::MOVUPSrm || Opcode == X86::MOVAPSrm ||
Opcode == X86::VMOVUPSrm || Opcode == X86::VMOVAPSrm ||
Opcode == X86::VMOVUPDrm || Opcode == X86::VMOVAPDrm ||
Opcode == X86::VMOVDQUrm || Opcode == X86::VMOVDQArm ||
Opcode == X86::VMOVUPSZ128rm || Opcode == X86::VMOVAPSZ128rm ||
Opcode == X86::VMOVUPDZ128rm || Opcode == X86::VMOVAPDZ128rm ||
Opcode == X86::VMOVDQU64Z128rm || Opcode == X86::VMOVDQA64Z128rm ||
Opcode == X86::VMOVDQU32Z128rm || Opcode == X86::VMOVDQA32Z128rm;
}
static bool isYMMLoadOpcode(unsigned Opcode) {
return Opcode == X86::VMOVUPSYrm || Opcode == X86::VMOVAPSYrm ||
Opcode == X86::VMOVUPDYrm || Opcode == X86::VMOVAPDYrm ||
Opcode == X86::VMOVDQUYrm || Opcode == X86::VMOVDQAYrm ||
Opcode == X86::VMOVUPSZ256rm || Opcode == X86::VMOVAPSZ256rm ||
Opcode == X86::VMOVUPDZ256rm || Opcode == X86::VMOVAPDZ256rm ||
Opcode == X86::VMOVDQU64Z256rm || Opcode == X86::VMOVDQA64Z256rm ||
Opcode == X86::VMOVDQU32Z256rm || Opcode == X86::VMOVDQA32Z256rm;
}
static bool isPotentialBlockedMemCpyLd(unsigned Opcode) {
return isXMMLoadOpcode(Opcode) || isYMMLoadOpcode(Opcode);
}
static bool isPotentialBlockedMemCpyPair(int LdOpcode, int StOpcode) {
switch (LdOpcode) {
case X86::MOVUPSrm:
case X86::MOVAPSrm:
return StOpcode == X86::MOVUPSmr || StOpcode == X86::MOVAPSmr;
case X86::VMOVUPSrm:
case X86::VMOVAPSrm:
return StOpcode == X86::VMOVUPSmr || StOpcode == X86::VMOVAPSmr;
case X86::VMOVUPDrm:
case X86::VMOVAPDrm:
return StOpcode == X86::VMOVUPDmr || StOpcode == X86::VMOVAPDmr;
case X86::VMOVDQUrm:
case X86::VMOVDQArm:
return StOpcode == X86::VMOVDQUmr || StOpcode == X86::VMOVDQAmr;
case X86::VMOVUPSZ128rm:
case X86::VMOVAPSZ128rm:
return StOpcode == X86::VMOVUPSZ128mr || StOpcode == X86::VMOVAPSZ128mr;
case X86::VMOVUPDZ128rm:
case X86::VMOVAPDZ128rm:
return StOpcode == X86::VMOVUPDZ128mr || StOpcode == X86::VMOVAPDZ128mr;
case X86::VMOVUPSYrm:
case X86::VMOVAPSYrm:
return StOpcode == X86::VMOVUPSYmr || StOpcode == X86::VMOVAPSYmr;
case X86::VMOVUPDYrm:
case X86::VMOVAPDYrm:
return StOpcode == X86::VMOVUPDYmr || StOpcode == X86::VMOVAPDYmr;
case X86::VMOVDQUYrm:
case X86::VMOVDQAYrm:
return StOpcode == X86::VMOVDQUYmr || StOpcode == X86::VMOVDQAYmr;
case X86::VMOVUPSZ256rm:
case X86::VMOVAPSZ256rm:
return StOpcode == X86::VMOVUPSZ256mr || StOpcode == X86::VMOVAPSZ256mr;
case X86::VMOVUPDZ256rm:
case X86::VMOVAPDZ256rm:
return StOpcode == X86::VMOVUPDZ256mr || StOpcode == X86::VMOVAPDZ256mr;
case X86::VMOVDQU64Z128rm:
case X86::VMOVDQA64Z128rm:
return StOpcode == X86::VMOVDQU64Z128mr || StOpcode == X86::VMOVDQA64Z128mr;
case X86::VMOVDQU32Z128rm:
case X86::VMOVDQA32Z128rm:
return StOpcode == X86::VMOVDQU32Z128mr || StOpcode == X86::VMOVDQA32Z128mr;
case X86::VMOVDQU64Z256rm:
case X86::VMOVDQA64Z256rm:
return StOpcode == X86::VMOVDQU64Z256mr || StOpcode == X86::VMOVDQA64Z256mr;
case X86::VMOVDQU32Z256rm:
case X86::VMOVDQA32Z256rm:
return StOpcode == X86::VMOVDQU32Z256mr || StOpcode == X86::VMOVDQA32Z256mr;
default:
return false;
}
}
static bool isPotentialBlockingStoreInst(int Opcode, int LoadOpcode) {
bool PBlock = false;
PBlock |= Opcode == X86::MOV64mr || Opcode == X86::MOV64mi32 ||
Opcode == X86::MOV32mr || Opcode == X86::MOV32mi ||
Opcode == X86::MOV16mr || Opcode == X86::MOV16mi ||
Opcode == X86::MOV8mr || Opcode == X86::MOV8mi;
if (isYMMLoadOpcode(LoadOpcode))
PBlock |= Opcode == X86::VMOVUPSmr || Opcode == X86::VMOVAPSmr ||
Opcode == X86::VMOVUPDmr || Opcode == X86::VMOVAPDmr ||
Opcode == X86::VMOVDQUmr || Opcode == X86::VMOVDQAmr ||
Opcode == X86::VMOVUPSZ128mr || Opcode == X86::VMOVAPSZ128mr ||
Opcode == X86::VMOVUPDZ128mr || Opcode == X86::VMOVAPDZ128mr ||
Opcode == X86::VMOVDQU64Z128mr ||
Opcode == X86::VMOVDQA64Z128mr ||
Opcode == X86::VMOVDQU32Z128mr || Opcode == X86::VMOVDQA32Z128mr;
return PBlock;
}
static const int MOV128SZ = 16;
static const int MOV64SZ = 8;
static const int MOV32SZ = 4;
static const int MOV16SZ = 2;
static const int MOV8SZ = 1;
static unsigned getYMMtoXMMLoadOpcode(unsigned LoadOpcode) {
switch (LoadOpcode) {
case X86::VMOVUPSYrm:
case X86::VMOVAPSYrm:
return X86::VMOVUPSrm;
case X86::VMOVUPDYrm:
case X86::VMOVAPDYrm:
return X86::VMOVUPDrm;
case X86::VMOVDQUYrm:
case X86::VMOVDQAYrm:
return X86::VMOVDQUrm;
case X86::VMOVUPSZ256rm:
case X86::VMOVAPSZ256rm:
return X86::VMOVUPSZ128rm;
case X86::VMOVUPDZ256rm:
case X86::VMOVAPDZ256rm:
return X86::VMOVUPDZ128rm;
case X86::VMOVDQU64Z256rm:
case X86::VMOVDQA64Z256rm:
return X86::VMOVDQU64Z128rm;
case X86::VMOVDQU32Z256rm:
case X86::VMOVDQA32Z256rm:
return X86::VMOVDQU32Z128rm;
default:
llvm_unreachable("Unexpected Load Instruction Opcode");
}
return 0;
}
static unsigned getYMMtoXMMStoreOpcode(unsigned StoreOpcode) {
switch (StoreOpcode) {
case X86::VMOVUPSYmr:
case X86::VMOVAPSYmr:
return X86::VMOVUPSmr;
case X86::VMOVUPDYmr:
case X86::VMOVAPDYmr:
return X86::VMOVUPDmr;
case X86::VMOVDQUYmr:
case X86::VMOVDQAYmr:
return X86::VMOVDQUmr;
case X86::VMOVUPSZ256mr:
case X86::VMOVAPSZ256mr:
return X86::VMOVUPSZ128mr;
case X86::VMOVUPDZ256mr:
case X86::VMOVAPDZ256mr:
return X86::VMOVUPDZ128mr;
case X86::VMOVDQU64Z256mr:
case X86::VMOVDQA64Z256mr:
return X86::VMOVDQU64Z128mr;
case X86::VMOVDQU32Z256mr:
case X86::VMOVDQA32Z256mr:
return X86::VMOVDQU32Z128mr;
default:
llvm_unreachable("Unexpected Load Instruction Opcode");
}
return 0;
}
static int getAddrOffset(MachineInstr *MI) {
const MCInstrDesc &Descl = MI->getDesc();
int AddrOffset = X86II::getMemoryOperandNo(Descl.TSFlags);
assert(AddrOffset != -1 && "Expected Memory Operand");
AddrOffset += X86II::getOperandBias(Descl);
return AddrOffset;
}
static MachineOperand &getBaseOperand(MachineInstr *MI) {
int AddrOffset = getAddrOffset(MI);
return MI->getOperand(AddrOffset + X86::AddrBaseReg);
}
static MachineOperand &getDispOperand(MachineInstr *MI) {
int AddrOffset = getAddrOffset(MI);
return MI->getOperand(AddrOffset + X86::AddrDisp);
}
// Relevant addressing modes contain only base register and immediate
// displacement or frameindex and immediate displacement.
// TODO: Consider expanding to other addressing modes in the future
static bool isRelevantAddressingMode(MachineInstr *MI) {
int AddrOffset = getAddrOffset(MI);
MachineOperand &Base = getBaseOperand(MI);
MachineOperand &Disp = getDispOperand(MI);
MachineOperand &Scale = MI->getOperand(AddrOffset + X86::AddrScaleAmt);
MachineOperand &Index = MI->getOperand(AddrOffset + X86::AddrIndexReg);
MachineOperand &Segment = MI->getOperand(AddrOffset + X86::AddrSegmentReg);
if (!((Base.isReg() && Base.getReg() != X86::NoRegister) || Base.isFI()))
return false;
if (!Disp.isImm())
return false;
if (Scale.getImm() != 1)
return false;
if (!(Index.isReg() && Index.getReg() == X86::NoRegister))
return false;
if (!(Segment.isReg() && Segment.getReg() == X86::NoRegister))
return false;
return true;
}
// Collect potentially blocking stores.
// Limit the number of instructions backwards we want to inspect
// since the effect of store block won't be visible if the store
// and load instructions have enough instructions in between to
// keep the core busy.
static SmallVector<MachineInstr *, 2>
findPotentialBlockers(MachineInstr *LoadInst) {
SmallVector<MachineInstr *, 2> PotentialBlockers;
unsigned BlockCount = 0;
const unsigned InspectionLimit = X86AvoidSFBInspectionLimit;
for (auto PBInst = std::next(MachineBasicBlock::reverse_iterator(LoadInst)),
E = LoadInst->getParent()->rend();
PBInst != E; ++PBInst) {
BlockCount++;
if (BlockCount >= InspectionLimit)
break;
MachineInstr &MI = *PBInst;
if (MI.getDesc().isCall())
return PotentialBlockers;
PotentialBlockers.push_back(&MI);
}
// If we didn't get to the instructions limit try predecessing blocks.
// Ideally we should traverse the predecessor blocks in depth with some
// coloring algorithm, but for now let's just look at the first order
// predecessors.
if (BlockCount < InspectionLimit) {
MachineBasicBlock *MBB = LoadInst->getParent();
int LimitLeft = InspectionLimit - BlockCount;
for (MachineBasicBlock::pred_iterator PB = MBB->pred_begin(),
PE = MBB->pred_end();
PB != PE; ++PB) {
MachineBasicBlock *PMBB = *PB;
int PredCount = 0;
for (MachineBasicBlock::reverse_iterator PBInst = PMBB->rbegin(),
PME = PMBB->rend();
PBInst != PME; ++PBInst) {
PredCount++;
if (PredCount >= LimitLeft)
break;
if (PBInst->getDesc().isCall())
break;
PotentialBlockers.push_back(&*PBInst);
}
}
}
return PotentialBlockers;
}
void X86AvoidSFBPass::buildCopy(MachineInstr *LoadInst, unsigned NLoadOpcode,
int64_t LoadDisp, MachineInstr *StoreInst,
unsigned NStoreOpcode, int64_t StoreDisp,
unsigned Size, int64_t LMMOffset,
int64_t SMMOffset) {
MachineOperand &LoadBase = getBaseOperand(LoadInst);
MachineOperand &StoreBase = getBaseOperand(StoreInst);
MachineBasicBlock *MBB = LoadInst->getParent();
MachineMemOperand *LMMO = *LoadInst->memoperands_begin();
MachineMemOperand *SMMO = *StoreInst->memoperands_begin();
unsigned Reg1 = MRI->createVirtualRegister(
TII->getRegClass(TII->get(NLoadOpcode), 0, TRI, *(MBB->getParent())));
BuildMI(*MBB, LoadInst, LoadInst->getDebugLoc(), TII->get(NLoadOpcode), Reg1)
.add(LoadBase)
.addImm(1)
.addReg(X86::NoRegister)
.addImm(LoadDisp)
.addReg(X86::NoRegister)
.addMemOperand(
MBB->getParent()->getMachineMemOperand(LMMO, LMMOffset, Size));
DEBUG(LoadInst->getPrevNode()->dump());
// If the load and store are consecutive, use the loadInst location to
// reduce register pressure.
MachineInstr *StInst = StoreInst;
if (StoreInst->getPrevNode() == LoadInst)
StInst = LoadInst;
BuildMI(*MBB, StInst, StInst->getDebugLoc(), TII->get(NStoreOpcode))
.add(StoreBase)
.addImm(1)
.addReg(X86::NoRegister)
.addImm(StoreDisp)
.addReg(X86::NoRegister)
.addReg(Reg1)
.addMemOperand(
MBB->getParent()->getMachineMemOperand(SMMO, SMMOffset, Size));
DEBUG(StInst->getPrevNode()->dump());
}
void X86AvoidSFBPass::buildCopies(int Size, MachineInstr *LoadInst,
int64_t LdDispImm, MachineInstr *StoreInst,
int64_t StDispImm, int64_t LMMOffset,
int64_t SMMOffset) {
int LdDisp = LdDispImm;
int StDisp = StDispImm;
while (Size > 0) {
if ((Size - MOV128SZ >= 0) && isYMMLoadOpcode(LoadInst->getOpcode())) {
Size = Size - MOV128SZ;
buildCopy(LoadInst, getYMMtoXMMLoadOpcode(LoadInst->getOpcode()), LdDisp,
StoreInst, getYMMtoXMMStoreOpcode(StoreInst->getOpcode()),
StDisp, MOV128SZ, LMMOffset, SMMOffset);
LdDisp += MOV128SZ;
StDisp += MOV128SZ;
LMMOffset += MOV128SZ;
SMMOffset += MOV128SZ;
continue;
}
if (Size - MOV64SZ >= 0) {
Size = Size - MOV64SZ;
buildCopy(LoadInst, X86::MOV64rm, LdDisp, StoreInst, X86::MOV64mr, StDisp,
MOV64SZ, LMMOffset, SMMOffset);
LdDisp += MOV64SZ;
StDisp += MOV64SZ;
LMMOffset += MOV64SZ;
SMMOffset += MOV64SZ;
continue;
}
if (Size - MOV32SZ >= 0) {
Size = Size - MOV32SZ;
buildCopy(LoadInst, X86::MOV32rm, LdDisp, StoreInst, X86::MOV32mr, StDisp,
MOV32SZ, LMMOffset, SMMOffset);
LdDisp += MOV32SZ;
StDisp += MOV32SZ;
LMMOffset += MOV32SZ;
SMMOffset += MOV32SZ;
continue;
}
if (Size - MOV16SZ >= 0) {
Size = Size - MOV16SZ;
buildCopy(LoadInst, X86::MOV16rm, LdDisp, StoreInst, X86::MOV16mr, StDisp,
MOV16SZ, LMMOffset, SMMOffset);
LdDisp += MOV16SZ;
StDisp += MOV16SZ;
LMMOffset += MOV16SZ;
SMMOffset += MOV16SZ;
continue;
}
if (Size - MOV8SZ >= 0) {
Size = Size - MOV8SZ;
buildCopy(LoadInst, X86::MOV8rm, LdDisp, StoreInst, X86::MOV8mr, StDisp,
MOV8SZ, LMMOffset, SMMOffset);
LdDisp += MOV8SZ;
StDisp += MOV8SZ;
LMMOffset += MOV8SZ;
SMMOffset += MOV8SZ;
continue;
}
}
assert(Size == 0 && "Wrong size division");
}
static void updateKillStatus(MachineInstr *LoadInst, MachineInstr *StoreInst) {
MachineOperand &LoadBase = getBaseOperand(LoadInst);
MachineOperand &StoreBase = getBaseOperand(StoreInst);
if (LoadBase.isReg()) {
MachineInstr *LastLoad = LoadInst->getPrevNode();
// If the original load and store to xmm/ymm were consecutive
// then the partial copies were also created in
// a consecutive order to reduce register pressure,
// and the location of the last load is before the last store.
if (StoreInst->getPrevNode() == LoadInst)
LastLoad = LoadInst->getPrevNode()->getPrevNode();
getBaseOperand(LastLoad).setIsKill(LoadBase.isKill());
}
if (StoreBase.isReg()) {
MachineInstr *StInst = StoreInst;
if (StoreInst->getPrevNode() == LoadInst)
StInst = LoadInst;
getBaseOperand(StInst->getPrevNode()).setIsKill(StoreBase.isKill());
}
}
bool X86AvoidSFBPass::alias(const MachineMemOperand &Op1,
const MachineMemOperand &Op2) const {
if (!Op1.getValue() || !Op2.getValue())
return true;
int64_t MinOffset = std::min(Op1.getOffset(), Op2.getOffset());
int64_t Overlapa = Op1.getSize() + Op1.getOffset() - MinOffset;
int64_t Overlapb = Op2.getSize() + Op2.getOffset() - MinOffset;
AliasResult AAResult =
AA->alias(MemoryLocation(Op1.getValue(), Overlapa, Op1.getAAInfo()),
MemoryLocation(Op2.getValue(), Overlapb, Op2.getAAInfo()));
return AAResult != NoAlias;
}
void X86AvoidSFBPass::findPotentiallylBlockedCopies(MachineFunction &MF) {
for (auto &MBB : MF)
for (auto &MI : MBB) {
if (!isPotentialBlockedMemCpyLd(MI.getOpcode()))
continue;
int DefVR = MI.getOperand(0).getReg();
if (!MRI->hasOneUse(DefVR))
continue;
for (auto UI = MRI->use_nodbg_begin(DefVR), UE = MRI->use_nodbg_end();
UI != UE;) {
MachineOperand &StoreMO = *UI++;
MachineInstr &StoreMI = *StoreMO.getParent();
// Skip cases where the memcpy may overlap.
if (StoreMI.getParent() == MI.getParent() &&
isPotentialBlockedMemCpyPair(MI.getOpcode(), StoreMI.getOpcode()) &&
isRelevantAddressingMode(&MI) &&
isRelevantAddressingMode(&StoreMI)) {
assert(MI.hasOneMemOperand() &&
"Expected one memory operand for load instruction");
assert(StoreMI.hasOneMemOperand() &&
"Expected one memory operand for store instruction");
if (!alias(**MI.memoperands_begin(), **StoreMI.memoperands_begin()))
BlockedLoadsStoresPairs.push_back(std::make_pair(&MI, &StoreMI));
}
}
}
}
unsigned X86AvoidSFBPass::getRegSizeInBytes(MachineInstr *LoadInst) {
auto TRC = TII->getRegClass(TII->get(LoadInst->getOpcode()), 0, TRI,
*LoadInst->getParent()->getParent());
return TRI->getRegSizeInBits(*TRC) / 8;
}
void X86AvoidSFBPass::breakBlockedCopies(
MachineInstr *LoadInst, MachineInstr *StoreInst,
const DisplacementSizeMap &BlockingStoresDispSizeMap) {
int64_t LdDispImm = getDispOperand(LoadInst).getImm();
int64_t StDispImm = getDispOperand(StoreInst).getImm();
int64_t LMMOffset = (*LoadInst->memoperands_begin())->getOffset();
int64_t SMMOffset = (*StoreInst->memoperands_begin())->getOffset();
int64_t LdDisp1 = LdDispImm;
int64_t LdDisp2 = 0;
int64_t StDisp1 = StDispImm;
int64_t StDisp2 = 0;
unsigned Size1 = 0;
unsigned Size2 = 0;
int64_t LdStDelta = StDispImm - LdDispImm;
for (auto DispSizePair : BlockingStoresDispSizeMap) {
LdDisp2 = DispSizePair.first;
StDisp2 = DispSizePair.first + LdStDelta;
Size2 = DispSizePair.second;
// Avoid copying overlapping areas.
if (LdDisp2 < LdDisp1) {
int OverlapDelta = LdDisp1 - LdDisp2;
LdDisp2 += OverlapDelta;
StDisp2 += OverlapDelta;
Size2 -= OverlapDelta;
}
Size1 = std::abs(std::abs(LdDisp2) - std::abs(LdDisp1));
// Build a copy for the point until the current blocking store's
// displacement.
buildCopies(Size1, LoadInst, LdDisp1, StoreInst, StDisp1, LMMOffset,
SMMOffset);
// Build a copy for the current blocking store.
buildCopies(Size2, LoadInst, LdDisp2, StoreInst, StDisp2, LMMOffset + Size1,
SMMOffset + Size1);
LdDisp1 = LdDisp2 + Size2;
StDisp1 = StDisp2 + Size2;
LMMOffset += Size1 + Size2;
SMMOffset += Size1 + Size2;
}
unsigned Size3 = (LdDispImm + getRegSizeInBytes(LoadInst)) - LdDisp1;
buildCopies(Size3, LoadInst, LdDisp1, StoreInst, StDisp1, LMMOffset,
LMMOffset);
}
static bool hasSameBaseOpValue(MachineInstr *LoadInst,
MachineInstr *StoreInst) {
MachineOperand &LoadBase = getBaseOperand(LoadInst);
MachineOperand &StoreBase = getBaseOperand(StoreInst);
if (LoadBase.isReg() != StoreBase.isReg())
return false;
if (LoadBase.isReg())
return LoadBase.getReg() == StoreBase.getReg();
return LoadBase.getIndex() == StoreBase.getIndex();
}
static bool isBlockingStore(int64_t LoadDispImm, unsigned LoadSize,
int64_t StoreDispImm, unsigned StoreSize) {
return ((StoreDispImm >= LoadDispImm) &&
(StoreDispImm <= LoadDispImm + (LoadSize - StoreSize)));
}
// Keep track of all stores blocking a load
static void
updateBlockingStoresDispSizeMap(DisplacementSizeMap &BlockingStoresDispSizeMap,
int64_t DispImm, unsigned Size) {
if (BlockingStoresDispSizeMap.count(DispImm)) {
// Choose the smallest blocking store starting at this displacement.
if (BlockingStoresDispSizeMap[DispImm] > Size)
BlockingStoresDispSizeMap[DispImm] = Size;
} else
BlockingStoresDispSizeMap[DispImm] = Size;
}
// Remove blocking stores contained in each other.
static void
removeRedundantBlockingStores(DisplacementSizeMap &BlockingStoresDispSizeMap) {
if (BlockingStoresDispSizeMap.size() <= 1)
return;
int64_t PrevDisp = BlockingStoresDispSizeMap.begin()->first;
unsigned PrevSize = BlockingStoresDispSizeMap.begin()->second;
SmallVector<int64_t, 2> ForRemoval;
for (auto DispSizePair = std::next(BlockingStoresDispSizeMap.begin());
DispSizePair != BlockingStoresDispSizeMap.end(); ++DispSizePair) {
int64_t CurrDisp = DispSizePair->first;
unsigned CurrSize = DispSizePair->second;
if (CurrDisp + CurrSize <= PrevDisp + PrevSize) {
ForRemoval.push_back(PrevDisp);
}
PrevDisp = CurrDisp;
PrevSize = CurrSize;
}
for (auto Disp : ForRemoval)
BlockingStoresDispSizeMap.erase(Disp);
}
bool X86AvoidSFBPass::runOnMachineFunction(MachineFunction &MF) {
bool Changed = false;
if (DisableX86AvoidStoreForwardBlocks || skipFunction(MF.getFunction()) ||
!MF.getSubtarget<X86Subtarget>().is64Bit())
return false;
MRI = &MF.getRegInfo();
assert(MRI->isSSA() && "Expected MIR to be in SSA form");
TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
TRI = MF.getSubtarget<X86Subtarget>().getRegisterInfo();
AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
DEBUG(dbgs() << "Start X86AvoidStoreForwardBlocks\n";);
// Look for a load then a store to XMM/YMM which look like a memcpy
findPotentiallylBlockedCopies(MF);
for (auto LoadStoreInstPair : BlockedLoadsStoresPairs) {
MachineInstr *LoadInst = LoadStoreInstPair.first;
int64_t LdDispImm = getDispOperand(LoadInst).getImm();
DisplacementSizeMap BlockingStoresDispSizeMap;
SmallVector<MachineInstr *, 2> PotentialBlockers =
findPotentialBlockers(LoadInst);
for (auto PBInst : PotentialBlockers) {
if (!isPotentialBlockingStoreInst(PBInst->getOpcode(),
LoadInst->getOpcode()) ||
!isRelevantAddressingMode(PBInst))
continue;
int64_t PBstDispImm = getDispOperand(PBInst).getImm();
assert(PBInst->hasOneMemOperand() && "Expected One Memory Operand");
unsigned PBstSize = (*PBInst->memoperands_begin())->getSize();
// This check doesn't cover all cases, but it will suffice for now.
// TODO: take branch probability into consideration, if the blocking
// store is in an unreached block, breaking the memcopy could lose
// performance.
if (hasSameBaseOpValue(LoadInst, PBInst) &&
isBlockingStore(LdDispImm, getRegSizeInBytes(LoadInst), PBstDispImm,
PBstSize))
updateBlockingStoresDispSizeMap(BlockingStoresDispSizeMap, PBstDispImm,
PBstSize);
}
if (BlockingStoresDispSizeMap.empty())
continue;
// We found a store forward block, break the memcpy's load and store
// into smaller copies such that each smaller store that was causing
// a store block would now be copied separately.
MachineInstr *StoreInst = LoadStoreInstPair.second;
DEBUG(dbgs() << "Blocked load and store instructions: \n");
DEBUG(LoadInst->dump());
DEBUG(StoreInst->dump());
DEBUG(dbgs() << "Replaced with:\n");
removeRedundantBlockingStores(BlockingStoresDispSizeMap);
breakBlockedCopies(LoadInst, StoreInst, BlockingStoresDispSizeMap);
updateKillStatus(LoadInst, StoreInst);
ForRemoval.push_back(LoadInst);
ForRemoval.push_back(StoreInst);
}
for (auto RemovedInst : ForRemoval) {
RemovedInst->eraseFromParent();
}
ForRemoval.clear();
BlockedLoadsStoresPairs.clear();
DEBUG(dbgs() << "End X86AvoidStoreForwardBlocks\n";);
return Changed;
}

View File

@ -62,6 +62,7 @@ void initializeX86CallFrameOptimizationPass(PassRegistry &);
void initializeX86CmovConverterPassPass(PassRegistry &);
void initializeX86ExecutionDomainFixPass(PassRegistry &);
void initializeX86DomainReassignmentPass(PassRegistry &);
void initializeX86AvoidSFBPassPass(PassRegistry &);
} // end namespace llvm
@ -80,6 +81,7 @@ extern "C" void LLVMInitializeX86Target() {
initializeX86CmovConverterPassPass(PR);
initializeX86ExecutionDomainFixPass(PR);
initializeX86DomainReassignmentPass(PR);
initializeX86AvoidSFBPassPass(PR);
}
static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
@ -449,6 +451,7 @@ void X86PassConfig::addPreRegAlloc() {
addPass(createX86FixupSetCC());
addPass(createX86OptimizeLEAs());
addPass(createX86CallFrameOptimization());
addPass(createX86AvoidStoreForwardingBlocks());
}
addPass(createX86WinAllocaExpander());

View File

@ -89,6 +89,7 @@
; CHECK-NEXT: X86 Fixup SetCC
; CHECK-NEXT: X86 LEA Optimize
; CHECK-NEXT: X86 Optimize Call Frame
; CHECK-NEXT: X86 Avoid Store Forwarding Block
; CHECK-NEXT: X86 WinAlloca Expander
; CHECK-NEXT: Detect Dead Lanes
; CHECK-NEXT: Process Implicit Definitions

View File

@ -0,0 +1,521 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-linux | FileCheck %s -check-prefix=CHECK
; RUN: llc < %s -mtriple=x86_64-linux --x86-disable-avoid-SFB | FileCheck %s --check-prefix=DISABLED
; RUN: llc < %s -mtriple=x86_64-linux -mcpu=core-avx2 | FileCheck %s -check-prefix=CHECK-AVX2
; RUN: llc < %s -mtriple=x86_64-linux -mcpu=skx | FileCheck %s -check-prefix=CHECK-AVX512
; ModuleID = '../testSFB/testOverlapBlocks.c'
source_filename = "../testSFB/testOverlapBlocks.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: nounwind uwtable
define dso_local void @test_overlap_1(i8* nocapture %A, i32 %x) local_unnamed_addr #0 {
; CHECK-LABEL: test_overlap_1:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movl $7, -8(%rdi)
; CHECK-NEXT: movq -16(%rdi), %rax
; CHECK-NEXT: movq %rax, (%rdi)
; CHECK-NEXT: movl -8(%rdi), %eax
; CHECK-NEXT: movl %eax, 8(%rdi)
; CHECK-NEXT: movl -4(%rdi), %eax
; CHECK-NEXT: movl %eax, 12(%rdi)
; CHECK-NEXT: movslq %esi, %rax
; CHECK-NEXT: movq %rax, -9(%rdi)
; CHECK-NEXT: movq %rax, -16(%rdi)
; CHECK-NEXT: movb $0, -1(%rdi)
; CHECK-NEXT: movq -16(%rdi), %rax
; CHECK-NEXT: movq %rax, 16(%rdi)
; CHECK-NEXT: movl -8(%rdi), %eax
; CHECK-NEXT: movl %eax, 24(%rdi)
; CHECK-NEXT: movzwl -4(%rdi), %eax
; CHECK-NEXT: movw %ax, 28(%rdi)
; CHECK-NEXT: movb -2(%rdi), %al
; CHECK-NEXT: movb %al, 30(%rdi)
; CHECK-NEXT: movb -1(%rdi), %al
; CHECK-NEXT: movb %al, 31(%rdi)
; CHECK-NEXT: retq
;
; DISABLED-LABEL: test_overlap_1:
; DISABLED: # %bb.0: # %entry
; DISABLED-NEXT: movl $7, -8(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, (%rdi)
; DISABLED-NEXT: movslq %esi, %rax
; DISABLED-NEXT: movq %rax, -9(%rdi)
; DISABLED-NEXT: movq %rax, -16(%rdi)
; DISABLED-NEXT: movb $0, -1(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, 16(%rdi)
; DISABLED-NEXT: retq
;
; CHECK-AVX2-LABEL: test_overlap_1:
; CHECK-AVX2: # %bb.0: # %entry
; CHECK-AVX2-NEXT: movl $7, -8(%rdi)
; CHECK-AVX2-NEXT: movq -16(%rdi), %rax
; CHECK-AVX2-NEXT: movq %rax, (%rdi)
; CHECK-AVX2-NEXT: movl -8(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 8(%rdi)
; CHECK-AVX2-NEXT: movl -4(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 12(%rdi)
; CHECK-AVX2-NEXT: movslq %esi, %rax
; CHECK-AVX2-NEXT: movq %rax, -9(%rdi)
; CHECK-AVX2-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX2-NEXT: movb $0, -1(%rdi)
; CHECK-AVX2-NEXT: movq -16(%rdi), %rax
; CHECK-AVX2-NEXT: movq %rax, 16(%rdi)
; CHECK-AVX2-NEXT: movl -8(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 24(%rdi)
; CHECK-AVX2-NEXT: movzwl -4(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 28(%rdi)
; CHECK-AVX2-NEXT: movb -2(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 30(%rdi)
; CHECK-AVX2-NEXT: movb -1(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 31(%rdi)
; CHECK-AVX2-NEXT: retq
;
; CHECK-AVX512-LABEL: test_overlap_1:
; CHECK-AVX512: # %bb.0: # %entry
; CHECK-AVX512-NEXT: movl $7, -8(%rdi)
; CHECK-AVX512-NEXT: movq -16(%rdi), %rax
; CHECK-AVX512-NEXT: movq %rax, (%rdi)
; CHECK-AVX512-NEXT: movl -8(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 8(%rdi)
; CHECK-AVX512-NEXT: movl -4(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 12(%rdi)
; CHECK-AVX512-NEXT: movslq %esi, %rax
; CHECK-AVX512-NEXT: movq %rax, -9(%rdi)
; CHECK-AVX512-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX512-NEXT: movb $0, -1(%rdi)
; CHECK-AVX512-NEXT: movq -16(%rdi), %rax
; CHECK-AVX512-NEXT: movq %rax, 16(%rdi)
; CHECK-AVX512-NEXT: movl -8(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 24(%rdi)
; CHECK-AVX512-NEXT: movzwl -4(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 28(%rdi)
; CHECK-AVX512-NEXT: movb -2(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 30(%rdi)
; CHECK-AVX512-NEXT: movb -1(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 31(%rdi)
; CHECK-AVX512-NEXT: retq
entry:
%add.ptr = getelementptr inbounds i8, i8* %A, i64 -16
%add.ptr1 = getelementptr inbounds i8, i8* %A, i64 -8
%0 = bitcast i8* %add.ptr1 to i32*
store i32 7, i32* %0, align 4
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %A, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
%conv = sext i32 %x to i64
%add.ptr2 = getelementptr inbounds i8, i8* %A, i64 -9
%1 = bitcast i8* %add.ptr2 to i64*
store i64 %conv, i64* %1, align 8
%2 = bitcast i8* %add.ptr to i64*
store i64 %conv, i64* %2, align 8
%add.ptr5 = getelementptr inbounds i8, i8* %A, i64 -1
store i8 0, i8* %add.ptr5, align 1
%add.ptr6 = getelementptr inbounds i8, i8* %A, i64 16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 %add.ptr6, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
ret void
}
; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) #1
; Function Attrs: nounwind uwtable
define dso_local void @test_overlap_2(i8* nocapture %A, i32 %x) local_unnamed_addr #0 {
; CHECK-LABEL: test_overlap_2:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movslq %esi, %rax
; CHECK-NEXT: movq %rax, -16(%rdi)
; CHECK-NEXT: movq -16(%rdi), %rcx
; CHECK-NEXT: movq %rcx, (%rdi)
; CHECK-NEXT: movq -8(%rdi), %rcx
; CHECK-NEXT: movq %rcx, 8(%rdi)
; CHECK-NEXT: movq %rax, -8(%rdi)
; CHECK-NEXT: movl $7, -12(%rdi)
; CHECK-NEXT: movl -16(%rdi), %eax
; CHECK-NEXT: movl %eax, 16(%rdi)
; CHECK-NEXT: movl -12(%rdi), %eax
; CHECK-NEXT: movl %eax, 20(%rdi)
; CHECK-NEXT: movq -8(%rdi), %rax
; CHECK-NEXT: movq %rax, 24(%rdi)
; CHECK-NEXT: retq
;
; DISABLED-LABEL: test_overlap_2:
; DISABLED: # %bb.0: # %entry
; DISABLED-NEXT: movslq %esi, %rax
; DISABLED-NEXT: movq %rax, -16(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, (%rdi)
; DISABLED-NEXT: movq %rax, -8(%rdi)
; DISABLED-NEXT: movl $7, -12(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, 16(%rdi)
; DISABLED-NEXT: retq
;
; CHECK-AVX2-LABEL: test_overlap_2:
; CHECK-AVX2: # %bb.0: # %entry
; CHECK-AVX2-NEXT: movslq %esi, %rax
; CHECK-AVX2-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX2-NEXT: movq -16(%rdi), %rcx
; CHECK-AVX2-NEXT: movq %rcx, (%rdi)
; CHECK-AVX2-NEXT: movq -8(%rdi), %rcx
; CHECK-AVX2-NEXT: movq %rcx, 8(%rdi)
; CHECK-AVX2-NEXT: movq %rax, -8(%rdi)
; CHECK-AVX2-NEXT: movl $7, -12(%rdi)
; CHECK-AVX2-NEXT: movl -16(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 16(%rdi)
; CHECK-AVX2-NEXT: movl -12(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 20(%rdi)
; CHECK-AVX2-NEXT: movq -8(%rdi), %rax
; CHECK-AVX2-NEXT: movq %rax, 24(%rdi)
; CHECK-AVX2-NEXT: retq
;
; CHECK-AVX512-LABEL: test_overlap_2:
; CHECK-AVX512: # %bb.0: # %entry
; CHECK-AVX512-NEXT: movslq %esi, %rax
; CHECK-AVX512-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX512-NEXT: movq -16(%rdi), %rcx
; CHECK-AVX512-NEXT: movq %rcx, (%rdi)
; CHECK-AVX512-NEXT: movq -8(%rdi), %rcx
; CHECK-AVX512-NEXT: movq %rcx, 8(%rdi)
; CHECK-AVX512-NEXT: movq %rax, -8(%rdi)
; CHECK-AVX512-NEXT: movl $7, -12(%rdi)
; CHECK-AVX512-NEXT: movl -16(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 16(%rdi)
; CHECK-AVX512-NEXT: movl -12(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 20(%rdi)
; CHECK-AVX512-NEXT: movq -8(%rdi), %rax
; CHECK-AVX512-NEXT: movq %rax, 24(%rdi)
; CHECK-AVX512-NEXT: retq
entry:
%add.ptr = getelementptr inbounds i8, i8* %A, i64 -16
%conv = sext i32 %x to i64
%0 = bitcast i8* %add.ptr to i64*
store i64 %conv, i64* %0, align 8
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %A, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
%add.ptr3 = getelementptr inbounds i8, i8* %A, i64 -8
%1 = bitcast i8* %add.ptr3 to i64*
store i64 %conv, i64* %1, align 8
%add.ptr4 = getelementptr inbounds i8, i8* %A, i64 -12
%2 = bitcast i8* %add.ptr4 to i32*
store i32 7, i32* %2, align 4
%add.ptr5 = getelementptr inbounds i8, i8* %A, i64 16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 %add.ptr5, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
ret void
}
; Function Attrs: nounwind uwtable
define dso_local void @test_overlap_3(i8* nocapture %A, i32 %x) local_unnamed_addr #0 {
; CHECK-LABEL: test_overlap_3:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movl $7, -10(%rdi)
; CHECK-NEXT: movl -16(%rdi), %eax
; CHECK-NEXT: movl %eax, (%rdi)
; CHECK-NEXT: movzwl -12(%rdi), %eax
; CHECK-NEXT: movw %ax, 4(%rdi)
; CHECK-NEXT: movl -10(%rdi), %eax
; CHECK-NEXT: movl %eax, 6(%rdi)
; CHECK-NEXT: movl -6(%rdi), %eax
; CHECK-NEXT: movl %eax, 10(%rdi)
; CHECK-NEXT: movzwl -2(%rdi), %eax
; CHECK-NEXT: movw %ax, 14(%rdi)
; CHECK-NEXT: movslq %esi, %rax
; CHECK-NEXT: movq %rax, -9(%rdi)
; CHECK-NEXT: movq %rax, -16(%rdi)
; CHECK-NEXT: movb $0, -1(%rdi)
; CHECK-NEXT: movq -16(%rdi), %rax
; CHECK-NEXT: movq %rax, 16(%rdi)
; CHECK-NEXT: movzwl -8(%rdi), %eax
; CHECK-NEXT: movw %ax, 24(%rdi)
; CHECK-NEXT: movl -6(%rdi), %eax
; CHECK-NEXT: movl %eax, 26(%rdi)
; CHECK-NEXT: movb -2(%rdi), %al
; CHECK-NEXT: movb %al, 30(%rdi)
; CHECK-NEXT: movb -1(%rdi), %al
; CHECK-NEXT: movb %al, 31(%rdi)
; CHECK-NEXT: retq
;
; DISABLED-LABEL: test_overlap_3:
; DISABLED: # %bb.0: # %entry
; DISABLED-NEXT: movl $7, -10(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, (%rdi)
; DISABLED-NEXT: movslq %esi, %rax
; DISABLED-NEXT: movq %rax, -9(%rdi)
; DISABLED-NEXT: movq %rax, -16(%rdi)
; DISABLED-NEXT: movb $0, -1(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, 16(%rdi)
; DISABLED-NEXT: retq
;
; CHECK-AVX2-LABEL: test_overlap_3:
; CHECK-AVX2: # %bb.0: # %entry
; CHECK-AVX2-NEXT: movl $7, -10(%rdi)
; CHECK-AVX2-NEXT: movl -16(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, (%rdi)
; CHECK-AVX2-NEXT: movzwl -12(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 4(%rdi)
; CHECK-AVX2-NEXT: movl -10(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 6(%rdi)
; CHECK-AVX2-NEXT: movl -6(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 10(%rdi)
; CHECK-AVX2-NEXT: movzwl -2(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 14(%rdi)
; CHECK-AVX2-NEXT: movslq %esi, %rax
; CHECK-AVX2-NEXT: movq %rax, -9(%rdi)
; CHECK-AVX2-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX2-NEXT: movb $0, -1(%rdi)
; CHECK-AVX2-NEXT: movq -16(%rdi), %rax
; CHECK-AVX2-NEXT: movq %rax, 16(%rdi)
; CHECK-AVX2-NEXT: movzwl -8(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 24(%rdi)
; CHECK-AVX2-NEXT: movl -6(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 26(%rdi)
; CHECK-AVX2-NEXT: movb -2(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 30(%rdi)
; CHECK-AVX2-NEXT: movb -1(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 31(%rdi)
; CHECK-AVX2-NEXT: retq
;
; CHECK-AVX512-LABEL: test_overlap_3:
; CHECK-AVX512: # %bb.0: # %entry
; CHECK-AVX512-NEXT: movl $7, -10(%rdi)
; CHECK-AVX512-NEXT: movl -16(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, (%rdi)
; CHECK-AVX512-NEXT: movzwl -12(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 4(%rdi)
; CHECK-AVX512-NEXT: movl -10(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 6(%rdi)
; CHECK-AVX512-NEXT: movl -6(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 10(%rdi)
; CHECK-AVX512-NEXT: movzwl -2(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 14(%rdi)
; CHECK-AVX512-NEXT: movslq %esi, %rax
; CHECK-AVX512-NEXT: movq %rax, -9(%rdi)
; CHECK-AVX512-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX512-NEXT: movb $0, -1(%rdi)
; CHECK-AVX512-NEXT: movq -16(%rdi), %rax
; CHECK-AVX512-NEXT: movq %rax, 16(%rdi)
; CHECK-AVX512-NEXT: movzwl -8(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 24(%rdi)
; CHECK-AVX512-NEXT: movl -6(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 26(%rdi)
; CHECK-AVX512-NEXT: movb -2(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 30(%rdi)
; CHECK-AVX512-NEXT: movb -1(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 31(%rdi)
; CHECK-AVX512-NEXT: retq
entry:
%add.ptr = getelementptr inbounds i8, i8* %A, i64 -16
%add.ptr1 = getelementptr inbounds i8, i8* %A, i64 -10
%0 = bitcast i8* %add.ptr1 to i32*
store i32 7, i32* %0, align 4
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %A, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
%conv = sext i32 %x to i64
%add.ptr2 = getelementptr inbounds i8, i8* %A, i64 -9
%1 = bitcast i8* %add.ptr2 to i64*
store i64 %conv, i64* %1, align 8
%2 = bitcast i8* %add.ptr to i64*
store i64 %conv, i64* %2, align 8
%add.ptr5 = getelementptr inbounds i8, i8* %A, i64 -1
store i8 0, i8* %add.ptr5, align 1
%add.ptr6 = getelementptr inbounds i8, i8* %A, i64 16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 %add.ptr6, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
ret void
}
; Function Attrs: nounwind uwtable
define dso_local void @test_overlap_4(i8* nocapture %A, i32 %x) local_unnamed_addr #0 {
; CHECK-LABEL: test_overlap_4:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movups -16(%rdi), %xmm0
; CHECK-NEXT: movups %xmm0, (%rdi)
; CHECK-NEXT: movslq %esi, %rax
; CHECK-NEXT: movq %rax, -8(%rdi)
; CHECK-NEXT: movl %eax, -16(%rdi)
; CHECK-NEXT: movl $0, -11(%rdi)
; CHECK-NEXT: movl -16(%rdi), %eax
; CHECK-NEXT: movl %eax, 16(%rdi)
; CHECK-NEXT: movb -12(%rdi), %al
; CHECK-NEXT: movb %al, 20(%rdi)
; CHECK-NEXT: movl -11(%rdi), %eax
; CHECK-NEXT: movl %eax, 21(%rdi)
; CHECK-NEXT: movl -7(%rdi), %eax
; CHECK-NEXT: movl %eax, 25(%rdi)
; CHECK-NEXT: movzwl -3(%rdi), %eax
; CHECK-NEXT: movw %ax, 29(%rdi)
; CHECK-NEXT: movb -1(%rdi), %al
; CHECK-NEXT: movb %al, 31(%rdi)
; CHECK-NEXT: retq
;
; DISABLED-LABEL: test_overlap_4:
; DISABLED: # %bb.0: # %entry
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, (%rdi)
; DISABLED-NEXT: movslq %esi, %rax
; DISABLED-NEXT: movq %rax, -8(%rdi)
; DISABLED-NEXT: movl %eax, -16(%rdi)
; DISABLED-NEXT: movl $0, -11(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, 16(%rdi)
; DISABLED-NEXT: retq
;
; CHECK-AVX2-LABEL: test_overlap_4:
; CHECK-AVX2: # %bb.0: # %entry
; CHECK-AVX2-NEXT: vmovups -16(%rdi), %xmm0
; CHECK-AVX2-NEXT: vmovups %xmm0, (%rdi)
; CHECK-AVX2-NEXT: movslq %esi, %rax
; CHECK-AVX2-NEXT: movq %rax, -8(%rdi)
; CHECK-AVX2-NEXT: movl %eax, -16(%rdi)
; CHECK-AVX2-NEXT: movl $0, -11(%rdi)
; CHECK-AVX2-NEXT: movl -16(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 16(%rdi)
; CHECK-AVX2-NEXT: movb -12(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 20(%rdi)
; CHECK-AVX2-NEXT: movl -11(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 21(%rdi)
; CHECK-AVX2-NEXT: movl -7(%rdi), %eax
; CHECK-AVX2-NEXT: movl %eax, 25(%rdi)
; CHECK-AVX2-NEXT: movzwl -3(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 29(%rdi)
; CHECK-AVX2-NEXT: movb -1(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 31(%rdi)
; CHECK-AVX2-NEXT: retq
;
; CHECK-AVX512-LABEL: test_overlap_4:
; CHECK-AVX512: # %bb.0: # %entry
; CHECK-AVX512-NEXT: vmovups -16(%rdi), %xmm0
; CHECK-AVX512-NEXT: vmovups %xmm0, (%rdi)
; CHECK-AVX512-NEXT: movslq %esi, %rax
; CHECK-AVX512-NEXT: movq %rax, -8(%rdi)
; CHECK-AVX512-NEXT: movl %eax, -16(%rdi)
; CHECK-AVX512-NEXT: movl $0, -11(%rdi)
; CHECK-AVX512-NEXT: movl -16(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 16(%rdi)
; CHECK-AVX512-NEXT: movb -12(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 20(%rdi)
; CHECK-AVX512-NEXT: movl -11(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 21(%rdi)
; CHECK-AVX512-NEXT: movl -7(%rdi), %eax
; CHECK-AVX512-NEXT: movl %eax, 25(%rdi)
; CHECK-AVX512-NEXT: movzwl -3(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 29(%rdi)
; CHECK-AVX512-NEXT: movb -1(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 31(%rdi)
; CHECK-AVX512-NEXT: retq
entry:
%add.ptr = getelementptr inbounds i8, i8* %A, i64 -16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %A, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
%conv = sext i32 %x to i64
%add.ptr1 = getelementptr inbounds i8, i8* %A, i64 -8
%0 = bitcast i8* %add.ptr1 to i64*
store i64 %conv, i64* %0, align 8
%1 = bitcast i8* %add.ptr to i32*
store i32 %x, i32* %1, align 4
%add.ptr3 = getelementptr inbounds i8, i8* %A, i64 -11
%2 = bitcast i8* %add.ptr3 to i32*
store i32 0, i32* %2, align 4
%add.ptr4 = getelementptr inbounds i8, i8* %A, i64 16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 %add.ptr4, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
ret void
}
; Function Attrs: nounwind uwtable
define dso_local void @test_overlap_5(i8* nocapture %A, i32 %x) local_unnamed_addr #0 {
; CHECK-LABEL: test_overlap_5:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: movups -16(%rdi), %xmm0
; CHECK-NEXT: movups %xmm0, (%rdi)
; CHECK-NEXT: movslq %esi, %rax
; CHECK-NEXT: movq %rax, -16(%rdi)
; CHECK-NEXT: movb %al, -14(%rdi)
; CHECK-NEXT: movb $0, -11(%rdi)
; CHECK-NEXT: movzwl -16(%rdi), %eax
; CHECK-NEXT: movw %ax, 16(%rdi)
; CHECK-NEXT: movb -14(%rdi), %al
; CHECK-NEXT: movb %al, 18(%rdi)
; CHECK-NEXT: movzwl -13(%rdi), %eax
; CHECK-NEXT: movw %ax, 19(%rdi)
; CHECK-NEXT: movb -11(%rdi), %al
; CHECK-NEXT: movb %al, 21(%rdi)
; CHECK-NEXT: movq -10(%rdi), %rax
; CHECK-NEXT: movq %rax, 22(%rdi)
; CHECK-NEXT: movzwl -2(%rdi), %eax
; CHECK-NEXT: movw %ax, 30(%rdi)
; CHECK-NEXT: retq
;
; DISABLED-LABEL: test_overlap_5:
; DISABLED: # %bb.0: # %entry
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, (%rdi)
; DISABLED-NEXT: movslq %esi, %rax
; DISABLED-NEXT: movq %rax, -16(%rdi)
; DISABLED-NEXT: movb %al, -14(%rdi)
; DISABLED-NEXT: movb $0, -11(%rdi)
; DISABLED-NEXT: movups -16(%rdi), %xmm0
; DISABLED-NEXT: movups %xmm0, 16(%rdi)
; DISABLED-NEXT: retq
;
; CHECK-AVX2-LABEL: test_overlap_5:
; CHECK-AVX2: # %bb.0: # %entry
; CHECK-AVX2-NEXT: vmovups -16(%rdi), %xmm0
; CHECK-AVX2-NEXT: vmovups %xmm0, (%rdi)
; CHECK-AVX2-NEXT: movslq %esi, %rax
; CHECK-AVX2-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX2-NEXT: movb %al, -14(%rdi)
; CHECK-AVX2-NEXT: movb $0, -11(%rdi)
; CHECK-AVX2-NEXT: movzwl -16(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 16(%rdi)
; CHECK-AVX2-NEXT: movb -14(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 18(%rdi)
; CHECK-AVX2-NEXT: movzwl -13(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 19(%rdi)
; CHECK-AVX2-NEXT: movb -11(%rdi), %al
; CHECK-AVX2-NEXT: movb %al, 21(%rdi)
; CHECK-AVX2-NEXT: movq -10(%rdi), %rax
; CHECK-AVX2-NEXT: movq %rax, 22(%rdi)
; CHECK-AVX2-NEXT: movzwl -2(%rdi), %eax
; CHECK-AVX2-NEXT: movw %ax, 30(%rdi)
; CHECK-AVX2-NEXT: retq
;
; CHECK-AVX512-LABEL: test_overlap_5:
; CHECK-AVX512: # %bb.0: # %entry
; CHECK-AVX512-NEXT: vmovups -16(%rdi), %xmm0
; CHECK-AVX512-NEXT: vmovups %xmm0, (%rdi)
; CHECK-AVX512-NEXT: movslq %esi, %rax
; CHECK-AVX512-NEXT: movq %rax, -16(%rdi)
; CHECK-AVX512-NEXT: movb %al, -14(%rdi)
; CHECK-AVX512-NEXT: movb $0, -11(%rdi)
; CHECK-AVX512-NEXT: movzwl -16(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 16(%rdi)
; CHECK-AVX512-NEXT: movb -14(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 18(%rdi)
; CHECK-AVX512-NEXT: movzwl -13(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 19(%rdi)
; CHECK-AVX512-NEXT: movb -11(%rdi), %al
; CHECK-AVX512-NEXT: movb %al, 21(%rdi)
; CHECK-AVX512-NEXT: movq -10(%rdi), %rax
; CHECK-AVX512-NEXT: movq %rax, 22(%rdi)
; CHECK-AVX512-NEXT: movzwl -2(%rdi), %eax
; CHECK-AVX512-NEXT: movw %ax, 30(%rdi)
; CHECK-AVX512-NEXT: retq
entry:
%add.ptr = getelementptr inbounds i8, i8* %A, i64 -16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %A, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
%conv = sext i32 %x to i64
%0 = bitcast i8* %add.ptr to i64*
store i64 %conv, i64* %0, align 8
%conv2 = trunc i32 %x to i8
%add.ptr3 = getelementptr inbounds i8, i8* %A, i64 -14
store i8 %conv2, i8* %add.ptr3, align 1
%add.ptr4 = getelementptr inbounds i8, i8* %A, i64 -11
store i8 0, i8* %add.ptr4, align 1
%add.ptr5 = getelementptr inbounds i8, i8* %A, i64 16
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 %add.ptr5, i8* nonnull align 4 %add.ptr, i64 16, i1 false)
ret void
}
attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { argmemonly nounwind }

File diff suppressed because it is too large Load Diff