Skip to content

[SandboxVec][DAG] MemDGNode for memory-dependency candidate nodes #109684

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 1, 2024

Conversation

vporpo
Copy link
Contributor

@vporpo vporpo commented Sep 23, 2024

This patch implements the MemDGNode class for DAG nodes that are candidates
for memory dependencies. These nodes form a chain that is accessible by
getPrevNode() and getNextNode().

It also implements a builder class that creates MemDGNode intervals from
Instructions.

@llvmbot
Copy link
Member

llvmbot commented Sep 23, 2024

@llvm/pr-subscribers-llvm-transforms

Author: vporpo (vporpo)

Changes

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.

Full diff: https://github.com/llvm/llvm-project/pull/109684.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h (+86)
  • (modified) llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp (+61)
  • (modified) llvm/unittests/Transforms/Vectorize/SandboxVectorizer/DependencyGraphTest.cpp (+102)
diff --git a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h
index 75b2073d0557c5..c040c00b68220e 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h
@@ -29,6 +29,8 @@
 
 namespace llvm::sandboxir {
 
+class DependencyGraph;
+
 /// A DependencyGraph Node that points to an Instruction and contains memory
 /// dependency edges.
 class DGNode {
@@ -45,6 +47,7 @@ class DGNode {
             (isa<AllocaInst>(I) && cast<AllocaInst>(I)->isUsedWithInAlloca()) ||
             I->isStackSaveOrRestoreIntrinsic();
   }
+  DGNode(const DGNode &Other) = delete;
   Instruction *getInstruction() const { return I; }
   void addMemPred(DGNode *PredN) { MemPreds.insert(PredN); }
   /// \Returns all memory dependency predecessors.
@@ -56,6 +59,19 @@ class DGNode {
   /// \Returns true if this may read/write memory, or if it has some ordering
   /// constraings, like with stacksave/stackrestore and alloca/inalloca.
   bool isMem() const { return IsMem; }
+  /// \Returns the previous DGNode in program order.
+  DGNode *getPrev(DependencyGraph &DAG) const;
+  /// \Returns the next DGNode in program order.
+  DGNode *getNext(DependencyGraph &DAG) const;
+  /// Walks up the instruction chain looking for the next memory dependency
+  /// candidate instruction.
+  /// \Returns the corresponding DAG Node, or null if no instruction found.
+  DGNode *getPrevMem(DependencyGraph &DAG) const;
+  /// Walks down the instr chain looking for the next memory dependency
+  /// candidate instruction.
+  /// \Returns the corresponding DAG Node, or null if no instruction found.
+  DGNode *getNextMem(DependencyGraph &DAG) const;
+
 #ifndef NDEBUG
   void print(raw_ostream &OS, bool PrintDeps = true) const;
   friend raw_ostream &operator<<(DGNode &N, raw_ostream &OS) {
@@ -66,9 +82,73 @@ class DGNode {
 #endif // NDEBUG
 };
 
+/// Walks in the order of the instruction chain but skips non-mem Nodes.
+/// This is used for building/updating the DAG.
+class MemDGNodeIterator {
+  DGNode *N;
+  DependencyGraph *DAG;
+
+public:
+  using difference_type = std::ptrdiff_t;
+  using value_type = DGNode;
+  using pointer = value_type *;
+  using reference = value_type &;
+  using iterator_category = std::bidirectional_iterator_tag;
+  MemDGNodeIterator(DGNode *N, DependencyGraph *DAG) : N(N), DAG(DAG) {
+    assert((N == nullptr || N->isMem()) && "Expects mem node!");
+  }
+  MemDGNodeIterator &operator++() {
+    assert(N != nullptr && "Already at end!");
+    N = N->getNextMem(*DAG);
+    return *this;
+  }
+  MemDGNodeIterator operator++(int) {
+    auto ItCopy = *this;
+    ++*this;
+    return ItCopy;
+  }
+  MemDGNodeIterator &operator--() {
+    N = N->getPrevMem(*DAG);
+    return *this;
+  }
+  MemDGNodeIterator operator--(int) {
+    auto ItCopy = *this;
+    --*this;
+    return ItCopy;
+  }
+  pointer operator*() { return N; }
+  const DGNode *operator*() const { return N; }
+  bool operator==(const MemDGNodeIterator &Other) const { return N == Other.N; }
+  bool operator!=(const MemDGNodeIterator &Other) const {
+    return !(*this == Other);
+  }
+};
+
+/// A MemDGNodeIterator with convenience builders and dump().
+class DGNodeRange : public iterator_range<MemDGNodeIterator> {
+public:
+  DGNodeRange(MemDGNodeIterator Begin, MemDGNodeIterator End)
+      : iterator_range(Begin, End) {}
+  /// An empty range.
+  DGNodeRange()
+      : iterator_range(MemDGNodeIterator(nullptr, nullptr),
+                       MemDGNodeIterator(nullptr, nullptr)) {}
+  /// Given \p TopN and \p BotN it finds their closest mem nodes in the range
+  /// TopN to BotN and returns the corresponding mem range.
+  /// Note: BotN (or its neighboring mem node) is included in the range.
+  static DGNodeRange makeMemRange(DGNode *TopN, DGNode *BotN,
+                                  DependencyGraph &DAG);
+  static DGNodeRange makeEmptyMemRange() { return DGNodeRange(); }
+#ifndef NDEBUG
+  LLVM_DUMP_METHOD void dump() const;
+#endif
+};
+
 class DependencyGraph {
 private:
   DenseMap<Instruction *, std::unique_ptr<DGNode>> InstrToNodeMap;
+  /// The DAG spans across all instructions in this interval.
+  InstrInterval DAGInterval;
 
 public:
   DependencyGraph() {}
@@ -77,6 +157,12 @@ class DependencyGraph {
     auto It = InstrToNodeMap.find(I);
     return It != InstrToNodeMap.end() ? It->second.get() : nullptr;
   }
+  /// Like getNode() but returns nullptr if \p I is nullptr.
+  DGNode *getNodeOrNull(Instruction *I) const {
+    if (I == nullptr)
+      return nullptr;
+    return getNode(I);
+  }
   DGNode *getOrCreateNode(Instruction *I) {
     auto [It, NotInMap] = InstrToNodeMap.try_emplace(I);
     if (NotInMap)
diff --git a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp
index 139e581ce03d96..58545df8f456e7 100644
--- a/llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp
+++ b/llvm/lib/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.cpp
@@ -11,6 +11,39 @@
 
 using namespace llvm::sandboxir;
 
+// TODO: Move this to Utils once it lands.
+/// \Returns the previous memory-dependency-candidate instruction before \p I in
+/// the instruction stream.
+static llvm::sandboxir::Instruction *
+getPrevMemDepInst(llvm::sandboxir::Instruction *I) {
+  for (I = I->getPrevNode(); I != nullptr; I = I->getPrevNode())
+    if (I->isMemDepCandidate() || I->isStackSaveOrRestoreIntrinsic())
+      return I;
+  return nullptr;
+}
+/// \Returns the next memory-dependency-candidate instruction after \p I in the
+/// instruction stream.
+static llvm::sandboxir::Instruction *
+getNextMemDepInst(llvm::sandboxir::Instruction *I) {
+  for (I = I->getNextNode(); I != nullptr; I = I->getNextNode())
+    if (I->isMemDepCandidate() || I->isStackSaveOrRestoreIntrinsic())
+      return I;
+  return nullptr;
+}
+
+DGNode *DGNode::getPrev(DependencyGraph &DAG) const {
+  return DAG.getNodeOrNull(I->getPrevNode());
+}
+DGNode *DGNode::getNext(DependencyGraph &DAG) const {
+  return DAG.getNodeOrNull(I->getNextNode());
+}
+DGNode *DGNode::getPrevMem(DependencyGraph &DAG) const {
+  return DAG.getNodeOrNull(getPrevMemDepInst(I));
+}
+DGNode *DGNode::getNextMem(DependencyGraph &DAG) const {
+  return DAG.getNodeOrNull(getNextMemDepInst(I));
+}
+
 #ifndef NDEBUG
 void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
   I->dumpOS(OS);
@@ -31,6 +64,34 @@ void DGNode::dump() const {
 }
 #endif // NDEBUG
 
+DGNodeRange DGNodeRange::makeMemRange(DGNode *TopN, DGNode *BotN,
+                                      DependencyGraph &DAG) {
+  assert((TopN == BotN ||
+          TopN->getInstruction()->comesBefore(BotN->getInstruction())) &&
+         "Expected TopN before BotN!");
+  // If TopN/BotN are not mem-dep candidate nodes we need to walk down/up the
+  // chain and find the mem-dep ones.
+  DGNode *MemTopN = TopN;
+  DGNode *MemBotN = BotN;
+  while (!MemTopN->isMem() && MemTopN != MemBotN)
+    MemTopN = MemTopN->getNext(DAG);
+  while (!MemBotN->isMem() && MemBotN != MemTopN)
+    MemBotN = MemBotN->getPrev(DAG);
+  // If we couldn't find a mem node in range TopN - BotN then it's empty.
+  if (!MemTopN->isMem())
+    return {};
+  // Now that we have the mem-dep nodes, create and return the range.
+  return DGNodeRange(MemDGNodeIterator(MemTopN, &DAG),
+                     MemDGNodeIterator(MemBotN->getNextMem(DAG), &DAG));
+}
+
+#ifndef NDEBUG
+void DGNodeRange::dump() const {
+  for (const DGNode *N : *this)
+    N->dump();
+}
+#endif // NDEBUG
+
 InstrInterval DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
   if (Instrs.empty())
     return {};
diff --git a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/DependencyGraphTest.cpp b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/DependencyGraphTest.cpp
index f6bfd097f20a4e..3c8e217264481c 100644
--- a/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/DependencyGraphTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/SandboxVectorizer/DependencyGraphTest.cpp
@@ -113,3 +113,105 @@ define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
   EXPECT_THAT(N1->memPreds(), testing::ElementsAre(N0));
   EXPECT_THAT(N2->memPreds(), testing::ElementsAre(N1));
 }
+
+TEST_F(DependencyGraphTest, DGNode_getPrev_getNext_getPrevMem_getNextMem) {
+  parseIR(C, R"IR(
+define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
+  store i8 %v0, ptr %ptr
+  add i8 %v0, %v0
+  store i8 %v1, ptr %ptr
+  ret void
+}
+)IR");
+  llvm::Function *LLVMF = &*M->getFunction("foo");
+  sandboxir::Context Ctx(C);
+  auto *F = Ctx.createFunction(LLVMF);
+  auto *BB = &*F->begin();
+  auto It = BB->begin();
+  auto *S0 = cast<sandboxir::StoreInst>(&*It++);
+  auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
+  auto *S1 = cast<sandboxir::StoreInst>(&*It++);
+  auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
+
+  sandboxir::DependencyGraph DAG;
+  DAG.extend({&*BB->begin(), BB->getTerminator()});
+
+  sandboxir::DGNode *S0N = DAG.getNode(S0);
+  sandboxir::DGNode *AddN = DAG.getNode(Add);
+  sandboxir::DGNode *S1N = DAG.getNode(S1);
+  sandboxir::DGNode *RetN = DAG.getNode(Ret);
+
+  EXPECT_EQ(S0N->getPrev(DAG), nullptr);
+  EXPECT_EQ(S0N->getNext(DAG), AddN);
+  EXPECT_EQ(S0N->getPrevMem(DAG), nullptr);
+  EXPECT_EQ(S0N->getNextMem(DAG), S1N);
+
+  EXPECT_EQ(AddN->getPrev(DAG), S0N);
+  EXPECT_EQ(AddN->getNext(DAG), S1N);
+  EXPECT_EQ(AddN->getPrevMem(DAG), S0N);
+  EXPECT_EQ(AddN->getNextMem(DAG), S1N);
+
+  EXPECT_EQ(S1N->getPrev(DAG), AddN);
+  EXPECT_EQ(S1N->getNext(DAG), RetN);
+  EXPECT_EQ(S1N->getPrevMem(DAG), S0N);
+  EXPECT_EQ(S1N->getNextMem(DAG), nullptr);
+
+  EXPECT_EQ(RetN->getPrev(DAG), S1N);
+  EXPECT_EQ(RetN->getNext(DAG), nullptr);
+  EXPECT_EQ(RetN->getPrevMem(DAG), S1N);
+  EXPECT_EQ(RetN->getNextMem(DAG), nullptr);
+}
+
+TEST_F(DependencyGraphTest, DGNodeRange) {
+  parseIR(C, R"IR(
+define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
+  add i8 %v0, %v0
+  store i8 %v0, ptr %ptr
+  add i8 %v0, %v0
+  store i8 %v1, ptr %ptr
+  ret void
+}
+)IR");
+  llvm::Function *LLVMF = &*M->getFunction("foo");
+  sandboxir::Context Ctx(C);
+  auto *F = Ctx.createFunction(LLVMF);
+  auto *BB = &*F->begin();
+  auto It = BB->begin();
+  auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
+  auto *S0 = cast<sandboxir::StoreInst>(&*It++);
+  auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
+  auto *S1 = cast<sandboxir::StoreInst>(&*It++);
+  auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
+
+  sandboxir::DependencyGraph DAG;
+  DAG.extend({&*BB->begin(), BB->getTerminator()});
+
+  sandboxir::DGNode *Add0N = DAG.getNode(Add0);
+  sandboxir::DGNode *S0N = DAG.getNode(S0);
+  sandboxir::DGNode *Add1N = DAG.getNode(Add1);
+  sandboxir::DGNode *S1N = DAG.getNode(S1);
+  sandboxir::DGNode *RetN = DAG.getNode(Ret);
+
+  // Check empty range.
+  EXPECT_THAT(sandboxir::DGNodeRange::makeEmptyMemRange(),
+              testing::ElementsAre());
+
+  // Both TopN and BotN are memory.
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, S1N, DAG),
+              testing::ElementsAre(S0N, S1N));
+  // Only TopN is memory.
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, RetN, DAG),
+              testing::ElementsAre(S0N, S1N));
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(S0N, Add1N, DAG),
+              testing::ElementsAre(S0N));
+  // Only BotN is memory.
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, S1N, DAG),
+              testing::ElementsAre(S0N, S1N));
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, S0N, DAG),
+              testing::ElementsAre(S0N));
+  // Neither TopN or BotN is memory.
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, RetN, DAG),
+              testing::ElementsAre(S0N, S1N));
+  EXPECT_THAT(sandboxir::DGNodeRange::makeMemRange(Add0N, Add0N, DAG),
+              testing::ElementsAre());
+}

