50#include <unordered_map>
54#define DEBUG_TYPE "interleaved-load-combine"
59STATISTIC(NumInterleavedLoadCombine,
"Number of combined loads");
64 cl::desc(
"Disable combining of interleaved loads"));
68struct InterleavedLoadCombineImpl {
73 :
F(
F), DT(DT), MSSA(MSSA),
98 LoadInst *findFirstLoad(
const std::set<LoadInst *> &LIs);
185 Polynomial(
Value *V) : V(V) {
190 A =
APInt(Ty->getBitWidth(), 0);
194 Polynomial(
const APInt &
A,
unsigned ErrorMSBs = 0)
195 : ErrorMSBs(ErrorMSBs),
A(
A) {}
200 Polynomial() =
default;
203 void incErrorMSBs(
unsigned amt) {
204 if (ErrorMSBs == (
unsigned)-1)
208 if (ErrorMSBs >
A.getBitWidth())
209 ErrorMSBs =
A.getBitWidth();
213 void decErrorMSBs(
unsigned amt) {
214 if (ErrorMSBs == (
unsigned)-1)
224 Polynomial &add(
const APInt &
C) {
241 if (
C.getBitWidth() !=
A.getBitWidth()) {
251 Polynomial &mul(
const APInt &
C) {
302 if (
C.getBitWidth() !=
A.getBitWidth()) {
320 decErrorMSBs(
C.countr_zero());
323 pushBOperation(
Mul,
C);
328 Polynomial &lshr(
const APInt &
C) {
459 if (
C.getBitWidth() !=
A.getBitWidth()) {
468 unsigned shiftAmt =
C.getZExtValue();
469 if (shiftAmt >=
C.getBitWidth())
470 return mul(
APInt(
C.getBitWidth(), 0));
477 if (
A.countr_zero() < shiftAmt)
478 ErrorMSBs =
A.getBitWidth();
480 incErrorMSBs(shiftAmt);
483 pushBOperation(LShr,
C);
484 A =
A.lshr(shiftAmt);
490 Polynomial &sextOrTrunc(
unsigned n) {
491 if (n <
A.getBitWidth()) {
494 decErrorMSBs(
A.getBitWidth() - n);
496 pushBOperation(Trunc,
APInt(
sizeof(n) * 8, n));
498 if (n >
A.getBitWidth()) {
501 incErrorMSBs(n -
A.getBitWidth());
503 pushBOperation(SExt,
APInt(
sizeof(n) * 8, n));
510 bool isFirstOrder()
const {
return V !=
nullptr; }
513 bool isCompatibleTo(
const Polynomial &o)
const {
515 if (
A.getBitWidth() != o.A.getBitWidth())
519 if (!isFirstOrder() && !o.isFirstOrder())
527 if (
B.size() != o.B.size())
530 auto *ob = o.B.begin();
531 for (
const auto &b :
B) {
542 Polynomial
operator-(
const Polynomial &o)
const {
544 if (!isCompatibleTo(o))
550 return Polynomial(
A - o.A, std::max(ErrorMSBs, o.ErrorMSBs));
555 Polynomial Result(*
this);
562 Polynomial Result(*
this);
568 bool isProvenEqualTo(
const Polynomial &o)
const {
570 Polynomial r = *
this - o;
571 return (r.ErrorMSBs == 0) && (!r.isFirstOrder()) && (r.A.
isZero());
577 bool isProvenExact()
const {
return ErrorMSBs == 0; }
583 for (
const auto &BO :
P.B)
590 OS <<
"[{#ErrBits:" << ErrorMSBs <<
"} ";
595 OS <<
"(" << *V <<
") ";
613 OS << b.second <<
") ";
617 OS <<
"+ " <<
A <<
"]";
626 void pushBOperation(
const BOps
Op,
const APInt &
C) {
627 if (isFirstOrder()) {
628 B.push_back(std::make_pair(
Op,
C));
653 return PV == O.PV && VTy == O.VTy && Ofs.isProvenEqualTo(O.Ofs);
657struct OffsetKeyHash {
658 size_t operator()(
const OffsetKey &K)
const {
672 VectorInfo(
const VectorInfo &c) : VTy(c.VTy) {
674 "Copying VectorInfo is neither implemented nor necessary,");
687 ElementInfo(Polynomial
Offset = Polynomial(),
LoadInst *LI =
nullptr)
698 std::set<LoadInst *> LIs;
701 std::set<Instruction *> Is;
716 VectorInfo &operator=(
const VectorInfo &other) =
delete;
718 virtual ~VectorInfo() {
delete[] EI; }
729 bool isInterleaved(
unsigned Factor,
const DataLayout &
DL)
const {
731 for (
unsigned i = 1; i < getDimension(); i++) {
732 if (!EI[i].Ofs.isProvenEqualTo(EI[0].Ofs + i * Factor *
Size)) {
750 return computeFromSVI(SVI, Result,
DL);
753 return computeFromLI(LI, Result,
DL);
756 return computeFromBCI(BCI, Result,
DL);
766 static bool computeFromBCI(
BitCastInst *BCI, VectorInfo &Result,
781 unsigned Factor = Result.VTy->getNumElements() / VTy->
getNumElements();
782 unsigned NewSize =
DL.getTypeAllocSize(Result.VTy->getElementType());
785 if (NewSize * Factor != OldSize)
789 if (!compute(
Op, Old,
DL))
792 for (
unsigned i = 0; i < Result.VTy->getNumElements(); i += Factor) {
793 for (
unsigned j = 0; j < Factor; j++) {
795 ElementInfo(Old.EI[i / Factor].Ofs + j * NewSize,
796 j == 0 ? Old.EI[i / Factor].LI :
nullptr);
802 Result.LIs.insert(Old.LIs.begin(), Old.LIs.end());
803 Result.Is.insert(Old.Is.begin(), Old.Is.end());
804 Result.Is.insert(BCI);
805 Result.SVI =
nullptr;
827 VectorInfo
LHS(ArgTy);
832 VectorInfo
RHS(ArgTy);
861 Result.LIs.insert(
LHS.LIs.begin(),
LHS.LIs.end());
862 Result.Is.insert(
LHS.Is.begin(),
LHS.Is.end());
865 Result.LIs.insert(
RHS.LIs.begin(),
RHS.LIs.end());
866 Result.Is.insert(
RHS.Is.begin(),
RHS.Is.end());
868 Result.Is.insert(SVI);
874 "Invalid ShuffleVectorInst (index out of bounds)");
877 Result.EI[j] = ElementInfo();
880 Result.EI[j] =
LHS.EI[i];
882 Result.EI[j] = ElementInfo();
887 Result.EI[j] = ElementInfo();
903 static bool computeFromLI(
LoadInst *LI, VectorInfo &Result,
914 if (!
DL.typeSizeEqualsStoreSize(Result.VTy->getElementType()))
922 Result.LIs.insert(LI);
923 Result.Is.insert(LI);
925 for (
unsigned i = 0; i < Result.getDimension(); i++) {
930 int64_t Ofs =
DL.getIndexedOffsetInType(Result.VTy, Idx);
931 Result.EI[i] = ElementInfo(
Offset + Ofs, i == 0 ? LI :
nullptr);
941 static void computePolynomialBinOp(
BinaryOperator &BO, Polynomial &Result) {
954 case Instruction::Add:
958 computePolynomial(*
LHS, Result);
959 Result.add(
C->getValue());
962 case Instruction::LShr:
966 computePolynomial(*
LHS, Result);
967 Result.lshr(
C->getValue());
974 Result = Polynomial(&BO);
981 static void computePolynomial(
Value &V, Polynomial &Result) {
983 computePolynomialBinOp(*BO, Result);
985 Result = Polynomial(&V);
994 static void computePolynomialFromPointer(
Value &Ptr, Polynomial &Result,
1000 Result = Polynomial();
1004 unsigned PointerBits =
1005 DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());
1011 case Instruction::BitCast:
1012 computePolynomialFromPointer(*CI.
getOperand(0), Result, BasePtr,
DL);
1016 Polynomial(PointerBits, 0);
1024 APInt BaseOffset(PointerBits, 0);
1027 if (
GEP.accumulateConstantOffset(
DL, BaseOffset)) {
1028 Result = Polynomial(BaseOffset);
1029 BasePtr =
GEP.getPointerOperand();
1034 unsigned idxOperand, e;
1036 for (idxOperand = 1, e =
GEP.getNumOperands(); idxOperand < e;
1045 if (idxOperand + 1 != e) {
1046 Result = Polynomial();
1052 computePolynomial(*
GEP.getOperand(idxOperand), Result);
1057 DL.getIndexedOffsetInType(
GEP.getSourceElementType(), Indices);
1060 unsigned ResultSize =
DL.getTypeAllocSize(
GEP.getResultElementType());
1061 Result.sextOrTrunc(PointerBits);
1062 Result.mul(
APInt(PointerBits, ResultSize));
1063 Result.add(BaseOffset);
1064 BasePtr =
GEP.getPointerOperand();
1071 Polynomial(
DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace()), 0);
1082 for (
unsigned i = 0; i < getDimension(); i++)
1083 OS << ((i == 0) ?
"[" :
", ") << EI[i].Ofs;
1092InterleavedLoadCombineImpl::findFirstLoad(
const std::set<LoadInst *> &LIs) {
1093 assert(!LIs.empty() &&
"No load instructions given.");
1096 BasicBlock *BB = (*LIs.begin())->getParent();
1098 *BB, [&LIs](Instruction &
I) ->
bool {
return is_contained(LIs, &
I); });
1105 OptimizationRemarkEmitter &ORE) {
1111 LoadInst *InsertionPoint = InterleavedLoad.
front()->EI[0].LI;
1114 if (!InsertionPoint)
1117 std::set<LoadInst *> LIs;
1118 std::set<Instruction *> Is;
1119 std::set<Instruction *> SVIs;
1126 unsigned Factor = InterleavedLoad.
size();
1129 for (
const VectorInfo *VI : InterleavedLoad) {
1131 LIs.insert(
VI->LIs.begin(),
VI->LIs.end());
1136 Is.insert(
VI->Is.begin(),
VI->Is.end());
1139 SVIs.insert(
VI->SVI);
1149 for (
const auto &
I : Is) {
1154 if (SVIs.find(
I) != SVIs.end())
1159 for (
auto *U :
I->users()) {
1171 LoadInst *
First = findFirstLoad(LIs);
1177 for (
auto *LI : LIs) {
1182 assert(!LIs.empty() &&
"There are no LoadInst to combine");
1185 for (
const VectorInfo *VI : InterleavedLoad) {
1193 Type *ETy = InterleavedLoad.front()->SVI->getType()->getElementType();
1194 unsigned ElementsPerSVI =
1201 Instruction::Load, ILTy, Factor, Indices, InsertionPoint->
getAlign(),
1210 auto LI = Builder.CreateAlignedLoad(ILTy, Ptr, InsertionPoint->
getAlign(),
1211 "interleaved.wide.load");
1212 auto MSSAU = MemorySSAUpdater(&MSSA);
1215 MSSAU.insertUse(MSSALoad,
true);
1219 for (
const VectorInfo *VI : InterleavedLoad) {
1220 SmallVector<int, 4>
Mask;
1221 for (
unsigned j = 0;
j < ElementsPerSVI;
j++)
1222 Mask.push_back(i + j * Factor);
1224 Builder.SetInsertPoint(
VI->SVI);
1225 auto SVI = Builder.CreateShuffleVector(LI, Mask,
"interleaved.shuffle");
1226 VI->SVI->replaceAllUsesWith(SVI);
1230 NumInterleavedLoadCombine++;
1232 return OptimizationRemark(
DEBUG_TYPE,
"Combined Interleaved Load", LI)
1233 <<
"Load interleaved combined with factor "
1240bool InterleavedLoadCombineImpl::run() {
1241 OptimizationRemarkEmitter ORE(&
F);
1242 bool changed =
false;
1245 auto &
DL =
F.getDataLayout();
1248 for (
unsigned Factor = MaxFactor; Factor >= 2; Factor--) {
1251 for (BasicBlock &BB :
F) {
1252 std::list<VectorInfo> Candidates;
1253 for (Instruction &
I : BB) {
1264 if (!VectorInfo::computeFromSVI(SVI, Candidates.back(),
DL)) {
1265 Candidates.pop_back();
1269 if (!Candidates.back().isInterleaved(Factor,
DL))
1270 Candidates.pop_back();
1278 std::unordered_map<OffsetKey, SmallVector<VectorInfo *, 1>, OffsetKeyHash>
1280 for (VectorInfo &
C : Candidates) {
1281 if (!
C.EI[0].Ofs.isProvenExact())
1283 OffsetMap[{
C.PV,
C.VTy,
C.EI[0].Ofs}].push_back(&
C);
1287 SmallPtrSet<const VectorInfo *, 16> Consumed;
1292 auto FindNeighbor = [&](
const OffsetKey &
Key) -> VectorInfo * {
1293 auto It = OffsetMap.find(
Key);
1294 if (It == OffsetMap.end())
1296 for (VectorInfo *Cand :
reverse(It->second))
1302 for (VectorInfo &C0 : Candidates) {
1303 if (Consumed.
contains(&C0) || !C0.EI[0].Ofs.isProvenExact())
1306 unsigned Size =
DL.getTypeAllocSize(C0.VTy->getElementType());
1311 for (
unsigned i = 1; i < Factor; i++) {
1313 FindNeighbor({C0.PV, C0.VTy, C0.EI[0].Ofs + i *
Size});
1318 if (Group.
size() != Factor)
1321 if (combine(Group, ORE)) {
1339struct InterleavedLoadCombine :
public FunctionPass {
1342 InterleavedLoadCombine() : FunctionPass(
ID) {}
1344 StringRef getPassName()
const override {
1345 return "Interleaved Load Combine Pass";
1349 if (DisableInterleavedLoadCombine)
1352 auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
1359 return InterleavedLoadCombineImpl(
1360 F, getAnalysis<DominatorTreeWrapperPass>().
getDomTree(),
1361 getAnalysis<MemorySSAWrapperPass>().getMSSA(),
1362 getAnalysis<TargetTransformInfoWrapperPass>().getTTI(
F),
1363 TPC->getTM<TargetMachine>())
1367 void getAnalysisUsage(AnalysisUsage &AU)
const override {
1371 FunctionPass::getAnalysisUsage(AU);
1384 bool Changed = InterleavedLoadCombineImpl(
F, DT, MemSSA,
TTI, *TM).run();
1388char InterleavedLoadCombine::ID = 0;
1392 "Combine interleaved loads into wide loads and shufflevector instructions",
1399 "Combine interleaved loads into wide loads and shufflevector instructions",
1404 auto P =
new InterleavedLoadCombine();
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static cl::opt< OutputCostKind > CostKind("cost-kind", cl::desc("Target cost kind"), cl::init(OutputCostKind::RecipThroughput), cl::values(clEnumValN(OutputCostKind::RecipThroughput, "throughput", "Reciprocal throughput"), clEnumValN(OutputCostKind::Latency, "latency", "Instruction latency"), clEnumValN(OutputCostKind::CodeSize, "code-size", "Code size"), clEnumValN(OutputCostKind::SizeAndLatency, "size-latency", "Code size and latency"), clEnumValN(OutputCostKind::All, "all", "Print all cost kinds")))
static bool runOnFunction(Function &F, bool PostInlining)
This file exposes an interface to building/using memory SSA to walk memory instructions using a use/d...
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
static DominatorTree getDomTree(Function &F)
This file defines the SmallPtrSet class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
This file describes how to lower LLVM code to machine code.
Target-Independent Code Generator Pass Configuration Options pass.
Class for arbitrary precision integers.
bool isZero() const
Determine if this value is zero, i.e. all bits are clear.
AnalysisUsage & addRequired()
Represent a constant reference to an array (0 or more elements consecutively in memory),...
const T & front() const
Get the first element.
size_t size() const
Get the array size.
LLVM Basic Block Representation.
InstListType::iterator iterator
Instruction iterators...
BinaryOps getOpcode() const
This class represents a no-op cast from one type to another.
This is the base class for all instructions that perform data casts.
Instruction::CastOps getOpcode() const
Return the opcode of this CastInst.
This is the shared class of boolean and integer constants.
A parsed version of the target data layout string in and methods for querying it.
Analysis pass which computes a DominatorTree.
Legacy analysis pass which computes a DominatorTree.
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
LLVM_ABI bool dominates(const BasicBlock *BB, const Use &U) const
Return true if the (end of the) basic block BB dominates the use U.
Class to represent fixed width SIMD vectors.
unsigned getNumElements() const
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
FunctionPass class - This class is used to implement most global optimizations.
an instruction for type-safe pointer arithmetic to access elements of arrays and structs
LLVM_ABI bool isCommutative() const LLVM_READONLY
Return true if the instruction is commutative:
LLVM_ABI bool isAtomic() const LLVM_READONLY
Return true if this instruction has an AtomicOrdering of unordered or higher.
Class to represent integer types.
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
An instruction for reading from memory.
unsigned getPointerAddressSpace() const
Returns the address space of the pointer operand.
Value * getPointerOperand()
bool isVolatile() const
Return true if this is a load from a volatile memory location.
Align getAlign() const
Return the alignment of the access that is being performed.
An analysis that produces MemorySSA for a function.
Legacy analysis pass which computes MemorySSA.
Encapsulates MemorySSA, including all data associated with memory accesses.
LLVM_ABI bool dominates(const MemoryAccess *A, const MemoryAccess *B) const
Given two memory accesses in potentially different blocks, determine whether MemoryAccess A dominates...
MemoryUseOrDef * getMemoryAccess(const Instruction *I) const
Given a memory Mod/Ref'ing instruction, get the MemorySSA access associated with it.
MemoryAccess * getDefiningAccess() const
Get the access that produces the memory state used by this Use.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
This instruction constructs a fixed permutation of two input vectors.
static LLVM_ABI void getShuffleMask(const Constant *Mask, SmallVectorImpl< int > &Result)
Convert the input shuffle mask operand to a vector of integers.
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Analysis pass providing the TargetTransformInfo.
virtual unsigned getMaxSupportedInterleaveFactor() const
Get the maximum supported factor for interleaved memory accesses.
This class defines information used to lower LLVM code to legal SelectionDAG operators that the targe...
Primary interface to the complete machine description for the target machine.
virtual const TargetSubtargetInfo * getSubtargetImpl(const Function &) const
Virtual method implemented by subclasses that returns a reference to that target's TargetSubtargetInf...
virtual const TargetLowering * getTargetLowering() const
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Value * getOperand(unsigned i) const
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
LLVMContext & getContext() const
All values hold a context through their type.
Type * getElementType() const
An opaque object representing a hash code.
const ParentTy * getParent() const
This class implements an extremely fast bulk output stream that can only output to a stream.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
@ FMA
FMA - Perform a * b + c with no intermediate rounding step.
@ BasicBlock
Various leaf nodes.
initializer< Ty > init(const Ty &Val)
DiagnosticInfoOptimizationBase::Argument NV
This is an optimization pass for GlobalISel generic memory operations.
hash_code hash_value(const FixedPointSemantics &Val)
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
bool operator==(const AddressRangeValuePair &LHS, const AddressRangeValuePair &RHS)
auto reverse(ContainerTy &&C)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
SmallVector< ValueTypeFromRangeType< R >, Size > to_vector(R &&Range)
Given a range of type R, iterate the entire range and return a SmallVector with elements of the vecto...
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
DWARFExpression::Operation Op
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
ArrayRef(const T &OneElt) -> ArrayRef< T >
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
constexpr auto seq(T Begin, T End)
Iterate over an integral type from Begin up to - but not including - End.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
APInt operator+(APInt a, const APInt &b)
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
LLVM_ABI FunctionPass * createInterleavedLoadCombinePass()
InterleavedLoadCombines Pass - This pass identifies interleaved loads and combines them into wide loa...
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.