Skip to content

Commit f1be516

Browse files
[SandboxVectorizer] New class to actually collect and manage seeds (#113386)
This relands d91318b, with test-only changes to make gcc-10 happy.
1 parent c170405 commit f1be516

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed

llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,33 @@ class SeedContainer {
284284
#endif // NDEBUG
285285
};
286286

287+
class SeedCollector {
288+
SeedContainer StoreSeeds;
289+
SeedContainer LoadSeeds;
290+
Context &Ctx;
291+
292+
/// \Returns the number of SeedBundle groups for all seed types.
293+
/// This is to be used for limiting compilation time.
294+
unsigned totalNumSeedGroups() const {
295+
return StoreSeeds.size() + LoadSeeds.size();
296+
}
297+
298+
public:
299+
SeedCollector(BasicBlock *BB, ScalarEvolution &SE);
300+
~SeedCollector();
301+
302+
iterator_range<SeedContainer::iterator> getStoreSeeds() {
303+
return {StoreSeeds.begin(), StoreSeeds.end()};
304+
}
305+
iterator_range<SeedContainer::iterator> getLoadSeeds() {
306+
return {LoadSeeds.begin(), LoadSeeds.end()};
307+
}
308+
#ifndef NDEBUG
309+
void print(raw_ostream &OS) const;
310+
LLVM_DUMP_METHOD void dump() const;
311+
#endif
312+
};
313+
287314
} // namespace llvm::sandboxir
288315

289316
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===- VecUtils.h -----------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Collector for SandboxVectorizer related convenience functions that don't
10+
// belong in other classes.
11+
12+
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_VECUTILS_H
13+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_VECUTILS_H
14+
15+
class Utils {
16+
public:
17+
/// \Returns the number of elements in \p Ty. That is the number of lanes if a
18+
/// fixed vector or 1 if scalar. ScalableVectors have unknown size and
19+
/// therefore are unsupported.
20+
static int getNumElements(Type *Ty) {
21+
assert(!isa<ScalableVectorType>(Ty));
22+
return Ty->isVectorTy() ? cast<FixedVectorType>(Ty)->getNumElements() : 1;
23+
}
24+
/// Returns \p Ty if scalar or its element type if vector.
25+
static Type *getElementType(Type *Ty) {
26+
return Ty->isVectorTy() ? cast<FixedVectorType>(Ty)->getElementType() : Ty;
27+
}
28+
}
29+
30+
#endif LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_VECUTILS_H