@tschuett
Copy link

You are upstreaming this because you have already experience with it and thought about the design.

Two questions: how does it relate to MemorySSA and graph traits?

@vporpo
Copy link
Contributor Author

vporpo commented Sep 23, 2024

It's not relying on MemorySSA for now, but we may switch to it at some point.
Regarding GraphTraits, they are not really needed for now, but I will implement them at some point.

@vporpo
Copy link
Contributor Author

vporpo commented Sep 24, 2024

I made a design change: There are now two DAG Node classes: DGNode for non-mem instructions and MemDGNode for memory dependency candidates. A chain links MemDGNode together.

@vporpo vporpo changed the title [SandboxVec][DAG] Implement functions for iterating through DGNodes [SandboxVec][DAG] MemDGNode for memory-dependency candidate nodes Sep 24, 2024
@vporpo
Copy link
Contributor Author

vporpo commented Sep 26, 2024

ping

Copy link
Contributor

@Sterling-Augustine Sterling-Augustine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor things, but mostly looks good.

@@ -66,9 +84,94 @@ class DGNode {
#endif // NDEBUG
};

/// A DependencyGraph Node for instructiosn that may read/write memory, or have
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instructions

(isa<AllocaInst>(I) && cast<AllocaInst>(I)->isUsedWithInAlloca()) ||
I->isStackSaveOrRestoreIntrinsic();
}

