51#include <unordered_map>
55#define DEBUG_TYPE "interleaved-load-combine"
60STATISTIC(NumInterleavedLoadCombine,
"Number of combined loads");
65 cl::desc(
"Disable combining of interleaved loads"));
69struct InterleavedLoadCombineImpl {
74 :
F(
F), DT(DT), MSSA(MSSA),
99 LoadInst *findFirstLoad(
const std::set<LoadInst *> &LIs);
186 Polynomial(
Value *V) : V(V) {
191 A =
APInt(Ty->getBitWidth(), 0);
195 Polynomial(
const APInt &
A,
unsigned ErrorMSBs = 0)
196 : ErrorMSBs(ErrorMSBs),
A(
A) {}
201 Polynomial() =
default;
204 void incErrorMSBs(
unsigned amt) {
205 if (ErrorMSBs == (
unsigned)-1)
209 if (ErrorMSBs >
A.getBitWidth())
210 ErrorMSBs =
A.getBitWidth();
214 void decErrorMSBs(
unsigned amt) {
215 if (ErrorMSBs == (
unsigned)-1)
225 Polynomial &add(
const APInt &
C) {
242 if (
C.getBitWidth() !=
A.getBitWidth()) {
252 Polynomial &mul(
const APInt &
C) {
303 if (
C.getBitWidth() !=
A.getBitWidth()) {
321 decErrorMSBs(
C.countr_zero());
324 pushBOperation(
Mul,
C);
329 Polynomial &lshr(
const APInt &
C) {
460 if (
C.getBitWidth() !=
A.getBitWidth()) {
469 unsigned shiftAmt =
C.getZExtValue();
470 if (shiftAmt >=
C.getBitWidth())
471 return mul(
APInt(
C.getBitWidth(), 0));
478 if (
A.countr_zero() < shiftAmt)
479 ErrorMSBs =
A.getBitWidth();
481 incErrorMSBs(shiftAmt);
484 pushBOperation(LShr,
C);
485 A =
A.lshr(shiftAmt);
491 Polynomial &sextOrTrunc(
unsigned n) {
492 if (n <
A.getBitWidth()) {
495 decErrorMSBs(
A.getBitWidth() - n);
497 pushBOperation(Trunc,
APInt(
sizeof(n) * 8, n));
499 if (n >
A.getBitWidth()) {
502 incErrorMSBs(n -
A.getBitWidth());
504 pushBOperation(SExt,
APInt(
sizeof(n) * 8, n));
511 bool isFirstOrder()
const {
return V !=
nullptr; }
514 bool isCompatibleTo(
const Polynomial &o)
const {
516 if (
A.getBitWidth() != o.A.getBitWidth())
520 if (!isFirstOrder() && !o.isFirstOrder())
528 if (
B.size() != o.B.size())
531 auto *ob = o.B.begin();
532 for (
const auto &b :
B) {
543 Polynomial
operator-(
const Polynomial &o)
const {
545 if (!isCompatibleTo(o))
551 return Polynomial(
A - o.A, std::max(ErrorMSBs, o.ErrorMSBs));
556 Polynomial Result(*
this);
563 Polynomial Result(*
this);
569 bool isProvenEqualTo(
const Polynomial &o)
const {
571 Polynomial r = *
this - o;
572 return (r.ErrorMSBs == 0) && (!r.isFirstOrder()) && (r.A.
isZero());
578 bool isProvenExact()
const {
return ErrorMSBs == 0; }
584 for (
const auto &BO :
P.B)
591 OS <<
"[{#ErrBits:" << ErrorMSBs <<
"} ";
596 OS <<
"(" << *V <<
") ";
614 OS << b.second <<
") ";
618 OS <<
"+ " <<
A <<
"]";
627 void pushBOperation(
const BOps
Op,
const APInt &
C) {
628 if (isFirstOrder()) {
629 B.push_back(std::make_pair(
Op,
C));
656 return PV == O.PV && VTy == O.VTy && Ofs.isProvenEqualTo(O.Ofs);
660struct OffsetKeyHash {
661 size_t operator()(
const OffsetKey &
K)
const {
675 VectorInfo(
const VectorInfo &c) : VTy(c.VTy) {
677 "Copying VectorInfo is neither implemented nor necessary,");
690 ElementInfo(Polynomial
Offset = Polynomial(),
LoadInst *LI =
nullptr)
701 std::set<LoadInst *> LIs;
704 std::set<Instruction *> Is;
719 VectorInfo &operator=(
const VectorInfo &other) =
delete;
721 virtual ~VectorInfo() {
delete[] EI; }
732 bool isInterleaved(
unsigned Factor,
const DataLayout &
DL)
const {
734 for (
unsigned i = 1; i < getDimension(); i++) {
735 if (!EI[i].Ofs.isProvenEqualTo(EI[0].Ofs + i * Factor *
Size)) {
753 return computeFromSVI(SVI, Result,
DL);
756 return computeFromLI(LI, Result,
DL);
759 return computeFromBCI(BCI, Result,
DL);
769 static bool computeFromBCI(
BitCastInst *BCI, VectorInfo &Result,
784 unsigned Factor = Result.VTy->getNumElements() / VTy->
getNumElements();
785 unsigned NewSize =
DL.getTypeAllocSize(Result.VTy->getElementType());
788 if (NewSize * Factor != OldSize)
792 if (!compute(
Op, Old,
DL))
795 for (
unsigned i = 0; i < Result.VTy->getNumElements(); i += Factor) {
796 for (
unsigned j = 0; j < Factor; j++) {
798 ElementInfo(Old.EI[i / Factor].Ofs + j * NewSize,
799 j == 0 ? Old.EI[i / Factor].LI :
nullptr);
805 Result.LIs.insert(Old.LIs.begin(), Old.LIs.end());
806 Result.Is.insert(Old.Is.begin(), Old.Is.end());
807 Result.Is.insert(BCI);
808 Result.SVI =
nullptr;
830 VectorInfo
LHS(ArgTy);
835 VectorInfo
RHS(ArgTy);
864 Result.LIs.insert(
LHS.LIs.begin(),
LHS.LIs.end());
865 Result.Is.insert(
LHS.Is.begin(),
LHS.Is.end());
868 Result.LIs.insert(
RHS.LIs.begin(),
RHS.LIs.end());
869 Result.Is.insert(
RHS.Is.begin(),
RHS.Is.end());
871 Result.Is.insert(SVI);
877 "Invalid ShuffleVectorInst (index out of bounds)");
880 Result.EI[j] = ElementInfo();
883 Result.EI[j] =
LHS.EI[i];
885 Result.EI[j] = ElementInfo();
890 Result.EI[j] = ElementInfo();
906 static bool computeFromLI(
LoadInst *LI, VectorInfo &Result,
917 if (!
DL.typeSizeEqualsStoreSize(Result.VTy->getElementType()))
925 Result.LIs.insert(LI);
926 Result.Is.insert(LI);
928 for (
unsigned i = 0; i < Result.getDimension(); i++) {
933 int64_t Ofs =
DL.getIndexedOffsetInType(Result.VTy, Idx);
934 Result.EI[i] = ElementInfo(
Offset + Ofs, i == 0 ? LI :
nullptr);
944 static void computePolynomialBinOp(
BinaryOperator &BO, Polynomial &Result) {
957 case Instruction::Add:
961 computePolynomial(*
LHS, Result);
962 Result.add(
C->getValue());
965 case Instruction::LShr:
969 computePolynomial(*
LHS, Result);
970 Result.lshr(
C->getValue());
977 Result = Polynomial(&BO);
984 static void computePolynomial(
Value &V, Polynomial &Result) {
986 computePolynomialBinOp(*BO, Result);
988 Result = Polynomial(&V);
997 static void computePolynomialFromPointer(
Value &Ptr, Polynomial &Result,
1003 Result = Polynomial();
1007 unsigned PointerBits =
1008 DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace());
1014 case Instruction::BitCast:
1015 computePolynomialFromPointer(*CI.
getOperand(0), Result, BasePtr,
DL);
1019 Polynomial(PointerBits, 0);
1027 APInt BaseOffset(PointerBits, 0);
1030 if (
GEP.accumulateConstantOffset(
DL, BaseOffset)) {
1031 Result = Polynomial(BaseOffset);
1032 BasePtr =
GEP.getPointerOperand();
1037 unsigned idxOperand, e;
1039 for (idxOperand = 1, e =
GEP.getNumOperands(); idxOperand < e;
1048 if (idxOperand + 1 != e) {
1049 Result = Polynomial();
1055 computePolynomial(*
GEP.getOperand(idxOperand), Result);
1060 DL.getIndexedOffsetInType(
GEP.getSourceElementType(), Indices);
1063 unsigned ResultSize =
DL.getTypeAllocSize(
GEP.getResultElementType());
1064 Result.sextOrTrunc(PointerBits);
1065 Result.mul(
APInt(PointerBits, ResultSize));
1066 Result.add(BaseOffset);
1067 BasePtr =
GEP.getPointerOperand();
1074 Polynomial(
DL.getIndexSizeInBits(PtrTy->getPointerAddressSpace()), 0);
1085 for (
unsigned i = 0; i < getDimension(); i++)
1086 OS << ((i == 0) ?
"[" :
", ") << EI[i].Ofs;
1095InterleavedLoadCombineImpl::findFirstLoad(
const std::set<LoadInst *> &LIs) {
1096 assert(!LIs.empty() &&
"No load instructions given.");
1099 BasicBlock *BB = (*LIs.begin())->getParent();
1101 *BB, [&LIs](Instruction &
I) ->
bool {
return is_contained(LIs, &
I); });
1108 OptimizationRemarkEmitter &ORE) {
1114 LoadInst *InsertionPoint = InterleavedLoad.
front()->EI[0].LI;
1117 if (!InsertionPoint)
1120 std::set<LoadInst *> LIs;
1121 std::set<Instruction *> Is;
1122 std::set<Instruction *> SVIs;
1129 unsigned Factor = InterleavedLoad.
size();
1132 for (
const VectorInfo *VI : InterleavedLoad) {
1134 LIs.insert(
VI->LIs.begin(),
VI->LIs.end());
1139 Is.insert(
VI->Is.begin(),
VI->Is.end());
1142 SVIs.insert(
VI->SVI);
1152 for (
const auto &
I : Is) {
1157 if (SVIs.find(
I) != SVIs.end())
1162 for (
auto *U :
I->users()) {
1174 LoadInst *
First = findFirstLoad(LIs);
1180 for (
auto *LI : LIs) {
1185 assert(!LIs.empty() &&
"There are no LoadInst to combine");
1194 for (
auto *LI : LIs)
1195 if (
Last->comesBefore(LI))
1198 Last->getIterator()))
1202 for (
const VectorInfo *VI : InterleavedLoad) {
1210 Type *ETy = InterleavedLoad.front()->SVI->getType()->getElementType();
1211 unsigned ElementsPerSVI =
1218 Instruction::Load, ILTy, Factor, Indices, InsertionPoint->
getAlign(),
1227 auto LI = Builder.CreateAlignedLoad(ILTy, Ptr, InsertionPoint->
getAlign(),
1228 "interleaved.wide.load");
1229 auto MSSAU = MemorySSAUpdater(&MSSA);
1232 MSSAU.insertUse(MSSALoad,
true);
1236 for (
const VectorInfo *VI : InterleavedLoad) {
1237 SmallVector<int, 4>
Mask;
1238 for (
unsigned j = 0;
j < ElementsPerSVI;
j++)
1239 Mask.push_back(i + j * Factor);
1241 Builder.SetInsertPoint(
VI->SVI);
1242 auto SVI = Builder.CreateShuffleVector(LI, Mask,
"interleaved.shuffle");
1243 VI->SVI->replaceAllUsesWith(SVI);
1247 NumInterleavedLoadCombine++;
1249 return OptimizationRemark(
DEBUG_TYPE,
"Combined Interleaved Load", LI)
1250 <<
"Load interleaved combined with factor "
1257bool InterleavedLoadCombineImpl::run() {
1258 OptimizationRemarkEmitter ORE(&
F);
1259 bool changed =
false;
1262 auto &
DL =
F.getDataLayout();
1265 for (
unsigned Factor = MaxFactor; Factor >= 2; Factor--) {
1269 for (BasicBlock &BB :
F) {
1270 std::list<VectorInfo> Candidates;
1271 for (Instruction &
I : BB) {
1281 VectorInfo &
C = Candidates.back();
1283 if (!VectorInfo::computeFromSVI(SVI,
C,
DL) ||
1284 !
C.isInterleaved(Factor,
DL)) {
1285 Candidates.pop_back();
1293 Candidates.pop_back();
1301 std::unordered_map<OffsetKey, SmallVector<VectorInfo *, 1>, OffsetKeyHash>
1303 for (VectorInfo &
C : Candidates) {
1304 if (!
C.EI[0].Ofs.isProvenExact())
1306 OffsetMap[{
C.PV,
C.VTy,
C.EI[0].Ofs}].push_back(&
C);
1310 SmallPtrSet<const VectorInfo *, 16> Consumed;
1315 auto FindNeighbor = [&](
const OffsetKey &
Key) -> VectorInfo * {
1316 auto It = OffsetMap.find(
Key);
1317 if (It == OffsetMap.end())
1319 for (VectorInfo *Cand :
reverse(It->second))
1325 for (VectorInfo &C0 : Candidates) {
1326 if (Consumed.
contains(&C0) || !C0.EI[0].Ofs.isProvenExact())
1329 unsigned Size =
DL.getTypeAllocSize(C0.VTy->getElementType());
1334 for (
unsigned i = 1; i < Factor; i++) {
1336 FindNeighbor({C0.PV, C0.VTy, C0.EI[0].Ofs + i *
Size});
1341 if (Group.
size() != Factor)
1344 if (combine(Group, ORE)) {
1362struct InterleavedLoadCombine :
public FunctionPass {
1365 InterleavedLoadCombine() : FunctionPass(
ID) {}
1367 StringRef getPassName()
const override {
1368 return "Interleaved Load Combine Pass";
1372 if (DisableInterleavedLoadCombine)
1375 auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
1382 return InterleavedLoadCombineImpl(
1383 F, getAnalysis<DominatorTreeWrapperPass>().
getDomTree(),
1384 getAnalysis<MemorySSAWrapperPass>().getMSSA(),
1385 getAnalysis<TargetTransformInfoWrapperPass>().getTTI(
F),
1386 TPC->getTM<TargetMachine>())
1390 void getAnalysisUsage(AnalysisUsage &AU)
const override {
1394 FunctionPass::getAnalysisUsage(AU);
1407 bool Changed = InterleavedLoadCombineImpl(
F, DT, MemSSA,
TTI, *TM).run();
1411char InterleavedLoadCombine::ID = 0;
1415 "Combine interleaved loads into wide loads and shufflevector instructions",
1422 "Combine interleaved loads into wide loads and shufflevector instructions",
1427 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
LLVM_ABI bool isGuaranteedToTransferExecutionToSuccessor(const Instruction *I)
Return true if this function can prove that the instruction I will always transfer execution to one o...
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.