Skip to content

Commit ff349f5

Browse files
author
git apple-llvm automerger
committed
Merge commit 'fd5e220fa63b' from llvm.org/main into next
2 parents 2b7378e + fd5e220 commit ff349f5

File tree

3 files changed

+226
-29
lines changed

3 files changed

+226
-29
lines changed

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

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,35 +29,61 @@
2929

3030
namespace llvm::sandboxir {
3131

32+
class DependencyGraph;
33+
class MemDGNode;
34+
35+
/// SubclassIDs for isa/dyn_cast etc.
36+
enum class DGNodeID {
37+
DGNode,
38+
MemDGNode,
39+
};
40+
3241
/// A DependencyGraph Node that points to an Instruction and contains memory
3342
/// dependency edges.
3443
class DGNode {
44+
protected:
3545
Instruction *I;
46+
// TODO: Use a PointerIntPair for SubclassID and I.
47+
/// For isa/dyn_cast etc.
48+
DGNodeID SubclassID;
3649
/// Memory predecessors.
37-
DenseSet<DGNode *> MemPreds;
38-
/// This is true if this may read/write memory, or if it has some ordering
39-
/// constraints, like with stacksave/stackrestore and alloca/inalloca.
40-
bool IsMem;
50+
DenseSet<MemDGNode *> MemPreds;
51+
52+
DGNode(Instruction *I, DGNodeID ID) : I(I), SubclassID(ID) {}
53+
friend class MemDGNode; // For constructor.
4154

4255
public:
43-
DGNode(Instruction *I) : I(I) {
44-
IsMem = I->isMemDepCandidate() ||
45-
(isa<AllocaInst>(I) && cast<AllocaInst>(I)->isUsedWithInAlloca()) ||
46-
I->isStackSaveOrRestoreIntrinsic();
56+
DGNode(Instruction *I) : I(I), SubclassID(DGNodeID::DGNode) {
57+
assert(!isMemDepCandidate(I) && "Expected Non-Mem instruction, ");
58+
}
59+
DGNode(const DGNode &Other) = delete;
60+
virtual ~DGNode() = default;
61+
/// \Returns true if this is before \p Other in program order.
62+
bool comesBefore(const DGNode *Other) { return I->comesBefore(Other->I); }
63+
/// \Returns true if \p I is a memory dependency candidate instruction.
64+
static bool isMemDepCandidate(Instruction *I) {
65+
AllocaInst *Alloca;
66+
return I->isMemDepCandidate() ||
67+
((Alloca = dyn_cast<AllocaInst>(I)) &&
68+
Alloca->isUsedWithInAlloca()) ||
69+
I->isStackSaveOrRestoreIntrinsic();
4770
}
71+
4872
Instruction *getInstruction() const { return I; }
49-
void addMemPred(DGNode *PredN) { MemPreds.insert(PredN); }
73+
void addMemPred(MemDGNode *PredN) { MemPreds.insert(PredN); }
5074
/// \Returns all memory dependency predecessors.
51-
iterator_range<DenseSet<DGNode *>::const_iterator> memPreds() const {
75+
iterator_range<DenseSet<MemDGNode *>::const_iterator> memPreds() const {
5276
return make_range(MemPreds.begin(), MemPreds.end());
5377
}
5478
/// \Returns true if there is a memory dependency N->this.
55-
bool hasMemPred(DGNode *N) const { return MemPreds.count(N); }
56-
/// \Returns true if this may read/write memory, or if it has some ordering
57-
/// constraints, like with stacksave/stackrestore and alloca/inalloca.
58-
bool isMem() const { return IsMem; }
79+
bool hasMemPred(DGNode *N) const {
80+
if (auto *MN = dyn_cast<MemDGNode>(N))
81+
return MemPreds.count(MN);
82+
return false;
83+
}
84+
5985
#ifndef NDEBUG
60-
void print(raw_ostream &OS, bool PrintDeps = true) const;
86+
virtual void print(raw_ostream &OS, bool PrintDeps = true) const;
6187
friend raw_ostream &operator<<(DGNode &N, raw_ostream &OS) {
6288
N.print(OS);
6389
return OS;
@@ -66,9 +92,46 @@ class DGNode {
6692
#endif // NDEBUG
6793
};
6894

95+
/// A DependencyGraph Node for instructions that may read/write memory, or have
96+
/// some ordering constraints, like with stacksave/stackrestore and
97+
/// alloca/inalloca.
98+
class MemDGNode final : public DGNode {
99+
MemDGNode *PrevMemN = nullptr;
100+
MemDGNode *NextMemN = nullptr;
101+
102+
void setNextNode(MemDGNode *N) { NextMemN = N; }
103+
void setPrevNode(MemDGNode *N) { PrevMemN = N; }
104+
friend class DependencyGraph; // For setNextNode(), setPrevNode().
105+
106+
public:
107+
MemDGNode(Instruction *I) : DGNode(I, DGNodeID::MemDGNode) {
108+
assert(isMemDepCandidate(I) && "Expected Mem instruction!");
109+
}
110+
static bool classof(const DGNode *Other) {
111+
return Other->SubclassID == DGNodeID::MemDGNode;
112+
}
113+
/// \Returns the previous Mem DGNode in instruction order.
114+
MemDGNode *getPrevNode() const { return PrevMemN; }
115+
/// \Returns the next Mem DGNode in instruction order.
116+
MemDGNode *getNextNode() const { return NextMemN; }
117+
};
118+
119+
/// Convenience builders for a MemDGNode interval.
120+
class MemDGNodeIntervalBuilder {
121+
public:
122+
/// Given \p Instrs it finds their closest mem nodes in the interval and
123+
/// returns the corresponding mem range. Note: BotN (or its neighboring mem
124+
/// node) is included in the range.
125+
static Interval<MemDGNode> make(const Interval<Instruction> &Instrs,
126+
DependencyGraph &DAG);
127+
static Interval<MemDGNode> makeEmpty() { return {}; }
128+
};
129+
69130
class DependencyGraph {
70131
private:
71132
DenseMap<Instruction *, std::unique_ptr<DGNode>> InstrToNodeMap;
133+
/// The DAG spans across all instructions in this interval.
134+
Interval<Instruction> DAGInterval;
72135

73136
public:
74137
DependencyGraph() {}
@@ -77,10 +140,20 @@ class DependencyGraph {
77140
auto It = InstrToNodeMap.find(I);
78141
return It != InstrToNodeMap.end() ? It->second.get() : nullptr;
79142
}
143+
/// Like getNode() but returns nullptr if \p I is nullptr.
144+
DGNode *getNodeOrNull(Instruction *I) const {
145+
if (I == nullptr)
146+
return nullptr;
147+
return getNode(I);
148+
}
80149
DGNode *getOrCreateNode(Instruction *I) {
81150
auto [It, NotInMap] = InstrToNodeMap.try_emplace(I);
82-
if (NotInMap)
83-
It->second = std::make_unique<DGNode>(I);
151+
if (NotInMap) {
152+
if (DGNode::isMemDepCandidate(I))
153+
It->second = std::make_unique<MemDGNode>(I);
154+
else
155+
It->second = std::make_unique<DGNode>(I);
156+
}
84157
return It->second.get();
85158
}
86159
/// Build/extend the dependency graph such that it includes \p Instrs. Returns

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ void DGNode::dump() const {
3131
}
3232
#endif // NDEBUG
3333

34+
Interval<MemDGNode>
35+
MemDGNodeIntervalBuilder::make(const Interval<Instruction> &Instrs,
36+
DependencyGraph &DAG) {
37+
// If top or bottom instructions are not mem-dep candidate nodes we need to
38+
// walk down/up the chain and find the mem-dep ones.
39+
Instruction *MemTopI = Instrs.top();
40+
Instruction *MemBotI = Instrs.bottom();
41+
while (!DGNode::isMemDepCandidate(MemTopI) && MemTopI != MemBotI)
42+
MemTopI = MemTopI->getNextNode();
43+
while (!DGNode::isMemDepCandidate(MemBotI) && MemBotI != MemTopI)
44+
MemBotI = MemBotI->getPrevNode();
45+
// If we couldn't find a mem node in range TopN - BotN then it's empty.
46+
if (!DGNode::isMemDepCandidate(MemTopI))
47+
return {};
48+
// Now that we have the mem-dep nodes, create and return the range.
49+
return Interval<MemDGNode>(cast<MemDGNode>(DAG.getNode(MemTopI)),
50+
cast<MemDGNode>(DAG.getNode(MemBotI)));
51+
}
52+
3453
Interval<Instruction> DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
3554
if (Instrs.empty())
3655
return {};
@@ -39,10 +58,18 @@ Interval<Instruction> DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
3958
auto *TopI = Interval.top();
4059
auto *BotI = Interval.bottom();
4160
DGNode *LastN = getOrCreateNode(TopI);
61+
MemDGNode *LastMemN = dyn_cast<MemDGNode>(LastN);
4262
for (Instruction *I = TopI->getNextNode(), *E = BotI->getNextNode(); I != E;
4363
I = I->getNextNode()) {
4464
auto *N = getOrCreateNode(I);
45-
N->addMemPred(LastN);
65+
N->addMemPred(LastMemN);
66+
// Build the Mem node chain.
67+
if (auto *MemN = dyn_cast<MemDGNode>(N)) {
68+
MemN->setPrevNode(LastMemN);
69+
if (LastMemN != nullptr)
70+
LastMemN->setNextNode(MemN);
71+
LastMemN = MemN;
72+
}
4673
LastN = N;
4774
}
4875
return Interval;

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

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct DependencyGraphTest : public testing::Test {
2929
}
3030
};
3131

32-
TEST_F(DependencyGraphTest, DGNode_IsMem) {
32+
TEST_F(DependencyGraphTest, MemDGNode) {
3333
parseIR(C, R"IR(
3434
declare void @llvm.sideeffect()
3535
declare void @llvm.pseudoprobe(i64, i64, i32, i64)
@@ -66,16 +66,16 @@ define void @foo(i8 %v1, ptr %ptr) {
6666

6767
sandboxir::DependencyGraph DAG;
6868
DAG.extend({&*BB->begin(), BB->getTerminator()});
69-
EXPECT_TRUE(DAG.getNode(Store)->isMem());
70-
EXPECT_TRUE(DAG.getNode(Load)->isMem());
71-
EXPECT_FALSE(DAG.getNode(Add)->isMem());
72-
EXPECT_TRUE(DAG.getNode(StackSave)->isMem());
73-
EXPECT_TRUE(DAG.getNode(StackRestore)->isMem());
74-
EXPECT_FALSE(DAG.getNode(SideEffect)->isMem());
75-
EXPECT_FALSE(DAG.getNode(PseudoProbe)->isMem());
76-
EXPECT_TRUE(DAG.getNode(FakeUse)->isMem());
77-
EXPECT_TRUE(DAG.getNode(Call)->isMem());
78-
EXPECT_FALSE(DAG.getNode(Ret)->isMem());
69+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Store)));
70+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Load)));
71+
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Add)));
72+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(StackSave)));
73+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(StackRestore)));
74+
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(SideEffect)));
75+
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(PseudoProbe)));
76+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(FakeUse)));
77+
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Call)));
78+
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Ret)));
7979
}
8080

