[llvm] [MachineOutliner] Leaf Descendants (PR #90275)

Xuan Zhang via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 14 21:05:28 PDT 2024


https://github.com/xuanzh-meta updated https://github.com/llvm/llvm-project/pull/90275

>From 887ef5491d0ee1daa8d79417046000d1e06b94e5 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 12 Apr 2024 10:57:51 -0700
Subject: [PATCH 1/8] consider leaf descendants to include more candidates for
 outlining

---
 llvm/include/llvm/Support/SuffixTree.h        |  34 ++++-
 llvm/include/llvm/Support/SuffixTreeNode.h    |  25 +++-
 llvm/lib/CodeGen/MachineOutliner.cpp          |   8 +-
 llvm/lib/Support/SuffixTree.cpp               | 109 +++++++++++---
 llvm/lib/Support/SuffixTreeNode.cpp           |   5 +
 .../machine-outliner-cfi-tail-some.mir        |   2 +-
 .../machine-outliner-leaf-descendants.ll      | 124 ++++++++++++++++
 .../AArch64/machine-outliner-overlap.mir      |  24 +++-
 .../machine-outliner-retaddr-sign-sp-mod.mir  |   2 +-
 .../machine-outliner-retaddr-sign-thunk.ll    |   4 +-
 .../AArch64/machine-outliner-throw2.ll        |   4 +-
 .../CodeGen/AArch64/machine-outliner-thunk.ll |   2 +-
 .../test/CodeGen/AArch64/machine-outliner.mir |   2 +-
 .../RISCV/machineoutliner-pcrel-lo.mir        |   8 +-
 llvm/unittests/Support/SuffixTreeTest.cpp     | 134 ++++++++++++++++++
 15 files changed, 453 insertions(+), 34 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll

diff --git a/llvm/include/llvm/Support/SuffixTree.h b/llvm/include/llvm/Support/SuffixTree.h
index 4940fbbf308d8..37b7366640430 100644
--- a/llvm/include/llvm/Support/SuffixTree.h
+++ b/llvm/include/llvm/Support/SuffixTree.h
@@ -42,6 +42,9 @@ class SuffixTree {
   /// Each element is an integer representing an instruction in the module.
   ArrayRef<unsigned> Str;
 
+  /// Whether to consider leaf descendants or only leaf children.
+  bool OutlinerLeafDescendants;
+
   /// A repeated substring in the tree.
   struct RepeatedSubstring {
     /// The length of the string.
@@ -130,11 +133,27 @@ class SuffixTree {
   /// this step.
   unsigned extend(unsigned EndIdx, unsigned SuffixesToAdd);
 
+  /// This vector contains all leaf nodes of this suffix tree. These leaf nodes
+  /// are identified using post-order depth-first traversal, so that the order
+  /// of these leaf nodes in the vector matches the order of the leaves in the
+  /// tree from left to right if one were to draw the tree on paper.
+  std::vector<SuffixTreeLeafNode *> LeafNodes;
+
+  /// Perform a post-order depth-first traversal of the tree and perform two
+  /// tasks during the traversal. The first is to populate LeafNodes, adding
+  /// nodes in order of the traversal. The second is to keep track of the leaf
+  /// descendants of every internal node by assigning values to LeftLeafIndex
+  /// and RightLefIndex fields of SuffixTreeNode for all internal nodes.
+  void setLeafNodes();
+
 public:
   /// Construct a suffix tree from a sequence of unsigned integers.
   ///
   /// \param Str The string to construct the suffix tree for.
-  SuffixTree(const ArrayRef<unsigned> &Str);
+  /// \param OutlinerLeafDescendants Whether to consider leaf descendants or
+  /// only leaf children (used by Machine Outliner).
+  SuffixTree(const ArrayRef<unsigned> &Str,
+             bool OutlinerLeafDescendants = false);
 
   /// Iterator for finding all repeated substrings in the suffix tree.
   struct RepeatedSubstringIterator {
@@ -154,6 +173,12 @@ class SuffixTree {
     /// instruction lengths.
     const unsigned MinLength = 2;
 
+    /// Vector of leaf nodes of the suffix tree.
+    const std::vector<SuffixTreeLeafNode *> &LeafNodes;
+
+    /// Whether to consider leaf descendants or only leaf children.
+    bool OutlinerLeafDescendants = !LeafNodes.empty();
+
     /// Move the iterator to the next repeated substring.
     void advance();
 
@@ -179,7 +204,10 @@ class SuffixTree {
       return !(*this == Other);
     }
 
-    RepeatedSubstringIterator(SuffixTreeInternalNode *N) : N(N) {
+    RepeatedSubstringIterator(
+        SuffixTreeInternalNode *N,
+        const std::vector<SuffixTreeLeafNode *> &LeafNodes = {})
+        : N(N), LeafNodes(LeafNodes) {
       // Do we have a non-null node?
       if (!N)
         return;
@@ -191,7 +219,7 @@ class SuffixTree {
   };
 
   typedef RepeatedSubstringIterator iterator;
-  iterator begin() { return iterator(Root); }
+  iterator begin() { return iterator(Root, LeafNodes); }
   iterator end() { return iterator(nullptr); }
 };
 
diff --git a/llvm/include/llvm/Support/SuffixTreeNode.h b/llvm/include/llvm/Support/SuffixTreeNode.h
index 7d0d1cf0c58b9..84b590f2deb0c 100644
--- a/llvm/include/llvm/Support/SuffixTreeNode.h
+++ b/llvm/include/llvm/Support/SuffixTreeNode.h
@@ -46,6 +46,17 @@ struct SuffixTreeNode {
   /// the root to this node.
   unsigned ConcatLen = 0;
 
+  /// These two indices give a range of indices for its leaf descendants.
+  /// Imagine drawing a tree on paper and assigning a unique index to each leaf
+  /// node in monotonically increasing order from left to right. This way of
+  /// numbering the leaf nodes allows us to associate a continuous range of
+  /// indices with each internal node. For example, if a node has leaf
+  /// descendants with indices i, i+1, ..., j, then its LeftLeafIdx is i and
+  /// its RightLeafIdx is j. These indices are for LeafNodes in the SuffixTree
+  /// class, which is constructed using post-order depth-first traversal.
+  unsigned LeftLeafIdx = EmptyIdx;
+  unsigned RightLeafIdx = EmptyIdx;
+
 public:
   // LLVM RTTI boilerplate.
   NodeKind getKind() const { return Kind; }
@@ -56,6 +67,18 @@ struct SuffixTreeNode {
   /// \returns the end index of this node.
   virtual unsigned getEndIdx() const = 0;
 
+  /// \return the index of this node's left most leaf node.
+  unsigned getLeftLeafIdx() const;
+
+  /// \return the index of this node's right most leaf node.
+  unsigned getRightLeafIdx() const;
+
+  /// Set the index of the left most leaf node of this node to \p Idx.
+  void setLeftLeafIdx(unsigned Idx);
+
+  /// Set the index of the right most leaf node of this node to \p Idx.
+  void setRightLeafIdx(unsigned Idx);
+
   /// Advance this node's StartIdx by \p Inc.
   void incrementStartIdx(unsigned Inc);
 
@@ -168,4 +191,4 @@ struct SuffixTreeLeafNode : SuffixTreeNode {
   virtual ~SuffixTreeLeafNode() = default;
 };
 } // namespace llvm
-#endif // LLVM_SUPPORT_SUFFIXTREE_NODE_H
\ No newline at end of file
+#endif // LLVM_SUPPORT_SUFFIXTREE_NODE_H
diff --git a/llvm/lib/CodeGen/MachineOutliner.cpp b/llvm/lib/CodeGen/MachineOutliner.cpp
index 9e2e6316dc6d1..6836f6cbc5409 100644
--- a/llvm/lib/CodeGen/MachineOutliner.cpp
+++ b/llvm/lib/CodeGen/MachineOutliner.cpp
@@ -121,6 +121,12 @@ static cl::opt<unsigned> OutlinerBenefitThreshold(
     cl::desc(
         "The minimum size in bytes before an outlining candidate is accepted"));
 
+static cl::opt<bool> OutlinerLeafDescendants(
+    "outliner-leaf-descendants", cl::init(true), cl::Hidden,
+    cl::desc("Consider all leaf descendants of internal nodes of the suffix "
+             "tree as candidates for outlining (if false, only leaf children "
+             "are considered)"));
+
 namespace {
 
 /// Maps \p MachineInstrs to unsigned integers and stores the mappings.
@@ -576,7 +582,7 @@ void MachineOutliner::emitOutlinedFunctionRemark(OutlinedFunction &OF) {
 void MachineOutliner::findCandidates(
     InstructionMapper &Mapper, std::vector<OutlinedFunction> &FunctionList) {
   FunctionList.clear();
-  SuffixTree ST(Mapper.UnsignedVec);
+  SuffixTree ST(Mapper.UnsignedVec, OutlinerLeafDescendants);
 
   // First, find all of the repeated substrings in the tree of minimum length
   // 2.
diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index c00c7989d1a64..e868dfe513860 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -11,9 +11,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/SuffixTree.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/SuffixTreeNode.h"
+#include <stack>
 
 using namespace llvm;
 
@@ -26,7 +28,9 @@ static size_t numElementsInSubstring(const SuffixTreeNode *N) {
   return N->getEndIdx() - N->getStartIdx() + 1;
 }
 
-SuffixTree::SuffixTree(const ArrayRef<unsigned> &Str) : Str(Str) {
+SuffixTree::SuffixTree(const ArrayRef<unsigned> &Str,
+                       bool OutlinerLeafDescendants)
+    : Str(Str), OutlinerLeafDescendants(OutlinerLeafDescendants) {
   Root = insertRoot();
   Active.Node = Root;
 
@@ -46,6 +50,11 @@ SuffixTree::SuffixTree(const ArrayRef<unsigned> &Str) : Str(Str) {
   // Set the suffix indices of each leaf.
   assert(Root && "Root node can't be nullptr!");
   setSuffixIndices();
+
+  // Collect all leaf nodes of the suffix tree. And for each internal node,
+  // record the range of leaf nodes that are descendants of it.
+  if (OutlinerLeafDescendants)
+    setLeafNodes();
 }
 
 SuffixTreeNode *SuffixTree::insertLeaf(SuffixTreeInternalNode &Parent,
@@ -105,6 +114,68 @@ void SuffixTree::setSuffixIndices() {
   }
 }
 
+void SuffixTree::setLeafNodes() {
+  // A stack that keeps track of nodes to visit for post-order DFS traversal.
+  std::stack<SuffixTreeNode *> ToVisit;
+  ToVisit.push(Root);
+
+  // This keeps track of the index of the next leaf node to be added to
+  // the LeafNodes vector of the suffix tree.
+  unsigned LeafCounter = 0;
+
+  // This keeps track of nodes whose children have been added to the stack
+  // during the post-order depth-first traversal of the tree.
+  llvm::SmallPtrSet<SuffixTreeInternalNode *, 32> ChildrenAddedToStack;
+
+  // Traverse the tree in post-order.
+  while (!ToVisit.empty()) {
+    SuffixTreeNode *CurrNode = ToVisit.top();
+    ToVisit.pop();
+    if (auto *CurrInternalNode = dyn_cast<SuffixTreeInternalNode>(CurrNode)) {
+      // The current node is an internal node.
+      if (ChildrenAddedToStack.find(CurrInternalNode) !=
+          ChildrenAddedToStack.end()) {
+        // If the children of the current node has been added to the stack,
+        // then this is the second time we visit this node and at this point,
+        // all of its children have already been processed. Now, we can
+        // set its LeftLeafIdx and RightLeafIdx;
+        auto it = CurrInternalNode->Children.begin();
+        if (it != CurrInternalNode->Children.end()) {
+          // Get the first child to use its RightLeafIdx. The RightLeafIdx is
+          // used as the first child is the initial one added to the stack, so
+          // it's the last one to be processed. This implies that the leaf
+          // descendants of the first child are assigned the largest index
+          // numbers.
+          CurrNode->setRightLeafIdx(it->second->getRightLeafIdx());
+          // get the last child to use its LeftLeafIdx.
+          while (std::next(it) != CurrInternalNode->Children.end())
+            it = std::next(it);
+          CurrNode->setLeftLeafIdx(it->second->getLeftLeafIdx());
+          assert(CurrNode->getLeftLeafIdx() <= CurrNode->getRightLeafIdx() &&
+                 "LeftLeafIdx should not be larger than RightLeafIdx");
+        }
+      } else {
+        // This is the first time we visit this node. This means that its
+        // children have not been added to the stack yet. Hence, we will add
+        // the current node back to the stack and add its children to the
+        // stack for processing.
+        ToVisit.push(CurrNode);
+        for (auto &ChildPair : CurrInternalNode->Children)
+          ToVisit.push(ChildPair.second);
+        ChildrenAddedToStack.insert(CurrInternalNode);
+      }
+    } else {
+      // The current node is a leaf node.
+      // We can simplyset its LeftLeafIdx and RightLeafIdx.
+      CurrNode->setLeftLeafIdx(LeafCounter);
+      CurrNode->setRightLeafIdx(LeafCounter);
+      LeafCounter++;
+      auto *CurrLeafNode = cast<SuffixTreeLeafNode>(CurrNode);
+      LeafNodes.push_back(CurrLeafNode);
+    }
+  }
+}
+
 unsigned SuffixTree::extend(unsigned EndIdx, unsigned SuffixesToAdd) {
   SuffixTreeInternalNode *NeedsLink = nullptr;
 
@@ -230,6 +301,7 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
 
   // Each leaf node represents a repeat of a string.
   SmallVector<unsigned> RepeatedSubstringStarts;
+  SmallVector<SuffixTreeLeafNode *> LeafDescendants;
 
   // Continue visiting nodes until we find one which repeats more than once.
   while (!InternalNodesToVisit.empty()) {
@@ -241,30 +313,35 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
     // it's too short, we'll quit.
     unsigned Length = Curr->getConcatLen();
 
-    // Iterate over each child, saving internal nodes for visiting, and
-    // leaf nodes' SuffixIdx in RepeatedSubstringStarts. Internal nodes
-    // represent individual strings, which may repeat.
-    for (auto &ChildPair : Curr->Children) {
+    // Iterate over each child, saving internal nodes for visiting.
+    // Internal nodes represent individual strings, which may repeat.
+    for (auto &ChildPair : Curr->Children)
       // Save all of this node's children for processing.
       if (auto *InternalChild =
-              dyn_cast<SuffixTreeInternalNode>(ChildPair.second)) {
+              dyn_cast<SuffixTreeInternalNode>(ChildPair.second))
         InternalNodesToVisit.push_back(InternalChild);
-        continue;
-      }
-
-      if (Length < MinLength)
-        continue;
-
-      // Have an occurrence of a potentially repeated string. Save it.
-      auto *Leaf = cast<SuffixTreeLeafNode>(ChildPair.second);
-      RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
-    }
+    
+    // If length of repeated substring is below threshold, then skip it.
+    if (Length < MinLength)
+      continue;
 
     // The root never represents a repeated substring. If we're looking at
     // that, then skip it.
     if (Curr->isRoot())
       continue;
 
+    // Collect leaf children or leaf descendants by OutlinerLeafDescendants.
+    if (!OutlinerLeafDescendants) {
+      for (auto &ChildPair : Curr->Children) 
+        if (auto *Leaf = dyn_cast<SuffixTreeLeafNode>(ChildPair.second))
+          RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
+    } else {
+      LeafDescendants.assign(LeafNodes.begin() + Curr->getLeftLeafIdx(),
+                             LeafNodes.begin() + Curr->getRightLeafIdx() + 1);
+      for (SuffixTreeLeafNode *Leaf : LeafDescendants)
+        RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
+    }
+
     // Do we have any repeated substrings?
     if (RepeatedSubstringStarts.size() < 2)
       continue;
diff --git a/llvm/lib/Support/SuffixTreeNode.cpp b/llvm/lib/Support/SuffixTreeNode.cpp
index 113b990fd352f..9f1f94a39895e 100644
--- a/llvm/lib/Support/SuffixTreeNode.cpp
+++ b/llvm/lib/Support/SuffixTreeNode.cpp
@@ -38,3 +38,8 @@ unsigned SuffixTreeLeafNode::getEndIdx() const {
 
 unsigned SuffixTreeLeafNode::getSuffixIdx() const { return SuffixIdx; }
 void SuffixTreeLeafNode::setSuffixIdx(unsigned Idx) { SuffixIdx = Idx; }
+
+unsigned SuffixTreeNode::getLeftLeafIdx() const { return LeftLeafIdx; }
+unsigned SuffixTreeNode::getRightLeafIdx() const { return RightLeafIdx; }
+void SuffixTreeNode::setLeftLeafIdx(unsigned Idx) { LeftLeafIdx = Idx; }
+void SuffixTreeNode::setRightLeafIdx(unsigned Idx) { RightLeafIdx = Idx; }
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-cfi-tail-some.mir b/llvm/test/CodeGen/AArch64/machine-outliner-cfi-tail-some.mir
index 67d411962ce4f..3afa1d5559a58 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-cfi-tail-some.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-cfi-tail-some.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=aarch64-apple-unknown -run-pass=machine-outliner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=aarch64-apple-unknown -run-pass=machine-outliner -verify-machineinstrs -outliner-leaf-descendants=false %s -o - | FileCheck %s
 
 # Outlining CFI instructions is unsafe if we cannot outline all of the CFI
 # instructions from a function.  This shows that we choose not to outline the
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll b/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll
new file mode 100644
index 0000000000000..29076204e89f6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll
@@ -0,0 +1,124 @@
+; This test is mainly for the -outliner-leaf-descendants flag for MachineOutliner.
+;
+; ===================== -outliner-leaf-descendants=false =====================
+; MachineOutliner finds THREE key `OutlinedFunction` and outlines them. They are:
+;   ```
+;     mov     w0, #1
+;     mov     w1, #2
+;     mov     w2, #3
+;     mov     w3, #4
+;     mov     w4, #5
+;     mov     w5, #6 or #7 or #8
+;     b
+;   ```
+; Each has:
+;   - `SequenceSize=28` and `OccurrenceCount=2`
+;   - each Candidate has `CallOverhead=4` and `FrameOverhead=0`
+;   - `NotOutlinedCost=28*2=56` and `OutliningCost=4*2+28+0=36`
+;   - `Benefit=56-36=20` and `Priority=56/36=1.56`
+;
+; ===================== -outliner-leaf-descendants=true =====================
+; MachineOutliner finds a FOURTH key `OutlinedFunction`, which is:
+;   ```
+;   mov     w0, #1
+;   mov     w1, #2
+;   mov     w2, #3
+;   mov     w3, #4
+;   mov     w4, #5
+;   ```
+; This corresponds to an internal node that has ZERO leaf children, but SIX leaf descendants.
+; It has:
+;   - `SequenceSize=20` and `OccurrenceCount=6`
+;   - each Candidate has `CallOverhead=12` and `FrameOverhead=4`
+;   - `NotOutlinedCost=20*6=120` and `OutliningCost=12*6+20+4=96`
+;   - `Benefit=120-96=24` and `Priority=120/96=1.25`
+;
+; The FOURTH `OutlinedFunction` has lower _priority_ compared to the first THREE `OutlinedFunction`
+; Hence, if we additionally include the `-sort-per-priority` flag,  the first THREE `OutlinedFunction` are outlined.
+
+; RUN: llc %s -enable-machine-outliner=always -outliner-leaf-descendants=false -filetype=obj -o %t
+; RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=CHECK-BASELINE
+
+; RUN: llc %s -enable-machine-outliner=always -outliner-leaf-descendants=false -outliner-benefit-threshold=22 -filetype=obj -o %t
+; RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=CHECK-NO-CANDIDATE
+
+; RUN: llc %s -enable-machine-outliner=always -outliner-leaf-descendants=true -filetype=obj -o %t
+; RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=CHECK-BASELINE
+
+; RUN: llc %s -enable-machine-outliner=always -outliner-leaf-descendants=true -outliner-benefit-threshold=22 -filetype=obj -o %t
+; RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=CHECK-LEAF-DESCENDANTS
+
+
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-macosx14.0.0"
+
+declare i32 @_Z3fooiiii(i32 noundef, i32 noundef, i32 noundef, i32 noundef, i32 noundef, i32 noundef)
+
+define i32 @_Z2f1v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 6)
+  ret i32 %1
+}
+
+define i32 @_Z2f2v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 6)
+  ret i32 %1
+}
+
+define i32 @_Z2f3v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 7)
+  ret i32 %1
+}
+
+define i32 @_Z2f4v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 7)
+  ret i32 %1
+}
+
+define i32 @_Z2f5v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 8)
+  ret i32 %1
+}
+
+define i32 @_Z2f6v() minsize {
+  %1 = tail call i32 @_Z3fooiiii(i32 noundef 1, i32 noundef 2, i32 noundef 3, i32 noundef 4, i32 noundef 5, i32 noundef 8)
+  ret i32 %1
+}
+
+; CHECK-BASELINE: <_OUTLINED_FUNCTION_0>:
+; CHECK-BASELINE-NEXT: mov     w0, #0x1
+; CHECK-BASELINE-NEXT: mov     w1, #0x2
+; CHECK-BASELINE-NEXT: mov     w2, #0x3
+; CHECK-BASELINE-NEXT: mov     w3, #0x4
+; CHECK-BASELINE-NEXT: mov     w4, #0x5
+; CHECK-BASELINE-NEXT: mov     w5, #0x6
+; CHECK-BASELINE-NEXT: b
+
+; CHECK-BASELINE: <_OUTLINED_FUNCTION_1>:
+; CHECK-BASELINE-NEXT: mov     w0, #0x1
+; CHECK-BASELINE-NEXT: mov     w1, #0x2
+; CHECK-BASELINE-NEXT: mov     w2, #0x3
+; CHECK-BASELINE-NEXT: mov     w3, #0x4
+; CHECK-BASELINE-NEXT: mov     w4, #0x5
+; CHECK-BASELINE-NEXT: mov     w5, #0x8
+; CHECK-BASELINE-NEXT: b
+
+; CHECK-BASELINE: <_OUTLINED_FUNCTION_2>:
+; CHECK-BASELINE-NEXT: mov     w0, #0x1
+; CHECK-BASELINE-NEXT: mov     w1, #0x2
+; CHECK-BASELINE-NEXT: mov     w2, #0x3
+; CHECK-BASELINE-NEXT: mov     w3, #0x4
+; CHECK-BASELINE-NEXT: mov     w4, #0x5
+; CHECK-BASELINE-NEXT: mov     w5, #0x7
+; CHECK-BASELINE-NEXT: b
+
+; CHECK-LEAF-DESCENDANTS: <_OUTLINED_FUNCTION_0>:
+; CHECK-LEAF-DESCENDANTS-NEXT: mov     w0, #0x1
+; CHECK-LEAF-DESCENDANTS-NEXT: mov     w1, #0x2
+; CHECK-LEAF-DESCENDANTS-NEXT: mov     w2, #0x3
+; CHECK-LEAF-DESCENDANTS-NEXT: mov     w3, #0x4
+; CHECK-LEAF-DESCENDANTS-NEXT: mov     w4, #0x5
+; CHECK-LEAF-DESCENDANTS-NEXT: ret
+
+; CHECK-LEAF-DESCENDANTS-NOT: <_OUTLINED_FUNCTION_1>:
+
+; CHECK-NO-CANDIDATE-NOT: <_OUTLINED_FUNCTION_0>:
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir b/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
index c6bd4c1d04d87..8b0e03dbee8d8 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
@@ -1,5 +1,6 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --include-generated-funcs
-# RUN: llc %s -mtriple aarch64 -debug-only=machine-outliner -run-pass=machine-outliner -o - 2>&1 | FileCheck %s
+# RUN: llc %s -mtriple aarch64 -outliner-leaf-descendants=false -debug-only=machine-outliner -run-pass=machine-outliner -o - 2>&1 | FileCheck %s
+# RUN: llc %s -mtriple aarch64 -debug-only=machine-outliner -run-pass=machine-outliner -o - 2>&1 | FileCheck %s --check-prefix=CHECK-LEAF
 # REQUIRES: asserts
 
 # CHECK: *** Discarding overlapping candidates ***
@@ -54,6 +55,27 @@ body:             |
     ; CHECK-NEXT: BL @OUTLINED_FUNCTION_0, implicit-def $lr, implicit $sp, implicit-def $lr, implicit-def $x8, implicit-def $x9, implicit $sp, implicit $x0, implicit $x9
     ; CHECK-NEXT: RET undef $x9
 
+    ; CHECK-LEAF-LABEL: name: overlap
+    ; CHECK-LEAF: liveins: $x0, $x9
+    ; CHECK-LEAF-NEXT: {{  $}}
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: $x8 = ADDXri $x0, 3, 0
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: $x8 = ADDXri $x0, 3, 0
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: BL @OUTLINED_FUNCTION_0
+    ; CHECK-LEAF-NEXT: RET undef $x9
+
+    ; CHECK-LEAF-LABEL: name: OUTLINED_FUNCTION_0
+    ; CHECK-LEAF: liveins: $x0, $x9, $lr
+    ; CHECK-LEAF-NEXT: {{  $}}
+    ; CHECK-LEAF-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-LEAF-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-LEAF-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-LEAF-NEXT: RET $lr
+
     ; fixme: outline!
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir
index c1c2720dec6ad..22e5edef2a939 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-sp-mod.mir
@@ -1,4 +1,4 @@
-# RUN: llc -verify-machineinstrs -run-pass=machine-outliner -run-pass=aarch64-ptrauth %s -o - | FileCheck %s
+# RUN: llc -verify-machineinstrs -run-pass=machine-outliner -run-pass=aarch64-ptrauth -outliner-leaf-descendants=false %s -o - | FileCheck %s
 
 --- |
   target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-thunk.ll b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-thunk.ll
index 9250718fc0d58..618973b9368d1 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-thunk.ll
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-retaddr-sign-thunk.ll
@@ -1,6 +1,6 @@
-; RUN: llc -mtriple aarch64-arm-linux-gnu --enable-machine-outliner \
+; RUN: llc -mtriple aarch64-arm-linux-gnu --enable-machine-outliner -outliner-leaf-descendants=false \
 ; RUN: -verify-machineinstrs %s -o - | FileCheck --check-prefixes CHECK,V8A %s
-; RUN-V83A: llc -mtriple aarch64 -enable-machine-outliner \
+; RUN-V83A: llc -mtriple aarch64 -enable-machine-outliner -outliner-leaf-descendants=false \
 ; RUN-V83A: -verify-machineinstrs -mattr=+v8.3a %s -o - > %t
 ; RUN-V83A: FileCheck --check-prefixes CHECK,V83A < %t %s
 
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-throw2.ll b/llvm/test/CodeGen/AArch64/machine-outliner-throw2.ll
index aa6e31d6ff21d..538e1165e39c1 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-throw2.ll
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-throw2.ll
@@ -1,5 +1,5 @@
-; RUN: llc -verify-machineinstrs -enable-machine-outliner -mtriple=aarch64 -frame-pointer=non-leaf < %s | FileCheck %s --check-prefix=NOOMIT
-; RUN: llc -verify-machineinstrs -enable-machine-outliner -mtriple=aarch64 -frame-pointer=none < %s | FileCheck %s --check-prefix=OMITFP
+; RUN: llc -verify-machineinstrs -enable-machine-outliner -outliner-leaf-descendants=false -mtriple=aarch64 -frame-pointer=non-leaf < %s | FileCheck %s --check-prefix=NOOMIT
+; RUN: llc -verify-machineinstrs -enable-machine-outliner -outliner-leaf-descendants=false -mtriple=aarch64 -frame-pointer=none < %s | FileCheck %s --check-prefix=OMITFP
 
 define void @_Z1giii(i32 %x, i32 %y, i32 %z) minsize {
 ; NOOMIT-LABEL: _Z1giii:
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-thunk.ll b/llvm/test/CodeGen/AArch64/machine-outliner-thunk.ll
index 8740aac0549ee..7e34adf16d25d 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-thunk.ll
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-thunk.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc < %s -enable-machine-outliner -verify-machineinstrs | FileCheck %s
+; RUN: llc < %s -enable-machine-outliner -outliner-leaf-descendants=false -verify-machineinstrs | FileCheck %s
 
 target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
 target triple = "aarch64-pc-linux-gnu"
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner.mir b/llvm/test/CodeGen/AArch64/machine-outliner.mir
index 83eda744d24a9..66779addaff0a 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner.mir
@@ -1,4 +1,4 @@
-# RUN: llc -mtriple=aarch64--- -run-pass=prologepilog -run-pass=machine-outliner -verify-machineinstrs -frame-pointer=non-leaf %s -o - | FileCheck %s
+# RUN: llc -mtriple=aarch64--- -run-pass=prologepilog -run-pass=machine-outliner -verify-machineinstrs -frame-pointer=non-leaf -outliner-leaf-descendants=false %s -o - | FileCheck %s
 --- |
 
   @x = common global i32 0, align 4
diff --git a/llvm/test/CodeGen/RISCV/machineoutliner-pcrel-lo.mir b/llvm/test/CodeGen/RISCV/machineoutliner-pcrel-lo.mir
index 34f7a93b6a168..8a83543b0280f 100644
--- a/llvm/test/CodeGen/RISCV/machineoutliner-pcrel-lo.mir
+++ b/llvm/test/CodeGen/RISCV/machineoutliner-pcrel-lo.mir
@@ -1,11 +1,11 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=riscv32 -x mir -run-pass=machine-outliner -simplify-mir -verify-machineinstrs < %s \
+# RUN: llc -mtriple=riscv32 -x mir -run-pass=machine-outliner -simplify-mir -verify-machineinstrs -outliner-leaf-descendants=false < %s \
 # RUN: | FileCheck %s
-# RUN: llc -mtriple=riscv64 -x mir -run-pass=machine-outliner -simplify-mir -verify-machineinstrs < %s \
+# RUN: llc -mtriple=riscv64 -x mir -run-pass=machine-outliner -simplify-mir -verify-machineinstrs -outliner-leaf-descendants=false < %s \
 # RUN: | FileCheck %s
-# RUN: llc -mtriple=riscv32 -x mir -run-pass=machine-outliner -simplify-mir --function-sections -verify-machineinstrs < %s \
+# RUN: llc -mtriple=riscv32 -x mir -run-pass=machine-outliner -simplify-mir --function-sections -verify-machineinstrs -outliner-leaf-descendants=false < %s \
 # RUN: | FileCheck -check-prefix=CHECK-FS %s
-# RUN: llc -mtriple=riscv64 -x mir -run-pass=machine-outliner -simplify-mir --function-sections -verify-machineinstrs < %s \
+# RUN: llc -mtriple=riscv64 -x mir -run-pass=machine-outliner -simplify-mir --function-sections -verify-machineinstrs -outliner-leaf-descendants=false < %s \
 # RUN: | FileCheck -check-prefix=CHECK-FS %s
 
 --- |
diff --git a/llvm/unittests/Support/SuffixTreeTest.cpp b/llvm/unittests/Support/SuffixTreeTest.cpp
index f5d8112ccf5ce..59f6dc9bf963f 100644
--- a/llvm/unittests/Support/SuffixTreeTest.cpp
+++ b/llvm/unittests/Support/SuffixTreeTest.cpp
@@ -140,4 +140,138 @@ TEST(SuffixTreeTest, TestExclusion) {
   }
 }
 
+// Tests that the SuffixTree is able to find the following substrings:
+// {1, 1} at indices 0, 1, 2, 3, and 4;
+// {1, 1, 1} at indices 0, 1, 2, and 3;
+// {1, 1, 1, 1}  at indices 0, 1, and 2; and
+// {1, 1, 1, 1, 1} at indices 0 and 1.
+//
+// This is a FIX to the Test TestSingleCharacterRepeat
+TEST(SuffixTreeTest, TestSingleCharacterRepeatWithLeafDescendants) {
+  std::vector<unsigned> RepeatedRepetitionData = {1, 1, 1, 1, 1, 1, 2};
+  std::vector<unsigned>::iterator RRDIt, RRDIt2;
+  SuffixTree ST(RepeatedRepetitionData, true);
+  std::vector<SuffixTree::RepeatedSubstring> SubStrings;
+  for (auto It = ST.begin(); It != ST.end(); It++)
+    SubStrings.push_back(*It);
+  EXPECT_EQ(SubStrings.size(), 4u);
+  for (SuffixTree::RepeatedSubstring &RS : SubStrings) {
+    EXPECT_EQ(RS.StartIndices.size(),
+              RepeatedRepetitionData.size() - RS.Length);
+    for (unsigned StartIdx : SubStrings[0].StartIndices) {
+      RRDIt = RRDIt2 = RepeatedRepetitionData.begin();
+      std::advance(RRDIt, StartIdx);
+      std::advance(RRDIt2, StartIdx + SubStrings[0].Length);
+      ASSERT_TRUE(
+          all_of(make_range<std::vector<unsigned>::iterator>(RRDIt, RRDIt2),
+                 [](unsigned Elt) { return Elt == 1; }));
+    }
+  }
+}
+
+// Tests that the SuffixTree is able to find three substrings
+// {1, 2, 3} at indices 6 and 10;
+// {2, 3} at indices 7 and 11; and
+// {1, 2} at indicies 0 and 3.
+//
+// FIXME: {1, 2} has indices 6 and 10 missing as it is a substring of {1, 2, 3}
+// See Test TestSubstringRepeatsWithLeafDescendants for the FIX
+TEST(SuffixTreeTest, TestSubstringRepeats) {
+  std::vector<unsigned> RepeatedRepetitionData = {1, 2, 100, 1, 2, 101, 1,
+                                                  2, 3, 103, 1, 2, 3,   104};
+  SuffixTree ST(RepeatedRepetitionData);
+  std::vector<SuffixTree::RepeatedSubstring> SubStrings;
+  for (auto It = ST.begin(); It != ST.end(); It++)
+    SubStrings.push_back(*It);
+  EXPECT_EQ(SubStrings.size(), 3u);
+  unsigned Len;
+  for (SuffixTree::RepeatedSubstring &RS : SubStrings) {
+    Len = RS.Length;
+    bool IsExpectedLen = (Len == 3u || Len == 2u);
+    ASSERT_TRUE(IsExpectedLen);
+    bool IsExpectedIndex;
+
+    if (Len == 3u) { // {1, 2, 3}
+      EXPECT_EQ(RS.StartIndices.size(), 2u);
+      for (unsigned StartIdx : RS.StartIndices) {
+        IsExpectedIndex = (StartIdx == 6u || StartIdx == 10u);
+        EXPECT_TRUE(IsExpectedIndex);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx], 1u);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 2u);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx + 2], 3u);
+      }
+    } else {
+      if (RepeatedRepetitionData[RS.StartIndices[0]] == 1u) { // {1, 2}
+        EXPECT_EQ(RS.StartIndices.size(), 2u);
+        for (unsigned StartIdx : RS.StartIndices) {
+          IsExpectedIndex = (StartIdx == 0u || StartIdx == 3u);
+          EXPECT_TRUE(IsExpectedIndex);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 2u);
+        }
+      } else { // {2, 3}
+        EXPECT_EQ(RS.StartIndices.size(), 2u);
+        for (unsigned StartIdx : RS.StartIndices) {
+          IsExpectedIndex = (StartIdx == 7u || StartIdx == 11u);
+          EXPECT_TRUE(IsExpectedIndex);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx], 2u);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 3u);
+        }
+      }
+    }
+  }
+}
+
+// Tests that the SuffixTree is able to find three substrings
+// {1, 2, 3} at indices 6 and 10;
+// {2, 3} at indices 7 and 11; and
+// {1, 2} at indicies 0, 3, 6, and 10.
+//
+// This is a FIX to the Test TestSubstringRepeats
+
+TEST(SuffixTreeTest, TestSubstringRepeatsWithLeafDescendants) {
+  std::vector<unsigned> RepeatedRepetitionData = {1, 2, 100, 1, 2, 101, 1,
+                                                  2, 3, 103, 1, 2, 3,   104};
+  SuffixTree ST(RepeatedRepetitionData, true);
+  std::vector<SuffixTree::RepeatedSubstring> SubStrings;
+  for (auto It = ST.begin(); It != ST.end(); It++)
+    SubStrings.push_back(*It);
+  EXPECT_EQ(SubStrings.size(), 3u);
+  unsigned Len;
+  for (SuffixTree::RepeatedSubstring &RS : SubStrings) {
+    Len = RS.Length;
+    bool IsExpectedLen = (Len == 3u || Len == 2u);
+    ASSERT_TRUE(IsExpectedLen);
+    bool IsExpectedIndex;
+
+    if (Len == 3u) { // {1, 2, 3}
+      EXPECT_EQ(RS.StartIndices.size(), 2u);
+      for (unsigned StartIdx : RS.StartIndices) {
+        IsExpectedIndex = (StartIdx == 6u || StartIdx == 10u);
+        EXPECT_TRUE(IsExpectedIndex);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx], 1u);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 2u);
+        EXPECT_EQ(RepeatedRepetitionData[StartIdx + 2], 3u);
+      }
+    } else {
+      if (RepeatedRepetitionData[RS.StartIndices[0]] == 1u) { // {1, 2}
+        EXPECT_EQ(RS.StartIndices.size(), 4u);
+        for (unsigned StartIdx : RS.StartIndices) {
+          IsExpectedIndex = (StartIdx == 0u || StartIdx == 3u ||
+                             StartIdx == 6u || StartIdx == 10u);
+          EXPECT_TRUE(IsExpectedIndex);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 2u);
+        }
+      } else { // {2, 3}
+        EXPECT_EQ(RS.StartIndices.size(), 2u);
+        for (unsigned StartIdx : RS.StartIndices) {
+          IsExpectedIndex = (StartIdx == 7u || StartIdx == 11u);
+          EXPECT_TRUE(IsExpectedIndex);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx], 2u);
+          EXPECT_EQ(RepeatedRepetitionData[StartIdx + 1], 3u);
+        }
+      }
+    }
+  }
+}
+
 } // namespace

>From 09465ff8bb0bed4c131ff060e526e25b14870a73 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 7 Jun 2024 15:13:57 -0700
Subject: [PATCH 2/8] clang-format

---
 llvm/lib/Support/SuffixTree.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index e868dfe513860..0d2e5baf5834d 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -320,7 +320,7 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
       if (auto *InternalChild =
               dyn_cast<SuffixTreeInternalNode>(ChildPair.second))
         InternalNodesToVisit.push_back(InternalChild);
-    
+
     // If length of repeated substring is below threshold, then skip it.
     if (Length < MinLength)
       continue;
@@ -332,7 +332,7 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
 
     // Collect leaf children or leaf descendants by OutlinerLeafDescendants.
     if (!OutlinerLeafDescendants) {
-      for (auto &ChildPair : Curr->Children) 
+      for (auto &ChildPair : Curr->Children)
         if (auto *Leaf = dyn_cast<SuffixTreeLeafNode>(ChildPair.second))
           RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
     } else {

>From 333fa3dc9f8f06cb6dfaf7c309120a3a806e0418 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 15:06:03 -0700
Subject: [PATCH 3/8] update comments in leaf descendants test

---
 .../test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll b/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll
index 29076204e89f6..d78eea70f0104 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-leaf-descendants.ll
@@ -33,8 +33,8 @@
 ;   - `NotOutlinedCost=20*6=120` and `OutliningCost=12*6+20+4=96`
 ;   - `Benefit=120-96=24` and `Priority=120/96=1.25`
 ;
-; The FOURTH `OutlinedFunction` has lower _priority_ compared to the first THREE `OutlinedFunction`
-; Hence, if we additionally include the `-sort-per-priority` flag,  the first THREE `OutlinedFunction` are outlined.
+; The FOURTH `OutlinedFunction` has lower _priority_ compared to the first THREE `OutlinedFunction`.
+; Hence, we use `-outliner-benefit-threshold=22` to check if the FOURTH `OutlinedFunction` is identified.
 
 ; RUN: llc %s -enable-machine-outliner=always -outliner-leaf-descendants=false -filetype=obj -o %t
 ; RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=CHECK-BASELINE

>From 2be4cb42c381e70500a9de9703f37c268afe69e0 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 15:06:56 -0700
Subject: [PATCH 4/8] clean up setLeafNodes

---
 llvm/lib/Support/SuffixTree.cpp | 79 +++++++++++++++++----------------
 1 file changed, 40 insertions(+), 39 deletions(-)

diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index 0d2e5baf5834d..dc9aaef827fd7 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -116,57 +116,60 @@ void SuffixTree::setSuffixIndices() {
 
 void SuffixTree::setLeafNodes() {
   // A stack that keeps track of nodes to visit for post-order DFS traversal.
-  std::stack<SuffixTreeNode *> ToVisit;
-  ToVisit.push(Root);
+  SmallVector<SuffixTreeNode *> ToVisit;
+  ToVisit.push_back(Root);
 
   // This keeps track of the index of the next leaf node to be added to
   // the LeafNodes vector of the suffix tree.
   unsigned LeafCounter = 0;
 
-  // This keeps track of nodes whose children have been added to the stack
-  // during the post-order depth-first traversal of the tree.
-  llvm::SmallPtrSet<SuffixTreeInternalNode *, 32> ChildrenAddedToStack;
+  // This keeps track of nodes whose children have been added to the stack.
+  // The value is a pair, representing a node's first and last children.
+  DenseMap<SuffixTreeInternalNode *,
+           std::pair<SuffixTreeNode *, SuffixTreeNode *>>
+      ChildrenMap;
 
   // Traverse the tree in post-order.
   while (!ToVisit.empty()) {
-    SuffixTreeNode *CurrNode = ToVisit.top();
-    ToVisit.pop();
+    SuffixTreeNode *CurrNode = ToVisit.back();
+    ToVisit.pop_back();
     if (auto *CurrInternalNode = dyn_cast<SuffixTreeInternalNode>(CurrNode)) {
       // The current node is an internal node.
-      if (ChildrenAddedToStack.find(CurrInternalNode) !=
-          ChildrenAddedToStack.end()) {
-        // If the children of the current node has been added to the stack,
-        // then this is the second time we visit this node and at this point,
-        // all of its children have already been processed. Now, we can
-        // set its LeftLeafIdx and RightLeafIdx;
+      auto I = ChildrenMap.find(CurrInternalNode);
+      if (I == ChildrenMap.end()) {
+        // This is the first time we visit this node.
+        // Its children have not been added to the stack yet. 
+        // We add current node back, and add its children to the stack.
+        // We keep track of the first and last children of the current node.
         auto it = CurrInternalNode->Children.begin();
         if (it != CurrInternalNode->Children.end()) {
-          // Get the first child to use its RightLeafIdx. The RightLeafIdx is
-          // used as the first child is the initial one added to the stack, so
-          // it's the last one to be processed. This implies that the leaf
-          // descendants of the first child are assigned the largest index
-          // numbers.
-          CurrNode->setRightLeafIdx(it->second->getRightLeafIdx());
-          // get the last child to use its LeftLeafIdx.
-          while (std::next(it) != CurrInternalNode->Children.end())
-            it = std::next(it);
-          CurrNode->setLeftLeafIdx(it->second->getLeftLeafIdx());
-          assert(CurrNode->getLeftLeafIdx() <= CurrNode->getRightLeafIdx() &&
-                 "LeftLeafIdx should not be larger than RightLeafIdx");
+          ToVisit.push_back(CurrNode);
+          SuffixTreeNode *FirstChild = it->second;
+          SuffixTreeNode *LastChild = nullptr;
+          for (; it != CurrInternalNode->Children.end(); ++it) {
+            LastChild = it->second;
+            ToVisit.push_back(LastChild);
+          }
+          ChildrenMap[CurrInternalNode] = {FirstChild, LastChild};
         }
       } else {
-        // This is the first time we visit this node. This means that its
-        // children have not been added to the stack yet. Hence, we will add
-        // the current node back to the stack and add its children to the
-        // stack for processing.
-        ToVisit.push(CurrNode);
-        for (auto &ChildPair : CurrInternalNode->Children)
-          ToVisit.push(ChildPair.second);
-        ChildrenAddedToStack.insert(CurrInternalNode);
+        // This is the second time we visit this node. 
+        // All of its children have already been processed. 
+        // Now, we can set its LeftLeafIdx and RightLeafIdx;
+        auto [FirstChild, LastChild] = I->second;
+        // Get the first child to use its RightLeafIdx. 
+        // The first child is the first one added to the stack, so it is
+        // the last one to be processed. Hence, the leaf descendants
+        // of the first child are assigned the largest index numbers.
+        CurrNode->setRightLeafIdx(FirstChild->getRightLeafIdx());
+        // Get the last child to use its LeftLeafIdx.
+        CurrNode->setLeftLeafIdx(LastChild->getLeftLeafIdx());
+        assert(CurrNode->getLeftLeafIdx() <= CurrNode->getRightLeafIdx() &&
+              "LeftLeafIdx should not be larger than RightLeafIdx");
       }
     } else {
       // The current node is a leaf node.
-      // We can simplyset its LeftLeafIdx and RightLeafIdx.
+      // We can simply set its LeftLeafIdx and RightLeafIdx.
       CurrNode->setLeftLeafIdx(LeafCounter);
       CurrNode->setRightLeafIdx(LeafCounter);
       LeafCounter++;
@@ -301,7 +304,6 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
 
   // Each leaf node represents a repeat of a string.
   SmallVector<unsigned> RepeatedSubstringStarts;
-  SmallVector<SuffixTreeLeafNode *> LeafDescendants;
 
   // Continue visiting nodes until we find one which repeats more than once.
   while (!InternalNodesToVisit.empty()) {
@@ -336,10 +338,9 @@ void SuffixTree::RepeatedSubstringIterator::advance() {
         if (auto *Leaf = dyn_cast<SuffixTreeLeafNode>(ChildPair.second))
           RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
     } else {
-      LeafDescendants.assign(LeafNodes.begin() + Curr->getLeftLeafIdx(),
-                             LeafNodes.begin() + Curr->getRightLeafIdx() + 1);
-      for (SuffixTreeLeafNode *Leaf : LeafDescendants)
-        RepeatedSubstringStarts.push_back(Leaf->getSuffixIdx());
+      for (unsigned I = Curr->getLeftLeafIdx(); I <= Curr->getRightLeafIdx();
+           ++I)
+        RepeatedSubstringStarts.push_back(LeafNodes[I]->getSuffixIdx());
     }
 
     // Do we have any repeated substrings?

>From e31d9904a39472bd8091b38ffb1667f41e98d2f0 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 15:21:35 -0700
Subject: [PATCH 5/8] clang-format

---
 llvm/lib/Support/SuffixTree.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index dc9aaef827fd7..8e794839d492d 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -138,7 +138,7 @@ void SuffixTree::setLeafNodes() {
       auto I = ChildrenMap.find(CurrInternalNode);
       if (I == ChildrenMap.end()) {
         // This is the first time we visit this node.
-        // Its children have not been added to the stack yet. 
+        // Its children have not been added to the stack yet.
         // We add current node back, and add its children to the stack.
         // We keep track of the first and last children of the current node.
         auto it = CurrInternalNode->Children.begin();
@@ -153,11 +153,11 @@ void SuffixTree::setLeafNodes() {
           ChildrenMap[CurrInternalNode] = {FirstChild, LastChild};
         }
       } else {
-        // This is the second time we visit this node. 
-        // All of its children have already been processed. 
+        // This is the second time we visit this node.
+        // All of its children have already been processed.
         // Now, we can set its LeftLeafIdx and RightLeafIdx;
         auto [FirstChild, LastChild] = I->second;
-        // Get the first child to use its RightLeafIdx. 
+        // Get the first child to use its RightLeafIdx.
         // The first child is the first one added to the stack, so it is
         // the last one to be processed. Hence, the leaf descendants
         // of the first child are assigned the largest index numbers.
@@ -165,7 +165,7 @@ void SuffixTree::setLeafNodes() {
         // Get the last child to use its LeftLeafIdx.
         CurrNode->setLeftLeafIdx(LastChild->getLeftLeafIdx());
         assert(CurrNode->getLeftLeafIdx() <= CurrNode->getRightLeafIdx() &&
-              "LeftLeafIdx should not be larger than RightLeafIdx");
+               "LeftLeafIdx should not be larger than RightLeafIdx");
       }
     } else {
       // The current node is a leaf node.

>From aaa53ce454e8e8ffe2235d95714ed4f09a386d57 Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 15:25:08 -0700
Subject: [PATCH 6/8] add check for OUTLINED_FUNCTION_0 in
 machine-outliner-overlap.mir

---
 .../CodeGen/AArch64/machine-outliner-overlap.mir  | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir b/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
index 8b0e03dbee8d8..48a97b65225f1 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-overlap.mir
@@ -55,6 +55,18 @@ body:             |
     ; CHECK-NEXT: BL @OUTLINED_FUNCTION_0, implicit-def $lr, implicit $sp, implicit-def $lr, implicit-def $x8, implicit-def $x9, implicit $sp, implicit $x0, implicit $x9
     ; CHECK-NEXT: RET undef $x9
 
+    ; CHECK-LABEL: name: OUTLINED_FUNCTION_0
+    ; CHECK: liveins: $x0, $x9, $lr
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $x8 = ADDXri $x0, 3, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: $x9 = ADDXri $x9, 16, 0
+    ; CHECK-NEXT: RET $lr
+
     ; CHECK-LEAF-LABEL: name: overlap
     ; CHECK-LEAF: liveins: $x0, $x9
     ; CHECK-LEAF-NEXT: {{  $}}
@@ -76,7 +88,6 @@ body:             |
     ; CHECK-LEAF-NEXT: $x9 = ADDXri $x9, 16, 0
     ; CHECK-LEAF-NEXT: RET $lr
 
-    ; fixme: outline!
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
@@ -86,7 +97,6 @@ body:             |
 
     $x8 = ADDXri $x0, 3, 0
 
-    ; outline
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
@@ -96,7 +106,6 @@ body:             |
 
     $x8 = ADDXri $x0, 3, 0
 
-    ; outline
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0
     $x9 = ADDXri $x9, 16, 0

>From 63abc9edf15a355bd0ad51dd67cb294793e9b67c Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 20:56:05 -0700
Subject: [PATCH 7/8] small fixes

---
 llvm/lib/Support/SuffixTree.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index 8e794839d492d..7c2a131bc2601 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -11,11 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/SuffixTree.h"
-#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/SuffixTreeNode.h"
-#include <stack>
 
 using namespace llvm;
 
@@ -131,8 +129,7 @@ void SuffixTree::setLeafNodes() {
 
   // Traverse the tree in post-order.
   while (!ToVisit.empty()) {
-    SuffixTreeNode *CurrNode = ToVisit.back();
-    ToVisit.pop_back();
+    SuffixTreeNode *CurrNode = ToVisit. pop_back_val();
     if (auto *CurrInternalNode = dyn_cast<SuffixTreeInternalNode>(CurrNode)) {
       // The current node is an internal node.
       auto I = ChildrenMap.find(CurrInternalNode);

>From a197dc979c93d51548bf2d263d636c4b989c6e6a Mon Sep 17 00:00:00 2001
From: Xuan Zhang <xuanzh at meta.com>
Date: Fri, 14 Jun 2024 21:05:14 -0700
Subject: [PATCH 8/8] format

---
 llvm/lib/Support/SuffixTree.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp
index 7c2a131bc2601..9187a09e20baa 100644
--- a/llvm/lib/Support/SuffixTree.cpp
+++ b/llvm/lib/Support/SuffixTree.cpp
@@ -129,7 +129,7 @@ void SuffixTree::setLeafNodes() {
 
   // Traverse the tree in post-order.
   while (!ToVisit.empty()) {
-    SuffixTreeNode *CurrNode = ToVisit. pop_back_val();
+    SuffixTreeNode *CurrNode = ToVisit.pop_back_val();
     if (auto *CurrInternalNode = dyn_cast<SuffixTreeInternalNode>(CurrNode)) {
       // The current node is an internal node.
       auto I = ChildrenMap.find(CurrInternalNode);



More information about the llvm-commits mailing list