[llvm] [SSAUpdater] Avoid scanning basic blocks to find instruction order. (PR #123803)

Joshua Cranmer via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 21 13:16:29 PST 2025


https://github.com/jcranmer-intel updated https://github.com/llvm/llvm-project/pull/123803

>From 8fa00f08a34275ca0bfc16f9b90ade84cb407c87 Mon Sep 17 00:00:00 2001
From: Joshua Cranmer <joshua.cranmer at intel.com>
Date: Tue, 21 Jan 2025 10:40:55 -0800
Subject: [PATCH 1/3] [SSAUpdater] Avoid scanning basic blocks to find
 instruction order.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes a compile-time regression caused by #116645, where an entry
basic block with a very large number of allocas and other instructions
caused SROA to take ~100× its expected runtime, as every alloca (with ~2
uses) now calls this method to find the order of those few instructions,
rescanning the very large basic block every single time.

Since this code was originally written, Instructions now have ordering
numbers available to determine relative order without unnecessarily
scanning the basic block.
---
 llvm/lib/Transforms/Utils/SSAUpdater.cpp | 25 ++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
index 4bf4acd6330f58..79d46a88cb454b 100644
--- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp
+++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
@@ -432,9 +432,7 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
       }
     }
 
-    // If so, we can queue them all as live in loads.  We don't have an
-    // efficient way to tell which on is first in the block and don't want to
-    // scan large blocks, so just add all loads as live ins.
+    // If so, we can queue them all as live in loads.
     if (!HasStore) {
       for (Instruction *I : BlockUses)
         LiveInLoads.push_back(cast<LoadInst>(I));
@@ -442,16 +440,22 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
       continue;
     }
 
+    // Sort all of the interesting instructions in the block so that we don't
+    // have to scan a large block just to find a few instructions.
+    std::sort(BlockUses.begin(), BlockUses.end(),
+              [](Instruction *A, Instruction *B) { return A->comesBefore(B); });
+
     // Otherwise, we have mixed loads and stores (or just a bunch of stores).
     // Since SSAUpdater is purely for cross-block values, we need to determine
     // the order of these instructions in the block.  If the first use in the
     // block is a load, then it uses the live in value.  The last store defines
-    // the live out value.  We handle this by doing a linear scan of the block.
+    // the live out value.
     Value *StoredValue = nullptr;
-    for (Instruction &I : *BB) {
-      if (LoadInst *L = dyn_cast<LoadInst>(&I)) {
+    for (Instruction *I : BlockUses) {
+      if (LoadInst *L = dyn_cast<LoadInst>(I)) {
         // If this is a load from an unrelated pointer, ignore it.
-        if (!isInstInList(L, Insts)) continue;
+        if (!isInstInList(L, Insts))
+          continue;
 
         // If we haven't seen a store yet, this is a live in use, otherwise
         // use the stored value.
@@ -465,14 +469,15 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
         continue;
       }
 
-      if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
+      if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
         // If this is a store to an unrelated pointer, ignore it.
-        if (!isInstInList(SI, Insts)) continue;
+        if (!isInstInList(SI, Insts))
+          continue;
         updateDebugInfo(SI);
 
         // Remember that this is the active value in the block.
         StoredValue = SI->getOperand(0);
-      } else if (auto *AI = dyn_cast<AllocaInst>(&I)) {
+      } else if (auto *AI = dyn_cast<AllocaInst>(I)) {
         // Check if this an alloca, in which case we treat it as a store of
         // getValueToUseForAlloca.
         if (!isInstInList(AI, Insts))

>From 93e0e471dc46da002628e713f84009ad96018f0d Mon Sep 17 00:00:00 2001
From: Joshua Cranmer <joshua.cranmer at intel.com>
Date: Tue, 21 Jan 2025 12:42:44 -0800
Subject: [PATCH 2/3] Remove redundant checks.

---
 llvm/lib/Transforms/Utils/SSAUpdater.cpp | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
index 79d46a88cb454b..92345f992028f6 100644
--- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp
+++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
@@ -453,10 +453,6 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
     Value *StoredValue = nullptr;
     for (Instruction *I : BlockUses) {
       if (LoadInst *L = dyn_cast<LoadInst>(I)) {
-        // If this is a load from an unrelated pointer, ignore it.
-        if (!isInstInList(L, Insts))
-          continue;
-
         // If we haven't seen a store yet, this is a live in use, otherwise
         // use the stored value.
         if (StoredValue) {
@@ -470,9 +466,6 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
       }
 
       if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
-        // If this is a store to an unrelated pointer, ignore it.
-        if (!isInstInList(SI, Insts))
-          continue;
         updateDebugInfo(SI);
 
         // Remember that this is the active value in the block.
@@ -480,8 +473,6 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
       } else if (auto *AI = dyn_cast<AllocaInst>(I)) {
         // Check if this an alloca, in which case we treat it as a store of
         // getValueToUseForAlloca.
-        if (!isInstInList(AI, Insts))
-          continue;
         StoredValue = getValueToUseForAlloca(AI);
       }
     }

>From 99ac764aa640f57160749e17fd7173df9d157a4c Mon Sep 17 00:00:00 2001
From: Joshua Cranmer <joshua.cranmer at intel.com>
Date: Tue, 21 Jan 2025 13:15:50 -0800
Subject: [PATCH 3/3] Address review comments.

---
 llvm/include/llvm/Transforms/Utils/SSAUpdater.h |  7 -------
 llvm/lib/Transforms/Utils/SSAUpdater.cpp        | 12 +++---------
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Utils/SSAUpdater.h b/llvm/include/llvm/Transforms/Utils/SSAUpdater.h
index 989cf0b2d0e7b4..4e5da81a7e8851 100644
--- a/llvm/include/llvm/Transforms/Utils/SSAUpdater.h
+++ b/llvm/include/llvm/Transforms/Utils/SSAUpdater.h
@@ -164,13 +164,6 @@ class LoadAndStorePromoter {
   /// removed from the code.
   void run(const SmallVectorImpl<Instruction *> &Insts);
 
-  /// Return true if the specified instruction is in the Inst list.
-  ///
-  /// The Insts list is the one passed into the constructor. Clients should
-  /// implement this with a more efficient version if possible.
-  virtual bool isInstInList(Instruction *I,
-                            const SmallVectorImpl<Instruction *> &Insts) const;
-
   /// This hook is invoked after all the stores are found and inserted as
   /// available values.
   virtual void doExtraRewritesBeforeFinalDeletion() {}
diff --git a/llvm/lib/Transforms/Utils/SSAUpdater.cpp b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
index 92345f992028f6..229b1d9f07f8c7 100644
--- a/llvm/lib/Transforms/Utils/SSAUpdater.cpp
+++ b/llvm/lib/Transforms/Utils/SSAUpdater.cpp
@@ -442,8 +442,9 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
 
     // Sort all of the interesting instructions in the block so that we don't
     // have to scan a large block just to find a few instructions.
-    std::sort(BlockUses.begin(), BlockUses.end(),
-              [](Instruction *A, Instruction *B) { return A->comesBefore(B); });
+    llvm::sort(
+        BlockUses.begin(), BlockUses.end(),
+        [](Instruction *A, Instruction *B) { return A->comesBefore(B); });
 
     // Otherwise, we have mixed loads and stores (or just a bunch of stores).
     // Since SSAUpdater is purely for cross-block values, we need to determine
@@ -529,10 +530,3 @@ void LoadAndStorePromoter::run(const SmallVectorImpl<Instruction *> &Insts) {
     User->eraseFromParent();
   }
 }
-
-bool
-LoadAndStorePromoter::isInstInList(Instruction *I,
-                                   const SmallVectorImpl<Instruction *> &Insts)
-                                   const {
-  return is_contained(Insts, I);
-}



More information about the llvm-commits mailing list