Skip to content

Commit 766ff99

Browse files
vporpojoaosaffran
authored andcommitted
[SandboxIR][Region] Implement an auxiliary vector in Region (llvm#126376)
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 2bb5da0 commit 766ff99

File tree

3 files changed

+210
-3
lines changed

3 files changed

+210
-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 = "sandboxaux";
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: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,41 @@ 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+
assert((Idx >= Aux.size() || Aux[Idx] == nullptr) &&
75+
"There is already an Instruction at Idx in Aux!");
76+
unsigned ExpectedSz = Idx + 1;
77+
if (Aux.size() < ExpectedSz) {
78+
auto SzBefore = Aux.size();
79+
Aux.resize(ExpectedSz);
80+
// Initialize the gap with nullptr.
81+
for (unsigned Idx = SzBefore; Idx + 1 < ExpectedSz; ++Idx)
82+
Aux[Idx] = nullptr;
83+
}
84+
Aux[Idx] = I;
85+
}
86+
87+
void Region::clearAux() {
88+
for (unsigned Idx : seq<unsigned>(0, Aux.size())) {
89+
auto *LLVMI = cast<llvm::Instruction>(Aux[Idx]->Val);
90+
LLVMI->setMetadata(AuxMDKind, nullptr);
91+
}
92+
Aux.clear();
93+
}
94+
6095
void Region::remove(Instruction *I) {
6196
// Keep track of the instruction cost. This need to be done *before* we remove
6297
// `I` from the region.
@@ -78,6 +113,15 @@ bool Region::operator==(const Region &Other) const {
78113
void Region::dump(raw_ostream &OS) const {
79114
for (auto *I : Insts)
80115
OS << *I << "\n";
116+
if (!Aux.empty()) {
117+
OS << "\nAux:\n";
118+
for (auto *I : Aux) {
119+
if (I == nullptr)
120+
OS << "NULL\n";
121+
else
122+
OS << *I << "\n";
123+
}
124+
}
81125
}
82126

83127
void Region::dump() const {
@@ -93,16 +137,34 @@ Region::createRegionsFromMD(Function &F, TargetTransformInfo &TTI) {
93137
auto &Ctx = F.getContext();
94138
for (BasicBlock &BB : F) {
95139
for (Instruction &Inst : BB) {
96-
if (auto *MDN = cast<llvm::Instruction>(Inst.Val)->getMetadata(MDKind)) {
140+
auto *LLVMI = cast<llvm::Instruction>(Inst.Val);
141+
if (auto *MDN = LLVMI->getMetadata(MDKind)) {
142+
Region *R = nullptr;
97143
auto [It, Inserted] = MDNToRegion.try_emplace(MDN);
98144
if (Inserted) {
99145
Regions.push_back(std::make_unique<Region>(Ctx, TTI));
100-
It->second = Regions.back().get();
146+
R = Regions.back().get();
147+
It->second = R;
148+
} else {
149+
R = It->second;
150+
}
151+
R->add(&Inst);
152+
153+
if (auto *AuxMDN = LLVMI->getMetadata(AuxMDKind)) {
154+
llvm::Constant *IdxC =
155+
dyn_cast<ConstantAsMetadata>(AuxMDN->getOperand(0))->getValue();
156+
auto Idx = cast<llvm::ConstantInt>(IdxC)->getSExtValue();
157+
R->setAux(Idx, &Inst);
101158
}
102-
It->second->add(&Inst);
103159
}
104160
}
105161
}
162+
#ifndef NDEBUG
163+
// Check that there are no gaps in the Aux vector.
164+
for (auto &RPtr : Regions)
165+
for (auto *I : RPtr->getAux())
166+
assert(I != nullptr && "Gap in Aux!");
167+
#endif
106168
return Regions;
107169
}
108170

llvm/unittests/SandboxIR/RegionTest.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,131 @@ 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, !sandboxaux !2
300+
%t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !3
301+
%t2 = add i8 %v, 2, !sandboxvec !1
302+
%t3 = add i8 %v, 3, !sandboxvec !1, !sandboxaux !2
303+
%t4 = add i8 %v, 4, !sandboxvec !1, !sandboxaux !4
304+
%t5 = add i8 %v, 5, !sandboxvec !1, !sandboxaux !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("sandboxaux"));
342+
EXPECT_TRUE(LLVMI1->getMetadata("sandboxaux"));
343+
Regions[0]->clearAux();
344+
EXPECT_TRUE(Regions[0]->getAux().empty());
345+
EXPECT_FALSE(LLVMI0->getMetadata("sandboxaux"));
346+
EXPECT_FALSE(LLVMI1->getMetadata("sandboxaux"));
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, !sandboxaux !2
354+
%t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !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+
// Check that we get an assertion failure if we try to set the same index more
371+
// than once.
372+
TEST_F(RegionTest, AuxSameIndex) {
373+
parseIR(C, R"IR(
374+
define void @foo(i8 %v) {
375+
%t0 = add i8 %v, 0, !sandboxvec !0, !sandboxaux !2
376+
%t1 = add i8 %v, 1, !sandboxvec !0, !sandboxaux !2
377+
ret void
378+
}
379+
380+
!0 = distinct !{!"sandboxregion"}
381+
!2 = !{i32 0}
382+
)IR");
383+
llvm::Function *LLVMF = &*M->getFunction("foo");
384+
sandboxir::Context Ctx(C);
385+
auto *F = Ctx.createFunction(LLVMF);
386+
#ifndef NDEBUG
387+
EXPECT_DEATH(sandboxir::Region::createRegionsFromMD(*F, *TTI), ".*already.*");
388+
#endif // NDEBUG
389+
}
390+
391+
TEST_F(RegionTest, AuxRoundTrip) {
392+
parseIR(C, R"IR(
393+
define i8 @foo(i8 %v0, i8 %v1) {
394+
%t0 = add i8 %v0, 1
395+
%t1 = add i8 %t0, %v1
396+
ret i8 %t1
397+
}
398+
)IR");
399+
llvm::Function *LLVMF = &*M->getFunction("foo");
400+
sandboxir::Context Ctx(C);
401+
auto *F = Ctx.createFunction(LLVMF);
402+
auto *BB = &*F->begin();
403+
auto It = BB->begin();
404+
auto *T0 = cast<sandboxir::Instruction>(&*It++);
405+
auto *T1 = cast<sandboxir::Instruction>(&*It++);
406+
407+
sandboxir::Region Rgn(Ctx, *TTI);
408+
Rgn.add(T0);
409+
Rgn.add(T1);
410+
#ifndef NDEBUG
411+
EXPECT_DEATH(Rgn.setAux({T0, T0}), ".*already.*");
412+
#endif
413+
Rgn.setAux({T1, T0});
414+
415+
SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
416+
sandboxir::Region::createRegionsFromMD(*F, *TTI);
417+
ASSERT_EQ(1U, Regions.size());
418+
#ifndef NDEBUG
419+
EXPECT_EQ(Rgn, *Regions[0].get());
420+
#endif
421+
EXPECT_THAT(Rgn.getAux(), testing::ElementsAre(T1, T0));
422+
}

0 commit comments

Comments
 (0)