29#include "llvm/IR/IntrinsicsARM.h"
41#define DEBUG_TYPE "arm-parallel-dsp"
43STATISTIC(NumSMLAD ,
"Number of smlad instructions generated");
47 cl::desc(
"Disable the ARM Parallel DSP pass"));
51 cl::desc(
"Limit the number of loads analysed"));
67 bool Exchange =
false;
74 bool HasTwoLoadInputs()
const {
78 LoadInst *getBaseLoad()
const {
90 SetVector<Instruction*> Adds;
98 void InsertAdd(Instruction *
I) { Adds.insert(
I); }
103 auto GetMulOperand = [](
Value *
V) -> Instruction* {
106 if (
I->getOpcode() == Instruction::Mul)
109 if (
I->getOpcode() == Instruction::Mul)
118 Muls.push_back(std::make_unique<MulCandidate>(
I,
LHS,
RHS));
121 for (
auto *
Add : Adds) {
124 if (
auto *
Mul = GetMulOperand(
Add->getOperand(0)))
126 if (
auto *
Mul = GetMulOperand(
Add->getOperand(1)))
134 bool InsertAcc(
Value *V) {
143 void AddMulPair(MulCandidate *Mul0, MulCandidate *Mul1,
144 bool Exchange =
false) {
146 << *Mul0->Root <<
"\n"
147 << *Mul1->Root <<
"\n");
151 Mul1->Exchange =
true;
152 MulPairs.push_back(std::make_pair(Mul0, Mul1));
158 bool is64Bit()
const {
return Root->getType()->isIntegerTy(64); }
163 Value *getAccumulator() {
return Acc; }
166 SetVector<Instruction*> &getAdds() {
return Adds; }
170 MulCandList &getMuls() {
return Muls; }
174 MulPairList &getMulPairs() {
return MulPairs; }
177 void UpdateRoot(Instruction *SMLAD) {
178 Root->replaceAllUsesWith(SMLAD);
183 for (
auto *
Add : Adds)
185 for (
auto &
Mul : Muls)
187 <<
" " << *
Mul->LHS <<
"\n"
188 <<
" " << *
Mul->RHS <<
"\n");
195 LoadInst *NewLd =
nullptr;
199 WidenedLoad(SmallVectorImpl<LoadInst*> &Lds, LoadInst *Wide)
203 LoadInst *getLoad() {
211 TargetLibraryInfo *TLI;
213 const DataLayout *DL;
215 std::map<LoadInst*, LoadInst*> LoadPairs;
216 SmallPtrSet<LoadInst*, 4> OffsetLoads;
217 std::map<LoadInst*, std::unique_ptr<WidenedLoad>> WideLoads;
220 bool IsNarrowSequence(
Value *V);
222 bool RecordMemoryOps(BasicBlock *BB);
224 bool AreSequentialLoads(LoadInst *Ld0, LoadInst *Ld1, MemInstList &VecMem);
225 LoadInst* CreateWideLoad(MemInstList &Loads, IntegerType *LoadTy);
233 bool MatchSMLAD(Function &
F);
238 ARMParallelDSP() : FunctionPass(ID) { }
240 void getAnalysisUsage(AnalysisUsage &AU)
const override {
241 FunctionPass::getAnalysisUsage(AU);
258 SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE();
259 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
260 TLI = &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(
F);
261 DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
262 auto &TPC = getAnalysis<TargetPassConfig>();
265 DL = &M->getDataLayout();
267 auto &TM = TPC.getTM<TargetMachine>();
268 auto *
ST = &TM.getSubtarget<ARMSubtarget>(
F);
270 if (!
ST->allowsUnalignedMem()) {
271 LLVM_DEBUG(
dbgs() <<
"Unaligned memory access not supported: not "
272 "running pass ARMParallelDSP\n");
277 LLVM_DEBUG(
dbgs() <<
"DSP extension not enabled: not running pass "
282 if (!
ST->isLittle()) {
283 LLVM_DEBUG(
dbgs() <<
"Only supporting little endian: not running pass "
284 <<
"ARMParallelDSP\n");
291 bool Changes = MatchSMLAD(
F);
298 MemInstList &VecMem) {
302 auto It = LoadPairs.find(Ld0);
303 if (It == LoadPairs.end() || It->second != Ld1)
312 VecMem.push_back(Ld0);
313 VecMem.push_back(Ld1);
322template<
unsigned MaxBitW
idth>
323bool ARMParallelDSP::IsNarrowSequence(
Value *V) {
325 if (SExt->getSrcTy()->getIntegerBitWidth() != MaxBitWidth)
330 return LoadPairs.count(Ld) || OffsetLoads.
count(Ld);
338bool ARMParallelDSP::RecordMemoryOps(BasicBlock *BB) {
340 SmallVector<Instruction*, 8> Writes;
347 for (
auto &
I : *BB) {
348 if (
I.mayWriteToMemory())
351 if (!Ld || !Ld->isSimple() ||
360 using InstSet = std::set<Instruction*>;
361 using DepMap = std::map<Instruction*, InstSet>;
366 for (
auto *
Write : Writes) {
367 for (
auto *
Read : Loads) {
368 MemoryLocation ReadLoc =
369 MemoryLocation(
Read->getPointerOperand(),
Size);
380 auto SafeToPair = [&](LoadInst *
Base, LoadInst *
Offset) {
382 LoadInst *Dominator = BaseFirst ?
Base :
Offset;
383 LoadInst *Dominated = BaseFirst ?
Offset :
Base;
385 if (
auto It = RAWDeps.find(Dominated); It != RAWDeps.end()) {
386 InstSet &WritesBefore = It->second;
388 for (
auto *Before : WritesBefore) {
399 for (
auto *
Base : Loads) {
400 for (
auto *
Offset : Loads) {
414 dbgs() <<
"Consecutive load pairs:\n";
415 for (auto &MapIt : LoadPairs) {
416 LLVM_DEBUG(dbgs() << *MapIt.first <<
", "
417 << *MapIt.second <<
"\n");
420 return LoadPairs.size() > 1;
427bool ARMParallelDSP::Search(
Value *V, BasicBlock *BB,
Reduction &R) {
433 return R.InsertAcc(V);
435 if (
I->getParent() != BB)
438 switch (
I->getOpcode()) {
441 case Instruction::PHI:
443 return R.InsertAcc(V);
444 case Instruction::Add: {
451 bool ValidLHS = Search(
LHS, BB, R);
452 bool ValidRHS = Search(
RHS, BB, R);
454 if (ValidLHS && ValidRHS)
458 if (
R.getRoot() ==
I)
461 return R.InsertAcc(
I);
463 case Instruction::Mul: {
464 Value *MulOp0 =
I->getOperand(0);
465 Value *MulOp1 =
I->getOperand(1);
466 return IsNarrowSequence<16>(MulOp0) && IsNarrowSequence<16>(MulOp1);
468 case Instruction::SExt:
469 return Search(
I->getOperand(0), BB, R);
505bool ARMParallelDSP::MatchSMLAD(Function &
F) {
509 SmallPtrSet<Instruction*, 4> AllAdds;
510 if (!RecordMemoryOps(&BB))
513 for (Instruction &
I :
reverse(BB)) {
514 if (
I.getOpcode() != Instruction::Add)
520 const auto *Ty =
I.getType();
521 if (!Ty->isIntegerTy(32) && !Ty->isIntegerTy(64))
525 if (!Search(&
I, &BB, R))
531 if (!CreateParallelPairs(R))
534 InsertParallelMACs(R);
537 LLVM_DEBUG(
dbgs() <<
"BB after inserting parallel MACs:\n" << BB);
544bool ARMParallelDSP::CreateParallelPairs(
Reduction &R) {
547 if (
R.getMuls().size() < 2)
551 for (
auto &MulCand :
R.getMuls()) {
552 if (!MulCand->HasTwoLoadInputs())
556 auto CanPair = [&](
Reduction &
R, MulCandidate *PMul0, MulCandidate *PMul1) {
561 auto Ld0 =
static_cast<LoadInst*
>(PMul0->LHS);
562 auto Ld1 =
static_cast<LoadInst*
>(PMul1->LHS);
563 auto Ld2 =
static_cast<LoadInst*
>(PMul0->RHS);
564 auto Ld3 =
static_cast<LoadInst*
>(PMul1->RHS);
567 if (Ld0 == Ld2 || Ld1 == Ld3)
570 if (AreSequentialLoads(Ld0, Ld1, PMul0->VecLd)) {
571 if (AreSequentialLoads(Ld2, Ld3, PMul1->VecLd)) {
573 R.AddMulPair(PMul0, PMul1);
575 }
else if (AreSequentialLoads(Ld3, Ld2, PMul1->VecLd)) {
578 R.AddMulPair(PMul0, PMul1,
true);
581 }
else if (AreSequentialLoads(Ld1, Ld0, PMul0->VecLd) &&
582 AreSequentialLoads(Ld2, Ld3, PMul1->VecLd)) {
587 R.AddMulPair(PMul1, PMul0,
true);
593 MulCandList &Muls =
R.getMuls();
594 const unsigned Elems = Muls.size();
595 for (
unsigned i = 0; i < Elems; ++i) {
596 MulCandidate *PMul0 =
static_cast<MulCandidate*
>(Muls[i].get());
600 for (
unsigned j = 0;
j < Elems; ++
j) {
604 MulCandidate *PMul1 =
static_cast<MulCandidate*
>(Muls[
j].get());
613 assert(PMul0 != PMul1 &&
"expected different chains");
615 if (CanPair(R, PMul0, PMul1))
619 return !
R.getMulPairs().empty();
622void ARMParallelDSP::InsertParallelMACs(
Reduction &R) {
624 auto CreateSMLAD = [&](LoadInst* WideLd0, LoadInst *WideLd1,
629 Value*
Args[] = { WideLd0, WideLd1, Acc };
637 SMLAD = Acc->
getType()->isIntegerTy(32)
651 "expected at least one instruction");
664 Value *Acc =
R.getAccumulator();
669 MulCandList &MulCands =
R.getMuls();
670 for (
auto &MulCand : MulCands) {
678 assert(
R.is64Bit() &&
"expected 64-bit result");
691 Builder.SetInsertPoint(GetInsertPoint(
Mul, Acc));
692 Acc = Builder.CreateAdd(
Mul, Acc);
699 }
else if (Acc->
getType() !=
R.getType()) {
700 Builder.SetInsertPoint(
R.getRoot());
701 Acc = Builder.CreateSExt(Acc,
R.getType());
705 llvm::sort(
R.getMulPairs(), [](
auto &PairA,
auto &PairB) {
706 const Instruction *A = PairA.first->Root;
707 const Instruction *B = PairB.first->Root;
708 return A->comesBefore(B);
712 for (
auto &Pair :
R.getMulPairs()) {
713 MulCandidate *LHSMul = Pair.first;
714 MulCandidate *RHSMul = Pair.second;
715 LoadInst *BaseLHS = LHSMul->getBaseLoad();
716 LoadInst *BaseRHS = RHSMul->getBaseLoad();
717 auto LIt = WideLoads.find(BaseLHS);
718 LoadInst *WideLHS = LIt != WideLoads.end()
719 ? LIt->second->getLoad()
720 : CreateWideLoad(LHSMul->VecLd, Ty);
721 auto RIt = WideLoads.find(BaseRHS);
722 LoadInst *WideRHS = RIt != WideLoads.end()
723 ? RIt->second->getLoad()
724 : CreateWideLoad(RHSMul->VecLd, Ty);
726 Instruction *InsertAfter = GetInsertPoint(WideLHS, WideRHS);
727 InsertAfter = GetInsertPoint(InsertAfter, Acc);
728 Acc = CreateSMLAD(WideLHS, WideRHS, Acc, RHSMul->Exchange, InsertAfter);
733LoadInst* ARMParallelDSP::CreateWideLoad(MemInstList &Loads,
734 IntegerType *LoadTy) {
735 assert(Loads.size() == 2 &&
"currently only support widening two loads");
737 LoadInst *
Base = Loads[0];
738 LoadInst *
Offset = Loads[1];
743 assert((BaseSExt && OffsetSExt)
744 &&
"Loads should have a single, extending, user");
746 std::function<void(
Value*,
Value*)> MoveBefore =
761 MoveBefore(
Op, Source);
772 Value *VecPtr =
Base->getPointerOperand();
773 LoadInst *WideLoad = IRB.CreateAlignedLoad(LoadTy, VecPtr,
Base->getAlign());
776 MoveBefore(
Base->getPointerOperand(), VecPtr);
777 MoveBefore(VecPtr, WideLoad);
782 Value *Bottom = IRB.CreateTrunc(WideLoad,
Base->getType());
783 Value *NewBaseSExt = IRB.CreateSExt(Bottom, BaseSExt->
getType());
788 Value *Top = IRB.CreateLShr(WideLoad, ShiftVal);
789 Value *Trunc = IRB.CreateTrunc(Top, OffsetTy);
790 Value *NewOffsetSExt = IRB.CreateSExt(Trunc, OffsetSExt->
getType());
795 <<
"Created Wide Load:\n"
798 << *NewBaseSExt <<
"\n"
801 << *NewOffsetSExt <<
"\n");
802 WideLoads.emplace(std::make_pair(
Base,
803 std::make_unique<WidenedLoad>(Loads, WideLoad)));
808 return new ARMParallelDSP();
811char ARMParallelDSP::ID = 0;
814 "Transform functions to use DSP intrinsics",
false,
false)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static cl::opt< bool > DisableParallelDSP("disable-arm-parallel-dsp", cl::Hidden, cl::init(false), cl::desc("Disable the ARM Parallel DSP pass"))
static cl::opt< unsigned > NumLoadLimit("arm-parallel-dsp-load-limit", cl::Hidden, cl::init(16), cl::desc("Limit the number of loads analysed"))
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static DeltaTreeNode * getRoot(void *Root)
static bool runOnFunction(Function &F, bool PostInlining)
This is the interface for a simple mod/ref and alias analysis over globals.
Module.h This file contains the declarations for the Module class.
loop Loop Strength Reduction
Machine Check Debug Module
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Func getContext().diagnose(DiagnosticInfoUnsupported(Func
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)
static SymbolRef::Type getType(const Symbol *Sym)
Target-Independent Code Generator Pass Configuration Options pass.
static bool is64Bit(const char *name)
ModRefInfo getModRefInfo(const Instruction *I, const std::optional< MemoryLocation > &OptLoc)
Check whether or not an instruction may read or write the optionally specified memory location.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
InstListType::iterator iterator
Instruction iterators...
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.
FunctionPass class - This class is used to implement most global optimizations.
PointerType * getType() const
Global values are always pointers.
LLVM_ABI bool comesBefore(const Instruction *Other) const
Given an instruction Other in the same basic block as this instruction, return true if this instructi...
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
unsigned getBitWidth() const
Get the number of bits in this IntegerType.
An instruction for reading from memory.
static constexpr LocationSize beforeOrAfterPointer()
Any location before or after the base pointer (but still within the underlying object).
Pass interface - Implemented by all 'passes'.
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
void insert_range(Range &&R)
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool isIntegerTy() const
True if this is an instance of IntegerType.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
LLVM_ABI void dump() const
Support for debugging, callable in GDB: V->dump()
const ParentTy * getParent() const
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
initializer< Ty > init(const Ty &Val)
friend class Instruction
Iterator for Instructions in a `BasicBlock.
This is an optimization pass for GlobalISel generic memory operations.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
auto reverse(ContainerTy &&C)
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
bool isModOrRefSet(const ModRefInfo MRI)
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...
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
@ Mul
Product of integers.
LLVM_ABI bool isConsecutiveAccess(Value *A, Value *B, const DataLayout &DL, ScalarEvolution &SE, bool CheckType=true)
Returns true if the memory operations A and B are consecutive.
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Pass * createARMParallelDSPPass()
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.