Instruction *getInstruction() const { return I; }
void addMemPred(DGNode *PredN) { MemPreds.insert(PredN); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this take a MemDGNode now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I just saw this comments. Yes, I think this should be MemDGNode.

static bool classof(const DGNode *Other) {
return Other->SubclassID == DGNodeID::MemDGNode;
}
/// \Returns the previosu Mem DGNode in instruction order.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

MemDGNode *NextMemN = nullptr;

void setNextNode(DGNode *N) { NextMemN = cast_or_null<MemDGNode>(N); }
void setPrevNode(DGNode *N) { PrevMemN = cast_or_null<MemDGNode>(N); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "cast_if_present" is the new hotness over cast_or_null.

How sure are we that these will always be MemDGNodes? And if we are sure, shouldn't it take MemDGNodes then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should take a MemDGNode * argument, let me change it.

/// \Returns true if \p I is a memory dependency candidate instruction.
static bool isMemDepCandidate(Instruction *I) {
return I->isMemDepCandidate() ||
(isa<AllocaInst>(I) && cast<AllocaInst>(I)->isUsedWithInAlloca()) ||

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dyn_cast!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative is the following which is not as readable as the original code.

if (I->isMemDepCandidate())
  return true;
auto *AllocaI = dyn_cast<AllocaInst>(I);
return AllocaI != nullptr ? AllocaI->isUsedWithInAlloca() || I->isStackSaveOrRestoreIntrinsic();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (I->isMemDepCandidate())
  return true;
if (auto *AllocaI = dyn_cast<AllocaInst>(I))
  if (AllocaI->isUsedWithInAlloca())
    return true;
return I->isStackSaveOrRestoreIntrinsic();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the original one is much better.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is a violation of the coding style guide.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Note that you should not use an isa<> test followed by a cast<>, for that use the dyn_cast<> operator."
https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can argue about the should but ....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me start a thread on discourse about it. It would be good to know what the rest of the community thinks about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me change it to what @david-xl suggested in the thread, which is still readable but not too verbose:

AllocaInst *Alloca;
return I->isMemDepCandidate() || 
      ((Alloca = dyn_cast<AllocaInst>(I)) && Alloca->isUsedWithInAlloca()) ||
      I->isStackSaveOrRestoreIntrinsic();

@vporpo vporpo force-pushed the DAG branch 3 times, most recently from f2e8949 to 098cc79 Compare September 27, 2024 20:42
@vporpo
Copy link
Contributor Author

vporpo commented Sep 27, 2024

I just realized I had pushed the version without the fix.

@vporpo
Copy link
Contributor Author

vporpo commented Sep 27, 2024

Now that the Interval is available there is no need for the custom iterator that was implemented in this patch. I was planning to do this cleanup in the follow-up patch, but it's probably best to do this here since it simplifies the code of this patch a bit.

@vporpo
Copy link
Contributor Author

vporpo commented Sep 30, 2024

Rebased and fixed typo

@vporpo
Copy link
Contributor Author

vporpo commented Oct 1, 2024

ping

Copy link
Contributor

@alinas alinas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions inline. This is very skeleton-like and some things are not clear where they're heading.

if (NotInMap)
It->second = std::make_unique<DGNode>(I);
if (NotInMap) {
if (I->isMemDepCandidate() || I->isStackSaveOrRestoreIntrinsic())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These conditions shouldn't be the same as in the static isMemDepCandidate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I should have used the static function for the check.

/// Given \p Instrs it finds their closest mem nodes in the interval and
/// returns the corresponding mem range. Note: BotN (or its neighboring mem
/// node) is included in the range.
static Interval<MemDGNode> make(const Interval<Instruction> &Instrs,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate why you need both Interval and a DAG? The DAG has an interval inside. Are they different intervals, what's the usage for each?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interval describes a range of nodes in program order. This particular interval is going to be used as the range of nodes to be scanned for dependencies as we are building the DAG. The DAG's interval is the range of instructions that are represented by the DAG, it's only used to check if some instruction/node is inside or outside the DAG.

Instruction *MemTopI = Instrs.top();
Instruction *MemBotI = Instrs.bottom();
while (!DGNode::isMemDepCandidate(MemTopI) && MemTopI != MemBotI)
MemTopI = MemTopI->getNextNode();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing how getNextNode works for an Instruction here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just skip the non memory-candidate instructions. getNextNode() gives you the next instruction in the instruction list.

This patch implements the MemDGNode class for DAG nodes that are candidates
for memory dependencies. These nodes form a chain that is accessible by
`getPrevNode()` and `getNextNode()`.

It also implements a builder class that creates MemDGNode intervals from
Instructions.
Copy link
Member

@tmsri tmsri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return I->isMemDepCandidate() ||
((Alloca = dyn_cast<AllocaInst>(I)) &&
Alloca->isUsedWithInAlloca()) ||
I->isStackSaveOrRestoreIntrinsic();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't these checks be moved into isMemDepCandidate for sandboxir::Instruction?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean in member function like sandboxir::Instruction::isMemDepCandidate() ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted offline and resolving, keeping it here is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why isMemDepCandidate() and isStackSaveOrRestoreIntrinsic() are in sandboxir::Instruction is that they need to access LLVM IR, but the combined check does not need to. Also I don't think this check will be used in other places.

@vporpo vporpo merged commit fd5e220 into llvm:main Oct 1, 2024
8 checks passed
zeroomega added a commit to zeroomega/llvm-project that referenced this pull request Oct 2, 2024
Sterling-Augustine pushed a commit to Sterling-Augustine/llvm-project that referenced this pull request Oct 3, 2024
…vm#109684)

This patch implements the MemDGNode class for DAG nodes that are
candidates
for memory dependencies. These nodes form a chain that is accessible by
`getPrevNode()` and `getNextNode()`.

It also implements a builder class that creates MemDGNode intervals from
Instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants