13f503932SSterling-Augustine //===- SeedCollector.cpp -0000000-----------------------------------------===// 293bfa788SSterling-Augustine // 393bfa788SSterling-Augustine // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 493bfa788SSterling-Augustine // See https://llvm.org/LICENSE.txt for license information. 593bfa788SSterling-Augustine // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 693bfa788SSterling-Augustine // 793bfa788SSterling-Augustine //===----------------------------------------------------------------------===// 893bfa788SSterling-Augustine 993bfa788SSterling-Augustine #include "llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h" 1093bfa788SSterling-Augustine #include "llvm/Analysis/LoopAccessAnalysis.h" 1193bfa788SSterling-Augustine #include "llvm/Analysis/ValueTracking.h" 1293bfa788SSterling-Augustine #include "llvm/IR/Type.h" 1393bfa788SSterling-Augustine #include "llvm/SandboxIR/Instruction.h" 1493bfa788SSterling-Augustine #include "llvm/SandboxIR/Utils.h" 1593bfa788SSterling-Augustine #include "llvm/Support/Debug.h" 1693bfa788SSterling-Augustine 1793bfa788SSterling-Augustine using namespace llvm; 1893bfa788SSterling-Augustine namespace llvm::sandboxir { 1993bfa788SSterling-Augustine 20b26c514bSSterling-Augustine cl::opt<unsigned> SeedBundleSizeLimit( 21b26c514bSSterling-Augustine "sbvec-seed-bundle-size-limit", cl::init(32), cl::Hidden, 22b26c514bSSterling-Augustine cl::desc("Limit the size of the seed bundle to cap compilation time.")); 23f1be5162SSterling-Augustine #define LoadSeedsDef "loads" 24f1be5162SSterling-Augustine #define StoreSeedsDef "stores" 25f1be5162SSterling-Augustine cl::opt<std::string> CollectSeeds( 26f1be5162SSterling-Augustine "sbvec-collect-seeds", cl::init(LoadSeedsDef "," StoreSeedsDef), cl::Hidden, 27f1be5162SSterling-Augustine cl::desc("Collect these seeds. Use empty for none or a comma-separated " 28f1be5162SSterling-Augustine "list of '" LoadSeedsDef "' and '" StoreSeedsDef "'.")); 29f1be5162SSterling-Augustine cl::opt<unsigned> SeedGroupsLimit( 30f1be5162SSterling-Augustine "sbvec-seed-groups-limit", cl::init(256), cl::Hidden, 31f1be5162SSterling-Augustine cl::desc("Limit the number of collected seeds groups in a BB to " 32f1be5162SSterling-Augustine "cap compilation time.")); 33b26c514bSSterling-Augustine 346312beefSvporpo ArrayRef<Instruction *> SeedBundle::getSlice(unsigned StartIdx, 3593bfa788SSterling-Augustine unsigned MaxVecRegBits, 3693bfa788SSterling-Augustine bool ForcePowerOf2) { 3793bfa788SSterling-Augustine // Use uint32_t here for compatibility with IsPowerOf2_32 3893bfa788SSterling-Augustine 3993bfa788SSterling-Augustine // BitCount tracks the size of the working slice. From that we can tell 4093bfa788SSterling-Augustine // when the working slice's size is a power-of-two and when it exceeds 4193bfa788SSterling-Augustine // the legal size in MaxVecBits. 4293bfa788SSterling-Augustine uint32_t BitCount = 0; 4393bfa788SSterling-Augustine uint32_t NumElements = 0; 4493bfa788SSterling-Augustine // Tracks the most recent slice where NumElements gave a power-of-2 BitCount 4593bfa788SSterling-Augustine uint32_t NumElementsPowerOfTwo = 0; 4693bfa788SSterling-Augustine uint32_t BitCountPowerOfTwo = 0; 4793bfa788SSterling-Augustine // Can't start a slice with a used instruction. 4893bfa788SSterling-Augustine assert(!isUsed(StartIdx) && "Expected unused at StartIdx"); 4993bfa788SSterling-Augustine for (auto S : make_range(Seeds.begin() + StartIdx, Seeds.end())) { 506312beefSvporpo // Stop if this instruction is used. This needs to be done before 516312beefSvporpo // getNumBits() because a "used" instruction may have been erased. 526312beefSvporpo if (isUsed(StartIdx + NumElements)) 536312beefSvporpo break; 5493bfa788SSterling-Augustine uint32_t InstBits = Utils::getNumBits(S); 556312beefSvporpo // Stop if adding it puts the slice over the limit. 566312beefSvporpo if (BitCount + InstBits > MaxVecRegBits) 5793bfa788SSterling-Augustine break; 5893bfa788SSterling-Augustine NumElements++; 5993bfa788SSterling-Augustine BitCount += InstBits; 6093bfa788SSterling-Augustine if (ForcePowerOf2 && isPowerOf2_32(BitCount)) { 6193bfa788SSterling-Augustine NumElementsPowerOfTwo = NumElements; 6293bfa788SSterling-Augustine BitCountPowerOfTwo = BitCount; 6393bfa788SSterling-Augustine } 6493bfa788SSterling-Augustine } 6593bfa788SSterling-Augustine if (ForcePowerOf2) { 6693bfa788SSterling-Augustine NumElements = NumElementsPowerOfTwo; 6793bfa788SSterling-Augustine BitCount = BitCountPowerOfTwo; 6893bfa788SSterling-Augustine } 6993bfa788SSterling-Augustine 70*25b90c4eSVasileios Porpodas // Return any non-empty slice 71*25b90c4eSVasileios Porpodas if (NumElements > 1) { 7293bfa788SSterling-Augustine assert((!ForcePowerOf2 || isPowerOf2_32(BitCount)) && 7393bfa788SSterling-Augustine "Must be a power of two"); 746312beefSvporpo return ArrayRef<Instruction *>(&Seeds[StartIdx], NumElements); 75*25b90c4eSVasileios Porpodas } 7693bfa788SSterling-Augustine return {}; 7793bfa788SSterling-Augustine } 7893bfa788SSterling-Augustine 79b26c514bSSterling-Augustine template <typename LoadOrStoreT> 80b26c514bSSterling-Augustine SeedContainer::KeyT SeedContainer::getKey(LoadOrStoreT *LSI) const { 81b26c514bSSterling-Augustine assert((isa<LoadInst>(LSI) || isa<StoreInst>(LSI)) && 82b26c514bSSterling-Augustine "Expected Load or Store!"); 83b26c514bSSterling-Augustine Value *Ptr = Utils::getMemInstructionBase(LSI); 84b26c514bSSterling-Augustine Instruction::Opcode Op = LSI->getOpcode(); 85b26c514bSSterling-Augustine Type *Ty = Utils::getExpectedType(LSI); 86b26c514bSSterling-Augustine if (auto *VTy = dyn_cast<VectorType>(Ty)) 87b26c514bSSterling-Augustine Ty = VTy->getElementType(); 88b26c514bSSterling-Augustine return {Ptr, Ty, Op}; 89b26c514bSSterling-Augustine } 90b26c514bSSterling-Augustine 91b26c514bSSterling-Augustine // Explicit instantiations 92b26c514bSSterling-Augustine template SeedContainer::KeyT 93b26c514bSSterling-Augustine SeedContainer::getKey<LoadInst>(LoadInst *LSI) const; 94b26c514bSSterling-Augustine template SeedContainer::KeyT 95b26c514bSSterling-Augustine SeedContainer::getKey<StoreInst>(StoreInst *LSI) const; 96b26c514bSSterling-Augustine 97b26c514bSSterling-Augustine bool SeedContainer::erase(Instruction *I) { 98b26c514bSSterling-Augustine assert((isa<LoadInst>(I) || isa<StoreInst>(I)) && "Expected Load or Store!"); 99b26c514bSSterling-Augustine auto It = SeedLookupMap.find(I); 100b26c514bSSterling-Augustine if (It == SeedLookupMap.end()) 101b26c514bSSterling-Augustine return false; 102b26c514bSSterling-Augustine SeedBundle *Bndl = It->second; 103b26c514bSSterling-Augustine Bndl->setUsed(I); 104b26c514bSSterling-Augustine return true; 105b26c514bSSterling-Augustine } 106b26c514bSSterling-Augustine 107b26c514bSSterling-Augustine template <typename LoadOrStoreT> void SeedContainer::insert(LoadOrStoreT *LSI) { 108b26c514bSSterling-Augustine // Find the bundle containing seeds for this symbol and type-of-access. 109b26c514bSSterling-Augustine auto &BundleVec = Bundles[getKey(LSI)]; 110b26c514bSSterling-Augustine // Fill this vector of bundles front to back so that only the last bundle in 111b26c514bSSterling-Augustine // the vector may have available space. This avoids iteration to find one with 112b26c514bSSterling-Augustine // space. 113b26c514bSSterling-Augustine if (BundleVec.empty() || BundleVec.back()->size() == SeedBundleSizeLimit) 114b26c514bSSterling-Augustine BundleVec.emplace_back(std::make_unique<MemSeedBundle<LoadOrStoreT>>(LSI)); 115b26c514bSSterling-Augustine else 116b26c514bSSterling-Augustine BundleVec.back()->insert(LSI, SE); 117b26c514bSSterling-Augustine 118b26c514bSSterling-Augustine SeedLookupMap[LSI] = BundleVec.back().get(); 119b26c514bSSterling-Augustine } 120b26c514bSSterling-Augustine 121b26c514bSSterling-Augustine // Explicit instantiations 122b26c514bSSterling-Augustine template void SeedContainer::insert<LoadInst>(LoadInst *); 123b26c514bSSterling-Augustine template void SeedContainer::insert<StoreInst>(StoreInst *); 124b26c514bSSterling-Augustine 125b26c514bSSterling-Augustine #ifndef NDEBUG 126ec24e23dSSterling-Augustine void SeedContainer::print(raw_ostream &OS) const { 127b26c514bSSterling-Augustine for (const auto &Pair : Bundles) { 128b26c514bSSterling-Augustine auto [I, Ty, Opc] = Pair.first; 129b26c514bSSterling-Augustine const auto &SeedsVec = Pair.second; 130b26c514bSSterling-Augustine std::string RefType = dyn_cast<LoadInst>(I) ? "Load" 131b26c514bSSterling-Augustine : dyn_cast<StoreInst>(I) ? "Store" 132b26c514bSSterling-Augustine : "Other"; 133ec24e23dSSterling-Augustine OS << "[Inst=" << *I << " Ty=" << Ty << " " << RefType << "]\n"; 134b26c514bSSterling-Augustine for (const auto &SeedPtr : SeedsVec) { 135ec24e23dSSterling-Augustine SeedPtr->dump(OS); 136ec24e23dSSterling-Augustine OS << "\n"; 137b26c514bSSterling-Augustine } 138b26c514bSSterling-Augustine } 139ec24e23dSSterling-Augustine OS << "\n"; 140b26c514bSSterling-Augustine } 141ec24e23dSSterling-Augustine 142ec24e23dSSterling-Augustine LLVM_DUMP_METHOD void SeedContainer::dump() const { print(dbgs()); } 143b26c514bSSterling-Augustine #endif // NDEBUG 144b26c514bSSterling-Augustine 145f1be5162SSterling-Augustine template <typename LoadOrStoreT> static bool isValidMemSeed(LoadOrStoreT *LSI) { 146c0ee8e22SSterling-Augustine if (!LSI->isSimple()) 147c0ee8e22SSterling-Augustine return false; 148f1be5162SSterling-Augustine auto *Ty = Utils::getExpectedType(LSI); 149f1be5162SSterling-Augustine // Omit types that are architecturally unvectorizable 150f1be5162SSterling-Augustine if (Ty->isX86_FP80Ty() || Ty->isPPC_FP128Ty()) 151f1be5162SSterling-Augustine return false; 152f1be5162SSterling-Augustine // Omit vector types without compile-time-known lane counts 153f1be5162SSterling-Augustine if (isa<ScalableVectorType>(Ty)) 154f1be5162SSterling-Augustine return false; 155f1be5162SSterling-Augustine if (auto *VTy = dyn_cast<FixedVectorType>(Ty)) 156f1be5162SSterling-Augustine return VectorType::isValidElementType(VTy->getElementType()); 157f1be5162SSterling-Augustine return VectorType::isValidElementType(Ty); 158f1be5162SSterling-Augustine } 159f1be5162SSterling-Augustine 160f1be5162SSterling-Augustine template bool isValidMemSeed<LoadInst>(LoadInst *LSI); 161f1be5162SSterling-Augustine template bool isValidMemSeed<StoreInst>(StoreInst *LSI); 162f1be5162SSterling-Augustine 163f1be5162SSterling-Augustine SeedCollector::SeedCollector(BasicBlock *BB, ScalarEvolution &SE) 164f1be5162SSterling-Augustine : StoreSeeds(SE), LoadSeeds(SE), Ctx(BB->getContext()) { 165f1be5162SSterling-Augustine 166f1be5162SSterling-Augustine bool CollectStores = CollectSeeds.find(StoreSeedsDef) != std::string::npos; 167f1be5162SSterling-Augustine bool CollectLoads = CollectSeeds.find(LoadSeedsDef) != std::string::npos; 168f1be5162SSterling-Augustine if (!CollectStores && !CollectLoads) 169f1be5162SSterling-Augustine return; 1707ba864b5SSterling-Augustine 1717ba864b5SSterling-Augustine EraseCallbackID = Ctx.registerEraseInstrCallback([this](Instruction *I) { 1727ba864b5SSterling-Augustine if (auto SI = dyn_cast<StoreInst>(I)) 1737ba864b5SSterling-Augustine StoreSeeds.erase(SI); 1747ba864b5SSterling-Augustine else if (auto LI = dyn_cast<LoadInst>(I)) 1757ba864b5SSterling-Augustine LoadSeeds.erase(LI); 1767ba864b5SSterling-Augustine }); 1777ba864b5SSterling-Augustine 178f1be5162SSterling-Augustine // Actually collect the seeds. 179f1be5162SSterling-Augustine for (auto &I : *BB) { 180f1be5162SSterling-Augustine if (StoreInst *SI = dyn_cast<StoreInst>(&I)) 181f1be5162SSterling-Augustine if (CollectStores && isValidMemSeed(SI)) 182f1be5162SSterling-Augustine StoreSeeds.insert(SI); 183f1be5162SSterling-Augustine if (LoadInst *LI = dyn_cast<LoadInst>(&I)) 184f1be5162SSterling-Augustine if (CollectLoads && isValidMemSeed(LI)) 185f1be5162SSterling-Augustine LoadSeeds.insert(LI); 186f1be5162SSterling-Augustine // Cap compilation time. 187f1be5162SSterling-Augustine if (totalNumSeedGroups() > SeedGroupsLimit) 188f1be5162SSterling-Augustine break; 189f1be5162SSterling-Augustine } 190f1be5162SSterling-Augustine } 191f1be5162SSterling-Augustine 192f1be5162SSterling-Augustine SeedCollector::~SeedCollector() { 1937ba864b5SSterling-Augustine Ctx.unregisterEraseInstrCallback(EraseCallbackID); 194f1be5162SSterling-Augustine } 195f1be5162SSterling-Augustine 196f1be5162SSterling-Augustine #ifndef NDEBUG 197f1be5162SSterling-Augustine void SeedCollector::print(raw_ostream &OS) const { 198f1be5162SSterling-Augustine OS << "=== StoreSeeds ===\n"; 199f1be5162SSterling-Augustine StoreSeeds.print(OS); 200f1be5162SSterling-Augustine OS << "=== LoadSeeds ===\n"; 201f1be5162SSterling-Augustine LoadSeeds.print(OS); 202f1be5162SSterling-Augustine } 203f1be5162SSterling-Augustine 204f1be5162SSterling-Augustine void SeedCollector::dump() const { print(dbgs()); } 205f1be5162SSterling-Augustine #endif 206f1be5162SSterling-Augustine 20793bfa788SSterling-Augustine } // namespace llvm::sandboxir 208