Skip to content

Commit a5707e6

Browse files
committed
[SandboxVec][DAG] Implement functions for iterating through DGNodes
This patch adds DGNode member functions for visiting them in program order. - DGNode::getPrev() and getNext() return the prev/next node. - DGNode::getPrevMem() and getNextMem() return the prev/next node that is a memory dependency candidate. - MemDGNodeIterator iterates through memory candidate nodes. - makeMemRange() returns a MemDGNodeIterator range given two nodes.
1 parent 8f31ee9 commit a5707e6

File tree

3 files changed

+249
-0
lines changed

3 files changed

+249
-0
lines changed

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

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
namespace llvm::sandboxir {
3131

32+
class DependencyGraph;
33+
3234
/// A DependencyGraph Node that points to an Instruction and contains memory
3335
/// dependency edges.
3436
class DGNode {
@@ -45,6 +47,7 @@ class DGNode {
4547
(isa<AllocaInst>(I) && cast<AllocaInst>(I)->isUsedWithInAlloca()) ||
4648
I->isStackSaveOrRestoreIntrinsic();
4749
}
50+
DGNode(const DGNode &Other) = delete;
4851
Instruction *getInstruction() const { return I; }
4952
void addMemPred(DGNode *PredN) { MemPreds.insert(PredN); }
5053
/// \Returns all memory dependency predecessors.
@@ -56,6 +59,19 @@ class DGNode {
5659
/// \Returns true if this may read/write memory, or if it has some ordering
5760
/// constraings, like with stacksave/stackrestore and alloca/inalloca.
5861
bool isMem() const { return IsMem; }
62+
/// \Returns the previous DGNode in program order.
63+
DGNode *getPrev(DependencyGraph &DAG) const;
64+
/// \Returns the next DGNode in program order.
65+
DGNode *getNext(DependencyGraph &DAG) const;
66+
/// Walks up the instruction chain looking for the next memory dependency
67+
/// candidate instruction.
68+
/// \Returns the corresponding DAG Node, or null if no instruction found.
69+
DGNode *getPrevMem(DependencyGraph &DAG) const;
70+
/// Walks down the instr chain looking for the next memory dependency
71+
/// candidate instruction.
72+
/// \Returns the corresponding DAG Node, or null if no instruction found.
73+
DGNode *getNextMem(DependencyGraph &DAG) const;
74+
5975
#ifndef NDEBUG
6076
void print(raw_ostream &OS, bool PrintDeps = true) const;
6177
friend raw_ostream &operator<<(DGNode &N, raw_ostream &OS) {
@@ -66,9 +82,73 @@ class DGNode {
6682
#endif // NDEBUG
6783
};
6884

85+
/// Walks in the order of the instruction chain but skips non-mem Nodes.
86+
/// This is used for building/updating the DAG.
87+
class MemDGNodeIterator {
88+
DGNode *N;
89+
DependencyGraph *DAG;
90+
91+
public:
92+
using difference_type = std::ptrdiff_t;
93+
using value_type = DGNode;
94+
using pointer = value_type *;
95+
using reference = value_type &;
96+
using iterator_category = std::bidirectional_iterator_tag;
97+
MemDGNodeIterator(DGNode *N, DependencyGraph *DAG) : N(N), DAG(DAG) {
98+
assert((N == nullptr || N->isMem()) && "Expects mem node!");
99+
}
100+
MemDGNodeIterator &operator++() {
101+
assert(N != nullptr && "Already at end!");
102+
N = N->getNextMem(*DAG);
103+
return *this;
104+
}
105+
MemDGNodeIterator operator++(int) {
106+
auto ItCopy = *this;
107+
++*this;
108+
return ItCopy;
109+
}
110+
MemDGNodeIterator &operator--() {
111+
N = N->getPrevMem(*DAG);
112+
return *this;
113+
}
114+
MemDGNodeIterator operator--(int) {
115+
auto ItCopy = *this;
116+
--*this;
117+
return ItCopy;
118+
}
119+
pointer operator*() { return N; }
120+
const DGNode *operator*() const { return N; }
121+
bool operator==(const MemDGNodeIterator &Other) const { return N == Other.N; }
122+
bool operator!=(const MemDGNodeIterator &Other) const {
123+
return !(*this == Other);
124+
}
125+
};
126+
127+
/// A MemDGNodeIterator with convenience builders and dump().
128+
class DGNodeRange : public iterator_range<MemDGNodeIterator> {
129+
public:
130+
DGNodeRange(MemDGNodeIterator Begin, MemDGNodeIterator End)
131+
: iterator_range(Begin, End) {}
132+
/// An empty range.
133+
DGNodeRange()
134+
: iterator_range(MemDGNodeIterator(nullptr, nullptr),
135+
MemDGNodeIterator(nullptr, nullptr)) {}
136+
/// Given \p TopN and \p BotN it finds their closest mem nodes in the range
137+
/// TopN to BotN and returns the corresponding mem range.
138+
/// Note: BotN (or its neighboring mem node) is included in the range.
139+
static DGNodeRange makeMemRange(DGNode *TopN, DGNode *BotN,
140+
DependencyGraph &DAG);
141+
static DGNodeRange makeEmptyMemRange() { return DGNodeRange(); }
142+
#ifndef NDEBUG
143+
LLVM_DUMP_METHOD void dump() const;
144+
#endif
145+
};
146+
69147
class DependencyGraph {
70148
private:
71149
DenseMap<Instruction *, std::unique_ptr<DGNode>> InstrToNodeMap;
150+
/// The DAG spans across all instructions in this interval.
151+
InstrInterval DAGInterval;
72152

73153
public:
74154
DependencyGraph() {}
@@ -77,6 +157,12 @@ class DependencyGraph {
77157
auto It = InstrToNodeMap.find(I);
78158
return It != InstrToNodeMap.end() ? It->second.get() : nullptr;
79159
}
160+
/// Like getNode() but returns nullptr if \p I is nullptr.
161+
DGNode *getNodeOrNull(Instruction *I) const {
162+
if (I == nullptr)
163+
return nullptr;
164+
return getNode(I);
165+
}
80166
DGNode *getOrCreateNode(Instruction *I) {
81167
auto [It, NotInMap] = InstrToNodeMap.try_emplace(I);
82168
if (NotInMap)

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,39 @@
1111

1212
using namespace llvm::sandboxir;
1313

14+
// TODO: Move this to Utils once it lands.
15+
/// \Returns the previous memory-dependency-candidate instruction before \p I in
16+
/// the instruction stream.
17+
static llvm::sandboxir::Instruction *
18+
getPrevMemDepInst(llvm::sandboxir::Instruction *I) {
19+
for (I = I->getPrevNode(); I != nullptr; I = I->getPrevNode())
20+
if (I->isMemDepCandidate() || I->isStackSaveOrRestoreIntrinsic())
21+
return I;
22+
return nullptr;
23+
}
24+
/// \Returns the next memory-dependency-candidate instruction after \p I in the
25+
/// instruction stream.
26+
static llvm::sandboxir::Instruction *
27+
getNextMemDepInst(llvm::sandboxir::Instruction *I) {
28+
for (I = I->getNextNode(); I != nullptr; I = I->getNextNode())
29+
if (I->isMemDepCandidate() || I->isStackSaveOrRestoreIntrinsic())
30+
return I;
31+
return nullptr;
32+
}
33+
34+
DGNode *DGNode::getPrev(DependencyGraph &DAG) const {
35+
return DAG.getNodeOrNull(I->getPrevNode());
36+
}
37+
DGNode *DGNode::getNext(DependencyGraph &DAG) const {
38+
return DAG.getNodeOrNull(I->getNextNode());
39+
}
40+
DGNode *DGNode::getPrevMem(DependencyGraph &DAG) const {
41+
return DAG.getNodeOrNull(getPrevMemDepInst(I));
42+
}
43+
DGNode *DGNode::getNextMem(DependencyGraph &DAG) const {
44+
return DAG.getNodeOrNull(getNextMemDepInst(I));
45+
}
46+
1447
#ifndef NDEBUG
1548
void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
1649
I->dumpOS(OS);
@@ -31,6 +64,34 @@ void DGNode::dump() const {
3164
}
3265
#endif // NDEBUG
3366

67+
DGNodeRange DGNodeRange::makeMemRange(DGNode *TopN, DGNode *BotN,
68+
DependencyGraph &DAG) {
69+
assert((TopN == BotN ||
70+
TopN->getInstruction()->comesBefore(BotN->getInstruction())) &&
71+
"Expected TopN before BotN!");
72+
// If TopN/BotN are not mem-dep candidate nodes we need to walk down/up the
73+
// chain and find the mem-dep ones.
74+
DGNode *MemTopN = TopN;
75+
DGNode *MemBotN = BotN;
76+
while (!MemTopN->isMem() && MemTopN != MemBotN)
77+
MemTopN = MemTopN->getNext(DAG);
78+
while (!MemBotN->isMem() && MemBotN != MemTopN)
79+
MemBotN = MemBotN->getPrev(DAG);
80+
// If we couldn't find a mem node in range TopN - BotN then it's empty.
81+
if (!MemTopN->isMem())
82+
return {};
83+
// Now that we have the mem-dep nodes, create and return the range.
84+
return DGNodeRange(MemDGNodeIterator(MemTopN, &DAG),
85+
MemDGNodeIterator(MemBotN->getNextMem(DAG), &DAG));
86+
}
87+
88+
#ifndef NDEBUG
89+
void DGNodeRange::dump() const {
90+
for (const DGNode *N : *this)
91+
N->dump();
92+
}
93+
#endif // NDEBUG
94+
3495
InstrInterval DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
3596
if (Instrs.empty())
3697
return {};

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

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,105 @@ define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
113113
EXPECT_THAT(N1->memPreds(), testing::ElementsAre(N0));
114114
EXPECT_THAT(N2->memPreds(), testing::ElementsAre(N1));
115115
}
116+
117+
TEST_F(DependencyGraphTest, DGNode_getPrev_getNext_getPrevMem_getNextMem) {
118+
parseIR(C, R"IR(
119+
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
120+
store i8 %v0, ptr %ptr
121+
add i8 %v0, %v0
122+
store i8 %v1, ptr %ptr
123+
ret void
124+
}
125+
)IR");
126+
llvm::Function *LLVMF = &*M->getFunction("foo");
127+
sandboxir::Context Ctx(C);
128+
auto *F = Ctx.createFunction(LLVMF);
129+
auto *BB = &*F->begin();
130+
auto It = BB->begin();
131+
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
132+
auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
133+
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
134+
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
135+
136+
sandboxir::DependencyGraph DAG;
137+
DAG.extend({&*BB->begin(), BB->getTerminator()});
138+
139+
sandboxir::DGNode *S0N = DAG.getNode(S0);
140+
sandboxir::DGNode *AddN = DAG.getNode(Add);
141+
sandboxir::DGNode *S1N = DAG.getNode(S1);
142+
sandboxir::DGNode *RetN = DAG.getNode(Ret);
143+
144+
EXPECT_EQ(S0N->getPrev(DAG), nullptr);
145+
EXPECT_EQ(S0N->getNext(DAG), AddN);
146+
EXPECT_EQ(S0N->getPrevMem(DAG), nullptr);
147+
EXPECT_EQ(S0N->getNextMem(DAG), S1N);
148+
149+
EXPECT_EQ(AddN->getPrev(DAG), S0N);
150+
EXPECT_EQ(AddN->getNext(DAG), S1N);
151+
EXPECT_EQ(AddN->getPrevMem(DAG), S0N);
152+
EXPECT_EQ(AddN->getNextMem(DAG), S1N);
153+
154+
EXPECT_EQ(S1N->getPrev(DAG), AddN);
155+
EXPECT_EQ(S1N->getNext(DAG), RetN);
156+
EXPECT_EQ(S1N->getPrevMem(DAG), S0N);
157+
EXPECT_EQ(S1N->getNextMem(DAG), nullptr);
158+
159+
EXPECT_EQ(RetN->getPrev(DAG), S1N);
160+
EXPECT_EQ(RetN->getNext(DAG), nullptr);
161+
EXPECT_EQ(RetN->getPrevMem(DAG), S1N);
162+
EXPECT_EQ(RetN->getNextMem(DAG), nullptr);
163+
}
164+
165+
TEST_F(DependencyGraphTest, DGNodeRange) {
166+
parseIR(C, R"IR(
167+
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
168+
add i8 %v0, %v0
169+
store i8 %v0, ptr %ptr
170+
add i8 %v0, %v0
171+
store i8 %v1, ptr %ptr
172+
ret void
173+
}
174+
)IR");
175+
llvm::Function *LLVMF = &*M->getFunction("foo");
176+
sandboxir::Context Ctx(C);
177+
auto *F = Ctx.createFunction(LLVMF);
178+
auto *BB = &*F->begin();
179+
auto It = BB->begin();
180+
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
181+
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
182+
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
183+
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
184+
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
185+
186+
sandboxir::DependencyGraph DAG;
187+
DAG.extend({&*BB->begin(), BB->getTerminator()});
188+
189+
sandboxir::DGNode *Add0N = DAG.getNode(Add0);
190+
sandboxir::DGNode *S0N = DAG.getNode(S0);
191+
sandboxir::DGNode *Add1N = DAG.getNode(Add1);
192+
sandboxir::DGNode *S1N = DAG.getNode(S1);
193+
sandboxir::DGNode *RetN = DAG.getNode(Ret);
194+
195+
// Check empty range.
196+
EXPECT_THAT(sandboxir::DGNodeRange::makeEmptyMemRange(),
197+
testing::ElementsAre());
198+
199+
// Both TopN and BotN are memory.
200+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, S1N, DAG),
201+
testing::ElementsAre(S0N, S1N));
202+
// Only TopN is memory.
203+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, RetN, DAG),
204+
testing::ElementsAre(S0N, S1N));
205+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, Add1N, DAG),
206+
testing::ElementsAre(S0N));
207+
// Only BotN is memory.
208+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, S1N, DAG),
209+
testing::ElementsAre(S0N, S1N));
210+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, S0N, DAG),
211+
testing::ElementsAre(S0N));
212+
// Neither TopN or BotN is memory.
213+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, RetN, DAG),
214+
testing::ElementsAre(S0N, S1N));
215+
EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, Add0N, DAG),
216+
testing::ElementsAre());
217+
}

0 commit comments

Comments
 (0)