Skip to content

Commit 4ddf85d

Browse files
committed
[SandboxIR][Region] Implement an auxiliary vector in Region
This patch adds additional functionality to the sandboxir Region. The Region is used as a way of passing a set of Instructions across region passes in a way that can be represented in the IR with metadata. This is a design choice that allows us to test region passes in isolation with lit tests. Up until now the region was only used to tag the instructions generated by the passes. There is a need to represent an ordered set of instructions, which can be used as a way to represent the initial seeds to the first vectorization pass. This patch implements this auxiliary vector that can be used to convey such information.
1 parent 69b8cf4 commit 4ddf85d

File tree

3 files changed

+187
-3
lines changed

3 files changed

+187
-3
lines changed

llvm/include/llvm/SandboxIR/Region.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,23 @@ class ScoreBoard {
8787
// | |
8888
// |Rgn3| -> Transform1 -> ... -> TransformN -> Check Cost
8989
// +----+
90+
//
91+
// The region can also hold an ordered sequence of "auxiliary" instructions.
92+
// This can be used to pass auxiliary information across region passes, like for
93+
// example the initial seed slice used by the bottom-up vectorizer.
9094

9195
class Region {
9296
/// All the instructions in the Region. Only new instructions generated during
9397
/// vectorization are part of the Region.
9498
SetVector<Instruction *> Insts;
99+
/// An auxiliary sequence of Instruction-Index pairs.
100+
SmallVector<Instruction *> Aux;
95101

96102
/// MDNode that we'll use to mark instructions as being part of the region.
97103
MDNode *RegionMDN;
98104
static constexpr const char *MDKind = "sandboxvec";
99105
static constexpr const char *RegionStr = "sandboxregion";
106+
static constexpr const char *AuxMDKind = "sbaux";
100107

101108
Context &Ctx;
102109
/// Keeps track of cost of instructions added and removed.
@@ -110,6 +117,10 @@ class Region {
110117
// TODO: Add cost modeling.
111118
// TODO: Add a way to encode/decode region info to/from metadata.
112119

120+
/// Set \p I as the \p Idx'th element in the auxiliary vector.
121+
/// NOTE: This is for internal use, it does not set the metadata.
122+
void setAux(unsigned Idx, Instruction *I);
123+
113124
public:
114125
Region(Context &Ctx, TargetTransformInfo &TTI);
115126
~Region();
@@ -124,6 +135,12 @@ class Region {
124135
bool contains(Instruction *I) const { return Insts.contains(I); }
125136
/// Returns true if the Region has no instructions.
126137
bool empty() const { return Insts.empty(); }
138+
/// Set the auxiliary vector.
139+
void setAux(ArrayRef<Instruction *> Aux);
140+
/// \Returns the auxiliary vector.
141+
const SmallVector<Instruction *> &getAux() const { return Aux; }
142+
/// Clears all auxiliary data.
143+
void clearAux();
127144

128145
using iterator = decltype(Insts.begin());
129146
iterator begin() { return Insts.begin(); }

llvm/lib/SandboxIR/Region.cpp

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,39 @@ void Region::add(Instruction *I) {
5757
Scoreboard.add(I);
5858
}
5959

60+
void Region::setAux(ArrayRef<Instruction *> Aux) {
61+
this->Aux = SmallVector<Instruction *>(Aux);
62+
auto &LLVMCtx = Ctx.LLVMCtx;
63+
for (auto [Idx, I] : enumerate(Aux)) {
64+
llvm::ConstantInt *IdxC =
65+
llvm::ConstantInt::get(LLVMCtx, llvm::APInt(32, Idx, false));
66+
assert(cast<llvm::Instruction>(I->Val)->getMetadata(AuxMDKind) == nullptr &&
67+
"Instruction already in Aux!");
68+
cast<llvm::Instruction>(I->Val)->setMetadata(
69+
AuxMDKind, MDNode::get(LLVMCtx, ConstantAsMetadata::get(IdxC)));
70+
}
71+
}
72+
73+
void Region::setAux(unsigned Idx, Instruction *I) {
74+
unsigned ExpectedSz = Idx + 1;
75+
if (Aux.size() < ExpectedSz) {
76+
auto SzBefore = Aux.size();
77+
Aux.resize(ExpectedSz);
78+
// Initialize the gap with nullptr.
79+
for (unsigned Idx = SzBefore; Idx + 1 < ExpectedSz; ++Idx)
80+
Aux[Idx] = nullptr;
81+
}
82+
Aux[Idx] = I;
83+
}
84+
85+
void Region::clearAux() {
86+
for (unsigned Idx : seq<unsigned>(0, Aux.size())) {
87+
auto *LLVMI = cast<llvm::Instruction>(Aux[Idx]->Val);
88+
LLVMI->setMetadata(AuxMDKind, nullptr);
89+
}
90+
Aux.clear();
91+
}
92+
6093
void Region::remove(Instruction *I) {
6194
// Keep track of the instruction cost. This need to be done *before* we remove
6295
// `I` from the region.
@@ -78,6 +111,15 @@ bool Region::operator==(const Region &Other) const {
78111
void Region::dump(raw_ostream &OS) const {
79112
for (auto *I : Insts)
80113
OS << *I << "\n";
114+
if (!Aux.empty()) {
115+
OS << "\nAux:\n";
116+
for (auto *I : Aux) {
117+
if (I == nullptr)
118+
OS << "NULL\n";
119+
else
120+
OS << *I << "\n";
121+
}
122+
}
81123
}
82124

83125
void Region::dump() const {
@@ -93,16 +135,34 @@ Region::createRegionsFromMD(Function &F, TargetTransformInfo &TTI) {
93135
auto &Ctx = F.getContext();
94136
for (BasicBlock &BB : F) {
95137
for (Instruction &Inst : BB) {
96-
if (auto *MDN = cast<llvm::Instruction>(Inst.Val)->getMetadata(MDKind)) {
138+
auto *LLVMI = cast<llvm::Instruction>(Inst.Val);
139+
if (auto *MDN = LLVMI->getMetadata(MDKind)) {
140+
Region *R = nullptr;
97141
auto [It, Inserted] = MDNToRegion.try_emplace(MDN);
98142
if (Inserted) {
99143
Regions.push_back(std::make_unique<Region>(Ctx, TTI));
100-
It->second = Regions.back().get();
144+
R = Regions.back().get();
145+
It->second = R;
146+
} else {
147+
R = It->second;
148+
}
149+
R->add(&Inst);
150+
151+
if (auto *AuxMDN = LLVMI->getMetadata(AuxMDKind)) {
152+
llvm::Constant *IdxC =
153+
dyn_cast<ConstantAsMetadata>(AuxMDN->getOperand(0))->getValue();
154+
auto Idx = cast<llvm::ConstantInt>(IdxC)->getSExtValue();
155+
R->setAux(Idx, &Inst);
101156
}
102-
It->second->add(&Inst);
103157
}
104158
}
105159
}
160+
#ifndef NDEBUG
161+
// Check that there are no gaps in the Aux vector.
162+
for (auto &RPtr : Regions)
163+
for (auto *I : RPtr->getAux())
164+
assert(I != nullptr && "Gap in Aux!");
165+
#endif
106166
return Regions;
107167
}
108168

llvm/unittests/SandboxIR/RegionTest.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,110 @@ define void @foo(i8 %v0, i8 %v1, i8 %v2) {
292292
EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2));
293293
EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
294294
}
295+
296+
TEST_F(RegionTest, Aux) {
297+
parseIR(C, R"IR(
298+
define void @foo(i8 %v) {
299+
%t0 = add i8 %v, 0, !sandboxvec !0, !sbaux !2
300+
%t1 = add i8 %v, 1, !sandboxvec !0, !sbaux !3
301+
%t2 = add i8 %v, 2, !sandboxvec !1
302+
%t3 = add i8 %v, 3, !sandboxvec !1, !sbaux !2
303+
%t4 = add i8 %v, 4, !sandboxvec !1, !sbaux !4
304+
%t5 = add i8 %v, 5, !sandboxvec !1, !sbaux !3
305+
ret void
306+
}
307+
308+
!0 = distinct !{!"sandboxregion"}
309+
!1 = distinct !{!"sandboxregion"}
310+
311+
!2 = !{i32 0}
312+
!3 = !{i32 1}
313+
!4 = !{i32 2}
314+
)IR");
315+
llvm::Function *LLVMF = &*M->getFunction("foo");
316+
auto *LLVMBB = &*LLVMF->begin();
317+
auto LLVMIt = LLVMBB->begin();
318+
auto *LLVMI0 = &*LLVMIt++;
319+
auto *LLVMI1 = &*LLVMIt++;
320+
sandboxir::Context Ctx(C);
321+
auto *F = Ctx.createFunction(LLVMF);
322+
auto *BB = &*F->begin();
323+
auto It = BB->begin();
324+
auto *T0 = cast<sandboxir::Instruction>(&*It++);
325+
auto *T1 = cast<sandboxir::Instruction>(&*It++);
326+
auto *T2 = cast<sandboxir::Instruction>(&*It++);
327+
auto *T3 = cast<sandboxir::Instruction>(&*It++);
328+
auto *T4 = cast<sandboxir::Instruction>(&*It++);
329+
auto *T5 = cast<sandboxir::Instruction>(&*It++);
330+
331+
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
332+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
333+
// Check that the regions are correct.
334+
EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T1));
335+
EXPECT_THAT(Regions[1]->insts(),
336+
testing::UnorderedElementsAre(T2, T3, T4, T5));
337+
// Check aux.
338+
EXPECT_THAT(Regions[0]->getAux(), testing::ElementsAre(T0, T1));
339+
EXPECT_THAT(Regions[1]->getAux(), testing::ElementsAre(T3, T5, T4));
340+
// Check clearAux().
341+
EXPECT_TRUE(LLVMI0->getMetadata("sbaux"));
342+
EXPECT_TRUE(LLVMI1->getMetadata("sbaux"));
343+
Regions[0]->clearAux();
344+
EXPECT_TRUE(Regions[0]->getAux().empty());
345+
EXPECT_FALSE(LLVMI0->getMetadata("sbaux"));
346+
EXPECT_FALSE(LLVMI1->getMetadata("sbaux"));
347+
}
348+
349+
// Check that Aux is well-formed.
350+
TEST_F(RegionTest, AuxVerify) {
351+
parseIR(C, R"IR(
352+
define void @foo(i8 %v) {
353+
%t0 = add i8 %v, 0, !sandboxvec !0, !sbaux !2
354+
%t1 = add i8 %v, 1, !sandboxvec !0, !sbaux !3
355+
ret void
356+
}
357+
358+
!0 = distinct !{!"sandboxregion"}
359+
!2 = !{i32 0}
360+
!3 = !{i32 2}
361+
)IR");
362+
llvm::Function *LLVMF = &*M->getFunction("foo");
363+
sandboxir::Context Ctx(C);
364+
auto *F = Ctx.createFunction(LLVMF);
365+
#ifndef NDEBUG
366+
EXPECT_DEATH(sandboxir::Region::createRegionsFromMD(*F, *TTI), ".*Gap*");
367+
#endif
368+
}
369+
370+
TEST_F(RegionTest, AuxRoundTrip) {
371+
parseIR(C, R"IR(
372+
define i8 @foo(i8 %v0, i8 %v1) {
373+
%t0 = add i8 %v0, 1
374+
%t1 = add i8 %t0, %v1
375+
ret i8 %t1
376+
}
377+
)IR");
378+
llvm::Function *LLVMF = &*M->getFunction("foo");
379+
sandboxir::Context Ctx(C);
380+
auto *F = Ctx.createFunction(LLVMF);
381+
auto *BB = &*F->begin();
382+
auto It = BB->begin();
383+
auto *T0 = cast<sandboxir::Instruction>(&*It++);
384+
auto *T1 = cast<sandboxir::Instruction>(&*It++);
385+
386+
sandboxir::Region Rgn(Ctx, *TTI);
387+
Rgn.add(T0);
388+
Rgn.add(T1);
389+
#ifndef NDEBUG
390+
EXPECT_DEATH(Rgn.setAux({T0, T0}), ".*already.*");
391+
#endif
392+
Rgn.setAux({T1, T0});
393+
394+
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
395+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
396+
ASSERT_EQ(1U, Regions.size());
397+
#ifndef NDEBUG
398+
EXPECT_EQ(Rgn, *Regions[0].get());
399+
#endif
400+
EXPECT_THAT(Rgn.getAux(), testing::ElementsAre(T1, T0));
401+
}

0 commit comments

Comments
 (0)