8181
TEST_F(DependencyGraphTest, Basic) {
@@ -115,3 +115,100 @@ define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
115115
EXPECT_THAT(N1->memPreds(), testing::ElementsAre(N0));
116116
EXPECT_THAT(N2->memPreds(), testing::ElementsAre(N1));
117117
}
118+
119+
TEST_F(DependencyGraphTest, MemDGNode_getPrevNode_getNextNode) {
120+
parseIR(C, R"IR(
121+
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
122+
store i8 %v0, ptr %ptr
123+
add i8 %v0, %v0
124+
store i8 %v1, ptr %ptr
125+
ret void
126+
}
127+
)IR");
128+
llvm::Function *LLVMF = &*M->getFunction("foo");
129+
sandboxir::Context Ctx(C);
130+
auto *F = Ctx.createFunction(LLVMF);
131+
auto *BB = &*F->begin();
132+
auto It = BB->begin();
133+
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
134+
[[maybe_unused]] auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
135+
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
136+
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
137+
138+
sandboxir::DependencyGraph DAG;
139+
DAG.extend({&*BB->begin(), BB->getTerminator()});
140+
141+
auto *S0N = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
142+
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
143+
144+
EXPECT_EQ(S0N->getPrevNode(), nullptr);
145+
EXPECT_EQ(S0N->getNextNode(), S1N);
146+
147+
EXPECT_EQ(S1N->getPrevNode(), S0N);
148+
EXPECT_EQ(S1N->getNextNode(), nullptr);
149+
}
150+
151+
TEST_F(DependencyGraphTest, DGNodeRange) {
152+
parseIR(C, R"IR(
153+
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
154+
add i8 %v0, %v0
155+
store i8 %v0, ptr %ptr
156+
add i8 %v0, %v0
157+
store i8 %v1, ptr %ptr
158+
ret void
159+
}
160+
)IR");
161+
llvm::Function *LLVMF = &*M->getFunction("foo");
162+
sandboxir::Context Ctx(C);
163+
auto *F = Ctx.createFunction(LLVMF);
164+
auto *BB = &*F->begin();
165+
auto It = BB->begin();
166+
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
167+
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
168+
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
169+
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
170+
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
171+
172+
sandboxir::DependencyGraph DAG;
173+
DAG.extend({&*BB->begin(), BB->getTerminator()});
174+
175+
auto *S0N = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
176+
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
177+
178+
// Check empty range.
179+
EXPECT_THAT(sandboxir::MemDGNodeIntervalBuilder::makeEmpty(),
180+
testing::ElementsAre());
181+
182+
// Returns the pointers in Range.
183+
auto getPtrVec = [](const auto &Range) {
184+
SmallVector<const sandboxir::DGNode *> Vec;
185+
for (const sandboxir::DGNode &N : Range)
186+
Vec.push_back(&N);
187+
return Vec;
188+
};
189+
// Both TopN and BotN are memory.
190+
EXPECT_THAT(
191+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, S1}, DAG)),
192+
testing::ElementsAre(S0N, S1N));
193+
// Only TopN is memory.
194+
EXPECT_THAT(
195+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, Ret}, DAG)),
196+
testing::ElementsAre(S0N, S1N));
197+
EXPECT_THAT(
198+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, Add1}, DAG)),
199+
testing::ElementsAre(S0N));
200+
// Only BotN is memory.
201+
EXPECT_THAT(
202+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, S1}, DAG)),
203+
testing::ElementsAre(S0N, S1N));
204+
EXPECT_THAT(
205+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, S0}, DAG)),
206+
testing::ElementsAre(S0N));
207+
// Neither TopN or BotN is memory.
208+
EXPECT_THAT(
209+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, Ret}, DAG)),
210+
testing::ElementsAre(S0N, S1N));
211+
EXPECT_THAT(
212+
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, Add0}, DAG)),
213+
testing::ElementsAre());
214+
}

0 commit comments

Comments
 (0)