Skip to content

Commit 93bfa78

Browse files
[SandboxVectorizer] Define SeedBundle: a set of instructions to be vectorized [retry] (#111073)
[Retry 110696 with a proper rebase.] Seed collection will assemble instructions to be vectorized into SeedBundles. This data structure is not intended to be used directly, but will be the basis for load bundles, store bundles, and so on.
1 parent 41b09c5 commit 93bfa78

File tree

5 files changed

+325
-1
lines changed

5 files changed

+325
-1
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//===- SeedCollector.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+
// This file contains the mechanism for collecting the seed instructions that
9+
// are used as starting points for forming the vectorization graph.
10+
//===----------------------------------------------------------------------===//
11+
12+
#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
13+
#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H
14+
15+
#include "llvm/ADT/BitVector.h"
16+
#include "llvm/ADT/iterator_range.h"
17+
#include "llvm/Analysis/ScalarEvolution.h"
18+
#include "llvm/SandboxIR/Instruction.h"
19+
#include "llvm/SandboxIR/Utils.h"
20+
#include "llvm/SandboxIR/Value.h"
21+
#include <iterator>
22+
#include <memory>
23+
24+
namespace llvm::sandboxir {
25+
26+
/// A set of candidate Instructions for vectorizing together.
27+
class SeedBundle {
28+
public:
29+
/// Initialize a bundle with \p I.
30+
explicit SeedBundle(Instruction *I) { insertAt(begin(), I); }
31+
explicit SeedBundle(SmallVector<Instruction *> &&L) : Seeds(std::move(L)) {
32+
for (auto &S : Seeds)
33+
NumUnusedBits += Utils::getNumBits(S);
34+
}
35+
/// No need to allow copies.
36+
SeedBundle(const SeedBundle &) = delete;
37+
SeedBundle &operator=(const SeedBundle &) = delete;
38+
virtual ~SeedBundle() {}
39+
40+
using iterator = SmallVector<Instruction *>::iterator;
41+
using const_iterator = SmallVector<Instruction *>::const_iterator;
42+
iterator begin() { return Seeds.begin(); }
43+
iterator end() { return Seeds.end(); }
44+
const_iterator begin() const { return Seeds.begin(); }
45+
const_iterator end() const { return Seeds.end(); }
46+
47+
Instruction *operator[](unsigned Idx) const { return Seeds[Idx]; }
48+
49+
/// Insert \p I into position \p P. Clients should choose Pos
50+
/// by symbol, symbol-offset, and program order (which depends if scheduling
51+
/// bottom-up or top-down).
52+
void insertAt(iterator Pos, Instruction *I) {
53+
#ifdef EXPENSIVE_CHECKS
54+
for (auto Itr : Seeds) {
55+
assert(*Itr != I && "Attempt to insert an instruction twice.");
56+
}
57+
#endif
58+
Seeds.insert(Pos, I);
59+
NumUnusedBits += Utils::getNumBits(I);
60+
}
61+
62+
unsigned getFirstUnusedElementIdx() const {
63+
for (unsigned ElmIdx : seq<unsigned>(0, Seeds.size()))
64+
if (!isUsed(ElmIdx))
65+
return ElmIdx;
66+
return Seeds.size();
67+
}
68+
/// Marks instruction \p I "used" within the bundle. Clients
69+
/// use this property when assembling a vectorized instruction from
70+
/// the seeds in a bundle. This allows constant time evaluation
71+
/// and "removal" from the list.
72+
void setUsed(Instruction *I) {
73+
auto It = std::find(begin(), end(), I);
74+
assert(It != end() && "Instruction not in the bundle!");
75+
auto Idx = It - begin();
76+
setUsed(Idx, 1, /*VerifyUnused=*/false);
77+
}
78+
79+
void setUsed(unsigned ElementIdx, unsigned Sz = 1, bool VerifyUnused = true) {
80+
if (ElementIdx + Sz >= UsedLanes.size())
81+
UsedLanes.resize(ElementIdx + Sz);
82+
for (unsigned Idx : seq<unsigned>(ElementIdx, ElementIdx + Sz)) {
83+
assert((!VerifyUnused || !UsedLanes.test(Idx)) &&
84+
"Already marked as used!");
85+
UsedLanes.set(Idx);
86+
UsedLaneCount++;
87+
}
88+
NumUnusedBits -= Utils::getNumBits(Seeds[ElementIdx]);
89+
}
90+
/// \Returns whether or not \p Element has been used.
91+
bool isUsed(unsigned Element) const {
92+
return Element < UsedLanes.size() && UsedLanes.test(Element);
93+
}
94+
bool allUsed() const { return UsedLaneCount == Seeds.size(); }
95+
unsigned getNumUnusedBits() const { return NumUnusedBits; }
96+
97+
/// \Returns a slice of seed elements, starting at the element \p StartIdx,
98+
/// with a total size <= \p MaxVecRegBits, or an empty slice if the
99+
/// requirements cannot be met . If \p ForcePowOf2 is true, then the returned
100+
/// slice will have a total number of bits that is a power of 2.
101+
MutableArrayRef<Instruction *>
102+
getSlice(unsigned StartIdx, unsigned MaxVecRegBits, bool ForcePowOf2);
103+
104+
protected:
105+
SmallVector<Instruction *> Seeds;
106+
/// The lanes that we have already vectorized.
107+
BitVector UsedLanes;
108+
/// Tracks used lanes for constant-time accessor.
109+
unsigned UsedLaneCount = 0;
110+
/// Tracks the remaining bits available to vectorize
111+
unsigned NumUnusedBits = 0;
112+
113+
public:
114+
#ifndef NDEBUG
115+
void dump(raw_ostream &OS) const {
116+
for (auto [ElmIdx, I] : enumerate(*this)) {
117+
OS.indent(2) << ElmIdx << ". ";
118+
if (isUsed(ElmIdx))
119+
OS << "[USED]";
120+
else
121+
OS << *I;
122+
OS << "\n";
123+
}
124+
}
125+
LLVM_DUMP_METHOD void dump() const {
126+
dump(dbgs());
127+
dbgs() << "\n";
128+
}
129+
#endif // NDEBUG
130+
};
131+
} // namespace llvm::sandboxir
132+
#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SEEDCOLLECTOR_H

llvm/lib/Transforms/Vectorize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_llvm_component_library(LLVMVectorize
66
SandboxVectorizer/DependencyGraph.cpp
77
SandboxVectorizer/Passes/BottomUpVec.cpp
88
SandboxVectorizer/SandboxVectorizer.cpp
9+
SandboxVectorizer/SeedCollector.cpp
910
SLPVectorizer.cpp
1011
Vectorize.cpp
1112
VectorCombine.cpp
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===- SeedCollection.cpp -0000000----------------------------------------===//
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+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h"
10+
#include "llvm/ADT/STLExtras.h"
11+
#include "llvm/Analysis/LoopAccessAnalysis.h"
12+
#include "llvm/Analysis/ValueTracking.h"
13+
#include "llvm/IR/BasicBlock.h"
14+
#include "llvm/IR/Type.h"
15+
#include "llvm/SandboxIR/Instruction.h"
16+
#include "llvm/SandboxIR/Utils.h"
17+
#include "llvm/Support/Debug.h"
18+
#include <span>
19+
20+
using namespace llvm;
21+
namespace llvm::sandboxir {
22+
23+
MutableArrayRef<Instruction *> SeedBundle::getSlice(unsigned StartIdx,
24+
unsigned MaxVecRegBits,
25+
bool ForcePowerOf2) {
26+
// Use uint32_t here for compatibility with IsPowerOf2_32
27+
28+
// BitCount tracks the size of the working slice. From that we can tell
29+
// when the working slice's size is a power-of-two and when it exceeds
30+
// the legal size in MaxVecBits.
31+
uint32_t BitCount = 0;
32+
uint32_t NumElements = 0;
33+
// Tracks the most recent slice where NumElements gave a power-of-2 BitCount
34+
uint32_t NumElementsPowerOfTwo = 0;
35+
uint32_t BitCountPowerOfTwo = 0;
36+
// Can't start a slice with a used instruction.
37+
assert(!isUsed(StartIdx) && "Expected unused at StartIdx");
38+
for (auto S : make_range(Seeds.begin() + StartIdx, Seeds.end())) {
39+
uint32_t InstBits = Utils::getNumBits(S);
40+
// Stop if this instruction is used, or if adding it puts the slice over
41+
// the limit.
42+
if (isUsed(StartIdx + NumElements) || BitCount + InstBits > MaxVecRegBits)
43+
break;
44+
NumElements++;
45+
BitCount += InstBits;
46+
if (ForcePowerOf2 && isPowerOf2_32(BitCount)) {
47+
NumElementsPowerOfTwo = NumElements;
48+
BitCountPowerOfTwo = BitCount;
49+
}
50+
}
51+
if (ForcePowerOf2) {
52+
NumElements = NumElementsPowerOfTwo;
53+
BitCount = BitCountPowerOfTwo;
54+
}
55+
56+
assert((!ForcePowerOf2 || isPowerOf2_32(BitCount)) &&
57+
"Must be a power of two");
58+
// Return any non-empty slice
59+
if (NumElements > 1)
60+
return MutableArrayRef<Instruction *>(&Seeds[StartIdx], NumElements);
61+
else
62+
return {};
63+
}
64+
65+
} // namespace llvm::sandboxir

llvm/unittests/Transforms/Vectorize/SandboxVectorizer/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ add_llvm_unittest(SandboxVectorizerTests
1111
DependencyGraphTest.cpp
1212
IntervalTest.cpp
1313
LegalityTest.cpp
14-
)
14+
SeedCollectorTest.cpp
15+
)
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//===- SeedCollectorTest.cpp ----------------------------------------===//
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+
#include "llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h"
10+
#include "llvm/AsmParser/Parser.h"
11+
#include "llvm/SandboxIR/Function.h"
12+
#include "llvm/SandboxIR/Instruction.h"
13+
#include "llvm/Support/SourceMgr.h"
14+
#include "llvm/Testing/Support/SupportHelpers.h"
15+
#include "gtest/gtest.h"
16+
#include <span>
17+
using namespace llvm;
18+
19+
struct SeedBundleTest : public testing::Test {
20+
LLVMContext C;
21+
std::unique_ptr<Module> M;
22+
23+
void parseIR(LLVMContext &C, const char *IR) {
24+
SMDiagnostic Err;
25+
M = parseAssemblyString(IR, Err, C);
26+
if (!M)
27+
Err.print("LegalityTest", errs());
28+
}
29+
};
30+
31+
TEST_F(SeedBundleTest, SeedBundle) {
32+
parseIR(C, R"IR(
33+
define void @foo(float %v0, i32 %i0, i16 %i1, i8 %i2) {
34+
bb:
35+
%add0 = fadd float %v0, %v0
36+
%add1 = fadd float %v0, %v0
37+
%add2 = add i8 %i2, %i2
38+
%add3 = add i16 %i1, %i1
39+
%add4 = add i32 %i0, %i0
40+
%add5 = add i16 %i1, %i1
41+
%add6 = add i8 %i2, %i2
42+
%add7 = add i8 %i2, %i2
43+
ret void
44+
}
45+
)IR");
46+
Function &LLVMF = *M->getFunction("foo");
47+
sandboxir::Context Ctx(C);
48+
auto &F = *Ctx.createFunction(&LLVMF);
49+
DataLayout DL(M->getDataLayout());
50+
auto *BB = &*F.begin();
51+
auto It = BB->begin();
52+
auto *I0 = &*It++;
53+
auto *I1 = &*It++;
54+
// Assume first two instructions are identical in the number of bits.
55+
const unsigned IOBits = sandboxir::Utils::getNumBits(I0, DL);
56+
// Constructor
57+
sandboxir::SeedBundle SBO(I0);
58+
EXPECT_EQ(*SBO.begin(), I0);
59+
// getNumUnusedBits after constructor
60+
EXPECT_EQ(SBO.getNumUnusedBits(), IOBits);
61+
// setUsed
62+
SBO.setUsed(I0);
63+
// allUsed
64+
EXPECT_TRUE(SBO.allUsed());
65+
// isUsed
66+
EXPECT_TRUE(SBO.isUsed(0));
67+
// getNumUnusedBits after setUsed
68+
EXPECT_EQ(SBO.getNumUnusedBits(), 0u);
69+
// insertAt
70+
SBO.insertAt(SBO.end(), I1);
71+
EXPECT_NE(*SBO.begin(), I1);
72+
// getNumUnusedBits after insertAt
73+
EXPECT_EQ(SBO.getNumUnusedBits(), IOBits);
74+
// allUsed
75+
EXPECT_FALSE(SBO.allUsed());
76+
// getFirstUnusedElement
77+
EXPECT_EQ(SBO.getFirstUnusedElementIdx(), 1u);
78+
79+
SmallVector<sandboxir::Instruction *> Insts;
80+
// add2 through add7
81+
Insts.push_back(&*It++);
82+
Insts.push_back(&*It++);
83+
Insts.push_back(&*It++);
84+
Insts.push_back(&*It++);
85+
Insts.push_back(&*It++);
86+
Insts.push_back(&*It++);
87+
unsigned BundleBits = 0;
88+
for (auto &S : Insts)
89+
BundleBits += sandboxir::Utils::getNumBits(S);
90+
// Ensure the instructions are as expected.
91+
EXPECT_EQ(BundleBits, 88u);
92+
auto Seeds = Insts;
93+
// Constructor
94+
sandboxir::SeedBundle SB1(std::move(Seeds));
95+
// getNumUnusedBits after constructor
96+
EXPECT_EQ(SB1.getNumUnusedBits(), BundleBits);
97+
// setUsed with index
98+
SB1.setUsed(1);
99+
// getFirstUnusedElementIdx
100+
EXPECT_EQ(SB1.getFirstUnusedElementIdx(), 0u);
101+
SB1.setUsed(unsigned(0));
102+
// getFirstUnusedElementIdx not at end
103+
EXPECT_EQ(SB1.getFirstUnusedElementIdx(), 2u);
104+
105+
// getSlice is (StartIdx, MaxVecRegBits, ForcePowerOf2). It's easier to
106+
// compare test cases without the parameter-name comments inline.
107+
auto Slice0 = SB1.getSlice(2, 64, true);
108+
EXPECT_THAT(Slice0,
109+
testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5]));
110+
auto Slice1 = SB1.getSlice(2, 72, true);
111+
EXPECT_THAT(Slice1,
112+
testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5]));
113+
auto Slice2 = SB1.getSlice(2, 80, true);
114+
EXPECT_THAT(Slice2,
115+
testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5]));
116+
117+
SB1.setUsed(2);
118+
auto Slice3 = SB1.getSlice(3, 64, false);
119+
EXPECT_THAT(Slice3, testing::ElementsAre(Insts[3], Insts[4], Insts[5]));
120+
// getSlice empty case
121+
SB1.setUsed(3);
122+
auto Slice4 = SB1.getSlice(4, /* MaxVecRegBits */ 8,
123+
/* ForcePowerOf2 */ true);
124+
EXPECT_EQ(Slice4.size(), 0u);
125+
}

0 commit comments

Comments
 (0)