llvm/lib/Transforms/Vectorize/SandboxVectorizer/SeedCollector.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ namespace llvm::sandboxir {
2222
cl::opt<unsigned> SeedBundleSizeLimit(
2323
"sbvec-seed-bundle-size-limit", cl::init(32), cl::Hidden,
2424
cl::desc("Limit the size of the seed bundle to cap compilation time."));
25+
#define LoadSeedsDef "loads"
26+
#define StoreSeedsDef "stores"
27+
cl::opt<std::string> CollectSeeds(
28+
"sbvec-collect-seeds", cl::init(LoadSeedsDef "," StoreSeedsDef), cl::Hidden,
29+
cl::desc("Collect these seeds. Use empty for none or a comma-separated "
30+
"list of '" LoadSeedsDef "' and '" StoreSeedsDef "'."));
31+
cl::opt<unsigned> SeedGroupsLimit(
32+
"sbvec-seed-groups-limit", cl::init(256), cl::Hidden,
33+
cl::desc("Limit the number of collected seeds groups in a BB to "
34+
"cap compilation time."));
2535

2636
MutableArrayRef<Instruction *> SeedBundle::getSlice(unsigned StartIdx,
2737
unsigned MaxVecRegBits,
@@ -131,4 +141,61 @@ void SeedContainer::print(raw_ostream &OS) const {
131141
LLVM_DUMP_METHOD void SeedContainer::dump() const { print(dbgs()); }
132142
#endif // NDEBUG
133143

144+
template <typename LoadOrStoreT> static bool isValidMemSeed(LoadOrStoreT *LSI) {
145+
if (LSI->isSimple())
146+
return true;
147+
auto *Ty = Utils::getExpectedType(LSI);
148+
// Omit types that are architecturally unvectorizable
149+
if (Ty->isX86_FP80Ty() || Ty->isPPC_FP128Ty())
150+
return false;
151+
// Omit vector types without compile-time-known lane counts
152+
if (isa<ScalableVectorType>(Ty))
153+
return false;
154+
if (auto *VTy = dyn_cast<FixedVectorType>(Ty))
155+
return VectorType::isValidElementType(VTy->getElementType());
156+
return VectorType::isValidElementType(Ty);
157+
}
158+
159+
template bool isValidMemSeed<LoadInst>(LoadInst *LSI);
160+
template bool isValidMemSeed<StoreInst>(StoreInst *LSI);
161+
162+
SeedCollector::SeedCollector(BasicBlock *BB, ScalarEvolution &SE)
163+
: StoreSeeds(SE), LoadSeeds(SE), Ctx(BB->getContext()) {
164+
// TODO: Register a callback for updating the Collector data structures upon
165+
// instr removal
166+
167+
bool CollectStores = CollectSeeds.find(StoreSeedsDef) != std::string::npos;
168+
bool CollectLoads = CollectSeeds.find(LoadSeedsDef) != std::string::npos;
169+
if (!CollectStores && !CollectLoads)
170+
return;
171+
// Actually collect the seeds.
172+
for (auto &I : *BB) {
173+
if (StoreInst *SI = dyn_cast<StoreInst>(&I))
174+
if (CollectStores && isValidMemSeed(SI))
175+
StoreSeeds.insert(SI);
176+
if (LoadInst *LI = dyn_cast<LoadInst>(&I))
177+
if (CollectLoads && isValidMemSeed(LI))
178+
LoadSeeds.insert(LI);
179+
// Cap compilation time.
180+
if (totalNumSeedGroups() > SeedGroupsLimit)
181+
break;
182+
}
183+
}
184+
185+
SeedCollector::~SeedCollector() {
186+
// TODO: Unregister the callback for updating the seed datastructures upon
187+
// instr removal
188+
}
189+
190+
#ifndef NDEBUG
191+
void SeedCollector::print(raw_ostream &OS) const {
192+
OS << "=== StoreSeeds ===\n";
193+
StoreSeeds.print(OS);
194+
OS << "=== LoadSeeds ===\n";
195+
LoadSeeds.print(OS);
196+
}
197+
198+
void SeedCollector::dump() const { print(dbgs()); }
199+
#endif
200+
134201
} // namespace llvm::sandboxir

llvm/unittests/Transforms/Vectorize/SandboxVectorizer/SeedCollectorTest.cpp

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@
2222

2323
using namespace llvm;
2424

25+
// TODO: gcc-10 has a bug that causes the below line not to compile due to some
26+
// macro-magic in gunit in combination with a class with pure-virtual
27+
// function. Once gcc-10 is no longer supported, replace this function with
28+
// something like the following:
29+
//
30+
// EXPECT_THAT(SB, testing::ElementsAre(St0, St1, St2, St3));
31+
static void
32+
ExpectThatElementsAre(sandboxir::SeedBundle &SR,
33+
llvm::ArrayRef<sandboxir::Instruction *> Contents) {
34+
EXPECT_EQ(range_size(SR), Contents.size());
35+
auto CI = Contents.begin();
36+
if (range_size(SR) == Contents.size())
37+
for (auto &S : SR)
38+
EXPECT_EQ(S, *CI++);
39+
}
40+
2541
struct SeedBundleTest : public testing::Test {
2642
LLVMContext C;
2743
std::unique_ptr<Module> M;
@@ -268,3 +284,171 @@ define void @foo(ptr %ptrA, float %val, ptr %ptrB) {
268284
}
269285
EXPECT_EQ(Cnt, 0u);
270286
}
287+
288+
TEST_F(SeedBundleTest, ConsecutiveStores) {
289+
// Where "Consecutive" means the stores address consecutive locations in
290+
// memory, but not in program order. Check to see that the collector puts them
291+
// in the proper order for vectorization.
292+
parseIR(C, R"IR(
293+
define void @foo(ptr noalias %ptr, float %val) {
294+
bb:
295+
%ptr0 = getelementptr float, ptr %ptr, i32 0
296+
%ptr1 = getelementptr float, ptr %ptr, i32 1
297+
%ptr2 = getelementptr float, ptr %ptr, i32 2
298+
%ptr3 = getelementptr float, ptr %ptr, i32 3
299+
store float %val, ptr %ptr0
300+
store float %val, ptr %ptr2
301+
store float %val, ptr %ptr1
302+
store float %val, ptr %ptr3
303+
ret void
304+
}
305+
)IR");
306+
Function &LLVMF = *M->getFunction("foo");
307+
DominatorTree DT(LLVMF);
308+
TargetLibraryInfoImpl TLII;
309+
TargetLibraryInfo TLI(TLII);
310+
DataLayout DL(M->getDataLayout());
311+
LoopInfo LI(DT);
312+
AssumptionCache AC(LLVMF);
313+
ScalarEvolution SE(LLVMF, TLI, AC, DT, LI);
314+
315+
sandboxir::Context Ctx(C);
316+
auto &F = *Ctx.createFunction(&LLVMF);
317+
auto BB = F.begin();
318+
sandboxir::SeedCollector SC(&*BB, SE);
319+
320+
// Find the stores
321+
auto It = std::next(BB->begin(), 4);
322+
// StX with X as the order by offset in memory
323+
auto *St0 = &*It++;
324+
auto *St2 = &*It++;
325+
auto *St1 = &*It++;
326+
auto *St3 = &*It++;
327+
328+
auto StoreSeedsRange = SC.getStoreSeeds();
329+
auto &SB = *StoreSeedsRange.begin();
330+
// Expect just one vector of store seeds
331+
EXPECT_EQ(range_size(StoreSeedsRange), 1u);
332+
ExpectThatElementsAre(SB, {St0, St1, St2, St3});
333+
}
334+
335+
TEST_F(SeedBundleTest, StoresWithGaps) {
336+
parseIR(C, R"IR(
337+
define void @foo(ptr noalias %ptr, float %val) {
338+
bb:
339+
%ptr0 = getelementptr float, ptr %ptr, i32 0
340+
%ptr1 = getelementptr float, ptr %ptr, i32 3
341+
%ptr2 = getelementptr float, ptr %ptr, i32 5
342+
%ptr3 = getelementptr float, ptr %ptr, i32 7
343+
store float %val, ptr %ptr0
344+
store float %val, ptr %ptr2
345+
store float %val, ptr %ptr1
346+
store float %val, ptr %ptr3
347+
ret void
348+
}
349+
)IR");
350+
Function &LLVMF = *M->getFunction("foo");
351+
DominatorTree DT(LLVMF);
352+
TargetLibraryInfoImpl TLII;
353+
TargetLibraryInfo TLI(TLII);
354+
DataLayout DL(M->getDataLayout());
355+
LoopInfo LI(DT);
356+
AssumptionCache AC(LLVMF);
357+
ScalarEvolution SE(LLVMF, TLI, AC, DT, LI);
358+
359+
sandboxir::Context Ctx(C);
360+
auto &F = *Ctx.createFunction(&LLVMF);
361+
auto BB = F.begin();
362+
sandboxir::SeedCollector SC(&*BB, SE);
363+
364+
// Find the stores
365+
auto It = std::next(BB->begin(), 4);
366+
// StX with X as the order by offset in memory
367+
auto *St0 = &*It++;
368+
auto *St2 = &*It++;
369+
auto *St1 = &*It++;
370+
auto *St3 = &*It++;
371+
372+
auto StoreSeedsRange = SC.getStoreSeeds();
373+
auto &SB = *StoreSeedsRange.begin();
374+
// Expect just one vector of store seeds
375+
EXPECT_EQ(range_size(StoreSeedsRange), 1u);
376+
ExpectThatElementsAre(SB, {St0, St1, St2, St3});
377+
}
378+
379+
TEST_F(SeedBundleTest, VectorStores) {
380+
parseIR(C, R"IR(
381+
define void @foo(ptr noalias %ptr, <2 x float> %val) {
382+
bb:
383+
%ptr0 = getelementptr float, ptr %ptr, i32 0
384+
%ptr1 = getelementptr float, ptr %ptr, i32 1
385+
store <2 x float> %val, ptr %ptr1
386+
store <2 x float> %val, ptr %ptr0
387+
ret void
388+
}
389+
)IR");
390+
Function &LLVMF = *M->getFunction("foo");
391+
DominatorTree DT(LLVMF);
392+
TargetLibraryInfoImpl TLII;
393+
TargetLibraryInfo TLI(TLII);
394+
DataLayout DL(M->getDataLayout());
395+
LoopInfo LI(DT);
396+
AssumptionCache AC(LLVMF);
397+
ScalarEvolution SE(LLVMF, TLI, AC, DT, LI);
398+
399+
sandboxir::Context Ctx(C);
400+
auto &F = *Ctx.createFunction(&LLVMF);
401+
auto BB = F.begin();
402+
sandboxir::SeedCollector SC(&*BB, SE);
403+
404+
// Find the stores
405+
auto It = std::next(BB->begin(), 2);
406+
// StX with X as the order by offset in memory
407+
auto *St1 = &*It++;
408+
auto *St0 = &*It++;
409+
410+
auto StoreSeedsRange = SC.getStoreSeeds();
411+
EXPECT_EQ(range_size(StoreSeedsRange), 1u);
412+
auto &SB = *StoreSeedsRange.begin();
413+
ExpectThatElementsAre(SB, {St0, St1});
414+
}
415+
416+
TEST_F(SeedBundleTest, MixedScalarVectors) {
417+
parseIR(C, R"IR(
418+
define void @foo(ptr noalias %ptr, float %v, <2 x float> %val) {
419+
bb:
420+
%ptr0 = getelementptr float, ptr %ptr, i32 0
421+
%ptr1 = getelementptr float, ptr %ptr, i32 1
422+
%ptr3 = getelementptr float, ptr %ptr, i32 3
423+
store float %v, ptr %ptr0
424+
store float %v, ptr %ptr3
425+
store <2 x float> %val, ptr %ptr1
426+
ret void
427+
}
428+
)IR");
429+
Function &LLVMF = *M->getFunction("foo");
430+
DominatorTree DT(LLVMF);
431+
TargetLibraryInfoImpl TLII;
432+
TargetLibraryInfo TLI(TLII);
433+
DataLayout DL(M->getDataLayout());
434+
LoopInfo LI(DT);
435+
AssumptionCache AC(LLVMF);
436+
ScalarEvolution SE(LLVMF, TLI, AC, DT, LI);
437+
438+
sandboxir::Context Ctx(C);
439+
auto &F = *Ctx.createFunction(&LLVMF);
440+
auto BB = F.begin();
441+
sandboxir::SeedCollector SC(&*BB, SE);
442+
443+
// Find the stores
444+
auto It = std::next(BB->begin(), 3);
445+
// StX with X as the order by offset in memory
446+
auto *St0 = &*It++;
447+
auto *St3 = &*It++;
448+
auto *St1 = &*It++;
449+
450+
auto StoreSeedsRange = SC.getStoreSeeds();
451+
EXPECT_EQ(range_size(StoreSeedsRange), 1u);
452+
auto &SB = *StoreSeedsRange.begin();
453+
ExpectThatElementsAre(SB, {St0, St1, St3});
454+
}

0 commit comments

Comments
 (0)